blob: ac924bad0a50e023aefc1c10517ab1d4ba95dbcd [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_AUDIO_OUTPUT_H_
#define SRC_MEDIA_AUDIO_AUDIO_CORE_AUDIO_OUTPUT_H_
#include <lib/async/cpp/task.h>
#include <lib/async/cpp/time.h>
#include <lib/zx/time.h>
#include <optional>
#include "src/media/audio/audio_core/audio_device.h"
#include "src/media/audio/audio_core/output_pipeline.h"
#include "src/media/audio/audio_core/process_config.h"
#include "src/media/audio/audio_core/reporter.h"
#include "src/media/audio/lib/timeline/timeline_function.h"
namespace media::audio {
class Packet;
class AudioOutput : public AudioDevice {
public:
~AudioOutput() override = default;
fit::promise<void, fuchsia::media::audio::UpdateEffectError> UpdateEffect(
const std::string& instance_name, const std::string& config) override;
// Replace the existing DeviceProfile and restart the OutputPipeline, for tuning purposes.
fit::promise<void, zx_status_t> UpdateDeviceProfile(
const DeviceConfig::OutputDeviceProfile::Parameters& params) override;
OutputPipeline* output_pipeline() const { return pipeline_.get(); }
// |media::audio::AudioDevice|
void SetGainInfo(const fuchsia::media::AudioGainInfo& info,
fuchsia::media::AudioGainValidFlags set_flags) override;
protected:
AudioOutput(const std::string& name, ThreadingModel* threading_model, DeviceRegistry* registry,
LinkMatrix* link_matrix);
AudioOutput(const std::string& name, ThreadingModel* threading_model, DeviceRegistry* registry,
LinkMatrix* link_matrix, std::unique_ptr<AudioDriver>);
void Process() FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain().token());
Reporter::OutputDevice& reporter() { return *reporter_; }
// |media::audio::AudioObject|
//
// If we're initializing a source link, then we're connecting a renderer to this output. Else
// if we're initializing a dest link, then we're being connected as a loopback so we should return
// our loopback stream.
fit::result<std::pair<std::shared_ptr<Mixer>, ExecutionDomain*>, zx_status_t>
InitializeSourceLink(const AudioObject& source, std::shared_ptr<ReadableStream> stream) final;
void CleanupSourceLink(const AudioObject& source, std::shared_ptr<ReadableStream> stream) final;
fit::result<std::shared_ptr<ReadableStream>, zx_status_t> InitializeDestLink(
const AudioObject& dest) override;
// Mark this output as needing to be mixed at the specified future time.
// async PostForTime requires a time in the CLOCK_MONOTONIC timebase, so we use that here.
void SetNextSchedTimeMono(zx::time next_sched_time_mono) {
next_sched_time_mono_ = next_sched_time_mono;
}
inline void ClearNextSchedTime() { next_sched_time_mono_ = std::nullopt; }
void SetupMixTask(const DeviceConfig::OutputDeviceProfile& profile, size_t max_block_size_frames,
TimelineFunction device_reference_clock_to_fractional_frame)
FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain().token());
virtual std::unique_ptr<OutputPipeline> CreateOutputPipeline(
const PipelineConfig& config, const VolumeCurve& volume_curve, size_t max_block_size_frames,
TimelineFunction device_reference_clock_to_fractional_frame, AudioClock& ref_clock);
void Cleanup() override FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain().token());
struct FrameSpan {
int64_t start;
uint64_t length;
bool is_mute;
};
// Start mixing frames for a periodic mix job. This is called internally during the periodic mix
// task for this output. Implementations can control mix behavior in the following ways:
//
// If |std::nullopt| is returned, then no frames will be mixed. Instead all inputs will be trimmed
// such that any client audio packets that will have been fully consumed by |device_ref_time| will
// still be released. There will be no call to |FinishMixJob|.
//
// If the retuned optional contains a FrameSpan with |is_mute| set to true, then no frames will
// be mixed. Instead all inputs will be trimmed such that any client audio packets that will have
// been fully consumed by |device_ref_time| will still be released. |FinishMixJob| will be called
// with the returned FrameSpan and a null payload buffer. It is the responsibility of
// |FinishMixJob| to produce the silence for the FrameSpan.
//
// If the retuned optional contains a FrameSpan with |is_mute| set to false, then the mix
// pipeline will be advanced by the requested frame region. |FinishMixJob| will be called with a
// FrameSpan that is 'at most' as long as the span in |StartMixJob|, but this length may be
// reduced if the pipeline is unable to fill a single, contiguous buffer will all the frames
// requested. If the entire region in StartMixJob is unable to be populated in a single pass, then
// StartMixJob will be called again to process any remaining frames.
virtual std::optional<AudioOutput::FrameSpan> StartMixJob(zx::time device_ref_time)
FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain().token()) = 0;
// Finish a mix job by moving the frame range span |span| into the hardware ring buffer using
// |buffer| as a source. |span.start| should be a value that was provided in |StartMixJob| and
// |span.length| should be at most the value returned from |StartMixJob|, but may be adjusted
// downwards if the full range cannot be produced.
//
// If |span.is_mute| is false, |buffer| must contain |span.length * channels| floating point
// samples of audio data.
//
// If |span.is_mute| is true, then |buffer| is ignored and instead silence will be inserted
// into the ring buffer for the frame range in |span|.
virtual void FinishMixJob(const AudioOutput::FrameSpan& span, const float* buffer)
FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain().token()) = 0;
// The maximum amount of time it can take to run all pending mix jobs when a device
// wakes up to process pending jobs.
virtual zx::duration MixDeadline() const FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain().token()) = 0;
private:
// Timer used to schedule periodic mixing.
void MixTimerThunk() {
OBTAIN_EXECUTION_DOMAIN_TOKEN(token, &mix_domain());
Process();
}
async::TaskClosureMethod<AudioOutput, &AudioOutput::MixTimerThunk> mix_timer_
FXL_GUARDED_BY(mix_domain().token()){this};
std::optional<zx::time> next_sched_time_mono_;
size_t max_block_size_frames_;
std::unique_ptr<OutputPipeline> pipeline_;
Reporter::Container<Reporter::OutputDevice, Reporter::kObjectsToCache>::Ptr reporter_;
ThreadCpuTimer cpu_timer_;
};
} // namespace media::audio
#endif // SRC_MEDIA_AUDIO_AUDIO_CORE_AUDIO_OUTPUT_H_