music_cmd.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 07 Nov 2018 07:47:50 -0800
changeset 924 9be60a9582a6
parent 848 3907db698eb5
child 926 d6c9518fb5ee
permissions -rw-r--r--
The Debian maintainers aren't using these rules, so enable dynamic loading of shared libraries by default for the Steam Linux Runtime
slouken@0
     1
/*
slouken@518
     2
  SDL_mixer:  An audio mixer library based on the SDL library
slouken@848
     3
  Copyright (C) 1997-2018 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@777
    25
#ifdef MUSIC_CMD
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@777
    35
#include <limits.h>
slouken@777
    36
#if defined(__linux__) && defined(__arm__)
slouken@777
    37
# include <linux/limits.h>
slouken@777
    38
#endif
slouken@0
    39
slouken@0
    40
#include "music_cmd.h"
slouken@0
    41
slouken@777
    42
slouken@777
    43
typedef struct {
slouken@777
    44
    char *file;
slouken@781
    45
    char *cmd;
slouken@777
    46
    pid_t pid;
slouken@797
    47
    int play_count;
slouken@777
    48
} MusicCMD;
slouken@777
    49
slouken@0
    50
slouken@0
    51
/* Load a music stream from the given file */
slouken@777
    52
static void *MusicCMD_CreateFromFile(const char *file)
slouken@0
    53
{
slouken@617
    54
    MusicCMD *music;
slouken@0
    55
slouken@781
    56
    if (!music_cmd) {
slouken@781
    57
        Mix_SetError("You must call Mix_SetMusicCMD() first");
slouken@781
    58
        return NULL;
slouken@781
    59
    }
slouken@781
    60
slouken@617
    61
    /* Allocate and fill the music structure */
slouken@777
    62
    music = (MusicCMD *)SDL_calloc(1, sizeof *music);
slouken@777
    63
    if (music == NULL) {
slouken@617
    64
        Mix_SetError("Out of memory");
slouken@777
    65
        return NULL;
slouken@617
    66
    }
slouken@617
    67
    music->file = SDL_strdup(file);
slouken@781
    68
    music->cmd = SDL_strdup(music_cmd);
slouken@617
    69
    music->pid = 0;
slouken@0
    70
slouken@617
    71
    /* We're done */
slouken@777
    72
    return music;
slouken@0
    73
}
slouken@0
    74
slouken@0
    75
/* Parse a command line buffer into arguments */
slouken@0
    76
static int ParseCommandLine(char *cmdline, char **argv)
slouken@0
    77
{
slouken@617
    78
    char *bufp;
slouken@617
    79
    int argc;
slouken@0
    80
slouken@617
    81
    argc = 0;
slouken@777
    82
    for (bufp = cmdline; *bufp;) {
slouken@617
    83
        /* Skip leading whitespace */
slouken@777
    84
        while (isspace(*bufp)) {
slouken@617
    85
            ++bufp;
slouken@617
    86
        }
slouken@617
    87
        /* Skip over argument */
slouken@777
    88
        if (*bufp == '"') {
slouken@617
    89
            ++bufp;
slouken@777
    90
            if (*bufp) {
slouken@777
    91
                if (argv) {
slouken@617
    92
                    argv[argc] = bufp;
slouken@617
    93
                }
slouken@617
    94
                ++argc;
slouken@617
    95
            }
slouken@617
    96
            /* Skip over word */
slouken@777
    97
            while (*bufp && (*bufp != '"')) {
slouken@617
    98
                ++bufp;
slouken@617
    99
            }
slouken@617
   100
        } else {
slouken@777
   101
            if (*bufp) {
slouken@777
   102
                if (argv) {
slouken@617
   103
                    argv[argc] = bufp;
slouken@617
   104
                }
slouken@617
   105
                ++argc;
slouken@617
   106
            }
slouken@617
   107
            /* Skip over word */
slouken@777
   108
            while (*bufp && ! isspace(*bufp)) {
slouken@617
   109
                ++bufp;
slouken@617
   110
            }
slouken@617
   111
        }
slouken@777
   112
        if (*bufp) {
slouken@777
   113
            if (argv) {
slouken@617
   114
                *bufp = '\0';
slouken@617
   115
            }
slouken@617
   116
            ++bufp;
slouken@617
   117
        }
slouken@617
   118
    }
slouken@777
   119
    if (argv) {
slouken@617
   120
        argv[argc] = NULL;
slouken@617
   121
    }
slouken@617
   122
    return(argc);
slouken@0
   123
}
slouken@0
   124
