| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <algorithm> |
| #include <limits> |
| #include <memory> |
| |
| #include "chromecast/media/audio/audio_fader.h" |
| #include "media/base/audio_bus.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using testing::_; |
| |
| namespace chromecast { |
| namespace media { |
| |
| namespace { |
| |
| const int kNumChannels = 2; |
| const int kFadeFrames = 128; |
| const int kSampleRate = 48000; |
| |
| std::unique_ptr<::media::AudioBus> CreateAudioBus(int num_frames) { |
| auto buffer = ::media::AudioBus::Create(kNumChannels, num_frames); |
| // Fill with invalid values. |
| for (int c = 0; c < buffer->channels(); ++c) { |
| float* channel_data = buffer->channel(c); |
| std::fill_n(channel_data, num_frames, -2.0f); |
| } |
| return buffer; |
| } |
| |
| class TestFaderSource : public AudioProvider { |
| public: |
| TestFaderSource() |
| : max_fill_frames_(std::numeric_limits<int>::max()), |
| total_requested_frames_(0), |
| last_requested_frames_(0), |
| last_filled_frames_(0) {} |
| |
| TestFaderSource(const TestFaderSource&) = delete; |
| TestFaderSource& operator=(const TestFaderSource&) = delete; |
| |
| // AudioProvider implementation: |
| int FillFrames(int num_frames, |
| int64_t playout_timestamp, |
| float* const* channel_data) override { |
| last_requested_frames_ = num_frames; |
| total_requested_frames_ += num_frames; |
| |
| int count = std::min(num_frames, max_fill_frames_); |
| last_filled_frames_ = count; |
| |
| for (int c = 0; c < kNumChannels; ++c) { |
| std::fill_n(channel_data[c], count, 1.0f); |
| } |
| |
| return count; |
| } |
| size_t num_channels() const override { return kNumChannels; } |
| int sample_rate() const override { return kSampleRate; } |
| |
| void set_max_fill_frames(int frames) { max_fill_frames_ = frames; } |
| |
| int total_requested_frames() const { return total_requested_frames_; } |
| int last_requested_frames() const { return last_requested_frames_; } |
| int last_filled_frames() const { return last_filled_frames_; } |
| |
| private: |
| int max_fill_frames_; |
| int total_requested_frames_; |
| int last_requested_frames_; |
| int last_filled_frames_; |
| }; |
| |
| } // namespace |
| |
| TEST(AudioFaderTest, Startup) { |
| TestFaderSource source; |
| AudioFader fader(&source, kFadeFrames, 1.0); |
| |
| // Fader has no buffered frames initially. |
| EXPECT_EQ(fader.buffered_frames(), 0); |
| |
| const int kFillSize = kFadeFrames * 2; |
| int frames_needed = fader.FramesNeededFromSource(kFillSize); |
| // The fader should fill its internal buffer, plus the size of the request. |
| EXPECT_EQ(frames_needed, kFadeFrames + kFillSize); |
| |
| auto dest = CreateAudioBus(kFillSize); |
| float* channels[kNumChannels]; |
| for (int c = 0; c < kNumChannels; ++c) { |
| channels[c] = dest->channel(c); |
| } |
| EXPECT_EQ(fader.FillFrames(kFillSize, 0, channels), kFillSize); |
| |
| // Test that FramesNeededFromSource() works correctly. |
| EXPECT_EQ(source.total_requested_frames(), frames_needed); |
| |
| // Fader's internal buffer should be full. |
| EXPECT_EQ(fader.buffered_frames(), kFadeFrames); |
| |
| // Data should be faded in. |
| EXPECT_EQ(dest->channel(0)[0], 0.0f); |
| EXPECT_EQ(dest->channel(0)[kFadeFrames], 1.0f); |
| } |
| |
| TEST(AudioFaderTest, FadeInOver2Buffers) { |
| TestFaderSource source; |
| AudioFader fader(&source, kFadeFrames, 1.0); |
| |
| // Fader has no buffered frames initially. |
| EXPECT_EQ(fader.buffered_frames(), 0); |
| |
| const int kFillSize = kFadeFrames * 2 / 3; |
| int frames_needed = fader.FramesNeededFromSource(kFillSize); |
| auto dest = CreateAudioBus(kFillSize); |
| float* channels[kNumChannels]; |
| for (int c = 0; c < kNumChannels; ++c) { |
| channels[c] = dest->channel(c); |
| } |
| EXPECT_EQ(fader.FillFrames(kFillSize, 0, channels), kFillSize); |
| |
| // Fader's internal buffer should be full. |
| EXPECT_EQ(fader.buffered_frames(), kFadeFrames); |
| // Data should be partially faded in. |
| EXPECT_EQ(dest->channel(0)[0], 0.0f); |
| EXPECT_GT(dest->channel(0)[kFillSize - 1], 0.0f); |
| EXPECT_LT(dest->channel(0)[kFillSize - 1], 1.0f); |
| |
| // Fill more data. |
| frames_needed += fader.FramesNeededFromSource(kFillSize); |
| EXPECT_EQ(fader.FillFrames(kFillSize, 0, channels), kFillSize); |
| EXPECT_EQ(fader.buffered_frames(), kFadeFrames); |
| |
| // Test that FramesNeededFromSource() works correctly. |
| EXPECT_EQ(source.total_requested_frames(), frames_needed); |
| |
| // Fader's internal buffer should be full. |
| EXPECT_EQ(fader.buffered_frames(), kFadeFrames); |
| |
| // Data should be faded in. |
| EXPECT_EQ(dest->channel(0)[kFillSize - 1], 1.0f); |
| } |
| |
| TEST(AudioFaderTest, ContinuePlaying) { |
| TestFaderSource source; |
| AudioFader fader(&source, kFadeFrames, 1.0); |
| |
| // Fader has no buffered frames initially. |
| EXPECT_EQ(fader.buffered_frames(), 0); |
| |
| const int kFillSize = kFadeFrames * 2; |
| auto dest = CreateAudioBus(kFillSize); |
| |
| int frames_needed = fader.FramesNeededFromSource(kFillSize); |
| float* channels[kNumChannels]; |
| for (int c = 0; c < kNumChannels; ++c) { |
| channels[c] = dest->channel(c); |
| } |
| EXPECT_EQ(fader.FillFrames(kFillSize, 0, channels), kFillSize); |
| |
| // Data should be faded in. |
| EXPECT_EQ(dest->channel(0)[kFadeFrames], 1.0f); |
| |
| // Now request more data. Data should remain fully faded in. |
| frames_needed += fader.FramesNeededFromSource(kFillSize); |
| EXPECT_EQ(fader.FillFrames(kFillSize, 0, channels), kFillSize); |
| EXPECT_EQ(dest->channel(0)[0], 1.0f); |
| |
| // Test that FramesNeededFromSource() works correctly. |
| EXPECT_EQ(source.total_requested_frames(), frames_needed); |
| |
| // Fader's internal buffer should be full. |
| EXPECT_EQ(fader.buffered_frames(), kFadeFrames); |
| } |
| |
| TEST(AudioFaderTest, FadeOut) { |
| TestFaderSource source; |
| AudioFader fader(&source, kFadeFrames, 1.0); |
| |
| // Fader has no buffered frames initially. |
| EXPECT_EQ(fader.buffered_frames(), 0); |
| |
| const int kFillSize = kFadeFrames * 2; |
| auto dest = CreateAudioBus(kFillSize); |
| |
| int frames_needed = fader.FramesNeededFromSource(kFillSize); |
| float* channels[kNumChannels]; |
| for (int c = 0; c < kNumChannels; ++c) { |
| channels[c] = dest->channel(c); |
| } |
| EXPECT_EQ(fader.FillFrames(kFillSize, 0, channels), kFillSize); |
| |
| // Data should be faded in. |
| EXPECT_EQ(dest->channel(0)[kFadeFrames], 1.0f); |
| |
| // Now request more data. Data should remain fully faded in. |
| frames_needed += fader.FramesNeededFromSource(kFillSize); |
| EXPECT_EQ(fader.FillFrames(kFillSize, 0, channels), kFillSize); |
| EXPECT_EQ(dest->channel(0)[0], 1.0f); |
| |
| // Now make the source not provide enough data. |
| EXPECT_GT(fader.FramesNeededFromSource(kFillSize), 0); |
| source.set_max_fill_frames(0); |
| frames_needed += fader.FramesNeededFromSource(kFillSize); |
| int filled = fader.FillFrames(kFillSize, 0, channels); |
| EXPECT_EQ(filled, kFadeFrames); |
| |
| // Test that FramesNeededFromSource() works correctly. |
| EXPECT_EQ(source.total_requested_frames(), frames_needed); |
| |
| // Data should be faded out. |
| EXPECT_EQ(dest->channel(0)[0], 1.0f); |
| EXPECT_LT(dest->channel(0)[filled - 1], 0.1f); |
| EXPECT_GE(dest->channel(0)[filled - 1], 0.0f); |
| |
| // Fader's internal buffer should be empty since we are fully faded out. |
| EXPECT_EQ(fader.buffered_frames(), 0); |
| } |
| |
| TEST(AudioFaderTest, FadeOutPartially) { |
| TestFaderSource source; |
| AudioFader fader(&source, kFadeFrames, 1.0); |
| |
| // Fader has no buffered frames initially. |
| EXPECT_EQ(fader.buffered_frames(), 0); |
| |
| const int kFillSize = kFadeFrames * 2; |
| auto dest = CreateAudioBus(kFillSize); |
| |
| int frames_needed = fader.FramesNeededFromSource(kFillSize); |
| float* channels[kNumChannels]; |
| for (int c = 0; c < kNumChannels; ++c) { |
| channels[c] = dest->channel(c); |
| } |
| EXPECT_EQ(fader.FillFrames(kFillSize, 0, channels), kFillSize); |
| |
| // Data should be faded in. |
| EXPECT_EQ(dest->channel(0)[kFadeFrames], 1.0f); |
| |
| // Now request more data. Data should remain fully faded in. |
| frames_needed += fader.FramesNeededFromSource(kFillSize); |
| EXPECT_EQ(fader.FillFrames(kFillSize, 0, channels), kFillSize); |
| EXPECT_EQ(dest->channel(0)[0], 1.0f); |
| |
| // Now make the source not provide enough data. |
| EXPECT_GT(fader.FramesNeededFromSource(kFillSize), 0); |
| source.set_max_fill_frames(0); |
| frames_needed += fader.FramesNeededFromSource(kFadeFrames / 3); |
| int filled = fader.FillFrames(kFadeFrames / 3, 0, channels); |
| EXPECT_EQ(filled, kFadeFrames / 3); |
| |
| // Data should be partially faded out. |
| EXPECT_EQ(dest->channel(0)[0], 1.0f); |
| EXPECT_LT(dest->channel(0)[filled - 1], 1.0f); |
| EXPECT_GE(dest->channel(0)[filled - 1], 0.0f); |
| float fade_min = dest->channel(0)[filled - 1]; |
| |
| // Fader's internal buffer should be partially full. |
| EXPECT_LT(fader.buffered_frames(), kFadeFrames); |
| |
| // Now let the source provide data again. |
| source.set_max_fill_frames(std::numeric_limits<int>::max()); |
| frames_needed += fader.FramesNeededFromSource(kFillSize); |
| EXPECT_EQ(fader.FillFrames(kFillSize, 0, channels), kFillSize); |
| // Data should fade back in from the point it faded out to. |
| EXPECT_GE(dest->channel(0)[0], fade_min); |
| EXPECT_EQ(dest->channel(0)[kFillSize - 1], 1.0f); |
| |
| // Test that FramesNeededFromSource() works correctly. |
| EXPECT_EQ(source.total_requested_frames(), frames_needed); |
| |
| // Fader's internal buffer should be full. |
| EXPECT_EQ(fader.buffered_frames(), kFadeFrames); |
| } |
| |
| TEST(AudioFaderTest, IncompleteFadeIn) { |
| TestFaderSource source; |
| AudioFader fader(&source, kFadeFrames, 1.0); |
| |
| // Fader has no buffered frames initially. |
| EXPECT_EQ(fader.buffered_frames(), 0); |
| |
| const int kFillSize = kFadeFrames * 2; |
| int frames_needed = fader.FramesNeededFromSource(kFillSize); |
| |
| // The source only partially fills the fader request. Since we're fading in |
| // from silence, the fader should output silence. |
| auto dest = CreateAudioBus(kFillSize); |
| source.set_max_fill_frames(10); |
| float* channels[kNumChannels]; |
| for (int c = 0; c < kNumChannels; ++c) { |
| channels[c] = dest->channel(c); |
| } |
| int filled = fader.FillFrames(kFillSize, 0, channels); |
| |
| // Test that FramesNeededFromSource() works correctly. |
| EXPECT_EQ(source.total_requested_frames(), frames_needed); |
| |
| // Fader's internal buffer should be empty. |
| EXPECT_EQ(fader.buffered_frames(), 0); |
| |
| // Data should be silent. |
| for (int i = 0; i < filled; ++i) { |
| EXPECT_EQ(dest->channel(0)[i], 0.0f); |
| } |
| } |
| |
| TEST(AudioFaderTest, FadeInPartially) { |
| TestFaderSource source; |
| AudioFader fader(&source, kFadeFrames, 1.0); |
| |
| // Fader has no buffered frames initially. |
| EXPECT_EQ(fader.buffered_frames(), 0); |
| |
| const int kFillSize = kFadeFrames * 2 / 3; |
| |
| int frames_needed = fader.FramesNeededFromSource(kFillSize); |
| auto dest = CreateAudioBus(kFillSize); |
| float* channels[kNumChannels]; |
| for (int c = 0; c < kNumChannels; ++c) { |
| channels[c] = dest->channel(c); |
| } |
| EXPECT_EQ(fader.FillFrames(kFillSize, 0, channels), kFillSize); |
| |
| // Fader's internal buffer should be full. |
| EXPECT_EQ(fader.buffered_frames(), kFadeFrames); |
| |
| // Data should be partially faded in. |
| EXPECT_EQ(dest->channel(0)[0], 0.0f); |
| EXPECT_GT(dest->channel(0)[kFillSize - 1], 0.0f); |
| EXPECT_LT(dest->channel(0)[kFillSize - 1], 1.0f); |
| float fade_max = dest->channel(0)[kFillSize - 1]; |
| |
| // Now tell the source not to provide any data. The fader output should fade |
| // back out to silence. |
| source.set_max_fill_frames(0); |
| frames_needed += fader.FramesNeededFromSource(kFillSize); |
| int filled = fader.FillFrames(kFillSize, 0, channels); |
| |
| // Data should be faded out. |
| EXPECT_LE(dest->channel(0)[0], fade_max); |
| EXPECT_GE(dest->channel(0)[0], 0.0f); |
| EXPECT_EQ(dest->channel(0)[filled - 1], 0.0f); |
| |
| // Test that FramesNeededFromSource() works correctly. |
| EXPECT_EQ(source.total_requested_frames(), frames_needed); |
| |
| // Fader's internal buffer should be empty. |
| EXPECT_EQ(fader.buffered_frames(), 0); |
| } |
| |
| } // namespace media |
| } // namespace chromecast |