Subversion Repositories eduke32

Rev

Rev 4386 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1652 terminx 1
//-------------------------------------------------------------------------
1473 terminx 2
/*
1652 terminx 3
Copyright (C) 2010 EDuke32 developers and contributors
1473 terminx 4
 
1652 terminx 5
This file is part of EDuke32.
1473 terminx 6
 
1652 terminx 7
EDuke32 is free software; you can redistribute it and/or
8
modify it under the terms of the GNU General Public License version 2
9
as published by the Free Software Foundation.
10
 
1473 terminx 11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
 
15
See the GNU General Public License for more details.
16
 
17
You should have received a copy of the GNU General Public License
18
along with this program; if not, write to the Free Software
19
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
1652 terminx 20
*/
21
//-------------------------------------------------------------------------
1473 terminx 22
 
23
/*
24
 * A reimplementation of Jim Dose's FX_MAN routines, using  SDL_mixer 1.2.
25
 *   Whee. FX_MAN is also known as the "Apogee Sound System", or "ASS" for
26
 *   short. How strangely appropriate that seems.
27
 */
28
 
4102 hendricks2 29
#define _NEED_SDLMIXER  1
30
 
1473 terminx 31
#include <stdio.h>
32
#include <errno.h>
33
 
1935 helixhorne 34
#if defined __APPLE__ && defined __BIG_ENDIAN__
35
// is* hacks for ppc...
36
# include "compat.h"
37
#endif
38
 
1473 terminx 39
#include "duke3d.h"
40
#include "cache1d.h"
41
 
42
#include "sdl_inc.h"
43
#include "music.h"
44
 
2628 helixhorne 45
#if !defined _WIN32 && !defined(GEKKO)
4167 helixhorne 46
//# define FORK_EXEC_MIDI 1
1725 helixhorne 47
#endif
48
 
49
#if defined FORK_EXEC_MIDI  // fork/exec based external midi player
1672 terminx 50
#include <stdlib.h>
51
#include <signal.h>
1663 helixhorne 52
#include <unistd.h>
53
#include <sys/mman.h>
54
#include <sys/wait.h>
55
static char **external_midi_argv;
56
static pid_t external_midi_pid=-1;
57
static int8_t external_midi_restart=0;
58
#endif
4386 terminx 59
 
60
#ifdef __ANDROID__ //TODO fix
4440 terminx 61
static char *external_midi_tempfn = "eduke32-music.mid";
4386 terminx 62
#else
1663 helixhorne 63
static char *external_midi_tempfn = "/tmp/eduke32-music.mid";
4386 terminx 64
#endif
65
 
1663 helixhorne 66
static int32_t external_midi = 0;
67
 
1473 terminx 68
int32_t MUSIC_ErrorCode = MUSIC_Ok;
69
 
70
static char warningMessage[80];
71
static char errorMessage[80];
72
 
73
static int32_t music_initialized = 0;
74
static int32_t music_context = 0;
75
static int32_t music_loopflag = MUSIC_PlayOnce;
76
static Mix_Music *music_musicchunk = NULL;
77
 
78
static void setErrorMessage(const char *msg)
79
{
2559 helixhorne 80
    Bstrncpyz(errorMessage, msg, sizeof(errorMessage));
81
}
1473 terminx 82
 
83
// The music functions...
84
 
2796 helixhorne 85
const char *MUSIC_ErrorString(int32_t ErrorNumber)
1473 terminx 86
{
87
    switch (ErrorNumber)
88
    {
89
    case MUSIC_Warning:
90
        return(warningMessage);
91
 
92
    case MUSIC_Error:
93
        return(errorMessage);
94
 
95
    case MUSIC_Ok:
96
        return("OK; no error.");
97
 
98
    case MUSIC_ASSVersion:
99
        return("Incorrect sound library version.");
100
 
101
    case MUSIC_SoundCardError:
102
        return("General sound card error.");
103
 
104
    case MUSIC_InvalidCard:
105
        return("Invalid sound card.");
106
 
107
    case MUSIC_MidiError:
108
        return("MIDI error.");
109
 
110
    case MUSIC_MPU401Error:
111
        return("MPU401 error.");
112
 
113
    case MUSIC_TaskManError:
114
        return("Task Manager error.");
115
 
116
        //case MUSIC_FMNotDetected:
117
        //    return("FM not detected error.");
118
 
119
    case MUSIC_DPMI_Error:
120
        return("DPMI error.");
121
 
122
    default:
123
        return("Unknown error.");
124
    } // switch
125
 
126
    return(NULL);
127
} // MUSIC_ErrorString
128
 
