From c1a4e0674baf7bf1e09961fa38dbd0408aad84dc Mon Sep 17 00:00:00 2001 From: wangqr Date: Tue, 29 Oct 2019 23:49:55 -0400 Subject: [PATCH] Move the ConvertAudioProvider into GetInt16MonoAudio function And use a dummy ConvertAudioProvider to keep backward compatibility. --- libaegisub/audio/provider.cpp | 96 +++++++++++++ libaegisub/audio/provider_convert.cpp | 128 ++---------------- .../include/libaegisub/audio/provider.h | 6 +- tests/tests/audio.cpp | 2 +- 4 files changed, 110 insertions(+), 122 deletions(-) diff --git a/libaegisub/audio/provider.cpp b/libaegisub/audio/provider.cpp index 64010744a..df2af5b6d 100644 --- a/libaegisub/audio/provider.cpp +++ b/libaegisub/audio/provider.cpp @@ -21,7 +21,103 @@ #include "libaegisub/log.h" #include "libaegisub/util.h" +namespace { + +template +class ConvertFloatToInt16 { + Source* src; +public: + ConvertFloatToInt16(Source* src) :src(src) {} + int16_t operator[](size_t idx) const { + Source expanded = src[idx] * 32768; + return expanded < -32768 ? -32768 : + expanded > 32767 ? 32767 : + static_cast(expanded); + } +}; + +// 8 bits per sample is assumed to be unsigned with a bias of 128, +// while everything else is assumed to be signed with zero bias +class ConvertIntToInt16 { + void* src; + int bytes_per_sample; +public: + ConvertIntToInt16(void* src, int bytes_per_sample) :src(src), bytes_per_sample(bytes_per_sample) {} + const int16_t& operator[](size_t idx) const { + return *reinterpret_cast(reinterpret_cast(src) + (idx + 1) * bytes_per_sample - sizeof(int16_t)); + } +}; +class ConvertUInt8ToInt16 { + uint8_t* src; +public: + ConvertUInt8ToInt16(uint8_t* src) :src(src) {} + int16_t operator[](size_t idx) const { + return int16_t(src[idx]-128) << 8; + } +}; + +template +class DownmixToMono { + Source src; + int channels; +public: + DownmixToMono(Source src, int channels) :src(src), channels(channels) {} + int16_t operator[](size_t idx) const { + int ret = 0; + // Just average the channels together + for (int i = 0; i < channels; ++i) + ret += src[idx * channels + i]; + return ret / channels; + } +}; +} + namespace agi { +void AudioProvider::FillBufferInt16Mono(int16_t* buf, int64_t start, int64_t count) const { + if (!float_samples && bytes_per_sample == 2 && channels == 1) { + FillBuffer(buf, start, count); + return; + } + void* buff = malloc(bytes_per_sample * count * channels); + FillBuffer(buff, start, count); + if (channels == 1) { + if (float_samples) { + if (bytes_per_sample == sizeof(float)) + for (int64_t i = 0; i < count; ++i) + buf[i] = ConvertFloatToInt16(reinterpret_cast(buff))[i]; + else if (bytes_per_sample == sizeof(double)) + for (int64_t i = 0; i < count; ++i) + buf[i] = ConvertFloatToInt16(reinterpret_cast(buff))[i]; + } + else { + if (bytes_per_sample == sizeof(uint8_t)) + for (int64_t i = 0; i < count; ++i) + buf[i] = ConvertUInt8ToInt16(reinterpret_cast(buff))[i]; + else + for (int64_t i = 0; i < count; ++i) + buf[i] = ConvertIntToInt16(buff, bytes_per_sample)[i]; + } + } + else { + if (float_samples) { + if (bytes_per_sample == sizeof(float)) + for (int64_t i = 0; i < count; ++i) + buf[i] = DownmixToMono >(ConvertFloatToInt16(reinterpret_cast(buff)), channels)[i]; + else if (bytes_per_sample == sizeof(double)) + for (int64_t i = 0; i < count; ++i) + buf[i] = DownmixToMono >(ConvertFloatToInt16(reinterpret_cast(buff)), channels)[i]; + } + else { + if (bytes_per_sample == sizeof(uint8_t)) + for (int64_t i = 0; i < count; ++i) + buf[i] = DownmixToMono(ConvertUInt8ToInt16(reinterpret_cast(buff)), channels)[i]; + else + for (int64_t i = 0; i < count; ++i) + buf[i] = DownmixToMono(ConvertIntToInt16(buff, bytes_per_sample), channels)[i]; + } + } +} + void AudioProvider::GetInt16MonoAudioWithVolume(int16_t *buf, int64_t start, int64_t count, double volume) const { GetInt16MonoAudio(buf, start, count); if (volume == 1.0) return; diff --git a/libaegisub/audio/provider_convert.cpp b/libaegisub/audio/provider_convert.cpp index 995903e79..32efa6ee9 100644 --- a/libaegisub/audio/provider_convert.cpp +++ b/libaegisub/audio/provider_convert.cpp @@ -23,115 +23,18 @@ using namespace agi; -/// Anything integral -> 16 bit signed machine-endian audio converter +/// Anything -> mono 16 bit signed machine-endian audio converter namespace { -template -class BitdepthConvertAudioProvider final : public AudioProviderWrapper { - int src_bytes_per_sample; - mutable std::vector src_buf; - +class ConvertAudioProvider final : public AudioProviderWrapper { public: - BitdepthConvertAudioProvider(std::unique_ptr src) : AudioProviderWrapper(std::move(src)) { - if (bytes_per_sample > 8) - throw AudioProviderError("Audio format converter: audio with bitdepths greater than 64 bits/sample is currently unsupported"); - - src_bytes_per_sample = bytes_per_sample; - bytes_per_sample = sizeof(Target); - } - - void FillBuffer(void *buf, int64_t start, int64_t count64) const override { - auto count = static_cast(count64); - assert(count64 >= 0 && count == static_cast(count64)); - - src_buf.resize(count * src_bytes_per_sample * channels); - source->GetAudio(src_buf.data(), start, count); - - auto dest = static_cast(buf); - - for (size_t i = 0; i < count * channels; ++i) { - int64_t sample = 0; - - // 8 bits per sample is assumed to be unsigned with a bias of 127, - // while everything else is assumed to be signed with zero bias - if (src_bytes_per_sample == 1) - sample = src_buf[i] - 128; - else { - for (int j = src_bytes_per_sample; j > 0; --j) { - sample <<= 8; - sample += src_buf[i * src_bytes_per_sample + j - 1]; - } - } - - if (static_cast(src_bytes_per_sample) > sizeof(Target)) - sample /= 1LL << (src_bytes_per_sample - sizeof(Target)) * 8; - else if (static_cast(src_bytes_per_sample) < sizeof(Target)) - sample *= 1LL << (sizeof(Target) - src_bytes_per_sample ) * 8; - - dest[i] = static_cast(sample); - } - } -}; - -/// Floating point -> 16 bit signed machine-endian audio converter -template -class FloatConvertAudioProvider final : public AudioProviderWrapper { - mutable std::vector src_buf; - -public: - FloatConvertAudioProvider(std::unique_ptr src) : AudioProviderWrapper(std::move(src)) { - bytes_per_sample = sizeof(Target); + ConvertAudioProvider(std::unique_ptr src) : AudioProviderWrapper(std::move(src)) { float_samples = false; - } - - void FillBuffer(void *buf, int64_t start, int64_t count64) const override { - auto count = static_cast(count64); - assert(count64 >= 0 && count == static_cast(count64)); - - src_buf.resize(count * channels); - source->GetAudio(&src_buf[0], start, count); - - auto dest = static_cast(buf); - - for (size_t i = 0; i < static_cast(count * channels); ++i) { - Source expanded; - if (src_buf[i] < 0) - expanded = static_cast(-src_buf[i] * std::numeric_limits::min()); - else - expanded = static_cast(src_buf[i] * std::numeric_limits::max()); - - dest[i] = expanded < std::numeric_limits::min() ? std::numeric_limits::min() : - expanded > std::numeric_limits::max() ? std::numeric_limits::max() : - static_cast(expanded); - } - } -}; - -/// Non-mono 16-bit signed machine-endian -> mono 16-bit signed machine endian converter -class DownmixAudioProvider final : public AudioProviderWrapper { - int src_channels; - mutable std::vector src_buf; - -public: - DownmixAudioProvider(std::unique_ptr src) : AudioProviderWrapper(std::move(src)) { - src_channels = channels; channels = 1; + bytes_per_sample = sizeof(int16_t); } - void FillBuffer(void *buf, int64_t start, int64_t count64) const override { - auto count = static_cast(count64); - assert(count64 >= 0 && count == static_cast(count64)); - - src_buf.resize(count * src_channels); - source->GetAudio(&src_buf[0], start, count); - - auto dst = static_cast(buf); - // Just average the channels together - while (count-- > 0) { - int sum = 0; - for (int c = 0; c < src_channels; ++c) - sum += src_buf[count * src_channels + c]; - dst[count] = static_cast(sum / src_channels); - } + void FillBuffer(void *buf, int64_t start, int64_t count) const override { + source->GetInt16MonoAudio(reinterpret_cast(buf), start, count); } }; @@ -175,23 +78,16 @@ public: namespace agi { std::unique_ptr CreateConvertAudioProvider(std::unique_ptr provider) { // Ensure 16-bit audio with proper endianness - if (provider->AreSamplesFloat()) { + if (provider->AreSamplesFloat()) LOG_D("audio_provider") << "Converting float to S16"; - if (provider->GetBytesPerSample() == sizeof(float)) - provider = agi::make_unique>(std::move(provider)); - else - provider = agi::make_unique>(std::move(provider)); - } - if (provider->GetBytesPerSample() != 2) { - LOG_D("audio_provider") << "Converting " << provider->GetBytesPerSample() << " bytes per sample or wrong endian to S16"; - provider = agi::make_unique>(std::move(provider)); - } + else if (provider->GetBytesPerSample() != 2) + LOG_D("audio_provider") << "Converting " << provider->GetBytesPerSample() << " bytes per sample to S16"; // We currently only support mono audio - if (provider->GetChannels() != 1) { + if (provider->GetChannels() != 1) LOG_D("audio_provider") << "Downmixing to mono from " << provider->GetChannels() << " channels"; - provider = agi::make_unique(std::move(provider)); - } + + provider = agi::make_unique(std::move(provider)); // Some players don't like low sample rate audio while (provider->GetSampleRate() < 32000) { diff --git a/libaegisub/include/libaegisub/audio/provider.h b/libaegisub/include/libaegisub/audio/provider.h index 33657a543..dd3f49e37 100644 --- a/libaegisub/include/libaegisub/audio/provider.h +++ b/libaegisub/include/libaegisub/audio/provider.h @@ -36,11 +36,7 @@ protected: bool float_samples = false; virtual void FillBuffer(void *buf, int64_t start, int64_t count) const = 0; - virtual void FillBufferInt16Mono(int16_t* buf, int64_t start, int64_t count) const { - if (float_samples || bytes_per_sample != 2 || channels != 1) - throw agi::InternalError("FillBufferInt16Mono called on unconverted audio stream"); - FillBuffer(buf, start, count); - } + virtual void FillBufferInt16Mono(int16_t* buf, int64_t start, int64_t count) const; void ZeroFill(void *buf, int64_t count) const; diff --git a/tests/tests/audio.cpp b/tests/tests/audio.cpp index ff8f40be1..9751f9d40 100644 --- a/tests/tests/audio.cpp +++ b/tests/tests/audio.cpp @@ -333,7 +333,7 @@ struct FloatAudioProvider : agi::AudioProvider { auto out = static_cast(buf); for (int64_t end = start + count; start < end; ++start) { auto shifted = start + SHRT_MIN; - *out++ = (Float)(1.0 * shifted / (shifted < 0 ? -SHRT_MIN : SHRT_MAX)); + *out++ = (Float)(shifted) / (-SHRT_MIN); } } };