diff options
author | Arisotura <thetotalworm@gmail.com> | 2020-05-02 20:25:39 +0200 |
---|---|---|
committer | Arisotura <thetotalworm@gmail.com> | 2020-05-02 20:25:39 +0200 |
commit | aa4344e249d855bfc2ddd52af61d3213d9e99db3 (patch) | |
tree | 18be264e3a75ad5f0872e444fecdfea404518768 | |
parent | 690f39ca3382f1e82a27c8a16dde1a4379f978f8 (diff) |
add audio output. HARK HARK HARK
-rw-r--r-- | src/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/frontend/FrontendUtil.h | 13 | ||||
-rw-r--r-- | src/frontend/Util_Audio.cpp | 77 | ||||
-rw-r--r-- | src/frontend/qt_sdl/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/frontend/qt_sdl/main.cpp | 96 |
5 files changed, 184 insertions, 6 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dca0ca9..245e6a2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,7 @@ add_library(core STATIC ARCodeList.cpp AREngine.cpp ARM.cpp + ARM_InstrTable.h ARMInterpreter.cpp ARMInterpreter_ALU.cpp ARMInterpreter_Branch.cpp @@ -12,11 +13,13 @@ add_library(core STATIC CP15.cpp CRC32.cpp DMA.cpp + FIFO.h GBACart.cpp GPU.cpp GPU2D.cpp GPU3D.cpp GPU3D_OpenGL.cpp + GPU3D_OpenGL_shaders.h GPU3D_Soft.cpp NDS.cpp NDSCart.cpp diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index d9f5c58..32f28d1 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -63,6 +63,19 @@ bool SaveState(const char* filename); // undo the latest savestate load void UndoStateLoad(); + +// initialize the audio utility +void Init_Audio(int outputfreq); + +// get how many samples to read from the core audio output +// based on how many are needed by the frontend (outlen in samples) +int AudioOut_GetNumSamples(int outlen); + +// resample audio from the core audio output to match the frontend's +// output frequency, and apply user-specified volume +// note: this assumes the output buffer is interleaved stereo +void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen); + } #endif // FRONTENDUTIL_H diff --git a/src/frontend/Util_Audio.cpp b/src/frontend/Util_Audio.cpp new file mode 100644 index 0000000..fe0ecab --- /dev/null +++ b/src/frontend/Util_Audio.cpp @@ -0,0 +1,77 @@ +/* + Copyright 2016-2020 Arisotura + + This file is part of melonDS. + + melonDS 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 3 of the License, or (at your option) + any later version. + + melonDS 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 melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include <stdio.h> +#include <string.h> +#include <math.h> + +#include "FrontendUtil.h" +#include "Config.h" +#include "qt_sdl/PlatformConfig.h" // FIXME!!! +#include "Platform.h" + +#include "NDS.h" +#include "GBACart.h" + + +namespace Frontend +{ + +int AudioOut_Freq; +float AudioOut_SampleFrac; + + +void Init_Audio(int outputfreq) +{ + AudioOut_Freq = outputfreq; + AudioOut_SampleFrac = 0; +} + +int AudioOut_GetNumSamples(int outlen) +{ + float f_len_in = (outlen * 32823.6328125) / (float)AudioOut_Freq; + f_len_in += AudioOut_SampleFrac; + int len_in = (int)floor(f_len_in); + AudioOut_SampleFrac = f_len_in - len_in; + + return len_in; +} + +void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen) +{ + float res_incr = inlen / (float)outlen; + float res_timer = 0; + int res_pos = 0; + + int volume = Config::AudioVolume; + + for (int i = 0; i < outlen; i++) + { + outbuf[i*2 ] = (inbuf[res_pos*2 ] * volume) >> 8; + outbuf[i*2+1] = (inbuf[res_pos*2+1] * volume) >> 8; + + res_timer += res_incr; + while (res_timer >= 1.0) + { + res_timer -= 1.0; + res_pos++; + } + } +} + +} diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index fb9f547..05a4029 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -6,6 +6,7 @@ SET(SOURCES_QT_SDL PlatformConfig.cpp ../Util_ROM.cpp + ../Util_Audio.cpp ../FrontendUtil.h ) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index bdf68bd..80c25b5 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -48,6 +48,8 @@ #include "Savestate.h" +// TODO: uniform variable spelling + char* EmuDirectory; bool RunningSomething; @@ -55,6 +57,48 @@ bool RunningSomething; MainWindow* mainWindow; EmuThread* emuThread; +SDL_AudioDeviceID audioDevice; +int audioFreq; +SDL_cond* audioSync; +SDL_mutex* audioSyncLock; + + +void audioCallback(void* data, Uint8* stream, int len) +{ + len /= (sizeof(s16) * 2); + + // resample incoming audio to match the output sample rate + + int len_in = Frontend::AudioOut_GetNumSamples(len); + s16 buf_in[1024*2]; + int num_in; + + SDL_LockMutex(audioSyncLock); + num_in = SPU::ReadOutput(buf_in, len_in); + SDL_CondSignal(audioSync); + SDL_UnlockMutex(audioSyncLock); + + if (num_in < 1) + { + memset(stream, 0, len*sizeof(s16)*2); + return; + } + + int margin = 6; + if (num_in < len_in-margin) + { + int last = num_in-1; + if (last < 0) last = 0; + + for (int i = num_in; i < len_in-margin; i++) + ((u32*)buf_in)[i] = ((u32*)buf_in)[last]; + + num_in = len_in-margin; + } + + Frontend::AudioOut_Resample(buf_in, num_in, (s16*)stream, len); +} + EmuThread::EmuThread(QObject* parent) : QThread(parent) { @@ -213,18 +257,18 @@ void EmuThread::run() mainWindow->update(); bool fastforward = false; - /*bool fastforward = HotkeyDown(HK_FastForward); + //bool fastforward = HotkeyDown(HK_FastForward); - if (Config::AudioSync && !fastforward) + if (Config::AudioSync && (!fastforward) && audioDevice) { - SDL_LockMutex(AudioSyncLock); + SDL_LockMutex(audioSyncLock); while (SPU::GetOutputSize() > 1024) { - int ret = SDL_CondWaitTimeout(AudioSync, AudioSyncLock, 500); + int ret = SDL_CondWaitTimeout(audioSync, audioSyncLock, 500); if (ret == SDL_MUTEX_TIMEDOUT) break; } - SDL_UnlockMutex(AudioSyncLock); - }*/ + SDL_UnlockMutex(audioSyncLock); + } float framerate = (1000.0f * nlines) / (60.0f * 263.0f); @@ -338,6 +382,7 @@ void EmuThread::emuRun() // checkme emit windowEmuStart(); + if (audioDevice) SDL_PauseAudioDevice(audioDevice, 0); } void EmuThread::emuPause(bool refresh) @@ -348,6 +393,7 @@ void EmuThread::emuPause(bool refresh) while (EmuStatus != status); //emit windowEmuPause(); + if (audioDevice) SDL_PauseAudioDevice(audioDevice, 1); } void EmuThread::emuUnpause() @@ -355,11 +401,14 @@ void EmuThread::emuUnpause() EmuRunning = PrevEmuStatus; //emit windowEmuUnpause(); + if (audioDevice) SDL_PauseAudioDevice(audioDevice, 0); } void EmuThread::emuStop() { EmuRunning = 0; + + if (audioDevice) SDL_PauseAudioDevice(audioDevice, 1); } bool EmuThread::emuIsRunning() @@ -908,6 +957,32 @@ int main(int argc, char** argv) } #endif + audioSync = SDL_CreateCond(); + audioSyncLock = SDL_CreateMutex(); + + audioFreq = 48000; // TODO: make configurable? + SDL_AudioSpec whatIwant, whatIget; + memset(&whatIwant, 0, sizeof(SDL_AudioSpec)); + whatIwant.freq = audioFreq; + whatIwant.format = AUDIO_S16LSB; + whatIwant.channels = 2; + whatIwant.samples = 1024; + whatIwant.callback = audioCallback; + audioDevice = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + if (!audioDevice) + { + printf("Audio init failed: %s\n", SDL_GetError()); + } + else + { + audioFreq = whatIget.freq; + printf("Audio output frequency: %d Hz\n", audioFreq); + SDL_PauseAudioDevice(audioDevice, 1); + } + + Frontend::Init_ROM(); + Frontend::Init_Audio(audioFreq); + mainWindow = new MainWindow(); mainWindow->show(); @@ -955,6 +1030,15 @@ int main(int argc, char** argv) emuThread->emuStop(); emuThread->wait(); + //if (Joystick) SDL_JoystickClose(Joystick); + if (audioDevice) SDL_CloseAudioDevice(audioDevice); + //if (MicDevice) SDL_CloseAudioDevice(MicDevice); + + SDL_DestroyCond(audioSync); + SDL_DestroyMutex(audioSyncLock); + + //if (MicWavBuffer) delete[] MicWavBuffer; + Config::Save(); SDL_Quit(); |