blob: 847489133a582fafe9034a54026fb39985b2031e [file] [log] [blame]
// Copyright 2020 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_TEST_VIRTUAL_DEVICE_H_
#define SRC_MEDIA_AUDIO_LIB_TEST_VIRTUAL_DEVICE_H_
#include <fuchsia/media/cpp/fidl.h>
#include <fuchsia/virtualaudio/cpp/fidl.h>
#include <lib/fzl/vmo-mapper.h>
#include <lib/zx/vmo.h>
#include <zircon/device/audio.h>
#include <memory>
#include "src/lib/testing/loop_fixture/real_loop_fixture.h"
#include "src/media/audio/lib/format/audio_buffer.h"
#include "src/media/audio/lib/test/hermetic_audio_environment.h"
#include "src/media/audio/lib/test/test_fixture.h"
#include "src/media/audio/lib/test/vmo_backed_buffer.h"
#include "src/media/audio/lib/timeline/timeline_function.h"
namespace media::audio::test {
struct DevicePlugProperties {
zx::time plug_change_time;
bool plugged;
bool hardwired;
bool can_notify;
};
struct DeviceClockProperties {
int32_t domain;
int32_t initial_rate_adjustment_ppm;
};
// This class is thread hostile: none of its methods can be called concurrently.
template <class Interface>
class VirtualDevice {
public:
static constexpr uint32_t kNotifyMs = 10;
static constexpr uint32_t kFifoDepthBytes = 0;
static constexpr auto kExternalDelay = zx::msec(0);
~VirtualDevice();
fidl::InterfacePtr<Interface>& fidl() { return fidl_; }
size_t frame_count() const { return frame_count_; }
uint64_t token() const { return token_; }
void set_token(uint64_t t) { token_ = t; }
// Reports whether the device has started.
bool Ready() const { return received_start_; }
// Returns a timestamp in the future that corresponds to byte 0 of the ring buffer.
// The returned time is guaranteed to be at least min_time in the future, even if that
// means waiting for more than one round trip through the ring buffer.
zx::time NextSynchronizedTimestamp(zx::time min_time = zx::time(0)) const;
// Returns the absolute ring buffer frame number corresponding to the given time. The
// "absolute" frame number starts at zero and increases monotonically. The actual ring
// buffer offset is given by absolute_frame_number % ring_buffer_size.
int64_t RingBufferFrameAtTimestamp(zx::time ref_time) const;
// For validating properties exported by inspect.
size_t inspect_id() const { return inspect_id_; }
protected:
VirtualDevice(TestFixture* fixture, HermeticAudioEnvironment* environment,
const audio_stream_unique_id_t& device_id, Format format, size_t frame_count,
size_t inspect_id, std::optional<DevicePlugProperties> plug_properties,
float expected_gain_db,
std::optional<DeviceClockProperties> device_clock_properties);
void ResetEvents();
void WatchEvents();
const Format format_;
const size_t frame_count_;
const size_t inspect_id_;
const float expected_gain_db_;
fidl::InterfacePtr<Interface> fidl_;
audio_sample_format_t driver_format_;
zx::vmo rb_vmo_;
VmoBackedBuffer rb_;
bool received_set_format_ = false;
bool received_start_ = false;
bool received_stop_ = false;
zx::time start_time_;
zx::time stop_time_;
TimelineFunction running_pos_to_ref_time_;
uint64_t stop_pos_ = 0;
uint64_t ring_pos_ = 0;
uint64_t running_ring_pos_ = 0;
uint64_t token_;
};
using VirtualOutputImpl = VirtualDevice<fuchsia::virtualaudio::Output>;
using VirtualInputImpl = VirtualDevice<fuchsia::virtualaudio::Input>;
template <fuchsia::media::AudioSampleFormat SampleFormat>
class VirtualOutput : public VirtualOutputImpl {
public:
using SampleT = typename AudioBuffer<SampleFormat>::SampleT;
// Take a snapshot of the device's ring buffer.
AudioBuffer<SampleFormat> SnapshotRingBuffer() { return rb_.Snapshot<SampleFormat>(); }
// Don't call this directly. Use HermeticAudioTest::CreateOutput so the object is
// appropriately bound into the test environment.
VirtualOutput(TestFixture* fixture, HermeticAudioEnvironment* environment,
const audio_stream_unique_id_t& device_id, Format format, size_t frame_count,
size_t inspect_id, std::optional<DevicePlugProperties> plug_properties,
float expected_gain_db,
std::optional<DeviceClockProperties> device_clock_properties)
: VirtualDevice(fixture, environment, device_id, format, frame_count, inspect_id,
plug_properties, expected_gain_db, device_clock_properties) {}
};
template <fuchsia::media::AudioSampleFormat SampleFormat>
class VirtualInput : public VirtualInputImpl {
public:
using SampleT = typename AudioBuffer<SampleFormat>::SampleT;
// Write a slice to the ring buffer at the given absolute frame number.
void WriteRingBufferAt(size_t ring_pos_in_frames, AudioBufferSlice<SampleFormat> slice) {
rb_.WriteAt<SampleFormat>(ring_pos_in_frames, slice);
}
// Don't call this directly. Use HermeticAudioTest::CreateInput so the object is
// appropriately bound into the test environment.
VirtualInput(TestFixture* fixture, HermeticAudioEnvironment* environment,
const audio_stream_unique_id_t& device_id, Format format, size_t frame_count,
size_t inspect_id, std::optional<DevicePlugProperties> plug_properties,
float expected_gain_db, std::optional<DeviceClockProperties> device_clock_properties)
: VirtualDevice(fixture, environment, device_id, format, frame_count, inspect_id,
plug_properties, expected_gain_db, device_clock_properties) {}
};
} // namespace media::audio::test
#endif // SRC_MEDIA_AUDIO_LIB_TEST_VIRTUAL_DEVICE_H_