music_cmd.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 17 Oct 2017 16:55:58 -0700
changeset 781 90487b4b7ade
parent 779 a2b494c054d5
child 797 b4b6adff699a
permissions -rw-r--r--
Only use the music_cmd interface if we've set a command handler
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@781
    45
    char *cmd;
slouken@777
    46
    pid_t pid;
slouken@777
    47
} MusicCMD;
slouken@777
    48
slouken@0
    49
slouken@0
    50
/* Load a music stream from the given file */
slouken@777
    51
static void *MusicCMD_CreateFromFile(const char *file)
slouken@0
    52
{
slouken@617
    53
    MusicCMD *music;
slouken@0
    54
slouken@781
    55
    if (!music_cmd) {
slouken@781
    56
        Mix_SetError("You must call Mix_SetMusicCMD() first");
slouken@781
    57
        return NULL;
slouken@781
    58
    }
slouken@781
    59
slouken@617
    60
    /* Allocate and fill the music structure */
slouken@777
    61
    music = (MusicCMD *)SDL_calloc(1, sizeof *music);
slouken@777
    62
    if (music == NULL) {
slouken@617
    63
        Mix_SetError("Out of memory");
slouken@777
    64
        return NULL;
slouken@617
    65
    }
slouken@617
    66
    music->file = SDL_strdup(file);
slouken@781
    67
    music->cmd = SDL_strdup(music_cmd);
slouken@617
    68
    music->pid = 0;
slouken@0
    69
slouken@617
    70
    /* We're done */
slouken@777
    71
    return music;
slouken@0
    72
}
slouken@0
    73
slouken@0
    74
/* Parse a command line buffer into arguments */
slouken@0
    75
static int ParseCommandLine(char *cmdline, char **argv)
slouken@0
    76
{
slouken@617
    77
    char *bufp;
slouken@617
    78
    int argc;
slouken@0
    79
slouken@617
    80
    argc = 0;
slouken@777
    81
    for (bufp = cmdline; *bufp;) {
slouken@617
    82
        /* Skip leading whitespace */
slouken@777
    83
        while (isspace(*bufp)) {
slouken@617
    84
            ++bufp;
slouken@617
    85
        }
slouken@617
    86
        /* Skip over argument */
slouken@777
    87
        if (*bufp == '"') {
slouken@617
    88
            ++bufp;
slouken@777
    89
            if (*bufp) {
slouken@777
    90
                if (argv) {
slouken@617
    91
                    argv[argc] = bufp;
slouken@617
    92
                }
slouken@617
    93
                ++argc;
slouken@617
    94
            }
slouken@617
    95
            /* Skip over word */
slouken@777
    96
            while (*bufp && (*bufp != '"')) {
slouken@617
    97
                ++bufp;
slouken@617
    98
            }
slouken@617
    99
        } else {
slouken@777
   100
            if (*bufp) {
slouken@777
   101
                if (argv) {
slouken@617
   102
                    argv[argc] = bufp;
slouken@617
   103
                }
slouken@617
   104
                ++argc;
slouken@617
   105
            }
slouken@617
   106
            /* Skip over word */
slouken@777
   107
            while (*bufp && ! isspace(*bufp)) {
slouken@617
   108
                ++bufp;
slouken@617
   109
            }
slouken@617
   110
        }
slouken@777
   111
        if (*bufp) {
slouken@777
   112
            if (argv) {
slouken@617
   113
                *bufp = '\0';
slouken@617
   114
            }
slouken@617
   115
            ++bufp;
slouken@617
   116
        }
slouken@617
   117
    }
slouken@777
   118
    if (argv) {
slouken@617
   119
        argv[argc] = NULL;
slouken@617
   120
    }
slouken@617
   121
    return(argc);
slouken@0
   122
}
slouken@0
   123
slouken@0
   124
static char **parse_args(char *command, char *last_arg)
slouken@0
   125
{
slouken@617
   126
    int argc;
slouken@617
   127
    char **argv;
slouken@0
   128
slouken@617
   129
    /* Parse the command line */
slouken@617
   130
    argc = ParseCommandLine(command, NULL);
slouken@777
   131
    if (last_arg) {
slouken@617
   132
        ++argc;
slouken@617
   133
    }
slouken@617
   134
    argv = (char **)SDL_malloc((argc+1)*(sizeof *argv));
slouken@777
   135
    if (argv == NULL) {
slouken@617
   136
        return(NULL);
slouken@617
   137
    }
slouken@617
   138
    argc = ParseCommandLine(command, argv);
slouken@0
   139
slouken@617
   140
    /* Add last command line argument */
slouken@777
   141
    if (last_arg) {
slouken@617
   142
        argv[argc++] = last_arg;
slouken@617
   143
    }
slouken@617
   144
    argv[argc] = NULL;
slouken@0
   145
slouken@617
   146
    /* We're ready! */
slouken@617
   147
    return(argv);
slouken@0
   148
}
slouken@0
   149
