diff --git a/src/sound/sound_server.cpp b/src/sound/sound_server.cpp index daa41e8b0..337cf1424 100644 --- a/src/sound/sound_server.cpp +++ b/src/sound/sound_server.cpp @@ -86,8 +86,17 @@ static struct { } MusicChannel; static void ChannelFinished(int channel); -static int *MixerBuffer; -static int MixerBufferSize; + +static struct { + SDL_AudioSpec Format; + SDL_mutex *Lock; + SDL_cond *Cond; + SDL_Thread *Thread; + + int *MixerBuffer; + Uint8 *Buffer; + bool Running; +} Audio; /*---------------------------------------------------------------------------- @@ -282,24 +291,18 @@ static void ClipMixToStereo16(const int *mix, int size, short *output) */ static void MixIntoBuffer(void *buffer, int samples) { - if (samples > MixerBufferSize) { - delete[] MixerBuffer; - MixerBuffer = new int[samples]; - MixerBufferSize = samples; - } - // FIXME: can save the memset here, if first channel sets the values - memset(MixerBuffer, 0, samples * sizeof(*MixerBuffer)); + memset(Audio.MixerBuffer, 0, samples * sizeof(*Audio.MixerBuffer)); if (EffectsEnabled) { // Add channels to mixer buffer - MixChannelsToStereo32(MixerBuffer, samples); + MixChannelsToStereo32(Audio.MixerBuffer, samples); } if (MusicEnabled) { // Add music to mixer buffer - MixMusicToStereo32(MixerBuffer, samples); + MixMusicToStereo32(Audio.MixerBuffer, samples); } - ClipMixToStereo16(MixerBuffer, samples, (short *)buffer); + ClipMixToStereo16(Audio.MixerBuffer, samples, (short *)buffer); } /** @@ -313,8 +316,34 @@ static void MixIntoBuffer(void *buffer, int samples) */ static void FillAudio(void *, Uint8 *stream, int len) { - len >>= 1; - MixIntoBuffer(stream, len); + Assert(len != Audio.Format.size); + SDL_memset(stream, 0, len); + + SDL_LockMutex(Audio.Lock); + SDL_MixAudio(stream, Audio.Buffer, len, SDL_MIX_MAXVOLUME); + + // Signal our FillThread, we can fill the Audio.Buffer again + SDL_CondSignal(Audio.Cond); + SDL_UnlockMutex(Audio.Lock); +} + +/** +** Fill audio thread. +*/ +static int FillThread(void *) +{ + while (Audio.Running == true) { + SDL_LockMutex(Audio.Lock); + int ret = SDL_CondWaitTimeout(Audio.Cond, Audio.Lock, 100); + if (ret == 0) { + SDL_LockAudio(); + MixIntoBuffer(Audio.Buffer, Audio.Format.samples * Audio.Format.channels); + SDL_UnlockAudio(); + } + SDL_UnlockMutex(Audio.Lock); + } + + return 0; } /*---------------------------------------------------------------------------- @@ -406,12 +435,12 @@ int SetChannelVolume(int channel, int volume) if (volume < 0) { volume = Channels[channel].Volume; } else { - SDL_LockAudio(); + SDL_LockMutex(Audio.Lock); volume = std::min(MaxVolume, volume); Channels[channel].Volume = volume; - SDL_UnlockAudio(); + SDL_UnlockMutex(Audio.Lock); } return volume; } @@ -436,9 +465,9 @@ int SetChannelStereo(int channel, int stereo) if (stereo < -128 || stereo > 127) { stereo = Channels[channel].Stereo; } else { - SDL_LockAudio(); + SDL_LockMutex(Audio.Lock); Channels[channel].Stereo = stereo; - SDL_UnlockAudio(); + SDL_UnlockMutex(Audio.Lock); } return stereo; } @@ -475,13 +504,13 @@ CSample *GetChannelSample(int channel) */ void StopChannel(int channel) { - SDL_LockAudio(); + SDL_LockMutex(Audio.Lock); if (channel >= 0 && channel < MaxChannels) { if (Channels[channel].Playing) { ChannelFinished(channel); } } - SDL_UnlockAudio(); + SDL_UnlockMutex(Audio.Lock); } /** @@ -489,13 +518,13 @@ void StopChannel(int channel) */ void StopAllChannels() { - SDL_LockAudio(); + SDL_LockMutex(Audio.Lock); for (int i = 0; i < MaxChannels; ++i) { if (Channels[i].Playing) { ChannelFinished(i); } } - SDL_UnlockAudio(); + SDL_UnlockMutex(Audio.Lock); } static CSample *LoadSample(const char *name, enum _play_audio_flags_ flag) @@ -558,11 +587,11 @@ int PlaySample(CSample *sample, Origin *origin) { int channel = -1; - SDL_LockAudio(); + SDL_LockMutex(Audio.Lock); if (SoundEnabled() && EffectsEnabled && sample && NextFreeChannel != MaxChannels) { channel = FillChannel(sample, EffectsVolume, 0, origin); } - SDL_UnlockAudio(); + SDL_UnlockMutex(Audio.Lock); return channel; } @@ -684,10 +713,10 @@ void StopMusic() if (MusicPlaying) { MusicPlaying = false; if (MusicChannel.Sample) { - SDL_LockAudio(); + SDL_LockMutex(Audio.Lock); delete MusicChannel.Sample; MusicChannel.Sample = NULL; - SDL_UnlockAudio(); + SDL_UnlockMutex(Audio.Lock); } } } @@ -779,7 +808,7 @@ static int InitSdlSound(int freq, int size) wanted.userdata = NULL; // Open the audio device, forcing the desired format - if (SDL_OpenAudio(&wanted, NULL) < 0) { + if (SDL_OpenAudio(&wanted, &Audio.Format) < 0) { fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError()); return -1; } @@ -809,6 +838,18 @@ int InitSound() for (int i = 0; i < MaxChannels; ++i) { Channels[i].Point = i + 1; } + + // Create mutex and cond for FillThread + Audio.MixerBuffer = new int[Audio.Format.samples * Audio.Format.channels]; + memset(Audio.MixerBuffer, 0, Audio.Format.samples * Audio.Format.channels * sizeof(int)); + Audio.Buffer = new Uint8[Audio.Format.size]; + memset(Audio.Buffer, 0, Audio.Format.size); + Audio.Lock = SDL_CreateMutex(); + Audio.Cond = SDL_CreateCond(); + Audio.Running = true; + + // Create thread to fill sdl audio buffer + Audio.Thread = SDL_CreateThread(FillThread, NULL); return 0; } @@ -817,10 +858,18 @@ int InitSound() */ void QuitSound() { + Audio.Running = false; + SDL_KillThread(Audio.Thread); + + SDL_DestroyCond(Audio.Cond); + SDL_DestroyMutex(Audio.Lock); + SDL_CloseAudio(); SoundInitialized = false; - delete[] MixerBuffer; - MixerBuffer = NULL; + delete[] Audio.MixerBuffer; + Audio.MixerBuffer = NULL; + delete[] Audio.Buffer; + Audio.Buffer = NULL; #ifdef USE_FLUIDSYNTH CleanFluidSynth(); #endif