/* * AudioReadTarget.h * ----------------- * Purpose: Callback class implementations for audio data read via CSoundFile::Read. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "BuildSettings.h" #include "Sndfile.h" #include "Dither.h" #include "../soundbase/SampleFormat.h" #include "../soundbase/SampleFormatConverters.h" #include "../soundbase/SampleFormatCopy.h" #include "MixerLoops.h" #include "Mixer.h" OPENMPT_NAMESPACE_BEGIN template class AudioReadTargetBuffer : public IAudioReadTarget { private: std::size_t countRendered; Dither &dither; protected: Tsample *outputBuffer; Tsample * const *outputBuffers; public: AudioReadTargetBuffer(Dither &dither_, Tsample *buffer, Tsample * const *buffers) : countRendered(0) , dither(dither_) , outputBuffer(buffer) , outputBuffers(buffers) { MPT_ASSERT(SampleFormat(SampleFormatTraits::sampleFormat).IsValid()); } std::size_t GetRenderedCount() const { return countRendered; } public: void DataCallback(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override { // Convert to output sample format and optionally perform dithering and clipping if needed const SampleFormat sampleFormat = SampleFormatTraits::sampleFormat; if(sampleFormat.IsInt()) { dither.Process(MixSoundBuffer, countChunk, channels, sampleFormat.GetBitsPerSample()); } if(outputBuffer) { ConvertInterleavedFixedPointToInterleaved(outputBuffer + (channels * countRendered), MixSoundBuffer, channels, countChunk); } if(outputBuffers) { Tsample *buffers[4] = { nullptr, nullptr, nullptr, nullptr }; for(std::size_t channel = 0; channel < channels; ++channel) { buffers[channel] = outputBuffers[channel] + countRendered; } ConvertInterleavedFixedPointToNonInterleaved(buffers, MixSoundBuffer, channels, countChunk); } countRendered += countChunk; } }; #if defined(MODPLUG_TRACKER) class AudioReadTargetBufferInterleavedDynamic : public IAudioReadTarget { private: const SampleFormat sampleFormat; bool clipFloat; Dither &dither; void *buffer; public: AudioReadTargetBufferInterleavedDynamic(SampleFormat sampleFormat_, bool clipFloat_, Dither &dither_, void *buffer_) : sampleFormat(sampleFormat_) , clipFloat(clipFloat_) , dither(dither_) , buffer(buffer_) { MPT_ASSERT_ALWAYS(sampleFormat.IsValid()); } void DataCallback(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override { switch(sampleFormat.value) { case SampleFormatUnsigned8: { typedef SampleFormatToType::type Tsample; AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); target.DataCallback(MixSoundBuffer, channels, countChunk); } break; case SampleFormatInt16: { typedef SampleFormatToType::type Tsample; AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); target.DataCallback(MixSoundBuffer, channels, countChunk); } break; case SampleFormatInt24: { typedef SampleFormatToType::type Tsample; AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); target.DataCallback(MixSoundBuffer, channels, countChunk); } break; case SampleFormatInt32: { typedef SampleFormatToType::type Tsample; AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); target.DataCallback(MixSoundBuffer, channels, countChunk); } break; case SampleFormatFloat32: if(clipFloat) { typedef SampleFormatToType::type Tsample; AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); target.DataCallback(MixSoundBuffer, channels, countChunk); } else { typedef SampleFormatToType::type Tsample; AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); target.DataCallback(MixSoundBuffer, channels, countChunk); } break; } // increment output buffer for potentially next callback buffer = reinterpret_cast(buffer) + (sampleFormat.GetBitsPerSample()/8) * channels * countChunk; } }; class AudioSourceBuffer : public IAudioSource { private: std::size_t countRendered; protected: SampleFormat sampleFormat; const void *inputBuffer; public: AudioSourceBuffer(SampleFormat sampleFormat, const void *buffer) : countRendered(0) , sampleFormat(sampleFormat) , inputBuffer(buffer) { MPT_ASSERT(sampleFormat.IsValid()); } virtual ~AudioSourceBuffer() { } std::size_t GetRenderedCount() const { return countRendered; } private: template void Fill(const Tsample *inputBuffer, int32 * const *MixInputBuffers, std::size_t channels, std::size_t countChunk) { for(std::size_t channel = 0; channel < channels; ++channel) { SC::ConvertToFixedPoint conv; for(std::size_t frame = 0; frame < countChunk; ++frame) { MixInputBuffers[channel][frame] = conv(inputBuffer[channel + ((countRendered + frame) * channels)]); } } countRendered += countChunk; } public: virtual void FillCallback(int32 * const *MixInputBuffers, std::size_t channels, std::size_t countChunk) { switch(sampleFormat.value) { case SampleFormatUnsigned8: { typedef SampleFormatToType::type Tsample; Fill(reinterpret_cast(inputBuffer), MixInputBuffers, channels, countChunk); } break; case SampleFormatInt16: { typedef SampleFormatToType::type Tsample; Fill(reinterpret_cast(inputBuffer), MixInputBuffers, channels, countChunk); } break; case SampleFormatInt24: { typedef SampleFormatToType::type Tsample; Fill(reinterpret_cast(inputBuffer), MixInputBuffers, channels, countChunk); } break; case SampleFormatInt32: { typedef SampleFormatToType::type Tsample; Fill(reinterpret_cast(inputBuffer), MixInputBuffers, channels, countChunk); } break; case SampleFormatFloat32: { typedef SampleFormatToType::type Tsample; Fill(reinterpret_cast(inputBuffer), MixInputBuffers, channels, countChunk); } break; } } }; #else // !MODPLUG_TRACKER template void ApplyGainBeforeConversionIfAppropriate(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk, float gainFactor) { // Apply final output gain for non floating point output ApplyGain(MixSoundBuffer, channels, countChunk, mpt::saturate_round(gainFactor * (1<<16))); } template<> void ApplyGainBeforeConversionIfAppropriate(int32 * /*MixSoundBuffer*/, std::size_t /*channels*/, std::size_t /*countChunk*/, float /*gainFactor*/) { // nothing } template void ApplyGainAfterConversionIfAppropriate(Tsample * /*buffer*/, Tsample * const * /*buffers*/, std::size_t /*countRendered*/, std::size_t /*channels*/, std::size_t /*countChunk*/, float /*gainFactor*/) { // nothing } template<> void ApplyGainAfterConversionIfAppropriate(float *buffer, float * const *buffers, std::size_t countRendered, std::size_t channels, std::size_t countChunk, float gainFactor) { // Apply final output gain for floating point output after conversion so we do not suffer underflow or clipping ApplyGain(buffer, buffers, countRendered, channels, countChunk, gainFactor); } template class AudioReadTargetGainBuffer : public AudioReadTargetBuffer { private: typedef AudioReadTargetBuffer Tbase; private: const float gainFactor; public: AudioReadTargetGainBuffer(Dither &dither, Tsample *buffer, Tsample * const *buffers, float gainFactor_) : Tbase(dither, buffer, buffers) , gainFactor(gainFactor_) { return; } public: void DataCallback(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override { const std::size_t countRendered_ = Tbase::GetRenderedCount(); ApplyGainBeforeConversionIfAppropriate(MixSoundBuffer, channels, countChunk, gainFactor); Tbase::DataCallback(MixSoundBuffer, channels, countChunk); ApplyGainAfterConversionIfAppropriate(Tbase::outputBuffer, Tbase::outputBuffers, countRendered_, channels, countChunk, gainFactor); } }; #endif // MODPLUG_TRACKER OPENMPT_NAMESPACE_END