| // Copyright 2022 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_LIB_PROCESSING_SAMPLER_H_ |
| #define SRC_MEDIA_AUDIO_LIB_PROCESSING_SAMPLER_H_ |
| |
| #include <cstdint> |
| #include <optional> |
| |
| #include "src/media/audio/lib/format2/fixed.h" |
| #include "src/media/audio/lib/processing/gain.h" |
| |
| namespace media_audio { |
| |
| // Interface that takes an array of source samples in any format, and processes corresponding array |
| // of destination samples in normalized 32-bit float format with a specified gain scale applied. |
| // |
| // The source and destination samples can be in different frame rates, channel configurations or |
| // sample formats. The samples that are processed from the source format will be converted into the |
| // destination format accordingly during `Process` call with respect to the derived implementation. |
| // |
| // Each `Process` call assumes a contiguous stream of source and destination samples. The caller |
| // must ensure that the requested source and destination samples are aligned with respect to their |
| // audio format and timeline. |
| // |
| // Each sampler define their positive and negative lengths of the filter that are expressed in |
| // fixed-point fractional source subframe units. These lengths convey which source frames will be |
| // referenced by the filter, when producing corresponding destination frames for a specific instant |
| // in time. |
| // |
| // Positive filter length refers to how far forward (positively) the filter looks, from the PTS in |
| // question; while negative filter length refers to how far backward (negatively) the filter looks, |
| // from that same PTS. The center frame position is included in the length. For example, a pure |
| // "sample and hold" sampler might have a positive filter length of `Fixed::FromRaw(1)` and a |
| // negative filter length of `kOneFrame`: |
| // |
| // center |
| // VV |
| // *************** |
| // ^ ^^ |
| // +---++ |
| // n p |
| // |
| // This class is not safe for concurrent use. |
| class Sampler { |
| public: |
| // Wraps source data. |
| struct Source { |
| // Pointer to the array of interleaved source samples in any sample format. |
| const void* samples; |
| // Pointer to the fractional offset from the start of source `samples` in frames, at which the |
| // first source frame should be processed. This offset will be updated once `Process` is |
| // finished in order to indicate the next frame offset to be processed in a future call. |
| Fixed* frame_offset_ptr; |
| // Number of source frames to be processed. |
| int64_t frame_count; |
| }; |
| |
| // Wraps destination data. |
| struct Dest { |
| // Pointer to the array of interleaved destination samples in normalized 32-bit float format. |
| float* samples; |
| // Pointer to the integral offset from the start of destination `samples` in frames, at which |
| // the first destination frame should be processed. This offset will be updated once `Process` |
| // is finished in order to indicate the next frame offset to be processed in a future call. |
| int64_t* frame_offset_ptr; |
| // Number of destination frames to be processed. |
| int64_t frame_count; |
| }; |
| |
| // Gain to be applied to the processed destination data. |
| struct Gain { |
| // Gain type. |
| GainType type = GainType::kUnity; |
| |
| union { |
| // Constant gain scale. This will be valid iff the gain `type != GainType::kRamping`. |
| float scale = kUnityGainScale; |
| // Pointer to the array of gain scale ramp, where each value represents the gain scale for |
| // each destination frame. The length of this ramp must match the destination frame count. |
| // This will be valid iff the gain `type == GainType::kRamping`. |
| const float* scale_ramp; |
| }; |
| }; |
| |
| // Default destructor. |
| virtual ~Sampler() = default; |
| |
| // Eagerly precomputes any needed data. If not called, that data will be lazily computed on the |
| // first call to `Process`. |
| // TODO(fxbug.dev/45074): This is for tests only and can be removed once filter creation is eager. |
| virtual void EagerlyPrepare() = 0; |
| |
| // Processes `source` into `dest` with `gain`. |
| virtual void Process(Source source, Dest dest, Gain gain, bool accumulate) = 0; |
| |
| // Returns positive filter length in frames. |
| Fixed pos_filter_length() const { return pos_filter_length_; } |
| |
| // Returns negative filter length in frames. |
| Fixed neg_filter_length() const { return neg_filter_length_; } |
| |
| protected: |
| Sampler(Fixed pos_filter_length, Fixed neg_filter_length) |
| : pos_filter_length_(pos_filter_length), neg_filter_length_(neg_filter_length) {} |
| |
| // Ceils `frac_position` in frames. |
| static constexpr int64_t Ceiling(int64_t frac_position) { |
| return ((frac_position - 1) >> Fixed::Format::FractionalBits) + 1; |
| } |
| |
| // Floors `frac_position` in frames. |
| static constexpr int64_t Floor(int64_t frac_position) { |
| return frac_position >> Fixed::Format::FractionalBits; |
| } |
| |
| private: |
| const Fixed pos_filter_length_; |
| const Fixed neg_filter_length_; |
| }; |
| |
| // Mixes `source_sample` to `dest_sample_ptr` with a gain `scale` of `Type`. |
| template <GainType Type, bool Accumulate> |
| inline void MixSample(float source_sample, float* dest_sample_ptr, float scale) { |
| if constexpr (Accumulate) { |
| if constexpr (Type != GainType::kSilent) { |
| *dest_sample_ptr += ApplyGain<Type>(source_sample, scale); |
| } |
| } else { |
| *dest_sample_ptr = ApplyGain<Type>(source_sample, scale); |
| } |
| } |
| |
| } // namespace media_audio |
| |
| #endif // SRC_MEDIA_AUDIO_LIB_PROCESSING_SAMPLER_H_ |