music_cmd.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 04 Jan 2004 17:41:55 +0000
changeset 241 503416fca921
parent 155 53f259dae79d
child 294 58eb208474e0
permissions -rw-r--r--
Updated copyright information for 2004 (Happy New Year!)
     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 
    23 /* $Id$ */
    24 
    25 /* This file supports an external command for playing music */
    26 
    27 #ifdef unix		/* This is a UNIX-specific hack */
    28 
    29 #include <sys/types.h>
    30 #include <sys/wait.h>
    31 #include <stdio.h>
    32 #include <stdlib.h>
    33 #include <unistd.h>
    34 #include <string.h>
    35 #include <signal.h>
    36 #include <ctype.h>
    37 
    38 #include "SDL_mixer.h"
    39 #include "music_cmd.h"
    40 
    41 /* Unimplemented */
    42 void MusicCMD_SetVolume(int volume)
    43 {
    44 	Mix_SetError("No way to modify external player volume");
    45 }
    46 
    47 /* Load a music stream from the given file */
    48 MusicCMD *MusicCMD_LoadSong(const char *cmd, const char *file)
    49 {
    50 	MusicCMD *music;
    51 
    52 	/* Allocate and fill the music structure */
    53 	music = (MusicCMD *)malloc(sizeof *music);
    54 	if ( music == NULL ) {
    55 		Mix_SetError("Out of memory");
    56 		return(NULL);
    57 	}
    58 	strncpy(music->file, file, (sizeof music->file)-1);
    59 	music->file[(sizeof music->file)-1] = '\0';
    60 	strncpy(music->cmd, cmd, (sizeof music->cmd)-1);
    61 	music->cmd[(sizeof music->cmd)-1] = '\0';
    62 	music->pid = 0;
    63 
    64 	/* We're done */
    65 	return(music);
    66 }
    67 
    68 /* Parse a command line buffer into arguments */
    69 static int ParseCommandLine(char *cmdline, char **argv)
    70 {
    71 	char *bufp;
    72 	int argc;
    73 
    74 	argc = 0;
    75 	for ( bufp = cmdline; *bufp; ) {
    76 		/* Skip leading whitespace */
    77 		while ( isspace(*bufp) ) {
    78 			++bufp;
    79 		}
    80 		/* Skip over argument */
    81 		if ( *bufp == '"' ) {
    82 			++bufp;
    83 			if ( *bufp ) {
    84 				if ( argv ) {
    85 					argv[argc] = bufp;
    86 				}
    87 				++argc;
    88 			}
    89 			/* Skip over word */
    90 			while ( *bufp && (*bufp != '"') ) {
    91 				++bufp;
    92 			}
    93 		} else {
    94 			if ( *bufp ) {
    95 				if ( argv ) {
    96 					argv[argc] = bufp;
    97 				}
    98 				++argc;
    99 			}
   100 			/* Skip over word */
   101 			while ( *bufp && ! isspace(*bufp) ) {
   102 				++bufp;
   103 			}
   104 		}
   105 		if ( *bufp ) {
   106 			if ( argv ) {
   107 				*bufp = '\0';
   108 			}
   109 			++bufp;
   110 		}
   111 	}
   112 	if ( argv ) {
   113 		argv[argc] = NULL;
   114 	}
   115 	return(argc);
   116 }
   117 
   118 static char **parse_args(char *command, char *last_arg)
   119 {
   120 	int argc;
   121 	char **argv;
   122 
   123 	/* Parse the command line */
   124 	argc = ParseCommandLine(command, NULL);
   125 	if ( last_arg ) {
   126 		++argc;
   127 	}
   128 	argv = (char **)malloc((argc+1)*(sizeof *argv));
   129 	if ( argv == NULL ) {
   130 		return(NULL);
   131 	}
   132 	argc = ParseCommandLine(command, argv);
   133 
   134 	/* Add last command line argument */
   135 	if ( last_arg ) {
   136 		argv[argc++] = last_arg;
   137 	}
   138 	argv[argc] = NULL;
   139 
   140 	/* We're ready! */
   141 	return(argv);
   142 }
   143 
   144 /* Start playback of a given music stream */
   145 void MusicCMD_Start(MusicCMD *music)
   146 {
   147 	music->pid = fork();
   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[PATH_MAX];
   157 		    char **argv;
   158 
   159 		    /* Execute the command */
   160 		    strcpy(command, music->cmd);
   161 		    argv = parse_args(command, music->file);
   162 		    if ( argv != NULL ) {
   163 			execvp(argv[0], argv);
   164 		    }
   165 
   166 		    /* exec() failed */
   167 		    perror(argv[0]);
   168 		    _exit(-1);
   169 		}
   170 		break;
   171 
   172 	    /* Parent process - executes here */
   173 	    default:
   174 		break;
   175 	}
   176 	return;
   177 }
   178 
   179 /* Stop playback of a stream previously started with MusicCMD_Start() */
   180 void MusicCMD_Stop(MusicCMD *music)
   181 {
   182 	int status;
   183 
   184 	if ( music->pid > 0 ) {
   185 		while ( kill(music->pid, 0) == 0 ) {
   186 			kill(music->pid, SIGTERM);
   187 			sleep(1);
   188 			waitpid(music->pid, &status, WNOHANG);
   189 		}
   190 		music->pid = 0;
   191 	}
   192 }
   193 
   194 /* Pause playback of a given music stream */
   195 void MusicCMD_Pause(MusicCMD *music)
   196 {
   197 	if ( music->pid > 0 ) {
   198 		kill(music->pid, SIGSTOP);
   199 	}
   200 }
   201 
   202 /* Resume playback of a given music stream */
   203 void MusicCMD_Resume(MusicCMD *music)
   204 {
   205 	if ( music->pid > 0 ) {
   206 		kill(music->pid, SIGCONT);
   207 	}
   208 }
   209 
   210 /* Close the given music stream */
   211 void MusicCMD_FreeSong(MusicCMD *music)
   212 {
   213 	free(music);
   214 }
   215 
   216 /* Return non-zero if a stream is currently playing */
   217 int MusicCMD_Active(MusicCMD *music)
   218 {
   219 	int status;
   220 	int active;
   221 
   222 	active = 0;
   223 	if ( music->pid > 0 ) {
   224 		waitpid(music->pid, &status, WNOHANG);
   225 		if ( kill(music->pid, 0) == 0 ) {
   226 			active = 1;
   227 		}
   228 	}
   229 	return(active);
   230 }
   231 
   232 #endif /* unix */