/* * Nestopia UE * * Copyright (C) 2012-2014 R. Danbrook * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * */ #include #include #include #include "config.h" #include "audio.h" #ifndef _MINGW #include ao_device *aodevice; ao_sample_format format; #endif extern settings_t conf; extern Emulator emulator; extern bool nst_pal; extern bool updateok; SDL_AudioSpec spec, obtained; SDL_AudioDeviceID dev; int16_t audiobuf[96000]; int framerate, channels, bufsize; bool altspeed = false; void audio_play() { bufsize = 2 * channels * (conf.audio_sample_rate / framerate); if (conf.audio_api == 0) { // SDL #if SDL_VERSION_ATLEAST(2,0,4) SDL_QueueAudio(dev, (const void*)audiobuf, bufsize); // Clear the audio queue arbitrarily to avoid it backing up too far if (SDL_GetQueuedAudioSize(dev) > (Uint32)(bufsize * 3)) { SDL_ClearQueuedAudio(dev); } #endif } #ifndef _MINGW else if (conf.audio_api == 1) { // libao ao_play(aodevice, (char*)audiobuf, bufsize); } #endif updateok = true; } void audio_init() { // Initialize audio device // Set the framerate based on the region. For PAL: (60 / 6) * 5 = 50 framerate = nst_pal ? (conf.timing_speed / 6) * 5 : conf.timing_speed; channels = conf.audio_stereo ? 2 : 1; memset(audiobuf, 0, sizeof(audiobuf)); #ifdef _MINGW conf.audio_api = 0; // Set SDL audio for MinGW #endif #if SDL_VERSION_ATLEAST(2,0,4) #else // Force libao if SDL lib is not modern enough if (conf.audio_api == 0) { conf.audio_api = 1; fprintf(stderr, "Audio: Forcing libao\n"); } #endif if (conf.audio_api == 0) { // SDL spec.freq = conf.audio_sample_rate; spec.format = AUDIO_S16SYS; spec.channels = channels; spec.silence = 0; spec.samples = 512; spec.userdata = 0; spec.callback = NULL; // Use SDL_QueueAudio instead dev = SDL_OpenAudioDevice(NULL, 0, &spec, &obtained, SDL_AUDIO_ALLOW_ANY_CHANGE); if (!dev) { fprintf(stderr, "Error opening audio device.\n"); } else { fprintf(stderr, "Audio: SDL - %dHz %d-bit, %d channel(s)\n", spec.freq, 16, spec.channels); } SDL_PauseAudioDevice(dev, 1); // Setting to 0 unpauses } #ifndef _MINGW else if (conf.audio_api == 1) { // libao ao_initialize(); int default_driver = ao_default_driver_id(); memset(&format, 0, sizeof(format)); format.bits = 16; format.channels = channels; format.rate = conf.audio_sample_rate; format.byte_format = AO_FMT_NATIVE; aodevice = ao_open_live(default_driver, &format, NULL); if (aodevice == NULL) { fprintf(stderr, "Error opening audio device.\n"); aodevice = ao_open_live(ao_driver_id("null"), &format, NULL); } else { fprintf(stderr, "Audio: libao - %dHz, %d-bit, %d channel(s)\n", format.rate, format.bits, format.channels); } } #endif } void audio_deinit() { // Deinitialize audio if (conf.audio_api == 0) { // SDL SDL_CloseAudioDevice(dev); } #ifndef _MINGW else if (conf.audio_api == 1) { // libao ao_close(aodevice); ao_shutdown(); } #endif } void audio_pause() { // Pause the SDL audio device if (conf.audio_api == 0) { // SDL SDL_PauseAudioDevice(dev, 1); } } void audio_unpause() { // Unpause the SDL audio device if (conf.audio_api == 0) { // SDL SDL_PauseAudioDevice(dev, 0); } } void audio_set_params(Sound::Output *soundoutput) { // Set audio parameters Sound sound(emulator); sound.SetSampleBits(16); sound.SetSampleRate(conf.audio_sample_rate); sound.SetSpeaker(conf.audio_stereo ? Sound::SPEAKER_STEREO : Sound::SPEAKER_MONO); sound.SetSpeed(Sound::DEFAULT_SPEED); audio_adj_volume(); soundoutput->samples[0] = audiobuf; soundoutput->length[0] = conf.audio_sample_rate / framerate; soundoutput->samples[1] = NULL; soundoutput->length[1] = 0; } void audio_adj_volume() { // Adjust the audio volume to the current settings Sound sound(emulator); sound.SetVolume(Sound::ALL_CHANNELS, conf.audio_volume); sound.SetVolume(Sound::CHANNEL_SQUARE1, conf.audio_vol_sq1); sound.SetVolume(Sound::CHANNEL_SQUARE2, conf.audio_vol_sq2); sound.SetVolume(Sound::CHANNEL_TRIANGLE, conf.audio_vol_tri); sound.SetVolume(Sound::CHANNEL_NOISE, conf.audio_vol_noise); sound.SetVolume(Sound::CHANNEL_DPCM, conf.audio_vol_dpcm); sound.SetVolume(Sound::CHANNEL_FDS, conf.audio_vol_fds); sound.SetVolume(Sound::CHANNEL_MMC5, conf.audio_vol_mmc5); sound.SetVolume(Sound::CHANNEL_VRC6, conf.audio_vol_vrc6); sound.SetVolume(Sound::CHANNEL_VRC7, conf.audio_vol_vrc7); sound.SetVolume(Sound::CHANNEL_N163, conf.audio_vol_n163); sound.SetVolume(Sound::CHANNEL_S5B, conf.audio_vol_s5b); } // Timing Functions bool timing_frameskip() { // Calculate whether to skip a frame or not if (conf.audio_api == 0) { // SDL // Wait until the audio is drained #if SDL_VERSION_ATLEAST(2,0,4) while (SDL_GetQueuedAudioSize(dev) > (Uint32)bufsize) { if (conf.timing_limiter) { SDL_Delay(1); } } #endif } static int flipper = 1; if (altspeed) { if (flipper > 2) { flipper = 0; return false; } else { flipper++; return true; } } return false; } void timing_set_default() { // Set the framerate to the default altspeed = false; framerate = nst_pal ? (conf.timing_speed / 6) * 5 : conf.timing_speed; #if SDL_VERSION_ATLEAST(2,0,4) if (conf.audio_api == 0) { SDL_ClearQueuedAudio(dev); } #endif } void timing_set_altspeed() { // Set the framerate to the alternate speed altspeed = true; framerate = conf.timing_altspeed; }