Implementing stereo support for G.722

This CL implements stereo support for G.722 through a new class
AudioDecoderG722Stereo derived from AudioDecoderG722.

Also implementing tests for G.722 stereo.

Review URL: https://webrtc-codereview.appspot.com/1073006

git-svn-id: http://webrtc.googlecode.com/svn/trunk@3452 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
henrik.lundin@webrtc.org
2013-02-01 11:49:28 +00:00
parent 7050f96bff
commit aaad6134b9
4 changed files with 192 additions and 18 deletions

View File

@ -39,6 +39,7 @@ class AudioDecoderTest : public ::testing::Test {
frame_size_(0),
data_length_(0),
encoded_bytes_(0),
channels_(1),
decoder_(NULL) {
input_file_ = webrtc::test::ProjectRootPath() +
"resources/audio_coding/testfile32kHz.pcm";
@ -51,7 +52,7 @@ class AudioDecoderTest : public ::testing::Test {
ASSERT_GT(data_length_, 0u) << "The test must set data_length_ > 0";
input_ = new int16_t[data_length_];
encoded_ = new uint8_t[data_length_ * 2];
decoded_ = new int16_t[data_length_];
decoded_ = new int16_t[data_length_ * channels_];
// Open input file.
input_fp_ = fopen(input_file_.c_str(), "rb");
ASSERT_TRUE(input_fp_ != NULL) << "Failed to open file " << input_file_;
@ -104,9 +105,10 @@ class AudioDecoderTest : public ::testing::Test {
&encoded_[encoded_bytes_]);
AudioDecoder::SpeechType speech_type;
size_t dec_len = decoder_->Decode(&encoded_[encoded_bytes_], enc_len,
&decoded_[processed_samples],
&decoded_[processed_samples *
channels_],
&speech_type);
EXPECT_EQ(frame_size_, dec_len);
EXPECT_EQ(frame_size_ * channels_, dec_len);
encoded_bytes_ += enc_len;
processed_samples += frame_size_;
}
@ -115,29 +117,35 @@ class AudioDecoderTest : public ::testing::Test {
EXPECT_LE(MseInputOutput(processed_samples, delay), mse);
}
// The absolute difference between the input and output is compared vs
// |tolerance|. The parameter |delay| is used to correct for codec delays.
void CompareInputOutput(size_t num_samples, int tolerance, int delay) const {
// The absolute difference between the input and output (the first channel) is
// compared vs |tolerance|. The parameter |delay| is used to correct for codec
// delays. If |channels_| is 2, the method verifies that the two channels are
// identical.
virtual void CompareInputOutput(size_t num_samples, int tolerance,
int delay) const {
assert(num_samples <= data_length_);
for (unsigned int n = 0; n < num_samples - delay; ++n) {
ASSERT_NEAR(input_[n], decoded_[n + delay], tolerance) <<
if (channels_ == 2) {
ASSERT_EQ(decoded_[channels_ * n], decoded_[channels_ * n + 1]) <<
"Stereo samples differ.";
}
ASSERT_NEAR(input_[n], decoded_[channels_ * n + delay], tolerance) <<
"Exit test on first diff; n = " << n;
DataLog::InsertCell("CodecTest", "input", input_[n]);
DataLog::InsertCell("CodecTest", "output", decoded_[n]);
DataLog::InsertCell("CodecTest", "output", decoded_[channels_ * n]);
DataLog::NextRow("CodecTest");
}
}
// Calculates mean-squared error between input and output. The parameter
// |delay| is used to correct for codec delays.
double MseInputOutput(size_t num_samples, int delay) const {
// Calculates mean-squared error between input and output (the first channel).
// The parameter |delay| is used to correct for codec delays.
virtual double MseInputOutput(size_t num_samples, int delay) const {
assert(num_samples <= data_length_);
if (num_samples == 0) return 0.0;
double squared_sum = 0.0;
for (unsigned int n = 0; n < num_samples - delay; ++n) {
squared_sum += (input_[n] - decoded_[n + delay]) *
(input_[n] - decoded_[n + delay]);
squared_sum += (input_[n] - decoded_[channels_ * n + delay]) *
(input_[n] - decoded_[channels_ * n + delay]);
}
return squared_sum / (num_samples - delay);
}
@ -158,11 +166,11 @@ class AudioDecoderTest : public ::testing::Test {
AudioDecoder::SpeechType speech_type1, speech_type2;
EXPECT_EQ(0, decoder_->Init());
size_t dec_len = decoder_->Decode(encoded, enc_len, output1, &speech_type1);
EXPECT_EQ(frame_size_, dec_len);
EXPECT_EQ(frame_size_ * channels_, dec_len);
// Re-init decoder and decode again.
EXPECT_EQ(0, decoder_->Init());
dec_len = decoder_->Decode(encoded_copy, enc_len, output2, &speech_type2);
EXPECT_EQ(frame_size_, dec_len);
EXPECT_EQ(frame_size_ * channels_, dec_len);
for (unsigned int n = 0; n < frame_size_; ++n) {
ASSERT_EQ(output1[n], output2[n]) << "Exit test on first diff; n = " << n;
}
@ -193,6 +201,7 @@ class AudioDecoderTest : public ::testing::Test {
size_t frame_size_;
size_t data_length_;
size_t encoded_bytes_;
size_t channels_;
AudioDecoder* decoder_;
};
@ -461,6 +470,46 @@ class AudioDecoderG722Test : public AudioDecoderTest {
G722EncInst* encoder_;
};
class AudioDecoderG722StereoTest : public AudioDecoderG722Test {
protected:
AudioDecoderG722StereoTest() : AudioDecoderG722Test() {
channels_ = 2;
// Delete the |decoder_| that was created by AudioDecoderG722Test and
// create an AudioDecoderG722Stereo object instead.
delete decoder_;
decoder_ = new AudioDecoderG722Stereo;
assert(decoder_);
}
virtual int EncodeFrame(const int16_t* input, size_t input_len_samples,
uint8_t* output) {
uint8_t* temp_output = new uint8_t[data_length_ * 2];
// Encode a mono payload using the base test class.
int mono_enc_len_bytes =
AudioDecoderG722Test::EncodeFrame(input, input_len_samples,
temp_output);
// The bit-stream consists of 4-bit samples:
// +--------+--------+--------+
// | s0 s1 | s2 s3 | s4 s5 |
// +--------+--------+--------+
//
// Duplicate them to the |output| such that the stereo stream becomes:
// +--------+--------+--------+
// | s0 s0 | s1 s1 | s2 s2 |
// +--------+--------+--------+
EXPECT_LE(mono_enc_len_bytes * 2, static_cast<int>(data_length_ * 2));
uint8_t* output_ptr = output;
for (int i = 0; i < mono_enc_len_bytes; ++i) {
*output_ptr = (temp_output[i] & 0xF0) + (temp_output[i] >> 4);
++output_ptr;
*output_ptr = (temp_output[i] << 4) + (temp_output[i] & 0x0F);
++output_ptr;
}
delete [] temp_output;
return mono_enc_len_bytes * 2;
}
};
class AudioDecoderOpusTest : public AudioDecoderTest {
protected:
AudioDecoderOpusTest() : AudioDecoderTest() {
@ -593,6 +642,20 @@ TEST_F(AudioDecoderG722Test, EncodeDecode) {
EXPECT_FALSE(decoder_->HasDecodePlc());
}
TEST_F(AudioDecoderG722StereoTest, CreateAndDestroy) {
EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderG722_2ch));
}
TEST_F(AudioDecoderG722StereoTest, EncodeDecode) {
int tolerance = 6176;
double mse = 238630.0;
int delay = 22; // Delay from input to output.
EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderG722_2ch));
EncodeDecodeTest(data_length_, tolerance, mse, delay);
ReInitTest();
EXPECT_FALSE(decoder_->HasDecodePlc());
}
TEST_F(AudioDecoderOpusTest, EncodeDecode) {
int tolerance = 6176;
double mse = 238630.0;
@ -622,7 +685,7 @@ TEST(AudioDecoder, CodecSampleRateHz) {
EXPECT_EQ(48000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16Bswb48kHz_2ch));
EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderPCM16B_5ch));
EXPECT_EQ(16000, AudioDecoder::CodecSampleRateHz(kDecoderG722));
EXPECT_EQ(-1, AudioDecoder::CodecSampleRateHz(kDecoderG722_2ch));
EXPECT_EQ(16000, AudioDecoder::CodecSampleRateHz(kDecoderG722_2ch));
EXPECT_EQ(-1, AudioDecoder::CodecSampleRateHz(kDecoderRED));
EXPECT_EQ(-1, AudioDecoder::CodecSampleRateHz(kDecoderAVT));
EXPECT_EQ(8000, AudioDecoder::CodecSampleRateHz(kDecoderCNGnb));
@ -656,7 +719,7 @@ TEST(AudioDecoder, CodecSupported) {
EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16Bswb48kHz_2ch));
EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderPCM16B_5ch));
EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderG722));
EXPECT_FALSE(AudioDecoder::CodecSupported(kDecoderG722_2ch));
EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderG722_2ch));
EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderRED));
EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderAVT));
EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderCNGnb));