music_cmd.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 02 Jan 2016 22:12:31 -0800
changeset 712 7e59d684b070
parent 711 f40c5ac95b12
child 725 bdf7b8d20566
permissions -rw-r--r--
Updated to version 2.0.1
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 /* This file supports an external command for playing music */
    24 
    25 #ifdef CMD_MUSIC
    26 
    27 #include <sys/types.h>
    28 #include <sys/wait.h>
    29 #include <stdio.h>
    30 #include <stdlib.h>
    31 #include <unistd.h>
    32 #include <string.h>
    33 #include <signal.h>
    34 #include <ctype.h>
    35 
    36 #include "SDL_mixer.h"
    37 #include "music_cmd.h"
    38 
    39 /* Unimplemented */
    40 void MusicCMD_SetVolume(int volume)
    41 {
    42     Mix_SetError("No way to modify external player volume");
    43 }
    44 
    45 /* Load a music stream from the given file */
    46 MusicCMD *MusicCMD_LoadSong(const char *cmd, const char *file)
    47 {
    48     MusicCMD *music;
    49 
    50     /* Allocate and fill the music structure */
    51     music = (MusicCMD *)SDL_malloc(sizeof *music);
    52     if ( music == NULL ) {
    53         Mix_SetError("Out of memory");
    54         return(NULL);
    55     }
    56     music->file = SDL_strdup(file);
    57     music->cmd = SDL_strdup(cmd);
    58     music->pid = 0;
    59 
    60     /* We're done */
    61     return(music);
    62 }
    63 
    64 /* Parse a command line buffer into arguments */
    65 static int ParseCommandLine(char *cmdline, char **argv)
    66 {
    67     char *bufp;
    68     int argc;
    69 
    70     argc = 0;
    71     for ( bufp = cmdline; *bufp; ) {
    72         /* Skip leading whitespace */
    73         while ( isspace(*bufp) ) {
    74             ++bufp;
    75         }
    76         /* Skip over argument */
    77         if ( *bufp == '"' ) {
    78             ++bufp;
    79             if ( *bufp ) {
    80                 if ( argv ) {
    81                     argv[argc] = bufp;
    82                 }
    83                 ++argc;
    84             }
    85             /* Skip over word */
    86             while ( *bufp && (*bufp != '"') ) {
    87                 ++bufp;
    88             }
    89         } else {
    90             if ( *bufp ) {
    91                 if ( argv ) {
    92                     argv[argc] = bufp;
    93                 }
    94                 ++argc;
    95             }
    96             /* Skip over word */
    97             while ( *bufp && ! isspace(*bufp) ) {
    98                 ++bufp;
    99             }
   100         }
   101         if ( *bufp ) {
   102             if ( argv ) {
   103                 *bufp = '\0';
   104             }
   105             ++bufp;
   106         }
   107     }
   108     if ( argv ) {
   109         argv[argc] = NULL;
   110     }
   111     return(argc);
   112 }
   113 
   114 static char **parse_args(char *command, char *last_arg)
   115 {
   116     int argc;
   117     char **argv;
   118 
   119     /* Parse the command line */
   120     argc = ParseCommandLine(command, NULL);
   121     if ( last_arg ) {
   122         ++argc;
   123     }
   124     argv = (char **)SDL_malloc((argc+1)*(sizeof *argv));
   125     if ( argv == NULL ) {
   126         return(NULL);
   127     }
   128     argc = ParseCommandLine(command, argv);
   129 
   130     /* Add last command line argument */
   131     if ( last_arg ) {
   132         argv[argc++] = last_arg;
   133     }
   134     argv[argc] = NULL;
   135 
   136     /* We're ready! */
   137     return(argv);
   138 }
   139 
   140 /* Start playback of a given music stream */
   141 void MusicCMD_Start(MusicCMD *music)
   142 {
   143 #ifdef HAVE_FORK
   144     music->pid = fork();
   145 #else
   146     music->pid = vfork();
   147 #endif
   148     switch(music->pid) {
   149         /* Failed fork() system call */
   150         case -1:
   151         Mix_SetError("fork() failed");
   152         return;
   153 
   154         /* Child process - executes here */
   155         case 0: {
   156             char *command;
   157             char **argv;
   158 
   159             /* Unblock signals in case we're called from a thread */
   160             {
   161             sigset_t mask;
   162             sigemptyset(&mask);
   163             sigprocmask(SIG_SETMASK, &mask, NULL);
   164             }
   165 
   166             /* Execute the command */
   167             command = SDL_strdup(music->cmd);
   168             argv = parse_args(command, music->file);
   169             if ( argv != NULL ) {
   170             execvp(argv[0], argv);
   171             }
   172             SDL_free(command);
   173 
   174             /* exec() failed */
   175             perror(argv[0]);
   176             _exit(-1);
   177         }
   178         break;
   179 
   180         /* Parent process - executes here */
   181         default:
   182         break;
   183     }
   184     return;
   185 }
   186 
   187 /* Stop playback of a stream previously started with MusicCMD_Start() */
   188 void MusicCMD_Stop(MusicCMD *music)
   189 {
   190     int status;
   191 
   192     if ( music->pid > 0 ) {
   193         while ( kill(music->pid, 0) == 0 ) {
   194             kill(music->pid, SIGTERM);
   195             sleep(1);
   196             waitpid(music->pid, &status, WNOHANG);
   197         }
   198         music->pid = 0;
   199     }
   200 }
   201 
   202 /* Pause playback of a given music stream */
   203 void MusicCMD_Pause(MusicCMD *music)
   204 {
   205     if ( music->pid > 0 ) {
   206         kill(music->pid, SIGSTOP);
   207     }
   208 }
   209 
   210 /* Resume playback of a given music stream */
   211 void MusicCMD_Resume(MusicCMD *music)
   212 {
   213     if ( music->pid > 0 ) {
   214         kill(music->pid, SIGCONT);
   215     }
   216 }
   217 
   218 /* Close the given music stream */
   219 void MusicCMD_FreeSong(MusicCMD *music)
   220 {
   221     SDL_free(music->file);
   222     SDL_free(music->cmd);
   223     SDL_free(music);
   224 }
   225 
   226 /* Return non-zero if a stream is currently playing */
   227 int MusicCMD_Active(MusicCMD *music)
   228 {
   229     int status;
   230     int active;
   231 
   232     active = 0;
   233     if ( music->pid > 0 ) {
   234         waitpid(music->pid, &status, WNOHANG);
   235         if ( kill(music->pid, 0) == 0 ) {
   236             active = 1;
   237         }
   238     }
   239     return(active);
   240 }
   241 
   242 #endif /* CMD_MUSIC */