129
int32_t MUSIC_Init(int32_t SoundCard, int32_t Address)
130
{
4440 terminx 131
#ifdef __ANDROID__
132
        music_initialized = 1;
133
        return(MUSIC_Ok);
134
#endif
1473 terminx 135
    // Use an external MIDI player if the user has specified to do so
136
    char *command = getenv("EDUKE32_MUSIC_CMD");
137
    const SDL_version *linked = Mix_Linked_Version();
138
 
2079 helixhorne 139
    UNREFERENCED_PARAMETER(SoundCard);
140
    UNREFERENCED_PARAMETER(Address);
141
 
1663 helixhorne 142
    if (music_initialized)
143
    {
144
        setErrorMessage("Music system is already initialized.");
145
        return(MUSIC_Error);
146
    } // if
147
 
1473 terminx 148
    if (SDL_VERSIONNUM(linked->major,linked->minor,linked->patch) < MIX_REQUIREDVERSION)
149
    {
150
        // reject running with SDL_Mixer versions older than what is stated in sdl_inc.h
151
        initprintf("You need at least v%d.%d.%d of SDL_mixer for music\n",SDL_MIXER_MIN_X,SDL_MIXER_MIN_Y,SDL_MIXER_MIN_Z);
152
        return(MUSIC_Error);
153
    }
154
 
155
    external_midi = (command != NULL && command[0] != 0);
156
 
1598 helixhorne 157
    if (external_midi)
158
    {
3219 hendricks2 159
#if defined FORK_EXEC_MIDI
3134 terminx 160
        int32_t ws=1, numargs=0, pagesize=sysconf(_SC_PAGE_SIZE);
161
        char *c, *cmd;
162
        size_t sz;
3219 hendricks2 163
#endif
3134 terminx 164
 
1708 helixhorne 165
        initprintf("Setting music command to \"%s\".\n", command);
1663 helixhorne 166
 
1725 helixhorne 167
#if !defined FORK_EXEC_MIDI
1598 helixhorne 168
        if (Mix_SetMusicCMD(command)==-1)
1663 helixhorne 169
        {
1598 helixhorne 170
            perror("Mix_SetMusicCMD");
1663 helixhorne 171
            goto fallback;
172
        }
173
#else
174
 
175
        if (pagesize==-1)
176
            goto fallback;
177
 
178
        for (c=command; *c; c++)
179
        {
180
            if (isspace(*c))
181
                ws = 1;
182
            else if (ws)
183
            {
184
                ws = 0;
185
                numargs++;
186
            }
187
        }
188
 
189
        if (numargs==0)
190
            goto fallback;
191
 
192
        sz = (numargs+2)*sizeof(char *) + (c-command+1);
1696 helixhorne 193
        sz = ((sz+pagesize-1)/pagesize)*pagesize;
4027 terminx 194
#if defined(__APPLE__) || defined(__ANDROID__)
1736 helixhorne 195
        external_midi_argv = Bcalloc(1,sz+pagesize);
1663 helixhorne 196
        if (!external_midi_argv)
197
            goto fallback;
1736 helixhorne 198
        external_midi_argv = (char **)((intptr_t)external_midi_argv + (pagesize-(((intptr_t)external_midi_argv)&(pagesize-1))));
2432 hendricks2 199
#else
1725 helixhorne 200
        if (posix_memalign((void **)&external_midi_argv, pagesize, sz))
201
            goto fallback;
2432 hendricks2 202
#endif
1663 helixhorne 203
        cmd = (char *)external_midi_argv + (numargs+2)*sizeof(char *);
204
        Bmemcpy(cmd, command, c-command+1);
205
 
206
        ws = 1;
207
        numargs = 0;
208
        for (c=cmd; *c; c++)
209
        {
210
            if (isspace(*c))
211
            {
212
                ws = 1;
213
                *c = 0;
214
            }
215
            else if (ws)
216
            {
217
                ws = 0;
218
                external_midi_argv[numargs++] = c;
219
            }
220
        }
221
        external_midi_argv[numargs] = external_midi_tempfn;
222
        external_midi_argv[numargs+1] = NULL;
223
 
224
        if (mprotect(external_midi_argv, sz, PROT_READ)==-1)  // make argv and command string read-only
225
        {
226
            perror("MUSIC_Init: mprotect");
227
            goto fallback;
228
        }
4167 helixhorne 229
# if 0
230
        {
231
            int i;
232
            initprintf("----Music argv:\n");
233
            for (i=0; i<numargs+1; i++)
234
                initprintf("  %s\n", external_midi_argv[i]);
235
            initprintf("----\n");
236
        }
237
# endif
1663 helixhorne 238
#endif
239
        music_initialized = 1;
240
        return(MUSIC_Ok);
241
 
242
fallback:
243
        initprintf("Error setting music command, falling back to timidity.\n");
1598 helixhorne 244
    }
1663 helixhorne 245
 
1598 helixhorne 246
    {
1799 helixhorne 247
        static const char *s[] = { "/etc/timidity.cfg", "/etc/timidity/timidity.cfg", "/etc/timidity/freepats.cfg" };
1663 helixhorne 248
        FILE *fp;
1598 helixhorne 249
        int32_t i;
250
 
4385 terminx 251
        for (i = ARRAY_SIZE(s)-1; i>=0; i--)
1473 terminx 252
        {
1567 terminx 253
            fp = Bfopen(s[i], "r");
1473 terminx 254
            if (fp == NULL)
255
            {
1567 terminx 256
                if (i == 0)
1473 terminx 257
                {
1567 terminx 258
                    initprintf("Error: couldn't open any of the following files:\n");
4385 terminx 259
                    for (i = ARRAY_SIZE(s)-1; i>=0; i--)
1567 terminx 260
                        initprintf("%s\n",s[i]);
1473 terminx 261
                    return(MUSIC_Error);
262
                }
263
                continue;
264
            }
265
            else break;
266
        }
267
        Bfclose(fp);
268
    }
269
 
270
    music_initialized = 1;
271
    return(MUSIC_Ok);
272
} // MUSIC_Init
273
 
