ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

sdl-mixer.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/scummsys.h"
00024 
00025 #if defined(SDL_BACKEND)
00026 
00027 #include "backends/mixer/sdl/sdl-mixer.h"
00028 #include "common/debug.h"
00029 #include "common/system.h"
00030 #include "common/config-manager.h"
00031 #include "common/textconsole.h"
00032 
00033 #if defined(GP2X)
00034 #define SAMPLES_PER_SEC 11025
00035 #elif defined(PLAYSTATION3) || defined(PSP2)
00036 #define SAMPLES_PER_SEC 48000
00037 #else
00038 #define SAMPLES_PER_SEC 44100
00039 #endif
00040 
00041 SdlMixerManager::SdlMixerManager()
00042     :
00043     _mixer(0),
00044     _audioSuspended(false) {
00045 
00046 }
00047 
00048 SdlMixerManager::~SdlMixerManager() {
00049     _mixer->setReady(false);
00050 
00051     SDL_CloseAudio();
00052 
00053     delete _mixer;
00054 }
00055 
00056 void SdlMixerManager::init() {
00057     // Start SDL Audio subsystem
00058     if (SDL_InitSubSystem(SDL_INIT_AUDIO) == -1) {
00059         error("Could not initialize SDL: %s", SDL_GetError());
00060     }
00061 
00062 #if SDL_VERSION_ATLEAST(2, 0, 0)
00063     const char *sdlDriverName = SDL_GetCurrentAudioDriver();
00064 #else
00065     const int maxNameLen = 20;
00066     char sdlDriverName[maxNameLen];
00067     sdlDriverName[0] = '\0';
00068     SDL_AudioDriverName(sdlDriverName, maxNameLen);
00069 #endif
00070     debug(1, "Using SDL Audio Driver \"%s\"", sdlDriverName);
00071 
00072     // Get the desired audio specs
00073     SDL_AudioSpec desired = getAudioSpec(SAMPLES_PER_SEC);
00074 
00075     // Needed as SDL_OpenAudio as of SDL-1.2.14 mutates fields in
00076     // "desired" if used directly.
00077     SDL_AudioSpec fmt = desired;
00078 
00079     // Start SDL audio with the desired specs
00080     if (SDL_OpenAudio(&fmt, &_obtained) != 0) {
00081         warning("Could not open audio device: %s", SDL_GetError());
00082 
00083         // The mixer is not marked as ready
00084         _mixer = new Audio::MixerImpl(g_system, desired.freq);
00085         return;
00086     }
00087 
00088     // The obtained sample format is not supported by the mixer, call
00089     // SDL_OpenAudio again with NULL as the second argument to force
00090     // SDL to do resampling to the desired audio spec.
00091     if (_obtained.format != desired.format) {
00092         debug(1, "SDL mixer sound format: %d differs from desired: %d", _obtained.format, desired.format);
00093         SDL_CloseAudio();
00094 
00095         if (SDL_OpenAudio(&fmt, NULL) != 0) {
00096             warning("Could not open audio device: %s", SDL_GetError());
00097 
00098             // The mixer is not marked as ready
00099             _mixer = new Audio::MixerImpl(g_system, desired.freq);
00100             return;
00101         }
00102 
00103         _obtained = desired;
00104     }
00105 
00106     debug(1, "Output sample rate: %d Hz", _obtained.freq);
00107     if (_obtained.freq != desired.freq)
00108         warning("SDL mixer output sample rate: %d differs from desired: %d", _obtained.freq, desired.freq);
00109 
00110     debug(1, "Output buffer size: %d samples", _obtained.samples);
00111     if (_obtained.samples != desired.samples)
00112         warning("SDL mixer output buffer size: %d differs from desired: %d", _obtained.samples, desired.samples);
00113 
00114 #ifndef __SYMBIAN32__
00115     // The SymbianSdlMixerManager does stereo->mono downmixing,
00116     // but otherwise we require stereo output.
00117     if (_obtained.channels != 2)
00118         error("SDL mixer output requires stereo output device");
00119 #endif
00120 
00121     _mixer = new Audio::MixerImpl(g_system, _obtained.freq);
00122     assert(_mixer);
00123     _mixer->setReady(true);
00124 
00125     startAudio();
00126 }
00127 
00128 static uint32 roundDownPowerOfTwo(uint32 samples) {
00129     // Public domain code from Sean Eron Anderson
00130     // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
00131     uint32 rounded = samples;
00132     --rounded;
00133     rounded |= rounded >> 1;
00134     rounded |= rounded >> 2;
00135     rounded |= rounded >> 4;
00136     rounded |= rounded >> 8;
00137     rounded |= rounded >> 16;
00138     ++rounded;
00139 
00140     if (rounded != samples)
00141         rounded >>= 1;
00142 
00143     return rounded;
00144 }
00145 
00146 SDL_AudioSpec SdlMixerManager::getAudioSpec(uint32 outputRate) {
00147     SDL_AudioSpec desired;
00148 
00149     const char *const appDomain = Common::ConfigManager::kApplicationDomain;
00150 
00151     // There was once a GUI option for this, but it was never used;
00152     // configurability is retained for advanced users only who wish to modify
00153     // their ScummVM config file directly
00154     uint32 freq = 0;
00155     if (ConfMan.hasKey("output_rate", appDomain))
00156         freq = ConfMan.getInt("output_rate", appDomain);
00157     if (freq <= 0)
00158         freq = outputRate;
00159 
00160     // One SDL "sample" is a complete audio frame (i.e. all channels = 1 sample)
00161     uint32 samples = 0;
00162 
00163     // Different games and host systems have different performance
00164     // characteristics which are not easily measured, so allow advanced users to
00165     // tweak their audio buffer size if they are experience excess latency or
00166     // drop-outs by setting this value in their ScummVM config file directly
00167     if (ConfMan.hasKey("audio_buffer_size", appDomain))
00168         samples = ConfMan.getInt("audio_buffer_size", appDomain);
00169 
00170     // 256 is an arbitrary minimum; 32768 is the largest power-of-two value
00171     // representable with uint16
00172     if (samples < 256 || samples > 32768)
00173         // By default, hold no more than 45ms worth of samples to avoid
00174         // perceptable audio lag (ATSC IS-191). For reference, DOSBox (as of Sep
00175         // 2017) uses a buffer size of 1024 samples by default for a 16-bit
00176         // stereo 44kHz mixer, which happens to be the next lowest power of two
00177         // below 45ms.
00178         samples = freq / (1000.0 / 45);
00179 
00180     memset(&desired, 0, sizeof(desired));
00181     desired.freq = freq;
00182     desired.format = AUDIO_S16SYS;
00183     desired.channels = 2;
00184     desired.samples = roundDownPowerOfTwo(samples);
00185     desired.callback = sdlCallback;
00186     desired.userdata = this;
00187 
00188     return desired;
00189 }
00190 
00191 void SdlMixerManager::startAudio() {
00192     // Start the sound system
00193     SDL_PauseAudio(0);
00194 }
00195 
00196 void SdlMixerManager::callbackHandler(byte *samples, int len) {
00197     assert(_mixer);
00198     _mixer->mixCallback(samples, len);
00199 }
00200 
00201 void SdlMixerManager::sdlCallback(void *this_, byte *samples, int len) {
00202     SdlMixerManager *manager = (SdlMixerManager *)this_;
00203     assert(manager);
00204 
00205     manager->callbackHandler(samples, len);
00206 }
00207 
00208 void SdlMixerManager::suspendAudio() {
00209     SDL_CloseAudio();
00210     _audioSuspended = true;
00211 }
00212 
00213 int SdlMixerManager::resumeAudio() {
00214     if (!_audioSuspended)
00215         return -2;
00216     if (SDL_OpenAudio(&_obtained, NULL) < 0) {
00217         return -1;
00218     }
00219     SDL_PauseAudio(0);
00220     _audioSuspended = false;
00221     return 0;
00222 }
00223 
00224 #endif


Generated on Sat Feb 23 2019 05:01:16 for ResidualVM by doxygen 1.7.1
curved edge   curved edge