From: <man...@us...> - 2013-07-04 15:02:01
|
Revision: 2496 http://sourceforge.net/p/modplug/code/2496 Author: manxorist Date: 2013-07-04 15:01:53 +0000 (Thu, 04 Jul 2013) Log Message: ----------- [Ref] WAV export: Rewrite normalization. [Fix] WAV export: Do not clamp rendered output before normalizing. [Imp] WAV export: Also support normalization for 32bit float output files. [Fix] WAV export: Properly dither normalized output files. [Ref] Remove soundlib/Waveform.cpp which is not needed anymore. [Ref] Remove deprecated DitherStateless. Modified Paths: -------------- trunk/OpenMPT/libopenmpt/libopenmpt.vcxproj trunk/OpenMPT/libopenmpt/libopenmpt.vcxproj.filters trunk/OpenMPT/mptrack/Mod2wave.cpp trunk/OpenMPT/mptrack/mptrack.rc trunk/OpenMPT/mptrack/mptrack_08.vcproj trunk/OpenMPT/mptrack/mptrack_10.vcxproj trunk/OpenMPT/mptrack/mptrack_10.vcxproj.filters trunk/OpenMPT/soundlib/Fastmix.cpp trunk/OpenMPT/soundlib/Sndfile.h Removed Paths: ------------- trunk/OpenMPT/soundlib/Waveform.cpp Modified: trunk/OpenMPT/libopenmpt/libopenmpt.vcxproj =================================================================== --- trunk/OpenMPT/libopenmpt/libopenmpt.vcxproj 2013-07-04 13:26:51 UTC (rev 2495) +++ trunk/OpenMPT/libopenmpt/libopenmpt.vcxproj 2013-07-04 15:01:53 UTC (rev 2496) @@ -450,7 +450,6 @@ <ClCompile Include="..\soundlib\tuning.cpp" /> <ClCompile Include="..\soundlib\tuningbase.cpp" /> <ClCompile Include="..\soundlib\tuningCollection.cpp" /> - <ClCompile Include="..\soundlib\Waveform.cpp" /> <ClCompile Include="..\soundlib\WAVTools.cpp" /> <ClCompile Include="..\soundlib\WindowedFIR.cpp" /> <ClCompile Include="..\soundlib\XMTools.cpp" /> Modified: trunk/OpenMPT/libopenmpt/libopenmpt.vcxproj.filters =================================================================== --- trunk/OpenMPT/libopenmpt/libopenmpt.vcxproj.filters 2013-07-04 13:26:51 UTC (rev 2495) +++ trunk/OpenMPT/libopenmpt/libopenmpt.vcxproj.filters 2013-07-04 15:01:53 UTC (rev 2496) @@ -439,9 +439,6 @@ <ClCompile Include="..\soundlib\tuningCollection.cpp"> <Filter>Source Files\soundlib</Filter> </ClCompile> - <ClCompile Include="..\soundlib\Waveform.cpp"> - <Filter>Source Files\soundlib</Filter> - </ClCompile> <ClCompile Include="..\soundlib\WAVTools.cpp"> <Filter>Source Files\soundlib</Filter> </ClCompile> Modified: trunk/OpenMPT/mptrack/Mod2wave.cpp =================================================================== --- trunk/OpenMPT/mptrack/Mod2wave.cpp 2013-07-04 13:26:51 UTC (rev 2495) +++ trunk/OpenMPT/mptrack/Mod2wave.cpp 2013-07-04 15:01:53 UTC (rev 2496) @@ -26,6 +26,9 @@ extern UINT nMixingRates[NUMMIXRATE]; extern LPCSTR gszChnCfgNames[3]; +static const GUID guid_MEDIASUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71}; +static const GUID guid_MEDIASUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71}; + /////////////////////////////////////////////////////////////////////////////////////////////////// // ID3v1 Genres @@ -185,8 +188,7 @@ //---------------------------------- { DWORD dwFormat = m_CbnSampleFormat.GetItemData(m_CbnSampleFormat.GetCurSel()); - UINT nBits = dwFormat & 0xFF; - ::EnableWindow( ::GetDlgItem(m_hWnd, IDC_CHECK5), (nBits <= 24) ? TRUE : FALSE ); + (void)dwFormat; } @@ -310,7 +312,6 @@ // MultiChannel configuration if ((WaveFormat.Format.wBitsPerSample == 32) || (WaveFormat.Format.nChannels > 2)) { - const GUID guid_MEDIASUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x0, 0xAA, 0x0, 0x38, 0x9B, 0x71}; WaveFormat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; WaveFormat.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); WaveFormat.Samples.wValidBitsPerSample = WaveFormat.Format.wBitsPerSample; @@ -601,12 +602,13 @@ //------------------------------ { static char buffer[MIXBUFFERSIZE * 4 * 4]; // channels * sizeof(biggestsample) + static float floatbuffer[MIXBUFFERSIZE * 4]; // channels + static int mixbuffer[MIXBUFFERSIZE * 4]; // channels MSG msg; CHAR s[80]; HWND progress = ::GetDlgItem(m_hWnd, IDC_PROGRESS1); UINT ok = IDOK, pos = 0; uint64 ullSamples = 0, ullMaxSamples; - LONG lMax = 256; if (!m_pSndFile || !m_lpszFileName) { @@ -614,6 +616,7 @@ return; } + float normalizePeak = 0.0f; std::string normalizeFileName = _tempnam("", "OpenMPT_mod2wave"); std::fstream normalizeFile; if(m_bNormalize) @@ -638,16 +641,13 @@ mixersettings.m_SampleFormat = (m_pWaveFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) ? SampleFormatFloat32 : (SampleFormat)m_pWaveFormat->wBitsPerSample; mixersettings.gnChannels = m_pWaveFormat->nChannels; m_pSndFile->m_SongFlags.reset(SONG_PAUSED | SONG_STEP); - if ((m_bNormalize) && (m_pWaveFormat->wBitsPerSample <= 24)) + if(m_bNormalize) { - mixersettings.m_SampleFormat = SampleFormatInt24; + mixersettings.m_SampleFormat = SampleFormatFloat32; #ifndef NO_AGC mixersettings.DSPMask &= ~SNDDSP_AGC; #endif if(mixersettings.m_nPreAmp > 128) mixersettings.m_nPreAmp = 128; - } else - { - m_bNormalize = false; } m_pSndFile->ResetChannels(); @@ -689,7 +689,7 @@ m_pSndFile->m_PatternCuePoints.reserve(m_pSndFile->Order.GetLength()); // Process the conversion - UINT nBytesPerSample = (m_pSndFile->m_MixerSettings.GetBitsPerSample() * m_pSndFile->m_MixerSettings.gnChannels) / 8; + // For calculating the remaining time DWORD dwStartTime = timeGetTime(); // For giving away some processing time every now and then @@ -702,7 +702,14 @@ CMainFrame::GetMainFrame()->InitRenderer(m_pSndFile); //rewbs.VSTTimeInfo for (UINT n = 0; ; n++) { - UINT lRead = m_pSndFile->ReadInterleaved(buffer, MIXBUFFERSIZE); + UINT lRead = 0; + if(m_bNormalize) + { + lRead = m_pSndFile->ReadInterleaved(floatbuffer, MIXBUFFERSIZE); + } else + { + lRead = m_pSndFile->ReadInterleaved(buffer, MIXBUFFERSIZE); + } // Process cue points (add base offset), if there are any to process. std::vector<PatternCuePoint>::reverse_iterator iter; @@ -729,23 +736,24 @@ if (m_bNormalize) { - UINT imax = lRead*3*m_pSndFile->m_MixerSettings.gnChannels; - for (UINT i=0; i<imax; i+=3) + std::size_t countSamples = lRead * m_pSndFile->m_MixerSettings.gnChannels; + const float *src = floatbuffer; + while(countSamples--) { - LONG l = ((((buffer[i+2] << 8) + buffer[i+1]) << 8) + buffer[i]) << 8; - l /= 256; - if (l > lMax) lMax = l; - if (-l > lMax) lMax = -l; + const float val = *src; + if(val > normalizePeak) normalizePeak = val; + else if(0.0f - val >= normalizePeak) normalizePeak = 0.0f - val; + src++; } - normalizeFile.write(buffer, lRead * nBytesPerSample); - if(normalizeFile.gcount() != lRead * nBytesPerSample) + normalizeFile.write(reinterpret_cast<const char*>(floatbuffer), lRead * sizeof(float)); + if(!normalizeFile) break; } else { - UINT lWrite = fwrite(buffer, 1, lRead * nBytesPerSample, file.GetFile()); + UINT lWrite = fwrite(buffer, 1, lRead * m_pSndFile->m_MixerSettings.gnChannels * (m_pSndFile->m_MixerSettings.GetBitsPerSample()/8), file.GetFile()); if (!lWrite) break; bytesWritten += lWrite; @@ -806,6 +814,9 @@ { ::SendMessage(progress, PBM_SETRANGE, 0, MAKELPARAM(0, 100)); + const float normalizeFactor = (normalizePeak != 0.0f) ? (1.0f / normalizePeak) : 1.0f; + Dither dither; + const DWORD dwBitSize = m_pWaveFormat->wBitsPerSample / 8; const uint64 framesTotal = ullSamples; int lastPercent = -1; @@ -816,16 +827,43 @@ uint64 framesToProcess = framesTotal; while(framesToProcess) { - const uint64 framesChunk = std::min<uint64>(framesToProcess, MIXBUFFERSIZE); + const std::size_t framesChunk = std::min<std::size_t>(mpt::saturate_cast<std::size_t>(framesToProcess), MIXBUFFERSIZE); + const std::size_t samplesChunk = framesChunk * m_pWaveFormat->nChannels; - normalizeFile.read(buffer, framesChunk * nBytesPerSample); - if(normalizeFile.gcount() != framesChunk * nBytesPerSample) + normalizeFile.read(reinterpret_cast<char*>(floatbuffer), framesChunk * sizeof(float)); + if(normalizeFile.gcount() != framesChunk * sizeof(float)) break; - m_pSndFile->Normalize24BitBuffer((LPBYTE)buffer, framesChunk * nBytesPerSample, lMax, dwBitSize); + for(std::size_t i = 0; i < samplesChunk; ++i) + { + floatbuffer[i] *= normalizeFactor; + } + if( + m_pWaveFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT + || (m_pWaveFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE && reinterpret_cast<const WAVEFORMATEXTENSIBLE*>(m_pWaveFormat)->SubFormat == guid_MEDIASUBTYPE_IEEE_FLOAT) + ) + { + // Do not dither for 32bit float output. + ASSERT(sizeof(float) == dwBitSize); + std::memcpy(buffer, floatbuffer, samplesChunk * sizeof(float)); + } else + { + // Convert float buffer to mixbuffer format so we can apply dither. + // This can probably be changed in the future when dither supports floating point input directly. + FloatToMonoMix(floatbuffer, mixbuffer, samplesChunk, MIXING_SCALEF); + dither.Process(mixbuffer, framesChunk, m_pWaveFormat->nChannels, m_pWaveFormat->wBitsPerSample); + switch(dwBitSize) + { + case 1: Convert32ToInterleaved(reinterpret_cast<uint8*>(buffer), mixbuffer, samplesChunk); break; + case 2: Convert32ToInterleaved(reinterpret_cast<int16*>(buffer), mixbuffer, samplesChunk); break; + case 3: Convert32ToInterleaved(reinterpret_cast<int24*>(buffer), mixbuffer, samplesChunk); break; + case 4: Convert32ToInterleaved(reinterpret_cast<int32*>(buffer), mixbuffer, samplesChunk); break; + default: ASSERT(false); break; + } + } - fwrite(buffer, 1, framesChunk * dwBitSize * m_pWaveFormat->nChannels, file.GetFile()); - bytesWritten += framesChunk * dwBitSize * m_pWaveFormat->nChannels; + fwrite(buffer, 1, samplesChunk * dwBitSize, file.GetFile()); + bytesWritten += samplesChunk * dwBitSize; { int percent = static_cast<int>(100 * framesProcessed / framesTotal); Modified: trunk/OpenMPT/mptrack/mptrack.rc =================================================================== --- trunk/OpenMPT/mptrack/mptrack.rc 2013-07-04 13:26:51 UTC (rev 2495) +++ trunk/OpenMPT/mptrack/mptrack.rc 2013-07-04 15:01:53 UTC (rev 2496) @@ -463,7 +463,7 @@ CONTROL "&Channel mode (one file per channel)",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,18,54,168,12 CONTROL "&Instrument mode (one file per instrument)",IDC_CHECK6, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,18,66,168,12 - CONTROL "&Normalize Output (experimental)",IDC_CHECK5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,18,84,168,12 + CONTROL "&Normalize Output",IDC_CHECK5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,18,84,168,12 CONTROL "Slow &render (for Kontakt+DFD)",IDC_GIVEPLUGSIDLETIME, "Button",BS_AUTOCHECKBOX | BS_LEFT | BS_MULTILINE | WS_TABSTOP,18,97,168,12 CONTROL "Limit file &size to: (KBytes)",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,18,138,111,11 Modified: trunk/OpenMPT/mptrack/mptrack_08.vcproj =================================================================== --- trunk/OpenMPT/mptrack/mptrack_08.vcproj 2013-07-04 13:26:51 UTC (rev 2495) +++ trunk/OpenMPT/mptrack/mptrack_08.vcproj 2013-07-04 15:01:53 UTC (rev 2496) @@ -615,10 +615,6 @@ > </File> <File - RelativePath="..\soundlib\Waveform.cpp" - > - </File> - <File RelativePath="..\soundlib\WindowedFIR.cpp" > </File> Modified: trunk/OpenMPT/mptrack/mptrack_10.vcxproj =================================================================== --- trunk/OpenMPT/mptrack/mptrack_10.vcxproj 2013-07-04 13:26:51 UTC (rev 2495) +++ trunk/OpenMPT/mptrack/mptrack_10.vcxproj 2013-07-04 15:01:53 UTC (rev 2496) @@ -296,7 +296,6 @@ <ClCompile Include="..\soundlib\Snd_fx.cpp" /> <ClCompile Include="..\soundlib\SoundFilePlayConfig.cpp" /> <ClCompile Include="..\soundlib\Tables.cpp" /> - <ClCompile Include="..\soundlib\Waveform.cpp" /> <ClCompile Include="..\soundlib\WAVTools.cpp" /> <ClCompile Include="..\soundlib\WindowedFIR.cpp" /> <ClCompile Include="..\soundlib\XMTools.cpp" /> Modified: trunk/OpenMPT/mptrack/mptrack_10.vcxproj.filters =================================================================== --- trunk/OpenMPT/mptrack/mptrack_10.vcxproj.filters 2013-07-04 13:26:51 UTC (rev 2495) +++ trunk/OpenMPT/mptrack/mptrack_10.vcxproj.filters 2013-07-04 15:01:53 UTC (rev 2496) @@ -253,9 +253,6 @@ <ClCompile Include="..\soundlib\Tables.cpp"> <Filter>Source Files\soundlib</Filter> </ClCompile> - <ClCompile Include="..\soundlib\Waveform.cpp"> - <Filter>Source Files\soundlib</Filter> - </ClCompile> <ClCompile Include="TuningDialog.cpp"> <Filter>Source Files\mptrack\Tuning</Filter> </ClCompile> Modified: trunk/OpenMPT/soundlib/Fastmix.cpp =================================================================== --- trunk/OpenMPT/soundlib/Fastmix.cpp 2013-07-04 13:26:51 UTC (rev 2495) +++ trunk/OpenMPT/soundlib/Fastmix.cpp 2013-07-04 15:01:53 UTC (rev 2496) @@ -2175,17 +2175,6 @@ } -void DitherStateless(int *pBuffer, UINT nSamples, UINT nBits) -//----------------------------------------------------------- -{ - #if defined(ENABLE_X86) - X86_Dither(pBuffer, nSamples, nBits, nullptr); - #else - C_Dither(pBuffer, nSamples, nBits, nullptr); - #endif -} - - #ifdef ENABLE_X86 static void X86_InterleaveFrontRear(int *pFrontBuf, int *pRearBuf, DWORD nFrames) //------------------------------------------------------------------------------- Modified: trunk/OpenMPT/soundlib/Sndfile.h =================================================================== --- trunk/OpenMPT/soundlib/Sndfile.h 2013-07-04 13:26:51 UTC (rev 2495) +++ trunk/OpenMPT/soundlib/Sndfile.h 2013-07-04 15:01:53 UTC (rev 2496) @@ -221,9 +221,7 @@ void Process(int *mixbuffer, std::size_t count, std::size_t channels, int bits); }; -DEPRECATED void DitherStateless(int *pBuffer, UINT nSamples, UINT nBits); - void StereoMixToFloat(const int *pSrc, float *pOut1, float *pOut2, UINT nCount, const float _i2fc); void FloatToStereoMix(const float *pIn1, const float *pIn2, int *pOut, UINT nCount, const float _f2ic); void MonoMixToFloat(const int *pSrc, float *pOut, UINT nCount, const float _i2fc); @@ -850,9 +848,6 @@ ModInstrument *AllocateInstrument(INSTRUMENTINDEX instr, SAMPLEINDEX assignedSample = 0); - // WAV export - UINT Normalize24BitBuffer(LPBYTE pbuffer, UINT cbsizebytes, DWORD lmax24, DWORD dwByteInc); - private: PLUGINDEX GetChannelPlugin(CHANNELINDEX nChn, PluginMutePriority respectMutes) const; PLUGINDEX GetActiveInstrumentPlugin(CHANNELINDEX, PluginMutePriority respectMutes) const; Deleted: trunk/OpenMPT/soundlib/Waveform.cpp =================================================================== --- trunk/OpenMPT/soundlib/Waveform.cpp 2013-07-04 13:26:51 UTC (rev 2495) +++ trunk/OpenMPT/soundlib/Waveform.cpp 2013-07-04 15:01:53 UTC (rev 2496) @@ -1,96 +0,0 @@ -/* - * Waveform.cpp - * ------------ - * Purpose: Common audio buffer conversion functions. - * Notes : (currently none) - * Authors: Olivier Lapicque - * OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - - -#include "stdafx.h" -#include "Sndfile.h" - -#ifdef ENABLE_X86 - -#pragma warning(disable:4100) -#pragma warning(disable:4731) - -static void X86_Normalize24BitBuffer(LPBYTE pbuffer, UINT dwSize, DWORD lmax24, int *poutput) -{ - _asm { - mov esi, pbuffer // esi = edi = pbuffer - mov edx, dwSize // edx = dwSize - mov ebx, lmax24 // ebx = max - mov edi, poutput // edi = output 32-bit buffer - push ebp - mov ebp, edx // ebp = dwSize - cmp ebx, 256 - jg normloop - mov ebx, 256 -normloop: - movsx eax, byte ptr [esi+2] - movzx edx, byte ptr [esi+1] - shl eax, 8 - or eax, edx - movzx edx, byte ptr [esi] - shl eax, 8 - or eax, edx // eax = 24-bit sample - mov edx, 1 << (31-MIXING_ATTENUATION) - add esi, 3 - imul edx - add edi, 4 - idiv ebx // eax = 28-bit normalized sample - dec ebp - mov dword ptr [edi-4], eax - jnz normloop - pop ebp - } -} - -#pragma warning(default:4100) - -#endif - - -UINT CSoundFile::Normalize24BitBuffer(LPBYTE pbuffer, UINT dwSize, DWORD lmax24, DWORD dwByteInc) -//----------------------------------------------------------------------------------------------- -{ -#ifdef ENABLE_X86 - int * const tempbuf = MixSoundBuffer; - int n = dwSize / 3; - while (n > 0) - { - int nbuf = (n > MIXBUFFERSIZE * 2) ? MIXBUFFERSIZE * 2 : n; - X86_Normalize24BitBuffer(pbuffer, nbuf, lmax24, tempbuf); - DitherStateless(tempbuf, nbuf, 8 * dwByteInc); - switch(dwByteInc) - { - case 1: Convert32ToInterleaved((uint8*)pbuffer, tempbuf, nbuf); break; - case 2: Convert32ToInterleaved((int16*)pbuffer, tempbuf, nbuf); break; - case 3: Convert32ToInterleaved((int24*)pbuffer, tempbuf, nbuf); break; - } - n -= nbuf; - pbuffer += dwByteInc * nbuf; - } - return (dwSize/3) * dwByteInc; -#else - LONG lMax = (lmax24 / 128) + 1; - UINT i = 0; - for (UINT j=0; j<dwSize; j+=3, i+=dwByteInc) - { - LONG l = ((((pbuffer[j+2] << 8) + pbuffer[j+1]) << 8) + pbuffer[j]) << 8; - l /= lMax; - if (dwByteInc > 1) - { - pbuffer[i] = (BYTE)(l & 0xFF); - pbuffer[i+1] = (BYTE)(l >> 8); - } else - { - pbuffer[i] = (BYTE)((l + 0x8000) >> 8); - } - } - return i; -#endif -} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |