music_cmd.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 17 Oct 2017 02:33:47 -0700
changeset 777 92882ef2ab81
parent 763 52007e252a1c
child 779 a2b494c054d5
permissions -rw-r--r--
Rewrote music.c to support any number of decode libraries using a compiled-in plugin interface
Mix_LoadWAV_RW() can now load sound formats that were previously available only as music.

This is still work in progress. Testing and project updates need to happen on other platforms.
slouken@0
     1
/*
slouken@518
     2
  SDL_mixer:  An audio mixer library based on the SDL library
slouken@725
     3
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
slouken@0
     4
slouken@518
     5
  This software is provided 'as-is', without any express or implied
slouken@518
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@518
     7
  arising from the use of this software.
slouken@0
     8
slouken@518
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@518
    10
  including commercial applications, and to alter it and redistribute it
slouken@518
    11
  freely, subject to the following restrictions:
slouken@0
    12
slouken@518
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@518
    14
     claim that you wrote the original software. If you use this software
slouken@518
    15
     in a product, an acknowledgment in the product documentation would be
slouken@518
    16
     appreciated but is not required.
slouken@518
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@518
    18
     misrepresented as being the original software.
slouken@518
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@0
    20
*/
slouken@294
    21
#include "SDL_config.h"
slouken@138
    22
slouken@0
    23
/* This file supports an external command for playing music */
slouken@0
    24
slouken@777
    25
#ifdef MUSIC_CMD
slouken@0
    26
slouken@0
    27
#include <sys/types.h>
slouken@0
    28
#include <sys/wait.h>
slouken@0
    29
#include <stdio.h>
slouken@0
    30
#include <stdlib.h>
slouken@0
    31
#include <unistd.h>
slouken@0
    32
#include <string.h>
slouken@0
    33
#include <signal.h>
slouken@155
    34
#include <ctype.h>
slouken@777
    35
#include <limits.h>
slouken@777
    36
#if defined(__linux__) && defined(__arm__)
slouken@777
    37
# include <linux/limits.h>
slouken@777
    38
#endif
slouken@0
    39
slouken@0
    40
#include "music_cmd.h"
slouken@0
    41
slouken@777
    42
slouken@777
    43
typedef struct {
slouken@777
    44
    char *file;
slouken@777
    45
    pid_t pid;
slouken@777
    46
} MusicCMD;
slouken@777
    47
slouken@0
    48
slouken@0
    49
/* Load a music stream from the given file */
slouken@777
    50
static void *MusicCMD_CreateFromFile(const char *file)
slouken@0
    51
{
slouken@617
    52
    MusicCMD *music;
slouken@0
    53
slouken@617
    54
    /* Allocate and fill the music structure */
slouken@777
    55
    music = (MusicCMD *)SDL_calloc(1, sizeof *music);
slouken@777
    56
    if (music == NULL) {
slouken@617
    57
        Mix_SetError("Out of memory");
slouken@777
    58
        return NULL;
slouken@617
    59
    }
slouken@617
    60
    music->file = SDL_strdup(file);
slouken@617
    61
    music->pid = 0;
slouken@0
    62
slouken@617
    63
    /* We're done */
slouken@777
    64
    return music;
slouken@0
    65
}
slouken@0
    66
slouken@0
    67
/* Parse a command line buffer into arguments */
slouken@0
    68
static int ParseCommandLine(char *cmdline, char **argv)
slouken@0
    69
{
slouken@617
    70
    char *bufp;
slouken@617
    71
    int argc;
slouken@0
    72
slouken@617
    73
    argc = 0;
slouken@777
    74
    for (bufp = cmdline; *bufp;) {
slouken@617
    75
        /* Skip leading whitespace */
slouken@777
    76
        while (isspace(*bufp)) {
slouken@617
    77
            ++bufp;
slouken@617
    78
        }
slouken@617
    79
        /* Skip over argument */
slouken@777
    80
        if (*bufp == '"') {
slouken@617
    81
            ++bufp;
slouken@777
    82
            if (*bufp) {
slouken@777
    83
                if (argv) {
slouken@617
    84
                    argv[argc] = bufp;
slouken@617
    85
                }
slouken@617
    86
                ++argc;
slouken@617
    87
            }
slouken@617
    88
            /* Skip over word */
slouken@777
    89
            while (*bufp && (*bufp != '"')) {
slouken@617
    90
                ++bufp;
slouken@617
    91
            }
slouken@617
    92
        } else {
slouken@777
    93
            if (*bufp) {
slouken@777
    94
                if (argv) {
slouken@617
    95
                    argv[argc] = bufp;
slouken@617
    96
                }
slouken@617
    97
                ++argc;
slouken@617
    98
            }
slouken@617
    99
            /* Skip over word */
slouken@777
   100
            while (*bufp && ! isspace(*bufp)) {
slouken@617
   101
                ++bufp;
slouken@617
   102
            }
slouken@617
   103
        }
slouken@777
   104
        if (*bufp) {
slouken@777
   105
            if (argv) {
slouken@617
   106
                *bufp = '\0';
slouken@617
   107
            }
slouken@617
   108
            ++bufp;
slouken@617
   109
        }
slouken@617
   110
    }
slouken@777
   111
    if (argv) {
slouken@617
   112
        argv[argc] = NULL;
slouken@617
   113
    }
slouken@617
   114
    return(argc);
slouken@0
   115
}
slouken@0
   116
