[mixer_service] Move ChannelStrip to lib/processing
With minor cleanup in the moved code respectively.
Bug: 87651
Multiply: audio-libprocessing-unittests
Multiply: audio_core_unittests
Multiply: audio_mixer_unittests
Multiply: audio-core-api-pipeline-tests
Multiply: audio-core-fidelity-test
Multiply: audio_fidelity_tests
Change-Id: I90706c998c134b63852a9136a09c733252871d0e
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/693795
Commit-Queue: Alper Gungormusler <alperg@google.com>
Reviewed-by: Tom Bergan <tombergan@google.com>
diff --git a/src/media/audio/audio_core/mixer/BUILD.gn b/src/media/audio/audio_core/mixer/BUILD.gn
index c0f5b57..5c66849 100644
--- a/src/media/audio/audio_core/mixer/BUILD.gn
+++ b/src/media/audio/audio_core/mixer/BUILD.gn
@@ -11,7 +11,6 @@
source_set("mixer") {
sources = [
- "channel_strip.h",
"gain.cc",
"gain.h",
"intersect.cc",
@@ -84,7 +83,6 @@
sources = [
"bookkeeping_unittest.cc",
- "channel_strip_unittest.cc",
"gain_unittest.cc",
"intersect_unittest.cc",
"no_op_unittest.cc",
diff --git a/src/media/audio/audio_core/mixer/channel_strip.h b/src/media/audio/audio_core/mixer/channel_strip.h
deleted file mode 100644
index 0065173..0000000
--- a/src/media/audio/audio_core/mixer/channel_strip.h
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2019 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_AUDIO_CORE_MIXER_CHANNEL_STRIP_H_
-#define SRC_MEDIA_AUDIO_AUDIO_CORE_MIXER_CHANNEL_STRIP_H_
-
-#include <lib/syslog/cpp/macros.h>
-
-#include <algorithm>
-#include <cstring>
-#include <vector>
-
-namespace media::audio::mixer {
-
-// ChannelStrip lightly manages sections of single-channel audio, useful when processing audio one
-// channel at a time. ChannelStrip is essentially a vector-of-vectors, but also contains convenience
-// methods to shift audio (either all-channels or a single channel) within each channel's "strip".
-class ChannelStrip {
- public:
- ChannelStrip(int32_t num_channels, int32_t length)
- : data_(num_channels), num_channels_(num_channels), len_(length) {
- FX_DCHECK(num_channels > 0);
- FX_DCHECK(length > 0);
-
- for (auto& channel : data_) {
- channel.resize(len_, 0.0f);
- }
- }
-
- ChannelStrip() : ChannelStrip(1, 1) {}
- ChannelStrip(const ChannelStrip& not_ctor_copyable) = delete;
- ChannelStrip& operator=(const ChannelStrip& not_copyable) = delete;
- ChannelStrip(ChannelStrip&& not_ctor_movable) = delete;
- ChannelStrip& operator=(ChannelStrip&& not_movable) = delete;
-
- // Used for debugging purposes only
- static inline std::string ToString(const ChannelStrip& channels);
-
- // Zero out all channels, leaving each strip (vector) at the specified length
- void Clear() {
- for (auto& channel : data_) {
- std::memset(channel.data(), 0, channel.size() * sizeof(channel[0]));
- }
- }
-
- // Shift the audio in all channels, by the specified amount
- void ShiftBy(size_t shift_by) {
- shift_by = std::min(shift_by, static_cast<size_t>(len_));
-
- for (auto& channel : data_) {
- memmove(channel.data(), channel.data() + shift_by, (len_ - shift_by) * sizeof(channel[0]));
- memset(channel.data() + (len_ - shift_by), 0, shift_by * sizeof(channel[0]));
- }
- }
-
- // This returns a vector containing audio data for a single channel. ChannelStrip is essentially a
- // vector-of-vector and is not "jagged": all channels have the same amount of audio data (all
- // vectors are the same length).
- // For this reason, one MUST NOT perform the following to a channel vector returned by []operator:
- // - change the underlying vector: ctor, dtor, operator=, assign, swap
- // - change the vector's size: clear, emplace[_back], erase[_if], insert, pop, push, resize,
- // or operator[] to set a value at index len_ or greater
- // Only the following can be performed on the vector returned by []operator:
- // - read/write existing audio samples: operator[] < len, at, back, data, front, iterators
- // - query size/capacity (allowed but unneeded): capacity, empty, max_size, reserve, size, ...
- std::vector<float>& operator[](size_t index) { return data_[index]; }
-
- int32_t num_channels() const { return num_channels_; }
- int64_t length() const { return len_; }
-
- private:
- std::vector<std::vector<float>> data_;
- int32_t num_channels_;
- int64_t len_;
-};
-
-// declared static, used for debugging purposes only
-// Log the contents of the channel strip, channel by channel.
-inline std::string ChannelStrip::ToString(const ChannelStrip& channels) {
- std::string strip("ChannelStrip: chans ");
- strip += std::to_string(channels.num_channels_) + ", len " + std::to_string(channels.len_) + "\n";
-
- for (auto chan = 0; chan < channels.num_channels_; ++chan) {
- strip += "\tChannel " + std::to_string(chan);
-
- for (auto idx = 0; idx < channels.len_; ++idx) {
- if (idx % 16 == 0) {
- strip += "\n[ " + std::to_string(idx) + "\t]";
- }
- strip += "\t" + std::to_string(channels.data_[chan][idx]);
- }
- strip += "\n";
- }
- return strip;
-}
-
-} // namespace media::audio::mixer
-
-#endif // SRC_MEDIA_AUDIO_AUDIO_CORE_MIXER_CHANNEL_STRIP_H_
diff --git a/src/media/audio/audio_core/mixer/channel_strip_unittest.cc b/src/media/audio/audio_core/mixer/channel_strip_unittest.cc
deleted file mode 100644
index e48bdd0..0000000
--- a/src/media/audio/audio_core/mixer/channel_strip_unittest.cc
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright 2019 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/audio_core/mixer/channel_strip.h"
-
-#include <gtest/gtest.h>
-
-namespace media::audio {
-namespace {
-
-void ValidateConstruction(mixer::ChannelStrip* strip, int32_t num_channels, int32_t length) {
- ASSERT_NE(strip, nullptr);
- ASSERT_GT(num_channels, 0);
- ASSERT_GT(length, 0);
-
- EXPECT_EQ(strip->num_channels(), num_channels);
- EXPECT_EQ(strip->length(), length);
-
- for (auto chan = 0; chan < num_channels; ++chan) {
- EXPECT_EQ((*strip)[chan].size(), static_cast<size_t>(length));
- for (auto idx = 0; idx < length; ++idx) {
- EXPECT_EQ((*strip)[chan][idx], 0.0f);
- }
- }
-}
-
-// Validate ctor default and parameters
-TEST(ChannelStripTest, Construction) {
- mixer::ChannelStrip data;
- auto num_chans = 1;
- auto strip_len = 1;
- ValidateConstruction(&data, num_chans, strip_len);
-
- num_chans = 1;
- strip_len = 3;
- mixer::ChannelStrip data2(num_chans, strip_len);
- ValidateConstruction(&data2, num_chans, strip_len);
-
- num_chans = 4;
- strip_len = 2;
- mixer::ChannelStrip data3(num_chans, strip_len);
- ValidateConstruction(&data3, num_chans, strip_len);
-}
-
-// Sanity test for [] operator
-TEST(ChannelStripTest, SetValues) {
- mixer::ChannelStrip data(2, 3);
-
- auto& channel_0 = data[0];
- auto& channel_0_watcher = data[0];
- auto& channel_1 = data[1];
- auto& channel_1_watcher = data[1];
- auto& data_watcher = data;
-
- data[0][0] = 1.0f;
- channel_0[1] = 2.0f;
- channel_1[0] = 3.0f;
- data[1][1] = 4.0f;
-
- EXPECT_EQ(data_watcher[0][0], 1.0f);
- EXPECT_EQ(channel_0_watcher[1], 2.0f);
- EXPECT_EQ(channel_1_watcher[0], 3.0f);
- EXPECT_EQ(data_watcher[1][1], 4.0f);
-}
-
-// Clear should zero-out the entire length of each chan, regardless of num_chans/length.
-TEST(ChannelStripTest, Clear) {
- mixer::ChannelStrip data(2, 2);
- auto& channel_1 = data[1];
-
- data[0][0] = 1.0f;
- data[0][1] = 2.0f;
- channel_1[0] = -3.0f;
- channel_1[1] = -4.0f;
-
- data.Clear();
-
- EXPECT_EQ(data[0][0], 0.0f);
- EXPECT_EQ(data[0][1], 0.0f);
- EXPECT_EQ(data[1][0], 0.0f);
- EXPECT_EQ(data[1][1], 0.0f);
- EXPECT_EQ(channel_1[0], 0.0f);
- EXPECT_EQ(channel_1[1], 0.0f);
-}
-
-// Test shifting by various conditions. Shift-by-0 should lead to no change. Shift-by-length (or
-// more) should clear the strip: all values are shifted out, replaced by shifted-in zeroes.
-TEST(ChannelStripTest, ShiftBy) {
- mixer::ChannelStrip data(2, 2);
-
- data[0][0] = 1.0f;
- data[0][1] = 2.0f;
-
- data[1][0] = -1.0f;
- data[1][1] = -2.0f;
-
- // Shift by 0 -- channels unchanged
- data.ShiftBy(0);
-
- EXPECT_EQ(data[0][0], 1.0f);
- EXPECT_EQ(data[0][1], 2.0f);
- EXPECT_EQ(data[1][0], -1.0f);
- EXPECT_EQ(data[1][1], -2.0f);
-
- // Shift by 1 - channels shift left, adding a zero
- data.ShiftBy(1);
-
- EXPECT_EQ(data[0][0], 2.0f);
- EXPECT_EQ(data[0][1], 0.0f);
- EXPECT_EQ(data[1][0], -2.0f);
- EXPECT_EQ(data[1][1], 0.0f);
-
- // Shift by 2 (len_), all data "shifted out", entirely zero
- data[0][1] = 3.0f;
- data[1][1] = -3.0f;
- data.ShiftBy(2);
-
- EXPECT_EQ(data[0][0], 0.0f);
- EXPECT_EQ(data[0][1], 0.0f);
- EXPECT_EQ(data[1][0], 0.0f);
- EXPECT_EQ(data[1][1], 0.0f);
-
- // Shift by 3 (more than len_), allowed and same as shifting by len_ (all zeros)
- data[0][0] = 4.0f;
- data[0][1] = 5.0f;
- data[1][0] = -4.0f;
- data[1][1] = -5.0f;
- data.ShiftBy(3);
-
- EXPECT_EQ(data[0][0], 0.0f);
- EXPECT_EQ(data[0][1], 0.0f);
- EXPECT_EQ(data[1][0], 0.0f);
- EXPECT_EQ(data[1][1], 0.0f);
-}
-
-} // namespace
-} // namespace media::audio
diff --git a/src/media/audio/audio_core/mixer/sinc_sampler.cc b/src/media/audio/audio_core/mixer/sinc_sampler.cc
index 840871f..c88eb0d 100644
--- a/src/media/audio/audio_core/mixer/sinc_sampler.cc
+++ b/src/media/audio/audio_core/mixer/sinc_sampler.cc
@@ -9,14 +9,15 @@
#include <algorithm>
#include <limits>
-#include "src/media/audio/audio_core/mixer/channel_strip.h"
#include "src/media/audio/audio_core/mixer/constants.h"
#include "src/media/audio/audio_core/mixer/position_manager.h"
+#include "src/media/audio/lib/processing/channel_strip.h"
#include "src/media/audio/lib/processing/filter.h"
#include "src/media/audio/lib/processing/sampler.h"
namespace media::audio::mixer {
+using ::media_audio::ChannelStrip;
using ::media_audio::SincFilter;
template <int32_t DestChanCount, typename SourceSampleType, int32_t SourceChanCount>
diff --git a/src/media/audio/lib/processing/BUILD.gn b/src/media/audio/lib/processing/BUILD.gn
index 9070960..a4a060c 100644
--- a/src/media/audio/lib/processing/BUILD.gn
+++ b/src/media/audio/lib/processing/BUILD.gn
@@ -38,6 +38,7 @@
source_set("sampler") {
sources = [
+ "channel_strip.h",
"coefficient_table_cache.h",
"filter.cc",
"filter.h",
@@ -116,6 +117,7 @@
output_name = "audio-libprocessing-unittests"
sources = [
+ "channel_strip_unittest.cc",
"coefficient_table_cache_unittest.cc",
"coefficient_table_unittest.cc",
"filter_unittest.cc",
diff --git a/src/media/audio/lib/processing/channel_strip.h b/src/media/audio/lib/processing/channel_strip.h
new file mode 100644
index 0000000..479aae5
--- /dev/null
+++ b/src/media/audio/lib/processing/channel_strip.h
@@ -0,0 +1,91 @@
+// Copyright 2022 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_PROCESSING_CHANNEL_STRIP_H_
+#define SRC_MEDIA_AUDIO_LIB_PROCESSING_CHANNEL_STRIP_H_
+
+#include <lib/stdcompat/span.h>
+#include <lib/syslog/cpp/macros.h>
+
+#include <algorithm>
+#include <cstring>
+#include <sstream>
+#include <string>
+
+namespace media_audio {
+
+// Class that manages planar audio data. This is useful when processing audio one channel at a time.
+class ChannelStrip {
+ public:
+ ChannelStrip(int64_t channel_count, int64_t frame_count)
+ : data_(new float[channel_count * frame_count]{}),
+ channel_count_(channel_count),
+ frame_count_(frame_count) {
+ FX_DCHECK(channel_count > 0);
+ FX_DCHECK(frame_count > 0);
+ }
+ ~ChannelStrip() { delete[] data_; }
+
+ // Non-copyable and non-movable.
+ ChannelStrip(const ChannelStrip& other) = delete;
+ ChannelStrip& operator=(const ChannelStrip& other) = delete;
+ ChannelStrip(ChannelStrip&& other) = delete;
+ ChannelStrip& operator=(ChannelStrip&& other) = delete;
+
+ // Logs the contents of the channel strip, channel by channel.
+ // Used for debugging purposes only.
+ static std::string ToString(const ChannelStrip& channel_strip) {
+ std::stringstream ss;
+ ss << "ChannelStrip: chans ";
+ ss << channel_strip.channel_count_ << ", len " << channel_strip.frame_count_ << "\n";
+ for (auto chan = 0; chan < channel_strip.channel_count_; ++chan) {
+ ss << "\tChannel " << chan;
+ for (auto i = 0; i < channel_strip.frame_count_; ++i) {
+ if (i % 16 == 0) {
+ ss << "\n[ " << i << "\t]";
+ }
+ ss << "\t" << channel_strip.data_[chan * channel_strip.frame_count_ + i];
+ }
+ ss << "\n";
+ }
+ return ss.str();
+ }
+
+ // Zeroes out all channels.
+ void Clear() { std::fill_n(data_, channel_count_ * frame_count_, 0.0f); }
+
+ // Shifts the audio data in all channels, by `shift_by` amount.
+ void ShiftBy(size_t shift_by) {
+ shift_by = std::min(shift_by, static_cast<size_t>(frame_count_));
+ for (auto chan = 0; chan < channel_count_; ++chan) {
+ float* channel_data = data_ + chan * frame_count_;
+ std::memmove(channel_data, channel_data + shift_by,
+ (frame_count_ - shift_by) * sizeof(data_[0]));
+ std::fill_n(channel_data + (frame_count_ - shift_by), shift_by, 0.0f);
+ }
+ }
+
+ // Returns a span containing audio data for a given `channel`.
+ cpp20::span<float> operator[](size_t channel) {
+ return cpp20::span{data_ + channel * frame_count_, static_cast<size_t>(frame_count_)};
+ }
+ cpp20::span<const float> operator[](size_t channel) const {
+ return cpp20::span{data_ + channel * frame_count_, static_cast<size_t>(frame_count_)};
+ }
+
+ // Returns the number of channels.
+ int64_t channel_count() const { return channel_count_; }
+
+ // Returns the number of frames (i.e., length of each channel).
+ int64_t frame_count() const { return frame_count_; }
+
+ private:
+ float* data_;
+ int64_t channel_count_;
+ int64_t frame_count_;
+};
+
+} // namespace media_audio
+
+#endif // SRC_MEDIA_AUDIO_AUDIO_CORE_MIXER_CHANNEL_STRIP_H_
diff --git a/src/media/audio/lib/processing/channel_strip_unittest.cc b/src/media/audio/lib/processing/channel_strip_unittest.cc
new file mode 100644
index 0000000..1a6dea4
--- /dev/null
+++ b/src/media/audio/lib/processing/channel_strip_unittest.cc
@@ -0,0 +1,148 @@
+// Copyright 2022 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/processing/channel_strip.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace media_audio {
+namespace {
+
+using ::testing::AllOf;
+using ::testing::Each;
+using ::testing::SizeIs;
+
+void ValidateConstruction(const ChannelStrip& strip, int64_t channel_count, int64_t frame_count) {
+ ASSERT_GT(channel_count, 0);
+ ASSERT_GT(frame_count, 0);
+
+ EXPECT_EQ(strip.channel_count(), channel_count);
+ EXPECT_EQ(strip.frame_count(), frame_count);
+ for (auto chan = 0; chan < channel_count; ++chan) {
+ EXPECT_THAT(strip[chan], AllOf(SizeIs(frame_count), Each(0.0f)));
+ }
+}
+
+TEST(ChannelStripTest, Construction) {
+ auto num_chans = 1;
+ auto strip_len = 1;
+ ChannelStrip data(num_chans, strip_len);
+ ValidateConstruction(data, num_chans, strip_len);
+
+ num_chans = 1;
+ strip_len = 3;
+ ChannelStrip data2(num_chans, strip_len);
+ ValidateConstruction(data2, num_chans, strip_len);
+
+ num_chans = 4;
+ strip_len = 2;
+ ChannelStrip data3(num_chans, strip_len);
+ ValidateConstruction(data3, num_chans, strip_len);
+}
+
+TEST(ChannelStripTest, SetValues) {
+ ChannelStrip data(2, 3);
+ ValidateConstruction(data, 2, 3);
+
+ auto channel_0 = data[0];
+ auto channel_1 = data[1];
+
+ data[0][0] = 1.0f;
+ channel_0[1] = 2.0f;
+ channel_1[0] = 3.0f;
+ data[1][1] = 4.0f;
+
+ EXPECT_FLOAT_EQ(channel_0[1], 2.0f);
+ EXPECT_FLOAT_EQ(channel_1[0], 3.0f);
+ EXPECT_FLOAT_EQ(data[0][0], 1.0f);
+ EXPECT_FLOAT_EQ(data[0][1], 2.0f);
+ EXPECT_FLOAT_EQ(data[1][0], 3.0f);
+ EXPECT_FLOAT_EQ(data[1][1], 4.0f);
+}
+
+TEST(ChannelStripTest, Clear) {
+ ChannelStrip data(2, 2);
+ ValidateConstruction(data, 2, 2);
+
+ auto channel_1 = data[1];
+
+ data[0][0] = 1.0f;
+ data[0][1] = 2.0f;
+ channel_1[0] = -3.0f;
+ channel_1[1] = -4.0f;
+
+ EXPECT_FLOAT_EQ(channel_1[0], -3.0f);
+ EXPECT_FLOAT_EQ(channel_1[1], -4.0f);
+ EXPECT_FLOAT_EQ(data[0][0], 1.0f);
+ EXPECT_FLOAT_EQ(data[0][1], 2.0f);
+ EXPECT_FLOAT_EQ(data[1][0], -3.0f);
+ EXPECT_FLOAT_EQ(data[1][1], -4.0f);
+
+ data.Clear();
+
+ EXPECT_FLOAT_EQ(channel_1[0], 0.0f);
+ EXPECT_FLOAT_EQ(channel_1[1], 0.0f);
+ EXPECT_FLOAT_EQ(data[0][0], 0.0f);
+ EXPECT_FLOAT_EQ(data[0][1], 0.0f);
+ EXPECT_FLOAT_EQ(data[1][0], 0.0f);
+ EXPECT_FLOAT_EQ(data[1][1], 0.0f);
+}
+
+TEST(ChannelStripTest, ShiftBy) {
+ ChannelStrip data(2, 2);
+ ValidateConstruction(data, 2, 2);
+
+ data[0][0] = 1.0f;
+ data[0][1] = 2.0f;
+ data[1][0] = -1.0f;
+ data[1][1] = -2.0f;
+
+ EXPECT_FLOAT_EQ(data[0][0], 1.0f);
+ EXPECT_FLOAT_EQ(data[0][1], 2.0f);
+ EXPECT_FLOAT_EQ(data[1][0], -1.0f);
+ EXPECT_FLOAT_EQ(data[1][1], -2.0f);
+
+ // Shift by 0, which should have no effect.
+ data.ShiftBy(0);
+
+ EXPECT_FLOAT_EQ(data[0][0], 1.0f);
+ EXPECT_FLOAT_EQ(data[0][1], 2.0f);
+ EXPECT_FLOAT_EQ(data[1][0], -1.0f);
+ EXPECT_FLOAT_EQ(data[1][1], -2.0f);
+
+ // Shift by 1, which should shift channels left, adding a single zero at the end.
+ data.ShiftBy(1);
+
+ EXPECT_FLOAT_EQ(data[0][0], 2.0f);
+ EXPECT_FLOAT_EQ(data[0][1], 0.0f);
+ EXPECT_FLOAT_EQ(data[1][0], -2.0f);
+ EXPECT_FLOAT_EQ(data[1][1], 0.0f);
+
+ // Reset shifted data, and shift by frame count, which should entirely zero out all channels.
+ data[0][1] = 3.0f;
+ data[1][1] = -3.0f;
+ data.ShiftBy(2);
+
+ EXPECT_FLOAT_EQ(data[0][0], 0.0f);
+ EXPECT_FLOAT_EQ(data[0][1], 0.0f);
+ EXPECT_FLOAT_EQ(data[1][0], 0.0f);
+ EXPECT_FLOAT_EQ(data[1][1], 0.0f);
+
+ // Reset shifted data, and shift by more than frame count, which should again entirely zero out
+ // all channels.
+ data[0][0] = 4.0f;
+ data[0][1] = 5.0f;
+ data[1][0] = -4.0f;
+ data[1][1] = -5.0f;
+ data.ShiftBy(3);
+
+ EXPECT_FLOAT_EQ(data[0][0], 0.0f);
+ EXPECT_FLOAT_EQ(data[0][1], 0.0f);
+ EXPECT_FLOAT_EQ(data[1][0], 0.0f);
+ EXPECT_FLOAT_EQ(data[1][1], 0.0f);
+}
+
+} // namespace
+} // namespace media_audio