blob: eef158109b7088ec92b05ffb789f5be77eac0bdb [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_VMO_BACKED_BUFFER_H_
#define SRC_MEDIA_AUDIO_LIB_TEST_VMO_BACKED_BUFFER_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 <memory>
#include "src/media/audio/lib/format/audio_buffer.h"
#include "src/media/audio/lib/format/format.h"
namespace media::audio::test {
class VmoBackedBuffer {
public:
VmoBackedBuffer(const Format& format, size_t frame_count)
: format_(format), frame_count_(frame_count) {}
// Allocate an appropriately-sized VMO. The memory is initialized to all zeros.
zx::vmo CreateAndMapVmo(bool writable_on_transfer) {
FX_CHECK(BufferStart() == nullptr);
auto rights = ZX_RIGHT_READ | ZX_RIGHT_MAP | ZX_RIGHT_TRANSFER;
if (writable_on_transfer) {
rights |= ZX_RIGHT_WRITE;
}
zx::vmo vmo;
zx_status_t status = vmo_mapper_.CreateAndMap(SizeBytes(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
nullptr, &vmo, rights);
FX_CHECK(status == ZX_OK) << "VmoMapper:::CreateAndMap failed: " << status;
Clear();
return vmo;
}
// Map a pre-allocated VMO into this buffer. The memory is initialized to all zeros.
void MapVmo(const zx::vmo& vmo) {
FX_CHECK(BufferStart() == nullptr);
uint64_t vmo_size;
zx_status_t status = vmo.get_size(&vmo_size);
ASSERT_EQ(status, ZX_OK) << "VMO get_size failed: " << status;
ASSERT_GE(vmo_size, SizeBytes())
<< "Buffer size " << SizeBytes() << " is greater than VMO size " << vmo_size;
zx_vm_option_t flags = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
status = vmo_mapper_.Map(vmo, 0u, SizeBytes(), flags);
ASSERT_EQ(status, ZX_OK) << "VmoMapper::Map failed: " << status;
Clear();
};
// Reports whether the buffer has been allocated.
bool IsValid() const { return BufferStart() != nullptr; }
// Size of this payload buffer.
size_t SizeBytes() const { return format_.bytes_per_frame() * frame_count_; }
// Take a snapshot of the buffer.
template <fuchsia::media::AudioSampleFormat SampleFormat>
AudioBuffer<SampleFormat> Snapshot() {
AudioBuffer<SampleFormat> out(format_, frame_count_);
memmove(&out.samples()[0], BufferStart(), SizeBytes());
return out;
}
// Take a snapshot of a slice of the buffer. The slice must not include a partial frame.
template <fuchsia::media::AudioSampleFormat SampleFormat>
AudioBuffer<SampleFormat> SnapshotSlice(size_t offset, size_t size_bytes) {
auto bpf = format_.bytes_per_frame();
FX_CHECK(size_bytes % bpf == 0) << "size_bytes " << size_bytes << " bytes_per_frame " << bpf;
AudioBuffer<SampleFormat> out(format_, size_bytes / bpf);
memmove(&out.samples()[0], BufferStart() + offset, size_bytes);
return out;
}
// Returns the offset (in frames) that will be written to by the next call to Append.
size_t GetCurrentOffset() const { return append_offset_frames_; }
// Append a slice to the buffer, advancing the current seek position.
template <fuchsia::media::AudioSampleFormat SampleFormat>
void Append(AudioBufferSlice<SampleFormat> slice) {
WriteAt(append_offset_frames_, slice);
append_offset_frames_ += slice.NumFrames();
}
// Reset the buffer to all zeros and seek to the start of the buffer.
void Clear() {
memset(BufferStart(), 0, SizeBytes());
append_offset_frames_ = 0;
}
// Seek to the given offset of the buffer, relative to the start of the buffer.
void Seek(size_t offset) { append_offset_frames_ = offset; }
// Write a slice to the given absolute frame number. The actual buffer index
// is given by start_frame % buffer_size and the write can wrap-around the end
// of the buffer, however the slice must fit within the buffer.
template <fuchsia::media::AudioSampleFormat SampleFormat>
void WriteAt(size_t frame_number, AudioBufferSlice<SampleFormat> slice) {
FX_CHECK(slice.NumFrames() <= frame_count_);
// First batch.
auto start_index = frame_number % frame_count_;
auto num_frames = std::min(frame_count_ - start_index, slice.NumFrames());
CopyToBuffer(start_index, AudioBufferSlice(slice.buf(), 0, num_frames));
// Optional second batch (wrap-around).
if (num_frames < slice.NumFrames()) {
CopyToBuffer(0, AudioBufferSlice(slice.buf(), num_frames, slice.NumFrames()));
}
}
// Set every sample to the given value.
template <fuchsia::media::AudioSampleFormat SampleFormat>
void Memset(typename SampleFormatTraits<SampleFormat>::SampleT value) {
for (size_t k = 0; k < frame_count_ * format_.channels(); k++) {
auto dst = reinterpret_cast<typename SampleFormatTraits<SampleFormat>::SampleT*>(
BufferStart() + k * format_.bytes_per_sample());
*dst = value;
}
}
private:
uint8_t* BufferStart() const { return reinterpret_cast<uint8_t*>(vmo_mapper_.start()); }
template <fuchsia::media::AudioSampleFormat SampleFormat>
void CopyToBuffer(size_t dst_frame_index, AudioBufferSlice<SampleFormat> slice) {
FX_CHECK(dst_frame_index < frame_count_);
FX_CHECK(dst_frame_index + slice.NumFrames() <= frame_count_);
auto dst = BufferStart() + dst_frame_index * format_.bytes_per_frame();
auto src = &slice.buf()->samples()[slice.SampleIndex(0, 0)];
memmove(dst, src, slice.NumBytes());
}
const Format format_;
const size_t frame_count_;
fzl::VmoMapper vmo_mapper_;
size_t append_offset_frames_ = 0;
};
} // namespace media::audio::test
#endif // SRC_MEDIA_AUDIO_LIB_TEST_VMO_BACKED_BUFFER_H_