| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "garnet/bin/media/audio_core/mixer/output_producer.h" |
| |
| #include <fbl/algorithm.h> |
| #include <cmath> |
| #include <limits> |
| #include <type_traits> |
| |
| #include "garnet/bin/media/audio_core/mixer/constants.h" |
| #include "lib/fidl/cpp/clone.h" |
| #include "lib/fxl/logging.h" |
| |
| namespace media::audio { |
| |
| // Converting audio between float and int is surprisingly controversial. |
| // (blog.bjornroche.com/2009/12/int-float-int-its-jungle-out-there.html etc. -- |
| // web-search "audio float int convert"). Our float32-based internal pipeline |
| // can accomodate float and int Sources without data loss (where Source is a |
| // client-submitted stream from AudioRenderer, or an input device), but for non- |
| // float Destinations (output device, or AudioCapturer stream to a client) we |
| // must clamp +1.0 values in DestConverter::Convert. When translating from float |
| // to int16 for example, we can translate -1.0 perfectly to -32768 (negative |
| // 0x8000), while +1.0 cannot become +32768 (positive 0x8000, exceeding int16's |
| // max) so it is clamped to 32767 (0x7FFF). |
| // |
| // Having said all this, the "practically clipping" value of +1.0 is rare in WAV |
| // files, and other sources should easily be able to reduce their input levels. |
| |
| // Template to produce destination samples from normalized samples. |
| template <typename DType, typename Enable = void> |
| class DestConverter; |
| |
| template <typename DType> |
| class DestConverter< |
| DType, typename std::enable_if<std::is_same<DType, uint8_t>::value>::type> { |
| public: |
| static inline constexpr DType Convert(float sample) { |
| return fbl::clamp<int32_t>( |
| round(sample * kFloatToInt8) + kOffsetInt8ToUint8, |
| std::numeric_limits<uint8_t>::min(), |
| std::numeric_limits<uint8_t>::max()); |
| } |
| }; |
| |
| template <typename DType> |
| class DestConverter< |
| DType, typename std::enable_if<std::is_same<DType, int16_t>::value>::type> { |
| public: |
| static inline constexpr DType Convert(float sample) { |
| return fbl::clamp<int32_t>(round(sample * kFloatToInt16), |
| std::numeric_limits<int16_t>::min(), |
| std::numeric_limits<int16_t>::max()); |
| } |
| }; |
| |
| template <typename DType> |
| class DestConverter< |
| DType, typename std::enable_if<std::is_same<DType, int32_t>::value>::type> { |
| public: |
| static inline constexpr DType Convert(float sample) { |
| return fbl::clamp<int64_t>(round(sample * kFloatToInt24In32), kMinInt24In32, |
| kMaxInt24In32); |
| } |
| }; |
| |
| template <typename DType> |
| class DestConverter< |
| DType, typename std::enable_if<std::is_same<DType, float>::value>::type> { |
| public: |
| // This will emit +1.0 values, which are legal per WAV format custom. |
| static inline constexpr DType Convert(float sample) { |
| return fbl::clamp(sample, -1.0f, 1.0f); |
| } |
| }; |
| |
| // Template to fill samples with silence based on sample type. |
| template <typename DType, typename Enable = void> |
| class SilenceMaker; |
| |
| template <typename DType> |
| class SilenceMaker< |
| DType, typename std::enable_if<std::is_same<DType, int16_t>::value || |
| std::is_same<DType, int32_t>::value || |
| std::is_same<DType, float>::value>::type> { |
| public: |
| static inline void Fill(void* dest, size_t samples) { |
| // This works even if DType is float/double: per IEEE-754, all 0s == +0.0. |
| ::memset(dest, 0, samples * sizeof(DType)); |
| } |
| }; |
| |
| template <typename DType> |
| class SilenceMaker< |
| DType, typename std::enable_if<std::is_same<DType, uint8_t>::value>::type> { |
| public: |
| static inline void Fill(void* dest, size_t samples) { |
| ::memset(dest, kOffsetInt8ToUint8, samples * sizeof(DType)); |
| } |
| }; |
| |
| // A templated class which implements the ProduceOutput and FillWithSilence |
| // methods of OutputProducer |
| template <typename DType> |
| class OutputProducerImpl : public OutputProducer { |
| public: |
| explicit OutputProducerImpl(const fuchsia::media::AudioStreamTypePtr& format) |
| : OutputProducer(format, sizeof(DType)) {} |
| |
| void ProduceOutput(const float* source, void* dest_void, |
| uint32_t frames) const override { |
| using DC = DestConverter<DType>; |
| auto* dest = static_cast<DType*>(dest_void); |
| |
| // Previously we clamped here; because of rounding, this is different for |
| // each output type, so it is now handled in Convert() specializations. |
| for (size_t i = 0; i < (static_cast<size_t>(frames) * channels_); ++i) { |
| dest[i] = DC::Convert(source[i]); |
| } |
| } |
| |
| void FillWithSilence(void* dest, uint32_t frames) const override { |
| SilenceMaker<DType>::Fill(dest, frames * channels_); |
| } |
| }; |
| |
| // Constructor/destructor for the common OutputProducer base class. |
| OutputProducer::OutputProducer(const fuchsia::media::AudioStreamTypePtr& format, |
| uint32_t bytes_per_sample) |
| : channels_(format->channels), |
| bytes_per_sample_(bytes_per_sample), |
| bytes_per_frame_(bytes_per_sample * format->channels) { |
| fidl::Clone(format, &format_); |
| } |
| |
| // Selection routine which will instantiate a particular templatized version of |
| // the output producer. |
| OutputProducerPtr OutputProducer::Select( |
| const fuchsia::media::AudioStreamTypePtr& format) { |
| if (!format || format->channels == 0u) { |
| FXL_LOG(ERROR) << "Invalid output format"; |
| return nullptr; |
| } |
| |
| switch (format->sample_format) { |
| case fuchsia::media::AudioSampleFormat::UNSIGNED_8: |
| return OutputProducerPtr(new OutputProducerImpl<uint8_t>(format)); |
| case fuchsia::media::AudioSampleFormat::SIGNED_16: |
| return OutputProducerPtr(new OutputProducerImpl<int16_t>(format)); |
| case fuchsia::media::AudioSampleFormat::SIGNED_24_IN_32: |
| return OutputProducerPtr(new OutputProducerImpl<int32_t>(format)); |
| case fuchsia::media::AudioSampleFormat::FLOAT: |
| return OutputProducerPtr(new OutputProducerImpl<float>(format)); |
| default: |
| FXL_LOG(ERROR) << "Unsupported output format " |
| << (uint32_t)format->sample_format; |
| return nullptr; |
| } |
| } |
| |
| } // namespace media::audio |