From: <man...@us...> - 2014-02-01 16:34:48
|
Revision: 3615 http://sourceforge.net/p/modplug/code/3615 Author: manxorist Date: 2014-02-01 16:34:40 +0000 (Sat, 01 Feb 2014) Log Message: ----------- [New] Add an alternate, templated dithering algorithm. Default is still the original ModPlug Tracker dither. (no GUI to select it yet) [Ref] sounddev: Add option to disable the internal dither of sound devices (useful for PortAudio). (no GUI yet) [Ref] Mod Export: Add option to select/disable dithering. (no GUI yet) Modified Paths: -------------- trunk/OpenMPT/mptrack/MainFrm.cpp trunk/OpenMPT/mptrack/Mod2wave.cpp trunk/OpenMPT/mptrack/StreamEncoder.h trunk/OpenMPT/mptrack/TrackerSettings.cpp trunk/OpenMPT/sounddev/SoundDevice.h trunk/OpenMPT/sounddev/SoundDevicePortAudio.cpp trunk/OpenMPT/soundlib/Dither.cpp trunk/OpenMPT/soundlib/Dither.h Modified: trunk/OpenMPT/mptrack/MainFrm.cpp =================================================================== --- trunk/OpenMPT/mptrack/MainFrm.cpp 2014-02-01 16:04:11 UTC (rev 3614) +++ trunk/OpenMPT/mptrack/MainFrm.cpp 2014-02-01 16:34:40 UTC (rev 3615) @@ -737,6 +737,7 @@ timingInfo.SystemTimestamp = timeInfo.SystemTimestamp; timingInfo.Speed = timeInfo.Speed; m_pSndFile->m_TimingInfo = timingInfo; + m_Dither.SetMode((DitherMode)settings.DitherType); StereoVuMeterTargetWrapper target(settings.sampleFormat, m_Dither, buffer); CSoundFile::samplecount_t renderedFrames = m_pSndFile->Read(numFrames, target); ASSERT(renderedFrames <= numFrames); Modified: trunk/OpenMPT/mptrack/Mod2wave.cpp =================================================================== --- trunk/OpenMPT/mptrack/Mod2wave.cpp 2014-02-01 16:04:11 UTC (rev 3614) +++ trunk/OpenMPT/mptrack/Mod2wave.cpp 2014-02-01 16:34:40 UTC (rev 3615) @@ -845,7 +845,8 @@ encTraits.defaultMode, encTraits.defaultBitrate, encTraits.defaultQuality, - encTraits.defaultFormat + encTraits.defaultFormat, + encTraits.defaultDitherType ) ) ); @@ -951,6 +952,7 @@ } Dither dither; + dither.SetMode((DitherMode)encSettings.Dither.Get()); m_SndFile.ResetChannels(); m_SndFile.SetMixerSettings(mixersettings); Modified: trunk/OpenMPT/mptrack/StreamEncoder.h =================================================================== --- trunk/OpenMPT/mptrack/StreamEncoder.h 2014-02-01 16:04:11 UTC (rev 3614) +++ trunk/OpenMPT/mptrack/StreamEncoder.h 2014-02-01 16:34:40 UTC (rev 3615) @@ -136,6 +136,7 @@ int defaultBitrate; float defaultQuality; int defaultFormat; + int defaultDitherType; Traits() : canCues(false) @@ -149,6 +150,7 @@ , defaultBitrate(0) , defaultQuality(0.0f) , defaultFormat(0) + , defaultDitherType(1) { return; } @@ -168,8 +170,9 @@ Setting<int> Bitrate; Setting<float> Quality; Setting<int> Format; + Setting<int> Dither; - Settings(SettingsContainer &conf, const std::string &encoderName, bool cues, bool tags, uint32 samplerate, uint16 channels, Encoder::Mode mode, int bitrate, float quality, int format) + Settings(SettingsContainer &conf, const std::string &encoderName, bool cues, bool tags, uint32 samplerate, uint16 channels, Encoder::Mode mode, int bitrate, float quality, int format, int dither) : Cues(conf, "Export", encoderName + "_" + "Cues", cues) , Tags(conf, "Export", encoderName + "_" + "Tags", tags) , Samplerate(conf, "Export", encoderName + "_" + "Samplerate", samplerate) @@ -178,6 +181,7 @@ , Bitrate(conf, "Export", encoderName + "_" + "Bitrate", bitrate) , Quality(conf, "Export", encoderName + "_" + "Quality", quality) , Format(conf, "Export", encoderName + "_" + "Format", format) + , Dither(conf, "Export", encoderName + "_" + "Dither", dither) { return; } Modified: trunk/OpenMPT/mptrack/TrackerSettings.cpp =================================================================== --- trunk/OpenMPT/mptrack/TrackerSettings.cpp 2014-02-01 16:04:11 UTC (rev 3614) +++ trunk/OpenMPT/mptrack/TrackerSettings.cpp 2014-02-01 16:34:40 UTC (rev 3615) @@ -525,6 +525,7 @@ Setting<bool> ExclusiveMode; Setting<bool> BoostThreadPriority; Setting<bool> UseHardwareTiming; + Setting<int> DitherType; Setting<SoundChannelMapping> ChannelMapping; public: @@ -540,6 +541,7 @@ , ExclusiveMode(conf, L"Sound Settings", deviceInfo.GetIdentifier() + L"_" + L"ExclusiveMode", defaults.ExclusiveMode) , BoostThreadPriority(conf, L"Sound Settings", deviceInfo.GetIdentifier() + L"_" + L"BoostThreadPriority", defaults.BoostThreadPriority) , UseHardwareTiming(conf, L"Sound Settings", deviceInfo.GetIdentifier() + L"_" + L"UseHardwareTiming", defaults.UseHardwareTiming) + , DitherType(conf, L"Sound Settings", deviceInfo.GetIdentifier() + L"_" + L"DitherType", defaults.DitherType) , ChannelMapping(conf, L"Sound Settings", deviceInfo.GetIdentifier() + L"_" + L"ChannelMapping", defaults.ChannelMapping) { // store informational data (not read back, jsut to allow the user to mock with the raw ini file) @@ -559,6 +561,7 @@ ExclusiveMode = settings.ExclusiveMode; BoostThreadPriority = settings.BoostThreadPriority; UseHardwareTiming = settings.UseHardwareTiming; + DitherType = settings.DitherType; ChannelMapping = settings.ChannelMapping; return *this; } @@ -574,6 +577,7 @@ settings.ExclusiveMode = ExclusiveMode; settings.BoostThreadPriority = BoostThreadPriority; settings.UseHardwareTiming = UseHardwareTiming; + settings.DitherType = DitherType; settings.ChannelMapping = ChannelMapping; return settings; } Modified: trunk/OpenMPT/sounddev/SoundDevice.h =================================================================== --- trunk/OpenMPT/sounddev/SoundDevice.h 2014-02-01 16:04:11 UTC (rev 3614) +++ trunk/OpenMPT/sounddev/SoundDevice.h 2014-02-01 16:34:40 UTC (rev 3615) @@ -252,6 +252,7 @@ bool ExclusiveMode; // Use hardware buffers directly bool BoostThreadPriority; // Boost thread priority for glitch-free audio rendering bool UseHardwareTiming; + int DitherType; SoundChannelMapping ChannelMapping; SoundDeviceSettings() : hWnd(NULL) @@ -263,6 +264,7 @@ , ExclusiveMode(false) , BoostThreadPriority(true) , UseHardwareTiming(false) + , DitherType(1) { return; } @@ -279,6 +281,7 @@ && BoostThreadPriority == cmp.BoostThreadPriority && UseHardwareTiming == cmp.UseHardwareTiming && ChannelMapping == cmp.ChannelMapping + && DitherType == cmp.DitherType ; } bool operator != (const SoundDeviceSettings &cmp) const @@ -308,6 +311,7 @@ bool CanUseHardwareTiming; bool CanChannelMapping; bool CanDriverPanel; + bool HasInternalDither; std::wstring ExclusiveModeDescription; SoundDeviceCaps() : currentSampleRate(0) @@ -318,6 +322,7 @@ , CanUseHardwareTiming(false) , CanChannelMapping(false) , CanDriverPanel(false) + , HasInternalDither(false) , ExclusiveModeDescription(L"Use device exclusively") { return; Modified: trunk/OpenMPT/sounddev/SoundDevicePortAudio.cpp =================================================================== --- trunk/OpenMPT/sounddev/SoundDevicePortAudio.cpp 2014-02-01 16:04:11 UTC (rev 3614) +++ trunk/OpenMPT/sounddev/SoundDevicePortAudio.cpp 2014-02-01 16:34:40 UTC (rev 3615) @@ -97,7 +97,12 @@ framesPerBuffer = paFramesPerBufferUnspecified; // let portaudio choose } if(Pa_IsFormatSupported(NULL, &m_StreamParameters, m_Settings.Samplerate) != paFormatIsSupported) return false; - if(Pa_OpenStream(&m_Stream, NULL, &m_StreamParameters, m_Settings.Samplerate, framesPerBuffer, paNoFlag, StreamCallbackWrapper, (void*)this) != paNoError) return false; + PaStreamFlags flags = paNoFlag; + if(m_Settings.DitherType == 0) + { + flags |= paDitherOff; + } + if(Pa_OpenStream(&m_Stream, NULL, &m_StreamParameters, m_Settings.Samplerate, framesPerBuffer, flags, StreamCallbackWrapper, (void*)this) != paNoError) return false; m_StreamInfo = Pa_GetStreamInfo(m_Stream); if(!m_StreamInfo) { @@ -185,6 +190,7 @@ caps.CanUseHardwareTiming = false; caps.CanChannelMapping = false; caps.CanDriverPanel = false; + caps.HasInternalDither = true; if(m_HostApi == Pa_HostApiTypeIdToHostApiIndex(paWASAPI)) { caps.CanExclusiveMode = true; Modified: trunk/OpenMPT/soundlib/Dither.cpp =================================================================== --- trunk/OpenMPT/soundlib/Dither.cpp 2014-02-01 16:04:11 UTC (rev 3614) +++ trunk/OpenMPT/soundlib/Dither.cpp 2014-02-01 16:34:40 UTC (rev 3615) @@ -13,6 +13,7 @@ #include "Dither.h" #include "Mixer.h" +#include "../common/misc_util.h" ////////////////////////////////////////////////////////////////////////// @@ -122,35 +123,167 @@ } +#define FASTRAND_MAX 0x7fff +#define FASTRAND_BITS 15 + +static forceinline int fastrand(uint32 &state) +//-------------------------------------------- +{ + state = 214013 * state + 2531011; + return (state >> 16) & 0x7FFF; +} + +static forceinline int fastrandbits(uint32 &state, int bits) +//---------------------------------------------------------- +{ + int result = 0; + if(bits > 0 * FASTRAND_BITS) result = (result << FASTRAND_BITS) | fastrand(state); + if(bits > 1 * FASTRAND_BITS) result = (result << FASTRAND_BITS) | fastrand(state); + if(bits > 2 * FASTRAND_BITS) result = (result << FASTRAND_BITS) | fastrand(state); + result &= (1 << bits) - 1; + return result; +} + +template<int targetbits, int channels, int ditherdepth = 1, bool triangular = false, bool shaped = true> +struct Dither_SimpleTemplate +{ +noinline void operator () (int *mixbuffer, std::size_t count, DitherSimpleState &state) +//------------------------------------------------------------------------------------- +{ + STATIC_ASSERT(sizeof(int) == 4); + STATIC_ASSERT(FASTRAND_BITS * 3 >= (32-targetbits) - MIXING_ATTENUATION); + const int rshift = (32-targetbits) - MIXING_ATTENUATION; + if(rshift <= 0) + { + // nothing to dither + return; + } + const int round_mask = ~((1<<rshift)-1); + const int round_offset = 1<<(rshift-1); + const int noise_bits = rshift + (ditherdepth - 1); + const int noise_bias = (1<<(noise_bits-1)); + DitherSimpleState s = state; + for(std::size_t i = 0; i < count; ++i) + { + for(std::size_t channel = 0; channel < channels; ++channel) + { + int noise = 0; + if(triangular) + { + noise = (fastrandbits(s.rng, noise_bits) + fastrandbits(s.rng, noise_bits)) >> 1; + } else + { + noise = fastrandbits(s.rng, noise_bits); + } + noise -= noise_bias; // un-bias + int val = *mixbuffer; + if(shaped) + { + val += (s.error[channel] >> 1); + } + int rounded = (val + noise + round_offset) & round_mask;; + s.error[channel] = val - rounded; + *mixbuffer = rounded; + mixbuffer++; + } + } + state = s; +} +}; + +static void Dither_Simple(int *mixbuffer, std::size_t count, std::size_t channels, int bits, DitherSimpleState &state) +//-------------------------------------------------------------------------------------------------------------------- +{ + switch(bits) + { + case 8: + switch(channels) + { + case 1: + Dither_SimpleTemplate<8,1>()(mixbuffer, count, state); + break; + case 2: + Dither_SimpleTemplate<8,2>()(mixbuffer, count, state); + break; + case 4: + Dither_SimpleTemplate<8,4>()(mixbuffer, count, state); + break; + } + break; + case 16: + switch(channels) + { + case 1: + Dither_SimpleTemplate<16,1>()(mixbuffer, count, state); + break; + case 2: + Dither_SimpleTemplate<16,2>()(mixbuffer, count, state); + break; + case 4: + Dither_SimpleTemplate<16,4>()(mixbuffer, count, state); + break; + } + break; + case 24: + switch(channels) + { + case 1: + Dither_SimpleTemplate<24,1>()(mixbuffer, count, state); + break; + case 2: + Dither_SimpleTemplate<24,2>()(mixbuffer, count, state); + break; + case 4: + Dither_SimpleTemplate<24,4>()(mixbuffer, count, state); + break; + } + break; + } +} + + void Dither::Reset() +//------------------ { state = DitherState(); } + Dither::Dither() +//-------------- { - mode = DitherModPlug; + mode = DitherDefault; } -void Dither::SetMode(DitherMode &mode_) + +void Dither::SetMode(DitherMode mode_) +//------------------------------------ { mode = mode_; } + DitherMode Dither::GetMode() const +//-------------------------------- { return mode; } + void Dither::Process(int *mixbuffer, std::size_t count, std::size_t channels, int bits) +//------------------------------------------------------------------------------------- { switch(mode) { case DitherNone: // nothing break; + case DitherDefault: case DitherModPlug: Dither_ModPlug(mixbuffer, count, channels, bits, state.modplug); break; + case DitherSimple: + Dither_Simple(mixbuffer, count, channels, bits, state.simple); + break; } } Modified: trunk/OpenMPT/soundlib/Dither.h =================================================================== --- trunk/OpenMPT/soundlib/Dither.h 2014-02-01 16:04:11 UTC (rev 3614) +++ trunk/OpenMPT/soundlib/Dither.h 2014-02-01 16:34:40 UTC (rev 3615) @@ -23,15 +23,31 @@ } }; +struct DitherSimpleState +{ + int32 error[4]; + uint32 rng; + DitherSimpleState() { + error[0] = 0; + error[1] = 0; + error[2] = 0; + error[3] = 0; + rng = 0x12345678; + } +}; + struct DitherState { DitherModPlugState modplug; + DitherSimpleState simple; }; enum DitherMode { DitherNone = 0, - DitherModPlug = 1 + DitherDefault = 1, // chosen by OpenMPT code, might change + DitherModPlug = 2, // rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker) + DitherSimple = 3 // rectangular, 1 bit depth, simple 1st order noise shaping }; class Dither @@ -41,7 +57,7 @@ DitherMode mode; public: Dither(); - void SetMode(DitherMode &mode); + void SetMode(DitherMode mode); DitherMode GetMode() const; void Reset(); void Process(int *mixbuffer, std::size_t count, std::size_t channels, int bits); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |