music_cmd.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 30 Apr 2006 20:27:33 +0000
changeset 294 58eb208474e0
parent 241 503416fca921
child 386 695494546b3c
permissions -rw-r--r--
Removed automake dependency, to allow Universal binaries on Mac OS X
     1 /*
     2     SDL_mixer:  An audio mixer library based on the SDL library
     3     Copyright (C) 1997-2004 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 #if defined(unix) || defined(__MACOSX__) /* This is a UNIX-specific hack */
    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 */
    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 */
    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 */
    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 
   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 */
   144 void MusicCMD_Start(MusicCMD *music)
   145 {
   146 	music->pid = fork();
   147 	switch(music->pid) {
   148 	    /* Failed fork() system call */
   149 	    case -1:
   150 		Mix_SetError("fork() failed");
   151 		return;
   152 
   153 	    /* Child process - executes here */
   154 	    case 0: {
   155 		    char command[PATH_MAX];
   156 		    char **argv;
   157 
   158 		    /* Execute the command */
   159 		    strcpy(command, music->cmd);
   160 		    argv = parse_args(command, music->file);
   161 		    if ( argv != NULL ) {
   162 			execvp(argv[0], argv);
   163 		    }
   164 
   165 		    /* exec() failed */
   166 		    perror(argv[0]);
   167 		    _exit(-1);
   168 		}
   169 		break;
   170 
   171 	    /* Parent process - executes here */
   172 	    default:
   173 		break;
   174 	}
   175 	return;
   176 }
   177 
   178 /* Stop playback of a stream previously started with MusicCMD_Start() */
   179 void MusicCMD_Stop(MusicCMD *music)
   180 {
   181 	int status;
   182 
   183 	if ( music->pid > 0 ) {
   184 		while ( kill(music->pid, 0) == 0 ) {
   185 			kill(music->pid, SIGTERM);
   186 			sleep(1);
   187 			waitpid(music->pid, &status, WNOHANG);
   188 		}
   189 		music->pid = 0;
   190 	}
   191 }
   192 
   193 /* Pause playback of a given music stream */
   194 void MusicCMD_Pause(MusicCMD *music)
   195 {
   196 	if ( music->pid > 0 ) {
   197 		kill(music->pid, SIGSTOP);
   198 	}
   199 }
   200 
   201 /* Resume playback of a given music stream */
   202 void MusicCMD_Resume(MusicCMD *music)
   203 {
   204 	if ( music->pid > 0 ) {
   205 		kill(music->pid, SIGCONT);
   206 	}
   207 }
   208 
   209 /* Close the given music stream */
   210 void MusicCMD_FreeSong(MusicCMD *music)
   211 {
   212 	free(music);
   213 }
   214 
   215 /* Return non-zero if a stream is currently playing */
   216 int MusicCMD_Active(MusicCMD *music)
   217 {
   218 	int status;
   219 	int active;
   220 
   221 	active = 0;
   222 	if ( music->pid > 0 ) {
   223 		waitpid(music->pid, &status, WNOHANG);
   224 		if ( kill(music->pid, 0) == 0 ) {
   225 			active = 1;
   226 		}
   227 	}
   228 	return(active);
   229 }
   230 
   231 #endif /* unix */