1 /*
2 SDL_mixer: An audio mixer library based on the SDL library
3 Copyright (C) 1997-2009 Sam Lantinga
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 Sam Lantinga
20 slouken@libsdl.org
21 */
22 #include "SDL_config.h"
23
24 /* This file supports an external command for playing music */
25
26 #ifdef CMD_MUSIC
27
28 #include <sys/types.h>
29 #include <sys/wait.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <signal.h>
35 #include <ctype.h>
36
37 #include "SDL_mixer.h"
38 #include "music_cmd.h"
39
40 /* Unimplemented */
MusicCMD_SetVolume(int volume)41 void MusicCMD_SetVolume(int volume)
42 {
43 Mix_SetError("No way to modify external player volume");
44 }
45
46 /* Load a music stream from the given file */
MusicCMD_LoadSong(const char * cmd,const char * file)47 MusicCMD *MusicCMD_LoadSong(const char *cmd, const char *file)
48 {
49 MusicCMD *music;
50
51 /* Allocate and fill the music structure */
52 music = (MusicCMD *)malloc(sizeof *music);
53 if ( music == NULL ) {
54 Mix_SetError("Out of memory");
55 return(NULL);
56 }
57 strncpy(music->file, file, (sizeof music->file)-1);
58 music->file[(sizeof music->file)-1] = '\0';
59 strncpy(music->cmd, cmd, (sizeof music->cmd)-1);
60 music->cmd[(sizeof music->cmd)-1] = '\0';
61 music->pid = 0;
62
63 /* We're done */
64 return(music);
65 }
66
67 /* Parse a command line buffer into arguments */
ParseCommandLine(char * cmdline,char ** argv)68 static int ParseCommandLine(char *cmdline, char **argv)
69 {
70 char *bufp;
71 int argc;
72
73 argc = 0;
74 for ( bufp = cmdline; *bufp; ) {
75 /* Skip leading whitespace */
76 while ( isspace(*bufp) ) {
77 ++bufp;
78 }
79 /* Skip over argument */
80 if ( *bufp == '"' ) {
81 ++bufp;
82 if ( *bufp ) {
83 if ( argv ) {
84 argv[argc] = bufp;
85 }
86 ++argc;
87 }
88 /* Skip over word */
89 while ( *bufp && (*bufp != '"') ) {
90 ++bufp;
91 }
92 } else {
93 if ( *bufp ) {
94 if ( argv ) {
95 argv[argc] = bufp;
96 }
97 ++argc;
98 }
99 /* Skip over word */
100 while ( *bufp && ! isspace(*bufp) ) {
101 ++bufp;
102 }
103 }
104 if ( *bufp ) {
105 if ( argv ) {
106 *bufp = '\0';
107 }
108 ++bufp;
109 }
110 }
111 if ( argv ) {
112 argv[argc] = NULL;
113 }
114 return(argc);
115 }
116
parse_args(char * command,char * last_arg)117 static char **parse_args(char *command, char *last_arg)
118 {
119 int argc;
120 char **argv;
121
122 /* Parse the command line */
123 argc = ParseCommandLine(command, NULL);
124 if ( last_arg ) {
125 ++argc;
126 }
127 argv = (char **)malloc((argc+1)*(sizeof *argv));
128 if ( argv == NULL ) {
129 return(NULL);
130 }
131 argc = ParseCommandLine(command, argv);
132
133 /* Add last command line argument */
134 if ( last_arg ) {
135 argv[argc++] = last_arg;
136 }
137 argv[argc] = NULL;
138
139 /* We're ready! */
140 return(argv);
141 }
142
143 /* Start playback of a given music stream */
MusicCMD_Start(MusicCMD * music)144 void MusicCMD_Start(MusicCMD *music)
145 {
146 #ifdef HAVE_FORK
147 music->pid = fork();
148 #else
149 music->pid = vfork();
150 #endif
151 switch(music->pid) {
152 /* Failed fork() system call */
153 case -1:
154 Mix_SetError("fork() failed");
155 return;
156
157 /* Child process - executes here */
158 case 0: {
159 char command[PATH_MAX];
160 char **argv;
161
162 /* Unblock signals in case we're called from a thread */
163 {
164 sigset_t mask;
165 sigemptyset(&mask);
166 sigprocmask(SIG_SETMASK, &mask, NULL);
167 }
168
169 /* Execute the command */
170 strcpy(command, music->cmd);
171 argv = parse_args(command, music->file);
172 if ( argv != NULL ) {
173 execvp(argv[0], argv);
174 }
175
176 /* exec() failed */
177 perror(argv[0]);
178 _exit(-1);
179 }
180 break;
181
182 /* Parent process - executes here */
183 default:
184 break;
185 }
186 return;
187 }
188
189 /* Stop playback of a stream previously started with MusicCMD_Start() */
MusicCMD_Stop(MusicCMD * music)190 void MusicCMD_Stop(MusicCMD *music)
191 {
192 int status;
193
194 if ( music->pid > 0 ) {
195 while ( kill(music->pid, 0) == 0 ) {
196 kill(music->pid, SIGTERM);
197 sleep(1);
198 waitpid(music->pid, &status, WNOHANG);
199 }
200 music->pid = 0;
201 }
202 }
203
204 /* Pause playback of a given music stream */
MusicCMD_Pause(MusicCMD * music)205 void MusicCMD_Pause(MusicCMD *music)
206 {
207 if ( music->pid > 0 ) {
208 kill(music->pid, SIGSTOP);
209 }
210 }
211
212 /* Resume playback of a given music stream */
MusicCMD_Resume(MusicCMD * music)213 void MusicCMD_Resume(MusicCMD *music)
214 {
215 if ( music->pid > 0 ) {
216 kill(music->pid, SIGCONT);
217 }
218 }
219
220 /* Close the given music stream */
MusicCMD_FreeSong(MusicCMD * music)221 void MusicCMD_FreeSong(MusicCMD *music)
222 {
223 free(music);
224 }
225
226 /* Return non-zero if a stream is currently playing */
MusicCMD_Active(MusicCMD * music)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 */
243