From: <man...@us...> - 2013-06-12 20:08:28
|
Revision: 2341 http://sourceforge.net/p/modplug/code/2341 Author: manxorist Date: 2013-06-12 20:08:21 +0000 (Wed, 12 Jun 2013) Log Message: ----------- [Ref] CSoundFile::Read has gotten somewhat unreadable over the years. Rewrite and reformat it completely to be hopefully more readable and maintainable. [Ref] Change CAGC::Process so that it works with seperate MixBuffer and RearMixBuffer so that all DSP effects can be processed in one go. [Ref] Move clearing the remaining unfilled output buffer to the only user that requires this behaviour: CMainFrame::AudioRead, because ISoundDevice requires to always get fully filled buffers. [Fix] Apply AGC volume changes to all channels at once instead of potentially decrementing volume in between processing different output channges. Also, limit ramp down speed to 1 step per sample (as it was for mono output). [Fix] Make the remaining global AGC state non-global. Modified Paths: -------------- trunk/OpenMPT/mptrack/MainFrm.cpp trunk/OpenMPT/sounddsp/AGC.cpp trunk/OpenMPT/sounddsp/AGC.h trunk/OpenMPT/sounddsp/DSP.cpp trunk/OpenMPT/sounddsp/DSP.h trunk/OpenMPT/soundlib/MixerSettings.h trunk/OpenMPT/soundlib/Sndfile.h trunk/OpenMPT/soundlib/Sndmix.cpp Modified: trunk/OpenMPT/mptrack/MainFrm.cpp =================================================================== --- trunk/OpenMPT/mptrack/MainFrm.cpp 2013-06-12 19:24:08 UTC (rev 2340) +++ trunk/OpenMPT/mptrack/MainFrm.cpp 2013-06-12 20:08:21 UTC (rev 2341) @@ -780,11 +780,26 @@ } -void CMainFrame::AudioRead(PVOID pvData, ULONG NumSamples) -//-------------------------------------------------------- +void CMainFrame::AudioRead(PVOID pvData, ULONG NumFrames) +//------------------------------------------------------- { OPENMPT_PROFILE_FUNCTION(Profiler::Audio); - m_pSndFile->ReadInterleaved(pvData, NumSamples); + CSoundFile::samplecount_t renderedFrames = m_pSndFile->ReadInterleaved(pvData, NumFrames); + ASSERT(renderedFrames <= NumFrames); + CSoundFile::samplecount_t remainingFrames = NumFrames - renderedFrames; + if(remainingFrames > 0) + { + // The sound device interface expects the whole buffer to be filled, always. + // Clear remaining buffer if not enough samples got rendered. + std::size_t frameSize = m_pSndFile->m_MixerSettings.gnChannels * (m_pSndFile->m_MixerSettings.GetBitsPerSample()/8); + if(m_pSndFile->m_MixerSettings.IsUnsignedSampleFormat()) + { + std::memset((char*)(pvData) + renderedFrames * frameSize, 0x80, remainingFrames * frameSize); + } else + { + std::memset((char*)(pvData) + renderedFrames * frameSize, 0, remainingFrames * frameSize); + } + } } @@ -931,10 +946,10 @@ int lmax = gnLVuMeter, rmax = gnRVuMeter; if (nChannels > 1) { - for (UINT i=0; i<nSamples; i+=nChannels) + for (UINT i=0; i<nSamples; i++) { - int vl = p[i]; - int vr = p[i+1]; + int vl = p[i*nChannels]; + int vr = p[i*nChannels+1]; if (vl < 0) vl = -vl; if (vr < 0) vr = -vr; if (vl > lmax) lmax = vl; @@ -944,7 +959,7 @@ { for (UINT i=0; i<nSamples; i++) { - int vl = p[i]; + int vl = p[i*nChannels]; if (vl < 0) vl = -vl; if (vl > lmax) lmax = vl; } Modified: trunk/OpenMPT/sounddsp/AGC.cpp =================================================================== --- trunk/OpenMPT/sounddsp/AGC.cpp 2013-06-12 19:24:08 UTC (rev 2340) +++ trunk/OpenMPT/sounddsp/AGC.cpp 2013-06-12 20:08:21 UTC (rev 2341) @@ -27,49 +27,87 @@ #define MIXING_LIMITMIN (-MIXING_LIMITMAX) -static UINT AGC(int *pBuffer, UINT nSamples, UINT nAGC) -//----------------------------------------------------- +static UINT ProcessAGC(int *pBuffer, int *pRearBuffer, UINT nSamples, UINT nChannels, int nAGC) +//--------------------------------------------------------------------------------------------- { - while(nSamples--) + if(nChannels == 1) { - int val = (int)(((int64)*pBuffer * (int32)nAGC) >> AGC_PRECISION); - if(val < MIXING_LIMITMIN || val > MIXING_LIMITMAX) nAGC--; - *pBuffer = val; - pBuffer++; + while(nSamples--) + { + int val = (int)(((int64)*pBuffer * (int32)nAGC) >> AGC_PRECISION); + if(val < MIXING_LIMITMIN || val > MIXING_LIMITMAX) nAGC--; + *pBuffer = val; + pBuffer++; + } + } else + { + if(nChannels == 2) + { + while(nSamples--) + { + int fl = (int)(((int64)pBuffer[0] * (int32)nAGC) >> AGC_PRECISION); + int fr = (int)(((int64)pBuffer[1] * (int32)nAGC) >> AGC_PRECISION); + bool dec = false; + dec = dec || (fl < MIXING_LIMITMIN || fl > MIXING_LIMITMAX); + dec = dec || (fr < MIXING_LIMITMIN || fr > MIXING_LIMITMAX); + if(dec) nAGC--; + pBuffer[0] = fl; + pBuffer[1] = fr; + pBuffer += 2; + } + } else if(nChannels == 4) + { + while(nSamples--) + { + int fl = (int)(((int64)pBuffer[0] * (int32)nAGC) >> AGC_PRECISION); + int fr = (int)(((int64)pBuffer[1] * (int32)nAGC) >> AGC_PRECISION); + int rl = (int)(((int64)pRearBuffer[0] * (int32)nAGC) >> AGC_PRECISION); + int rr = (int)(((int64)pRearBuffer[1] * (int32)nAGC) >> AGC_PRECISION); + bool dec = false; + dec = dec || (fl < MIXING_LIMITMIN || fl > MIXING_LIMITMAX); + dec = dec || (fr < MIXING_LIMITMIN || fr > MIXING_LIMITMAX); + dec = dec || (rl < MIXING_LIMITMIN || rl > MIXING_LIMITMAX); + dec = dec || (rr < MIXING_LIMITMIN || rr > MIXING_LIMITMAX); + if(dec) nAGC--; + pBuffer[0] = fl; + pBuffer[1] = fr; + pRearBuffer[0] = rl; + pRearBuffer[1] = rr; + pBuffer += 2; + pRearBuffer += 2; + } + } } return nAGC; } -CAGC::CAGC() +CAGC::CAGC() //---------- { - m_nAGC = AGC_UNITY; + Initialize(TRUE, 44100); } -void CAGC::Process(int * MixSoundBuffer, int count, DWORD MixingFreq, UINT nChannels) -//----------------------------------------------------------------------------------- +void CAGC::Process(int *MixSoundBuffer, int *RearSoundBuffer, int count, UINT nChannels) +//-------------------------------------------------------------------------------------- { - static DWORD gAGCRecoverCount = 0; - UINT agc = AGC(MixSoundBuffer, count, m_nAGC); + UINT agc = ProcessAGC(MixSoundBuffer, RearSoundBuffer, count, nChannels, m_nAGC); // Some kind custom law, so that the AGC stays quite stable, but slowly // goes back up if the sound level stays below a level inversely proportional // to the AGC level. (J'me comprends) - if ((agc >= m_nAGC) && (m_nAGC < AGC_UNITY)) + if((agc >= m_nAGC) && (m_nAGC < AGC_UNITY)) { - gAGCRecoverCount += count; - UINT agctimeout = MixingFreq >> (AGC_PRECISION-8); - if (nChannels < 2) agctimeout >>= 1; - if (gAGCRecoverCount >= agctimeout) + m_nAGCRecoverCount += count; + if(m_nAGCRecoverCount >= m_Timeout) { - gAGCRecoverCount = 0; + m_nAGCRecoverCount = 0; m_nAGC++; } } else { m_nAGC = agc; - gAGCRecoverCount = 0; + m_nAGCRecoverCount = 0; } } @@ -82,10 +120,15 @@ } -void CAGC::Reset() -//---------------- +void CAGC::Initialize(BOOL bReset, DWORD MixingFreq) +//-------------------------------------------------- { - m_nAGC = AGC_UNITY; + if(bReset) + { + m_nAGC = AGC_UNITY; + m_nAGCRecoverCount = 0; + } + m_Timeout = (MixingFreq >> (AGC_PRECISION-8)) >> 1; } Modified: trunk/OpenMPT/sounddsp/AGC.h =================================================================== --- trunk/OpenMPT/sounddsp/AGC.h 2013-06-12 19:24:08 UTC (rev 2340) +++ trunk/OpenMPT/sounddsp/AGC.h 2013-06-12 20:08:21 UTC (rev 2341) @@ -19,14 +19,14 @@ { private: UINT m_nAGC; + DWORD m_nAGCRecoverCount; + UINT m_Timeout; public: CAGC(); - ~CAGC() {} + void Initialize(BOOL bReset, DWORD MixingFreq); public: - void Process(int * MixSoundBuffer, int count, DWORD MixingFreq, UINT nChannels); + void Process(int *MixSoundBuffer, int *RearSoundBuffer, int count, UINT nChannels); void Adjust(UINT oldVol, UINT newVol); - void Reset(); -private: }; #endif // NO_AGC Modified: trunk/OpenMPT/sounddsp/DSP.cpp =================================================================== --- trunk/OpenMPT/sounddsp/DSP.cpp 2013-06-12 19:24:08 UTC (rev 2340) +++ trunk/OpenMPT/sounddsp/DSP.cpp 2013-06-12 20:08:21 UTC (rev 2341) @@ -281,7 +281,7 @@ } -void CDSP::Process(int * MixSoundBuffer, int * MixRearBuffer, int count, DWORD DSPMask, UINT nChannels) +void CDSP::Process(int * MixSoundBuffer, int * MixRearBuffer, int count, UINT nChannels, DWORD DSPMask) //----------------------------------------------------------------------------------------------------- { @@ -296,14 +296,10 @@ if (nChannels > 2) ProcessQuadSurround(MixSoundBuffer, MixRearBuffer, count); else ProcessStereoSurround(MixSoundBuffer, count); } - // DC Removal + // Bass Expansion if (DSPMask & SNDDSP_MEGABASS) { X86_StereoDCRemoval(MixSoundBuffer, count, &nDCRFlt_Y1l, &nDCRFlt_X1l, &nDCRFlt_Y1r, &nDCRFlt_X1r); - } - // Bass Expansion - if (DSPMask & SNDDSP_MEGABASS) - { int *px = MixSoundBuffer; int x1 = nXBassFlt_X1; int y1 = nXBassFlt_Y1; @@ -344,15 +340,11 @@ } else { - - // DC Removal + + // Bass Expansion if (DSPMask & SNDDSP_MEGABASS) { X86_MonoDCRemoval(MixSoundBuffer, count, &nDCRFlt_Y1l, &nDCRFlt_X1l); - } - // Bass Expansion - if (DSPMask & SNDDSP_MEGABASS) - { int *px = MixSoundBuffer; int x1 = nXBassFlt_X1; int y1 = nXBassFlt_Y1; Modified: trunk/OpenMPT/sounddsp/DSP.h =================================================================== --- trunk/OpenMPT/sounddsp/DSP.h 2013-06-12 19:24:08 UTC (rev 2340) +++ trunk/OpenMPT/sounddsp/DSP.h 2013-06-12 20:08:21 UTC (rev 2341) @@ -77,7 +77,6 @@ public: CDSP(); - ~CDSP() {} public: void SetSettings(const CDSPSettings &settings) { m_Settings = settings; } // [XBass level 0(quiet)-100(loud)], [cutoff in Hz 10-100] @@ -85,7 +84,7 @@ // [Surround level 0(quiet)-100(heavy)] [delay in ms, usually 5-40ms] BOOL SetSurroundParameters(UINT nDepth, UINT nDelay); void Initialize(BOOL bReset, DWORD MixingFreq, DWORD DSPMask); - void Process(int * MixSoundBuffer, int * MixRearBuffer, int count, DWORD DSPMask, UINT nChannels); + void Process(int * MixSoundBuffer, int * MixRearBuffer, int count, UINT nChannels, DWORD DSPMask); private: void ProcessStereoSurround(int * MixSoundBuffer, int count); void ProcessQuadSurround(int * MixSoundBuffer, int * MixRearBuffer, int count); Modified: trunk/OpenMPT/soundlib/MixerSettings.h =================================================================== --- trunk/OpenMPT/soundlib/MixerSettings.h 2013-06-12 19:24:08 UTC (rev 2340) +++ trunk/OpenMPT/soundlib/MixerSettings.h 2013-06-12 20:08:21 UTC (rev 2341) @@ -45,14 +45,31 @@ DWORD m_FinalOutputGain; // factor multiplied to the final mixer output just before clipping and dithering, fixed point 16.16 #endif + bool IsValid() const + { + return true + && (gnChannels == 1 || gnChannels == 2 || gnChannels == 4) + && (m_SampleFormat != SampleFormatInvalid) + && (gdwMixingFreq > 0) + && (GetBitsPerSample() % 8 == 0) + && (GetBitsPerSample() > 0) + ; + } bool IsUnsignedSampleFormat() const { + if(m_SampleFormat == SampleFormatInvalid) return false; return m_SampleFormat == SampleFormatUnsigned8; } bool IsFloatSampleFormat() const { + if(m_SampleFormat == SampleFormatInvalid) return false; return m_SampleFormat == SampleFormatFloat32; } + bool IsIntSampleFormat() const + { + if(m_SampleFormat == SampleFormatInvalid) return false; + return m_SampleFormat != SampleFormatFloat32; + } uint8 GetBitsPerSample() const { switch(m_SampleFormat) @@ -72,7 +89,9 @@ case SampleFormatFloat32: return 32; break; - default: return 8; break; + default: + return 0; + break; } } Modified: trunk/OpenMPT/soundlib/Sndfile.h =================================================================== --- trunk/OpenMPT/soundlib/Sndfile.h 2013-06-12 19:24:08 UTC (rev 2340) +++ trunk/OpenMPT/soundlib/Sndfile.h 2013-06-12 20:08:21 UTC (rev 2341) @@ -640,12 +640,15 @@ samplecount_t ReadInterleaved(void *outputBuffer, samplecount_t count); samplecount_t ReadNonInterleaved(void * const *outputBuffers, samplecount_t count); private: - UINT Read(UINT cbBuffer, LPVOID lpBuffer, void * const *outputBuffers = nullptr); + samplecount_t Read(samplecount_t count, void *outputBuffer, void * const *outputBuffers); void CreateStereoMix(int count); public: BOOL FadeSong(UINT msec); BOOL GlobalFadeSong(UINT msec); +private: + void ProcessDSP(std::size_t countChunk); void ProcessPlugins(UINT nCount); +public: samplecount_t GetTotalSampleCount() const { return m_lTotalSampleCount; } bool HasPositionChanged() { bool b = m_bPositionChanged; m_bPositionChanged = false; return b; } bool IsRenderingToDisc() const { return m_bIsRendering; } @@ -830,13 +833,15 @@ #ifdef MODPLUG_TRACKER void ProcessMidiOut(CHANNELINDEX nChn); #endif // MODPLUG_TRACKER - void ApplyGlobalVolume(int *SoundBuffer, int *RearBuffer, long lCount); + void ApplyGlobalVolume(int *SoundBuffer, int *RearBuffer, long countChunk); #ifndef MODPLUG_TRACKER - void ApplyFinalOutputGain(int SoundBuffer[], int RearBuffer[], long lCount); // lCount meaning the number of frames, totally independet from the numer of channels - void ApplyFinalOutputGainFloat(float *beg, float *end); -#endif + void ApplyFinalOutputGain(int *soundBuffer, std::size_t countChunk); + void ApplyFinalOutputGainFloat(float *outputBuffer, float * const *outputBuffers, std::size_t offset, std::size_t channels, std::size_t countChunk); +#endif // !MODPLUG_TRACKER + void ConvertMixBufferToOutput(void *outputBuffer, void * const *outputBuffers, std::size_t countRendered, std::size_t countChunk); + // System-Dependant functions public: static void *AllocateSample(UINT nbytes); Modified: trunk/OpenMPT/soundlib/Sndmix.cpp =================================================================== --- trunk/OpenMPT/soundlib/Sndmix.cpp 2013-06-12 19:24:08 UTC (rev 2340) +++ trunk/OpenMPT/soundlib/Sndmix.cpp 2013-06-12 20:08:21 UTC (rev 2341) @@ -33,33 +33,12 @@ PMIXPLUGINCREATEPROC CSoundFile::gpMixPluginCreateProc = NULL; #endif -typedef void (* LPCONVERTPROC)(LPVOID, int *, DWORD); -static void Convert32To8( LPVOID lpBuffer, int *pBuffer, DWORD nSamples) { return Convert32ToInterleaved((uint8*)lpBuffer, pBuffer, nSamples); } -static void Convert32To16(LPVOID lpBuffer, int *pBuffer, DWORD nSamples) { return Convert32ToInterleaved((int16*)lpBuffer, pBuffer, nSamples); } -static void Convert32To24(LPVOID lpBuffer, int *pBuffer, DWORD nSamples) { return Convert32ToInterleaved((int24*)lpBuffer, pBuffer, nSamples); } -static void Convert32To32(LPVOID lpBuffer, int *pBuffer, DWORD nSamples) { return Convert32ToInterleaved((int32*)lpBuffer, pBuffer, nSamples); } -static void Convert32ToFloat32(LPVOID lpBuffer, int *pBuffer, DWORD nSamples) { return Convert32ToInterleaved((float*)lpBuffer, pBuffer, nSamples); } +void InterleaveFrontRear(int *pFrontBuf, int *pRearBuf, DWORD nFrames); +void StereoFill(int *pBuffer, UINT nSamples, LPLONG lpROfs, LPLONG lpLOfs); +void MonoFromStereo(int *pMixBuf, UINT nSamples); -template<typename Tsample> -void Convert32ToNonInterleaved(void * const *outputBuffers, std::size_t offset, const int *mixbuffer, std::size_t channels, std::size_t count) -{ - Tsample *buffers[4]; - MemsetZero(buffers); - for(std::size_t channel = 0; channel < channels; ++channel) - { - buffers[channel] = reinterpret_cast<Tsample*>(outputBuffers[channel]); - buffers[channel] += offset; // skip to output position - } - Convert32ToNonInterleaved(buffers, mixbuffer, channels, count); -} - - -extern void InterleaveFrontRear(int *pFrontBuf, int *pRearBuf, DWORD nFrames); -extern void StereoFill(int *pBuffer, UINT nSamples, LPLONG lpROfs, LPLONG lpLOfs); -extern void MonoFromStereo(int *pMixBuf, UINT nSamples); - // Log tables for pre-amp // Pre-amp (or more precisely: Pre-attenuation) depends on the number of channels, // Which this table takes care of. @@ -144,7 +123,7 @@ m_EQ.Initialize(bReset, m_MixerSettings.gdwMixingFreq); #endif #ifndef NO_AGC - if(bReset) m_AGC.Reset(); + m_AGC.Initialize(bReset, m_MixerSettings.gdwMixingFreq); #endif m_Dither.Reset(); } @@ -171,7 +150,6 @@ pramp->nRampLength = nRampLength; pramp->dwFlags.set(CHN_VOLUMERAMP); } - m_SongFlags.set(SONG_FADINGSONG); return TRUE; } @@ -201,239 +179,246 @@ } -UINT CSoundFile::Read(UINT count, LPVOID lpDestBuffer, void * const *outputBuffers) -//--------------------------------------------------------------------------------- +CSoundFile::samplecount_t CSoundFile::Read(samplecount_t count, void *outputBuffer, void * const *outputBuffers) +//-------------------------------------------------------------------------------------------------------------- { - LPBYTE lpBuffer = (LPBYTE)lpDestBuffer; - LPCONVERTPROC pCvt = nullptr; - samplecount_t lMax, lCount, lSampleCount; - size_t lSampleSize; - UINT nMaxPlugins; - std::size_t renderedCount = 0; + ALWAYS_ASSERT(m_MixerSettings.IsValid()); - nMaxPlugins = MAX_MIXPLUGINS; - while ((nMaxPlugins > 0) && (!m_MixPlugins[nMaxPlugins-1].pMixPlugin)) nMaxPlugins--; - - UINT nMixStatCount = 0; + int mixStatCount = 0; - lSampleSize = m_MixerSettings.gnChannels; - switch(m_MixerSettings.m_SampleFormat) + bool mixPlugins = false; + for(PLUGINDEX i = 0; i < MAX_MIXPLUGINS; ++i) { - case SampleFormatUnsigned8: pCvt = Convert32To8 ; break; - case SampleFormatInt16: pCvt = Convert32To16; break; - case SampleFormatInt24: pCvt = Convert32To24; break; - case SampleFormatInt32: pCvt = Convert32To32; break; - case SampleFormatFloat32: pCvt = Convert32ToFloat32; break; - default: return 0; break; + if(m_MixPlugins[i].pMixPlugin) + { + mixPlugins = true; + break; + } } - lSampleSize *= m_MixerSettings.GetBitsPerSample()/8; - lMax = count; - if((!lMax) || ((!lpBuffer) && (!outputBuffers)) || (!m_nChannels)) return 0; - samplecount_t lRead = lMax; - if(m_SongFlags[SONG_ENDREACHED]) - goto MixDone; + const samplecount_t countGoal = count; + samplecount_t countRendered = 0; + samplecount_t countToRender = countGoal; - while (lRead > 0) + while(!m_SongFlags[SONG_ENDREACHED] && countToRender > 0) { + // Update Channel Data - if (!m_nBufferCount) - { + if(!m_nBufferCount) + { // last tick or fade completely processed, find out what to do next if(m_SongFlags[SONG_FADINGSONG]) - { + { // song was faded out m_SongFlags.set(SONG_ENDREACHED); - m_nBufferCount = lRead; + } else if(ReadNote()) + { // render next tick (normal progress) + ASSERT(m_nBufferCount > 0); + #ifdef MODPLUG_TRACKER + // Save pattern cue points for WAV rendering here (if we reached a new pattern, that is.) + if(IsRenderingToDisc() && (m_PatternCuePoints.empty() || m_nCurrentOrder != m_PatternCuePoints.back().order)) + { + PatternCuePoint cue; + cue.offset = countRendered; + cue.order = m_nCurrentOrder; + cue.processed = false; // We don't know the base offset in the file here. It has to be added in the main conversion loop. + m_PatternCuePoints.push_back(cue); + } + #endif } else - - if (ReadNote()) - { -#ifdef MODPLUG_TRACKER - // Save pattern cue points for WAV rendering here (if we reached a new pattern, that is.) - if(IsRenderingToDisc() && (m_PatternCuePoints.empty() || m_nCurrentOrder != m_PatternCuePoints.back().order)) - { - PatternCuePoint cue; - cue.offset = lMax - lRead; - cue.order = m_nCurrentOrder; - cue.processed = false; // We don't know the base offset in the file here. It has to be added in the main conversion loop. - m_PatternCuePoints.push_back(cue); - } -#endif - } else - { -#ifdef MODPLUG_TRACKER - if ((m_nMaxOrderPosition) && (m_nCurrentOrder >= m_nMaxOrderPosition)) - { + { // no new pattern data + #ifdef MODPLUG_TRACKER + if((m_nMaxOrderPosition) && (m_nCurrentOrder >= m_nMaxOrderPosition)) + { + m_SongFlags.set(SONG_ENDREACHED); + } + #endif // MODPLUG_TRACKER + if(IsRenderingToDisc()) + { // rewbs: disable song fade when rendering. m_SongFlags.set(SONG_ENDREACHED); - break; + } else + { // end of song reached, fade it out + if(FadeSong(FADESONGDELAY)) // sets m_nBufferCount xor returns false + { // FadeSong sets m_nBufferCount here + ASSERT(m_nBufferCount > 0); + m_SongFlags.set(SONG_FADINGSONG); + } else + { + m_SongFlags.set(SONG_ENDREACHED); + } } -#endif // MODPLUG_TRACKER + } - if (!FadeSong(FADESONGDELAY) || IsRenderingToDisc()) //rewbs: disable song fade when rendering. - { - m_SongFlags.set(SONG_ENDREACHED); - if (lRead == lMax || IsRenderingToDisc()) //rewbs: don't complete buffer when rendering - goto MixDone; - m_nBufferCount = lRead; - } - } } - lCount = m_nBufferCount; - if (lCount > MIXBUFFERSIZE) lCount = MIXBUFFERSIZE; - if (lCount > lRead) lCount = lRead; - if (!lCount) - break; + if(m_SongFlags[SONG_ENDREACHED]) + { + break; // mix done + } - ASSERT(lCount <= MIXBUFFERSIZE); // ensure MIXBUFFERSIZE really is our max buffer size + ASSERT(m_nBufferCount > 0); // assert that we have actually something to do - lSampleCount = lCount; + const samplecount_t countChunk = std::min<samplecount_t>(MIXBUFFERSIZE, std::min<samplecount_t>(m_nBufferCount, countToRender)); - if(nMixStatCount == 0) - { - // reset mixer channel count before we are calling CreateStereoMix the first time this round, if we are not updating the mixer state, we do not reset statistics + if(mixStatCount == 0) + { // reset mixer channel count before we are calling CreateStereoMix the first time this round, if we are not updating the mixer state, we do not reset statistics m_nMixStat = 0; } - nMixStatCount++; - - if (m_MixerSettings.gnChannels >= 2) - { - lSampleCount *= 2; - CreateStereoMix(lCount); + mixStatCount++; -#ifndef NO_REVERB - m_Reverb.Process(MixSoundBuffer, lCount); -#endif // NO_REVERB + CreateStereoMix(countChunk); - if (nMaxPlugins) ProcessPlugins(lCount); + #ifndef NO_REVERB + m_Reverb.Process(MixSoundBuffer, countChunk); + #endif // NO_REVERB - // Apply global volume - if (m_PlayConfig.getGlobalVolumeAppliesToMaster()) - { - ApplyGlobalVolume(MixSoundBuffer, MixRearBuffer, lCount); - } - } else + if(mixPlugins) { - CreateStereoMix(lCount); + ProcessPlugins(countChunk); + } -#ifndef NO_REVERB - m_Reverb.Process(MixSoundBuffer, lCount); -#endif // NO_REVERB + if(m_MixerSettings.gnChannels == 1) + { + MonoFromStereo(MixSoundBuffer, countChunk); + } - if (nMaxPlugins) ProcessPlugins(lCount); - MonoFromStereo(MixSoundBuffer, lCount); - - // Apply global volume - if (m_PlayConfig.getGlobalVolumeAppliesToMaster()) - { - ApplyGlobalVolume(MixSoundBuffer, MixRearBuffer, lCount); - } + if(m_PlayConfig.getGlobalVolumeAppliesToMaster()) + { + ApplyGlobalVolume(MixSoundBuffer, MixRearBuffer, countChunk); } -#ifndef NO_DSP - m_DSP.Process(MixSoundBuffer, MixRearBuffer, lCount, m_MixerSettings.DSPMask, m_MixerSettings.gnChannels); -#endif - -#ifndef NO_EQ - // Graphic Equalizer - if (m_MixerSettings.DSPMask & SNDDSP_EQ) + if(m_MixerSettings.DSPMask) { - m_EQ.Process(MixSoundBuffer, MixRearBuffer, lCount, m_MixerSettings.gnChannels); + ProcessDSP(countChunk); } -#endif // NO_EQ -#ifndef MODPLUG_TRACKER - if(!m_MixerSettings.IsFloatSampleFormat()) + if(m_MixerSettings.gnChannels == 4) { - // Apply final output gain for non floating point output - ApplyFinalOutputGain(MixSoundBuffer, MixRearBuffer, lCount); + InterleaveFrontRear(MixSoundBuffer, MixRearBuffer, countChunk); } -#endif -#ifndef NO_AGC - // Automatic Gain Control - if (m_MixerSettings.DSPMask & SNDDSP_AGC) m_AGC.Process(MixSoundBuffer, lSampleCount, m_MixerSettings.gdwMixingFreq, m_MixerSettings.gnChannels); -#endif // NO_AGC + #ifdef MODPLUG_TRACKER + if(gpSndMixHook) + { // Currently only used for VU Meter + gpSndMixHook(MixSoundBuffer, countChunk, m_MixerSettings.gnChannels); + } + #endif // MODPLUG_TRACKER - UINT lTotalSampleCount = lSampleCount; // Including rear channels + // Convert to output sample format and optionally perform dithering and clipping if needed + ConvertMixBufferToOutput(outputBuffer, outputBuffers, countRendered, countChunk); - // Multichannel - if (m_MixerSettings.gnChannels > 2) + // Buffer ready + countRendered += countChunk; + countToRender -= countChunk; + m_nBufferCount -= countChunk; + m_lTotalSampleCount += countChunk; // increase sample count for VSTTimeInfo. + + } + + // mix done + + if(mixStatCount > 0) + { + m_nMixStat = (m_nMixStat + mixStatCount - 1) / mixStatCount; // round up + } + + return countRendered; + +} + + +void CSoundFile::ProcessDSP(std::size_t countChunk) +//------------------------------------------------- +{ + #ifndef NO_DSP + if(m_MixerSettings.DSPMask & (SNDDSP_SURROUND|SNDDSP_MEGABASS|SNDDSP_NOISEREDUCTION)) { - InterleaveFrontRear(MixSoundBuffer, MixRearBuffer, lCount); - lTotalSampleCount *= 2; + m_DSP.Process(MixSoundBuffer, MixRearBuffer, countChunk, m_MixerSettings.gnChannels, m_MixerSettings.DSPMask); } + #endif // NO_DSP - // Noise Shaping - if(m_Resampler.IsHQ()) - m_Dither.Process(MixSoundBuffer, lCount, m_MixerSettings.gnChannels, m_MixerSettings.GetBitsPerSample()); - -#ifdef MODPLUG_TRACKER - // Hook Function - if (gpSndMixHook) + #ifndef NO_EQ + if(m_MixerSettings.DSPMask & SNDDSP_EQ) { - //Currently only used for VU Meter, so it's OK to do it after global Vol. - gpSndMixHook(MixSoundBuffer, lTotalSampleCount, m_MixerSettings.gnChannels); + m_EQ.Process(MixSoundBuffer, MixRearBuffer, countChunk, m_MixerSettings.gnChannels); } -#endif + #endif // NO_EQ - if(lpBuffer) // old style interleaved output buffer + #ifndef NO_AGC + if(m_MixerSettings.DSPMask & SNDDSP_AGC) { - #ifndef MODPLUG_TRACKER - LPBYTE buf_beg = lpBuffer; - #endif + m_AGC.Process(MixSoundBuffer, MixRearBuffer, countChunk, m_MixerSettings.gnChannels); + } + #endif // NO_AGC +} - // Convert to output sample format and optionally perform clipping if needed - pCvt(lpBuffer, MixSoundBuffer, lTotalSampleCount); - lpBuffer += lTotalSampleCount * (m_MixerSettings.GetBitsPerSample()/8); - #ifndef MODPLUG_TRACKER - LPBYTE buf_end = lpBuffer; - // Apply final output gain for floating point output after conversion so we do not suffer underflow or clipping - if(m_MixerSettings.IsFloatSampleFormat()) - { - ApplyFinalOutputGainFloat(reinterpret_cast<float*>(buf_beg), reinterpret_cast<float*>(buf_end)); - } - #endif +template<typename Tsample> +void ConvertToOutput(void *outputBuffer, void * const *outputBuffers, std::size_t countRendered, int *mixbuffer, std::size_t countChunk, std::size_t channels) +//------------------------------------------------------------------------------------------------------------------------------------------------------------ +{ + if(outputBuffer) + { + Convert32ToInterleaved(reinterpret_cast<Tsample*>(outputBuffer) + (channels * countRendered), mixbuffer, channels * countChunk); + } + if(outputBuffers) + { + Tsample *buffers[4] = { nullptr, nullptr, nullptr, nullptr }; + for(std::size_t channel = 0; channel < channels; ++channel) + { + buffers[channel] = reinterpret_cast<Tsample*>(outputBuffers[channel]) + countRendered; } + Convert32ToNonInterleaved(buffers, mixbuffer, channels, countChunk); + } +} - if(outputBuffers) // non-interleaved one output buffer per channel + +void CSoundFile::ConvertMixBufferToOutput(void *outputBuffer, void * const *outputBuffers, std::size_t countRendered, std::size_t countChunk) +//------------------------------------------------------------------------------------------------------------------------------------------- +{ + // Convert to output sample format and optionally perform dithering and clipping if needed + + #ifndef MODPLUG_TRACKER + if(m_MixerSettings.IsIntSampleFormat()) { - switch(m_MixerSettings.m_SampleFormat) - { - case SampleFormatUnsigned8: Convert32ToNonInterleaved<uint8>(outputBuffers, renderedCount, MixSoundBuffer, m_MixerSettings.gnChannels, lCount); break; - case SampleFormatInt16: Convert32ToNonInterleaved<int16>(outputBuffers, renderedCount, MixSoundBuffer, m_MixerSettings.gnChannels, lCount); break; - case SampleFormatInt24: Convert32ToNonInterleaved<int24>(outputBuffers, renderedCount, MixSoundBuffer, m_MixerSettings.gnChannels, lCount); break; - case SampleFormatInt32: Convert32ToNonInterleaved<int32>(outputBuffers, renderedCount, MixSoundBuffer, m_MixerSettings.gnChannels, lCount); break; - case SampleFormatFloat32: Convert32ToNonInterleaved<float>(outputBuffers, renderedCount, MixSoundBuffer, m_MixerSettings.gnChannels, lCount); break; - default: - break; - } - #ifndef MODPLUG_TRACKER - if(m_MixerSettings.IsFloatSampleFormat()) - { - // Apply final output gain for floating point output after conversion so we do not suffer underflow or clipping - for(std::size_t channel = 0; channel < m_MixerSettings.gnChannels; ++channel) - { - ApplyFinalOutputGainFloat(reinterpret_cast<float*>(outputBuffers[channel]) + renderedCount, reinterpret_cast<float*>(outputBuffers[channel]) + renderedCount + lCount); - } - } - #endif // !MODPLUG_TRACKER + // Apply final output gain for non floating point output + ApplyFinalOutputGain(MixSoundBuffer, countChunk); } + #endif // !MODPLUG_TRACKER - // Buffer ready - renderedCount += lCount; - lRead -= lCount; - m_nBufferCount -= lCount; - m_lTotalSampleCount += lCount; // increase sample count for VSTTimeInfo. + if(m_MixerSettings.IsIntSampleFormat()) + { + m_Dither.Process(MixSoundBuffer, countChunk, m_MixerSettings.gnChannels, m_MixerSettings.GetBitsPerSample()); } -MixDone: - if (lRead && lpBuffer) memset(lpBuffer, (m_MixerSettings.m_SampleFormat == SampleFormatUnsigned8) ? 0x80 : 0, lRead * lSampleSize); // clear remaining interleaved output buffer - if(nMixStatCount > 0) + + switch(m_MixerSettings.m_SampleFormat) { - m_nMixStat = (m_nMixStat + nMixStatCount - 1) / nMixStatCount; // round up + case SampleFormatUnsigned8: + ConvertToOutput<uint8>(outputBuffer, outputBuffers, countRendered, MixSoundBuffer, countChunk, m_MixerSettings.gnChannels); + break; + case SampleFormatInt16: + ConvertToOutput<int16>(outputBuffer, outputBuffers, countRendered, MixSoundBuffer, countChunk, m_MixerSettings.gnChannels); + break; + case SampleFormatInt24: + ConvertToOutput<int24>(outputBuffer, outputBuffers, countRendered, MixSoundBuffer, countChunk, m_MixerSettings.gnChannels); + break; + case SampleFormatInt32: + ConvertToOutput<int32>(outputBuffer, outputBuffers, countRendered, MixSoundBuffer, countChunk, m_MixerSettings.gnChannels); + break; + case SampleFormatFloat32: + ConvertToOutput<float>(outputBuffer, outputBuffers, countRendered, MixSoundBuffer, countChunk, m_MixerSettings.gnChannels); + break; + case SampleFormatInvalid: + break; } - return renderedCount; + + #ifndef MODPLUG_TRACKER + if(m_MixerSettings.IsFloatSampleFormat()) + { + // Apply final output gain for floating point output after conversion so we do not suffer underflow or clipping + ApplyFinalOutputGainFloat(reinterpret_cast<float*>(outputBuffer), reinterpret_cast<float*const*>(outputBuffers), countRendered, m_MixerSettings.gnChannels, countChunk); + } + #endif // !MODPLUG_TRACKER + } @@ -1700,7 +1685,8 @@ //////////////////////////////////////////////////////////////////////////////////// if (!m_nMusicTempo) return FALSE; - m_nSamplesPerTick = m_nBufferCount = GetTickDuration(m_nMusicTempo, m_nMusicSpeed, m_nCurrentRowsPerBeat); + m_nSamplesPerTick = GetTickDuration(m_nMusicTempo, m_nMusicSpeed, m_nCurrentRowsPerBeat); + m_nBufferCount = m_nSamplesPerTick; // Master Volume + Pre-Amplification / Attenuation setup DWORD nMasterVol; @@ -2362,7 +2348,10 @@ #ifndef MODPLUG_TRACKER -void CSoundFile::ApplyFinalOutputGain(int SoundBuffer[], int RearBuffer[], long lCount) { + +void CSoundFile::ApplyFinalOutputGain(int *soundBuffer, std::size_t countChunk) +//----------------------------------------------------------------------------- +{ if(m_MixerSettings.m_FinalOutputGain == (1<<16)) { // nothing to do, gain == +/- 0dB @@ -2370,45 +2359,51 @@ } // no clipping prevention is done here int32 factor = m_MixerSettings.m_FinalOutputGain; - int * buf = SoundBuffer; - int * rbuf = RearBuffer; - if(m_MixerSettings.gnChannels == 1) + int * buf = soundBuffer; + for(std::size_t i=0; i<countChunk*m_MixerSettings.gnChannels; ++i) { - for(int i=0; i<lCount; ++i) - { - *buf = Util::muldiv(*buf, factor, 1<<16); - buf++; - } - } else if(m_MixerSettings.gnChannels == 2) + *buf = Util::muldiv(*buf, factor, 1<<16); + buf++; + } +} + +static void ApplyFinalOutputGainFloatBuffer(float *beg, float *end, float factor) +//------------------------------------------------------------------------------- +{ + // no clipping is done here + for(float *i = beg; i != end; ++i) { - for(int i=0; i<lCount*2; ++i) - { - *buf = Util::muldiv(*buf, factor, 1<<16); - buf++; - } - } else if(m_MixerSettings.gnChannels == 4) - { - for(int i=0; i<lCount*2; ++i) - { - *buf = Util::muldiv(*buf, factor, 1<<16); - *rbuf = Util::muldiv(*rbuf, factor, 1<<16); - buf++; - rbuf++; - } + *i *= factor; } } -void CSoundFile::ApplyFinalOutputGainFloat(float *beg, float *end) { + +void CSoundFile::ApplyFinalOutputGainFloat(float * outputBuffer, float * const *outputBuffers, std::size_t offset, std::size_t channels, std::size_t countChunk) +//-------------------------------------------------------------------------------------------------------------------------------------------------------------- +{ if(m_MixerSettings.m_FinalOutputGain == (1<<16)) { // nothing to do, gain == +/- 0dB - return; + return; } - // no clipping prevention is done here - float factor = static_cast<float>(m_MixerSettings.m_FinalOutputGain) * (1.0f / static_cast<float>(1<<16)); - for(float *i = beg; i != end; ++i) + const float factor = static_cast<float>(m_MixerSettings.m_FinalOutputGain) * (1.0f / static_cast<float>(1<<16)); + if(outputBuffer) { - *i *= factor; + ApplyFinalOutputGainFloatBuffer( + outputBuffer + (channels * offset), + outputBuffer + (channels * (offset + countChunk)), + factor); } + if(outputBuffers) + { + for(std::size_t channel = 0; channel < channels; ++channel) + { + ApplyFinalOutputGainFloatBuffer( + outputBuffers[channel] + offset, + outputBuffers[channel] + offset + countChunk, + factor); + } + } } -#endif +#endif // !MODPLUG_TRACKER + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |