music_cmd.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 19 Oct 2009 01:03:40 +0000
changeset 462 028b317d01d2
parent 447 50248d0ec654
child 467 4b52401dda92
permissions -rw-r--r--
Added the AudioUnit framework for the native MIDI code.
     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 #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 		    /* Unblock signals in case we're called from a thread */
   159 		    {
   160 			sigset_t mask;
   161 			sigemptyset(&mask);
   162 			sigprocmask(SIG_SETMASK, &mask, NULL);
   163 		    }
   164 
   165 		    /* Execute the command */
   166 		    strcpy(command, music->cmd);
   167 		    argv = parse_args(command, music->file);
   168 		    if ( argv != NULL ) {
   169 			execvp(argv[0], argv);
   170 		    }
   171 
   172 		    /* exec() failed */
   173 		    perror(argv[0]);
   174 		    _exit(-1);
   175 		}
   176 		break;
   177 
   178 	    /* Parent process - executes here */
   179 	    default:
   180 		break;
   181 	}
   182 	return;
   183 }
   184 
   185 /* Stop playback of a stream previously started with MusicCMD_Start() */
   186 void MusicCMD_Stop(MusicCMD *music)
   187 {
   188 	int status;
   189 
   190 	if ( music->pid > 0 ) {
   191 		while ( kill(music->pid, 0) == 0 ) {
   192 			kill(music->pid, SIGTERM);
   193 			sleep(1);
   194 			waitpid(music->pid, &status, WNOHANG);
   195 		}
   196 		music->pid = 0;
   197 	}
   198 }
   199 
   200 /* Pause playback of a given music stream */
   201 void MusicCMD_Pause(MusicCMD *music)
   202 {
   203 	if ( music->pid > 0 ) {
   204 		kill(music->pid, SIGSTOP);
   205 	}
   206 }
   207 
   208 /* Resume playback of a given music stream */
   209 void MusicCMD_Resume(MusicCMD *music)
   210 {
   211 	if ( music->pid > 0 ) {
   212 		kill(music->pid, SIGCONT);
   213 	}
   214 }
   215 
   216 /* Close the given music stream */
   217 void MusicCMD_FreeSong(MusicCMD *music)
   218 {
   219 	free(music);
   220 }
   221 
   222 /* Return non-zero if a stream is currently playing */
   223 int MusicCMD_Active(MusicCMD *music)
   224 {
   225 	int status;
   226 	int active;
   227 
   228 	active = 0;
   229 	if ( music->pid > 0 ) {
   230 		waitpid(music->pid, &status, WNOHANG);
   231 		if ( kill(music->pid, 0) == 0 ) {
   232 			active = 1;
   233 		}
   234 	}
   235 	return(active);
   236 }
   237 
   238 #endif /* unix */