slouken@0
   117
static char **parse_args(char *command, char *last_arg)
slouken@0
   118
{
slouken@617
   119
    int argc;
slouken@617
   120
    char **argv;
slouken@0
   121
slouken@617
   122
    /* Parse the command line */
slouken@617
   123
    argc = ParseCommandLine(command, NULL);
slouken@777
   124
    if (last_arg) {
slouken@617
   125
        ++argc;
slouken@617
   126
    }
slouken@617
   127
    argv = (char **)SDL_malloc((argc+1)*(sizeof *argv));
slouken@777
   128
    if (argv == NULL) {
slouken@617
   129
        return(NULL);
slouken@617
   130
    }
slouken@617
   131
    argc = ParseCommandLine(command, argv);
slouken@0
   132
slouken@617
   133
    /* Add last command line argument */
slouken@777
   134
    if (last_arg) {
slouken@617
   135
        argv[argc++] = last_arg;
slouken@617
   136
    }
slouken@617
   137
    argv[argc] = NULL;
slouken@0
   138
slouken@617
   139
    /* We're ready! */
slouken@617
   140
    return(argv);
slouken@0
   141
}
slouken@0
   142
slouken@0
   143
/* Start playback of a given music stream */
slouken@777
   144
static int MusicCMD_Play(void *context)
slouken@0
   145
{
slouken@777
   146
    MusicCMD *music = (MusicCMD *)context;
slouken@777
   147
slouken@777
   148
    if (!music_cmd) {
slouken@777
   149
        Mix_SetError("You must call Mix_SetMusicCMD() first");
slouken@777
   150
        return -1;
slouken@777
   151
    }
slouken@777
   152
slouken@467
   153
#ifdef HAVE_FORK
slouken@617
   154
    music->pid = fork();
slouken@467
   155
#else
slouken@617
   156
    music->pid = vfork();
slouken@467
   157
#endif
slouken@617
   158
    switch(music->pid) {
slouken@777
   159
    /* Failed fork() system call */
slouken@777
   160
    case -1:
slouken@617
   161
        Mix_SetError("fork() failed");
slouken@777
   162
        return -1;
slouken@0
   163
slouken@777
   164
    /* Child process - executes here */
slouken@777
   165
    case 0: {
slouken@777
   166
        char *command;
slouken@777
   167
        char **argv;
slouken@0
   168
slouken@777
   169
        /* Unblock signals in case we're called from a thread */
slouken@777
   170
        {
slouken@777
   171
            sigset_t mask;
slouken@777
   172
            sigemptyset(&mask);
slouken@777
   173
            sigprocmask(SIG_SETMASK, &mask, NULL);
slouken@777
   174
        }
slouken@447
   175
slouken@777
   176
        /* Execute the command */
slouken@777
   177
        argv = parse_args(music_cmd, music->file);
slouken@777
   178
        if (argv != NULL) {
slouken@777
   179
            execvp(argv[0], argv);
slouken@763
   180
slouken@777
   181
            /* exec() failed */
slouken@777
   182
            perror(argv[0]);
slouken@617
   183
        }
slouken@777
   184
        _exit(-1);
slouken@777
   185
    }
slouken@777
   186
    break;
slouken@0
   187
slouken@777
   188
    /* Parent process - executes here */
slouken@777
   189
    default:
slouken@617
   190
        break;
slouken@617
   191
    }
slouken@777
   192
    return 0;
slouken@777
   193
}
slouken@777
   194
