blob: 6e820633d86ff9dc3745dfebf54dddba5fa721ed [file] [log] [blame]
// Copyright 2021 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_EFFECTS_STAGE_V2_H_
#define SRC_MEDIA_AUDIO_AUDIO_CORE_EFFECTS_STAGE_V2_H_
#include <fidl/fuchsia.audio.effects/cpp/wire.h>
#include <lib/fpromise/result.h>
#include <lib/service/llcpp/service.h>
#include <memory>
#include "src/media/audio/audio_core/reusable_buffer.h"
#include "src/media/audio/audio_core/stream.h"
#include "src/media/audio/audio_core/utils.h"
#include "src/media/audio/lib/clock/audio_clock.h"
namespace media::audio {
// EffectsStageV2 produces frames by passing a source stream through a FIDL effects processor.
class EffectsStageV2 : public ReadableStream {
public:
static fpromise::result<std::shared_ptr<EffectsStageV2>, zx_status_t> Create(
fuchsia_audio_effects::wire::ProcessorConfiguration config,
std::shared_ptr<ReadableStream> source);
// |media::audio::ReadableStream|
TimelineFunctionSnapshot ref_time_to_frac_presentation_frame() const override;
AudioClock& reference_clock() override { return source_->reference_clock(); }
void SetPresentationDelay(zx::duration external_delay) override;
// Manages buffers for the FIDL connection.
// Exported so it can be tested and used in tests.
struct FidlBuffers {
// Will crash if the VMOs are not R+W mappable.
static FidlBuffers Create(const fuchsia_mem::wire::Range& input_range,
const fuchsia_mem::wire::Range& output_range);
void* input;
void* output;
size_t input_size;
size_t output_size;
// This will have one entry if the input and output buffers share the same VMO,
// else it will have two entries.
std::vector<fbl::RefPtr<RefCountedVmoMapper>> mappers;
};
private:
EffectsStageV2(fuchsia_audio_effects::wire::ProcessorConfiguration config,
std::shared_ptr<ReadableStream> source);
// |media::audio::ReadableStream|
std::optional<ReadableStream::Buffer> ReadLockImpl(ReadLockContext& ctx, Fixed dest_frame,
int64_t frame_count) override;
void TrimImpl(Fixed dest_frame) override;
int64_t FillCache(ReadLockContext& ctx, Fixed dest_frame, int64_t frame_count);
void CallProcess(ReadLockContext& ctx, StreamUsageMask source_usage_mask,
float source_total_applied_gain_db);
zx::duration ComputeIntrinsicMinLeadTime() const;
std::shared_ptr<ReadableStream> source_;
fidl::WireSyncClient<fuchsia_audio_effects::Processor> processor_;
FidlBuffers fidl_buffers_;
const int64_t max_frames_per_call_; // guaranteed > 0
const int64_t block_size_frames_; // guaranteed > 0
const int64_t output_shift_frames_; // how much the output is shifted relative to the input
// We must process frames in batches that are multiples of block_size_frames_. Our cache
// accumulates data from source_ until we've buffered at least one full batch, at which
// point we run the effect and store the output of the effect in cache.dest_buffer.
// The cache lives until we Trim past source_buffer_.end().
//
// For example:
//
// +------------------------+
// | source_buffer_ |
// +------------------------+
// ^ ^ ^ ^ ^
// A B C D E
//
// 1. Caller asks for frames [A,B). Assume D = A+block_size. We read frames [A,D) from
// source_ into source_buffer_, then process those frames, leaving the processed
// data in cache_.dest_buffer. We return processed frames [A,B).
//
// 2. Caller asks for frames [B,C). This intersects source_buffer_, so we return
// processed frames [B,C).
//
// 3. Caller asks for frames [C,E). This intersects source_buffer_, so we return processed
// frames [C,D). When the caller is done with those frames, we receive a Trim(D) call
// (via ReadableStream::Buffer::~Buffer), which sets cache_ to std::nullopt.
//
// 4. Caller asks for frames [D,E). The above process repeats.
struct Cache {
// Properties of source_buffer_.
StreamUsageMask source_usage_mask;
float source_total_applied_gain_db;
// Destination frames after processing. This refers to the same set of frames as source_buffer_,
// and if the effect processes in-place, it points at source_buffer_.payload().
void* dest_buffer;
};
std::optional<Cache> cache_;
// This is non-empty iff cache_ != std::nullopt.
ReusableBuffer source_buffer_;
// Buffer holding one pair of encoded FIDL Process request and response message.
fidl::SyncClientBuffer<fuchsia_audio_effects::Processor::Process> process_buffer_;
};
} // namespace media::audio
#endif // SRC_MEDIA_AUDIO_AUDIO_CORE_EFFECTS_STAGE_V2_H_