slouken@0
   125
static char **parse_args(char *command, char *last_arg)
slouken@0
   126
{
slouken@617
   127
    int argc;
slouken@617
   128
    char **argv;
slouken@0
   129
slouken@617
   130
    /* Parse the command line */
slouken@617
   131
    argc = ParseCommandLine(command, NULL);
slouken@777
   132
    if (last_arg) {
slouken@617
   133
        ++argc;
slouken@617
   134
    }
slouken@617
   135
    argv = (char **)SDL_malloc((argc+1)*(sizeof *argv));
slouken@777
   136
    if (argv == NULL) {
slouken@617
   137
        return(NULL);
slouken@617
   138
    }
slouken@617
   139
    argc = ParseCommandLine(command, argv);
slouken@0
   140
slouken@617
   141
    /* Add last command line argument */
slouken@777
   142
    if (last_arg) {
slouken@617
   143
        argv[argc++] = last_arg;
slouken@617
   144
    }
slouken@617
   145
    argv[argc] = NULL;
slouken@0
   146
slouken@617
   147
    /* We're ready! */
slouken@617
   148
    return(argv);
slouken@0
   149
}
slouken@0
   150
slouken@0
   151
/* Start playback of a given music stream */
slouken@797
   152
static int MusicCMD_Play(void *context, int play_count)
slouken@0
   153
{
slouken@777
   154
    MusicCMD *music = (MusicCMD *)context;
slouken@777
   155
slouken@797
   156
    music->play_count = play_count;
slouken@467
   157
#ifdef HAVE_FORK
slouken@617
   158
    music->pid = fork();
slouken@467
   159
#else
slouken@617
   160
    music->pid = vfork();
slouken@467
   161
#endif
slouken@617
   162
    switch(music->pid) {
slouken@777
   163
    /* Failed fork() system call */
slouken@777
   164
    case -1:
slouken@617
   165
        Mix_SetError("fork() failed");
slouken@777
   166
        return -1;
slouken@0
   167
slouken@777
   168
    /* Child process - executes here */
slouken@777
   169
    case 0: {
slouken@777
   170
        char **argv;
slouken@0
   171
slouken@777
   172
        /* Unblock signals in case we're called from a thread */
slouken@777
   173
        {
slouken@777
   174
            sigset_t mask;
slouken@777
   175
            sigemptyset(&mask);
slouken@777
   176
            sigprocmask(SIG_SETMASK, &mask, NULL);
slouken@777
   177
        }
slouken@447
   178
slouken@777
   179
        /* Execute the command */
slouken@781
   180
        argv = parse_args(music->cmd, music->file);
slouken@777
   181
        if (argv != NULL) {
slouken@777
   182
            execvp(argv[0], argv);
slouken@763
   183
slouken@777
   184
            /* exec() failed */
slouken@777
   185
            perror(argv[0]);
slouken@617
   186
        }
slouken@777
   187
        _exit(-1);
slouken@777
   188
    }
slouken@777
   189
    break;
slouken@0
   190
slouken@777
   191
    /* Parent process - executes here */
slouken@777
   192
    default:
slouken@617
   193
        break;
slouken@617
   194
    }
slouken@777
   195
    return 0;
slouken@777
   196
}
slouken@777
   197
slouken@777
   198
/* Return non-zero if a stream is currently playing */
slouken@777
   199
