From d43ecd0bd1c3387a1d7ea968069b093e1ba81f2d Mon Sep 17 00:00:00 2001 From: magumagu Date: Thu, 10 Apr 2014 18:28:19 -0700 Subject: [PATCH 1/6] Rewrite handling of DTK (streaming) audio. The primary motivation here is to make sure we submit samples from the CPU thread. This makes sure the timing of related interrupts accurate, and generally keeps the different kinds of audio synchronized. This will also allow improvements to audio dumping functionality. The new code is also more concise because it gets rid of some duplicated audio mixing code. --- Source/Core/AudioCommon/AudioCommon.cpp | 5 +- Source/Core/AudioCommon/Mixer.cpp | 73 ++++++++----- Source/Core/AudioCommon/Mixer.h | 59 ++++++----- Source/Core/Core/HW/AudioInterface.cpp | 132 ------------------------ Source/Core/Core/HW/AudioInterface.h | 4 - Source/Core/Core/HW/DVDInterface.cpp | 122 ++++++++++++---------- 6 files changed, 146 insertions(+), 249 deletions(-) diff --git a/Source/Core/AudioCommon/AudioCommon.cpp b/Source/Core/AudioCommon/AudioCommon.cpp index c476fe6a5c..5cc57800b5 100644 --- a/Source/Core/AudioCommon/AudioCommon.cpp +++ b/Source/Core/AudioCommon/AudioCommon.cpp @@ -28,10 +28,7 @@ namespace AudioCommon { SoundStream *InitSoundStream(void *hWnd) { - unsigned int AISampleRate, DACSampleRate; - AudioInterface::Callback_GetSampleRate(AISampleRate, DACSampleRate); - delete soundStream; - CMixer *mixer = new CMixer(AISampleRate, DACSampleRate, 48000); + CMixer *mixer = new CMixer(48000); // TODO: possible memleak with mixer diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp index c1ff49dd03..3e75777f9a 100644 --- a/Source/Core/AudioCommon/Mixer.cpp +++ b/Source/Core/AudioCommon/Mixer.cpp @@ -6,6 +6,7 @@ #include "AudioCommon/Mixer.h" #include "Common/Atomic.h" #include "Common/CPUDetect.h" +#include "Common/MathUtil.h" #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/HW/AudioInterface.h" @@ -19,20 +20,8 @@ #endif // Executed from sound stream thread -unsigned int CMixer::Mix(short* samples, unsigned int numSamples, bool consider_framelimit) +unsigned int CMixer::MixerFifo::Mix(short* samples, unsigned int numSamples, bool consider_framelimit) { - if (!samples) - return 0; - - std::lock_guard lk(m_csMixing); - - if (PowerPC::GetState() != PowerPC::CPU_RUNNING) - { - // Silence - memset(samples, 0, numSamples * 4); - return numSamples; - } - unsigned int currentSample = 0; // Cache access in non-volatile variable @@ -56,14 +45,14 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples, bool consider_ //remember fractional offset u32 framelimit = SConfig::GetInstance().m_Framelimit; - float aid_sample_rate = AudioInterface::GetAIDSampleRate() + offset; + float aid_sample_rate = m_input_sample_rate + offset; if (consider_framelimit && framelimit > 2) { aid_sample_rate = aid_sample_rate * (framelimit - 1) * 5 / VideoInterface::TargetRefreshRate; } static u32 frac = 0; - const u32 ratio = (u32)( 65536.0f * aid_sample_rate / (float)m_sampleRate ); + const u32 ratio = (u32)( 65536.0f * aid_sample_rate / (float)m_mixer->m_sampleRate ); if (ratio > 0x10000) ERROR_LOG(AUDIO, "ratio out of range"); @@ -74,11 +63,15 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples, bool consider_ s16 l1 = Common::swap16(m_buffer[indexR & INDEX_MASK]); //current s16 l2 = Common::swap16(m_buffer[indexR2 & INDEX_MASK]); //next int sampleL = ((l1 << 16) + (l2 - l1) * (u16)frac) >> 16; + sampleL += samples[currentSample + 1]; + MathUtil::Clamp(&sampleL, -32767, 32767); samples[currentSample+1] = sampleL; s16 r1 = Common::swap16(m_buffer[(indexR + 1) & INDEX_MASK]); //current s16 r2 = Common::swap16(m_buffer[(indexR2 + 1) & INDEX_MASK]); //next int sampleR = ((r1 << 16) + (r2 - r1) * (u16)frac) >> 16; + sampleR += samples[currentSample]; + MathUtil::Clamp(&sampleR, -32767, 32767); samples[currentSample] = sampleR; frac += ratio; @@ -87,36 +80,55 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples, bool consider_ } // Padding - unsigned short s[2]; + short s[2]; s[0] = Common::swap16(m_buffer[(indexR - 1) & INDEX_MASK]); s[1] = Common::swap16(m_buffer[(indexR - 2) & INDEX_MASK]); for (; currentSample < numSamples*2; currentSample+=2) { - samples[currentSample] = s[0]; - samples[currentSample+1] = s[1]; + int sampleR = s[0] + samples[currentSample]; + MathUtil::Clamp(&sampleR, -32767, 32767); + samples[currentSample] = sampleR; + int sampleL = s[1] + samples[currentSample + 1]; + MathUtil::Clamp(&sampleL, -32767, 32767); + samples[currentSample + 1] = sampleL; } // Flush cached variable Common::AtomicStore(m_indexR, indexR); - // Add the DTK Music - // Re-sampling is done inside - AudioInterface::Callback_GetStreaming(samples, numSamples, m_sampleRate); - if (m_logAudio) - g_wave_writer.AddStereoSamples(samples, numSamples); - return numSamples; } +unsigned int CMixer::Mix(short* samples, unsigned int num_samples, bool consider_framelimit) +{ + if (!samples) + return 0; -void CMixer::PushSamples(const short *samples, unsigned int num_samples) + std::lock_guard lk(m_csMixing); + + memset(samples, 0, num_samples * 2 * sizeof(short)); + + if (PowerPC::GetState() != PowerPC::CPU_RUNNING) + { + // Silence + return num_samples; + } + + m_dma_mixer.Mix(samples, num_samples, consider_framelimit); + m_streaming_mixer.Mix(samples, num_samples, consider_framelimit); + if (m_logAudio) + g_wave_writer.AddStereoSamples(samples, num_samples); + return num_samples; +} + +void CMixer::MixerFifo::PushSamples(const short *samples, unsigned int num_samples) { // Cache access in non-volatile variable // indexR isn't allowed to cache in the audio throttling loop as it // needs to get updates to not deadlock. u32 indexW = Common::AtomicLoad(m_indexW); - if (m_throttle) + if (m_mixer->m_throttle) { // The auto throttle function. This loop will put a ceiling on the CPU MHz. while (num_samples * 2 + ((indexW - Common::AtomicLoad(m_indexR)) & INDEX_MASK) >= MAX_SAMPLES * 2) @@ -155,3 +167,12 @@ void CMixer::PushSamples(const short *samples, unsigned int num_samples) return; } +void CMixer::PushSamples(const short *samples, unsigned int num_samples) +{ + m_dma_mixer.PushSamples(samples, num_samples); +} + +void CMixer::PushStreamingSamples(const short *samples, unsigned int num_samples) +{ + m_streaming_mixer.PushSamples(samples, num_samples); +} diff --git a/Source/Core/AudioCommon/Mixer.h b/Source/Core/AudioCommon/Mixer.h index f7fcdbf14b..b693c3bb3f 100644 --- a/Source/Core/AudioCommon/Mixer.h +++ b/Source/Core/AudioCommon/Mixer.h @@ -21,23 +21,15 @@ class CMixer { public: - CMixer(unsigned int AISampleRate = 48000, unsigned int DACSampleRate = 48000, unsigned int BackendSampleRate = 32000) - : m_aiSampleRate(AISampleRate) - , m_dacSampleRate(DACSampleRate) - , m_bits(16) - , m_channels(2) + CMixer(unsigned int BackendSampleRate) + : m_dma_mixer(this, 32000) + , m_streaming_mixer(this, 48000) + , m_sampleRate(BackendSampleRate) , m_logAudio(0) - , m_indexW(0) - , m_indexR(0) - , m_numLeftI(0.0f) + , m_throttle(false) + , m_speed(0) { - // AyuanX: The internal (Core & DSP) sample rate is fixed at 32KHz - // So when AI/DAC sample rate differs than 32KHz, we have to do re-sampling - m_sampleRate = BackendSampleRate; - - memset(m_buffer, 0, sizeof(m_buffer)); - - INFO_LOG(AUDIO_INTERFACE, "Mixer is initialized (AISampleRate:%i, DACSampleRate:%i)", AISampleRate, DACSampleRate); + INFO_LOG(AUDIO_INTERFACE, "Mixer is initialized"); } virtual ~CMixer() {} @@ -47,7 +39,8 @@ public: // Called from main thread virtual void PushSamples(const short* samples, unsigned int num_samples); - unsigned int GetSampleRate() const {return m_sampleRate;} + virtual void PushStreamingSamples(const short* samples, unsigned int num_samples); + unsigned int GetSampleRate() const { return m_sampleRate; } void SetThrottle(bool use) { m_throttle = use;} @@ -87,11 +80,30 @@ public: void UpdateSpeed(volatile float val) { m_speed = val; } protected: + class MixerFifo { + public: + MixerFifo(CMixer *mixer, unsigned sample_rate) + : m_mixer(mixer) + , m_input_sample_rate(sample_rate) + , m_indexW(0) + , m_indexR(0) + , m_numLeftI(0.0f) + { + memset(m_buffer, 0, sizeof(m_buffer)); + } + void PushSamples(const short* samples, unsigned int num_samples); + unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true); + private: + CMixer *m_mixer; + unsigned m_input_sample_rate; + short m_buffer[MAX_SAMPLES * 2]; + volatile u32 m_indexW; + volatile u32 m_indexR; + float m_numLeftI; + }; + MixerFifo m_dma_mixer; + MixerFifo m_streaming_mixer; unsigned int m_sampleRate; - unsigned int m_aiSampleRate; - unsigned int m_dacSampleRate; - int m_bits; - int m_channels; WaveFileWriter g_wave_writer; @@ -99,14 +111,7 @@ protected: bool m_throttle; - short m_buffer[MAX_SAMPLES * 2]; - volatile u32 m_indexW; - volatile u32 m_indexR; - std::mutex m_csMixing; - float m_numLeftI; volatile float m_speed; // Current rate of the emulation (1.0 = 100% speed) -private: - }; diff --git a/Source/Core/Core/HW/AudioInterface.cpp b/Source/Core/Core/HW/AudioInterface.cpp index 1512dba200..517af05742 100644 --- a/Source/Core/Core/HW/AudioInterface.cpp +++ b/Source/Core/Core/HW/AudioInterface.cpp @@ -56,10 +56,8 @@ This file mainly deals with the [Drive I/F], however [AIDFR] controls #include "Core/CoreTiming.h" #include "Core/HW/AudioInterface.h" #include "Core/HW/CPU.h" -#include "Core/HW/DVDInterface.h" #include "Core/HW/MMIO.h" #include "Core/HW/ProcessorInterface.h" -#include "Core/HW/StreamADPCM.h" #include "Core/HW/SystemTimers.h" #include "Core/PowerPC/PowerPC.h" @@ -205,9 +203,6 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base) m_Control.PSTAT = tmpAICtrl.PSTAT; g_LastCPUTime = CoreTiming::GetTicks(); - // Tell Drive Interface to start/stop streaming - DVDInterface::g_bStream = tmpAICtrl.PSTAT; - CoreTiming::RemoveEvent(et_AI); CoreTiming::ScheduleEvent(((int)GetAIPeriod() / 2), et_AI); } @@ -272,133 +267,6 @@ void GenerateAISInterrupt() GenerateAudioInterrupt(); } -void Callback_GetSampleRate(unsigned int &_AISampleRate, unsigned int &_DACSampleRate) -{ - _AISampleRate = g_AISSampleRate; - _DACSampleRate = g_AIDSampleRate; -} - -// Callback for the disc streaming -// WARNING - called from audio thread -unsigned int Callback_GetStreaming(short* _pDestBuffer, unsigned int _numSamples, unsigned int _sampleRate) -{ - if (m_Control.PSTAT && !CCPU::IsStepping()) - { - static int pos = 0; - static short pcm[NGCADPCM::SAMPLES_PER_BLOCK*2]; - const int lvolume = m_Volume.left; - const int rvolume = m_Volume.right; - - if (g_AISSampleRate == 48000 && _sampleRate == 32000) - { - _dbg_assert_msg_(AUDIO_INTERFACE, !(_numSamples & 1), "Number of Samples: %i must be even!", _numSamples); - _numSamples = _numSamples * 3 / 2; - } - - int pcm_l = 0, pcm_r = 0; - for (unsigned int i = 0; i < _numSamples; i++) - { - if (pos == 0) - ReadStreamBlock(pcm); - - if (g_AISSampleRate == 48000 && _sampleRate == 32000) //downsample 48>32 - { - if (i % 3) - { - pcm_l = (((pcm_l + (int)pcm[pos*2]) / 2 * lvolume) >> 8) + (int)(*_pDestBuffer); - MathUtil::Clamp(&pcm_l, -32767, 32767); - *_pDestBuffer++ = pcm_l; - - pcm_r = (((pcm_r + (int)pcm[pos*2+1]) / 2 * rvolume) >> 8) + (int)(*_pDestBuffer); - MathUtil::Clamp(&pcm_r, -32767, 32767); - *_pDestBuffer++ = pcm_r; - } - pcm_l = pcm[pos*2]; - pcm_r = pcm[pos*2+1]; - - pos++; - } - else if (g_AISSampleRate == 32000 && _sampleRate == 48000) //upsample 32>48 - { - //starts with one sample of 0 - const u32 ratio = (u32)( 65536.0f * 32000.0f / (float)_sampleRate ); - static u32 frac = 0; - - static s16 l1 = 0; - static s16 l2 = 0; - - if (frac >= 0x10000 || frac == 0) - { - frac &= 0xffff; - - l1 = l2; //current - l2 = pcm[pos * 2]; //next - } - - pcm_l = ((l1 << 16) + (l2 - l1) * (u16)frac) >> 16; - pcm_r = ((l1 << 16) + (l2 - l1) * (u16)frac) >> 16; - - - pcm_l = (pcm_l * lvolume >> 8) + (int)(*_pDestBuffer); - MathUtil::Clamp(&pcm_l, -32767, 32767); - *_pDestBuffer++ = pcm_l; - - pcm_r = (pcm_r * lvolume >> 8) + (int)(*_pDestBuffer); - MathUtil::Clamp(&pcm_r, -32767, 32767); - *_pDestBuffer++ = pcm_r; - - frac += ratio; - pos += frac >> 16; - - } - else //1:1 no resampling - { - pcm_l = (((int)pcm[pos*2] * lvolume) >> 8) + (int)(*_pDestBuffer); - MathUtil::Clamp(&pcm_l, -32767, 32767); - *_pDestBuffer++ = pcm_l; - - pcm_r = (((int)pcm[pos*2+1] * rvolume) >> 8) + (int)(*_pDestBuffer); - MathUtil::Clamp(&pcm_r, -32767, 32767); - *_pDestBuffer++ = pcm_r; - - pos++; - } - - if (pos == NGCADPCM::SAMPLES_PER_BLOCK) - pos = 0; - } - } - else - { - // Don't overwrite existed sample data - /* - for (unsigned int i = 0; i < _numSamples * 2; i++) - { - _pDestBuffer[i] = 0; //silence! - } - */ - } - - return _numSamples; -} - -// WARNING - called from audio thread -void ReadStreamBlock(s16 *_pPCM) -{ - u8 tempADPCM[NGCADPCM::ONE_BLOCK_SIZE]; - if (DVDInterface::DVDReadADPCM(tempADPCM, NGCADPCM::ONE_BLOCK_SIZE)) - { - NGCADPCM::DecodeBlock(_pPCM, tempADPCM); - } - else - { - memset(_pPCM, 0, NGCADPCM::SAMPLES_PER_BLOCK*2); - } - - // our whole streaming code is "faked" ... so it shouldn't increase the sample counter - // streaming will never work correctly this way, but at least the program will think all is alright. -} - static void IncreaseSampleCount(const u32 _iAmount) { if (m_Control.PSTAT) diff --git a/Source/Core/Core/HW/AudioInterface.h b/Source/Core/Core/HW/AudioInterface.h index 404f14dec3..0b36692083 100644 --- a/Source/Core/Core/HW/AudioInterface.h +++ b/Source/Core/Core/HW/AudioInterface.h @@ -22,10 +22,6 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base); void Update(u64 userdata, int cyclesLate); -// Called by DSP emulator -void Callback_GetSampleRate(unsigned int &_AISampleRate, unsigned int &_DACSampleRate); -unsigned int Callback_GetStreaming(short* _pDestBuffer, unsigned int _numSamples, unsigned int _sampleRate = 48000); - // Get the audio rates (48000 or 32000 only) unsigned int GetAIDSampleRate(); diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index 53e8ebfef8..4e4e24ac4c 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -4,6 +4,8 @@ #include +#include "AudioCommon/AudioCommon.h" + #include "Common/ChunkFile.h" #include "Common/Common.h" #include "Common/Thread.h" @@ -208,6 +210,7 @@ u32 g_ErrorCode = 0; bool g_bDiscInside = false; bool g_bStream = false; int tc = 0; +int dtk = 0; static u64 g_last_read_offset; static u64 g_last_read_time; @@ -215,10 +218,6 @@ static u64 g_last_read_time; // GC-AM only static unsigned char media_buffer[0x40]; -// Needed because data and streaming audio access needs to be managed by the "drive" -// (both requests can happen at the same time, audio takes precedence) -static std::mutex dvdread_section; - static int ejectDisc; static int insertDisc; @@ -262,6 +261,63 @@ void TransferComplete(u64 userdata, int cyclesLate) FinishExecuteRead(); } +void ReadDTKSamples(u64 userdata, int cyclesLate) +{ + static const int NUM_SAMPLES = 48000 / 1000 * 7; // 7ms of 48kHz samples + short tempPCM[NUM_SAMPLES * 2]; + unsigned samples_processed = 0; + do + { + if (AudioPos >= CurrentStart + CurrentLength) + { + AudioPos = LoopStart; + CurrentStart = LoopStart; + CurrentLength = LoopLength; + NGCADPCM::InitFilter(); + AudioInterface::GenerateAISInterrupt(); + + // If there isn't any audio to stream, stop streaming. + if (AudioPos >= CurrentStart + CurrentLength) + { + g_bStream = false; + return; + } + + break; + } + + u8 tempADPCM[NGCADPCM::ONE_BLOCK_SIZE]; + // TODO: What if we can't read from AudioPos? + VolumeHandler::ReadToPtr(tempADPCM, AudioPos, sizeof(tempADPCM)); + AudioPos += sizeof(tempADPCM); + NGCADPCM::DecodeBlock(tempPCM + samples_processed * 2, tempADPCM); + samples_processed += NGCADPCM::SAMPLES_PER_BLOCK; + } while (samples_processed < NUM_SAMPLES); + for (unsigned i = 0; i < samples_processed * 2; ++i) + { + // TODO: Fix the mixer so it can accept non-byte-swapped samples. + tempPCM[i] = Common::swap16(tempPCM[i]); + } + soundStream->GetMixer()->PushStreamingSamples(tempPCM, samples_processed); + int ticks_to_dtk = int(SystemTimers::GetTicksPerSecond() * u64(samples_processed) / 48000); + CoreTiming::ScheduleEvent(ticks_to_dtk - cyclesLate, dtk); +} + +void StartDTKStreaming() +{ + // We wait 100ms before we actually start streaming to try to simulate + // seek time. Not completely accurate, but better than starting the + // stream instantly. + g_bStream = true; + CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond() / 10, dtk); +} + +void StopDTKStreaming() +{ + g_bStream = false; + CoreTiming::RemoveAllEvents(dtk); +} + void Init() { m_DISR.Hex = 0; @@ -288,6 +344,7 @@ void Init() insertDisc = CoreTiming::RegisterEvent("InsertDisc", InsertDiscCallback); tc = CoreTiming::RegisterEvent("TransferComplete", TransferComplete); + dtk = CoreTiming::RegisterEvent("StreamingTimer", ReadDTKSamples); } void Shutdown() @@ -369,57 +426,9 @@ void ClearCoverInterrupt() bool DVDRead(u32 _iDVDOffset, u32 _iRamAddress, u32 _iLength) { - // We won't need the crit sec when DTK streaming has been rewritten correctly. - std::lock_guard lk(dvdread_section); return VolumeHandler::ReadToPtr(Memory::GetPointer(_iRamAddress), _iDVDOffset, _iLength); } -bool DVDReadADPCM(u8* _pDestBuffer, u32 _iNumSamples) -{ - _iNumSamples &= ~31; - - if (AudioPos == 0) - { - memset(_pDestBuffer, 0, _iNumSamples); // probably __AI_SRC_INIT :P - } - else - { - std::lock_guard lk(dvdread_section); - VolumeHandler::ReadToPtr(_pDestBuffer, AudioPos, _iNumSamples); - } - - // loop check - if (g_bStream) - { - AudioPos += _iNumSamples; - - if (AudioPos >= CurrentStart + CurrentLength) - { - if (LoopStart == 0) - { - AudioPos = 0; - CurrentStart = 0; - CurrentLength = 0; - } - else - { - AudioPos = LoopStart; - CurrentStart = LoopStart; - CurrentLength = LoopLength; - } - NGCADPCM::InitFilter(); - AudioInterface::GenerateAISInterrupt(); - } - - //WARN_LOG(DVDINTERFACE,"ReadADPCM"); - return true; - } - else - { - return false; - } -} - void RegisterMMIO(MMIO::Mapping* mmio, u32 base) { mmio->Register(base | DI_STATUS_REGISTER, @@ -927,12 +936,11 @@ void ExecuteCommand() CurrentStart = pos; CurrentLength = length; NGCADPCM::InitFilter(); - g_bStream = true; + StartDTKStreaming(); } LoopStart = pos; LoopLength = length; - g_bStream = (m_DICMDBUF[0].CMDBYTE1 == 0); // This command can start/stop the stream // Stop stream if (m_DICMDBUF[0].CMDBYTE1 == 1) @@ -942,6 +950,8 @@ void ExecuteCommand() LoopLength = 0; CurrentStart = 0; CurrentLength = 0; + if (g_bStream) + StopDTKStreaming(); } WARN_LOG(DVDINTERFACE, "(Audio) Stream subcmd = %08x offset = %08x length=%08x", @@ -990,12 +1000,12 @@ void ExecuteCommand() case DVDLowAudioBufferConfig: if (m_DICMDBUF[0].CMDBYTE1 == 1) { - g_bStream = true; + // TODO: What is this actually supposed to do? WARN_LOG(DVDINTERFACE, "(Audio): Audio enabled"); } else { - g_bStream = false; + // TODO: What is this actually supposed to do? WARN_LOG(DVDINTERFACE, "(Audio): Audio disabled"); } break; From 40e2ce4f27c2c261b1f1ea59138958b144e3d0f4 Mon Sep 17 00:00:00 2001 From: magumagu Date: Mon, 12 May 2014 23:34:53 -0700 Subject: [PATCH 2/6] Audio mixer: remove check for ratio > 1.0. The code actually handles this case correctly; the algorithm is linear interpolation between the two closest samples, and the way it is written should work correctly with any ratio. --- Source/Core/AudioCommon/Mixer.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp index 3e75777f9a..fd8486d8e5 100644 --- a/Source/Core/AudioCommon/Mixer.cpp +++ b/Source/Core/AudioCommon/Mixer.cpp @@ -54,9 +54,7 @@ unsigned int CMixer::MixerFifo::Mix(short* samples, unsigned int numSamples, boo static u32 frac = 0; const u32 ratio = (u32)( 65536.0f * aid_sample_rate / (float)m_mixer->m_sampleRate ); - if (ratio > 0x10000) - ERROR_LOG(AUDIO, "ratio out of range"); - + // TODO: consider a higher-quality resampling algorithm. for (; currentSample < numSamples*2 && ((indexW-indexR) & INDEX_MASK) > 2; currentSample+=2) { u32 indexR2 = indexR + 2; //next sample From affb7c17fc17e6c28a190f7a854533af4b44a980 Mon Sep 17 00:00:00 2001 From: magumagu Date: Sat, 14 Jun 2014 16:08:21 -0700 Subject: [PATCH 3/6] Audio mixer: fix a couple compiler warnings. --- Source/Core/AudioCommon/Mixer.cpp | 2 +- Source/Core/AudioCommon/Mixer.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp index fd8486d8e5..ac8a78ae7e 100644 --- a/Source/Core/AudioCommon/Mixer.cpp +++ b/Source/Core/AudioCommon/Mixer.cpp @@ -34,7 +34,7 @@ unsigned int CMixer::MixerFifo::Mix(short* samples, unsigned int numSamples, boo u32 indexR = Common::AtomicLoad(m_indexR); u32 indexW = Common::AtomicLoad(m_indexW); - float numLeft = ((indexW - indexR) & INDEX_MASK) / 2; + float numLeft = (float)(((indexW - indexR) & INDEX_MASK) / 2); m_numLeftI = (numLeft + m_numLeftI*(CONTROL_AVG-1)) / CONTROL_AVG; float offset = (m_numLeftI - LOW_WATERMARK) * CONTROL_FACTOR; if (offset > MAX_FREQ_SHIFT) offset = MAX_FREQ_SHIFT; diff --git a/Source/Core/AudioCommon/Mixer.h b/Source/Core/AudioCommon/Mixer.h index b693c3bb3f..a796633205 100644 --- a/Source/Core/AudioCommon/Mixer.h +++ b/Source/Core/AudioCommon/Mixer.h @@ -15,7 +15,7 @@ #define LOW_WATERMARK 1280 // 40 ms #define MAX_FREQ_SHIFT 200 // per 32000 Hz -#define CONTROL_FACTOR 0.2 // in freq_shift per fifo size offset +#define CONTROL_FACTOR 0.2f // in freq_shift per fifo size offset #define CONTROL_AVG 32 class CMixer { From a282f181cd596c309acd10e9c96ba247a43f0c2b Mon Sep 17 00:00:00 2001 From: magumagu Date: Sun, 15 Jun 2014 15:08:47 -0700 Subject: [PATCH 4/6] Streaming/DTK audio: fix pausing playback. --- Source/Core/Core/HW/AudioInterface.cpp | 8 ++++-- Source/Core/Core/HW/AudioInterface.h | 2 ++ Source/Core/Core/HW/DVDInterface.cpp | 35 ++++++++++++++++++++------ Source/Core/Core/HW/DVDInterface.h | 1 - 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/Source/Core/Core/HW/AudioInterface.cpp b/Source/Core/Core/HW/AudioInterface.cpp index 517af05742..9600390c9c 100644 --- a/Source/Core/Core/HW/AudioInterface.cpp +++ b/Source/Core/Core/HW/AudioInterface.cpp @@ -143,8 +143,7 @@ void DoState(PointerWrap &p) static void GenerateAudioInterrupt(); static void UpdateInterrupts(); static void IncreaseSampleCount(const u32 _uAmount); -void ReadStreamBlock(s16* _pPCM); -u64 GetAIPeriod(); +static u64 GetAIPeriod(); int et_AI; void Init() @@ -307,4 +306,9 @@ u64 GetAIPeriod() return period; } +bool IsAISPlaying() +{ + return m_Control.PSTAT; +} + } // end of namespace AudioInterface diff --git a/Source/Core/Core/HW/AudioInterface.h b/Source/Core/Core/HW/AudioInterface.h index 0b36692083..40df02fe24 100644 --- a/Source/Core/Core/HW/AudioInterface.h +++ b/Source/Core/Core/HW/AudioInterface.h @@ -27,4 +27,6 @@ unsigned int GetAIDSampleRate(); void GenerateAISInterrupt(); +bool IsAISPlaying(); + } // namespace diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index 4e4e24ac4c..46f3e86e93 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -261,11 +261,9 @@ void TransferComplete(u64 userdata, int cyclesLate) FinishExecuteRead(); } -void ReadDTKSamples(u64 userdata, int cyclesLate) +static u32 ProcessDTKSamples(short *tempPCM, u32 num_samples) { - static const int NUM_SAMPLES = 48000 / 1000 * 7; // 7ms of 48kHz samples - short tempPCM[NUM_SAMPLES * 2]; - unsigned samples_processed = 0; + u32 samples_processed = 0; do { if (AudioPos >= CurrentStart + CurrentLength) @@ -280,9 +278,7 @@ void ReadDTKSamples(u64 userdata, int cyclesLate) if (AudioPos >= CurrentStart + CurrentLength) { g_bStream = false; - return; } - break; } @@ -292,13 +288,36 @@ void ReadDTKSamples(u64 userdata, int cyclesLate) AudioPos += sizeof(tempADPCM); NGCADPCM::DecodeBlock(tempPCM + samples_processed * 2, tempADPCM); samples_processed += NGCADPCM::SAMPLES_PER_BLOCK; - } while (samples_processed < NUM_SAMPLES); + } while (samples_processed < num_samples); for (unsigned i = 0; i < samples_processed * 2; ++i) { // TODO: Fix the mixer so it can accept non-byte-swapped samples. tempPCM[i] = Common::swap16(tempPCM[i]); } + return samples_processed; +} + +void DTKStreamingCallback(u64 userdata, int cyclesLate) +{ + // Send audio to the mixer. + static const int NUM_SAMPLES = 48000 / 2000 * 7; // 3.5ms of 48kHz samples + short tempPCM[NUM_SAMPLES * 2]; + unsigned samples_processed; + if (AudioInterface::IsAISPlaying()) + { + samples_processed = ProcessDTKSamples(tempPCM, NUM_SAMPLES); + } + else + { + memset(tempPCM, 0, sizeof(tempPCM)); + samples_processed = NUM_SAMPLES; + } soundStream->GetMixer()->PushStreamingSamples(tempPCM, samples_processed); + + // If we reached the end of the audio, stop. + if (!g_bStream) + return; + int ticks_to_dtk = int(SystemTimers::GetTicksPerSecond() * u64(samples_processed) / 48000); CoreTiming::ScheduleEvent(ticks_to_dtk - cyclesLate, dtk); } @@ -344,7 +363,7 @@ void Init() insertDisc = CoreTiming::RegisterEvent("InsertDisc", InsertDiscCallback); tc = CoreTiming::RegisterEvent("TransferComplete", TransferComplete); - dtk = CoreTiming::RegisterEvent("StreamingTimer", ReadDTKSamples); + dtk = CoreTiming::RegisterEvent("StreamingTimer", DTKStreamingCallback); } void Shutdown() diff --git a/Source/Core/Core/HW/DVDInterface.h b/Source/Core/Core/HW/DVDInterface.h index 6b03c7f123..8f53d824fc 100644 --- a/Source/Core/Core/HW/DVDInterface.h +++ b/Source/Core/Core/HW/DVDInterface.h @@ -35,7 +35,6 @@ void ClearCoverInterrupt(); bool DVDRead(u32 _iDVDOffset, u32 _iRamAddress, u32 _iLength); // For AudioInterface bool DVDReadADPCM(u8* _pDestBuffer, u32 _iNumSamples); -extern bool g_bStream; // Not sure about endianness here. I'll just name them like this... From 1f604e87be50c8561a6bfc82d75ce03f43f506e2 Mon Sep 17 00:00:00 2001 From: magumagu Date: Wed, 18 Jun 2014 12:11:09 -0700 Subject: [PATCH 5/6] Try to make DTK rewrite handle events the same way as the old code. --- Source/Core/Core/HW/AudioInterface.cpp | 9 ++++---- Source/Core/Core/HW/AudioInterface.h | 2 -- Source/Core/Core/HW/DVDInterface.cpp | 29 ++++++-------------------- Source/Core/Core/HW/DVDInterface.h | 4 +--- 4 files changed, 11 insertions(+), 33 deletions(-) diff --git a/Source/Core/Core/HW/AudioInterface.cpp b/Source/Core/Core/HW/AudioInterface.cpp index 9600390c9c..519b84d1f0 100644 --- a/Source/Core/Core/HW/AudioInterface.cpp +++ b/Source/Core/Core/HW/AudioInterface.cpp @@ -56,6 +56,7 @@ This file mainly deals with the [Drive I/F], however [AIDFR] controls #include "Core/CoreTiming.h" #include "Core/HW/AudioInterface.h" #include "Core/HW/CPU.h" +#include "Core/HW/DVDInterface.h" #include "Core/HW/MMIO.h" #include "Core/HW/ProcessorInterface.h" #include "Core/HW/SystemTimers.h" @@ -202,6 +203,9 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base) m_Control.PSTAT = tmpAICtrl.PSTAT; g_LastCPUTime = CoreTiming::GetTicks(); + // Tell Drive Interface to start/stop streaming + DVDInterface::g_bStream = tmpAICtrl.PSTAT; + CoreTiming::RemoveEvent(et_AI); CoreTiming::ScheduleEvent(((int)GetAIPeriod() / 2), et_AI); } @@ -306,9 +310,4 @@ u64 GetAIPeriod() return period; } -bool IsAISPlaying() -{ - return m_Control.PSTAT; -} - } // end of namespace AudioInterface diff --git a/Source/Core/Core/HW/AudioInterface.h b/Source/Core/Core/HW/AudioInterface.h index 40df02fe24..0b36692083 100644 --- a/Source/Core/Core/HW/AudioInterface.h +++ b/Source/Core/Core/HW/AudioInterface.h @@ -27,6 +27,4 @@ unsigned int GetAIDSampleRate(); void GenerateAISInterrupt(); -bool IsAISPlaying(); - } // namespace diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index 46f3e86e93..7507a632f7 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -303,7 +303,7 @@ void DTKStreamingCallback(u64 userdata, int cyclesLate) static const int NUM_SAMPLES = 48000 / 2000 * 7; // 3.5ms of 48kHz samples short tempPCM[NUM_SAMPLES * 2]; unsigned samples_processed; - if (AudioInterface::IsAISPlaying()) + if (g_bStream) { samples_processed = ProcessDTKSamples(tempPCM, NUM_SAMPLES); } @@ -314,29 +314,10 @@ void DTKStreamingCallback(u64 userdata, int cyclesLate) } soundStream->GetMixer()->PushStreamingSamples(tempPCM, samples_processed); - // If we reached the end of the audio, stop. - if (!g_bStream) - return; - int ticks_to_dtk = int(SystemTimers::GetTicksPerSecond() * u64(samples_processed) / 48000); CoreTiming::ScheduleEvent(ticks_to_dtk - cyclesLate, dtk); } -void StartDTKStreaming() -{ - // We wait 100ms before we actually start streaming to try to simulate - // seek time. Not completely accurate, but better than starting the - // stream instantly. - g_bStream = true; - CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond() / 10, dtk); -} - -void StopDTKStreaming() -{ - g_bStream = false; - CoreTiming::RemoveAllEvents(dtk); -} - void Init() { m_DISR.Hex = 0; @@ -364,6 +345,8 @@ void Init() tc = CoreTiming::RegisterEvent("TransferComplete", TransferComplete); dtk = CoreTiming::RegisterEvent("StreamingTimer", DTKStreamingCallback); + + CoreTiming::ScheduleEvent(0, dtk); } void Shutdown() @@ -955,11 +938,11 @@ void ExecuteCommand() CurrentStart = pos; CurrentLength = length; NGCADPCM::InitFilter(); - StartDTKStreaming(); } LoopStart = pos; LoopLength = length; + g_bStream = (m_DICMDBUF[0].CMDBYTE1 == 0); // This command can start/stop the stream // Stop stream if (m_DICMDBUF[0].CMDBYTE1 == 1) @@ -969,8 +952,6 @@ void ExecuteCommand() LoopLength = 0; CurrentStart = 0; CurrentLength = 0; - if (g_bStream) - StopDTKStreaming(); } WARN_LOG(DVDINTERFACE, "(Audio) Stream subcmd = %08x offset = %08x length=%08x", @@ -1020,11 +1001,13 @@ void ExecuteCommand() if (m_DICMDBUF[0].CMDBYTE1 == 1) { // TODO: What is this actually supposed to do? + g_bStream = true; WARN_LOG(DVDINTERFACE, "(Audio): Audio enabled"); } else { // TODO: What is this actually supposed to do? + g_bStream = false; WARN_LOG(DVDINTERFACE, "(Audio): Audio disabled"); } break; diff --git a/Source/Core/Core/HW/DVDInterface.h b/Source/Core/Core/HW/DVDInterface.h index 8f53d824fc..06b05fd300 100644 --- a/Source/Core/Core/HW/DVDInterface.h +++ b/Source/Core/Core/HW/DVDInterface.h @@ -33,9 +33,7 @@ void ClearCoverInterrupt(); // DVD Access Functions bool DVDRead(u32 _iDVDOffset, u32 _iRamAddress, u32 _iLength); -// For AudioInterface -bool DVDReadADPCM(u8* _pDestBuffer, u32 _iNumSamples); - +extern bool g_bStream; // Not sure about endianness here. I'll just name them like this... enum DIErrorLow From ca0203a1ccaa9aa84bb3f74fa0548924438a1a40 Mon Sep 17 00:00:00 2001 From: magumagu Date: Thu, 26 Jun 2014 16:15:18 -0700 Subject: [PATCH 6/6] AudioCommon: restore support for DTK volume setting. --- Source/Core/AudioCommon/Mixer.cpp | 20 +++++++++++++++++++- Source/Core/AudioCommon/Mixer.h | 7 +++++++ Source/Core/Core/HW/AudioInterface.cpp | 7 ++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp index ac8a78ae7e..388eb56b0d 100644 --- a/Source/Core/AudioCommon/Mixer.cpp +++ b/Source/Core/AudioCommon/Mixer.cpp @@ -54,6 +54,9 @@ unsigned int CMixer::MixerFifo::Mix(short* samples, unsigned int numSamples, boo static u32 frac = 0; const u32 ratio = (u32)( 65536.0f * aid_sample_rate / (float)m_mixer->m_sampleRate ); + s32 lvolume = m_LVolume; + s32 rvolume = m_RVolume; + // TODO: consider a higher-quality resampling algorithm. for (; currentSample < numSamples*2 && ((indexW-indexR) & INDEX_MASK) > 2; currentSample+=2) { u32 indexR2 = indexR + 2; //next sample @@ -61,6 +64,7 @@ unsigned int CMixer::MixerFifo::Mix(short* samples, unsigned int numSamples, boo s16 l1 = Common::swap16(m_buffer[indexR & INDEX_MASK]); //current s16 l2 = Common::swap16(m_buffer[indexR2 & INDEX_MASK]); //next int sampleL = ((l1 << 16) + (l2 - l1) * (u16)frac) >> 16; + sampleL = (sampleL * lvolume) >> 8; sampleL += samples[currentSample + 1]; MathUtil::Clamp(&sampleL, -32767, 32767); samples[currentSample+1] = sampleL; @@ -68,6 +72,7 @@ unsigned int CMixer::MixerFifo::Mix(short* samples, unsigned int numSamples, boo s16 r1 = Common::swap16(m_buffer[(indexR + 1) & INDEX_MASK]); //current s16 r2 = Common::swap16(m_buffer[(indexR2 + 1) & INDEX_MASK]); //next int sampleR = ((r1 << 16) + (r2 - r1) * (u16)frac) >> 16; + sampleR = (sampleR * rvolume) >> 8; sampleR += samples[currentSample]; MathUtil::Clamp(&sampleR, -32767, 32767); samples[currentSample] = sampleR; @@ -81,7 +86,9 @@ unsigned int CMixer::MixerFifo::Mix(short* samples, unsigned int numSamples, boo short s[2]; s[0] = Common::swap16(m_buffer[(indexR - 1) & INDEX_MASK]); s[1] = Common::swap16(m_buffer[(indexR - 2) & INDEX_MASK]); - for (; currentSample < numSamples*2; currentSample+=2) + s[0] = (s[0] * lvolume) >> 8; + s[1] = (s[1] * rvolume) >> 8; + for (; currentSample < numSamples * 2; currentSample += 2) { int sampleR = s[0] + samples[currentSample]; MathUtil::Clamp(&sampleR, -32767, 32767); @@ -174,3 +181,14 @@ void CMixer::PushStreamingSamples(const short *samples, unsigned int num_samples { m_streaming_mixer.PushSamples(samples, num_samples); } + +void CMixer::SetStreamingVolume(unsigned int lvolume, unsigned int rvolume) +{ + m_streaming_mixer.SetVolume(lvolume, rvolume); +} + +void CMixer::MixerFifo::SetVolume(unsigned int lvolume, unsigned int rvolume) +{ + m_LVolume = lvolume + (lvolume >> 7); + m_RVolume = rvolume + (rvolume >> 7); +} diff --git a/Source/Core/AudioCommon/Mixer.h b/Source/Core/AudioCommon/Mixer.h index a796633205..6400cf8da4 100644 --- a/Source/Core/AudioCommon/Mixer.h +++ b/Source/Core/AudioCommon/Mixer.h @@ -41,6 +41,7 @@ public: virtual void PushSamples(const short* samples, unsigned int num_samples); virtual void PushStreamingSamples(const short* samples, unsigned int num_samples); unsigned int GetSampleRate() const { return m_sampleRate; } + void SetStreamingVolume(unsigned int lvolume, unsigned int rvolume); void SetThrottle(bool use) { m_throttle = use;} @@ -88,17 +89,23 @@ protected: , m_indexW(0) , m_indexR(0) , m_numLeftI(0.0f) + , m_LVolume(256) + , m_RVolume(256) { memset(m_buffer, 0, sizeof(m_buffer)); } void PushSamples(const short* samples, unsigned int num_samples); unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true); + void SetVolume(unsigned int lvolume, unsigned int rvolume); private: CMixer *m_mixer; unsigned m_input_sample_rate; short m_buffer[MAX_SAMPLES * 2]; volatile u32 m_indexW; volatile u32 m_indexR; + // Volume ranges from 0-256 + volatile s32 m_LVolume; + volatile s32 m_RVolume; float m_numLeftI; }; MixerFifo m_dma_mixer; diff --git a/Source/Core/Core/HW/AudioInterface.cpp b/Source/Core/Core/HW/AudioInterface.cpp index 519b84d1f0..2b9746dbc8 100644 --- a/Source/Core/Core/HW/AudioInterface.cpp +++ b/Source/Core/Core/HW/AudioInterface.cpp @@ -50,6 +50,8 @@ This file mainly deals with the [Drive I/F], however [AIDFR] controls TODO maybe the files should be merged? */ +#include "AudioCommon/AudioCommon.h" + #include "Common/Common.h" #include "Common/MathUtil.h" @@ -232,7 +234,10 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base) mmio->Register(base | AI_VOLUME_REGISTER, MMIO::DirectRead(&m_Volume.hex), - MMIO::DirectWrite(&m_Volume.hex) + MMIO::ComplexWrite([](u32, u32 val) { + m_Volume.hex = val; + soundStream->GetMixer()->SetStreamingVolume(m_Volume.left, m_Volume.right); + }) ); mmio->Register(base | AI_SAMPLE_COUNTER,