music_cmd.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 11 Oct 2009 08:40:35 +0000
changeset 447 50248d0ec654
parent 386 695494546b3c
child 467 4b52401dda92
permissions -rw-r--r--
Fixed bug #357

This was a sneaky one.
When music loops, the looping music play call is done from the audio thread, and SDL threads block signals to avoid signal side effects. So we just need to unblock those signals before executing the new process.
slouken@0
     1
/*
slouken@138
     2
    SDL_mixer:  An audio mixer library based on the SDL library
slouken@386
     3
    Copyright (C) 1997-2009 Sam Lantinga
slouken@0
     4
slouken@0
     5
    This library is free software; you can redistribute it and/or
slouken@0
     6
    modify it under the terms of the GNU Library General Public
slouken@0
     7
    License as published by the Free Software Foundation; either
slouken@0
     8
    version 2 of the License, or (at your option) any later version.
slouken@0
     9
slouken@0
    10
    This library is distributed in the hope that it will be useful,
slouken@0
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@0
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@0
    13
    Library General Public License for more details.
slouken@0
    14
slouken@0
    15
    You should have received a copy of the GNU Library General Public
slouken@0
    16
    License along with this library; if not, write to the Free
slouken@0
    17
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
slouken@0
    18
slouken@0
    19
    Sam Lantinga
slouken@138
    20
    slouken@libsdl.org
slouken@0
    21
*/
slouken@294
    22
#include "SDL_config.h"
slouken@138
    23
slouken@0
    24
/* This file supports an external command for playing music */
slouken@0
    25
slouken@294
    26
#if defined(unix) || defined(__MACOSX__) /* This is a UNIX-specific hack */
slouken@0
    27
slouken@0
    28
#include <sys/types.h>
slouken@0
    29
#include <sys/wait.h>
slouken@0
    30
#include <stdio.h>
slouken@0
    31
#include <stdlib.h>
slouken@0
    32
#include <unistd.h>
slouken@0
    33
#include <string.h>
slouken@0
    34
#include <signal.h>
slouken@155
    35
#include <ctype.h>
slouken@0
    36
slouken@34
    37
#include "SDL_mixer.h"
slouken@0
    38
#include "music_cmd.h"
slouken@0
    39
slouken@0
    40
/* Unimplemented */
slouken@0
    41
void MusicCMD_SetVolume(int volume)
slouken@0
    42
{
slouken@0
    43
	Mix_SetError("No way to modify external player volume");
slouken@0
    44
}
slouken@0
    45
slouken@0
    46
/* Load a music stream from the given file */
slouken@0
    47
MusicCMD *MusicCMD_LoadSong(const char *cmd, const char *file)
slouken@0
    48
{
slouken@0
    49
	MusicCMD *music;
slouken@0
    50
slouken@0
    51
	/* Allocate and fill the music structure */
slouken@0
    52
	music = (MusicCMD *)malloc(sizeof *music);
slouken@0
    53
	if ( music == NULL ) {
slouken@0
    54
		Mix_SetError("Out of memory");
slouken@0
    55
		return(NULL);
slouken@0
    56
	}
slouken@0
    57
	strncpy(music->file, file, (sizeof music->file)-1);
slouken@0
    58
	music->file[(sizeof music->file)-1] = '\0';
slouken@0
    59
	strncpy(music->cmd, cmd, (sizeof music->cmd)-1);
slouken@0
    60
	music->cmd[(sizeof music->cmd)-1] = '\0';
slouken@0
    61
	music->pid = 0;
slouken@0
    62
slouken@0
    63
	/* We're done */
slouken@0
    64
	return(music);
slouken@0
    65
}
slouken@0
    66
slouken@0
    67
/* Parse a command line buffer into arguments */
slouken@0
    68
static int ParseCommandLine(char *cmdline, char **argv)
slouken@0
    69
{
slouken@0
    70
	char *bufp;
slouken@0
    71
	int argc;
slouken@0
    72
slouken@0
    73
	argc = 0;
slouken@0
    74
	for ( bufp = cmdline; *bufp; ) {
slouken@0
    75
		/* Skip leading whitespace */
slouken@0
    76
		while ( isspace(*bufp) ) {
slouken@0
    77
			++bufp;
slouken@0
    78
		}
slouken@0
    79
		/* Skip over argument */
slouken@0
    80
		if ( *bufp == '"' ) {
slouken@0
    81
			++bufp;
slouken@0
    82
			if ( *bufp ) {
slouken@0
    83
				if ( argv ) {
slouken@0
    84
					argv[argc] = bufp;
slouken@0
    85
				}
slouken@0
    86
				++argc;
slouken@0
    87
			}
slouken@0
    88
			/* Skip over word */
slouken@0
    89
			while ( *bufp && (*bufp != '"') ) {
slouken@0
    90
				++bufp;
slouken@0
    91
			}
slouken@0
    92
		} else {
slouken@0
    93
			if ( *bufp ) {
slouken@0
    94
				if ( argv ) {
slouken@0
    95
					argv[argc] = bufp;
slouken@0
    96
				}
slouken@0
    97
				++argc;
slouken@0
    98
			}
slouken@0
    99
			/* Skip over word */
slouken@0
   100
			while ( *bufp && ! isspace(*bufp) ) {
slouken@0
   101
				++bufp;
slouken@0
   102
			}
slouken@0
   103
		}
slouken@0
   104
		if ( *bufp ) {
slouken@0
   105
			if ( argv ) {
slouken@0
   106
				*bufp = '\0';
slouken@0
   107
			}
slouken@0
   108
			++bufp;
slouken@0
   109
		}
slouken@0
   110
	}
slouken@0
   111
	if ( argv ) {
slouken@0
   112
		argv[argc] = NULL;
slouken@0
   113
	}
slouken@0
   114
	return(argc);
slouken@0
   115
}
slouken@0
   116
