From: <man...@us...> - 2015-05-21 12:05:46
|
Revision: 5133 http://sourceforge.net/p/modplug/code/5133 Author: manxorist Date: 2015-05-21 12:05:38 +0000 (Thu, 21 May 2015) Log Message: ----------- [Imp] sounddev: Add support for recording channels on ASIO and PortAudio devices. This feature is not yet exposed in any way beyond the sound device implementation itself. Modified Paths: -------------- trunk/OpenMPT/mptrack/MainFrm.cpp trunk/OpenMPT/mptrack/Mainfrm.h trunk/OpenMPT/sounddev/SoundDevice.cpp trunk/OpenMPT/sounddev/SoundDevice.h trunk/OpenMPT/sounddev/SoundDeviceASIO.cpp trunk/OpenMPT/sounddev/SoundDeviceASIO.h trunk/OpenMPT/sounddev/SoundDeviceDirectSound.cpp trunk/OpenMPT/sounddev/SoundDevicePortAudio.cpp trunk/OpenMPT/sounddev/SoundDevicePortAudio.h trunk/OpenMPT/sounddev/SoundDeviceWaveout.cpp Modified: trunk/OpenMPT/mptrack/MainFrm.cpp =================================================================== --- trunk/OpenMPT/mptrack/MainFrm.cpp 2015-05-21 11:48:12 UTC (rev 5132) +++ trunk/OpenMPT/mptrack/MainFrm.cpp 2015-05-21 12:05:38 UTC (rev 5133) @@ -702,10 +702,11 @@ }; -void CMainFrame::SoundSourceRead(const SoundDevice::Settings &settings, const SoundDevice::Flags &flags, const SoundDevice::BufferAttributes &bufferAttributes, SoundDevice::TimeInfo timeInfo, std::size_t numFrames, void *buffer) -//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void CMainFrame::SoundSourceRead(const SoundDevice::Settings &settings, const SoundDevice::Flags &flags, const SoundDevice::BufferAttributes &bufferAttributes, SoundDevice::TimeInfo timeInfo, std::size_t numFrames, void *buffer, const void *inputBuffer) +//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- { MPT_TRACE(); + MPT_UNREFERENCED_PARAMETER(inputBuffer); MPT_ASSERT(InAudioThread()); OPENMPT_PROFILE_FUNCTION(Profiler::Audio); TimingInfo timingInfo; Modified: trunk/OpenMPT/mptrack/Mainfrm.h =================================================================== --- trunk/OpenMPT/mptrack/Mainfrm.h 2015-05-21 11:48:12 UTC (rev 5132) +++ trunk/OpenMPT/mptrack/Mainfrm.h 2015-05-21 12:05:38 UTC (rev 5133) @@ -410,7 +410,7 @@ void SoundSourcePostStopCallback(); bool SoundSourceIsLockedByCurrentThread() const; void SoundSourceLock(); - void SoundSourceRead(const SoundDevice::Settings &settings, const SoundDevice::Flags &flags, const SoundDevice::BufferAttributes &bufferAttributes, SoundDevice::TimeInfo timeInfo, std::size_t numFrames, void *buffer); + void SoundSourceRead(const SoundDevice::Settings &settings, const SoundDevice::Flags &flags, const SoundDevice::BufferAttributes &bufferAttributes, SoundDevice::TimeInfo timeInfo, std::size_t numFrames, void *buffer, const void *inputBuffer); void SoundSourceDone(const SoundDevice::Settings &settings, const SoundDevice::Flags &flags, const SoundDevice::BufferAttributes &bufferAttributes, SoundDevice::TimeInfo timeInfo); void SoundSourceUnlock(); Modified: trunk/OpenMPT/sounddev/SoundDevice.cpp =================================================================== --- trunk/OpenMPT/sounddev/SoundDevice.cpp 2015-05-21 11:48:12 UTC (rev 5132) +++ trunk/OpenMPT/sounddev/SoundDevice.cpp 2015-05-21 12:05:38 UTC (rev 5133) @@ -277,15 +277,15 @@ } -void Base::SourceAudioRead(void *buffer, std::size_t numFrames) -//------------------------------------------------------------- +void Base::SourceAudioRead(void *buffer, const void *inputBuffer, std::size_t numFrames) +//-------------------------------------------------------------------------------------- { MPT_TRACE(); if(numFrames <= 0) { return; } - m_Source->SoundSourceRead(m_Settings, m_Flags, GetEffectiveBufferAttributes(), m_TimeInfo, numFrames, buffer); + m_Source->SoundSourceRead(m_Settings, m_Flags, GetEffectiveBufferAttributes(), m_TimeInfo, numFrames, buffer, inputBuffer); } Modified: trunk/OpenMPT/sounddev/SoundDevice.h =================================================================== --- trunk/OpenMPT/sounddev/SoundDevice.h 2015-05-21 11:48:12 UTC (rev 5132) +++ trunk/OpenMPT/sounddev/SoundDevice.h 2015-05-21 12:05:38 UTC (rev 5133) @@ -81,7 +81,7 @@ virtual void SoundSourcePostStopCallback() = 0; virtual bool SoundSourceIsLockedByCurrentThread() const = 0; virtual void SoundSourceLock() = 0; - virtual void SoundSourceRead(const SoundDevice::Settings &settings, const SoundDevice::Flags &flags, const SoundDevice::BufferAttributes &bufferAttributes, SoundDevice::TimeInfo timeInfo, std::size_t numFrames, void *buffer) = 0; + virtual void SoundSourceRead(const SoundDevice::Settings &settings, const SoundDevice::Flags &flags, const SoundDevice::BufferAttributes &bufferAttributes, SoundDevice::TimeInfo timeInfo, std::size_t numFrames, void *buffer, const void *inputBuffer) = 0; virtual void SoundSourceDone(const SoundDevice::Settings &settings, const SoundDevice::Flags &flags, const SoundDevice::BufferAttributes &bufferAttributes, SoundDevice::TimeInfo timeInfo) = 0; virtual void SoundSourceUnlock() = 0; public: @@ -266,23 +266,27 @@ double UpdateInterval; // seconds uint32 Samplerate; SoundDevice::ChannelMapping Channels; + uint8 InputChannels; SampleFormat sampleFormat; bool ExclusiveMode; // Use hardware buffers directly bool BoostThreadPriority; // Boost thread priority for glitch-free audio rendering bool KeepDeviceRunning; bool UseHardwareTiming; int DitherType; + uint32 InputSourceID; Settings() : Latency(0.1) , UpdateInterval(0.005) , Samplerate(48000) , Channels(2) + , InputChannels(0) , sampleFormat(SampleFormatFloat32) , ExclusiveMode(false) , BoostThreadPriority(true) , KeepDeviceRunning(true) , UseHardwareTiming(false) , DitherType(1) + , InputSourceID(0) { return; } @@ -293,12 +297,14 @@ && Util::Round<int64>(UpdateInterval * 1000000000.0) == Util::Round<int64>(cmp.UpdateInterval * 1000000000.0) // compare in nanoseconds && Samplerate == cmp.Samplerate && Channels == cmp.Channels + && InputChannels == cmp.InputChannels && sampleFormat == cmp.sampleFormat && ExclusiveMode == cmp.ExclusiveMode && BoostThreadPriority == cmp.BoostThreadPriority && KeepDeviceRunning == cmp.KeepDeviceRunning && UseHardwareTiming == cmp.UseHardwareTiming && DitherType == cmp.DitherType + && InputSourceID == cmp.InputSourceID ; } bool operator != (const SoundDevice::Settings &cmp) const @@ -313,6 +319,10 @@ { return Samplerate * GetBytesPerFrame(); } + std::size_t GetTotalChannels() const + { + return InputChannels + Channels; + } }; @@ -345,6 +355,8 @@ bool CanKeepDeviceRunning; bool CanUseHardwareTiming; bool CanChannelMapping; + bool CanInput; + bool HasNamedInputSources; bool CanDriverPanel; bool HasInternalDither; mpt::ustring ExclusiveModeDescription; @@ -362,6 +374,8 @@ , CanKeepDeviceRunning(false) , CanUseHardwareTiming(false) , CanChannelMapping(false) + , CanInput(false) + , HasNamedInputSources(false) , CanDriverPanel(false) , HasInternalDither(false) , ExclusiveModeDescription(MPT_USTRING("Use device exclusively")) @@ -381,6 +395,7 @@ std::vector<uint32> supportedSampleRates; std::vector<uint32> supportedExclusiveSampleRates; std::vector<mpt::ustring> channelNames; + std::vector<std::pair<uint32, mpt::ustring> > inputSourceNames; DynamicCaps() : currentSampleRate(0) { @@ -539,7 +554,7 @@ bool SourceIsLockedByCurrentThread() const; void SourceFillAudioBufferLocked(); void SourceAudioPreRead(std::size_t numFrames, std::size_t framesLatency); - void SourceAudioRead(void *buffer, std::size_t numFrames); + void SourceAudioRead(void *buffer, const void *inputBuffer, std::size_t numFrames); void SourceAudioDone(); void RequestClose() { m_RequestFlags.fetch_or(RequestFlagClose); } Modified: trunk/OpenMPT/sounddev/SoundDeviceASIO.cpp =================================================================== --- trunk/OpenMPT/sounddev/SoundDeviceASIO.cpp 2015-05-21 11:48:12 UTC (rev 5132) +++ trunk/OpenMPT/sounddev/SoundDeviceASIO.cpp 2015-05-21 12:05:38 UTC (rev 5133) @@ -206,6 +206,10 @@ m_SampleBufferInt16.clear(); m_SampleBufferInt24.clear(); m_SampleBufferInt32.clear(); + m_SampleInputBufferFloat.clear(); + m_SampleInputBufferInt16.clear(); + m_SampleInputBufferInt24.clear(); + m_SampleInputBufferInt32.clear(); m_CanOutputReady = false; m_DeviceRunning = false; @@ -253,14 +257,17 @@ InitMembers(); - Log(mpt::String::Print("ASIO: Open('%1'): %2-bit, %3 channels, %4Hz, hw-timing=%5" + Log(mpt::String::Print("ASIO: Open('%1'): %2-bit, (%3,%4) channels, %5Hz, hw-timing=%6" , mpt::ToLocale(GetDeviceInternalID()) , m_Settings.sampleFormat.GetBitsPerSample() + , m_Settings.InputChannels , m_Settings.Channels , m_Settings.Samplerate , m_Settings.UseHardwareTiming )); + SoundDevice::ChannelMapping inputChannelMapping = SoundDevice::ChannelMapping::BaseChannel(m_Settings.InputChannels, m_Settings.InputSourceID); + try { @@ -288,6 +295,14 @@ { throw ASIOException("Channel mapping requires more channels than available."); } + if(m_Settings.InputChannels > inputChannels) + { + throw ASIOException("Not enough input channels."); + } + if(inputChannelMapping.GetRequiredDeviceChannels() > inputChannels) + { + throw ASIOException("Channel mapping requires more channels than available."); + } Log(mpt::String::Print("ASIO: setSampleRate(sampleRate=%1)", m_Settings.Samplerate)); asioCall(setSampleRate(m_Settings.Samplerate)); @@ -360,12 +375,19 @@ MPT_ASSERT(false); } - m_BufferInfo.resize(m_Settings.Channels); - for(int channel = 0; channel < m_Settings.Channels; ++channel) + m_BufferInfo.resize(m_Settings.GetTotalChannels()); + for(std::size_t channel = 0; channel < m_Settings.GetTotalChannels(); ++channel) { MemsetZero(m_BufferInfo[channel]); - m_BufferInfo[channel].isInput = ASIOFalse; - m_BufferInfo[channel].channelNum = m_Settings.Channels.ToDevice(channel); + if(channel < m_Settings.InputChannels) + { + m_BufferInfo[channel].isInput = ASIOTrue; + m_BufferInfo[channel].channelNum = inputChannelMapping.ToDevice(channel); + } else + { + m_BufferInfo[channel].isInput = ASIOFalse; + m_BufferInfo[channel].channelNum = m_Settings.Channels.ToDevice(channel - m_Settings.InputChannels); + } } m_Callbacks.bufferSwitch = CallbackBufferSwitch; m_Callbacks.sampleRateDidChange = CallbackSampleRateDidChange; @@ -374,20 +396,27 @@ MPT_ASSERT_ALWAYS(g_CallbacksInstance == nullptr); g_CallbacksInstance = this; Log(mpt::String::Print("ASIO: createBuffers(numChannels=%1, bufferSize=%2)", m_Settings.Channels, m_nAsioBufferLen)); - asioCall(createBuffers(&m_BufferInfo[0], m_Settings.Channels, m_nAsioBufferLen, &m_Callbacks)); + asioCall(createBuffers(&m_BufferInfo[0], m_Settings.GetTotalChannels(), m_nAsioBufferLen, &m_Callbacks)); m_BuffersCreated = true; - m_ChannelInfo.resize(m_Settings.Channels); - for(int channel = 0; channel < m_Settings.Channels; ++channel) + m_ChannelInfo.resize(m_Settings.GetTotalChannels()); + for(std::size_t channel = 0; channel < m_Settings.GetTotalChannels(); ++channel) { MemsetZero(m_ChannelInfo[channel]); - m_ChannelInfo[channel].isInput = ASIOFalse; - m_ChannelInfo[channel].channel = m_Settings.Channels.ToDevice(channel); + if(channel < m_Settings.InputChannels) + { + m_ChannelInfo[channel].isInput = ASIOTrue; + m_ChannelInfo[channel].channel = inputChannelMapping.ToDevice(channel); + } else + { + m_ChannelInfo[channel].isInput = ASIOFalse; + m_ChannelInfo[channel].channel = m_Settings.Channels.ToDevice(channel - m_Settings.InputChannels); + } asioCall(getChannelInfo(&m_ChannelInfo[channel])); MPT_ASSERT(m_ChannelInfo[channel].isActive); mpt::String::SetNullTerminator(m_ChannelInfo[channel].name); Log(mpt::String::Print("ASIO: getChannelInfo(isInput=%1 channel=%2) => isActive=%3 channelGroup=%4 type=%5 name='%6'" - , ASIOFalse + , (channel < m_Settings.InputChannels) ? ASIOTrue : ASIOFalse , m_Settings.Channels.ToDevice(channel) , m_ChannelInfo[channel].isActive , m_ChannelInfo[channel].channelGroup @@ -399,7 +428,7 @@ bool allChannelsAreFloat = true; bool allChannelsAreInt16 = true; bool allChannelsAreInt24 = true; - for(int channel = 0; channel < m_Settings.Channels; ++channel) + for(std::size_t channel = 0; channel < m_Settings.GetTotalChannels(); ++channel) { if(!IsSampleTypeFloat(m_ChannelInfo[channel].type)) { @@ -418,21 +447,25 @@ { m_Settings.sampleFormat = SampleFormatFloat32; m_SampleBufferFloat.resize(m_nAsioBufferLen * m_Settings.Channels); + m_SampleInputBufferFloat.resize(m_nAsioBufferLen * m_Settings.InputChannels); } else if(allChannelsAreInt16) { m_Settings.sampleFormat = SampleFormatInt16; m_SampleBufferInt16.resize(m_nAsioBufferLen * m_Settings.Channels); + m_SampleInputBufferInt16.resize(m_nAsioBufferLen * m_Settings.InputChannels); } else if(allChannelsAreInt24) { m_Settings.sampleFormat = SampleFormatInt24; m_SampleBufferInt24.resize(m_nAsioBufferLen * m_Settings.Channels); + m_SampleInputBufferInt24.resize(m_nAsioBufferLen * m_Settings.InputChannels); } else { m_Settings.sampleFormat = SampleFormatInt32; m_SampleBufferInt32.resize(m_nAsioBufferLen * m_Settings.Channels); + m_SampleInputBufferInt32.resize(m_nAsioBufferLen * m_Settings.InputChannels); } - for(int channel = 0; channel < m_Settings.Channels; ++channel) + for(std::size_t channel = 0; channel < m_Settings.GetTotalChannels(); ++channel) { if(m_BufferInfo[channel].buffers[0]) { @@ -631,6 +664,10 @@ m_SampleBufferInt16.clear(); m_SampleBufferInt24.clear(); m_SampleBufferInt32.clear(); + m_SampleInputBufferFloat.clear(); + m_SampleInputBufferInt16.clear(); + m_SampleInputBufferInt24.clear(); + m_SampleInputBufferInt32.clear(); m_ChannelInfo.clear(); if(m_BuffersCreated) { @@ -857,22 +894,155 @@ { MPT_TRACE(); const bool rendersilence = !useSource; - const int channels = m_Settings.Channels; const std::size_t countChunk = m_nAsioBufferLen; + const std::size_t inputChannels = m_Settings.InputChannels; + const std::size_t outputChannels = m_Settings.Channels; + for(std::size_t inputChannel = 0; inputChannel < inputChannels; ++inputChannel) + { + std::size_t channel = inputChannel; + void *src = m_BufferInfo[channel].buffers[static_cast<unsigned long>(m_BufferIndex) & 1]; + if(IsSampleTypeBigEndian(m_ChannelInfo[channel].type)) + { + if(src) + { + SwapEndian(reinterpret_cast<uint8*>(src), countChunk, GetSampleSize(m_ChannelInfo[channel].type)); + } + } + if(m_Settings.sampleFormat == SampleFormatFloat32) + { + float *const dstFloat = &m_SampleInputBufferFloat[0]; + if(!src) + { + // Skip if we did get no buffer for this channel, + // Fill with zeroes. + std::fill(dstFloat, dstFloat + countChunk, 0.0f); + continue; + } + switch(m_ChannelInfo[channel].type) + { + case ASIOSTFloat32MSB: + case ASIOSTFloat32LSB: + CopyChannelToInterleaved<SC::Convert<float, float> >(dstFloat, reinterpret_cast<float*>(src), inputChannels, countChunk, inputChannel); + break; + case ASIOSTFloat64MSB: + case ASIOSTFloat64LSB: + CopyChannelToInterleaved<SC::Convert<float, double> >(dstFloat, reinterpret_cast<double*>(src), inputChannels, countChunk, inputChannel); + break; + default: + ASSERT(false); + break; + } + } else if(m_Settings.sampleFormat == SampleFormatInt16) + { + int16 *const dstInt16 = &m_SampleInputBufferInt16[0]; + if(!src) + { + // Skip if we did get no buffer for this channel, + // Fill with zeroes. + std::fill(dstInt16, dstInt16 + countChunk, 0); + continue; + } + switch(m_ChannelInfo[channel].type) + { + case ASIOSTInt16MSB: + case ASIOSTInt16LSB: + CopyChannelToInterleaved<SC::Convert<int16, int16> >(dstInt16, reinterpret_cast<int16*>(src), inputChannels, countChunk, inputChannel); + break; + default: + ASSERT(false); + break; + } + } else if(m_Settings.sampleFormat == SampleFormatInt24) + { + int24 *const dstInt24 = &m_SampleInputBufferInt24[0]; + if(!src) + { + // Skip if we did get no buffer for this channel, + // Fill with zeroes. + std::fill(dstInt24, dstInt24 + countChunk, int24(0)); + continue; + } + switch(m_ChannelInfo[channel].type) + { + case ASIOSTInt24MSB: + case ASIOSTInt24LSB: + CopyChannelToInterleaved<SC::Convert<int24, int24> >(dstInt24, reinterpret_cast<int24*>(src), inputChannels, countChunk, inputChannel); + break; + default: + ASSERT(false); + break; + } + } else if(m_Settings.sampleFormat == SampleFormatInt32) + { + int32 *const dstInt32 = &m_SampleInputBufferInt32[0]; + if(!src) + { + // Skip if we did get no buffer for this channel, + // Fill with zeroes. + std::fill(dstInt32, dstInt32 + countChunk, 0); + continue; + } + switch(m_ChannelInfo[channel].type) + { + case ASIOSTInt16MSB: + case ASIOSTInt16LSB: + CopyChannelToInterleaved<SC::Convert<int32, int16> >(dstInt32, reinterpret_cast<int16*>(src), inputChannels, countChunk, inputChannel); + break; + case ASIOSTInt24MSB: + case ASIOSTInt24LSB: + CopyChannelToInterleaved<SC::Convert<int32, int24> >(dstInt32, reinterpret_cast<int24*>(src), inputChannels, countChunk, inputChannel); + break; + case ASIOSTInt32MSB: + case ASIOSTInt32LSB: + CopyChannelToInterleaved<SC::Convert<int32, int32> >(dstInt32, reinterpret_cast<int32*>(src), inputChannels, countChunk, inputChannel); + break; + case ASIOSTFloat32MSB: + case ASIOSTFloat32LSB: + CopyChannelToInterleaved<SC::Convert<int32, float> >(dstInt32, reinterpret_cast<float*>(src), inputChannels, countChunk, inputChannel); + break; + case ASIOSTFloat64MSB: + case ASIOSTFloat64LSB: + CopyChannelToInterleaved<SC::Convert<int32, double> >(dstInt32, reinterpret_cast<double*>(src), inputChannels, countChunk, inputChannel); + break; + case ASIOSTInt32MSB16: + case ASIOSTInt32LSB16: + CopyChannelToInterleaved<SC::ConvertShiftUp<int32, int32, 16> >(dstInt32, reinterpret_cast<int32*>(src), inputChannels, countChunk, inputChannel); + break; + case ASIOSTInt32MSB18: + case ASIOSTInt32LSB18: + CopyChannelToInterleaved<SC::ConvertShiftUp<int32, int32, 14> >(dstInt32, reinterpret_cast<int32*>(src), inputChannels, countChunk, inputChannel); + break; + case ASIOSTInt32MSB20: + case ASIOSTInt32LSB20: + CopyChannelToInterleaved<SC::ConvertShiftUp<int32, int32, 12> >(dstInt32, reinterpret_cast<int32*>(src), inputChannels, countChunk, inputChannel); + break; + case ASIOSTInt32MSB24: + case ASIOSTInt32LSB24: + CopyChannelToInterleaved<SC::ConvertShiftUp<int32, int32, 8> >(dstInt32, reinterpret_cast<int32*>(src), inputChannels, countChunk, inputChannel); + break; + default: + ASSERT(false); + break; + } + } else + { + ASSERT(false); + } + } if(rendersilence) { if(m_Settings.sampleFormat == SampleFormatFloat32) { - std::memset(&m_SampleBufferFloat[0], 0, countChunk * channels * sizeof(float)); + std::memset(&m_SampleBufferFloat[0], 0, countChunk * outputChannels * sizeof(float)); } else if(m_Settings.sampleFormat == SampleFormatInt16) { - std::memset(&m_SampleBufferInt16[0], 0, countChunk * channels * sizeof(int16)); + std::memset(&m_SampleBufferInt16[0], 0, countChunk * outputChannels * sizeof(int16)); } else if(m_Settings.sampleFormat == SampleFormatInt24) { - std::memset(&m_SampleBufferInt24[0], 0, countChunk * channels * sizeof(int24)); + std::memset(&m_SampleBufferInt24[0], 0, countChunk * outputChannels * sizeof(int24)); } else if(m_Settings.sampleFormat == SampleFormatInt32) { - std::memset(&m_SampleBufferInt32[0], 0, countChunk * channels * sizeof(int32)); + std::memset(&m_SampleBufferInt32[0], 0, countChunk * outputChannels * sizeof(int32)); } else { MPT_ASSERT(false); @@ -882,24 +1052,25 @@ SourceAudioPreRead(countChunk, m_nAsioBufferLen); if(m_Settings.sampleFormat == SampleFormatFloat32) { - SourceAudioRead(&m_SampleBufferFloat[0], countChunk); + SourceAudioRead(&m_SampleBufferFloat[0], (m_SampleInputBufferFloat.size() > 0) ? &m_SampleInputBufferFloat[0] : nullptr, countChunk); } else if(m_Settings.sampleFormat == SampleFormatInt16) { - SourceAudioRead(&m_SampleBufferInt16[0], countChunk); + SourceAudioRead(&m_SampleBufferInt16[0], (m_SampleInputBufferInt16.size() > 0) ? &m_SampleInputBufferInt16[0] : nullptr, countChunk); } else if(m_Settings.sampleFormat == SampleFormatInt24) { - SourceAudioRead(&m_SampleBufferInt24[0], countChunk); + SourceAudioRead(&m_SampleBufferInt24[0], (m_SampleInputBufferInt24.size() > 0) ? &m_SampleInputBufferInt24[0] : nullptr, countChunk); } else if(m_Settings.sampleFormat == SampleFormatInt32) { - SourceAudioRead(&m_SampleBufferInt32[0], countChunk); + SourceAudioRead(&m_SampleBufferInt32[0], (m_SampleInputBufferInt32.size() > 0) ? &m_SampleInputBufferInt32[0] : nullptr, countChunk); } else { MPT_ASSERT(false); } } - for(int channel = 0; channel < channels; ++channel) + for(std::size_t outputChannel = 0; outputChannel < outputChannels; ++outputChannel) { - void *dst = m_BufferInfo[channel].buffers[(unsigned long)m_BufferIndex & 1]; + std::size_t channel = outputChannel + m_Settings.InputChannels; + void *dst = m_BufferInfo[channel].buffers[static_cast<unsigned long>(m_BufferIndex) & 1]; if(!dst) { // Skip if we did get no buffer for this channel @@ -912,11 +1083,11 @@ { case ASIOSTFloat32MSB: case ASIOSTFloat32LSB: - CopyInterleavedToChannel<SC::Convert<float, float> >(reinterpret_cast<float*>(dst), srcFloat, channels, countChunk, channel); + CopyInterleavedToChannel<SC::Convert<float, float> >(reinterpret_cast<float*>(dst), srcFloat, outputChannels, countChunk, outputChannel); break; case ASIOSTFloat64MSB: case ASIOSTFloat64LSB: - CopyInterleavedToChannel<SC::Convert<double, float> >(reinterpret_cast<double*>(dst), srcFloat, channels, countChunk, channel); + CopyInterleavedToChannel<SC::Convert<double, float> >(reinterpret_cast<double*>(dst), srcFloat, outputChannels, countChunk, outputChannel); break; default: MPT_ASSERT(false); @@ -929,7 +1100,7 @@ { case ASIOSTInt16MSB: case ASIOSTInt16LSB: - CopyInterleavedToChannel<SC::Convert<int16, int16> >(reinterpret_cast<int16*>(dst), srcInt16, channels, countChunk, channel); + CopyInterleavedToChannel<SC::Convert<int16, int16> >(reinterpret_cast<int16*>(dst), srcInt16, outputChannels, countChunk, outputChannel); break; default: MPT_ASSERT(false); @@ -942,7 +1113,7 @@ { case ASIOSTInt24MSB: case ASIOSTInt24LSB: - CopyInterleavedToChannel<SC::Convert<int24, int24> >(reinterpret_cast<int24*>(dst), srcInt24, channels, countChunk, channel); + CopyInterleavedToChannel<SC::Convert<int24, int24> >(reinterpret_cast<int24*>(dst), srcInt24, outputChannels, countChunk, outputChannel); break; default: MPT_ASSERT(false); @@ -955,39 +1126,39 @@ { case ASIOSTInt16MSB: case ASIOSTInt16LSB: - CopyInterleavedToChannel<SC::Convert<int16, int32> >(reinterpret_cast<int16*>(dst), srcInt32, channels, countChunk, channel); + CopyInterleavedToChannel<SC::Convert<int16, int32> >(reinterpret_cast<int16*>(dst), srcInt32, outputChannels, countChunk, outputChannel); break; case ASIOSTInt24MSB: case ASIOSTInt24LSB: - CopyInterleavedToChannel<SC::Convert<int24, int32> >(reinterpret_cast<int24*>(dst), srcInt32, channels, countChunk, channel); + CopyInterleavedToChannel<SC::Convert<int24, int32> >(reinterpret_cast<int24*>(dst), srcInt32, outputChannels, countChunk, outputChannel); break; case ASIOSTInt32MSB: case ASIOSTInt32LSB: - CopyInterleavedToChannel<SC::Convert<int32, int32> >(reinterpret_cast<int32*>(dst), srcInt32, channels, countChunk, channel); + CopyInterleavedToChannel<SC::Convert<int32, int32> >(reinterpret_cast<int32*>(dst), srcInt32, outputChannels, countChunk, outputChannel); break; case ASIOSTFloat32MSB: case ASIOSTFloat32LSB: - CopyInterleavedToChannel<SC::Convert<float, int32> >(reinterpret_cast<float*>(dst), srcInt32, channels, countChunk, channel); + CopyInterleavedToChannel<SC::Convert<float, int32> >(reinterpret_cast<float*>(dst), srcInt32, outputChannels, countChunk, outputChannel); break; case ASIOSTFloat64MSB: case ASIOSTFloat64LSB: - CopyInterleavedToChannel<SC::Convert<double, int32> >(reinterpret_cast<double*>(dst), srcInt32, channels, countChunk, channel); + CopyInterleavedToChannel<SC::Convert<double, int32> >(reinterpret_cast<double*>(dst), srcInt32, outputChannels, countChunk, outputChannel); break; case ASIOSTInt32MSB16: case ASIOSTInt32LSB16: - CopyInterleavedToChannel<SC::ConvertShift<int32, int32, 16> >(reinterpret_cast<int32*>(dst), srcInt32, channels, countChunk, channel); + CopyInterleavedToChannel<SC::ConvertShift<int32, int32, 16> >(reinterpret_cast<int32*>(dst), srcInt32, outputChannels, countChunk, outputChannel); break; case ASIOSTInt32MSB18: case ASIOSTInt32LSB18: - CopyInterleavedToChannel<SC::ConvertShift<int32, int32, 14> >(reinterpret_cast<int32*>(dst), srcInt32, channels, countChunk, channel); + CopyInterleavedToChannel<SC::ConvertShift<int32, int32, 14> >(reinterpret_cast<int32*>(dst), srcInt32, outputChannels, countChunk, outputChannel); break; case ASIOSTInt32MSB20: case ASIOSTInt32LSB20: - CopyInterleavedToChannel<SC::ConvertShift<int32, int32, 12> >(reinterpret_cast<int32*>(dst), srcInt32, channels, countChunk, channel); + CopyInterleavedToChannel<SC::ConvertShift<int32, int32, 12> >(reinterpret_cast<int32*>(dst), srcInt32, outputChannels, countChunk, outputChannel); break; case ASIOSTInt32MSB24: case ASIOSTInt32LSB24: - CopyInterleavedToChannel<SC::ConvertShift<int32, int32, 8> >(reinterpret_cast<int32*>(dst), srcInt32, channels, countChunk, channel); + CopyInterleavedToChannel<SC::ConvertShift<int32, int32, 8> >(reinterpret_cast<int32*>(dst), srcInt32, outputChannels, countChunk, outputChannel); break; default: MPT_ASSERT(false); @@ -1386,6 +1557,8 @@ caps.CanKeepDeviceRunning = true; caps.CanUseHardwareTiming = true; caps.CanChannelMapping = true; + caps.CanInput = true; + caps.HasNamedInputSources = true; caps.CanDriverPanel = true; caps.LatencyMin = 0.000001; // 1 us @@ -1467,6 +1640,24 @@ } caps.channelNames.push_back(name); } + for(long i = 0; i < inputChannels; ++i) + { + ASIOChannelInfo channelInfo; + MemsetZero(channelInfo); + channelInfo.channel = i; + channelInfo.isInput = ASIOTrue; + mpt::ustring name = mpt::ufmt::dec(i); + try + { + asioCall(getChannelInfo(&channelInfo)); + mpt::String::SetNullTerminator(channelInfo.name); + name = mpt::ToUnicode(mpt::CharsetLocale, channelInfo.name); + } catch(...) + { + // continue + } + caps.inputSourceNames.push_back(std::make_pair(static_cast<uint32>(i), name)); + } } catch(...) { // continue Modified: trunk/OpenMPT/sounddev/SoundDeviceASIO.h =================================================================== --- trunk/OpenMPT/sounddev/SoundDeviceASIO.h 2015-05-21 11:48:12 UTC (rev 5132) +++ trunk/OpenMPT/sounddev/SoundDeviceASIO.h 2015-05-21 12:05:38 UTC (rev 5133) @@ -71,6 +71,10 @@ std::vector<int16> m_SampleBufferInt16; std::vector<int24> m_SampleBufferInt24; std::vector<int32> m_SampleBufferInt32; + std::vector<float> m_SampleInputBufferFloat; + std::vector<int16> m_SampleInputBufferInt16; + std::vector<int24> m_SampleInputBufferInt24; + std::vector<int32> m_SampleInputBufferInt32; bool m_CanOutputReady; bool m_DeviceRunning; Modified: trunk/OpenMPT/sounddev/SoundDeviceDirectSound.cpp =================================================================== --- trunk/OpenMPT/sounddev/SoundDeviceDirectSound.cpp 2015-05-21 11:48:12 UTC (rev 5132) +++ trunk/OpenMPT/sounddev/SoundDeviceDirectSound.cpp 2015-05-21 12:05:38 UTC (rev 5133) @@ -142,6 +142,8 @@ caps.CanBoostThreadPriority = true; caps.CanUseHardwareTiming = false; caps.CanChannelMapping = false; + caps.CanInput = false; + caps.HasNamedInputSources = false; caps.CanDriverPanel = false; caps.ExclusiveModeDescription = MPT_USTRING("Use primary buffer"); caps.DefaultSettings.sampleFormat = SampleFormatInt16; @@ -238,6 +240,8 @@ bool CDSoundDevice::InternalOpen() //-------------------------------- { + if(m_Settings.InputChannels > 0) return false; + WAVEFORMATEXTENSIBLE wfext; if(!FillWaveFormatExtensible(wfext, m_Settings)) return false; WAVEFORMATEX *pwfx = &wfext.Format; @@ -455,8 +459,8 @@ SourceAudioPreRead(dwSize1/bytesPerFrame + dwSize2/bytesPerFrame, dwLatency/bytesPerFrame); - SourceAudioRead(buf1, dwSize1/bytesPerFrame); - SourceAudioRead(buf2, dwSize2/bytesPerFrame); + SourceAudioRead(buf1, nullptr, dwSize1/bytesPerFrame); + SourceAudioRead(buf2, nullptr, dwSize2/bytesPerFrame); if(m_pMixBuffer->Unlock(buf1, dwSize1, buf2, dwSize2) != DS_OK) { Modified: trunk/OpenMPT/sounddev/SoundDevicePortAudio.cpp =================================================================== --- trunk/OpenMPT/sounddev/SoundDevicePortAudio.cpp 2015-05-21 11:48:12 UTC (rev 5132) +++ trunk/OpenMPT/sounddev/SoundDevicePortAudio.cpp 2015-05-21 12:05:38 UTC (rev 5133) @@ -40,6 +40,7 @@ m_DeviceIndex = ConvertStrTo<PaDeviceIndex>(GetDeviceInternalID()); m_HostApiType = Pa_GetHostApiInfo(Pa_GetDeviceInfo(m_DeviceIndex)->hostApi)->type; MemsetZero(m_StreamParameters); + MemsetZero(m_InputStreamParameters); m_Stream = 0; m_StreamInfo = 0; m_CurrentFrameCount = 0; @@ -58,9 +59,11 @@ //----------------------------------- { MemsetZero(m_StreamParameters); + MemsetZero(m_InputStreamParameters); m_Stream = 0; m_StreamInfo = 0; m_CurrentFrameBuffer = 0; + m_CurrentFrameBufferInput = 0; m_CurrentFrameCount = 0; m_StreamParameters.device = m_DeviceIndex; if(m_StreamParameters.device == -1) return false; @@ -114,13 +117,19 @@ { m_Flags.NeedsClippedFloat = false; } - if(Pa_IsFormatSupported(NULL, &m_StreamParameters, m_Settings.Samplerate) != paFormatIsSupported) return false; + m_InputStreamParameters = m_StreamParameters; + if(!HasInputChannelsOnSameDevice()) + { + m_InputStreamParameters.device = static_cast<PaDeviceIndex>(m_Settings.InputSourceID); + } + m_InputStreamParameters.channelCount = m_Settings.InputChannels; + if(Pa_IsFormatSupported((m_Settings.InputChannels > 0) ? &m_InputStreamParameters : NULL, &m_StreamParameters, m_Settings.Samplerate) != paFormatIsSupported) 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; + if(Pa_OpenStream(&m_Stream, (m_Settings.InputChannels > 0) ? &m_InputStreamParameters : NULL, &m_StreamParameters, m_Settings.Samplerate, framesPerBuffer, flags, StreamCallbackWrapper, (void*)this) != paNoError) return false; m_StreamInfo = Pa_GetStreamInfo(m_Stream); if(!m_StreamInfo) { @@ -145,10 +154,12 @@ Pa_Sleep(Util::Round<long>(bufferAttributes.Latency * 2.0 * 1000.0 + 0.5)); // wait for broken wdm drivers not closing the stream immediatly } MemsetZero(m_StreamParameters); + MemsetZero(m_InputStreamParameters); m_StreamInfo = 0; m_Stream = 0; m_CurrentFrameCount = 0; m_CurrentFrameBuffer = 0; + m_CurrentFrameBufferInput = 0; } return true; } @@ -173,7 +184,7 @@ { if(m_CurrentFrameCount == 0) return; SourceAudioPreRead(m_CurrentFrameCount, Util::Round<std::size_t>(m_CurrentRealLatency * m_StreamInfo->sampleRate)); - SourceAudioRead(m_CurrentFrameBuffer, m_CurrentFrameCount); + SourceAudioRead(m_CurrentFrameBuffer, m_CurrentFrameBufferInput, m_CurrentFrameCount); m_StatisticPeriodFrames.store(m_CurrentFrameCount); SourceAudioDone(); } @@ -221,14 +232,25 @@ caps.CanBoostThreadPriority = false; caps.CanUseHardwareTiming = false; caps.CanChannelMapping = false; + caps.CanInput = false; + caps.HasNamedInputSources = false; caps.CanDriverPanel = false; caps.HasInternalDither = true; + caps.DefaultSettings.sampleFormat = SampleFormatFloat32; const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(m_DeviceIndex); if(deviceInfo) { caps.DefaultSettings.Latency = deviceInfo->defaultLowOutputLatency; } - caps.DefaultSettings.sampleFormat = SampleFormatFloat32; + if(HasInputChannelsOnSameDevice()) + { + caps.CanInput = true; + caps.HasNamedInputSources = false; + } else + { + caps.CanInput = (EnumerateInputOnlyDevices(m_HostApiType).size() > 0); + caps.HasNamedInputSources = caps.CanInput; + } if(m_HostApiType == paWASAPI) { caps.CanExclusiveMode = true; @@ -266,7 +288,7 @@ { SoundDevice::DynamicCaps caps; PaDeviceIndex device = m_DeviceIndex; - if(device == -1) + if(device == paNoDevice) { return caps; } @@ -294,6 +316,17 @@ caps.supportedExclusiveSampleRates.push_back(baseSampleRates[n]); } } + + if(!HasInputChannelsOnSameDevice()) + { + caps.inputSourceNames.clear(); + std::vector<std::pair<PaDeviceIndex, mpt::ustring> > inputDevices = EnumerateInputOnlyDevices(m_HostApiType); + for(std::vector<std::pair<PaDeviceIndex, mpt::ustring> >::const_iterator it = inputDevices.begin(); it != inputDevices.end(); ++it) + { + caps.inputSourceNames.push_back(std::make_pair(static_cast<uint32>(it->first), it->second)); + } + } + return caps; } @@ -350,10 +383,12 @@ m_CurrentRealLatency = timeInfo->outputBufferDacTime - timeInfo->currentTime; } m_CurrentFrameBuffer = output; + m_CurrentFrameBufferInput = input; m_CurrentFrameCount = frameCount; SourceFillAudioBufferLocked(); m_CurrentFrameCount = 0; m_CurrentFrameBuffer = 0; + m_CurrentFrameBufferInput = 0; return paContinue; } @@ -452,6 +487,68 @@ } + + +std::vector<std::pair<PaDeviceIndex, mpt::ustring> > CPortaudioDevice::EnumerateInputOnlyDevices(PaHostApiTypeId hostApiType) +//--------------------------------------------------------------------------------------------------------------------------- +{ + std::vector<std::pair<PaDeviceIndex, mpt::ustring> > result; + for(PaDeviceIndex dev = 0; dev < Pa_GetDeviceCount(); ++dev) + { + if(!Pa_GetDeviceInfo(dev)) + { + continue; + } + if(Pa_GetDeviceInfo(dev)->hostApi < 0) + { + continue; + } + if(!Pa_GetHostApiInfo(Pa_GetDeviceInfo(dev)->hostApi)) + { + continue; + } + if(!Pa_GetDeviceInfo(dev)->name) + { + continue; + } + if(!Pa_GetHostApiInfo(Pa_GetDeviceInfo(dev)->hostApi)->name) + { + continue; + } + if(Pa_GetDeviceInfo(dev)->maxInputChannels <= 0) + { + continue; + } + if(Pa_GetDeviceInfo(dev)->maxOutputChannels > 0) + { // only find devices with only input channels + continue; + } + if(Pa_GetHostApiInfo(Pa_GetDeviceInfo(dev)->hostApi)->type != hostApiType) + { + continue; + } + result.push_back(std::make_pair(dev, mpt::ToUnicode(mpt::CharsetUTF8, Pa_GetDeviceInfo(dev)->name))); + } + return result; +} + + +bool CPortaudioDevice::HasInputChannelsOnSameDevice() const +//--------------------------------------------------------- +{ + if(m_DeviceIndex == paNoDevice) + { + return false; + } + const PaDeviceInfo *deviceinfo = Pa_GetDeviceInfo(m_DeviceIndex); + if(!deviceinfo) + { + return false; + } + return (deviceinfo->maxInputChannels > 0); +} + + static void PortaudioLog(const char *text) //---------------------------------------- { Modified: trunk/OpenMPT/sounddev/SoundDevicePortAudio.h =================================================================== --- trunk/OpenMPT/sounddev/SoundDevicePortAudio.h 2015-05-21 11:48:12 UTC (rev 5132) +++ trunk/OpenMPT/sounddev/SoundDevicePortAudio.h 2015-05-21 12:05:38 UTC (rev 5133) @@ -29,24 +29,30 @@ class CPortaudioDevice: public SoundDevice::Base //========================================= { + protected: + PaDeviceIndex m_DeviceIndex; PaHostApiTypeId m_HostApiType; PaStreamParameters m_StreamParameters; + PaStreamParameters m_InputStreamParameters; PaWasapiStreamInfo m_WasapiStreamInfo; PaStream * m_Stream; const PaStreamInfo * m_StreamInfo; void * m_CurrentFrameBuffer; + const void * m_CurrentFrameBufferInput; unsigned long m_CurrentFrameCount; double m_CurrentRealLatency; // seconds mpt::atomic_uint32_t m_StatisticPeriodFrames; public: + CPortaudioDevice(SoundDevice::Info info); ~CPortaudioDevice(); public: + bool InternalOpen(); bool InternalClose(); void InternalFillAudioBuffer(); @@ -69,6 +75,7 @@ ); public: + static int StreamCallbackWrapper( const void *input, void *output, unsigned long frameCount, @@ -79,6 +86,12 @@ static std::vector<SoundDevice::Info> EnumerateDevices(); +private: + + bool HasInputChannelsOnSameDevice() const; + + static std::vector<std::pair<PaDeviceIndex, mpt::ustring> > EnumerateInputOnlyDevices(PaHostApiTypeId hostApiType); + }; Modified: trunk/OpenMPT/sounddev/SoundDeviceWaveout.cpp =================================================================== --- trunk/OpenMPT/sounddev/SoundDeviceWaveout.cpp 2015-05-21 11:48:12 UTC (rev 5132) +++ trunk/OpenMPT/sounddev/SoundDeviceWaveout.cpp 2015-05-21 12:05:38 UTC (rev 5133) @@ -75,6 +75,8 @@ caps.CanKeepDeviceRunning = false; caps.CanUseHardwareTiming = false; caps.CanChannelMapping = false; + caps.CanInput = false; + caps.HasNamedInputSources = false; caps.CanDriverPanel = false; caps.HasInternalDither = false; caps.ExclusiveModeDescription = MPT_USTRING("Use direct mode"); @@ -133,6 +135,10 @@ //------------------------------ { MPT_TRACE(); + if(m_Settings.InputChannels > 0) + { + return false; + } WAVEFORMATEXTENSIBLE wfext; if(!FillWaveFormatExtensible(wfext, m_Settings)) { @@ -265,7 +271,7 @@ { nLatency += m_nWaveBufferSize; SourceAudioPreRead(m_nWaveBufferSize / bytesPerFrame, nLatency / bytesPerFrame); - SourceAudioRead(m_WaveBuffers[m_nWriteBuffer].lpData, m_nWaveBufferSize / bytesPerFrame); + SourceAudioRead(m_WaveBuffers[m_nWriteBuffer].lpData, nullptr, m_nWaveBufferSize / bytesPerFrame); nBytesWritten += m_nWaveBufferSize; m_WaveBuffers[m_nWriteBuffer].dwBufferLength = m_nWaveBufferSize; InterlockedIncrement(&m_nBuffersPending); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |