blob: af141bc86cf8ef3372fa5c7c0f82f370dca32deb [file] [log] [blame]
// 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.
#ifndef SRC_MEDIA_AUDIO_AUDIO_CORE_MIXER_MIXER_UTILS_H_
#define SRC_MEDIA_AUDIO_AUDIO_CORE_MIXER_MIXER_UTILS_H_
#include <type_traits>
#include <fbl/algorithm.h>
#include "src/media/audio/audio_core/mixer/constants.h"
#include "src/media/audio/audio_core/mixer/gain.h"
namespace media::audio::mixer {
// mixer_utils.h is a collection of inline templated utility functions meant to
// be used by mixer implementations and expanded/optimized at compile time in
// order to produce efficient inner mixing loops for all of the different
// variations of source/destination sample type/channel counts.
//
// ScalerType
//
// Enum used to differentiate between different scaling optimization types.
enum class ScalerType {
MUTED, // Massive attenuation. Just skip data.
NE_UNITY, // Non-unity non-zero gain. Scaling is needed.
EQ_UNITY, // Unity gain. Scaling is not needed.
RAMPING, // Scaling is needed, using a non-constant scaler value
};
//
// SampleNormalizer
//
// Template to read and normalize samples into float32 [ -1.0 , 1.0 ] format.
template <typename SrcSampleType, typename Enable = void>
class SampleNormalizer;
template <typename SrcSampleType>
class SampleNormalizer<SrcSampleType,
typename std::enable_if_t<std::is_same_v<SrcSampleType, uint8_t>>> {
public:
static inline float Read(const SrcSampleType* src) {
return kInt8ToFloat * (static_cast<int32_t>(*src) - kOffsetInt8ToUint8);
}
};
template <typename SrcSampleType>
class SampleNormalizer<SrcSampleType,
typename std::enable_if_t<std::is_same_v<SrcSampleType, int16_t>>> {
public:
static inline float Read(const SrcSampleType* src) { return kInt16ToFloat * (*src); }
};
template <typename SrcSampleType>
class SampleNormalizer<SrcSampleType,
typename std::enable_if_t<std::is_same_v<SrcSampleType, int32_t>>> {
public:
static inline float Read(const SrcSampleType* src) { return kInt24In32ToFloat * (*src); }
};
template <typename SrcSampleType>
class SampleNormalizer<SrcSampleType,
typename std::enable_if_t<std::is_same_v<SrcSampleType, float>>> {
public:
static inline float Read(const SrcSampleType* src) { return *src; }
};
//
// SampleScaler
//
// Template used to scale normalized sample vals by supplied amplitude scalers.
template <ScalerType ScaleType, typename Enable = void>
class SampleScaler;
template <ScalerType ScaleType>
class SampleScaler<ScaleType, typename std::enable_if_t<(ScaleType == ScalerType::MUTED)>> {
public:
static inline float Scale(float, Gain::AScale) { return 0.0f; }
};
template <ScalerType ScaleType>
class SampleScaler<ScaleType, typename std::enable_if_t<(ScaleType == ScalerType::NE_UNITY) ||
(ScaleType == ScalerType::RAMPING)>> {
public:
static inline float Scale(float val, Gain::AScale scale) { return scale * val; }
};
template <ScalerType ScaleType>
class SampleScaler<ScaleType, typename std::enable_if_t<(ScaleType == ScalerType::EQ_UNITY)>> {
public:
static inline float Scale(float val, Gain::AScale) { return val; }
};
//
// SrcReader
//
// Template to read normalized source samples, and combine channels if required.
template <typename SrcSampleType, size_t SrcChanCount, size_t DestChanCount, typename Enable = void>
class SrcReader;
template <typename SrcSampleType, size_t SrcChanCount, size_t DestChanCount>
class SrcReader<SrcSampleType, SrcChanCount, DestChanCount,
typename std::enable_if_t<(SrcChanCount == DestChanCount) || (SrcChanCount == 1) ||
((SrcChanCount == 2) && (DestChanCount == 4))>> {
public:
static inline float Read(const SrcSampleType* src) {
return SampleNormalizer<SrcSampleType>::Read(src);
}
};
template <typename SrcSampleType, size_t SrcChanCount, size_t DestChanCount>
class SrcReader<SrcSampleType, SrcChanCount, DestChanCount,
typename std::enable_if_t<(SrcChanCount == 2) && (DestChanCount == 1)>> {
public:
// This simple 2:1 channel mapping assumes a "LR" stereo configuration for the source channels.
// Each dest frame's single value is essentially the average of the 2 source chans.
static inline float Read(const SrcSampleType* src) {
return 0.5f * (SampleNormalizer<SrcSampleType>::Read(src + 0) +
SampleNormalizer<SrcSampleType>::Read(src + 1));
}
};
template <typename SrcSampleType, size_t SrcChanCount, size_t DestChanCount>
class SrcReader<SrcSampleType, SrcChanCount, DestChanCount,
typename std::enable_if_t<(SrcChanCount == 4) && (DestChanCount == 1)>> {
public:
// This simple 4:1 channel mapping averages the incoming 4 source channels to determine the value
// for the lone destination channel.
static inline float Read(const SrcSampleType* src) {
return 0.25f * (SampleNormalizer<SrcSampleType>::Read(src + 0) +
SampleNormalizer<SrcSampleType>::Read(src + 1) +
SampleNormalizer<SrcSampleType>::Read(src + 2) +
SampleNormalizer<SrcSampleType>::Read(src + 3));
}
};
template <typename SrcSampleType, size_t SrcChanCount, size_t DestChanCount>
class SrcReader<SrcSampleType, SrcChanCount, DestChanCount,
typename std::enable_if_t<(SrcChanCount == 4) && (DestChanCount == 2)>> {
public:
// This simple 4:2 channel mapping assumes a "LRLR" configuration for the 4 source channels (e.g.
// a "four corners" Quad config: FrontL|FrontR|BackL|BackR). Thus in each 4-chan source frame and
// 2-chan dest frame, we mix source chans 0+2 to dest chan 0, and source chans 1+3 to dest chan 1.
static inline float Read(const SrcSampleType* src) {
return 0.5f * (SampleNormalizer<SrcSampleType>::Read(src + 0) +
SampleNormalizer<SrcSampleType>::Read(src + 2));
}
};
//
// Interpolation variants
//
// We specify alpha in fixed-point 19.13: a max val of "1.0" is 0x00002000.
constexpr float kFramesPerPtsSubframe = 1.0f / (1 << kPtsFractionalBits);
// First-order Linear Interpolation formula (Position-fraction):
// out = Pf(S' - S) + S
inline float LinearInterpolate(float A, float B, uint32_t alpha) {
return ((B - A) * kFramesPerPtsSubframe * alpha) + A;
}
//
// DestMixer
//
// Template to mix normalized destination samples with normalized source samples
// based on scaling and accumulation policy.
template <ScalerType ScaleType, bool DoAccumulate, typename Enable = void>
class DestMixer;
template <ScalerType ScaleType, bool DoAccumulate>
class DestMixer<ScaleType, DoAccumulate, typename std::enable_if_t<DoAccumulate == false>> {
public:
static inline constexpr float Mix(float, float sample, Gain::AScale scale) {
return SampleScaler<ScaleType>::Scale(sample, scale);
}
};
template <ScalerType ScaleType, bool DoAccumulate>
class DestMixer<ScaleType, DoAccumulate, typename std::enable_if_t<DoAccumulate == true>> {
public:
static inline constexpr float Mix(float dest, float sample, Gain::AScale scale) {
return SampleScaler<ScaleType>::Scale(sample, scale) + dest;
}
};
} // namespace media::audio::mixer
#endif // SRC_MEDIA_AUDIO_AUDIO_CORE_MIXER_MIXER_UTILS_H_