diff options
author | Michael Graczyk <mgraczyk@chromium.org> | 2015-07-23 11:41:39 -0700 |
---|---|---|
committer | Michael Graczyk <mgraczyk@chromium.org> | 2015-07-23 18:41:45 +0000 |
commit | 86c6d33aec684d08189d498912e47cbc17c4d2db (patch) | |
tree | f0ae6bd941a35121bb19420f012f2aa3486e977e /webrtc/common_audio | |
parent | fcfdb08b59b8dea7de2e5a79bfc263e1919f034e (diff) | |
download | webrtc-86c6d33aec684d08189d498912e47cbc17c4d2db.tar.gz |
Allow more than 2 input channels in AudioProcessing.
The number of output channels is constrained to be equal to either 1 or the
number of input channels.
An earlier version of this commit caused a crash on AEC dump.
TBR=aluebs@webrtc.org,pbos@webrtc.org
Review URL: https://codereview.webrtc.org/1248393003 .
Cr-Commit-Position: refs/heads/master@{#9626}
Diffstat (limited to 'webrtc/common_audio')
-rw-r--r-- | webrtc/common_audio/audio_util.cc | 9 | ||||
-rw-r--r-- | webrtc/common_audio/audio_util_unittest.cc | 148 | ||||
-rw-r--r-- | webrtc/common_audio/include/audio_util.h | 79 | ||||
-rw-r--r-- | webrtc/common_audio/wav_file.cc | 5 |
4 files changed, 207 insertions, 34 deletions
diff --git a/webrtc/common_audio/audio_util.cc b/webrtc/common_audio/audio_util.cc index 2047295cb9..be67c48f60 100644 --- a/webrtc/common_audio/audio_util.cc +++ b/webrtc/common_audio/audio_util.cc @@ -39,4 +39,13 @@ void FloatS16ToFloat(const float* src, size_t size, float* dest) { dest[i] = FloatS16ToFloat(src[i]); } +template <> +void DownmixInterleavedToMono<int16_t>(const int16_t* interleaved, + int num_multichannel_frames, + int num_channels, + int16_t* deinterleaved) { + DownmixInterleavedToMonoImpl<int16_t, int32_t>( + interleaved, num_multichannel_frames, num_channels, deinterleaved); +} + } // namespace webrtc diff --git a/webrtc/common_audio/audio_util_unittest.cc b/webrtc/common_audio/audio_util_unittest.cc index 2cdf53813c..3ac3911445 100644 --- a/webrtc/common_audio/audio_util_unittest.cc +++ b/webrtc/common_audio/audio_util_unittest.cc @@ -8,11 +8,15 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/common_audio/include/audio_util.h" #include "webrtc/typedefs.h" namespace webrtc { +namespace { + +using ::testing::ElementsAreArray; void ExpectArraysEq(const int16_t* ref, const int16_t* test, int length) { for (int i = 0; i < length; ++i) { @@ -28,11 +32,17 @@ void ExpectArraysEq(const float* ref, const float* test, int length) { TEST(AudioUtilTest, FloatToS16) { const int kSize = 9; - const float kInput[kSize] = { - 0.f, 0.4f / 32767.f, 0.6f / 32767.f, -0.4f / 32768.f, -0.6f / 32768.f, - 1.f, -1.f, 1.1f, -1.1f}; - const int16_t kReference[kSize] = { - 0, 0, 1, 0, -1, 32767, -32768, 32767, -32768}; + const float kInput[kSize] = {0.f, + 0.4f / 32767.f, + 0.6f / 32767.f, + -0.4f / 32768.f, + -0.6f / 32768.f, + 1.f, + -1.f, + 1.1f, + -1.1f}; + const int16_t kReference[kSize] = {0, 0, 1, 0, -1, + 32767, -32768, 32767, -32768}; int16_t output[kSize]; FloatToS16(kInput, kSize, output); ExpectArraysEq(kReference, output, kSize); @@ -50,8 +60,8 @@ TEST(AudioUtilTest, S16ToFloat) { TEST(AudioUtilTest, FloatS16ToS16) { const int kSize = 7; - const float kInput[kSize] = { - 0.f, 0.4f, 0.5f, -0.4f, -0.5f, 32768.f, -32769.f}; + const float kInput[kSize] = {0.f, 0.4f, 0.5f, -0.4f, + -0.5f, 32768.f, -32769.f}; const int16_t kReference[kSize] = {0, 0, 1, 0, -1, 32767, -32768}; int16_t output[kSize]; FloatS16ToS16(kInput, kSize, output); @@ -60,11 +70,17 @@ TEST(AudioUtilTest, FloatS16ToS16) { TEST(AudioUtilTest, FloatToFloatS16) { const int kSize = 9; - const float kInput[kSize] = { - 0.f, 0.4f / 32767.f, 0.6f / 32767.f, -0.4f / 32768.f, -0.6f / 32768.f, - 1.f, -1.f, 1.1f, -1.1f}; - const float kReference[kSize] = { - 0.f, 0.4f, 0.6f, -0.4f, -0.6f, 32767.f, -32768.f, 36043.7f, -36044.8f}; + const float kInput[kSize] = {0.f, + 0.4f / 32767.f, + 0.6f / 32767.f, + -0.4f / 32768.f, + -0.6f / 32768.f, + 1.f, + -1.f, + 1.1f, + -1.1f}; + const float kReference[kSize] = {0.f, 0.4f, 0.6f, -0.4f, -0.6f, + 32767.f, -32768.f, 36043.7f, -36044.8f}; float output[kSize]; FloatToFloatS16(kInput, kSize, output); ExpectArraysEq(kReference, output, kSize); @@ -72,11 +88,17 @@ TEST(AudioUtilTest, FloatToFloatS16) { TEST(AudioUtilTest, FloatS16ToFloat) { const int kSize = 9; - const float kInput[kSize] = { - 0.f, 0.4f, 0.6f, -0.4f, -0.6f, 32767.f, -32768.f, 36043.7f, -36044.8f}; - const float kReference[kSize] = { - 0.f, 0.4f / 32767.f, 0.6f / 32767.f, -0.4f / 32768.f, -0.6f / 32768.f, - 1.f, -1.f, 1.1f, -1.1f}; + const float kInput[kSize] = {0.f, 0.4f, 0.6f, -0.4f, -0.6f, + 32767.f, -32768.f, 36043.7f, -36044.8f}; + const float kReference[kSize] = {0.f, + 0.4f / 32767.f, + 0.6f / 32767.f, + -0.4f / 32768.f, + -0.6f / 32768.f, + 1.f, + -1.f, + 1.1f, + -1.1f}; float output[kSize]; FloatS16ToFloat(kInput, kSize, output); ExpectArraysEq(kReference, output, kSize); @@ -114,4 +136,96 @@ TEST(AudioUtilTest, InterleavingMonoIsIdentical) { ExpectArraysEq(mono, interleaved, kSamplesPerChannel); } +TEST(AudioUtilTest, DownmixInterleavedToMono) { + { + const int kNumFrames = 4; + const int kNumChannels = 1; + const int16_t interleaved[kNumChannels * kNumFrames] = {1, 2, -1, -3}; + int16_t deinterleaved[kNumFrames]; + + DownmixInterleavedToMono(interleaved, kNumFrames, kNumChannels, + deinterleaved); + + EXPECT_THAT(deinterleaved, ElementsAreArray(interleaved)); + } + { + const int kNumFrames = 2; + const int kNumChannels = 2; + const int16_t interleaved[kNumChannels * kNumFrames] = {10, 20, -10, -30}; + int16_t deinterleaved[kNumFrames]; + + DownmixInterleavedToMono(interleaved, kNumFrames, kNumChannels, + deinterleaved); + const int16_t expected[kNumFrames] = {15, -20}; + + EXPECT_THAT(deinterleaved, ElementsAreArray(expected)); + } + { + const int kNumFrames = 3; + const int kNumChannels = 3; + const int16_t interleaved[kNumChannels * kNumFrames] = { + 30000, 30000, 24001, -5, -10, -20, -30000, -30999, -30000}; + int16_t deinterleaved[kNumFrames]; + + DownmixInterleavedToMono(interleaved, kNumFrames, kNumChannels, + deinterleaved); + const int16_t expected[kNumFrames] = {28000, -11, -30333}; + + EXPECT_THAT(deinterleaved, ElementsAreArray(expected)); + } +} + +TEST(AudioUtilTest, DownmixToMonoTest) { + { + const int kNumFrames = 4; + const int kNumChannels = 1; + const float input_data[kNumChannels][kNumFrames] = {{1.f, 2.f, -1.f, -3.f}}; + const float* input[kNumChannels]; + for (int i = 0; i < kNumChannels; ++i) { + input[i] = input_data[i]; + } + + float downmixed[kNumFrames]; + + DownmixToMono<float, float>(input, kNumFrames, kNumChannels, downmixed); + + EXPECT_THAT(downmixed, ElementsAreArray(input_data[0])); + } + { + const int kNumFrames = 3; + const int kNumChannels = 2; + const float input_data[kNumChannels][kNumFrames] = {{1.f, 2.f, -1.f}, + {3.f, 0.f, 1.f}}; + const float* input[kNumChannels]; + for (int i = 0; i < kNumChannels; ++i) { + input[i] = input_data[i]; + } + + float downmixed[kNumFrames]; + const float expected[kNumFrames] = {2.f, 1.f, 0.f}; + + DownmixToMono<float, float>(input, kNumFrames, kNumChannels, downmixed); + + EXPECT_THAT(downmixed, ElementsAreArray(expected)); + } + { + const int kNumFrames = 3; + const int kNumChannels = 3; + const int16_t input_data[kNumChannels][kNumFrames] = { + {30000, -5, -30000}, {30000, -10, -30999}, {24001, -20, -30000}}; + const int16_t* input[kNumChannels]; + for (int i = 0; i < kNumChannels; ++i) { + input[i] = input_data[i]; + } + + int16_t downmixed[kNumFrames]; + const int16_t expected[kNumFrames] = {28000, -11, -30333}; + + DownmixToMono<int16_t, int32_t>(input, kNumFrames, kNumChannels, downmixed); + + EXPECT_THAT(downmixed, ElementsAreArray(expected)); + } +} + +} // namespace } // namespace webrtc diff --git a/webrtc/common_audio/include/audio_util.h b/webrtc/common_audio/include/audio_util.h index 8262649145..b217c683fd 100644 --- a/webrtc/common_audio/include/audio_util.h +++ b/webrtc/common_audio/include/audio_util.h @@ -12,7 +12,9 @@ #define WEBRTC_COMMON_AUDIO_INCLUDE_AUDIO_UTIL_H_ #include <limits> +#include <cstring> +#include "webrtc/base/checks.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/typedefs.h" @@ -26,10 +28,10 @@ typedef std::numeric_limits<int16_t> limits_int16; // FloatS16: float [-32768.0, 32767.0] static inline int16_t FloatToS16(float v) { if (v > 0) - return v >= 1 ? limits_int16::max() : - static_cast<int16_t>(v * limits_int16::max() + 0.5f); - return v <= -1 ? limits_int16::min() : - static_cast<int16_t>(-v * limits_int16::min() - 0.5f); + return v >= 1 ? limits_int16::max() + : static_cast<int16_t>(v * limits_int16::max() + 0.5f); + return v <= -1 ? limits_int16::min() + : static_cast<int16_t>(-v * limits_int16::min() - 0.5f); } static inline float S16ToFloat(int16_t v) { @@ -42,10 +44,9 @@ static inline int16_t FloatS16ToS16(float v) { static const float kMaxRound = limits_int16::max() - 0.5f; static const float kMinRound = limits_int16::min() + 0.5f; if (v > 0) - return v >= kMaxRound ? limits_int16::max() : - static_cast<int16_t>(v + 0.5f); - return v <= kMinRound ? limits_int16::min() : - static_cast<int16_t>(v - 0.5f); + return v >= kMaxRound ? limits_int16::max() + : static_cast<int16_t>(v + 0.5f); + return v <= kMinRound ? limits_int16::min() : static_cast<int16_t>(v - 0.5f); } static inline float FloatToFloatS16(float v) { @@ -69,8 +70,10 @@ void FloatS16ToFloat(const float* src, size_t size, float* dest); // |deinterleaved| buffers (|num_channel| buffers with |samples_per_channel| // per buffer). template <typename T> -void Deinterleave(const T* interleaved, int samples_per_channel, - int num_channels, T* const* deinterleaved) { +void Deinterleave(const T* interleaved, + int samples_per_channel, + int num_channels, + T* const* deinterleaved) { for (int i = 0; i < num_channels; ++i) { T* channel = deinterleaved[i]; int interleaved_idx = i; @@ -85,8 +88,10 @@ void Deinterleave(const T* interleaved, int samples_per_channel, // |interleaved|. There must be sufficient space allocated in |interleaved| // (|samples_per_channel| * |num_channels|). template <typename T> -void Interleave(const T* const* deinterleaved, int samples_per_channel, - int num_channels, T* interleaved) { +void Interleave(const T* const* deinterleaved, + int samples_per_channel, + int num_channels, + T* interleaved) { for (int i = 0; i < num_channels; ++i) { const T* channel = deinterleaved[i]; int interleaved_idx = i; @@ -97,6 +102,56 @@ void Interleave(const T* const* deinterleaved, int samples_per_channel, } } +template <typename T, typename Intermediate> +void DownmixToMono(const T* const* input_channels, + int num_frames, + int num_channels, + T* out) { + for (int i = 0; i < num_frames; ++i) { + Intermediate value = input_channels[0][i]; + for (int j = 1; j < num_channels; ++j) { + value += input_channels[j][i]; + } + out[i] = value / num_channels; + } +} + +// Downmixes an interleaved multichannel signal to a single channel by averaging +// all channels. +template <typename T, typename Intermediate> +void DownmixInterleavedToMonoImpl(const T* interleaved, + int num_frames, + int num_channels, + T* deinterleaved) { + DCHECK_GT(num_channels, 0); + DCHECK_GT(num_frames, 0); + + const T* const end = interleaved + num_frames * num_channels; + + while (interleaved < end) { + const T* const frame_end = interleaved + num_channels; + + Intermediate value = *interleaved++; + while (interleaved < frame_end) { + value += *interleaved++; + } + + *deinterleaved++ = value / num_channels; + } +} + +template <typename T> +void DownmixInterleavedToMono(const T* interleaved, + int num_frames, + int num_channels, + T* deinterleaved); + +template <> +void DownmixInterleavedToMono<int16_t>(const int16_t* interleaved, + int num_frames, + int num_channels, + int16_t* deinterleaved); + } // namespace webrtc #endif // WEBRTC_COMMON_AUDIO_INCLUDE_AUDIO_UTIL_H_ diff --git a/webrtc/common_audio/wav_file.cc b/webrtc/common_audio/wav_file.cc index 995043461a..a0c792c54a 100644 --- a/webrtc/common_audio/wav_file.cc +++ b/webrtc/common_audio/wav_file.cc @@ -123,11 +123,6 @@ void WavWriter::WriteSamples(const int16_t* samples, size_t num_samples) { num_samples_ += static_cast<uint32_t>(written); CHECK(written <= std::numeric_limits<uint32_t>::max() || num_samples_ >= written); // detect uint32_t overflow - CHECK(CheckWavParameters(num_channels_, - sample_rate_, - kWavFormat, - kBytesPerSample, - num_samples_)); } void WavWriter::WriteSamples(const float* samples, size_t num_samples) { |