slouken@777
   195
/* Return non-zero if a stream is currently playing */
slouken@777
   196
static SDL_bool MusicCMD_IsPlaying(void *context)
slouken@777
   197
{
slouken@777
   198
    MusicCMD *music = (MusicCMD *)context;
slouken@777
   199
    int status;
slouken@777
   200
slouken@777
   201
    if (music->pid > 0) {
slouken@777
   202
        waitpid(music->pid, &status, WNOHANG);
slouken@777
   203
        if (kill(music->pid, 0) == 0) {
slouken@777
   204
            return SDL_TRUE;
slouken@777
   205
        }
slouken@777
   206
    }
slouken@777
   207
    return SDL_FALSE;
slouken@777
   208
}
slouken@777
   209
slouken@777
   210
/* Pause playback of a given music stream */
slouken@777
   211
static void MusicCMD_Pause(void *context)
slouken@777
   212
{
slouken@777
   213
    MusicCMD *music = (MusicCMD *)context;
slouken@777
   214
    if (music->pid > 0) {
slouken@777
   215
        kill(music->pid, SIGSTOP);
slouken@777
   216
    }
slouken@777
   217
}
slouken@777
   218
slouken@777
   219
/* Resume playback of a given music stream */
slouken@777
   220
static void MusicCMD_Resume(void *context)
slouken@777
   221
{
slouken@777
   222
    MusicCMD *music = (MusicCMD *)context;
slouken@777
   223
    if (music->pid > 0) {
slouken@777
   224
        kill(music->pid, SIGCONT);
slouken@777
   225
    }
slouken@0
   226
}
slouken@0
   227
slouken@0
   228
/* Stop playback of a stream previously started with MusicCMD_Start() */
slouken@777
   229
static void MusicCMD_Stop(void *context)
slouken@0
   230
{
slouken@777
   231
    MusicCMD *music = (MusicCMD *)context;
slouken@617
   232
    int status;
slouken@0
   233
slouken@777
   234
    if (music->pid > 0) {
slouken@777
   235
        while (kill(music->pid, 0) == 0) {
slouken@617
   236
            kill(music->pid, SIGTERM);
slouken@617
   237
            sleep(1);
slouken@617
   238
            waitpid(music->pid, &status, WNOHANG);
slouken@617
   239
        }
slouken@617
   240
        music->pid = 0;
slouken@617
   241
    }
slouken@0
   242
}
slouken@0
   243
slouken@777
   244
/* Close the given music stream */
slouken@777
   245
int MusicCMD_Delete(void *context)
slouken@0
   246
{
slouken@777
   247
    MusicCMD *music = (MusicCMD *)context;
slouken@777
   248
    SDL_free(music->file);
slouken@777
   249
    SDL_free(music);
slouken@777
   250
    return 0;
slouken@0
   251
}
slouken@0
   252
slouken@777
   253
Mix_MusicInterface Mix_MusicInterface_CMD =
slouken@0
   254
{
slouken@777
   255
    "CMD",
slouken@777
   256
    MIX_MUSIC_CMD,
slouken@777
   257
    MUS_CMD,
slouken@777
   258
    SDL_FALSE,
slouken@777
   259
    SDL_FALSE,
slouken@0
   260
slouken@777
   261
    NULL,   /* Load */
slouken@777
   262
    NULL,   /* Open */
slouken@777
   263
    NULL,   /* CreateFromRW */
slouken@777
   264
    MusicCMD_CreateFromFile,
slouken@777
   265
    NULL,   /* SetVolume */
slouken@777
   266
    MusicCMD_Play,
slouken@777
   267
    MusicCMD_IsPlaying,
slouken@777
   268
    NULL,   /* GetAudio */
slouken@777
   269
    NULL,   /* Seek */
slouken@777
   270
    MusicCMD_Pause,
slouken@777
   271
    MusicCMD_Resume,
slouken@777
   272
    MusicCMD_Stop,
slouken@777
   273
    MusicCMD_Delete,
slouken@777
   274
    NULL,   /* Close */
slouken@777
   275
    NULL,   /* Unload */
slouken@777
   276
};
slouken@0
   277
slouken@777
   278
#endif /* MUSIC_CMD */
slouken@0
   279
slouken@777
   280
/* vi: set ts=4 sw=4 expandtab: */