static SDL_bool MusicCMD_IsPlaying(void *context)
slouken@777
   200
{
slouken@777
   201
    MusicCMD *music = (MusicCMD *)context;
slouken@777
   202
    int status;
slouken@777
   203
slouken@777
   204
    if (music->pid > 0) {
slouken@777
   205
        waitpid(music->pid, &status, WNOHANG);
slouken@777
   206
        if (kill(music->pid, 0) == 0) {
slouken@777
   207
            return SDL_TRUE;
slouken@777
   208
        }
slouken@797
   209
slouken@797
   210
        /* We might want to loop */
slouken@797
   211
        if (music->play_count != 1) {
slouken@797
   212
            int play_count = -1;
slouken@797
   213
            if (music->play_count > 0) {
slouken@797
   214
                play_count = (music->play_count - 1);
slouken@797
   215
            }
slouken@797
   216
            MusicCMD_Play(music, play_count);
slouken@797
   217
            return SDL_TRUE;
slouken@797
   218
        }
slouken@777
   219
    }
slouken@777
   220
    return SDL_FALSE;
slouken@777
   221
}
slouken@777
   222
slouken@777
   223
/* Pause playback of a given music stream */
slouken@777
   224
static void MusicCMD_Pause(void *context)
slouken@777
   225
{
slouken@777
   226
    MusicCMD *music = (MusicCMD *)context;
slouken@777
   227
    if (music->pid > 0) {
slouken@777
   228
        kill(music->pid, SIGSTOP);
slouken@777
   229
    }
slouken@777
   230
}
slouken@777
   231
slouken@777
   232
/* Resume playback of a given music stream */
slouken@777
   233
static void MusicCMD_Resume(void *context)
slouken@777
   234
{
slouken@777
   235
    MusicCMD *music = (MusicCMD *)context;
slouken@777
   236
    if (music->pid > 0) {
slouken@777
   237
        kill(music->pid, SIGCONT);
slouken@777
   238
    }
slouken@0
   239
}
slouken@0
   240
slouken@0
   241
/* Stop playback of a stream previously started with MusicCMD_Start() */
slouken@777
   242
static void MusicCMD_Stop(void *context)
slouken@0
   243
{
slouken@777
   244
    MusicCMD *music = (MusicCMD *)context;
slouken@617
   245
    int status;
slouken@0
   246
slouken@777
   247
    if (music->pid > 0) {
slouken@777
   248
        while (kill(music->pid, 0) == 0) {
slouken@617
   249
            kill(music->pid, SIGTERM);
slouken@617
   250
            sleep(1);
slouken@617
   251
            waitpid(music->pid, &status, WNOHANG);
slouken@617
   252
        }
slouken@617
   253
        music->pid = 0;
slouken@617
   254
    }
slouken@0
   255
}
slouken@0
   256
slouken@777
   257
/* Close the given music stream */
slouken@779
   258
void MusicCMD_Delete(void *context)
slouken@0
   259
{
slouken@777
   260
    MusicCMD *music = (MusicCMD *)context;
slouken@777
   261
    SDL_free(music->file);
slouken@777
   262
    SDL_free(music);
slouken@0
   263
}
slouken@0
   264
slouken@777
   265
Mix_MusicInterface Mix_MusicInterface_CMD =
slouken@0
   266
{
slouken@777
   267
    "CMD",
slouken@777
   268
    MIX_MUSIC_CMD,
slouken@777
   269
    MUS_CMD,
slouken@777
   270
    SDL_FALSE,
slouken@777
   271
    SDL_FALSE,
slouken@0
   272
slouken@777
   273
    NULL,   /* Load */
slouken@777
   274
    NULL,   /* Open */
slouken@777
   275
    NULL,   /* CreateFromRW */
slouken@777
   276
    MusicCMD_CreateFromFile,
slouken@777
   277
    NULL,   /* SetVolume */
slouken@777
   278
    MusicCMD_Play,
slouken@777
   279
    MusicCMD_IsPlaying,
slouken@777
   280
    NULL,   /* GetAudio */
slouken@777
   281
    NULL,   /* Seek */
slouken@777
   282
    MusicCMD_Pause,
slouken@777
   283
    MusicCMD_Resume,
slouken@777
   284
    MusicCMD_Stop,
slouken@777
   285
    MusicCMD_Delete,
slouken@777
   286
    NULL,   /* Close */
slouken@777
   287
    NULL,   /* Unload */
slouken@777
   288
};
slouken@0
   289
slouken@777
   290
#endif /* MUSIC_CMD */
slouken@0
   291
slouken@777
   292
/* vi: set ts=4 sw=4 expandtab: */