slouken@0
   117
static char **parse_args(char *command, char *last_arg)
slouken@0
   118
{
slouken@0
   119
	int argc;
slouken@0
   120
	char **argv;
slouken@0
   121
slouken@0
   122
	/* Parse the command line */
slouken@0
   123
	argc = ParseCommandLine(command, NULL);
slouken@0
   124
	if ( last_arg ) {
slouken@0
   125
		++argc;
slouken@0
   126
	}
slouken@0
   127
	argv = (char **)malloc((argc+1)*(sizeof *argv));
slouken@0
   128
	if ( argv == NULL ) {
slouken@0
   129
		return(NULL);
slouken@0
   130
	}
slouken@0
   131
	argc = ParseCommandLine(command, argv);
slouken@0
   132
slouken@0
   133
	/* Add last command line argument */
slouken@0
   134
	if ( last_arg ) {
slouken@0
   135
		argv[argc++] = last_arg;
slouken@0
   136
	}
slouken@0
   137
	argv[argc] = NULL;
slouken@0
   138
slouken@0
   139
	/* We're ready! */
slouken@0
   140
	return(argv);
slouken@0
   141
}
slouken@0
   142
slouken@0
   143
/* Start playback of a given music stream */
slouken@0
   144
void MusicCMD_Start(MusicCMD *music)
slouken@0
   145
{
slouken@0
   146
	music->pid = fork();
slouken@0
   147
	switch(music->pid) {
slouken@0
   148
	    /* Failed fork() system call */
slouken@0
   149
	    case -1:
slouken@0
   150
		Mix_SetError("fork() failed");
slouken@0
   151
		return;
slouken@0
   152
slouken@0
   153
	    /* Child process - executes here */
slouken@0
   154
	    case 0: {
slouken@0
   155
		    char command[PATH_MAX];
slouken@0
   156
		    char **argv;
slouken@0
   157
slouken@447
   158
		    /* Unblock signals in case we're called from a thread */
slouken@447
   159
		    {
slouken@447
   160
			sigset_t mask;
slouken@447
   161
			sigemptyset(&mask);
slouken@447
   162
			sigprocmask(SIG_SETMASK, &mask, NULL);
slouken@447
   163
		    }
slouken@447
   164
slouken@0
   165
		    /* Execute the command */
slouken@0
   166
		    strcpy(command, music->cmd);
slouken@0
   167
		    argv = parse_args(command, music->file);
slouken@0
   168
		    if ( argv != NULL ) {
slouken@0
   169
			execvp(argv[0], argv);
slouken@0
   170
		    }
slouken@0
   171
slouken@0
   172
		    /* exec() failed */
slouken@0
   173
		    perror(argv[0]);
slouken@0
   174
		    _exit(-1);
slouken@0
   175
		}
slouken@0
   176
		break;
slouken@0
   177
slouken@0
   178
	    /* Parent process - executes here */
slouken@0
   179
	    default:
slouken@0
   180
		break;
slouken@0
   181
	}
slouken@0
   182
	return;
slouken@0
   183
}
slouken@0
   184
slouken@0
   185
/* Stop playback of a stream previously started with MusicCMD_Start() */
slouken@0
   186
void MusicCMD_Stop(MusicCMD *music)
slouken@0
   187
{
slouken@0
   188
	int status;
slouken@0
   189
slouken@0
   190
	if ( music->pid > 0 ) {
slouken@0
   191
		while ( kill(music->pid, 0) == 0 ) {
slouken@0
   192
			kill(music->pid, SIGTERM);
slouken@0
   193
			sleep(1);
slouken@0
   194
			waitpid(music->pid, &status, WNOHANG);
slouken@0
   195
		}
slouken@0
   196
		music->pid = 0;
slouken@0
   197
	}
slouken@0
   198
}
slouken@0
   199
slouken@0
   200
/* Pause playback of a given music stream */
slouken@0
   201
void MusicCMD_Pause(MusicCMD *music)
slouken@0
   202
{
slouken@0
   203
	if ( music->pid > 0 ) {
slouken@0
   204
		kill(music->pid, SIGSTOP);
slouken@0
   205
	}
slouken@0
   206
}
slouken@0
   207
slouken@0
   208
/* Resume playback of a given music stream */
slouken@0
   209
void MusicCMD_Resume(MusicCMD *music)
slouken@0
   210
{
slouken@0
   211
	if ( music->pid > 0 ) {
slouken@0
   212
		kill(music->pid, SIGCONT);
slouken@0
   213
	}
slouken@0
   214
}
slouken@0
   215
slouken@0
   216
/* Close the given music stream */
slouken@0
   217
void MusicCMD_FreeSong(MusicCMD *music)
slouken@0
   218
{
slouken@0
   219
	free(music);
slouken@0
   220
}
slouken@0
   221
slouken@0
   222
/* Return non-zero if a stream is currently playing */
slouken@0
   223
int MusicCMD_Active(MusicCMD *music)
slouken@0
   224
{
slouken@0
   225
	int status;
slouken@0
   226
	int active;
slouken@0
   227
slouken@0
   228
	active = 0;
slouken@0
   229
	if ( music->pid > 0 ) {
slouken@0
   230
		waitpid(music->pid, &status, WNOHANG);
slouken@0
   231
		if ( kill(music->pid, 0) == 0 ) {
slouken@0
   232
			active = 1;
slouken@0
   233
		}
slouken@0
   234
	}
slouken@0
   235
	return(active);
slouken@0
   236
}
slouken@0
   237
slouken@0
   238
#endif /* unix */