slouken@0
   150
/* Start playback of a given music stream */
slouken@777
   151
static int MusicCMD_Play(void *context)
slouken@0
   152
{
slouken@777
   153
    MusicCMD *music = (MusicCMD *)context;
slouken@777
   154
slouken@467
   155
#ifdef HAVE_FORK
slouken@617
   156
    music->pid = fork();
slouken@467
   157
#else
slouken@617
   158
    music->pid = vfork();
slouken@467
   159
#endif
slouken@617
   160
    switch(music->pid) {
slouken@777
   161
    /* Failed fork() system call */
slouken@777
   162
    case -1:
slouken@617
   163
        Mix_SetError("fork() failed");
slouken@777
   164
        return -1;
slouken@0
   165
slouken@777
   166
    /* Child process - executes here */
slouken@777
   167
    case 0: {
slouken@777
   168
        char **argv;
slouken@0
   169
slouken@777
   170
        /* Unblock signals in case we're called from a thread */
slouken@777
   171
        {
slouken@777
   172
            sigset_t mask;
slouken@777
   173
            sigemptyset(&mask);
slouken@777
   174
            sigprocmask(SIG_SETMASK, &mask, NULL);
slouken@777
   175
        }
slouken@447
   176
slouken@777
   177
        /* Execute the command */
slouken@781
   178
        argv = parse_args(music->cmd, music->file);
slouken@777
   179
        if (argv != NULL) {
slouken@777
   180
            execvp(argv[0], argv);
slouken@763
   181
slouken@777
   182
            /* exec() failed */
slouken@777
   183
            perror(argv[0]);
slouken@617
   184
        }
slouken@777
   185
        _exit(-1);
slouken@777
   186
    }
slouken@777
   187
    break;
slouken@0
   188
slouken@777
   189
    /* Parent process - executes here */
slouken@777
   190
    default:
slouken@617
   191
        break;
slouken@617
   192
    }
slouken@777
   193
    return 0;
slouken@777
   194
}
slouken@777
   195
slouken@777
   196
/* Return non-zero if a stream is currently playing */
slouken@777
   197
static SDL_bool MusicCMD_IsPlaying(void *context)
slouken@777
   198
{
slouken@777
   199
    MusicCMD *music = (MusicCMD *)context;
slouken@777
   200
    int status;
slouken@777
   201
slouken@777
   202
    if (music->pid > 0) {
slouken@777
   203
        waitpid(music->pid, &status, WNOHANG);
slouken@777
   204
        if (kill(music->pid, 0) == 0) {
slouken@777
   205
            return SDL_TRUE;
slouken@777
   206
        }
slouken@777
   207
    }
slouken@777
   208
    return SDL_FALSE;
slouken@777
   209
}
slouken@777
   210
slouken@777
   211
/* Pause playback of a given music stream */
slouken@777
   212
static void MusicCMD_Pause(void *context)
slouken@777
   213
{
slouken@777
   214
    MusicCMD *music = (MusicCMD *)context;
slouken@777
   215
    if (music->pid > 0) {
slouken@777
   216
        kill(music->pid, SIGSTOP);
slouken@777
   217
    }
slouken@777
   218
}
slouken@777
   219
slouken@777
   220
/* Resume playback of a given music stream */
slouken@777
   221
static void MusicCMD_Resume(void *context)
slouken@777
   222
{
slouken@777
   223
    MusicCMD *music = (MusicCMD *)context;
slouken@777
   224
    if (music->pid > 0) {
slouken@777
   225
        kill(music->pid, SIGCONT);
slouken@777
   226
    }
slouken@0
   227
}
slouken@0
   228
slouken@0
   229
/* Stop playback of a stream previously started with MusicCMD_Start() */
slouken@777
   230
static void MusicCMD_Stop(void *context)
slouken@0
   231
{
slouken@777
   232
    MusicCMD *music = (MusicCMD *)context;
slouken@617
   233
    int status;
slouken@0
   234
slouken@777
   235
    if (music->pid > 0) {
slouken@777
   236
        while (kill(music->pid, 0) == 0) {
slouken@617
   237
            kill(music->pid, SIGTERM);
slouken@617
   238
            sleep(1);
slouken@617
   239
            waitpid(music->pid, &status, WNOHANG);
slouken@617
   240
        }
slouken@617
   241
        music->pid = 0;
slouken@617
   242
    }
slouken@0
   243
}
slouken@0
   244
slouken@777
   245
/* Close the given music stream */
slouken@779
   246
void MusicCMD_Delete(void *context)
slouken@0
   247
{
slouken@777
   248
    MusicCMD *music = (MusicCMD *)context;
slouken@777
   249
    SDL_free(music->file);
slouken@777
   250
    SDL_free(music);
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: */