music_cmd.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 13 Jan 2012 03:15:19 -0500
changeset 561 87bdb4c81c0b
parent 518 8bc9b5fd2aae
child 573 23d69b6621d0
permissions -rw-r--r--
Fixed memory crash loading Ogg Vorbis files on Windows
The pointer to the audio data could come from SDL_LoadWAV_RW() or from our other loaders, and we need to use the correct free() to release the memory. So we'll just use the SDL memory functions for consistency.
This pretty much only matters on Windows where we can't guarantee a certain C runtime so we provide our own malloc() and friends.
     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 */
    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 */
    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 */
    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 
   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 */
   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() */
   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 */
   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 */
   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 */
   220 void MusicCMD_FreeSong(MusicCMD *music)
   221 {
   222 	SDL_free(music);
   223 }
   224 
   225 /* Return non-zero if a stream is currently playing */
   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 */