1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
|
/*
* 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 "openmpt/all/BuildSettings.hpp"
#include "mpt/audio/span.hpp"
#include "mpt/base/macros.hpp"
#include "mpt/string/types.hpp"
#include "openmpt/soundbase/SampleFormat.hpp"
#include "openmpt/soundbase/CopyMix.hpp"
#include "openmpt/soundbase/Dither.hpp"
#include "openmpt/soundbase/DitherModPlug.hpp"
#include "openmpt/soundbase/DitherNone.hpp"
#include "openmpt/soundbase/DitherSimple.hpp"
#include "MixerLoops.h"
#include "Mixer.h"
#include "Sndfile.h"
#include <type_traits>
#include <variant>
#include <cstddef>
OPENMPT_NAMESPACE_BEGIN
using Dither_Default = Dither_Simple;
class DitherNamesOpenMPT
{
public:
static mpt::ustring GetModeName(std::size_t mode)
{
mpt::ustring result;
switch(mode)
{
case 0:
// no dither
result = MPT_USTRING("no");
break;
case 1:
// chosen by OpenMPT code, might change
result = MPT_USTRING("default");
break;
case 2:
// rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker)
result = MPT_USTRING("0.5 bit");
break;
case 3:
// rectangular, 1 bit depth, simple 1st order noise shaping
result = MPT_USTRING("1 bit");
break;
default:
result = MPT_USTRING("");
break;
}
return result;
}
};
using DithersOpenMPT =
Dithers<std::variant<MultiChannelDither<Dither_None>, MultiChannelDither<Dither_Default>, MultiChannelDither<Dither_ModPlug>, MultiChannelDither<Dither_Simple>>, DitherNamesOpenMPT, 4, 1, 0, mpt::good_prng>;
struct DithersWrapperOpenMPT
: DithersOpenMPT
{
template <typename Trd>
DithersWrapperOpenMPT(Trd &rd, std::size_t mode = DithersOpenMPT::DefaultDither, std::size_t channels = DithersOpenMPT::DefaultChannels)
: DithersOpenMPT(rd, mode, channels)
{
return;
}
};
template <typename Taudio_span, typename TDithers = DithersOpenMPT>
class AudioTargetBuffer
: public IAudioTarget
{
private:
std::size_t countRendered;
TDithers &dithers;
protected:
Taudio_span outputBuffer;
public:
AudioTargetBuffer(Taudio_span buf, TDithers &dithers_)
: countRendered(0)
, dithers(dithers_)
, outputBuffer(buf)
{
return;
}
std::size_t GetRenderedCount() const { return countRendered; }
public:
void Process(mpt::audio_span_interleaved<MixSampleInt> buffer) override
{
std::visit(
[&](auto &ditherInstance)
{
ConvertBufferMixInternalFixedToBuffer<MixSampleIntTraits::mix_fractional_bits, false>(mpt::make_audio_span_with_offset(outputBuffer, countRendered), buffer, ditherInstance, buffer.size_channels(), buffer.size_frames());
},
dithers.Variant()
);
countRendered += buffer.size_frames();
}
void Process(mpt::audio_span_interleaved<MixSampleFloat> buffer) override
{
std::visit(
[&](auto &ditherInstance)
{
ConvertBufferMixInternalToBuffer<false>(mpt::make_audio_span_with_offset(outputBuffer, countRendered), buffer, ditherInstance, buffer.size_channels(), buffer.size_frames());
},
dithers.Variant()
);
countRendered += buffer.size_frames();
}
};
template <typename Taudio_span, typename TDithers = DithersOpenMPT>
class AudioTargetBufferWithGain
: public AudioTargetBuffer<Taudio_span>
{
private:
using Tbase = AudioTargetBuffer<Taudio_span>;
private:
const MixSampleFloat gainFactor;
public:
AudioTargetBufferWithGain(Taudio_span buf, TDithers &dithers, float gainFactor_)
: Tbase(buf, dithers)
, gainFactor(static_cast<MixSampleFloat>(gainFactor_))
{
return;
}
public:
void Process(mpt::audio_span_interleaved<MixSampleInt> buffer) override
{
const std::size_t countRendered_ = Tbase::GetRenderedCount();
if constexpr(!std::is_floating_point<typename Taudio_span::sample_type>::value)
{
int32 gainFactor16_16 = mpt::saturate_round<int32>(gainFactor * (1 << 16));
if(gainFactor16_16 != (1<<16))
{
// only apply gain when != +/- 0dB
// no clipping prevention is done here
for(std::size_t frame = 0; frame < buffer.size_frames(); ++frame)
{
for(std::size_t channel = 0; channel < buffer.size_channels(); ++channel)
{
buffer(channel, frame) = Util::muldiv(buffer(channel, frame), gainFactor16_16, 1 << 16);
}
}
}
}
Tbase::Process(buffer);
if constexpr(std::is_floating_point<typename Taudio_span::sample_type>::value)
{
if(gainFactor != MixSampleFloat(1.0))
{
// only apply gain when != +/- 0dB
for(std::size_t frame = 0; frame < buffer.size_frames(); ++frame)
{
for(std::size_t channel = 0; channel < buffer.size_channels(); ++channel)
{
Tbase::outputBuffer(channel, countRendered_ + frame) *= static_cast<typename Taudio_span::sample_type>(gainFactor);
}
}
}
}
}
void Process(mpt::audio_span_interleaved<MixSampleFloat> buffer) override
{
if(gainFactor != MixSampleFloat(1.0))
{
// only apply gain when != +/- 0dB
for(std::size_t frame = 0; frame < buffer.size_frames(); ++frame)
{
for(std::size_t channel = 0; channel < buffer.size_channels(); ++channel)
{
buffer(channel, frame) *= gainFactor;
}
}
}
Tbase::Process(buffer);
}
};
OPENMPT_NAMESPACE_END
|