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.
slouken@0
     1
/*
slouken@518
     2
  SDL_mixer:  An audio mixer library based on the SDL library
slouken@518
     3
  Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
slouken@0
     4
slouken@518
     5
  This software is provided 'as-is', without any express or implied
slouken@518
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@518
     7
  arising from the use of this software.
slouken@0
     8
slouken@518
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@518
    10
  including commercial applications, and to alter it and redistribute it
slouken@518
    11
  freely, subject to the following restrictions:
slouken@0
    12
slouken@518
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@518
    14
     claim that you wrote the original software. If you use this software
slouken@518
    15
     in a product, an acknowledgment in the product documentation would be
slouken@518
    16
     appreciated but is not required.
slouken@518
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@518
    18
     misrepresented as being the original software.
slouken@518
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@0
    20
*/
slouken@294
    21
#include "SDL_config.h"
slouken@138
    22
slouken@0
    23
/* This file supports an external command for playing music */
slouken@0
    24
slouken@467
    25
#ifdef CMD_MUSIC
slouken@0
    26
slouken@0
    27
#include <sys/types.h>
slouken@0
    28
#include <sys/wait.h>
slouken@0
    29
#include <stdio.h>
slouken@0
    30
#include <stdlib.h>
slouken@0
    31
#include <unistd.h>
slouken@0
    32
#include <string.h>
slouken@0
    33
#include <signal.h>
slouken@155
    34
#include <ctype.h>
slouken@0
    35
slouken@34
    36
#include "SDL_mixer.h"
slouken@0
    37
#include "music_cmd.h"
slouken@0
    38
slouken@0
    39
/* Unimplemented */
slouken@0
    40
void MusicCMD_SetVolume(int volume)
slouken@0
    41
{
slouken@0
    42
	Mix_SetError("No way to modify external player volume");
slouken@0
    43
}
slouken@0
    44
slouken@0
    45
/* Load a music stream from the given file */
slouken@0
    46
MusicCMD *MusicCMD_LoadSong(const char *cmd, const char *file)
slouken@0
    47
{
slouken@0
    48
	MusicCMD *music;
slouken@0
    49
slouken@0
    50
	/* Allocate and fill the music structure */
slouken@561
    51
	music = (MusicCMD *)SDL_malloc(sizeof *music);
slouken@0
    52
	if ( music == NULL ) {
slouken@0
    53
		Mix_SetError("Out of memory");
slouken@0
    54
		return(NULL);
slouken@0
    55
	}
slouken@0
    56
	strncpy(music->file, file, (sizeof music->file)-1);
slouken@0
    57
	music->file[(sizeof music->file)-1] = '\0';
slouken@0
    58
	strncpy(music->cmd, cmd, (sizeof music->cmd)-1);
slouken@0
    59
	music->cmd[(sizeof music->cmd)-1] = '\0';
slouken@0
    60
	music->pid = 0;
slouken@0
    61
slouken@0
    62
	/* We're done */
slouken@0
    63
	return(music);
slouken@0
    64
}
slouken@0
    65
slouken@0
    66
/* Parse a command line buffer into arguments */
slouken@0
    67
static int ParseCommandLine(char *cmdline, char **argv)
slouken@0
    68
{
slouken@0
    69
	char *bufp;
slouken@0
    70
	int argc;
slouken@0
    71
slouken@0
    72
	argc = 0;
slouken@0
    73
	for ( bufp = cmdline; *bufp; ) {
slouken@0
    74
		/* Skip leading whitespace */
slouken@0
    75
		while ( isspace(*bufp) ) {
slouken@0
    76
			++bufp;
slouken@0
    77
		}
slouken@0
    78
		/* Skip over argument */
slouken@0
    79
		if ( *bufp == '"' ) {
slouken@0
    80
			++bufp;
slouken@0
    81
			if ( *bufp ) {
slouken@0
    82
				if ( argv ) {
slouken@0
    83
					argv[argc] = bufp;
slouken@0
    84
				}
slouken@0
    85
				++argc;
slouken@0
    86
			}
slouken@0
    87
			/* Skip over word */
slouken@0
    88
			while ( *bufp && (*bufp != '"') ) {
slouken@0
    89
				++bufp;
slouken@0
    90
			}
slouken@0
    91
		} else {
slouken@0
    92
			if ( *bufp ) {
slouken@0
    93
				if ( argv ) {
slouken@0
    94
					argv[argc] = bufp;
slouken@0
    95
				}
slouken@0
    96
				++argc;
slouken@0
    97
			}
slouken@0
    98
			/* Skip over word */
slouken@0
    99
			while ( *bufp && ! isspace(*bufp) ) {
slouken@0
   100
				++bufp;
slouken@0
   101
			}
slouken@0
   102
		}
slouken@0
   103
		if ( *bufp ) {
slouken@0
   104
			if ( argv ) {
slouken@0
   105
				*bufp = '\0';
slouken@0
   106
			}
slouken@0
   107
			++bufp;
slouken@0
   108
		}
slouken@0
   109
	}
slouken@0
   110
	if ( argv ) {
slouken@0
   111
		argv[argc] = NULL;
slouken@0
   112
	}
slouken@0
   113
	return(argc);
slouken@0
   114
}
slouken@0
   115
slouken@0
   116
