--- a/src/MacSrc/SDLSound.c +++ b/src/MacSrc/SDLSound.c @@ -19,24 +19,6 @@ // Startup the sound system - SDL_AudioSpec spec, obtained; - spec.freq = 48000; - spec.format = AUDIO_S16SYS; - spec.channels = 2; - spec.samples = 2048; - spec.callback = AudioStreamCallback; - spec.userdata = (void *)&cutscene_audiostream; - - extern SDL_AudioDeviceID device; - device = SDL_OpenAudioDevice(NULL, 0, &spec, &obtained, 0); - - if (device == 0) { - ERROR("Could not open SDL audio: %s", SDL_GetError()); - } else { - INFO("Opened Music Stream, deviceID %d, freq %d, size %d, format %d, channels %d, samples %d", device, - obtained.freq, obtained.size, obtained.format, obtained.channels, obtained.samples); - } - if (Mix_Init(MIX_INIT_MP3) < 0) { ERROR("%s: Init failed", __FUNCTION__); } @@ -50,10 +32,18 @@ Mix_HookMusic(MusicCallback, (void *)&MusicDev); Mix_VolumeMusic(MIX_MAX_VOLUME); // use max volume for music stream + // Cutscene/audiolog playback (AudioStreamCallback, see cutsloop.c) used to + // run through its own SDL_OpenAudioDevice() device, separate from this + // one - SDL only ever allows one open non-capture device on Android's + // audio backends, so that second open always failed with "Audio device + // already open" and both SFX and MIDI music (both routed through this + // Mix_OpenAudio device) silently never played. Mixed into this device's + // post-mix stage instead, so there is only ever one device open. + Mix_SetPostMix(AudioStreamCallback, (void *)&cutscene_audiostream); + InitReadXMI(); atexit(Mix_CloseAudio); - atexit(SDL_CloseAudio); return OK; } --- a/src/MacSrc/Shock.c +++ b/src/MacSrc/Shock.c @@ -60,8 +60,6 @@ SDL_Palette *sdlPalette; SDL_Renderer *renderer; -SDL_AudioDeviceID device; - int num_args; char **arg_values; --- a/src/GameSrc/cutsloop.c +++ b/src/GameSrc/cutsloop.c @@ -83,15 +83,25 @@ extern char FrnSubtitle[256]; extern char GerSubtitle[256]; -extern SDL_AudioDeviceID device; - - +// Runs as SDL_mixer's post-mix hook (see snd_start_digital() in +// SDLSound.c) rather than a dedicated SDL audio device's callback, so +// SFX/MIDI music sharing the same device are already mixed into `stream` +// by the time this runs - mix in rather than overwrite, so they aren't +// silenced whenever a cutscene or audiolog is playing. void AudioStreamCallback(void *userdata, unsigned char *stream, int len) { + static Uint8 mixbuf[MOVIE_DEFAULT_BLOCKLEN * 4]; SDL_AudioStream *as = *(SDL_AudioStream **)userdata; - if (as != NULL && SDL_AudioStreamAvailable(as) > 0) - SDL_AudioStreamGet(as, stream, len); + if (as == NULL || SDL_AudioStreamAvailable(as) <= 0) + return; + + if (len > (int)sizeof(mixbuf)) + len = (int)sizeof(mixbuf); + + int got = SDL_AudioStreamGet(as, mixbuf, len); + if (got > 0) + SDL_MixAudioFormat(stream, mixbuf, AUDIO_S16SYS, got, SDL_MIX_MAXVOLUME); } @@ -153,11 +163,13 @@ if (cutscene_audiostream != NULL) { - SDL_PauseAudioDevice(device, 1); - SDL_Delay(1); - + // AudioStreamCallback (SDL_mixer's post-mix hook) runs continuously on + // SDL_mixer's own audio thread and reads this pointer - lock around + // freeing it so that thread can't be mid-read when it's freed. + SDL_LockAudio(); SDL_FreeAudioStream(cutscene_audiostream); cutscene_audiostream = NULL; + SDL_UnlockAudio(); if (cutscene_audiobuffer) { @@ -184,8 +196,6 @@ if (cutscene_audiostream) { - SDL_PauseAudioDevice(device, 0); - if (cutscene_audiobuffer_size > 0) { // === adjust volume in buffer here === @@ -325,10 +335,9 @@ if (sfx_on) { - SDL_PauseAudioDevice(device, 1); - SDL_Delay(1); - + SDL_LockAudio(); cutscene_audiostream = SDL_NewAudioStream(AUDIO_U8, 1, fix_int(amovie->a.sampleRate), AUDIO_S16SYS, 2, 48000); + SDL_UnlockAudio(); cutscene_audiobuffer_pos = cutscene_audiobuffer; } --- a/src/GameSrc/audiolog.c +++ b/src/GameSrc/audiolog.c @@ -61,8 +61,6 @@ extern uchar curr_alog_vol; extern char which_lang; -extern SDL_AudioDeviceID device; - errtype audiolog_init(void) { return OK; } errtype audiolog_play(int email_id) { @@ -114,10 +112,9 @@ DEBUG("%s: Playing email", __FUNCTION__); - SDL_PauseAudioDevice(device, 1); - SDL_Delay(1); - + SDL_LockAudio(); cutscene_audiostream = SDL_NewAudioStream(AUDIO_U8, 1, fix_int(palog->a.sampleRate), AUDIO_S16SYS, 2, 48000); + SDL_UnlockAudio(); audiolog_audiobuffer_pos = audiolog_audiobuffer; @@ -150,11 +147,14 @@ } if (cutscene_audiostream != NULL) { - SDL_PauseAudioDevice(device, 1); - SDL_Delay(1); - + // AudioStreamCallback (SDL_mixer's post-mix hook, see cutsloop.c) + // runs continuously on SDL_mixer's own audio thread and reads this + // pointer - lock around freeing it so that thread can't be + // mid-read when it's freed. + SDL_LockAudio(); SDL_FreeAudioStream(cutscene_audiostream); cutscene_audiostream = NULL; + SDL_UnlockAudio(); if (audiolog_audiobuffer) { free(audiolog_audiobuffer); @@ -177,8 +177,6 @@ errtype audiolog_loop_callback(void) { if (cutscene_audiostream) { - SDL_PauseAudioDevice(device, 0); - if (audiolog_audiobuffer_size > 0) { int i, vol = curr_alog_vol * 127 / 100; // convert from 0-100 to 0-127