| // Copyright 2013 The Chromium 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 <limits> |
| #include <memory> |
| |
| #include "cobalt/media/base/audio_buffer.h" |
| #include "cobalt/media/base/audio_bus.h" |
| #include "cobalt/media/base/test_helpers.h" |
| #include "starboard/types.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace cobalt { |
| namespace media { |
| |
| static const int kSampleRate = 4800; |
| |
| static void VerifyBusWithOffset(AudioBus* bus, int offset, int frames, |
| float start, float start_offset, |
| float increment) { |
| for (int ch = 0; ch < bus->channels(); ++ch) { |
| const float v = start_offset + start + ch * bus->frames() * increment; |
| for (int i = offset; i < offset + frames; ++i) { |
| ASSERT_FLOAT_EQ(v + i * increment, bus->channel(ch)[i]) << "i=" << i |
| << ", ch=" << ch; |
| } |
| } |
| } |
| |
| static void VerifyBus(AudioBus* bus, int frames, float start, float increment) { |
| VerifyBusWithOffset(bus, 0, frames, start, 0, increment); |
| } |
| |
| static void TrimRangeTest(SampleFormat sample_format) { |
| const ChannelLayout channel_layout = CHANNEL_LAYOUT_4_0; |
| const int channels = ChannelLayoutToChannelCount(channel_layout); |
| const int frames = kSampleRate / 10; |
| const base::TimeDelta timestamp = base::TimeDelta(); |
| const base::TimeDelta duration = base::TimeDelta::FromMilliseconds(100); |
| scoped_refptr<AudioBuffer> buffer = |
| MakeAudioBuffer<float>(sample_format, channel_layout, channels, |
| kSampleRate, 0, 1, frames, timestamp); |
| EXPECT_EQ(frames, buffer->frame_count()); |
| EXPECT_EQ(timestamp, buffer->timestamp()); |
| EXPECT_EQ(duration, buffer->duration()); |
| |
| std::unique_ptr<AudioBus> bus = AudioBus::Create(channels, frames); |
| |
| // Verify all frames before trimming. |
| buffer->ReadFrames(frames, 0, 0, bus.get()); |
| VerifyBus(bus.get(), frames, 0, 1); |
| |
| // Trim 10ms of frames from the middle of the buffer. |
| int trim_start = frames / 2; |
| const int trim_length = kSampleRate / 100; |
| const base::TimeDelta trim_duration = base::TimeDelta::FromMilliseconds(10); |
| buffer->TrimRange(trim_start, trim_start + trim_length); |
| EXPECT_EQ(frames - trim_length, buffer->frame_count()); |
| EXPECT_EQ(timestamp, buffer->timestamp()); |
| EXPECT_EQ(duration - trim_duration, buffer->duration()); |
| bus->Zero(); |
| buffer->ReadFrames(buffer->frame_count(), 0, 0, bus.get()); |
| VerifyBus(bus.get(), trim_start, 0, 1); |
| VerifyBusWithOffset(bus.get(), trim_start, buffer->frame_count() - trim_start, |
| 0, trim_length, 1); |
| |
| // Trim 10ms of frames from the start, which just adjusts the buffer's |
| // internal start offset. |
| buffer->TrimStart(trim_length); |
| trim_start -= trim_length; |
| EXPECT_EQ(frames - 2 * trim_length, buffer->frame_count()); |
| EXPECT_EQ(timestamp, buffer->timestamp()); |
| EXPECT_EQ(duration - 2 * trim_duration, buffer->duration()); |
| bus->Zero(); |
| buffer->ReadFrames(buffer->frame_count(), 0, 0, bus.get()); |
| VerifyBus(bus.get(), trim_start, trim_length, 1); |
| VerifyBusWithOffset(bus.get(), trim_start, buffer->frame_count() - trim_start, |
| trim_length, trim_length, 1); |
| |
| // Trim 10ms of frames from the end, which just adjusts the buffer's frame |
| // count. |
| buffer->TrimEnd(trim_length); |
| EXPECT_EQ(frames - 3 * trim_length, buffer->frame_count()); |
| EXPECT_EQ(timestamp, buffer->timestamp()); |
| EXPECT_EQ(duration - 3 * trim_duration, buffer->duration()); |
| bus->Zero(); |
| buffer->ReadFrames(buffer->frame_count(), 0, 0, bus.get()); |
| VerifyBus(bus.get(), trim_start, trim_length, 1); |
| VerifyBusWithOffset(bus.get(), trim_start, buffer->frame_count() - trim_start, |
| trim_length, trim_length, 1); |
| |
| // Trim another 10ms from the inner portion of the buffer. |
| buffer->TrimRange(trim_start, trim_start + trim_length); |
| EXPECT_EQ(frames - 4 * trim_length, buffer->frame_count()); |
| EXPECT_EQ(timestamp, buffer->timestamp()); |
| EXPECT_EQ(duration - 4 * trim_duration, buffer->duration()); |
| bus->Zero(); |
| buffer->ReadFrames(buffer->frame_count(), 0, 0, bus.get()); |
| VerifyBus(bus.get(), trim_start, trim_length, 1); |
| VerifyBusWithOffset(bus.get(), trim_start, buffer->frame_count() - trim_start, |
| trim_length, trim_length * 2, 1); |
| |
| // Trim off the end using TrimRange() to ensure end index is exclusive. |
| buffer->TrimRange(buffer->frame_count() - trim_length, buffer->frame_count()); |
| EXPECT_EQ(frames - 5 * trim_length, buffer->frame_count()); |
| EXPECT_EQ(timestamp, buffer->timestamp()); |
| EXPECT_EQ(duration - 5 * trim_duration, buffer->duration()); |
| bus->Zero(); |
| buffer->ReadFrames(buffer->frame_count(), 0, 0, bus.get()); |
| VerifyBus(bus.get(), trim_start, trim_length, 1); |
| VerifyBusWithOffset(bus.get(), trim_start, buffer->frame_count() - trim_start, |
| trim_length, trim_length * 2, 1); |
| |
| // Trim off the start using TrimRange() to ensure start index is inclusive. |
| buffer->TrimRange(0, trim_length); |
| trim_start -= trim_length; |
| EXPECT_EQ(frames - 6 * trim_length, buffer->frame_count()); |
| EXPECT_EQ(timestamp, buffer->timestamp()); |
| EXPECT_EQ(duration - 6 * trim_duration, buffer->duration()); |
| bus->Zero(); |
| buffer->ReadFrames(buffer->frame_count(), 0, 0, bus.get()); |
| VerifyBus(bus.get(), trim_start, 2 * trim_length, 1); |
| VerifyBusWithOffset(bus.get(), trim_start, buffer->frame_count() - trim_start, |
| trim_length * 2, trim_length * 2, 1); |
| } |
| |
| TEST(AudioBufferTest, CopyFrom) { |
| const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_MONO; |
| scoped_refptr<AudioBuffer> original_buffer = MakeAudioBuffer<uint8_t>( |
| kSampleFormatU8, kChannelLayout, |
| ChannelLayoutToChannelCount(kChannelLayout), kSampleRate, 1, 1, |
| kSampleRate / 100, base::TimeDelta()); |
| scoped_refptr<AudioBuffer> new_buffer = AudioBuffer::CopyFrom( |
| kSampleFormatU8, original_buffer->channel_layout(), |
| original_buffer->channel_count(), original_buffer->sample_rate(), |
| original_buffer->frame_count(), &original_buffer->channel_data()[0], |
| original_buffer->timestamp()); |
| EXPECT_EQ(original_buffer->frame_count(), new_buffer->frame_count()); |
| EXPECT_EQ(original_buffer->timestamp(), new_buffer->timestamp()); |
| EXPECT_EQ(original_buffer->duration(), new_buffer->duration()); |
| EXPECT_EQ(original_buffer->sample_rate(), new_buffer->sample_rate()); |
| EXPECT_EQ(original_buffer->channel_count(), new_buffer->channel_count()); |
| EXPECT_EQ(original_buffer->channel_layout(), new_buffer->channel_layout()); |
| EXPECT_FALSE(original_buffer->end_of_stream()); |
| } |
| |
| TEST(AudioBufferTest, CreateEOSBuffer) { |
| scoped_refptr<AudioBuffer> buffer = AudioBuffer::CreateEOSBuffer(); |
| EXPECT_TRUE(buffer->end_of_stream()); |
| } |
| |
| TEST(AudioBufferTest, FrameSize) { |
| const uint8_t kTestData[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, |
| 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; |
| const base::TimeDelta kTimestamp = base::TimeDelta::FromMicroseconds(1337); |
| |
| const uint8_t* const data[] = {kTestData}; |
| scoped_refptr<AudioBuffer> buffer = |
| AudioBuffer::CopyFrom(kSampleFormatU8, CHANNEL_LAYOUT_STEREO, 2, |
| kSampleRate, 16, data, kTimestamp); |
| EXPECT_EQ(16, buffer->frame_count()); // 2 channels of 8-bit data |
| |
| buffer = AudioBuffer::CopyFrom(kSampleFormatF32, CHANNEL_LAYOUT_4_0, 4, |
| kSampleRate, 2, data, kTimestamp); |
| EXPECT_EQ(2, buffer->frame_count()); // now 4 channels of 32-bit data |
| } |
| |
| TEST(AudioBufferTest, ReadU8) { |
| const ChannelLayout channel_layout = CHANNEL_LAYOUT_4_0; |
| const int channels = ChannelLayoutToChannelCount(channel_layout); |
| const int frames = 10; |
| const base::TimeDelta start_time; |
| scoped_refptr<AudioBuffer> buffer = |
| MakeAudioBuffer<uint8_t>(kSampleFormatU8, channel_layout, channels, |
| kSampleRate, 128, 1, frames, start_time); |
| std::unique_ptr<AudioBus> bus = AudioBus::Create(channels, frames); |
| buffer->ReadFrames(frames, 0, 0, bus.get()); |
| VerifyBus(bus.get(), frames, 0, 1.0f / 127.0f); |
| |
| // Now read the same data one frame at a time. |
| bus->Zero(); |
| for (int i = 0; i < frames; ++i) buffer->ReadFrames(1, i, i, bus.get()); |
| VerifyBus(bus.get(), frames, 0, 1.0f / 127.0f); |
| } |
| |
| TEST(AudioBufferTest, ReadS16) { |
| const ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; |
| const int channels = ChannelLayoutToChannelCount(channel_layout); |
| const int frames = 10; |
| const base::TimeDelta start_time; |
| scoped_refptr<AudioBuffer> buffer = |
| MakeAudioBuffer<int16_t>(kSampleFormatS16, channel_layout, channels, |
| kSampleRate, 1, 1, frames, start_time); |
| std::unique_ptr<AudioBus> bus = AudioBus::Create(channels, frames); |
| buffer->ReadFrames(frames, 0, 0, bus.get()); |
| VerifyBus(bus.get(), frames, 1.0f / std::numeric_limits<int16_t>::max(), |
| 1.0f / std::numeric_limits<int16_t>::max()); |
| |
| // Now read the same data one frame at a time. |
| bus->Zero(); |
| for (int i = 0; i < frames; ++i) buffer->ReadFrames(1, i, i, bus.get()); |
| VerifyBus(bus.get(), frames, 1.0f / std::numeric_limits<int16_t>::max(), |
| 1.0f / std::numeric_limits<int16_t>::max()); |
| } |
| |
| TEST(AudioBufferTest, ReadS32) { |
| const ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; |
| const int channels = ChannelLayoutToChannelCount(channel_layout); |
| const int frames = 20; |
| const base::TimeDelta start_time; |
| scoped_refptr<AudioBuffer> buffer = |
| MakeAudioBuffer<int32_t>(kSampleFormatS32, channel_layout, channels, |
| kSampleRate, 1, 1, frames, start_time); |
| std::unique_ptr<AudioBus> bus = AudioBus::Create(channels, frames); |
| buffer->ReadFrames(frames, 0, 0, bus.get()); |
| VerifyBus(bus.get(), frames, 1.0f / std::numeric_limits<int32_t>::max(), |
| 1.0f / std::numeric_limits<int32_t>::max()); |
| |
| // Read second 10 frames. |
| bus->Zero(); |
| buffer->ReadFrames(10, 10, 0, bus.get()); |
| VerifyBus(bus.get(), 10, 11.0f / std::numeric_limits<int32_t>::max(), |
| 1.0f / std::numeric_limits<int32_t>::max()); |
| } |
| |
| TEST(AudioBufferTest, ReadF32) { |
| const ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; |
| const int channels = ChannelLayoutToChannelCount(channel_layout); |
| const int frames = 20; |
| const base::TimeDelta start_time; |
| scoped_refptr<AudioBuffer> buffer = |
| MakeAudioBuffer<float>(kSampleFormatF32, channel_layout, channels, |
| kSampleRate, 1.0f, 1.0f, frames, start_time); |
| std::unique_ptr<AudioBus> bus = AudioBus::Create(channels, frames); |
| buffer->ReadFrames(10, 0, 0, bus.get()); |
| VerifyBus(bus.get(), 10, 1, 1); |
| |
| // Read second 10 frames. |
| bus->Zero(); |
| buffer->ReadFrames(10, 10, 0, bus.get()); |
| VerifyBus(bus.get(), 10, 11, 1); |
| } |
| |
| TEST(AudioBufferTest, ReadS16Planar) { |
| const ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; |
| const int channels = ChannelLayoutToChannelCount(channel_layout); |
| const int frames = 20; |
| const base::TimeDelta start_time; |
| scoped_refptr<AudioBuffer> buffer = |
| MakeAudioBuffer<int16_t>(kSampleFormatPlanarS16, channel_layout, channels, |
| kSampleRate, 1, 1, frames, start_time); |
| std::unique_ptr<AudioBus> bus = AudioBus::Create(channels, frames); |
| buffer->ReadFrames(10, 0, 0, bus.get()); |
| VerifyBus(bus.get(), 10, 1.0f / std::numeric_limits<int16_t>::max(), |
| 1.0f / std::numeric_limits<int16_t>::max()); |
| |
| // Read all the frames backwards, one by one. ch[0] should be 20, 19, ... |
| bus->Zero(); |
| for (int i = frames - 1; i >= 0; --i) buffer->ReadFrames(1, i, i, bus.get()); |
| VerifyBus(bus.get(), frames, 1.0f / std::numeric_limits<int16_t>::max(), |
| 1.0f / std::numeric_limits<int16_t>::max()); |
| |
| // Read 0 frames with different offsets. Existing data in AudioBus should be |
| // unchanged. |
| buffer->ReadFrames(0, 0, 0, bus.get()); |
| VerifyBus(bus.get(), frames, 1.0f / std::numeric_limits<int16_t>::max(), |
| 1.0f / std::numeric_limits<int16_t>::max()); |
| buffer->ReadFrames(0, 0, 10, bus.get()); |
| VerifyBus(bus.get(), frames, 1.0f / std::numeric_limits<int16_t>::max(), |
| 1.0f / std::numeric_limits<int16_t>::max()); |
| buffer->ReadFrames(0, 10, 0, bus.get()); |
| VerifyBus(bus.get(), frames, 1.0f / std::numeric_limits<int16_t>::max(), |
| 1.0f / std::numeric_limits<int16_t>::max()); |
| } |
| |
| TEST(AudioBufferTest, ReadF32Planar) { |
| const ChannelLayout channel_layout = CHANNEL_LAYOUT_4_0; |
| const int channels = ChannelLayoutToChannelCount(channel_layout); |
| const int frames = 100; |
| const base::TimeDelta start_time; |
| scoped_refptr<AudioBuffer> buffer = |
| MakeAudioBuffer<float>(kSampleFormatPlanarF32, channel_layout, channels, |
| kSampleRate, 1.0f, 1.0f, frames, start_time); |
| |
| // Read all 100 frames from the buffer. F32 is planar, so ch[0] should be 1, |
| // 2, 3, 4, ..., ch[1] should be 101, 102, 103, ..., and so on for all 4 |
| // channels. |
| std::unique_ptr<AudioBus> bus = AudioBus::Create(channels, 100); |
| buffer->ReadFrames(frames, 0, 0, bus.get()); |
| VerifyBus(bus.get(), frames, 1, 1); |
| |
| // Now read 20 frames from the middle of the buffer. |
| bus->Zero(); |
| buffer->ReadFrames(20, 50, 0, bus.get()); |
| VerifyBus(bus.get(), 20, 51, 1); |
| } |
| |
| TEST(AudioBufferTest, EmptyBuffer) { |
| const ChannelLayout channel_layout = CHANNEL_LAYOUT_4_0; |
| const int channels = ChannelLayoutToChannelCount(channel_layout); |
| const int frames = kSampleRate / 100; |
| const base::TimeDelta start_time; |
| scoped_refptr<AudioBuffer> buffer = AudioBuffer::CreateEmptyBuffer( |
| channel_layout, channels, kSampleRate, frames, start_time); |
| EXPECT_EQ(frames, buffer->frame_count()); |
| EXPECT_EQ(start_time, buffer->timestamp()); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(10), buffer->duration()); |
| EXPECT_FALSE(buffer->end_of_stream()); |
| |
| // Read all 100 frames from the buffer. All data should be 0. |
| std::unique_ptr<AudioBus> bus = AudioBus::Create(channels, frames); |
| buffer->ReadFrames(frames, 0, 0, bus.get()); |
| VerifyBus(bus.get(), frames, 0, 0); |
| } |
| |
| TEST(AudioBufferTest, Trim) { |
| const ChannelLayout channel_layout = CHANNEL_LAYOUT_4_0; |
| const int channels = ChannelLayoutToChannelCount(channel_layout); |
| const int frames = kSampleRate / 10; |
| const base::TimeDelta start_time; |
| const base::TimeDelta duration = base::TimeDelta::FromMilliseconds(100); |
| scoped_refptr<AudioBuffer> buffer = |
| MakeAudioBuffer<float>(kSampleFormatPlanarF32, channel_layout, channels, |
| kSampleRate, 0.0f, 1.0f, frames, start_time); |
| EXPECT_EQ(frames, buffer->frame_count()); |
| EXPECT_EQ(start_time, buffer->timestamp()); |
| EXPECT_EQ(duration, buffer->duration()); |
| |
| const int ten_ms_of_frames = kSampleRate / 100; |
| const base::TimeDelta ten_ms = base::TimeDelta::FromMilliseconds(10); |
| |
| std::unique_ptr<AudioBus> bus = AudioBus::Create(channels, frames); |
| buffer->ReadFrames(buffer->frame_count(), 0, 0, bus.get()); |
| VerifyBus(bus.get(), buffer->frame_count(), 0.0f, 1.0f); |
| |
| // Trim off 10ms of frames from the start. |
| buffer->TrimStart(ten_ms_of_frames); |
| EXPECT_EQ(start_time, buffer->timestamp()); |
| EXPECT_EQ(frames - ten_ms_of_frames, buffer->frame_count()); |
| EXPECT_EQ(duration - ten_ms, buffer->duration()); |
| buffer->ReadFrames(buffer->frame_count(), 0, 0, bus.get()); |
| VerifyBus(bus.get(), buffer->frame_count(), ten_ms_of_frames, 1.0f); |
| |
| // Trim off 10ms of frames from the end. |
| buffer->TrimEnd(ten_ms_of_frames); |
| EXPECT_EQ(start_time, buffer->timestamp()); |
| EXPECT_EQ(frames - 2 * ten_ms_of_frames, buffer->frame_count()); |
| EXPECT_EQ(duration - 2 * ten_ms, buffer->duration()); |
| buffer->ReadFrames(buffer->frame_count(), 0, 0, bus.get()); |
| VerifyBus(bus.get(), buffer->frame_count(), ten_ms_of_frames, 1.0f); |
| |
| // Trim off 40ms more from the start. |
| buffer->TrimStart(4 * ten_ms_of_frames); |
| EXPECT_EQ(start_time, buffer->timestamp()); |
| EXPECT_EQ(frames - 6 * ten_ms_of_frames, buffer->frame_count()); |
| EXPECT_EQ(duration - 6 * ten_ms, buffer->duration()); |
| buffer->ReadFrames(buffer->frame_count(), 0, 0, bus.get()); |
| VerifyBus(bus.get(), buffer->frame_count(), 5 * ten_ms_of_frames, 1.0f); |
| |
| // Trim off the final 40ms from the end. |
| buffer->TrimEnd(4 * ten_ms_of_frames); |
| EXPECT_EQ(0, buffer->frame_count()); |
| EXPECT_EQ(start_time, buffer->timestamp()); |
| EXPECT_EQ(base::TimeDelta(), buffer->duration()); |
| } |
| |
| TEST(AudioBufferTest, TrimRangePlanar) { |
| TrimRangeTest(kSampleFormatPlanarF32); |
| } |
| |
| TEST(AudioBufferTest, TrimRangeInterleaved) { TrimRangeTest(kSampleFormatF32); } |
| |
| } // namespace media |
| } // namespace cobalt |