274
 
275
int32_t MUSIC_Shutdown(void)
276
{
277
    // TODO - make sure this is being called from the menu -- SA
1725 helixhorne 278
#if !defined FORK_EXEC_MIDI
1473 terminx 279
    if (external_midi)
280
        Mix_SetMusicCMD(NULL);
1663 helixhorne 281
#endif
1473 terminx 282
 
283
    MUSIC_StopSong();
284
    music_context = 0;
285
    music_initialized = 0;
286
    music_loopflag = MUSIC_PlayOnce;
287
 
288
    return(MUSIC_Ok);
289
} // MUSIC_Shutdown
290
 
291
 
292
void MUSIC_SetMaxFMMidiChannel(int32_t channel)
293
{
294
    UNREFERENCED_PARAMETER(channel);
295
} // MUSIC_SetMaxFMMidiChannel
296
 
297
 
298
void MUSIC_SetVolume(int32_t volume)
299
{
300
    volume = max(0, volume);
301
    volume = min(volume, 255);
302
 
303
    Mix_VolumeMusic(volume >> 1);  // convert 0-255 to 0-128.
304
} // MUSIC_SetVolume
305
 
306
 
307
void MUSIC_SetMidiChannelVolume(int32_t channel, int32_t volume)
308
{
309
    UNREFERENCED_PARAMETER(channel);
310
    UNREFERENCED_PARAMETER(volume);
311
} // MUSIC_SetMidiChannelVolume
312
 
313
 
314
void MUSIC_ResetMidiChannelVolumes(void)
315
{
316
} // MUSIC_ResetMidiChannelVolumes
317
 
318
 
319
int32_t MUSIC_GetVolume(void)
320
{
321
    return(Mix_VolumeMusic(-1) << 1);  // convert 0-128 to 0-255.
322
} // MUSIC_GetVolume
323
 
324
 
325
void MUSIC_SetLoopFlag(int32_t loopflag)
326
{
327
    music_loopflag = loopflag;
328
} // MUSIC_SetLoopFlag
329
 
330
 
331
int32_t MUSIC_SongPlaying(void)
332
{
333
    return((Mix_PlayingMusic()) ? TRUE : FALSE);
334
} // MUSIC_SongPlaying
335
 
336
 
337
void MUSIC_Continue(void)
338
{
339
    if (Mix_PausedMusic())
340
        Mix_ResumeMusic();
341
} // MUSIC_Continue
342
 
343
 
344
void MUSIC_Pause(void)
345
{
346
    Mix_PauseMusic();
347
} // MUSIC_Pause
348
 
