| // 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. |
| |
| #include "src/media/audio/lib/format/audio_buffer.h" |
| |
| #include <fuchsia/media/cpp/fidl.h> |
| |
| #include <vector> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| namespace media::audio { |
| |
| constexpr auto kSampleFormat = fuchsia::media::AudioSampleFormat::SIGNED_16; |
| static const Format kFormat = Format::Create(fuchsia::media::AudioStreamType{ |
| .sample_format = kSampleFormat, |
| .channels = 2, |
| .frames_per_second = 48000, |
| }) |
| .take_value(); |
| |
| TEST(AudioBufferTest, Basics) { |
| AudioBuffer<kSampleFormat> buffer(kFormat, 10); |
| EXPECT_EQ(buffer.NumFrames(), 10); |
| EXPECT_EQ(buffer.NumSamples(), buffer.NumFrames() * 2); |
| EXPECT_EQ(buffer.NumBytes(), buffer.NumSamples() * static_cast<int64_t>(sizeof(int16_t))); |
| |
| AudioBufferSlice slice1(&buffer); |
| EXPECT_EQ(slice1.NumFrames(), buffer.NumFrames()); |
| EXPECT_EQ(slice1.NumSamples(), buffer.NumSamples()); |
| EXPECT_EQ(slice1.NumBytes(), buffer.NumBytes()); |
| |
| AudioBufferSlice slice2(&buffer, 5, 8); |
| EXPECT_EQ(slice2.NumFrames(), 3); |
| EXPECT_EQ(slice2.NumSamples(), 6); |
| EXPECT_EQ(slice2.NumBytes(), 12); |
| } |
| |
| // Verify Buffer::samples (lvalue and rvalue), and SampleIndex() / SampleAt() for Buffer and Slice |
| TEST(AudioBufferTest, SampleAccess) { |
| AudioBuffer<kSampleFormat> buffer(kFormat, 10); |
| buffer.samples()[0] = 10000; |
| buffer.samples()[1] = 11; |
| buffer.samples()[10] = 222; |
| buffer.samples()[15] = 3333; |
| |
| EXPECT_EQ(buffer.SampleIndex(0, 0), 0); |
| EXPECT_EQ(buffer.SampleIndex(0, 1), 1); |
| EXPECT_EQ(buffer.SampleIndex(5, 0), 10); |
| EXPECT_EQ(buffer.SampleIndex(7, 1), 15); |
| |
| EXPECT_EQ(buffer.SampleAt(0, 0), 10000); |
| EXPECT_EQ(buffer.SampleAt(0, 1), 11); |
| EXPECT_EQ(buffer.SampleAt(5, 0), 222); |
| EXPECT_EQ(buffer.SampleAt(7, 1), 3333); |
| |
| AudioBufferSlice slice(&buffer); |
| EXPECT_EQ(slice.SampleIndex(0, 0), buffer.SampleIndex(0, 0)); |
| EXPECT_EQ(slice.SampleIndex(7, 1), buffer.SampleIndex(7, 1)); |
| |
| EXPECT_EQ(slice.SampleAt(0, 0), buffer.samples()[0]); |
| EXPECT_EQ(slice.SampleAt(5, 0), buffer.samples()[10]); |
| EXPECT_EQ(slice.SampleAt(0, 1), buffer.SampleAt(0, 1)); |
| EXPECT_EQ(slice.SampleAt(7, 1), buffer.SampleAt(7, 1)); |
| |
| AudioBufferSlice slice2(&buffer, 5, 8); |
| EXPECT_EQ(slice2.SampleIndex(0, 1), 11); |
| EXPECT_EQ(slice2.SampleIndex(2, 0), 14); |
| |
| EXPECT_EQ(slice2.SampleAt(0, 0), buffer.SampleAt(5, 0)); |
| EXPECT_EQ(slice2.SampleAt(2, 1), slice.SampleAt(7, 1)); |
| |
| EXPECT_EQ(*(slice2.begin()), buffer.SampleAt(5, 0)); |
| EXPECT_EQ(*(slice2.end() - 1), slice.SampleAt(7, 1)); |
| } |
| |
| // An AudioBufferSlice can be appended to an AudioBuffer |
| TEST(AudioBufferTest, AppendSlice) { |
| // 2 frames with 2 channels per frame, so buffer has samples [0..3] |
| AudioBuffer<kSampleFormat> buffer(kFormat, 2); |
| buffer.samples()[2] = 2345; |
| |
| // buffer2 has 3 frames, thus samples [0..5] |
| AudioBuffer<kSampleFormat> buffer2(kFormat, 3); |
| buffer2.samples()[3] = 3333; |
| |
| // slice contains only buffer2[2..5] |
| AudioBufferSlice slice2(&buffer2, 1, 3); |
| |
| // Appending slice[] will copy buffer2[2..5] to buffer[4..7] |
| buffer.Append(slice2); |
| EXPECT_EQ(buffer.NumFrames(), 4); |
| EXPECT_EQ(buffer.NumSamples(), 8); |
| EXPECT_EQ(buffer.NumBytes(), 16); |
| EXPECT_EQ(buffer.samples()[2], 2345); |
| EXPECT_EQ(buffer.samples()[5], 3333); |
| |
| // buffer2 and slice2 should be unchanged |
| EXPECT_EQ(buffer2.samples()[3], 3333); |
| EXPECT_EQ(buffer2.NumFrames(), 3); |
| EXPECT_EQ(buffer2.NumSamples(), 6); |
| EXPECT_EQ(buffer2.NumBytes(), 12); |
| |
| EXPECT_EQ(slice2.SampleAt(0, 1), 3333); |
| EXPECT_EQ(slice2.NumFrames(), 2); |
| EXPECT_EQ(slice2.NumSamples(), 4); |
| EXPECT_EQ(slice2.NumBytes(), 8); |
| } |
| |
| // Multiple mono AudioBufferSlice can be interleaved to an AudioBuffer |
| TEST(AudioBufferTest, Interleave) { |
| constexpr int32_t kFrameRate = 32000; |
| const Format kFormat1 = Format::Create(fuchsia::media::AudioStreamType{ |
| .sample_format = kSampleFormat, |
| .channels = 1, |
| .frames_per_second = kFrameRate, |
| }) |
| .take_value(); |
| |
| // Mono 20 frames, with values 0..19 |
| AudioBuffer<kSampleFormat> buffer(kFormat1, 20); |
| for (int16_t frame = 0; frame < 20; ++frame) { |
| buffer.samples()[frame] = frame; |
| } |
| |
| // Slice #0 has vals 0..3; #1 has 4..7; #2 8..11; #3 12..15; #4 16..19. |
| auto slices = std::vector<AudioBufferSlice<kSampleFormat>>(); |
| slices.push_back(AudioBufferSlice(&buffer, 0, 4)); |
| slices.push_back(AudioBufferSlice(&buffer, 4, 8)); |
| slices.push_back(AudioBufferSlice(&buffer, 8, 12)); |
| slices.push_back(AudioBufferSlice(&buffer, 12, 16)); |
| slices.push_back(AudioBufferSlice(&buffer, 16, 20)); |
| |
| // Interleave these five slices into a 5-channel file. |
| auto interleaved = AudioBuffer<kSampleFormat>::Interleave(slices); |
| EXPECT_EQ(interleaved.format().channels(), 5); |
| |
| // All characteristics except channels must match the original slices. |
| EXPECT_EQ(interleaved.NumFrames(), 4); |
| EXPECT_EQ(interleaved.format().frames_per_second(), kFrameRate); |
| EXPECT_EQ(interleaved.format().sample_format(), kSampleFormat); |
| |
| // In resulting buffer, first frame has values [0,4,8,12,16], second frame [1,5,9,13,17], etc. |
| for (auto frame = 0u; frame < interleaved.NumFrames(); ++frame) { |
| // Within a frame, values should increase by 4 with each successive channel. |
| EXPECT_EQ(interleaved.SampleAt(frame, 0), static_cast<int16_t>(frame)); |
| EXPECT_EQ(interleaved.SampleAt(frame, 1), interleaved.SampleAt(frame, 0) + 4); |
| EXPECT_EQ(interleaved.SampleAt(frame, 2), interleaved.SampleAt(frame, 1) + 4); |
| EXPECT_EQ(interleaved.SampleAt(frame, 3), interleaved.SampleAt(frame, 2) + 4); |
| EXPECT_EQ(interleaved.SampleAt(frame, 4), interleaved.SampleAt(frame, 3) + 4); |
| } |
| } |
| |
| } // namespace media::audio |