1 /*
2 SDL_mixer: An audio mixer library based on the SDL library
3 Copyright (C) 1997-2012 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 */
MusicCMD_SetVolume(int volume)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 */
MusicCMD_LoadSong(const char * cmd,const char * 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 strncpy(music->file, file, (sizeof music->file)-1);
57 music->file[(sizeof music->file)-1] = '\0';
58 strncpy(music->cmd, cmd, (sizeof music->cmd)-1);
59 music->cmd[(sizeof music->cmd)-1] = '\0';
60 music->pid = 0;
61
62 /* We're done */
63 return(music);
64 }
65
66 /* Parse a command line buffer into arguments */
ParseCommandLine(char * cmdline,char ** argv)67 static int ParseCommandLine(char *cmdline, char **argv)
68 {
69 char *bufp;
70 int argc;
71
72 argc = 0;
73 for ( bufp = cmdline; *bufp; ) {
74 /* Skip leading whitespace */
75 while ( isspace(*bufp) ) {
76 ++bufp;
77 }
78 /* Skip over argument */
79 if ( *bufp == '"' ) {
80 ++bufp;
81 if ( *bufp ) {
82 if ( argv ) {
83 argv[argc] = bufp;
84 }
85 ++argc;
86 }
87 /* Skip over word */
88 while ( *bufp && (*bufp != '"') ) {
89 ++bufp;
90 }
91 } else {
92 if ( *bufp ) {
93 if ( argv ) {
94 argv[argc] = bufp;
95 }
96 ++argc;
97 }
98 /* Skip over word */
99 while ( *bufp && ! isspace(*bufp) ) {
100 ++bufp;
101 }
102 }
103 if ( *bufp ) {
104 if ( argv ) {
105 *bufp = '\0';
106 }
107 ++bufp;
108 }
109 }
110 if ( argv ) {
111 argv[argc] = NULL;
112 }
113 return(argc);
114 }
115
parse_args(char * command,char * last_arg)116 static char **parse_args(char *command, char *last_arg)
117 {
118 int argc;
119 char **argv;
120
121 /* Parse the command line */
122 argc = ParseCommandLine(command, NULL);
123 if ( last_arg ) {
124 ++argc;
125 }
126 argv = (char **)SDL_malloc((argc+1)*(sizeof *argv));
127 if ( argv == NULL ) {
128 return(NULL);
129 }
130 argc = ParseCommandLine(command, argv);
131
132 /* Add last command line argument */
133 if ( last_arg ) {
134 argv[argc++] = last_arg;
135 }
136 argv[argc] = NULL;
137
138 /* We're ready! */
139 return(argv);
140 }
141
142 /* Start playback of a given music stream */
MusicCMD_Start(MusicCMD * music)143 void MusicCMD_Start(MusicCMD *music)
144 {
145 #ifdef HAVE_FORK
146 music->pid = fork();
147 #else
148 music->pid = vfork();
149 #endif
150 switch(music->pid) {
151 /* Failed fork() system call */
152 case -1:
153 Mix_SetError("fork() failed");
154 return;
155
156 /* Child process - executes here */
157 case 0: {
158 char command[PATH_MAX];
159 char **argv;
160
161 /* Unblock signals in case we're called from a thread */
162 {
163 sigset_t mask;
164 sigemptyset(&mask);
165 sigprocmask(SIG_SETMASK, &mask, NULL);
166 }
167
168 /* Execute the command */
169 strcpy(command, music->cmd);
170 argv = parse_args(command, music->file);
171 if ( argv != NULL ) {
172 execvp(argv[0], argv);
173 }
174
175 /* exec() failed */
176 perror(argv[0]);
177 _exit(-1);
178 }
179 break;
180
181 /* Parent process - executes here */
182 default:
183 break;
184 }
185 return;
186 }
187
188 /* Stop playback of a stream previously started with MusicCMD_Start() */
MusicCMD_Stop(MusicCMD * music)189 void MusicCMD_Stop(MusicCMD *music)
190 {
191 int status;
192
193 if ( music->pid > 0 ) {
194 while ( kill(music->pid, 0) == 0 ) {
195 kill(music->pid, SIGTERM);
196 sleep(1);
197 waitpid(music->pid, &status, WNOHANG);
198 }
199 music->pid = 0;
200 }
201 }
202
203 /* Pause playback of a given music stream */
MusicCMD_Pause(MusicCMD * music)204 void MusicCMD_Pause(MusicCMD *music)
205 {
206 if ( music->pid > 0 ) {
207 kill(music->pid, SIGSTOP);
208 }
209 }
210
211 /* Resume playback of a given music stream */
MusicCMD_Resume(MusicCMD * music)212 void MusicCMD_Resume(MusicCMD *music)
213 {
214 if ( music->pid > 0 ) {
215 kill(music->pid, SIGCONT);
216 }
217 }
218
219 /* Close the given music stream */
MusicCMD_FreeSong(MusicCMD * music)220 void MusicCMD_FreeSong(MusicCMD *music)
221 {
222 SDL_free(music);
223 }
224
225 /* Return non-zero if a stream is currently playing */
MusicCMD_Active(MusicCMD * music)226 int MusicCMD_Active(MusicCMD *music)
227 {
228 int status;
229 int active;
230
231 active = 0;
232 if ( music->pid > 0 ) {
233 waitpid(music->pid, &status, WNOHANG);
234 if ( kill(music->pid, 0) == 0 ) {
235 active = 1;
236 }
237 }
238 return(active);
239 }
240
241 #endif /* CMD_MUSIC */
242