349
int32_t MUSIC_StopSong(void)
350
{
1725 helixhorne 351
#if defined FORK_EXEC_MIDI
1663 helixhorne 352
    if (external_midi)
353
    {
354
        if (external_midi_pid > 0)
355
        {
356
            int32_t ret;
2025 helixhorne 357
            struct timespec ts;
1663 helixhorne 358
 
359
            external_midi_restart = 0;  // make SIGCHLD handler a no-op
360
 
2025 helixhorne 361
            ts.tv_sec = 0;
362
            ts.tv_nsec = 5000000;  // sleep 5ms at most
363
 
1663 helixhorne 364
            kill(external_midi_pid, SIGTERM);
2025 helixhorne 365
            nanosleep(&ts, NULL);
1663 helixhorne 366
            ret = waitpid(external_midi_pid, NULL, WNOHANG|WUNTRACED);
367
//            printf("(%d)", ret);
368
 
369
            if (ret != external_midi_pid)
370
            {
371
                if (ret==-1)
372
                    perror("waitpid");
373
                else
374
                {
375
                    // we tried to be nice, but no...
376
                    kill(external_midi_pid, SIGKILL);
377
                    initprintf("%s: wait for SIGTERM timed out.\n", __func__);
378
                    if (waitpid(external_midi_pid, NULL, WUNTRACED)==-1)
379
                        perror("waitpid (2)");
380
                }
381
            }
382
 
383
            external_midi_pid = -1;
384
        }
385
 
386
        return(MUSIC_Ok);
387
    }
388
#endif
389
 
1473 terminx 390
    //if (!fx_initialized)
391
    if (!Mix_QuerySpec(NULL, NULL, NULL))
392
    {
393
        setErrorMessage("Need FX system initialized, too. Sorry.");
394
        return(MUSIC_Error);
395
    } // if
396
 
397
    if ((Mix_PlayingMusic()) || (Mix_PausedMusic()))
398
        Mix_HaltMusic();
399
 
400
    if (music_musicchunk)
401
        Mix_FreeMusic(music_musicchunk);
402
 
403
    music_musicchunk = NULL;
404
 
405
    return(MUSIC_Ok);
406
} // MUSIC_StopSong
407
 
1725 helixhorne 408
#if defined FORK_EXEC_MIDI
1663 helixhorne 409
static void playmusic()
410
{
411
    pid_t pid = vfork();
412
 
413
    if (pid==-1)  // error
414
    {
415
        initprintf("%s: vfork: %s\n", __func__, strerror(errno));
416
    }
417
    else if (pid==0)  // child
418
    {
4167 helixhorne 419
        // exec without PATH lookup
420
        if (execv(external_midi_argv[0], external_midi_argv) < 0)
1663 helixhorne 421
        {
422
            perror("execv");
423
            _exit(1);
424
        }
425
    }
426
    else  // parent
427
    {
428
        external_midi_pid = pid;
429
    }
430
}
431
 
432
static void sigchld_handler(int signo)
433
{
434
    if (signo==SIGCHLD && external_midi_restart)
435
    {
436
        int status;
437
 
2025 helixhorne 438
        if (external_midi_pid > 0)
439
        {
440
            if (waitpid(external_midi_pid, &status, WUNTRACED)==-1)
441
                perror("waitpid (3)");
1663 helixhorne 442
 
2025 helixhorne 443
            if (WIFEXITED(status) && WEXITSTATUS(status)==0)
444
            {
445
                // loop ...
446
                playmusic();
447
            }
1663 helixhorne 448
        }
449
    }
450
}
451
#endif
452
 