static char **parse_args(char *command, char *last_arg)
slouken@0
   117
{
slouken@0
   118
	int argc;
slouken@0
   119
	char **argv;
slouken@0
   120
slouken@0
   121
	/* Parse the command line */
slouken@0
   122
	argc = ParseCommandLine(command, NULL);
slouken@0
   123
	if ( last_arg ) {
slouken@0
   124
		++argc;
slouken@0
   125
	}
slouken@561
   126
	argv = (char **)SDL_malloc((argc+1)*(sizeof *argv));
slouken@0
   127
	if ( argv == NULL ) {
slouken@0
   128
		return(NULL);
slouken@0
   129
	}
slouken@0
   130
	argc = ParseCommandLine(command, argv);
slouken@0
   131
slouken@0
   132
	/* Add last command line argument */
slouken@0
   133
	if ( last_arg ) {
slouken@0
   134
		argv[argc++] = last_arg;
slouken@0
   135
	}
slouken@0
   136
	argv[argc] = NULL;
slouken@0
   137
slouken@0
   138
	/* We're ready! */
slouken@0
   139
	return(argv);
slouken@0
   140
}
slouken@0
   141
slouken@0
   142
/* Start playback of a given music stream */
slouken@0
   143
void MusicCMD_Start(MusicCMD *music)
slouken@0
   144
{
slouken@467
   145
#ifdef HAVE_FORK
slouken@0
   146
	music->pid = fork();
slouken@467
   147
#else
slouken@467
   148
	music->pid = vfork();
slouken@467
   149
#endif
slouken@0
   150
	switch(music->pid) {
slouken@0
   151
	    /* Failed fork() system call */
slouken@0
   152
	    case -1:
slouken@0
   153
		Mix_SetError("fork() failed");
slouken@0
   154
		return;
slouken@0
   155
slouken@0
   156
	    /* Child process - executes here */
slouken@0
   157
	    case 0: {
slouken@0
   158
		    char command[PATH_MAX];
slouken@0
   159
		    char **argv;
slouken@0
   160
slouken@447
   161
		    /* Unblock signals in case we're called from a thread */
slouken@447
   162
		    {
slouken@447
   163
			sigset_t mask;
slouken@447
   164
			sigemptyset(&mask);
slouken@447
   165
			sigprocmask(SIG_SETMASK, &mask, NULL);
slouken@447
   166
		    }
slouken@447
   167
slouken@0
   168
		    /* Execute the command */
slouken@0
   169
		    strcpy(command, music->cmd);
slouken@0
   170
		    argv = parse_args(command, music->file);
slouken@0
   171
		    if ( argv != NULL ) {
slouken@0
   172
			execvp(argv[0], argv);
slouken@0
   173
		    }
slouken@0
   174
slouken@0
   175
		    /* exec() failed */
slouken@0
   176
		    perror(argv[0]);
slouken@0
   177
		    _exit(-1);
slouken@0
   178
		}
slouken@0
   179
		break;
slouken@0
   180
slouken@0
   181
	    /* Parent process - executes here */
slouken@0
   182
	    default:
slouken@0
   183
		break;
slouken@0
   184
	}
slouken@0
   185
	return;
slouken@0
   186
}
slouken@0
   187
slouken@0
   188
/* Stop playback of a stream previously started with MusicCMD_Start() */
slouken@0
   189
void MusicCMD_Stop(MusicCMD *music)
slouken@0
   190
{
slouken@0
   191
	int status;
slouken@0
   192
slouken@0
   193
	if ( music->pid > 0 ) {
slouken@0
   194
		while ( kill(music->pid, 0) == 0 ) {
slouken@0
   195
			kill(music->pid, SIGTERM);
slouken@0
   196
			sleep(1);
slouken@0
   197
			waitpid(music->pid, &status, WNOHANG);
slouken@0
   198
		}
slouken@0
   199
		music->pid = 0;
slouken@0
   200
	}
slouken@0
   201
}
slouken@0
   202
slouken@0
   203
/* Pause playback of a given music stream */
slouken@0
   204
void MusicCMD_Pause(MusicCMD *music)
slouken@0
   205
{
slouken@0
   206
	if ( music->pid > 0 ) {
slouken@0
   207
		kill(music->pid, SIGSTOP);
slouken@0
   208
	}
slouken@0
   209
}
slouken@0
   210
slouken@0
   211
/* Resume playback of a given music stream */
slouken@0
   212
void MusicCMD_Resume(MusicCMD *music)
slouken@0
   213
{
slouken@0
   214
	if ( music->pid > 0 ) {
slouken@0
   215
		kill(music->pid, SIGCONT);
slouken@0
   216
	}
slouken@0
   217
}
slouken@0
   218
slouken@0
   219
/* Close the given music stream */
slouken@0
   220
void MusicCMD_FreeSong(MusicCMD *music)
slouken@0
   221
{
slouken@561
   222
	SDL_free(music);
slouken@0
   223
}
slouken@0
   224
slouken@0
   225
/* Return non-zero if a stream is currently playing */
slouken@0
   226
int MusicCMD_Active(MusicCMD *music)
slouken@0
   227
{
slouken@0
   228
	int status;
slouken@0
   229
	int active;
slouken@0
   230
slouken@0
   231
	active = 0;
slouken@0
   232
	if ( music->pid > 0 ) {
slouken@0
   233
		waitpid(music->pid, &status, WNOHANG);
slouken@0
   234
		if ( kill(music->pid, 0) == 0 ) {
slouken@0
   235
			active = 1;
slouken@0
   236
		}
slouken@0
   237
	}
slouken@0
   238
	return(active);
slouken@0
   239
}
slouken@0
   240
slouken@467
   241
#endif /* CMD_MUSIC */