rlm@1: // VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. rlm@1: // Copyright (C) 2008 VBA-M development team rlm@1: rlm@1: // This program is free software; you can redistribute it and/or modify rlm@1: // it under the terms of the GNU General Public License as published by rlm@1: // the Free Software Foundation; either version 2, or(at your option) rlm@1: // any later version. rlm@1: // rlm@1: // This program is distributed in the hope that it will be useful, rlm@1: // but WITHOUT ANY WARRANTY; without even the implied warranty of rlm@1: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the rlm@1: // GNU General Public License for more details. rlm@1: // rlm@1: // You should have received a copy of the GNU General Public License rlm@1: // along with this program; if not, write to the Free Software Foundation, rlm@1: // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. rlm@1: rlm@1: #include "SoundSDL.h" rlm@1: rlm@1: extern int emulating; rlm@1: extern bool speedup; rlm@1: rlm@1: // Hold up to 100 ms of data in the ring buffer rlm@1: const float SoundSDL::_delay = 0.1f; rlm@1: rlm@1: SoundSDL::SoundSDL(): rlm@1: _rbuf(0), rlm@1: _initialized(false) rlm@1: { rlm@1: rlm@1: } rlm@1: rlm@1: void SoundSDL::soundCallback(void *data, u8 *stream, int len) rlm@1: { rlm@1: reinterpret_cast(data)->read(reinterpret_cast(stream), len); rlm@1: } rlm@1: rlm@1: void SoundSDL::read(u16 * stream, int length) rlm@1: { rlm@1: if (!_initialized || length <= 0 || !emulating) rlm@1: return; rlm@1: rlm@1: SDL_mutexP(_mutex); rlm@1: _rbuf.read(stream, std::min(static_cast(length) / 2, _rbuf.used())); rlm@1: rlm@1: SDL_CondSignal(_cond); rlm@1: SDL_mutexV(_mutex); rlm@1: } rlm@1: rlm@1: void SoundSDL::write(u16 * finalWave, int length) rlm@1: { rlm@1: if (!_initialized) rlm@1: return; rlm@1: rlm@1: if (SDL_GetAudioStatus() != SDL_AUDIO_PLAYING) rlm@1: SDL_PauseAudio(0); rlm@1: rlm@1: SDL_mutexP(_mutex); rlm@1: rlm@1: unsigned int samples = length / 4; rlm@1: rlm@1: std::size_t avail; rlm@1: while ((avail = _rbuf.avail() / 2) < samples) rlm@1: { rlm@1: _rbuf.write(finalWave, avail * 2); rlm@1: rlm@1: finalWave += avail * 2; rlm@1: samples -= avail; rlm@1: rlm@1: // If emulating and not in speed up mode, synchronize to audio rlm@1: // by waiting till there is enough room in the buffer rlm@1: if (emulating && !speedup) rlm@1: { rlm@1: SDL_CondWait(_cond,_mutex); rlm@1: } rlm@1: else rlm@1: { rlm@1: // Drop the remaining of the audio data rlm@1: SDL_mutexV(_mutex); rlm@1: return; rlm@1: } rlm@1: } rlm@1: rlm@1: _rbuf.write(finalWave, samples * 2); rlm@1: rlm@1: SDL_mutexV(_mutex); rlm@1: } rlm@1: rlm@1: rlm@1: bool SoundSDL::init() rlm@1: { rlm@1: SDL_AudioSpec audio; rlm@1: audio.freq = SDL_SAMPLE_RATE; rlm@1: audio.format = AUDIO_S16SYS; rlm@1: audio.channels = 2; rlm@1: audio.samples = 1024; rlm@1: audio.callback = soundCallback; rlm@1: audio.userdata = this; rlm@1: rlm@1: if(SDL_OpenAudio(&audio, NULL)) rlm@1: { rlm@1: fprintf(stderr,"Failed to open audio: %s\n", SDL_GetError()); rlm@1: return false; rlm@1: } rlm@1: rlm@1: _rbuf.reset(_delay * SDL_SAMPLE_RATE * 2); rlm@1: rlm@1: _cond = SDL_CreateCond(); rlm@1: _mutex = SDL_CreateMutex(); rlm@1: _initialized = true; rlm@1: rlm@1: return true; rlm@1: } rlm@1: rlm@1: SoundSDL::~SoundSDL() rlm@1: { rlm@1: if (!_initialized) rlm@1: return; rlm@1: rlm@1: SDL_mutexP(_mutex); rlm@1: int iSave = emulating; rlm@1: emulating = 0; rlm@1: SDL_CondSignal(_cond); rlm@1: SDL_mutexV(_mutex); rlm@1: rlm@1: SDL_DestroyCond(_cond); rlm@1: _cond = NULL; rlm@1: rlm@1: SDL_DestroyMutex(_mutex); rlm@1: _mutex = NULL; rlm@1: rlm@1: SDL_CloseAudio(); rlm@1: rlm@1: emulating = iSave; rlm@1: } rlm@1: rlm@1: void SoundSDL::pause() rlm@1: { rlm@1: if (!_initialized) rlm@1: return; rlm@1: rlm@1: SDL_PauseAudio(1); rlm@1: } rlm@1: rlm@1: void SoundSDL::resume() rlm@1: { rlm@1: if (!_initialized) rlm@1: return; rlm@1: rlm@1: SDL_PauseAudio(0); rlm@1: } rlm@1: rlm@1: void SoundSDL::reset() rlm@1: { rlm@1: } rlm@1: rlm@1: bool SoundSDL::setThrottle(unsigned short throttle){ rlm@1: switch(throttle){ rlm@1: case 25: rlm@1: case 50: rlm@1: case 100: rlm@1: case 200: rlm@1: case 400: rlm@1: break; rlm@1: default: rlm@1: return false; rlm@1: } rlm@1: SDL_CloseAudio(); rlm@1: SDL_AudioSpec audio; rlm@1: audio.freq = SDL_SAMPLE_RATE*throttle/100; rlm@1: audio.format = AUDIO_S16SYS; rlm@1: audio.channels = 2; rlm@1: audio.samples = 1024; rlm@1: audio.callback = soundCallback; rlm@1: audio.userdata = this; rlm@1: _rbuf.reset((_delay * SDL_SAMPLE_RATE * throttle * 2)/100); rlm@1: return !SDL_OpenAudio(&audio,NULL); rlm@1: }