From: <man...@us...> - 2013-08-06 11:46:01
|
Revision: 2583 http://sourceforge.net/p/modplug/code/2583 Author: manxorist Date: 2013-08-06 11:45:45 +0000 (Tue, 06 Aug 2013) Log Message: ----------- Merged revision(s) 2554-2582 from branches/manx/sampleformat-ref: [Ref] First step towards decoupling SampleFormat from MixerSettings. ........ [Ref] Move SampleFormat out of struct MixerSettings. ........ [Ref] Make CMainFrame::AudioRead a tiny bit more readable. ........ [Ref] Support mixbuffer sample format (28.4 fixed point, called int28q4 here) as output format in CSoundFile::Read(). ........ [Ref] Move output sample format conversion out of CSoundFile. ........ [Ref] Move Dither state out of CSoundFile. ........ [Ref] Make CSoundFile::Read() public. [Ref] Minor cleanups. ........ [Ref] For consistency, make Convert32ToInterleaved also take an explicit channels parameter. ........ [Ref] Remove global mixer callback hook. Implement stereo VU meters via a wrapper around the sample format conversion sink that gets passed to CSoundFile::Read(). ........ [Ref] Split sample format conversion functors into decoding and converting functors. Remove ::input_inc from converting functors and let them take the value by value instead of a pointer to the value. ........ [Ref] Add fixed point conversion functors usable to convert mixbuffer format to output sample formats to SampleFormatConverters.h. [Ref] Base Convert32ToInterleaved() and Convert32ToNonInterleaved() on SampleFormatConverters.h. ........ [Ref] Update comments in SampleFormatConverters.h . ........ [Ref] Move output sample format conversion loops from Fastmix.cpp to SampleFormatConverters.h . Remove the wrapper functions. ........ [Ref] Merge ISoundDevice::Open/Configure and make them not require a struct WAVEFORMATEX. Build struct WAVEFORMATEXTENSIBLE internally for the APIs that need it. ........ [Fix] ConvertFixedPoint<float32, int32> used a wrong amplification factor. ........ [New] sounddev: Support 32bit floating point output. [New] Make floating point output selectable in options dialog. ........ [Ref] Correct typo. ........ [Ref] Move ApplyFinalOutputGain() out of CSoundFile and rename it to ApplyGain(). ........ [Ref] Template SoundFileDefaultSink based on the sample type. ........ [Ref] Rename SoundFileDefaultSink to AudioStreamSinkToBuffer. ........ [Ref] Move CSoundFile::ReadInterleaved out of CSoundFile to the one single user that's left ( Mod2wave.cpp ). ........ [Ref] Factor out output gain handling from AudioStreamSinkToBuffer because it is only used in libopenmpt and not mptrack. ........ [Ref] Small cleanups. ........ [Fix] GCC 4.4 build fixes. ........ [Ref] Better distinguish between attenuation/headroom and fixedpoint/fractional interpretation of the mixbuffer format. [Ref] Rename misnamed int28q4 to fixed5p27 . ........ [Ref] Rename IAudioStreamSink to IAudioReadTarget and related renamings. ........ Modified Paths: -------------- trunk/OpenMPT/common/typedefs.h trunk/OpenMPT/libopenmpt/libopenmpt_impl.cpp trunk/OpenMPT/libopenmpt/libopenmpt_impl.hpp trunk/OpenMPT/mptrack/MainFrm.cpp trunk/OpenMPT/mptrack/Mainfrm.h trunk/OpenMPT/mptrack/Mod2wave.cpp trunk/OpenMPT/mptrack/Mpdlgs.cpp trunk/OpenMPT/mptrack/Mpdlgs.h trunk/OpenMPT/mptrack/TrackerSettings.cpp trunk/OpenMPT/mptrack/TrackerSettings.h trunk/OpenMPT/sounddev/SoundDevice.cpp trunk/OpenMPT/sounddev/SoundDevice.h trunk/OpenMPT/sounddev/SoundDevices.h trunk/OpenMPT/soundlib/Fastmix.cpp trunk/OpenMPT/soundlib/MixerSettings.cpp trunk/OpenMPT/soundlib/MixerSettings.h trunk/OpenMPT/soundlib/SampleFormatConverters.h trunk/OpenMPT/soundlib/SampleFormats.cpp trunk/OpenMPT/soundlib/Snd_defs.h trunk/OpenMPT/soundlib/Sndfile.h trunk/OpenMPT/soundlib/Sndmix.cpp Property Changed: ---------------- trunk/OpenMPT/ Index: trunk/OpenMPT =================================================================== --- trunk/OpenMPT 2013-08-06 11:28:29 UTC (rev 2582) +++ trunk/OpenMPT 2013-08-06 11:45:45 UTC (rev 2583) Property changes on: trunk/OpenMPT ___________________________________________________________________ Modified: svn:mergeinfo ## -6,6 +6,7 ## /branches/manx/nonglobal-mixer:1715-1841 /branches/manx/profiler:1813 /branches/manx/project-files-cleanups:1378-1382 +/branches/manx/sampleformat-ref:2554-2582 /branches/manx/serialization-utils-cleanup:2382-2386,2395 /branches/manx/snddev-fixes:1605-1713 /branches/manx/stdstring-names:2223,2228 \ No newline at end of property Modified: trunk/OpenMPT/common/typedefs.h =================================================================== --- trunk/OpenMPT/common/typedefs.h 2013-08-06 11:28:29 UTC (rev 2582) +++ trunk/OpenMPT/common/typedefs.h 2013-08-06 11:45:45 UTC (rev 2583) @@ -231,6 +231,22 @@ #define int24_min (0-0x00800000) #define int24_max (0+0x007fffff) +struct fixed5p27 +{ + // 5 integer bits (including sign) + // 27 fractional bits + int32 raw; + + static fixed5p27 Raw(int32 x) { return fixed5p27().SetRaw(x); } + + fixed5p27& SetRaw(int32 x) { raw = x; return *this; } + int32 GetRaw() const { return raw; } + +}; +STATIC_ASSERT(sizeof(fixed5p27) == 4); +#define fixed5p27_min fixed5p27::Raw(int32_min) +#define fixed5p27_max fixed5p27::Raw(int32_max) + struct uint8_4 { uint8 x[4]; Modified: trunk/OpenMPT/libopenmpt/libopenmpt_impl.cpp =================================================================== --- trunk/OpenMPT/libopenmpt/libopenmpt_impl.cpp 2013-08-06 11:28:29 UTC (rev 2582) +++ trunk/OpenMPT/libopenmpt/libopenmpt_impl.cpp 2013-08-06 11:45:45 UTC (rev 2583) @@ -23,6 +23,8 @@ #include <cstring> #include "soundlib/Sndfile.h" +#include "soundlib/Dither.h" +#include "soundlib/SampleFormatConverters.h" #include "soundlib/FileReader.h" namespace openmpt { @@ -62,13 +64,6 @@ } }; // class log_forwarder -static std::int32_t float_to_fx16( float x ) { - return static_cast<std::int32_t>( x * (1<<16) ); -} -static float fx16_to_float( std::int32_t x ) { - return static_cast<float>( x * (1.0f/(1<<16)) ); -} - class loader_log : public ILog { private: mutable std::vector<std::pair<LogLevel,std::string> > m_Messages; @@ -155,19 +150,16 @@ } } -void module_impl::apply_mixer_settings( std::int32_t samplerate, int channels, bool format_float ) { - SampleFormat format = ( format_float ? SampleFormatFloat32 : SampleFormatInt16 ); +void module_impl::apply_mixer_settings( std::int32_t samplerate, int channels ) { if ( static_cast<std::int32_t>( m_sndFile->m_MixerSettings.gdwMixingFreq ) != samplerate || - static_cast<int>( m_sndFile->m_MixerSettings.gnChannels ) != channels || - m_sndFile->m_MixerSettings.m_SampleFormat != format + static_cast<int>( m_sndFile->m_MixerSettings.gnChannels ) != channels ) { MixerSettings mixersettings = m_sndFile->m_MixerSettings; std::int32_t volrampin_us = mixersettings.GetVolumeRampUpMicroseconds(); std::int32_t volrampout_us = mixersettings.GetVolumeRampDownMicroseconds(); mixersettings.gdwMixingFreq = samplerate; mixersettings.gnChannels = channels; - mixersettings.m_SampleFormat = format; mixersettings.SetVolumeRampUpMicroseconds( volrampin_us ); mixersettings.SetVolumeRampDownMicroseconds( volrampout_us ); m_sndFile->SetMixerSettings( mixersettings ); @@ -178,9 +170,11 @@ } void module_impl::init( const std::map< std::string, std::string > & ctls ) { m_sndFile = std::unique_ptr<CSoundFile>(new CSoundFile()); + m_Dither = std::unique_ptr<Dither>(new Dither()); m_LogForwarder = std::unique_ptr<log_forwarder>(new log_forwarder(m_Log)); m_sndFile->SetCustomLog( m_LogForwarder.get() ); m_currentPositionSeconds = 0.0; + m_Gain = 1.0f; for ( std::map< std::string, std::string >::const_iterator i = ctls.begin(); i != ctls.end(); ++i ) { ctl_set( i->first, i->second ); } @@ -205,9 +199,10 @@ std::size_t count_read = 0; while ( count > 0 ) { std::int16_t * const buffers[4] = { left + count_read, right + count_read, rear_left + count_read, rear_right + count_read }; - std::size_t count_chunk = m_sndFile->ReadNonInterleaved( - reinterpret_cast<void*const*>( buffers ), - static_cast<CSoundFile::samplecount_t>( std::min<std::uint64_t>( count, std::numeric_limits<CSoundFile::samplecount_t>::max() / 2 / 4 / 4 ) ) // safety margin / samplesize / channels + AudioReadTargetGainBuffer<std::int16_t> target(*m_Dither, 0, buffers, m_Gain); + std::size_t count_chunk = m_sndFile->Read( + static_cast<CSoundFile::samplecount_t>( std::min<std::uint64_t>( count, std::numeric_limits<CSoundFile::samplecount_t>::max() / 2 / 4 / 4 ) ), // safety margin / samplesize / channels + target ); if ( count_chunk == 0 ) { break; @@ -221,9 +216,10 @@ std::size_t count_read = 0; while ( count > 0 ) { float * const buffers[4] = { left + count_read, right + count_read, rear_left + count_read, rear_right + count_read }; - std::size_t count_chunk = m_sndFile->ReadNonInterleaved( - reinterpret_cast<void*const*>( buffers ), - static_cast<CSoundFile::samplecount_t>( std::min<std::uint64_t>( count, std::numeric_limits<CSoundFile::samplecount_t>::max() / 2 / 4 / 4 ) ) // safety margin / samplesize / channels + AudioReadTargetGainBuffer<float> target(*m_Dither, 0, buffers, m_Gain); + std::size_t count_chunk = m_sndFile->Read( + static_cast<CSoundFile::samplecount_t>( std::min<std::uint64_t>( count, std::numeric_limits<CSoundFile::samplecount_t>::max() / 2 / 4 / 4 ) ), // safety margin / samplesize / channels + target ); if ( count_chunk == 0 ) { break; @@ -236,9 +232,10 @@ std::size_t module_impl::read_interleaved_wrapper( std::size_t count, std::size_t channels, std::int16_t * interleaved ) { std::size_t count_read = 0; while ( count > 0 ) { - std::size_t count_chunk = m_sndFile->ReadInterleaved( - reinterpret_cast<void*>( interleaved + count_read * channels ), - static_cast<CSoundFile::samplecount_t>( std::min<std::uint64_t>( count, std::numeric_limits<CSoundFile::samplecount_t>::max() / 2 / 4 / 4 ) ) // safety margin / samplesize / channels + AudioReadTargetGainBuffer<std::int16_t> target(*m_Dither, interleaved + count_read * channels, 0, m_Gain); + std::size_t count_chunk = m_sndFile->Read( + static_cast<CSoundFile::samplecount_t>( std::min<std::uint64_t>( count, std::numeric_limits<CSoundFile::samplecount_t>::max() / 2 / 4 / 4 ) ), // safety margin / samplesize / channels + target ); if ( count_chunk == 0 ) { break; @@ -251,9 +248,10 @@ std::size_t module_impl::read_interleaved_wrapper( std::size_t count, std::size_t channels, float * interleaved ) { std::size_t count_read = 0; while ( count > 0 ) { - std::size_t count_chunk = m_sndFile->ReadInterleaved( - reinterpret_cast<void*>( interleaved + count_read * channels ), - static_cast<CSoundFile::samplecount_t>( std::min<std::uint64_t>( count, std::numeric_limits<CSoundFile::samplecount_t>::max() / 2 / 4 / 4 ) ) // safety margin / samplesize / channels + AudioReadTargetGainBuffer<float> target(*m_Dither, interleaved + count_read * channels, 0, m_Gain); + std::size_t count_chunk = m_sndFile->Read( + static_cast<CSoundFile::samplecount_t>( std::min<std::uint64_t>( count, std::numeric_limits<CSoundFile::samplecount_t>::max() / 2 / 4 / 4 ) ), // safety margin / samplesize / channels + target ); if ( count_chunk == 0 ) { break; @@ -348,7 +346,7 @@ std::int32_t module_impl::get_render_param( int param ) const { switch ( param ) { case module::RENDER_MASTERGAIN_MILLIBEL: { - return static_cast<std::int32_t>( 1000.0f * 2.0f * std::log10( fx16_to_float( m_sndFile->m_MixerSettings.m_FinalOutputGain ) ) ); + return static_cast<std::int32_t>( 1000.0f * 2.0f * std::log10( m_Gain ) ); } break; case module::RENDER_STEREOSEPARATION_PERCENT: { return m_sndFile->m_MixerSettings.m_nStereoSeparation * 100 / 128; @@ -368,12 +366,7 @@ void module_impl::set_render_param( int param, std::int32_t value ) { switch ( param ) { case module::RENDER_MASTERGAIN_MILLIBEL: { - float gainFactor = static_cast<float>( std::pow( 10.0f, value * 0.001f * 0.5f ) ); - if ( static_cast<std::int32_t>( m_sndFile->m_MixerSettings.m_FinalOutputGain ) != float_to_fx16( gainFactor ) ) { - MixerSettings settings = m_sndFile->m_MixerSettings; - settings.m_FinalOutputGain = float_to_fx16( gainFactor ); - m_sndFile->SetMixerSettings( settings ); - } + m_Gain = static_cast<float>( std::pow( 10.0f, value * 0.001f * 0.5f ) ); } break; case module::RENDER_STEREOSEPARATION_PERCENT: { std::int32_t newvalue = value * 128 / 100; @@ -405,7 +398,7 @@ if ( !mono ) { throw openmpt::exception("null pointer"); } - apply_mixer_settings( samplerate, 1, false ); + apply_mixer_settings( samplerate, 1 ); count = read_wrapper( count, mono, 0, 0, 0 ); m_currentPositionSeconds += static_cast<double>( count ) / static_cast<double>( samplerate ); return count; @@ -414,7 +407,7 @@ if ( !left || !right ) { throw openmpt::exception("null pointer"); } - apply_mixer_settings( samplerate, 2, false ); + apply_mixer_settings( samplerate, 2 ); count = read_wrapper( count, left, right, 0, 0 ); m_currentPositionSeconds += static_cast<double>( count ) / static_cast<double>( samplerate ); return count; @@ -423,7 +416,7 @@ if ( !left || !right || !rear_left || !rear_right ) { throw openmpt::exception("null pointer"); } - apply_mixer_settings( samplerate, 4, false ); + apply_mixer_settings( samplerate, 4 ); count = read_wrapper( count, left, right, rear_left, rear_right ); m_currentPositionSeconds += static_cast<double>( count ) / static_cast<double>( samplerate ); return count; @@ -432,7 +425,7 @@ if ( !mono ) { throw openmpt::exception("null pointer"); } - apply_mixer_settings( samplerate, 1, true ); + apply_mixer_settings( samplerate, 1 ); count = read_wrapper( count, mono, 0, 0, 0 ); m_currentPositionSeconds += static_cast<double>( count ) / static_cast<double>( samplerate ); return count; @@ -441,7 +434,7 @@ if ( !left || !right ) { throw openmpt::exception("null pointer"); } - apply_mixer_settings( samplerate, 2, true ); + apply_mixer_settings( samplerate, 2 ); count = read_wrapper( count, left, right, 0, 0 ); m_currentPositionSeconds += static_cast<double>( count ) / static_cast<double>( samplerate ); return count; @@ -450,7 +443,7 @@ if ( !left || !right || !rear_left || !rear_right ) { throw openmpt::exception("null pointer"); } - apply_mixer_settings( samplerate, 4, true ); + apply_mixer_settings( samplerate, 4 ); count = read_wrapper( count, left, right, rear_left, rear_right ); m_currentPositionSeconds += static_cast<double>( count ) / static_cast<double>( samplerate ); return count; @@ -459,7 +452,7 @@ if ( !interleaved_stereo ) { throw openmpt::exception("null pointer"); } - apply_mixer_settings( samplerate, 2, false ); + apply_mixer_settings( samplerate, 2 ); count = read_interleaved_wrapper( count, 2, interleaved_stereo ); m_currentPositionSeconds += static_cast<double>( count ) / static_cast<double>( samplerate ); return count; @@ -468,7 +461,7 @@ if ( !interleaved_quad ) { throw openmpt::exception("null pointer"); } - apply_mixer_settings( samplerate, 4, false ); + apply_mixer_settings( samplerate, 4 ); count = read_interleaved_wrapper( count, 4, interleaved_quad ); m_currentPositionSeconds += static_cast<double>( count ) / static_cast<double>( samplerate ); return count; @@ -477,7 +470,7 @@ if ( !interleaved_stereo ) { throw openmpt::exception("null pointer"); } - apply_mixer_settings( samplerate, 2, true ); + apply_mixer_settings( samplerate, 2 ); count = read_interleaved_wrapper( count, 2, interleaved_stereo ); m_currentPositionSeconds += static_cast<double>( count ) / static_cast<double>( samplerate ); return count; @@ -486,7 +479,7 @@ if ( !interleaved_quad ) { throw openmpt::exception("null pointer"); } - apply_mixer_settings( samplerate, 4, true ); + apply_mixer_settings( samplerate, 4 ); count = read_interleaved_wrapper( count, 4, interleaved_quad ); m_currentPositionSeconds += static_cast<double>( count ) / static_cast<double>( samplerate ); return count; Modified: trunk/OpenMPT/libopenmpt/libopenmpt_impl.hpp =================================================================== --- trunk/OpenMPT/libopenmpt/libopenmpt_impl.hpp 2013-08-06 11:28:29 UTC (rev 2582) +++ trunk/OpenMPT/libopenmpt/libopenmpt_impl.hpp 2013-08-06 11:45:45 UTC (rev 2583) @@ -19,6 +19,7 @@ // forward declarations class FileReader; class CSoundFile; +class Dither; namespace openmpt { @@ -56,12 +57,14 @@ std::unique_ptr<log_forwarder> m_LogForwarder; double m_currentPositionSeconds; std::unique_ptr<CSoundFile> m_sndFile; + std::unique_ptr<Dither> m_Dither; + float m_Gain; std::vector<std::string> m_loaderMessages; public: void PushToCSoundFileLog( const std::string & text ) const; void PushToCSoundFileLog( int loglevel, const std::string & text ) const; private: - void apply_mixer_settings( std::int32_t samplerate, int channels, bool format_float ); + void apply_mixer_settings( std::int32_t samplerate, int channels ); void apply_libopenmpt_defaults(); void init( const std::map< std::string, std::string > & ctls ); static void load( CSoundFile & sndFile, const FileReader & file ); Modified: trunk/OpenMPT/mptrack/MainFrm.cpp =================================================================== --- trunk/OpenMPT/mptrack/MainFrm.cpp 2013-08-06 11:28:29 UTC (rev 2582) +++ trunk/OpenMPT/mptrack/MainFrm.cpp 2013-08-06 11:45:45 UTC (rev 2583) @@ -12,6 +12,7 @@ #include "mptrack.h" #include "MainFrm.h" #include "../sounddev/SoundDevice.h" +#include "../soundlib/SampleFormatConverters.h" #include "moddoc.h" #include "childfrm.h" #include "Dlsbank.h" @@ -242,8 +243,6 @@ SetTitle(title); OnUpdateFrameTitle(false); - CSoundFile::gpSndMixHook = CalcStereoVuMeters; - // Check for valid sound device if (!EnumerateSoundDevices(SNDDEV_GET_TYPE(TrackerSettings::Instance().m_nWaveDevice), SNDDEV_GET_NUMBER(TrackerSettings::Instance().m_nWaveDevice), nullptr, 0)) { @@ -371,8 +370,6 @@ BOOL CMainFrame::DestroyWindow() //------------------------------ { - CSoundFile::gpSndMixHook = nullptr; - // Uninstall Keyboard Hook if (ghKbdHook) { @@ -780,19 +777,92 @@ } +//============================== +class StereoVuMeterTargetWrapper +//============================== + : public IAudioReadTarget +{ +private: + const SampleFormat sampleFormat; + Dither &dither; + void *buffer; +public: + StereoVuMeterTargetWrapper(SampleFormat sampleFormat_, Dither &dither_, void *buffer_) + : sampleFormat(sampleFormat_) + , dither(dither_) + , buffer(buffer_) + { + ALWAYS_ASSERT(sampleFormat.IsValid()); + } + virtual void DataCallback(int *MixSoundBuffer, std::size_t channels, std::size_t countChunk) + { + CMainFrame::CalcStereoVuMeters(MixSoundBuffer, countChunk, channels); + switch(sampleFormat.value) + { + case SampleFormatUnsigned8: + { + typedef SampleFormatToType<SampleFormatUnsigned8>::type Tsample; + AudioReadTargetBuffer<Tsample> target(dither, reinterpret_cast<Tsample*>(buffer), nullptr); + target.DataCallback(MixSoundBuffer, channels, countChunk); + } + break; + case SampleFormatInt16: + { + typedef SampleFormatToType<SampleFormatInt16>::type Tsample; + AudioReadTargetBuffer<Tsample> target(dither, reinterpret_cast<Tsample*>(buffer), nullptr); + target.DataCallback(MixSoundBuffer, channels, countChunk); + } + break; + case SampleFormatInt24: + { + typedef SampleFormatToType<SampleFormatInt24>::type Tsample; + AudioReadTargetBuffer<Tsample> target(dither, reinterpret_cast<Tsample*>(buffer), nullptr); + target.DataCallback(MixSoundBuffer, channels, countChunk); + } + break; + case SampleFormatInt32: + { + typedef SampleFormatToType<SampleFormatInt32>::type Tsample; + AudioReadTargetBuffer<Tsample> target(dither, reinterpret_cast<Tsample*>(buffer), nullptr); + target.DataCallback(MixSoundBuffer, channels, countChunk); + } + break; + case SampleFormatFloat32: + { + typedef SampleFormatToType<SampleFormatFloat32>::type Tsample; + AudioReadTargetBuffer<Tsample> target(dither, reinterpret_cast<Tsample*>(buffer), nullptr); + target.DataCallback(MixSoundBuffer, channels, countChunk); + } + break; + case SampleFormatFixed5p27: + { + typedef SampleFormatToType<SampleFormatFixed5p27>::type Tsample; + AudioReadTargetBuffer<Tsample> target(dither, reinterpret_cast<Tsample*>(buffer), nullptr); + target.DataCallback(MixSoundBuffer, channels, countChunk); + } + break; + } + // increment output buffer for potentially next callback + buffer = (char*)buffer + (sampleFormat.GetBitsPerSample()/8) * channels * countChunk; + } +}; + + void CMainFrame::AudioRead(PVOID pvData, ULONG NumFrames) //------------------------------------------------------- { OPENMPT_PROFILE_FUNCTION(Profiler::Audio); - CSoundFile::samplecount_t renderedFrames = m_pSndFile->ReadInterleaved(pvData, NumFrames); + const SampleFormat sampleFormat = TrackerSettings::Instance().m_SampleFormat; + StereoVuMeterTargetWrapper target(sampleFormat, m_Dither, pvData); + CSoundFile::samplecount_t renderedFrames = m_pSndFile->Read(NumFrames, target); 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::size_t frameSize = m_pSndFile->m_MixerSettings.gnChannels * (sampleFormat.GetBitsPerSample()/8); + if(sampleFormat.IsUnsigned()) { std::memset((char*)(pvData) + renderedFrames * frameSize, 0x80, remainingFrames * frameSize); } else @@ -819,51 +889,35 @@ } -bool CMainFrame::audioTryOpeningDevice(UINT channels, UINT bits, UINT samplespersec) -//---------------------------------------------------------------------------------- +bool CMainFrame::audioTryOpeningDevice(UINT channels, SampleFormat sampleFormat, UINT samplespersec) +//-------------------------------------------------------------------------------------------------- { - WAVEFORMATEXTENSIBLE WaveFormat; - - UINT bytespersample = (bits/8) * channels; - WaveFormat.Format.wFormatTag = WAVE_FORMAT_PCM; - WaveFormat.Format.nChannels = (unsigned short) channels; - WaveFormat.Format.nSamplesPerSec = samplespersec; - WaveFormat.Format.nAvgBytesPerSec = samplespersec * bytespersample; - WaveFormat.Format.nBlockAlign = (unsigned short)bytespersample; - WaveFormat.Format.wBitsPerSample = (unsigned short)bits; - WaveFormat.Format.cbSize = 0; - // MultiChannel configuration - if ((WaveFormat.Format.wBitsPerSample == 32) || (WaveFormat.Format.nChannels > 2)) + Util::lock_guard<Util::mutex> lock(m_SoundDeviceMutex); + const UINT nDevType = SNDDEV_GET_TYPE(TrackerSettings::Instance().m_nWaveDevice); + if(gpSoundDevice && (gpSoundDevice->GetDeviceType() != nDevType)) { - 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; - switch(WaveFormat.Format.nChannels) - { - case 1: WaveFormat.dwChannelMask = 0x0004; break; // FRONT_CENTER - case 2: WaveFormat.dwChannelMask = 0x0003; break; // FRONT_LEFT | FRONT_RIGHT - case 3: WaveFormat.dwChannelMask = 0x0103; break; // FRONT_LEFT|FRONT_RIGHT|BACK_CENTER - case 4: WaveFormat.dwChannelMask = 0x0033; break; // FRONT_LEFT|FRONT_RIGHT|BACK_LEFT|BACK_RIGHT - default: WaveFormat.dwChannelMask = 0; return false; break; - } - WaveFormat.SubFormat = guid_MEDIASUBTYPE_PCM; + delete gpSoundDevice; + gpSoundDevice = NULL; } + if(!gpSoundDevice) { - Util::lock_guard<Util::mutex> lock(m_SoundDeviceMutex); - UINT nDevType = SNDDEV_GET_TYPE(TrackerSettings::Instance().m_nWaveDevice); - if(gpSoundDevice && (gpSoundDevice->GetDeviceType() != nDevType)) - { - delete gpSoundDevice; - gpSoundDevice = NULL; - } - if(!gpSoundDevice) gpSoundDevice = CreateSoundDevice(nDevType); - if(!gpSoundDevice) return false; - gpSoundDevice->SetSource(this); - gpSoundDevice->Configure(m_hWnd, TrackerSettings::Instance().m_LatencyMS, TrackerSettings::Instance().m_UpdateIntervalMS, TrackerSettings::Instance().GetSoundDeviceFlags()); - if (!gpSoundDevice->Open(SNDDEV_GET_NUMBER(TrackerSettings::Instance().m_nWaveDevice), &WaveFormat.Format)) return false; + gpSoundDevice = CreateSoundDevice(nDevType); } - return true; + if(!gpSoundDevice) + { + return false; + } + gpSoundDevice->SetSource(this); + SoundDeviceSettings settings; + settings.hWnd = m_hWnd; + settings.LatencyMS = TrackerSettings::Instance().m_LatencyMS; + settings.UpdateIntervalMS = TrackerSettings::Instance().m_UpdateIntervalMS; + settings.fulCfgOptions = TrackerSettings::Instance().GetSoundDeviceFlags(); + settings.Samplerate = samplespersec; + settings.Channels = (uint8)channels; + settings.BitsPerSample = (uint8)sampleFormat.GetBitsPerSample(); + settings.FloatingPoint = sampleFormat.IsFloat(); + return gpSoundDevice->Open(SNDDEV_GET_NUMBER(TrackerSettings::Instance().m_nWaveDevice), settings); } @@ -886,18 +940,18 @@ if(!err) { err = !audioTryOpeningDevice(TrackerSettings::Instance().m_MixerSettings.gnChannels, - TrackerSettings::Instance().m_MixerSettings.m_SampleFormat, + TrackerSettings::Instance().m_SampleFormat, TrackerSettings::Instance().m_MixerSettings.gdwMixingFreq); SampleFormat fixedBitsPerSample = SampleFormatInvalid; { Util::lock_guard<Util::mutex> lock(m_SoundDeviceMutex); - fixedBitsPerSample = (gpSoundDevice) ? static_cast<SampleFormat>(gpSoundDevice->HasFixedBitsPerSample()) : SampleFormatInvalid; + fixedBitsPerSample = (gpSoundDevice) ? SampleFormat(gpSoundDevice->HasFixedBitsPerSample()) : SampleFormatInvalid; } - if(err && (fixedBitsPerSample && (fixedBitsPerSample != TrackerSettings::Instance().m_MixerSettings.m_SampleFormat))) + if(err && (fixedBitsPerSample.IsValid() && (fixedBitsPerSample != TrackerSettings::Instance().m_SampleFormat))) { - if(fixedBitsPerSample) TrackerSettings::Instance().m_MixerSettings.m_SampleFormat = fixedBitsPerSample; + TrackerSettings::Instance().m_SampleFormat = fixedBitsPerSample; err = !audioTryOpeningDevice(TrackerSettings::Instance().m_MixerSettings.gnChannels, - TrackerSettings::Instance().m_MixerSettings.m_SampleFormat, + TrackerSettings::Instance().m_SampleFormat, TrackerSettings::Instance().m_MixerSettings.gdwMixingFreq); } } @@ -1701,7 +1755,7 @@ BOOL CMainFrame::SetupSoundCard(DWORD deviceflags, DWORD rate, SampleFormat sampleformat, UINT nChns, UINT latency_ms, UINT updateinterval_ms, LONG wd) -//--------------------------------------------------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------------------------------------------------------- { const bool isPlaying = IsPlaying(); if ((TrackerSettings::Instance().GetSoundDeviceFlags() != deviceflags) @@ -1709,7 +1763,7 @@ || (TrackerSettings::Instance().m_nWaveDevice != wd) || (TrackerSettings::Instance().m_LatencyMS != latency_ms) || (TrackerSettings::Instance().m_UpdateIntervalMS != updateinterval_ms) - || (TrackerSettings::Instance().m_MixerSettings.m_SampleFormat != sampleformat) + || (TrackerSettings::Instance().m_SampleFormat != sampleformat) || (TrackerSettings::Instance().m_MixerSettings.gnChannels != nChns)) { CModDoc *pActiveMod = NULL; @@ -1723,7 +1777,7 @@ TrackerSettings::Instance().SetSoundDeviceFlags(deviceflags); TrackerSettings::Instance().m_LatencyMS = latency_ms; TrackerSettings::Instance().m_UpdateIntervalMS = updateinterval_ms; - TrackerSettings::Instance().m_MixerSettings.m_SampleFormat = sampleformat; + TrackerSettings::Instance().m_SampleFormat = sampleformat; TrackerSettings::Instance().m_MixerSettings.gnChannels = nChns; { CriticalSection cs; @@ -1892,7 +1946,7 @@ CPropertySheet dlg("OpenMPT Setup", this, m_nLastOptionsPage); COptionsGeneral general; - COptionsSoundcard sounddlg(TrackerSettings::Instance().m_MixerSettings.gdwMixingFreq, TrackerSettings::Instance().GetSoundDeviceFlags(), TrackerSettings::Instance().m_MixerSettings.m_SampleFormat, TrackerSettings::Instance().m_MixerSettings.gnChannels, TrackerSettings::Instance().m_LatencyMS, TrackerSettings::Instance().m_UpdateIntervalMS, TrackerSettings::Instance().m_nWaveDevice); + COptionsSoundcard sounddlg(TrackerSettings::Instance().m_MixerSettings.gdwMixingFreq, TrackerSettings::Instance().GetSoundDeviceFlags(), TrackerSettings::Instance().m_SampleFormat, TrackerSettings::Instance().m_MixerSettings.gnChannels, TrackerSettings::Instance().m_LatencyMS, TrackerSettings::Instance().m_UpdateIntervalMS, TrackerSettings::Instance().m_nWaveDevice); COptionsKeyboard keyboard; COptionsColors colors; COptionsPlayer playerdlg; Modified: trunk/OpenMPT/mptrack/Mainfrm.h =================================================================== --- trunk/OpenMPT/mptrack/Mainfrm.h 2013-08-06 11:28:29 UTC (rev 2582) +++ trunk/OpenMPT/mptrack/Mainfrm.h 2013-08-06 11:45:45 UTC (rev 2583) @@ -16,6 +16,7 @@ #include "../common/mutex.h" #include "../sounddev/SoundDevice.h" #include "../soundlib/Sndfile.h" +#include "../soundlib/Dither.h" class CInputHandler; class CModDoc; @@ -272,6 +273,7 @@ // Low-Level Audio mutable Util::mutex m_SoundDeviceMutex; ISoundDevice *gpSoundDevice; + Dither m_Dither; HANDLE m_hNotifyWakeUp; HANDLE m_hNotifyThread; DWORD m_dwNotifyThreadId; @@ -328,7 +330,7 @@ void AudioDone(ULONG NumSamples, ULONG SamplesLatency); void AudioDone(ULONG NumSamples); - bool audioTryOpeningDevice(UINT channels, UINT bits, UINT samplespersec); + bool audioTryOpeningDevice(UINT channels, SampleFormat sampleFormat, UINT samplespersec); bool audioOpenDevice(); bool audioReopenDevice(); void audioCloseDevice(); Modified: trunk/OpenMPT/mptrack/Mod2wave.cpp =================================================================== --- trunk/OpenMPT/mptrack/Mod2wave.cpp 2013-08-06 11:28:29 UTC (rev 2582) +++ trunk/OpenMPT/mptrack/Mod2wave.cpp 2013-08-06 11:45:45 UTC (rev 2583) @@ -20,12 +20,67 @@ #include "WAVTools.h" #include "../common/version.h" #include "ACMConvert.h" +#include "../soundlib/Dither.h" +#include "../soundlib/SampleFormatConverters.h" #include <fstream> extern UINT nMixingRates[NUMMIXRATE]; extern LPCSTR gszChnCfgNames[3]; + +static CSoundFile::samplecount_t ReadInterleaved(CSoundFile &sndFile, void *outputBuffer, CSoundFile::samplecount_t count, SampleFormat sampleFormat, Dither &dither) +//------------------------------------------------------------------------------------------------------------------------------------------------------------------- +{ + switch(sampleFormat.value) + { + case SampleFormatUnsigned8: + { + typedef SampleFormatToType<SampleFormatUnsigned8>::type Tsample; + AudioReadTargetBuffer<Tsample> target(dither, reinterpret_cast<Tsample*>(outputBuffer), nullptr); + return sndFile.Read(count, target); + } + break; + case SampleFormatInt16: + { + typedef SampleFormatToType<SampleFormatInt16>::type Tsample; + AudioReadTargetBuffer<Tsample> target(dither, reinterpret_cast<Tsample*>(outputBuffer), nullptr); + return sndFile.Read(count, target); + } + break; + case SampleFormatInt24: + { + typedef SampleFormatToType<SampleFormatInt24>::type Tsample; + AudioReadTargetBuffer<Tsample> target(dither, reinterpret_cast<Tsample*>(outputBuffer), nullptr); + return sndFile.Read(count, target); + } + break; + case SampleFormatInt32: + { + typedef SampleFormatToType<SampleFormatInt32>::type Tsample; + AudioReadTargetBuffer<Tsample> target(dither, reinterpret_cast<Tsample*>(outputBuffer), nullptr); + return sndFile.Read(count, target); + } + break; + case SampleFormatFloat32: + { + typedef SampleFormatToType<SampleFormatFloat32>::type Tsample; + AudioReadTargetBuffer<Tsample> target(dither, reinterpret_cast<Tsample*>(outputBuffer), nullptr); + return sndFile.Read(count, target); + } + break; + case SampleFormatFixed5p27: + { + typedef SampleFormatToType<SampleFormatFixed5p27>::type Tsample; + AudioReadTargetBuffer<Tsample> target(dither, reinterpret_cast<Tsample*>(outputBuffer), nullptr); + return sndFile.Read(count, target); + } + break; + } + return 0; +} + + 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}; @@ -635,15 +690,17 @@ file.Open(m_lpszFileName); } + Dither dither; + + SampleFormat sampleFormat = (m_pWaveFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) ? SampleFormatFloat32 : (SampleFormat)m_pWaveFormat->wBitsPerSample; MixerSettings oldmixersettings = m_pSndFile->m_MixerSettings; MixerSettings mixersettings = TrackerSettings::Instance().m_MixerSettings; mixersettings.gdwMixingFreq = m_pWaveFormat->nSamplesPerSec; - 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) { - mixersettings.m_SampleFormat = SampleFormatFloat32; + sampleFormat = SampleFormatFloat32; #ifndef NO_AGC mixersettings.DSPMask &= ~SNDDSP_AGC; #endif @@ -705,10 +762,10 @@ UINT lRead = 0; if(m_bNormalize) { - lRead = m_pSndFile->ReadInterleaved(floatbuffer, MIXBUFFERSIZE); + lRead = ReadInterleaved(*m_pSndFile, floatbuffer, MIXBUFFERSIZE, sampleFormat, dither); } else { - lRead = m_pSndFile->ReadInterleaved(buffer, MIXBUFFERSIZE); + lRead = ReadInterleaved(*m_pSndFile, buffer, MIXBUFFERSIZE, sampleFormat, dither); } // Process cue points (add base offset), if there are any to process. @@ -753,7 +810,7 @@ } else { - UINT lWrite = fwrite(buffer, 1, lRead * m_pSndFile->m_MixerSettings.gnChannels * (m_pSndFile->m_MixerSettings.GetBitsPerSample()/8), file.GetFile()); + UINT lWrite = fwrite(buffer, 1, lRead * m_pSndFile->m_MixerSettings.gnChannels * (sampleFormat.GetBitsPerSample()/8), file.GetFile()); if (!lWrite) break; bytesWritten += lWrite; @@ -815,7 +872,6 @@ ::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; @@ -854,10 +910,10 @@ 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; + case 1: ConvertInterleavedFixedPointToInterleaved<MIXING_FRACTIONAL_BITS>(reinterpret_cast<uint8*>(buffer), mixbuffer, m_pWaveFormat->nChannels, framesChunk); break; + case 2: ConvertInterleavedFixedPointToInterleaved<MIXING_FRACTIONAL_BITS>(reinterpret_cast<int16*>(buffer), mixbuffer, m_pWaveFormat->nChannels, framesChunk); break; + case 3: ConvertInterleavedFixedPointToInterleaved<MIXING_FRACTIONAL_BITS>(reinterpret_cast<int24*>(buffer), mixbuffer, m_pWaveFormat->nChannels, framesChunk); break; + case 4: ConvertInterleavedFixedPointToInterleaved<MIXING_FRACTIONAL_BITS>(reinterpret_cast<int32*>(buffer), mixbuffer, m_pWaveFormat->nChannels, framesChunk); break; default: ASSERT(false); break; } } @@ -1002,6 +1058,8 @@ FILE *f; MixerSettings mixersettings = TrackerSettings::Instance().m_MixerSettings; + SampleFormat sampleFormat = SampleFormatInvalid; + Dither dither; progress = ::GetDlgItem(m_hWnd, IDC_PROGRESS1); if ((!m_pSndFile) || (!m_lpszFileName) || (!m_pwfx) || (!m_hadid)) goto OnError; @@ -1056,11 +1114,11 @@ // Write ID3v2.4 Tags m_FileTags.WriteID3v2Tags(f); } + sampleFormat = SampleFormatInt16; DWORD oldsndcfg = m_pSndFile->m_MixerSettings.MixerFlags; oldrepeat = m_pSndFile->GetRepeatCount(); const uint64 dwSongTime = static_cast<uint64>(m_pSndFile->GetSongTime() + 0.5); mixersettings.gdwMixingFreq = wfxSrc.nSamplesPerSec; - mixersettings.m_SampleFormat = SampleFormatInt16; mixersettings.gnChannels = wfxSrc.nChannels; m_pSndFile->SetRepeatCount(0); m_pSndFile->ResetChannels(); @@ -1104,7 +1162,7 @@ UINT lRead = 0; if (!bFinished) { - lRead = m_pSndFile->ReadInterleaved(pcmBuffer + WAVECONVERTBUFSIZE - pcmBufSize, pcmBufSize/(m_pSndFile->m_MixerSettings.gnChannels*m_pSndFile->m_MixerSettings.GetBitsPerSample()/8)); + lRead = ReadInterleaved(*m_pSndFile, pcmBuffer + WAVECONVERTBUFSIZE - pcmBufSize, pcmBufSize/(m_pSndFile->m_MixerSettings.gnChannels*sampleFormat.GetBitsPerSample()/8), sampleFormat, dither); if (!lRead) bFinished = true; } ullSamples += lRead; Modified: trunk/OpenMPT/mptrack/Mpdlgs.cpp =================================================================== --- trunk/OpenMPT/mptrack/Mpdlgs.cpp 2013-08-06 11:28:29 UTC (rev 2582) +++ trunk/OpenMPT/mptrack/Mpdlgs.cpp 2013-08-06 11:45:45 UTC (rev 2583) @@ -249,26 +249,53 @@ } UpdateControls(m_nSoundDevice); } + + UpdateChannelsFormat(m_nSoundDevice); + + return TRUE; +} + + +void COptionsSoundcard::UpdateChannelsFormat(int dev) +//--------------------------------------------------- +{ // Sample Format + CHAR s[128]; + UINT n = 0; + m_CbnQuality.ResetContent(); + for(UINT channels = 4; channels >= 1; channels /= 2) { - UINT n = 0; - for (UINT i=0; i<3*3; i++) + for(UINT bits = 32; bits >= 8; bits -= 8) { - UINT j = 3*3-1-i; - UINT nBits = 8 << (j % 3); - UINT nChannels = 1 << (j/3); - if ((((nChannels <= 2) && (nBits <= 16)) || (theApp.IsWaveExEnabled()) || (bAsio)) - && ((nBits >= 16) || (nChannels <= 2))) + if(channels > 2 && bits > 16 && !theApp.IsWaveExEnabled()) { - wsprintf(s, "%s, %d Bit", gszChnCfgNames[j/3], nBits); + continue; + } + if(bits != 16 && bits !=32 && SNDDEV_GET_TYPE(dev) == SNDDEV_ASIO) + { + // CASIODevice only supports 16bit or 32bit input for now + continue; + } + if(bits == 32 && SNDDEV_GET_TYPE(dev) != SNDDEV_ASIO) + { + wsprintf(s, "%s, %d Bits, Float", gszChnCfgNames[(channels+2)/2-1], bits); UINT ndx = m_CbnQuality.AddString(s); - m_CbnQuality.SetItemData( ndx, (nChannels << 8) | nBits ); - if (((SampleFormat)nBits == m_SampleFormat) && (nChannels == m_nChannels)) n = ndx; + m_CbnQuality.SetItemData(ndx, (channels << 8) | (32+128)); + if((SampleFormat)(32+128) == m_SampleFormat && channels == m_nChannels) + { + n = ndx; + } } + wsprintf(s, "%s, %d Bit", gszChnCfgNames[(channels+2)/2-1], bits); + UINT ndx = m_CbnQuality.AddString(s); + m_CbnQuality.SetItemData(ndx, (channels << 8) | bits); + if((SampleFormat)bits == m_SampleFormat && channels == m_nChannels) + { + n = ndx; + } } - m_CbnQuality.SetCurSel(n); } - return TRUE; + m_CbnQuality.SetCurSel(n); } @@ -338,6 +365,7 @@ int dev = m_CbnDevice.GetItemData(n); UpdateControls(dev); UpdateSampleRates(dev); + UpdateChannelsFormat(dev); OnSettingsChanged(); } } Modified: trunk/OpenMPT/mptrack/Mpdlgs.h =================================================================== --- trunk/OpenMPT/mptrack/Mpdlgs.h 2013-08-06 11:28:29 UTC (rev 2582) +++ trunk/OpenMPT/mptrack/Mpdlgs.h 2013-08-06 11:45:45 UTC (rev 2583) @@ -41,6 +41,7 @@ private: void UpdateSampleRates(int dev); + void UpdateChannelsFormat(int dev); void UpdateControls(int dev); void SetPreAmpSliderPosition(); Modified: trunk/OpenMPT/mptrack/TrackerSettings.cpp =================================================================== --- trunk/OpenMPT/mptrack/TrackerSettings.cpp 2013-08-06 11:28:29 UTC (rev 2582) +++ trunk/OpenMPT/mptrack/TrackerSettings.cpp 2013-08-06 11:45:45 UTC (rev 2583) @@ -76,6 +76,7 @@ m_UpdateIntervalMS = SNDDEV_DEFAULT_UPDATEINTERVAL_MS; m_SoundDeviceExclusiveMode = false; m_SoundDeviceBoostThreadPriority = true; + m_SampleFormat = SampleFormatInt16; #ifndef NO_EQ // Default EQ settings @@ -360,7 +361,7 @@ m_MixerSettings.DSPMask = CMainFrame::GetPrivateProfileDWord("Sound Settings", "Quality", m_MixerSettings.DSPMask, iniFile); m_ResamplerSettings.SrcMode = (ResamplingMode)CMainFrame::GetPrivateProfileDWord("Sound Settings", "SrcMode", m_ResamplerSettings.SrcMode, iniFile); m_MixerSettings.gdwMixingFreq = CMainFrame::GetPrivateProfileDWord("Sound Settings", "Mixing_Rate", 0, iniFile); - m_MixerSettings.m_SampleFormat = (SampleFormat)CMainFrame::GetPrivateProfileDWord("Sound Settings", "BitsPerSample", (DWORD)m_MixerSettings.m_SampleFormat, iniFile); + m_SampleFormat = CMainFrame::GetPrivateProfileLong("Sound Settings", "BitsPerSample", m_SampleFormat, iniFile); m_MixerSettings.gnChannels = CMainFrame::GetPrivateProfileDWord("Sound Settings", "ChannelMode", m_MixerSettings.gnChannels, iniFile); DWORD LatencyMS = CMainFrame::GetPrivateProfileDWord("Sound Settings", "Latency", 0, iniFile); DWORD UpdateIntervalMS = CMainFrame::GetPrivateProfileDWord("Sound Settings", "UpdateInterval", 0, iniFile); @@ -693,9 +694,9 @@ RegQueryValueEx(key, "RowSpacing", NULL, &dwREG_DWORD, (LPBYTE)&m_nRowHighlightMeasures, &dwDWORDSize); RegQueryValueEx(key, "RowSpacing2", NULL, &dwREG_DWORD, (LPBYTE)&m_nRowHighlightBeats, &dwDWORDSize); RegQueryValueEx(key, "LoopSong", NULL, &dwREG_DWORD, (LPBYTE)&gbLoopSong, &dwDWORDSize); - DWORD dummy_sampleformat = (DWORD)m_MixerSettings.m_SampleFormat; + DWORD dummy_sampleformat = m_SampleFormat; RegQueryValueEx(key, "BitsPerSample", NULL, &dwREG_DWORD, (LPBYTE)&dummy_sampleformat, &dwDWORDSize); - m_MixerSettings.m_SampleFormat = (SampleFormat)dummy_sampleformat; + m_SampleFormat = (long)dummy_sampleformat; RegQueryValueEx(key, "ChannelMode", NULL, &dwREG_DWORD, (LPBYTE)&m_MixerSettings.gnChannels, &dwDWORDSize); RegQueryValueEx(key, "MidiImportSpeed", NULL, &dwREG_DWORD, (LPBYTE)&midiImportSpeed, &dwDWORDSize); RegQueryValueEx(key, "MidiImportPatLen", NULL, &dwREG_DWORD, (LPBYTE)&midiImportPatternLen, &dwDWORDSize); @@ -841,7 +842,7 @@ CMainFrame::WritePrivateProfileDWord("Sound Settings", "Quality", m_MixerSettings.DSPMask, iniFile); CMainFrame::WritePrivateProfileDWord("Sound Settings", "SrcMode", m_ResamplerSettings.SrcMode, iniFile); CMainFrame::WritePrivateProfileDWord("Sound Settings", "Mixing_Rate", m_MixerSettings.gdwMixingFreq, iniFile); - CMainFrame::WritePrivateProfileDWord("Sound Settings", "BitsPerSample", (DWORD)m_MixerSettings.m_SampleFormat, iniFile); + CMainFrame::WritePrivateProfileLong("Sound Settings", "BitsPerSample", m_SampleFormat, iniFile); CMainFrame::WritePrivateProfileDWord("Sound Settings", "ChannelMode", m_MixerSettings.gnChannels, iniFile); CMainFrame::WritePrivateProfileDWord("Sound Settings", "Latency", m_LatencyMS, iniFile); CMainFrame::WritePrivateProfileDWord("Sound Settings", "UpdateInterval", m_UpdateIntervalMS, iniFile); Modified: trunk/OpenMPT/mptrack/TrackerSettings.h =================================================================== --- trunk/OpenMPT/mptrack/TrackerSettings.h 2013-08-06 11:28:29 UTC (rev 2582) +++ trunk/OpenMPT/mptrack/TrackerSettings.h 2013-08-06 11:45:45 UTC (rev 2583) @@ -220,6 +220,7 @@ COLORREF rgbCustomColors[MAX_MODCOLORS]; MixerSettings m_MixerSettings; + SampleFormat m_SampleFormat; CResamplerSettings m_ResamplerSettings; #ifndef NO_REVERB CReverbSettings m_ReverbSettings; Modified: trunk/OpenMPT/sounddev/SoundDevice.cpp =================================================================== --- trunk/OpenMPT/sounddev/SoundDevice.cpp 2013-08-06 11:28:29 UTC (rev 2582) +++ trunk/OpenMPT/sounddev/SoundDevice.cpp 2013-08-06 11:45:45 UTC (rev 2583) @@ -32,12 +32,9 @@ //-------------------------- { m_Source = nullptr; - m_LatencyMS = SNDDEV_DEFAULT_LATENCY_MS; - m_UpdateIntervalMS = SNDDEV_DEFAULT_UPDATEINTERVAL_MS; - m_fulCfgOptions = 0; - m_RealLatencyMS = static_cast<float>(m_LatencyMS); - m_RealUpdateIntervalMS = static_cast<float>(m_UpdateIntervalMS); + m_RealLatencyMS = static_cast<float>(m_Settings.LatencyMS); + m_RealUpdateIntervalMS = static_cast<float>(m_Settings.UpdateIntervalMS); m_IsPlaying = false; } @@ -46,25 +43,65 @@ ISoundDevice::~ISoundDevice() //--------------------------- { + return; } -void ISoundDevice::Configure(HWND hwnd, UINT LatencyMS, UINT UpdateIntervalMS, DWORD fdwCfgOptions) -//------------------------------------------------------------------------------------------------- +bool ISoundDevice::FillWaveFormatExtensible(WAVEFORMATEXTENSIBLE &WaveFormat) +//--------------------------------------------------------------------------- { - if(LatencyMS < SNDDEV_MINLATENCY_MS) LatencyMS = SNDDEV_MINLATENCY_MS; - if(LatencyMS > SNDDEV_MAXLATENCY_MS) LatencyMS = SNDDEV_MAXLATENCY_MS; - if(UpdateIntervalMS < SNDDEV_MINUPDATEINTERVAL_MS) UpdateIntervalMS = SNDDEV_MINUPDATEINTERVAL_MS; - if(UpdateIntervalMS > SNDDEV_MAXUPDATEINTERVAL_MS) UpdateIntervalMS = SNDDEV_MAXUPDATEINTERVAL_MS; - m_LatencyMS = LatencyMS; - m_UpdateIntervalMS = UpdateIntervalMS; - m_fulCfgOptions = fdwCfgOptions; - m_hWnd = hwnd; - m_RealLatencyMS = static_cast<float>(m_LatencyMS); - m_RealUpdateIntervalMS = static_cast<float>(m_UpdateIntervalMS); + MemsetZero(WaveFormat); + UINT bytespersample = (m_Settings.BitsPerSample/8) * m_Settings.Channels; + if(m_Settings.FloatingPoint && m_Settings.BitsPerSample != 32) return false; + WaveFormat.Format.wFormatTag = m_Settings.FloatingPoint ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM; + WaveFormat.Format.nChannels = (WORD)m_Settings.Channels; + WaveFormat.Format.nSamplesPerSec = m_Settings.Samplerate; + WaveFormat.Format.nAvgBytesPerSec = m_Settings.Samplerate * bytespersample; + WaveFormat.Format.nBlockAlign = (WORD)bytespersample; + WaveFormat.Format.wBitsPerSample = (WORD)m_Settings.BitsPerSample; + WaveFormat.Format.cbSize = 0; + if((WaveFormat.Format.wBitsPerSample > 16 && !m_Settings.FloatingPoint) || (WaveFormat.Format.nChannels > 2)) + { + WaveFormat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + WaveFormat.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); + WaveFormat.Samples.wValidBitsPerSample = WaveFormat.Format.wBitsPerSample; + switch(WaveFormat.Format.nChannels) + { + case 1: WaveFormat.dwChannelMask = SPEAKER_FRONT_CENTER; break; + case 2: WaveFormat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; + case 3: WaveFormat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_CENTER; break; + case 4: WaveFormat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; + default: WaveFormat.dwChannelMask = 0; return false; break; + } + const GUID guid_MEDIASUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x0, 0xAA, 0x0, 0x38, 0x9B, 0x71}; + const GUID guid_MEDIASUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71}; + WaveFormat.SubFormat = m_Settings.FloatingPoint ? guid_MEDIASUBTYPE_IEEE_FLOAT : guid_MEDIASUBTYPE_PCM; + } + return true; } +bool ISoundDevice::Open(UINT device, const SoundDeviceSettings &settings) +//----------------------------------------------------------------------- +{ + m_Settings = settings; + if(m_Settings.LatencyMS < SNDDEV_MINLATENCY_MS) m_Settings.LatencyMS = SNDDEV_MINLATENCY_MS; + if(m_Settings.LatencyMS > SNDDEV_MAXLATENCY_MS) m_Settings.LatencyMS = SNDDEV_MAXLATENCY_MS; + if(m_Settings.UpdateIntervalMS < SNDDEV_MINUPDATEINTERVAL_MS) m_Settings.UpdateIntervalMS = SNDDEV_MINUPDATEINTERVAL_MS; + if(m_Settings.UpdateIntervalMS > SNDDEV_MAXUPDATEINTERVAL_MS) m_Settings.UpdateIntervalMS = SNDDEV_MAXUPDATEINTERVAL_MS; + m_RealLatencyMS = static_cast<float>(m_Settings.LatencyMS); + m_RealUpdateIntervalMS = static_cast<float>(m_Settings.UpdateIntervalMS); + return InternalOpen(device); +} + + +bool ISoundDevice::Close() +//------------------------ +{ + return InternalClose(); +} + + void ISoundDevice::SourceFillAudioBufferLocked() //---------------------------------------------- { @@ -430,7 +467,7 @@ if(!terminate) { - CPriorityBooster priorityBooster(*this, (m_SoundDevice.m_fulCfgOptions & SNDDEV_OPTIONS_BOOSTTHREADPRIORITY)?true:false); + CPriorityBooster priorityBooster(*this, (m_SoundDevice.m_Settings.fulCfgOptions & SNDDEV_OPTIONS_BOOSTTHREADPRIORITY)?true:false); CPeriodicWaker periodicWaker(*this, 0.001 * m_SoundDevice.GetRealUpdateIntervalMS()); m_SoundDevice.StartFromSoundThread(); @@ -546,9 +583,13 @@ } -BOOL CWaveDevice::Open(UINT nDevice, LPWAVEFORMATEX pwfx) -//------------------------------------------------------- +bool CWaveDevice::InternalOpen(UINT nDevice) +//------------------------------------------ { + WAVEFORMATEXTENSIBLE wfext; + if(!FillWaveFormatExtensible(wfext)) return false; + WAVEFORMATEX *pwfx = &wfext.Format; + LONG nWaveDev; if (m_hWaveOut) Close(); @@ -557,15 +598,15 @@ { sndPlaySound(NULL, 0); LONG err = waveOutOpen(&m_hWaveOut, nWaveDev, pwfx, (DWORD_PTR)WaveOutCallBack, (DWORD_PTR)this, CALLBACK_FUNCTION); - if (err) return FALSE; + if (err) return false; } m_nBytesPerSec = pwfx->nAvgBytesPerSec; m_BytesPerSample = (pwfx->wBitsPerSample/8) * pwfx->nChannels; - m_nWaveBufferSize = (m_UpdateIntervalMS * pwfx->nAvgBytesPerSec) / 1000; + m_nWaveBufferSize = (m_Settings.UpdateIntervalMS * pwfx->nAvgBytesPerSec) / 1000; m_nWaveBufferSize = (m_nWaveBufferSize + 7) & ~7; if (m_nWaveBufferSize < WAVEOUT_MINBUFFERSIZE) m_nWaveBufferSize = WAVEOUT_MINBUFFERSIZE; if (m_nWaveBufferSize > WAVEOUT_MAXBUFFERSIZE) m_nWaveBufferSize = WAVEOUT_MAXBUFFERSIZE; - ULONG NumBuffers = m_LatencyMS * pwfx->nAvgBytesPerSec / ( m_nWaveBufferSize * 1000 ); + ULONG NumBuffers = m_Settings.LatencyMS * pwfx->nAvgBytesPerSec / ( m_nWaveBufferSize * 1000 ); NumBuffers = CLAMP(NumBuffers, 3, WAVEOUT_MAXBUFFERS); m_nPreparedHeaders = 0; m_WaveBuffers.resize(NumBuffers); @@ -586,17 +627,17 @@ if (!m_nPreparedHeaders) { Close(); - return FALSE; + return false; } m_RealLatencyMS = m_nWaveBufferSize * m_nPreparedHeaders * 1000.0f / m_nBytesPerSec; m_RealUpdateIntervalMS = m_nWaveBufferSize * 1000.0f / m_nBytesPerSec; m_nBuffersPending = 0; m_nWriteBuffer = 0; - return TRUE; + return true; } -BOOL CWaveDevice::Close() -//----------------------- +bool CWaveDevice::InternalClose() +//------------------------------- { Reset(); if (m_hWaveOut) @@ -612,7 +653,7 @@ m_hWaveOut = NULL; Sleep(1); // Linux WINE-friendly } - return TRUE; + return true; } @@ -815,28 +856,32 @@ } -BOOL CDSoundDevice::Open(UINT nDevice, LPWAVEFORMATEX pwfx) -//--------------------------------------------------------- +bool CDSoundDevice::InternalOpen(UINT nDevice) +//-------------------------------------------- { + WAVEFORMATEXTENSIBLE wfext; + if(!FillWaveFormatExtensible(wfext)) return false; + WAVEFORMATEX *pwfx = &wfext.Format; + DSBUFFERDESC dsbd; DSBCAPS dsc; - UINT nPriorityLevel = (m_fulCfgOptions & SNDDEV_OPTIONS_EXCLUSIVE) ? DSSCL_WRITEPRIMARY : DSSCL_PRIORITY; + UINT nPriorityLevel = (m_Settings.fulCfgOptions & SNDDEV_OPTIONS_EXCLUSIVE) ? DSSCL_WRITEPRIMARY : DSSCL_PRIORITY; - if (m_piDS) return TRUE; - if (!gpDSoundEnumerate) return FALSE; + if (m_piDS) return true; + if (!gpDSoundEnumerate) return false; if (!gbDSoundEnumerated) gpDSoundEnumerate((LPDSENUMCALLBACK)DSEnumCallback, NULL); - if ((nDevice >= gnDSoundDevices) || (!gpDSoundCreate)) return FALSE; - if (gpDSoundCreate(glpDSoundGUID[nDevice], &m_piDS, NULL) != DS_OK) return FALSE; - if (!m_piDS) return FALSE; - m_piDS->SetCooperativeLevel(m_hWnd, nPriorityLevel); + if ((nDevice >= gnDSoundDevices) || (!gpDSoundCreate)) return false; + if (gpDSoundCreate(glpDSoundGUID[nDevice], &m_piDS, NULL) != DS_OK) return false; + if (!m_piDS) return false; + m_piDS->SetCooperativeLevel(m_Settings.hWnd, nPriorityLevel); m_bMixRunning = FALSE; - m_nDSoundBufferSize = (m_LatencyMS * pwfx->nAvgBytesPerSec) / 1000; + m_nDSoundBufferSize = (m_Settings.LatencyMS * pwfx->nAvgBytesPerSec) / 1000; m_nDSoundBufferSize = (m_nDSoundBufferSize + 15) & ~15; if(m_nDSoundBufferSize < DSOUND_MINBUFFERSIZE) m_nDSoundBufferSize = DSOUND_MINBUFFERSIZE; if(m_nDSoundBufferSize > DSOUND_MAXBUFFERSIZE) m_nDSoundBufferSize = DSOUND_MAXBUFFERSIZE; m_nBytesPerSec = pwfx->nAvgBytesPerSec; m_BytesPerSample = (pwfx->wBitsPerSample/8) * pwfx->nChannels; - if(!(m_fulCfgOptions & SNDDEV_OPTIONS_EXCLUSIVE)) + if(!(m_Settings.fulCfgOptions & SNDDEV_OPTIONS_EXCLUSIVE)) { // Set the format of the primary buffer dsbd.dwSize = sizeof(dsbd); @@ -847,7 +892,7 @@ if (m_piDS->CreateSoundBuffer(&dsbd, &m_pPrimary, NULL) != DS_OK) { Close(); - return FALSE; + return false; } m_pPrimary->SetFormat(pwfx); /////////////////////////////////////////////////// @@ -860,7 +905,7 @@ if (m_piDS->CreateSoundBuffer(&dsbd, &m_pMixBuffer, NULL) != DS_OK) { Close(); - return FALSE; + return false; } } else { @@ -872,18 +917,18 @@ if (m_piDS->CreateSoundBuffer(&dsbd, &m_pPrimary, NULL) != DS_OK) { Close(); - return FALSE; + return false; } if (m_pPrimary->SetFormat(pwfx) != DS_OK) { Close(); - return FALSE; + return false; } dsc.dwSize = sizeof(dsc); if (m_pPrimary->GetCaps(&dsc) != DS_OK) { Close(); - return FALSE; + return false; } m_nDSoundBufferSize = dsc.dwBufferBytes; m_pMixBuffer = m_pPrimary; @@ -904,14 +949,14 @@ if (dwStat & DSBSTATUS_BUFFERLOST) m_pMixBuffer->Restore(); } m_RealLatencyMS = m_nDSoundBufferSize * 1000.0f / m_nBytesPerSec; - m_RealUpdateIntervalMS = CLAMP(static_cast<float>(m_UpdateIntervalMS), 1.0f, m_nDSoundBufferSize * 1000.0f / ( 2.0f * m_nBytesPerSec ) ); + m_RealUpdateIntervalMS = CLAMP(static_cast<float>(m_Settings.UpdateIntervalMS), 1.0f, m_nDSoundBufferSize * 1000.0f / ( 2.0f * m_nBytesPerSec ) ); m_dwWritePos = 0xFFFFFFFF; - return TRUE; + return true; } -BOOL CDSoundDevice::Close() -//------------------------- +bool CDSoundDevice::InternalClose() +//--------------------------------- { if (m_pMixBuffer) { @@ -929,7 +974,7 @@ m_piDS = NULL; } m_bMixRunning = FALSE; - return TRUE; + return true; } @@ -1173,7 +1218,6 @@ m_Callbacks.bufferSwitchTimeInfo = BufferSwitchTimeInfo; m_nBitsPerSample = 0; // Unknown m_nCurrentDevice = (ULONG)-1; - m_nSamplesPerSec = 0; m_bMixRunning = FALSE; InterlockedExchange(&m_RenderSilence, 0); InterlockedExchange(&m_RenderingSilence, 0); @@ -1188,14 +1232,16 @@ } -BOOL CASIODevice::Open(UINT nDevice, LPWAVEFORMATEX pwfx) -//------------------------------------------------------- +bool CASIODevice::InternalOpen(UINT nDevice) +//------------------------------------------ { - BOOL bOk = FALSE; + bool bOk = false; + if(m_Settings.FloatingPoint) return false; // for now + if (IsOpen()) Close(); if (!gbAsioEnumerated) EnumerateDevices(nDevice, NULL, 0); - if (nDevice >= gnNumAsioDrivers) return FALSE; + if (nDevice >= gnNumAsioDrivers) return false; if (nDevice != m_nCurrentDevice) { m_nCurrentDevice = nDevice; @@ -1203,7 +1249,7 @@ } #ifdef ASIO_LOG Log("CASIODevice::Open(%d:\"%s\"): %d-bit, %d channels, %dHz\n", - nDevice, gAsioDrivers[nDevice].name, pwfx->wBitsPerSample, pwfx->nChannels, pwfx->nSamplesPerSec); + nDevice, gAsioDrivers[nDevice].name, m_Settings.BitsPerSample, m_Settings.Channels, m_Settings.Samplerate); #endif OpenDevice(nDevice); @@ -1212,23 +1258,23 @@ long nInputChannels = 0, nOutputChannels = 0; long minSize = 0, maxSize = 0, preferredSize = 0, granularity = 0; - if ((pwfx->nChannels > ASIO_MAX_CHANNELS) - || ((pwfx->wBitsPerSample != 16) && (pwfx->wBitsPerSample != 32))) goto abort; - m_nChannels = pwfx->nChannels; + if ((m_Settings.Channels > ASIO_MAX_CHANNELS) + || ((m_Settings.BitsPerSample != 16) && (m_Settings.BitsPerSample != 32))) goto abort; + m_nChannels = m_Settings.Channels; m_pAsioDrv->getChannels(&nInputChannels, &nOutputChannels); #ifdef ASIO_LOG Log(" getChannels: %d inputs, %d outputs\n", nInputChannels, nOutputChannels); #endif - if (pwfx->nChannels > nOutputChannels) goto abort; - if (m_pAsioDrv->setSampleRate(pwfx->nSamplesPerSec) != ASE_OK) + if (m_Settings.Channels > nOutputChannels) goto abort; + if (m_pAsioDrv->setSampleRate(m_Settings.Samplerate) != ASE_OK) { #ifdef ASIO_LOG - Log(" setSampleRate(%d) failed (sample rate not supported)!\n", pwfx->nSamplesPerSec); + Log(" setSampleRate(%d) failed (sample rate not supported)!\n", m_Settings.Samplerate); #endif goto abort; } - m_nBitsPerSample = pwfx->wBitsPerSample; - for (UINT ich=0; ich<pwfx->nChannels; ich++) + m_nBitsPerSample = m_Settings.BitsPerSample; + for (UINT ich=0; ich<m_Settings.Channels; ich++) { m_ChannelInfo[ich].channel = ich; m_ChannelInfo[ich].isInput = ASIOFalse; @@ -1269,7 +1315,7 @@ Log(" getBufferSize(): minSize=%d maxSize=%d preferredSize=%d granularity=%d\n", minSize, maxSize, preferredSize, granularity); #endif - m_nAsioBufferLen = ((m_LatencyMS * pwfx->nSamplesPerSec) / 2000); + m_nAsioBufferLen = ((m_Settings.LatencyMS * m_Settings.Samplerate) / 2000); if (m_nAsioBufferLen < (UINT)minSize) m_nAsioBufferLen = minSize; else if (m_nAsioBufferLen > (UINT)maxSize) m_nAsioBufferLen = maxSize; else if (granularity < 0) @@ -1297,9 +1343,8 @@ } m_nAsioBufferLen = n; } - m_nSamplesPerSec = pwfx->nSamplesPerSec; - m_RealLatencyMS = m_nAsioBufferLen * 2 * 1000.0f / m_nSamplesPerSec; - m_RealUpdateIntervalMS = m_nAsioBufferLen * 1000.0f / m_nSamplesPerSec; + m_RealLatencyMS = m_nAsioBufferLen * 2 * 1000.0f / m_Settings.Samplerate; + m_RealUpdateIntervalMS = m_nAsioBufferLen * 1000.0f / m_Settings.Samplerate; #ifdef ASIO_LOG Log(" Using buffersize=%d samples\n", m_nAsioBufferLen); #endif @@ -1317,7 +1362,7 @@ } } m_bPostOutput = (m_pAsioDrv->outputReady() == ASE_OK) ? TRUE : FALSE; - bOk = TRUE; + bOk = true; } #ifdef ASIO_LOG else Log(" createBuffers failed!\n"); @@ -1417,8 +1462,8 @@ } -BOOL CASIODevice::Close() -//----------------------- +bool CASIODevice::InternalClose() +//------------------------------- { if (IsOpen()) { @@ -1450,7 +1495,7 @@ { gpCurrentAsio = NULL; } - return TRUE; + return true; } @@ -1489,7 +1534,7 @@ CLSID clsid = gAsioDrivers[nDevice].clsid; if (CoCreateInstance(clsid,0,CLSCTX_INPROC_SERVER, clsid, (void **)&m_pAsioDrv) == S_OK) { - m_pAsioDrv->init((void *)m_hWnd); + m_pAsioDrv->init((void *)m_Settings.hWnd); } else { #ifdef ASIO_LOG @@ -2039,8 +2084,8 @@ } -BOOL CPortaudioDevice::Open(UINT nDevice, LPWAVEFORMATEX pwfx) -//------------------------------------------------------------ +bool CPortaudioDevice::InternalOpen(UINT nDevice) +//----------------------------------------------- { MemsetZero(m_StreamParameters); m_Stream = 0; @@ -2048,18 +2093,25 @@ m_CurrentFrameCount = 0; m_StreamParameters.device = HostApiOutputIndexToGlobalDeviceIndex(nDevice, m_HostApi); if(m_StreamParameters.device == -1) return false; - m_StreamParameters.channelCount = pwfx->nChannels; - switch(pwfx->wBitsPerSample) + m_StreamParameters.channelCount = m_Settings.Channels; + if(m_Settings.FloatingPoint) { + if(m_Settings.BitsPerSample != 32) return false; + m_StreamParameters.sampleFormat = paFloat32; + } else + { + switch... [truncated message content] |