1473 terminx 453
// Duke3D-specific.  --ryan.
454
// void MUSIC_PlayMusic(char *_filename)
455
int32_t MUSIC_PlaySong(char *song, int32_t loopflag)
456
{
4440 terminx 457
//      initprintf("MUSIC_PlaySong");
1473 terminx 458
    MUSIC_StopSong();
459
 
1598 helixhorne 460
    if (external_midi)
461
    {
1663 helixhorne 462
        FILE *fp;
463
 
1725 helixhorne 464
#if defined FORK_EXEC_MIDI
1663 helixhorne 465
        static int32_t sigchld_handler_set = 0;
466
 
467
        if (!sigchld_handler_set)
468
        {
3116 hendricks2 469
            struct sigaction sa;
470
            sa.sa_handler=sigchld_handler;
471
            sa.sa_flags=0;
1663 helixhorne 472
            sigemptyset(&sa.sa_mask);
473
 
474
            if (sigaction(SIGCHLD, &sa, NULL)==-1)
475
                initprintf("%s: sigaction: %s\n", __func__, strerror(errno));
476
 
477
            sigchld_handler_set = 1;
478
        }
479
#endif
480
 
481
        fp = Bfopen(external_midi_tempfn, "wb");
1598 helixhorne 482
        if (fp)
483
        {
484
            fwrite(song, 1, g_musicSize, fp);
485
            Bfclose(fp);
1663 helixhorne 486
 
1725 helixhorne 487
#if defined FORK_EXEC_MIDI
1663 helixhorne 488
            external_midi_restart = loopflag;
489
            playmusic();
490
#else
491
            music_musicchunk = Mix_LoadMUS(external_midi_tempfn);
1598 helixhorne 492
            if (!music_musicchunk)
493
                initprintf("Mix_LoadMUS: %s\n", Mix_GetError());
1663 helixhorne 494
#endif
1598 helixhorne 495
        }
1663 helixhorne 496
        else initprintf("%s: fopen: %s\n", __func__, strerror(errno));
1598 helixhorne 497
    }
498
    else
4074 hendricks2 499
        music_musicchunk = Mix_LoadMUS_RW(SDL_RWFromMem((char *) song, g_musicSize)
500
#if (SDL_MAJOR_VERSION > 1)
501
            , SDL_FALSE
502
#endif
503
            );
1598 helixhorne 504
 
1473 terminx 505
    if (music_musicchunk != NULL)
1598 helixhorne 506
        if (Mix_PlayMusic(music_musicchunk, (loopflag == MUSIC_LoopSong)?-1:0) == -1)
507
            initprintf("Mix_PlayMusic: %s\n", Mix_GetError());
1481 terminx 508
 
1473 terminx 509
    return MUSIC_Ok;
510
}
511
 
512
 
513
void MUSIC_SetContext(int32_t context)
514
{
515
    music_context = context;
516
} // MUSIC_SetContext
517
 
518
 
519
int32_t MUSIC_GetContext(void)
520
{
521
    return(music_context);
522
} // MUSIC_GetContext
523
 
524
 
525
void MUSIC_SetSongTick(uint32_t PositionInTicks)
526
{
527
    UNREFERENCED_PARAMETER(PositionInTicks);
528
} // MUSIC_SetSongTick
529
 
530
 
531
void MUSIC_SetSongTime(uint32_t milliseconds)
532
{
533
    UNREFERENCED_PARAMETER(milliseconds);
534
}// MUSIC_SetSongTime
535
 
536
 
537
void MUSIC_SetSongPosition(int32_t measure, int32_t beat, int32_t tick)
538
{
539
    UNREFERENCED_PARAMETER(measure);
540
    UNREFERENCED_PARAMETER(beat);
541
    UNREFERENCED_PARAMETER(tick);
542
} // MUSIC_SetSongPosition
543
 
544
 
545
void MUSIC_GetSongPosition(songposition *pos)
546
{
547
    UNREFERENCED_PARAMETER(pos);
548
} // MUSIC_GetSongPosition
549
 
550
 
551
void MUSIC_GetSongLength(songposition *pos)
552
{
553
    UNREFERENCED_PARAMETER(pos);
554
} // MUSIC_GetSongLength
555
 
556
 
557
int32_t MUSIC_FadeVolume(int32_t tovolume, int32_t milliseconds)
558
{
559
    UNREFERENCED_PARAMETER(tovolume);
560
    Mix_FadeOutMusic(milliseconds);
561
    return(MUSIC_Ok);
562
} // MUSIC_FadeVolume
563
 
564
 
565
int32_t MUSIC_FadeActive(void)
566
{
567
    return((Mix_FadingMusic() == MIX_FADING_OUT) ? TRUE : FALSE);
568
} // MUSIC_FadeActive
569
 
570
 
571
void MUSIC_StopFade(void)
572
{
573
} // MUSIC_StopFade
574
 
575
 
1662 terminx 576
void MUSIC_RerouteMidiChannel(int32_t channel, int32_t (*function)(int32_t, int32_t, int32_t))
1473 terminx 577
{
578
    UNREFERENCED_PARAMETER(channel);
579
    UNREFERENCED_PARAMETER(function);
580
} // MUSIC_RerouteMidiChannel
581
 
582
 
583
void MUSIC_RegisterTimbreBank(char *timbres)
584
{
585
    UNREFERENCED_PARAMETER(timbres);
586
} // MUSIC_RegisterTimbreBank
587
 
588
 
589
void MUSIC_Update(void)
590
{}