Add support for multi-channel DTMF tone generation
This CL opens up support for DTMF tones to be played to multi-channel
outputs. The same tones are replicated across all channels. Unit tests
are updated.
Also adding a new method AudioMultiVector::CopyChannel.
BUG=crbug/407114
R=tina.legrand@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/20279004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@7056 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/audio_coding/neteq/audio_multi_vector.cc b/webrtc/modules/audio_coding/neteq/audio_multi_vector.cc
index 5a208a6..64278ed 100644
--- a/webrtc/modules/audio_coding/neteq/audio_multi_vector.cc
+++ b/webrtc/modules/audio_coding/neteq/audio_multi_vector.cc
@@ -202,6 +202,12 @@
return channels_[0]->Empty();
}
+void AudioMultiVector::CopyChannel(size_t from_channel, size_t to_channel) {
+ assert(from_channel < num_channels_);
+ assert(to_channel < num_channels_);
+ channels_[from_channel]->CopyFrom(channels_[to_channel]);
+}
+
const AudioVector& AudioMultiVector::operator[](size_t index) const {
return *(channels_[index]);
}
diff --git a/webrtc/modules/audio_coding/neteq/audio_multi_vector.h b/webrtc/modules/audio_coding/neteq/audio_multi_vector.h
index 908de93..1d05af7 100644
--- a/webrtc/modules/audio_coding/neteq/audio_multi_vector.h
+++ b/webrtc/modules/audio_coding/neteq/audio_multi_vector.h
@@ -117,6 +117,11 @@
virtual bool Empty() const;
+ // Copies the data between two channels in the AudioMultiVector. The method
+ // does not add any new channel. Thus, |from_channel| and |to_channel| must
+ // both be valid channel numbers.
+ virtual void CopyChannel(size_t from_channel, size_t to_channel);
+
// Accesses and modifies a channel (i.e., an AudioVector object) of this
// AudioMultiVector.
const AudioVector& operator[](size_t index) const;
diff --git a/webrtc/modules/audio_coding/neteq/audio_multi_vector_unittest.cc b/webrtc/modules/audio_coding/neteq/audio_multi_vector_unittest.cc
index 9476038..7dabe64 100644
--- a/webrtc/modules/audio_coding/neteq/audio_multi_vector_unittest.cc
+++ b/webrtc/modules/audio_coding/neteq/audio_multi_vector_unittest.cc
@@ -300,6 +300,31 @@
}
}
+// Test the CopyChannel method, when the test is instantiated with at least two
+// channels.
+TEST_P(AudioMultiVectorTest, CopyChannel) {
+ if (num_channels_ < 2)
+ return;
+
+ AudioMultiVector vec(num_channels_);
+ vec.PushBackInterleaved(array_interleaved_, interleaved_length_);
+ // Create a reference copy.
+ AudioMultiVector ref(num_channels_);
+ ref.PushBack(vec);
+ // Copy from first to last channel.
+ vec.CopyChannel(0, num_channels_ - 1);
+ // Verify that the first and last channels are identical; the others should
+ // be left untouched.
+ for (size_t i = 0; i < array_length(); ++i) {
+ // Verify that all but the last channel are untouched.
+ for (size_t channel = 0; channel < num_channels_ - 1; ++channel) {
+ EXPECT_EQ(ref[channel][i], vec[channel][i]);
+ }
+ // Verify that the last and the first channels are identical.
+ EXPECT_EQ(vec[0][i], vec[num_channels_ - 1][i]);
+ }
+}
+
INSTANTIATE_TEST_CASE_P(TestNumChannels,
AudioMultiVectorTest,
::testing::Values(static_cast<size_t>(1),
diff --git a/webrtc/modules/audio_coding/neteq/dtmf_tone_generator.cc b/webrtc/modules/audio_coding/neteq/dtmf_tone_generator.cc
index 34c615d..3429bcd 100644
--- a/webrtc/modules/audio_coding/neteq/dtmf_tone_generator.cc
+++ b/webrtc/modules/audio_coding/neteq/dtmf_tone_generator.cc
@@ -158,10 +158,6 @@
if (num_samples < 0 || !output) {
return kParameterError;
}
- assert(output->Channels() == 1); // Not adapted for multi-channel yet.
- if (output->Channels() != 1) {
- return kStereoNotSupported;
- }
output->AssertSize(num_samples);
for (int i = 0; i < num_samples; ++i) {
@@ -185,6 +181,10 @@
(*output)[0][i] =
static_cast<int16_t>((temp_val * amplitude_ + 8192) >> 14);
}
+ // Copy first channel to all other channels.
+ for (size_t channel = 1; channel < output->Channels(); ++channel) {
+ output->CopyChannel(0, channel);
+ }
return num_samples;
}
diff --git a/webrtc/modules/audio_coding/neteq/dtmf_tone_generator.h b/webrtc/modules/audio_coding/neteq/dtmf_tone_generator.h
index fc1e5e4..232eba4 100644
--- a/webrtc/modules/audio_coding/neteq/dtmf_tone_generator.h
+++ b/webrtc/modules/audio_coding/neteq/dtmf_tone_generator.h
@@ -24,7 +24,6 @@
enum ReturnCodes {
kNotInitialized = -1,
kParameterError = -2,
- kStereoNotSupported = -3,
};
DtmfToneGenerator();
diff --git a/webrtc/modules/audio_coding/neteq/dtmf_tone_generator_unittest.cc b/webrtc/modules/audio_coding/neteq/dtmf_tone_generator_unittest.cc
index 94f79dc..829d126 100644
--- a/webrtc/modules/audio_coding/neteq/dtmf_tone_generator_unittest.cc
+++ b/webrtc/modules/audio_coding/neteq/dtmf_tone_generator_unittest.cc
@@ -19,9 +19,129 @@
namespace webrtc {
-TEST(DtmfToneGenerator, CreateAndDestroy) {
- DtmfToneGenerator* tone_gen = new DtmfToneGenerator();
- delete tone_gen;
+class DtmfToneGeneratorTest : public ::testing::Test {
+ protected:
+ static const double kLowFreqHz[16];
+ static const double kHighFreqHz[16];
+ // This is the attenuation applied to all cases.
+ const double kBaseAttenuation = 16141.0 / 16384.0;
+ const double k3dbAttenuation = 23171.0 / 32768;
+ const int kNumSamples = 10;
+
+ void TestAllTones(int fs_hz, int channels) {
+ AudioMultiVector signal(channels);
+
+ for (int event = 0; event <= 15; ++event) {
+ std::ostringstream ss;
+ ss << "Checking event " << event << " at sample rate " << fs_hz;
+ SCOPED_TRACE(ss.str());
+ const int kAttenuation = 0;
+ ASSERT_EQ(0, tone_gen_.Init(fs_hz, event, kAttenuation));
+ EXPECT_TRUE(tone_gen_.initialized());
+ EXPECT_EQ(kNumSamples, tone_gen_.Generate(kNumSamples, &signal));
+
+ double f1 = kLowFreqHz[event];
+ double f2 = kHighFreqHz[event];
+ const double pi = 3.14159265358979323846;
+
+ for (int n = 0; n < kNumSamples; ++n) {
+ double x = k3dbAttenuation * sin(2.0 * pi * f1 / fs_hz * (-n - 1)) +
+ sin(2.0 * pi * f2 / fs_hz * (-n - 1));
+ x *= kBaseAttenuation;
+ x = ldexp(x, 14); // Scale to Q14.
+ for (int channel = 0; channel < channels; ++channel) {
+ EXPECT_NEAR(x, static_cast<double>(signal[channel][n]), 25);
+ }
+ }
+
+ tone_gen_.Reset();
+ EXPECT_FALSE(tone_gen_.initialized());
+ }
+ }
+
+ void TestAmplitudes(int fs_hz, int channels) {
+ AudioMultiVector signal(channels);
+ AudioMultiVector ref_signal(channels);
+
+ const int event_vec[] = {0, 4, 9, 13}; // Test a few events.
+ for (int e = 0; e < 4; ++e) {
+ int event = event_vec[e];
+ // Create full-scale reference.
+ ASSERT_EQ(0, tone_gen_.Init(fs_hz, event, 0)); // 0 attenuation.
+ EXPECT_EQ(kNumSamples, tone_gen_.Generate(kNumSamples, &ref_signal));
+ // Test every 5 steps (to save time).
+ for (int attenuation = 1; attenuation <= 36; attenuation += 5) {
+ std::ostringstream ss;
+ ss << "Checking event " << event << " at sample rate " << fs_hz;
+ ss << "; attenuation " << attenuation;
+ SCOPED_TRACE(ss.str());
+ ASSERT_EQ(0, tone_gen_.Init(fs_hz, event, attenuation));
+ EXPECT_EQ(kNumSamples, tone_gen_.Generate(kNumSamples, &signal));
+ for (int n = 0; n < kNumSamples; ++n) {
+ double attenuation_factor =
+ pow(10, -static_cast<double>(attenuation) / 20);
+ // Verify that the attenuation is correct.
+ for (int channel = 0; channel < channels; ++channel) {
+ EXPECT_NEAR(attenuation_factor * ref_signal[channel][n],
+ signal[channel][n],
+ 2);
+ }
+ }
+
+ tone_gen_.Reset();
+ }
+ }
+ }
+
+ DtmfToneGenerator tone_gen_;
+};
+
+// Low and high frequencies for events 0 through 15.
+const double DtmfToneGeneratorTest::kLowFreqHz[16] = {
+ 941.0, 697.0, 697.0, 697.0, 770.0, 770.0, 770.0, 852.0,
+ 852.0, 852.0, 941.0, 941.0, 697.0, 770.0, 852.0, 941.0};
+const double DtmfToneGeneratorTest::kHighFreqHz[16] = {
+ 1336.0, 1209.0, 1336.0, 1477.0, 1209.0, 1336.0, 1477.0, 1209.0,
+ 1336.0, 1477.0, 1209.0, 1477.0, 1633.0, 1633.0, 1633.0, 1633.0};
+
+TEST_F(DtmfToneGeneratorTest, Test8000Mono) {
+ TestAllTones(8000, 1);
+ TestAmplitudes(8000, 1);
+}
+
+TEST_F(DtmfToneGeneratorTest, Test16000Mono) {
+ TestAllTones(16000, 1);
+ TestAmplitudes(16000, 1);
+}
+
+TEST_F(DtmfToneGeneratorTest, Test32000Mono) {
+ TestAllTones(32000, 1);
+ TestAmplitudes(32000, 1);
+}
+
+TEST_F(DtmfToneGeneratorTest, Test48000Mono) {
+ TestAllTones(48000, 1);
+ TestAmplitudes(48000, 1);
+}
+
+TEST_F(DtmfToneGeneratorTest, Test8000Stereo) {
+ TestAllTones(8000, 2);
+ TestAmplitudes(8000, 2);
+}
+
+TEST_F(DtmfToneGeneratorTest, Test16000Stereo) {
+ TestAllTones(16000, 2);
+ TestAmplitudes(16000, 2);
+}
+
+TEST_F(DtmfToneGeneratorTest, Test32000Stereo) {
+ TestAllTones(32000, 2);
+ TestAmplitudes(32000, 2);
+}
+
+TEST_F(DtmfToneGeneratorTest, Test48000Stereo) {
+ TestAllTones(48000, 2);
+ TestAmplitudes(48000, 2);
}
TEST(DtmfToneGenerator, TestErrors) {
@@ -58,85 +178,4 @@
tone_gen.Generate(kNumSamples, NULL));
}
-TEST(DtmfToneGenerator, TestTones) {
- DtmfToneGenerator tone_gen;
- const int kAttenuation = 0;
- const int kNumSamples = 10;
- AudioMultiVector signal(1); // One channel.
-
- // Low and high frequencies for events 0 through 15.
- const double low_freq_hz[] = { 941.0, 697.0, 697.0, 697.0, 770.0, 770.0,
- 770.0, 852.0, 852.0, 852.0, 941.0, 941.0, 697.0, 770.0, 852.0, 941.0 };
- const double hi_freq_hz[] = { 1336.0, 1209.0, 1336.0, 1477.0, 1209.0, 1336.0,
- 1477.0, 1209.0, 1336.0, 1477.0, 1209.0, 1477.0, 1633.0, 1633.0, 1633.0,
- 1633.0 };
- const double attenuate_3dB = 23171.0 / 32768; // 3 dB attenuation.
- const double base_attenuation = 16141.0 / 16384.0; // This is the attenuation
- // applied to all cases.
- const int fs_vec[] = { 8000, 16000, 32000, 48000 };
- for (int f = 0; f < 4; ++f) {
- int fs = fs_vec[f];
- for (int event = 0; event <= 15; ++event) {
- std::ostringstream ss;
- ss << "Checking event " << event << " at sample rate " << fs;
- SCOPED_TRACE(ss.str());
- ASSERT_EQ(0, tone_gen.Init(fs, event, kAttenuation));
- EXPECT_TRUE(tone_gen.initialized());
- EXPECT_EQ(kNumSamples, tone_gen.Generate(kNumSamples, &signal));
-
- double f1 = low_freq_hz[event];
- double f2 = hi_freq_hz[event];
- const double pi = 3.14159265358979323846;
-
- for (int n = 0; n < kNumSamples; ++n) {
- double x = attenuate_3dB * sin(2.0 * pi * f1 / fs * (-n - 1))
- + sin(2.0 * pi * f2 / fs * (-n - 1));
- x *= base_attenuation;
- x = ldexp(x, 14); // Scale to Q14.
- static const int kChannel = 0;
- EXPECT_NEAR(x, static_cast<double>(signal[kChannel][n]), 25);
- }
-
- tone_gen.Reset();
- EXPECT_FALSE(tone_gen.initialized());
- }
- }
-}
-
-TEST(DtmfToneGenerator, TestAmplitudes) {
- DtmfToneGenerator tone_gen;
- const int kNumSamples = 10;
- AudioMultiVector signal(1); // One channel.
- AudioMultiVector ref_signal(1); // One channel.
-
- const int fs_vec[] = { 8000, 16000, 32000, 48000 };
- const int event_vec[] = { 0, 4, 9, 13 }; // Test a few events.
- for (int f = 0; f < 4; ++f) {
- int fs = fs_vec[f];
- int event = event_vec[f];
- // Create full-scale reference.
- ASSERT_EQ(0, tone_gen.Init(fs, event, 0)); // 0 attenuation.
- EXPECT_EQ(kNumSamples, tone_gen.Generate(kNumSamples, &ref_signal));
- // Test every 5 steps (to save time).
- for (int attenuation = 1; attenuation <= 36; attenuation += 5) {
- std::ostringstream ss;
- ss << "Checking event " << event << " at sample rate " << fs;
- ss << "; attenuation " << attenuation;
- SCOPED_TRACE(ss.str());
- ASSERT_EQ(0, tone_gen.Init(fs, event, attenuation));
- EXPECT_EQ(kNumSamples, tone_gen.Generate(kNumSamples, &signal));
- for (int n = 0; n < kNumSamples; ++n) {
- double attenuation_factor =
- pow(10, -static_cast<double>(attenuation)/20);
- // Verify that the attenuation is correct.
- static const int kChannel = 0;
- EXPECT_NEAR(attenuation_factor * ref_signal[kChannel][n],
- signal[kChannel][n], 2);
- }
-
- tone_gen.Reset();
- }
- }
-}
-
} // namespace webrtc