From: <man...@us...> - 2014-02-15 17:12:19
|
Revision: 3720 http://sourceforge.net/p/modplug/code/3720 Author: manxorist Date: 2014-02-15 17:12:09 +0000 (Sat, 15 Feb 2014) Log Message: ----------- [Mod] sounddev: Windows since Vista has a limiter/compressor with way too high release time in the audio path that kicks in as soon as there are samples > 0dBFs (i.e. the absolute float value is greater than 1.0). This happens for all APIs that get processed through the system-wide audio engine. It does not happen for exclusive mode WASAPI streams or direct WaveRT (labeled WDM-KS in PortAudio) streams. As there is no known way to disable this annoying behavior, avoid unclipped samples on affected Windows versions and APIs and clip them ourselves before handing them to the APIs. Modified Paths: -------------- trunk/OpenMPT/mptrack/MainFrm.cpp trunk/OpenMPT/mptrack/Mainfrm.h trunk/OpenMPT/mptrack/Mod2wave.cpp trunk/OpenMPT/sounddev/SoundDevice.cpp trunk/OpenMPT/sounddev/SoundDevice.h trunk/OpenMPT/sounddev/SoundDeviceDirectSound.cpp trunk/OpenMPT/sounddev/SoundDevicePortAudio.cpp trunk/OpenMPT/sounddev/SoundDeviceWaveout.cpp trunk/OpenMPT/soundlib/AudioReadTarget.h trunk/OpenMPT/soundlib/SampleFormatConverters.h Modified: trunk/OpenMPT/mptrack/MainFrm.cpp =================================================================== --- trunk/OpenMPT/mptrack/MainFrm.cpp 2014-02-15 16:54:10 UTC (rev 3719) +++ trunk/OpenMPT/mptrack/MainFrm.cpp 2014-02-15 17:12:09 UTC (rev 3720) @@ -669,11 +669,13 @@ { private: const SampleFormat sampleFormat; + bool clipFloat; Dither &dither; void *buffer; public: - StereoVuMeterTargetWrapper(SampleFormat sampleFormat_, Dither &dither_, void *buffer_) + StereoVuMeterTargetWrapper(SampleFormat sampleFormat_, bool clipFloat_, Dither &dither_, void *buffer_) : sampleFormat(sampleFormat_) + , clipFloat(clipFloat_) , dither(dither_) , buffer(buffer_) { @@ -713,10 +715,16 @@ } break; case SampleFormatFloat32: + if(clipFloat) { typedef SampleFormatToType<SampleFormatFloat32>::type Tsample; - AudioReadTargetBuffer<Tsample> target(dither, reinterpret_cast<Tsample*>(buffer), nullptr); + AudioReadTargetBuffer<Tsample, true> target(dither, reinterpret_cast<Tsample*>(buffer), nullptr); target.DataCallback(MixSoundBuffer, channels, countChunk); + } else + { + typedef SampleFormatToType<SampleFormatFloat32>::type Tsample; + AudioReadTargetBuffer<Tsample, false> target(dither, reinterpret_cast<Tsample*>(buffer), nullptr); + target.DataCallback(MixSoundBuffer, channels, countChunk); } break; } @@ -726,8 +734,8 @@ }; -void CMainFrame::AudioRead(const SoundDeviceSettings &settings, const SoundBufferAttributes &bufferAttributes, SoundTimeInfo timeInfo, std::size_t numFrames, void *buffer) -//------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void CMainFrame::AudioRead(const SoundDeviceSettings &settings, const SoundDeviceFlags &flags, const SoundBufferAttributes &bufferAttributes, SoundTimeInfo timeInfo, std::size_t numFrames, void *buffer) +//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- { ASSERT(InAudioThread()); OPENMPT_PROFILE_FUNCTION(Profiler::Audio); @@ -738,7 +746,7 @@ timingInfo.Speed = timeInfo.Speed; m_pSndFile->m_TimingInfo = timingInfo; m_Dither.SetMode((DitherMode)settings.DitherType); - StereoVuMeterTargetWrapper target(settings.sampleFormat, m_Dither, buffer); + StereoVuMeterTargetWrapper target(settings.sampleFormat, flags.NeedsClippedFloat, m_Dither, buffer); CSoundFile::samplecount_t renderedFrames = m_pSndFile->Read(numFrames, target); ASSERT(renderedFrames <= numFrames); CSoundFile::samplecount_t remainingFrames = numFrames - renderedFrames; @@ -758,10 +766,11 @@ } -void CMainFrame::AudioDone(const SoundDeviceSettings &settings, const SoundBufferAttributes &bufferAttributes, SoundTimeInfo timeInfo, std::size_t numFrames, int64 streamPosition) -//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void CMainFrame::AudioDone(const SoundDeviceSettings &settings, const SoundDeviceFlags &flags, const SoundBufferAttributes &bufferAttributes, SoundTimeInfo timeInfo, std::size_t numFrames, int64 streamPosition) +//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- { MPT_UNREFERENCED_PARAMETER(settings); + MPT_UNREFERENCED_PARAMETER(flags); MPT_UNREFERENCED_PARAMETER(bufferAttributes); MPT_UNREFERENCED_PARAMETER(timeInfo); ASSERT(InAudioThread()); Modified: trunk/OpenMPT/mptrack/Mainfrm.h =================================================================== --- trunk/OpenMPT/mptrack/Mainfrm.h 2014-02-15 16:54:10 UTC (rev 3719) +++ trunk/OpenMPT/mptrack/Mainfrm.h 2014-02-15 17:12:09 UTC (rev 3720) @@ -352,8 +352,8 @@ // from ISoundSource void FillAudioBufferLocked(IFillAudioBuffer &callback); - void AudioRead(const SoundDeviceSettings &settings, const SoundBufferAttributes &bufferAttributes, SoundTimeInfo timeInfo, std::size_t numFrames, void *buffer); - void AudioDone(const SoundDeviceSettings &settings, const SoundBufferAttributes &bufferAttributes, SoundTimeInfo timeInfo, std::size_t numFrames, int64 streamPosition); + void AudioRead(const SoundDeviceSettings &settings, const SoundDeviceFlags &flags, const SoundBufferAttributes &bufferAttributes, SoundTimeInfo timeInfo, std::size_t numFrames, void *buffer); + void AudioDone(const SoundDeviceSettings &settings, const SoundDeviceFlags &flags, const SoundBufferAttributes &bufferAttributes, SoundTimeInfo timeInfo, std::size_t numFrames, int64 streamPosition); // from ISoundMessageReceiver void AudioMessage(const std::string &str); Modified: trunk/OpenMPT/mptrack/Mod2wave.cpp =================================================================== --- trunk/OpenMPT/mptrack/Mod2wave.cpp 2014-02-15 16:54:10 UTC (rev 3719) +++ trunk/OpenMPT/mptrack/Mod2wave.cpp 2014-02-15 17:12:09 UTC (rev 3720) @@ -1242,10 +1242,10 @@ dither.Process(mixbuffer, framesChunk, channels, m_Settings.FinalSampleFormat.GetBitsPerSample()); switch(m_Settings.FinalSampleFormat.value) { - case SampleFormatUnsigned8: ConvertInterleavedFixedPointToInterleaved<MIXING_FRACTIONAL_BITS>(reinterpret_cast<uint8*>(buffer), mixbuffer, channels, framesChunk); break; - case SampleFormatInt16: ConvertInterleavedFixedPointToInterleaved<MIXING_FRACTIONAL_BITS>(reinterpret_cast<int16*>(buffer), mixbuffer, channels, framesChunk); break; - case SampleFormatInt24: ConvertInterleavedFixedPointToInterleaved<MIXING_FRACTIONAL_BITS>(reinterpret_cast<int24*>(buffer), mixbuffer, channels, framesChunk); break; - case SampleFormatInt32: ConvertInterleavedFixedPointToInterleaved<MIXING_FRACTIONAL_BITS>(reinterpret_cast<int32*>(buffer), mixbuffer, channels, framesChunk); break; + case SampleFormatUnsigned8: ConvertInterleavedFixedPointToInterleaved<MIXING_FRACTIONAL_BITS,false>(reinterpret_cast<uint8*>(buffer), mixbuffer, channels, framesChunk); break; + case SampleFormatInt16: ConvertInterleavedFixedPointToInterleaved<MIXING_FRACTIONAL_BITS,false>(reinterpret_cast<int16*>(buffer), mixbuffer, channels, framesChunk); break; + case SampleFormatInt24: ConvertInterleavedFixedPointToInterleaved<MIXING_FRACTIONAL_BITS,false>(reinterpret_cast<int24*>(buffer), mixbuffer, channels, framesChunk); break; + case SampleFormatInt32: ConvertInterleavedFixedPointToInterleaved<MIXING_FRACTIONAL_BITS,false>(reinterpret_cast<int32*>(buffer), mixbuffer, channels, framesChunk); break; default: ASSERT(false); break; } fileEnc->WriteInterleavedConverted(framesChunk, buffer); Modified: trunk/OpenMPT/sounddev/SoundDevice.cpp =================================================================== --- trunk/OpenMPT/sounddev/SoundDevice.cpp 2014-02-15 16:54:10 UTC (rev 3719) +++ trunk/OpenMPT/sounddev/SoundDevice.cpp 2014-02-15 17:12:09 UTC (rev 3720) @@ -220,6 +220,7 @@ { return false; } + m_Flags = SoundDeviceFlags(); m_BufferAttributes.Latency = m_Settings.LatencyMS / 1000.0; m_BufferAttributes.UpdateInterval = m_Settings.UpdateIntervalMS / 1000.0; m_BufferAttributes.NumBuffers = 0; @@ -290,7 +291,7 @@ { return; } - m_Source->AudioRead(m_Settings, m_BufferAttributes, m_TimeInfo, numFrames, buffer); + m_Source->AudioRead(m_Settings, m_Flags, m_BufferAttributes, m_TimeInfo, numFrames, buffer); } @@ -309,7 +310,7 @@ m_StreamPositionOutputFrames = m_StreamPositionRenderFrames - framesLatency; framesRendered = m_StreamPositionRenderFrames; } - m_Source->AudioDone(m_Settings, m_BufferAttributes, m_TimeInfo, numFrames, framesRendered); + m_Source->AudioDone(m_Settings, m_Flags, m_BufferAttributes, m_TimeInfo, numFrames, framesRendered); } Modified: trunk/OpenMPT/sounddev/SoundDevice.h =================================================================== --- trunk/OpenMPT/sounddev/SoundDevice.h 2014-02-15 16:54:10 UTC (rev 3719) +++ trunk/OpenMPT/sounddev/SoundDevice.h 2014-02-15 17:12:09 UTC (rev 3720) @@ -31,6 +31,7 @@ struct SoundDeviceSettings; +struct SoundDeviceFlags; struct SoundBufferAttributes; @@ -73,8 +74,8 @@ { public: virtual void FillAudioBufferLocked(IFillAudioBuffer &callback) = 0; // take any locks needed while rendering audio and then call FillAudioBuffer - virtual void AudioRead(const SoundDeviceSettings &settings, const SoundBufferAttributes &bufferAttributes, SoundTimeInfo timeInfo, std::size_t numFrames, void *buffer) = 0; - virtual void AudioDone(const SoundDeviceSettings &settings, const SoundBufferAttributes &bufferAttributes, SoundTimeInfo timeInfo, std::size_t numFrames, int64 streamPosition) = 0; // in sample frames + virtual void AudioRead(const SoundDeviceSettings &settings, const SoundDeviceFlags &flags, const SoundBufferAttributes &bufferAttributes, SoundTimeInfo timeInfo, std::size_t numFrames, void *buffer) = 0; + virtual void AudioDone(const SoundDeviceSettings &settings, const SoundDeviceFlags &flags, const SoundBufferAttributes &bufferAttributes, SoundTimeInfo timeInfo, std::size_t numFrames, int64 streamPosition) = 0; // in sample frames }; @@ -292,6 +293,25 @@ }; +struct SoundDeviceFlags +{ + // Windows since Vista has a limiter/compressor in the audio path that kicks + // in as soon as there are samples > 0dBFs (i.e. the absolute float value > + // 1.0). This happens for all APIs that get processed through the system- + // wide audio engine. It does not happen for exclusive mode WASAPI streams + // or direct WaveRT (labeled WDM-KS in PortAudio) streams. As there is no + // known way to disable this annoying behavior, avoid unclipped samples on + // affected windows versions and clip them ourselves before handing them to + // the APIs. + bool NeedsClippedFloat; + SoundDeviceFlags() + : NeedsClippedFloat(false) + { + return; + } +}; + + struct SoundDeviceCaps { uint32 currentSampleRate; @@ -361,6 +381,7 @@ protected: SoundDeviceSettings m_Settings; + SoundDeviceFlags m_Flags; private: Modified: trunk/OpenMPT/sounddev/SoundDeviceDirectSound.cpp =================================================================== --- trunk/OpenMPT/sounddev/SoundDeviceDirectSound.cpp 2014-02-15 16:54:10 UTC (rev 3719) +++ trunk/OpenMPT/sounddev/SoundDeviceDirectSound.cpp 2014-02-15 17:12:09 UTC (rev 3720) @@ -261,6 +261,7 @@ } m_dwWritePos = 0xFFFFFFFF; SetWakeupInterval(std::min(m_Settings.UpdateIntervalMS / 1000.0, m_nDSoundBufferSize / (2.0 * m_Settings.GetBytesPerSecond()))); + m_Flags.NeedsClippedFloat = (mpt::Windows::GetWinNTVersion() >= mpt::Windows::VerWinVista); SoundBufferAttributes bufferAttributes; bufferAttributes.Latency = m_nDSoundBufferSize * 1.0 / m_Settings.GetBytesPerSecond(); bufferAttributes.UpdateInterval = std::min(m_Settings.UpdateIntervalMS / 1000.0, m_nDSoundBufferSize / (2.0 * m_Settings.GetBytesPerSecond())); Modified: trunk/OpenMPT/sounddev/SoundDevicePortAudio.cpp =================================================================== --- trunk/OpenMPT/sounddev/SoundDevicePortAudio.cpp 2014-02-15 16:54:10 UTC (rev 3719) +++ trunk/OpenMPT/sounddev/SoundDevicePortAudio.cpp 2014-02-15 17:12:09 UTC (rev 3720) @@ -83,6 +83,7 @@ { if(m_Settings.ExclusiveMode) { + m_Flags.NeedsClippedFloat = false; m_StreamParameters.suggestedLatency = 0.0; // let portaudio choose framesPerBuffer = paFramesPerBufferUnspecified; // let portaudio choose MemsetZero(m_WasapiStreamInfo); @@ -91,10 +92,23 @@ m_WasapiStreamInfo.version = 1; m_WasapiStreamInfo.flags = paWinWasapiExclusive; m_StreamParameters.hostApiSpecificStreamInfo = &m_WasapiStreamInfo; + } else + { + m_Flags.NeedsClippedFloat = true; } } else if(m_HostApi == Pa_HostApiTypeIdToHostApiIndex(paWDMKS)) { + m_Flags.NeedsClippedFloat = false; framesPerBuffer = paFramesPerBufferUnspecified; // let portaudio choose + } else if(m_HostApi == Pa_HostApiTypeIdToHostApiIndex(paMME)) + { + m_Flags.NeedsClippedFloat = (mpt::Windows::GetWinNTVersion() >= mpt::Windows::VerWinVista); + } else if(m_HostApi == Pa_HostApiTypeIdToHostApiIndex(paDirectSound)) + { + m_Flags.NeedsClippedFloat = (mpt::Windows::GetWinNTVersion() >= mpt::Windows::VerWinVista); + } else + { + m_Flags.NeedsClippedFloat = false; } if(Pa_IsFormatSupported(NULL, &m_StreamParameters, m_Settings.Samplerate) != paFormatIsSupported) return false; PaStreamFlags flags = paNoFlag; Modified: trunk/OpenMPT/sounddev/SoundDeviceWaveout.cpp =================================================================== --- trunk/OpenMPT/sounddev/SoundDeviceWaveout.cpp 2014-02-15 16:54:10 UTC (rev 3719) +++ trunk/OpenMPT/sounddev/SoundDeviceWaveout.cpp 2014-02-15 17:12:09 UTC (rev 3720) @@ -103,6 +103,7 @@ m_nWriteBuffer = 0; SetWakeupEvent(m_ThreadWakeupEvent); SetWakeupInterval(m_nWaveBufferSize * 1.0 / m_Settings.GetBytesPerSecond()); + m_Flags.NeedsClippedFloat = (mpt::Windows::GetWinNTVersion() >= mpt::Windows::VerWinVista); SoundBufferAttributes bufferAttributes; bufferAttributes.Latency = m_nWaveBufferSize * m_nPreparedHeaders * 1.0 / m_Settings.GetBytesPerSecond(); bufferAttributes.UpdateInterval = m_nWaveBufferSize * 1.0 / m_Settings.GetBytesPerSecond(); Modified: trunk/OpenMPT/soundlib/AudioReadTarget.h =================================================================== --- trunk/OpenMPT/soundlib/AudioReadTarget.h 2014-02-15 16:54:10 UTC (rev 3719) +++ trunk/OpenMPT/soundlib/AudioReadTarget.h 2014-02-15 17:12:09 UTC (rev 3720) @@ -16,7 +16,7 @@ #include "Mixer.h" -template<typename Tsample> +template<typename Tsample, bool clipOutput = false> class AudioReadTargetBuffer : public IAudioReadTarget { @@ -51,7 +51,7 @@ if(outputBuffer) { - ConvertInterleavedFixedPointToInterleaved<MIXING_FRACTIONAL_BITS>(outputBuffer + (channels * countRendered), MixSoundBuffer, channels, countChunk); + ConvertInterleavedFixedPointToInterleaved<MIXING_FRACTIONAL_BITS, clipOutput>(outputBuffer + (channels * countRendered), MixSoundBuffer, channels, countChunk); } if(outputBuffers) { @@ -60,7 +60,7 @@ { buffers[channel] = outputBuffers[channel] + countRendered; } - ConvertInterleavedFixedPointToNonInterleaved<MIXING_FRACTIONAL_BITS>(buffers, MixSoundBuffer, channels, countChunk); + ConvertInterleavedFixedPointToNonInterleaved<MIXING_FRACTIONAL_BITS, clipOutput>(buffers, MixSoundBuffer, channels, countChunk); } countRendered += countChunk; Modified: trunk/OpenMPT/soundlib/SampleFormatConverters.h =================================================================== --- trunk/OpenMPT/soundlib/SampleFormatConverters.h 2014-02-15 16:54:10 UTC (rev 3719) +++ trunk/OpenMPT/soundlib/SampleFormatConverters.h 2014-02-15 17:12:09 UTC (rev 3720) @@ -428,11 +428,11 @@ }; -template <typename Tdst, typename Tsrc, int fractionalBits> +template <typename Tdst, typename Tsrc, int fractionalBits, bool clipOutput> struct ConvertFixedPoint; -template <int fractionalBits> -struct ConvertFixedPoint<uint8, int32, fractionalBits> +template <int fractionalBits, bool clipOutput> +struct ConvertFixedPoint<uint8, int32, fractionalBits, clipOutput> { typedef int32 input_t; typedef uint8 output_t; @@ -448,8 +448,8 @@ } }; -template <int fractionalBits> -struct ConvertFixedPoint<int16, int32, fractionalBits> +template <int fractionalBits, bool clipOutput> +struct ConvertFixedPoint<int16, int32, fractionalBits, clipOutput> { typedef int32 input_t; typedef int16 output_t; @@ -465,8 +465,8 @@ } }; -template <int fractionalBits> -struct ConvertFixedPoint<int24, int32, fractionalBits> +template <int fractionalBits, bool clipOutput> +struct ConvertFixedPoint<int24, int32, fractionalBits, clipOutput> { typedef int32 input_t; typedef int24 output_t; @@ -482,8 +482,8 @@ } }; -template <int fractionalBits> -struct ConvertFixedPoint<int32, int32, fractionalBits> +template <int fractionalBits, bool clipOutput> +struct ConvertFixedPoint<int32, int32, fractionalBits, clipOutput> { typedef int32 input_t; typedef int32 output_t; @@ -494,8 +494,8 @@ } }; -template <int fractionalBits> -struct ConvertFixedPoint<float32, int32, fractionalBits> +template <int fractionalBits, bool clipOutput> +struct ConvertFixedPoint<float32, int32, fractionalBits, clipOutput> { typedef int32 input_t; typedef float32 output_t; @@ -508,7 +508,16 @@ forceinline output_t operator() (input_t val) { STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); - return val * factor; + if(clipOutput) + { + float32 out = val * factor; + if(out < -1.0f) out = -1.0f; + if(out > 1.0f) out = 1.0f; + return out; + } else + { + return val * factor; + } } }; @@ -826,11 +835,11 @@ } -template<int fractionalBits, typename Tsample, typename Tfixed> +template<int fractionalBits, bool clipOutput, typename Tsample, typename Tfixed> void ConvertInterleavedFixedPointToInterleaved(Tsample * MPT_RESTRICT p, const Tfixed * MPT_RESTRICT mixbuffer, std::size_t channels, std::size_t count) //------------------------------------------------------------------------------------------------------------------------------------------------------ { - SC::ConvertFixedPoint<Tsample, int, fractionalBits> conv; + SC::ConvertFixedPoint<Tsample, int, fractionalBits, clipOutput> conv; count *= channels; for(std::size_t i = 0; i < count; ++i) { @@ -838,11 +847,11 @@ } } -template<int fractionalBits, typename Tsample, typename Tfixed> +template<int fractionalBits, bool clipOutput, typename Tsample, typename Tfixed> void ConvertInterleavedFixedPointToNonInterleaved(Tsample * const * const MPT_RESTRICT buffers, const Tfixed * MPT_RESTRICT mixbuffer, std::size_t channels, std::size_t count) //----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- { - SC::ConvertFixedPoint<Tsample, int, fractionalBits> conv; + SC::ConvertFixedPoint<Tsample, int, fractionalBits, clipOutput> conv; for(std::size_t i = 0; i < count; ++i) { for(std::size_t channel = 0; channel < channels; ++channel) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |