aboutsummaryrefslogtreecommitdiff
path: root/webrtc/common_audio
diff options
context:
space:
mode:
authorMichael Graczyk <mgraczyk@chromium.org>2015-07-23 11:41:39 -0700
committerMichael Graczyk <mgraczyk@chromium.org>2015-07-23 18:41:45 +0000
commit86c6d33aec684d08189d498912e47cbc17c4d2db (patch)
treef0ae6bd941a35121bb19420f012f2aa3486e977e /webrtc/common_audio
parentfcfdb08b59b8dea7de2e5a79bfc263e1919f034e (diff)
downloadwebrtc-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.cc9
-rw-r--r--webrtc/common_audio/audio_util_unittest.cc148
-rw-r--r--webrtc/common_audio/include/audio_util.h79
-rw-r--r--webrtc/common_audio/wav_file.cc5
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) {