From 03b686d2dfe8b7e9dbd81c6b38a092006897dbb0 Mon Sep 17 00:00:00 2001 From: Randell Jesup Date: Sun, 21 Jul 2013 03:47:24 -0400 Subject: [PATCH] Bug 886886: replace fixed-ratio capture resampler in webrtc with speex resample r=derf,jmspeex --- CLOBBER | 4 +- .../resampler/include/resampler.h | 84 +- .../common_audio/resampler/resampler.cc | 1122 ++--------------- .../common_audio/resampler/resampler.gypi | 14 + .../resampler/resampler_unittest.cc | 177 ++- .../audio_coding/main/source/acm_resampler.cc | 4 +- .../voice_engine/output_mixer_internal.cc | 2 +- .../voice_engine/output_mixer_unittest.cc | 28 +- .../webrtc/voice_engine/transmit_mixer.cc | 4 +- 9 files changed, 268 insertions(+), 1171 deletions(-) diff --git a/CLOBBER b/CLOBBER index 30af35d89306..c41fadd77ba8 100644 --- a/CLOBBER +++ b/CLOBBER @@ -17,5 +17,5 @@ # # Modifying this file will now automatically clobber the buildbot machines \o/ # -Bug 895670 - Remove SetupScreen and associated assets -Android-only resource clobber (just rm objdir/mobile/android/base/res) +Bug 886886 - replace fixed-ratio capture resampler in webrtc with speex resampler +Requires a clobber due to modification of a .gypi file without a .gyp or configure.in change diff --git a/media/webrtc/trunk/webrtc/common_audio/resampler/include/resampler.h b/media/webrtc/trunk/webrtc/common_audio/resampler/include/resampler.h index de5e94621177..fd2c033c484d 100644 --- a/media/webrtc/trunk/webrtc/common_audio/resampler/include/resampler.h +++ b/media/webrtc/trunk/webrtc/common_audio/resampler/include/resampler.h @@ -17,98 +17,48 @@ #define WEBRTC_RESAMPLER_RESAMPLER_H_ #include "typedefs.h" +#include "speex/speex_resampler.h" namespace webrtc { -// TODO(andrew): the implementation depends on the exact values of this enum. -// It should be rewritten in a less fragile way. +#define FIXED_RATE_RESAMPLER 0x10 enum ResamplerType { - // 4 MSB = Number of channels - // 4 LSB = Synchronous or asynchronous - - kResamplerSynchronous = 0x10, - kResamplerAsynchronous = 0x11, - kResamplerSynchronousStereo = 0x20, - kResamplerAsynchronousStereo = 0x21, - kResamplerInvalid = 0xff -}; - -// TODO(andrew): doesn't need to be part of the interface. -enum ResamplerMode -{ - kResamplerMode1To1, - kResamplerMode1To2, - kResamplerMode1To3, - kResamplerMode1To4, - kResamplerMode1To6, - kResamplerMode1To12, - kResamplerMode2To3, - kResamplerMode2To11, - kResamplerMode4To11, - kResamplerMode8To11, - kResamplerMode11To16, - kResamplerMode11To32, - kResamplerMode2To1, - kResamplerMode3To1, - kResamplerMode4To1, - kResamplerMode6To1, - kResamplerMode12To1, - kResamplerMode3To2, - kResamplerMode11To2, - kResamplerMode11To4, - kResamplerMode11To8 + kResamplerSynchronous = 0x00, + kResamplerSynchronousStereo = 0x01, + kResamplerFixedSynchronous = 0x00 | FIXED_RATE_RESAMPLER, + kResamplerFixedSynchronousStereo = 0x01 | FIXED_RATE_RESAMPLER, }; class Resampler { - public: Resampler(); // TODO(andrew): use an init function instead. - Resampler(int inFreq, int outFreq, ResamplerType type); + Resampler(int in_freq, int out_freq, ResamplerType type); ~Resampler(); // Reset all states - int Reset(int inFreq, int outFreq, ResamplerType type); + int Reset(int in_freq, int out_freq, ResamplerType type); // Reset all states if any parameter has changed - int ResetIfNeeded(int inFreq, int outFreq, ResamplerType type); + int ResetIfNeeded(int in_freq, int out_freq, ResamplerType type); // Synchronous resampling, all output samples are written to samplesOut - int Push(const int16_t* samplesIn, int lengthIn, int16_t* samplesOut, - int maxLen, int &outLen); - - // Asynchronous resampling, input - int Insert(int16_t* samplesIn, int lengthIn); - - // Asynchronous resampling output, remaining samples are buffered - int Pull(int16_t* samplesOut, int desiredLen, int &outLen); + int Push(const int16_t* samples_in, int length_in, + int16_t* samples_out, int max_len, int &out_len); private: - // Generic pointers since we don't know what states we'll need - void* state1_; - void* state2_; - void* state3_; + bool IsFixedRate() { return !!(type_ & FIXED_RATE_RESAMPLER); } - // Storage if needed - int16_t* in_buffer_; - int16_t* out_buffer_; - int in_buffer_size_; - int out_buffer_size_; - int in_buffer_size_max_; - int out_buffer_size_max_; + SpeexResamplerState* state_; // State - int my_in_frequency_khz_; - int my_out_frequency_khz_; - ResamplerMode my_mode_; - ResamplerType my_type_; - - // Extra instance for stereo - Resampler* slave_left_; - Resampler* slave_right_; + int in_freq_; + int out_freq_; + int channels_; + ResamplerType type_; }; } // namespace webrtc diff --git a/media/webrtc/trunk/webrtc/common_audio/resampler/resampler.cc b/media/webrtc/trunk/webrtc/common_audio/resampler/resampler.cc index 2df6e90a69b1..f933d3e69326 100644 --- a/media/webrtc/trunk/webrtc/common_audio/resampler/resampler.cc +++ b/media/webrtc/trunk/webrtc/common_audio/resampler/resampler.cc @@ -15,1070 +15,128 @@ #include #include +#include #include "signal_processing_library.h" #include "resampler.h" +// TODO(jesup) better adjust per platform ability +// Note: if these are changed (higher), you may need to change the +// KernelDelay values in the unit tests here and in output_mixer. +#if defined(WEBRTC_ANDROID) || defined(WEBRTC_GONK) +#define RESAMPLER_QUALITY 2 +#else +#define RESAMPLER_QUALITY 3 +#endif namespace webrtc { -Resampler::Resampler() +Resampler::Resampler() : state_(NULL), type_(kResamplerSynchronous) { - state1_ = NULL; - state2_ = NULL; - state3_ = NULL; - in_buffer_ = NULL; - out_buffer_ = NULL; - in_buffer_size_ = 0; - out_buffer_size_ = 0; - in_buffer_size_max_ = 0; - out_buffer_size_max_ = 0; - // we need a reset before we will work - my_in_frequency_khz_ = 0; - my_out_frequency_khz_ = 0; - my_mode_ = kResamplerMode1To1; - my_type_ = kResamplerInvalid; - slave_left_ = NULL; - slave_right_ = NULL; + // Note: Push will fail until Reset() is called } -Resampler::Resampler(int inFreq, int outFreq, ResamplerType type) +Resampler::Resampler(int in_freq, int out_freq, ResamplerType type) : + state_(NULL) // all others get initialized in reset { - state1_ = NULL; - state2_ = NULL; - state3_ = NULL; - in_buffer_ = NULL; - out_buffer_ = NULL; - in_buffer_size_ = 0; - out_buffer_size_ = 0; - in_buffer_size_max_ = 0; - out_buffer_size_max_ = 0; - // we need a reset before we will work - my_in_frequency_khz_ = 0; - my_out_frequency_khz_ = 0; - my_mode_ = kResamplerMode1To1; - my_type_ = kResamplerInvalid; - slave_left_ = NULL; - slave_right_ = NULL; - - Reset(inFreq, outFreq, type); + Reset(in_freq, out_freq, type); } Resampler::~Resampler() { - if (state1_) - { - free(state1_); - } - if (state2_) - { - free(state2_); - } - if (state3_) - { - free(state3_); - } - if (in_buffer_) - { - free(in_buffer_); - } - if (out_buffer_) - { - free(out_buffer_); - } - if (slave_left_) - { - delete slave_left_; - } - if (slave_right_) - { - delete slave_right_; - } + if (state_) + { + speex_resampler_destroy(state_); + } } -int Resampler::ResetIfNeeded(int inFreq, int outFreq, ResamplerType type) +int Resampler::ResetIfNeeded(int in_freq, int out_freq, ResamplerType type) { - int tmpInFreq_kHz = inFreq / 1000; - int tmpOutFreq_kHz = outFreq / 1000; + if (!state_ || type != type_) + { + // Note that fixed-rate resamplers where input == output rate will + // have state_ == NULL, and will call Reset() here - but reset won't + // do anything beyond overwrite the member vars unless it needs a + // real resampler. + return Reset(in_freq, out_freq, type); - if ((tmpInFreq_kHz != my_in_frequency_khz_) || (tmpOutFreq_kHz != my_out_frequency_khz_) - || (type != my_type_)) + } else if (in_freq != in_freq_ || out_freq != out_freq_) { + if (speex_resampler_set_rate(state_, in_freq, out_freq) == + RESAMPLER_ERR_SUCCESS) { - return Reset(inFreq, outFreq, type); - } else - { - return 0; + in_freq_ = in_freq; + out_freq_ = out_freq; + return 0; } -} - -int Resampler::Reset(int inFreq, int outFreq, ResamplerType type) -{ - - if (state1_) - { - free(state1_); - state1_ = NULL; - } - if (state2_) - { - free(state2_); - state2_ = NULL; - } - if (state3_) - { - free(state3_); - state3_ = NULL; - } - if (in_buffer_) - { - free(in_buffer_); - in_buffer_ = NULL; - } - if (out_buffer_) - { - free(out_buffer_); - out_buffer_ = NULL; - } - if (slave_left_) - { - delete slave_left_; - slave_left_ = NULL; - } - if (slave_right_) - { - delete slave_right_; - slave_right_ = NULL; - } - - in_buffer_size_ = 0; - out_buffer_size_ = 0; - in_buffer_size_max_ = 0; - out_buffer_size_max_ = 0; - - // This might be overridden if parameters are not accepted. - my_type_ = type; - - // Start with a math exercise, Euclid's algorithm to find the gcd: - - int a = inFreq; - int b = outFreq; - int c = a % b; - while (c != 0) - { - a = b; - b = c; - c = a % b; - } - // b is now the gcd; - - // We need to track what domain we're in. - my_in_frequency_khz_ = inFreq / 1000; - my_out_frequency_khz_ = outFreq / 1000; - - // Scale with GCD - inFreq = inFreq / b; - outFreq = outFreq / b; - - // Do we need stereo? - if ((my_type_ & 0xf0) == 0x20) - { - // Change type to mono - type = static_cast( - ((static_cast(type) & 0x0f) + 0x10)); - slave_left_ = new Resampler(inFreq, outFreq, type); - slave_right_ = new Resampler(inFreq, outFreq, type); - } - - if (inFreq == outFreq) - { - my_mode_ = kResamplerMode1To1; - } else if (inFreq == 1) - { - switch (outFreq) - { - case 2: - my_mode_ = kResamplerMode1To2; - break; - case 3: - my_mode_ = kResamplerMode1To3; - break; - case 4: - my_mode_ = kResamplerMode1To4; - break; - case 6: - my_mode_ = kResamplerMode1To6; - break; - case 12: - my_mode_ = kResamplerMode1To12; - break; - default: - my_type_ = kResamplerInvalid; - return -1; - } - } else if (outFreq == 1) - { - switch (inFreq) - { - case 2: - my_mode_ = kResamplerMode2To1; - break; - case 3: - my_mode_ = kResamplerMode3To1; - break; - case 4: - my_mode_ = kResamplerMode4To1; - break; - case 6: - my_mode_ = kResamplerMode6To1; - break; - case 12: - my_mode_ = kResamplerMode12To1; - break; - default: - my_type_ = kResamplerInvalid; - return -1; - } - } else if ((inFreq == 2) && (outFreq == 3)) - { - my_mode_ = kResamplerMode2To3; - } else if ((inFreq == 2) && (outFreq == 11)) - { - my_mode_ = kResamplerMode2To11; - } else if ((inFreq == 4) && (outFreq == 11)) - { - my_mode_ = kResamplerMode4To11; - } else if ((inFreq == 8) && (outFreq == 11)) - { - my_mode_ = kResamplerMode8To11; - } else if ((inFreq == 3) && (outFreq == 2)) - { - my_mode_ = kResamplerMode3To2; - } else if ((inFreq == 11) && (outFreq == 2)) - { - my_mode_ = kResamplerMode11To2; - } else if ((inFreq == 11) && (outFreq == 4)) - { - my_mode_ = kResamplerMode11To4; - } else if ((inFreq == 11) && (outFreq == 16)) - { - my_mode_ = kResamplerMode11To16; - } else if ((inFreq == 11) && (outFreq == 32)) - { - my_mode_ = kResamplerMode11To32; - } else if ((inFreq == 11) && (outFreq == 8)) - { - my_mode_ = kResamplerMode11To8; - } else - { - my_type_ = kResamplerInvalid; - return -1; - } - - // Now create the states we need - switch (my_mode_) - { - case kResamplerMode1To1: - // No state needed; - break; - case kResamplerMode1To2: - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - break; - case kResamplerMode1To3: - state1_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz)); - WebRtcSpl_ResetResample16khzTo48khz((WebRtcSpl_State16khzTo48khz *)state1_); - break; - case kResamplerMode1To4: - // 1:2 - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - // 2:4 - state2_ = malloc(8 * sizeof(int32_t)); - memset(state2_, 0, 8 * sizeof(int32_t)); - break; - case kResamplerMode1To6: - // 1:2 - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - // 2:6 - state2_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz)); - WebRtcSpl_ResetResample16khzTo48khz((WebRtcSpl_State16khzTo48khz *)state2_); - break; - case kResamplerMode1To12: - // 1:2 - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - // 2:4 - state2_ = malloc(8 * sizeof(int32_t)); - memset(state2_, 0, 8 * sizeof(int32_t)); - // 4:12 - state3_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz)); - WebRtcSpl_ResetResample16khzTo48khz( - (WebRtcSpl_State16khzTo48khz*) state3_); - break; - case kResamplerMode2To3: - // 2:6 - state1_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz)); - WebRtcSpl_ResetResample16khzTo48khz((WebRtcSpl_State16khzTo48khz *)state1_); - // 6:3 - state2_ = malloc(8 * sizeof(int32_t)); - memset(state2_, 0, 8 * sizeof(int32_t)); - break; - case kResamplerMode2To11: - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - - state2_ = malloc(sizeof(WebRtcSpl_State8khzTo22khz)); - WebRtcSpl_ResetResample8khzTo22khz((WebRtcSpl_State8khzTo22khz *)state2_); - break; - case kResamplerMode4To11: - state1_ = malloc(sizeof(WebRtcSpl_State8khzTo22khz)); - WebRtcSpl_ResetResample8khzTo22khz((WebRtcSpl_State8khzTo22khz *)state1_); - break; - case kResamplerMode8To11: - state1_ = malloc(sizeof(WebRtcSpl_State16khzTo22khz)); - WebRtcSpl_ResetResample16khzTo22khz((WebRtcSpl_State16khzTo22khz *)state1_); - break; - case kResamplerMode11To16: - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - - state2_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz)); - WebRtcSpl_ResetResample22khzTo16khz((WebRtcSpl_State22khzTo16khz *)state2_); - break; - case kResamplerMode11To32: - // 11 -> 22 - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - - // 22 -> 16 - state2_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz)); - WebRtcSpl_ResetResample22khzTo16khz((WebRtcSpl_State22khzTo16khz *)state2_); - - // 16 -> 32 - state3_ = malloc(8 * sizeof(int32_t)); - memset(state3_, 0, 8 * sizeof(int32_t)); - - break; - case kResamplerMode2To1: - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - break; - case kResamplerMode3To1: - state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz)); - WebRtcSpl_ResetResample48khzTo16khz((WebRtcSpl_State48khzTo16khz *)state1_); - break; - case kResamplerMode4To1: - // 4:2 - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - // 2:1 - state2_ = malloc(8 * sizeof(int32_t)); - memset(state2_, 0, 8 * sizeof(int32_t)); - break; - case kResamplerMode6To1: - // 6:2 - state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz)); - WebRtcSpl_ResetResample48khzTo16khz((WebRtcSpl_State48khzTo16khz *)state1_); - // 2:1 - state2_ = malloc(8 * sizeof(int32_t)); - memset(state2_, 0, 8 * sizeof(int32_t)); - break; - case kResamplerMode12To1: - // 12:4 - state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz)); - WebRtcSpl_ResetResample48khzTo16khz( - (WebRtcSpl_State48khzTo16khz*) state1_); - // 4:2 - state2_ = malloc(8 * sizeof(int32_t)); - memset(state2_, 0, 8 * sizeof(int32_t)); - // 2:1 - state3_ = malloc(8 * sizeof(int32_t)); - memset(state3_, 0, 8 * sizeof(int32_t)); - break; - case kResamplerMode3To2: - // 3:6 - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - // 6:2 - state2_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz)); - WebRtcSpl_ResetResample48khzTo16khz((WebRtcSpl_State48khzTo16khz *)state2_); - break; - case kResamplerMode11To2: - state1_ = malloc(sizeof(WebRtcSpl_State22khzTo8khz)); - WebRtcSpl_ResetResample22khzTo8khz((WebRtcSpl_State22khzTo8khz *)state1_); - - state2_ = malloc(8 * sizeof(int32_t)); - memset(state2_, 0, 8 * sizeof(int32_t)); - - break; - case kResamplerMode11To4: - state1_ = malloc(sizeof(WebRtcSpl_State22khzTo8khz)); - WebRtcSpl_ResetResample22khzTo8khz((WebRtcSpl_State22khzTo8khz *)state1_); - break; - case kResamplerMode11To8: - state1_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz)); - WebRtcSpl_ResetResample22khzTo16khz((WebRtcSpl_State22khzTo16khz *)state1_); - break; - - } - + return -1; + } else { return 0; + } } -// Synchronous resampling, all output samples are written to samplesOut -int Resampler::Push(const int16_t * samplesIn, int lengthIn, int16_t* samplesOut, - int maxLen, int &outLen) +int Resampler::Reset(int in_freq, int out_freq, ResamplerType type) { - // Check that the resampler is not in asynchronous mode - if (my_type_ & 0x0f) + uint32_t channels = (type == kResamplerSynchronousStereo || + type == kResamplerFixedSynchronousStereo) ? 2 : 1; + + if (state_) + { + speex_resampler_destroy(state_); + } + type_ = type; + channels_ = channels; + in_freq_ = in_freq; + out_freq_ = out_freq; + + // For fixed-rate, same-rate resamples we just memcpy and so don't spin up a resampler + if (in_freq != out_freq || !IsFixedRate()) + { + state_ = speex_resampler_init(channels, in_freq, out_freq, RESAMPLER_QUALITY, NULL); + if (!state_) { - return -1; + return -1; } + } + return 0; +} - // Do we have a stereo signal? - if ((my_type_ & 0xf0) == 0x20) +// Synchronous resampling, all output samples are written to samples_out +// TODO(jesup) Change to take samples-per-channel in and out +int Resampler::Push(const int16_t* samples_in, int length_in, + int16_t* samples_out, int max_len, int &out_len) +{ + if (max_len < length_in) + { + return -1; + } + if (!state_) + { + if (!IsFixedRate() || in_freq_ != out_freq_) { - - // Split up the signal and call the slave object for each channel - - int16_t* left = (int16_t*)malloc(lengthIn * sizeof(int16_t) / 2); - int16_t* right = (int16_t*)malloc(lengthIn * sizeof(int16_t) / 2); - int16_t* out_left = (int16_t*)malloc(maxLen / 2 * sizeof(int16_t)); - int16_t* out_right = - (int16_t*)malloc(maxLen / 2 * sizeof(int16_t)); - int res = 0; - for (int i = 0; i < lengthIn; i += 2) - { - left[i >> 1] = samplesIn[i]; - right[i >> 1] = samplesIn[i + 1]; - } - - // It's OK to overwrite the local parameter, since it's just a copy - lengthIn = lengthIn / 2; - - int actualOutLen_left = 0; - int actualOutLen_right = 0; - // Do resampling for right channel - res |= slave_left_->Push(left, lengthIn, out_left, maxLen / 2, actualOutLen_left); - res |= slave_right_->Push(right, lengthIn, out_right, maxLen / 2, actualOutLen_right); - if (res || (actualOutLen_left != actualOutLen_right)) - { - free(left); - free(right); - free(out_left); - free(out_right); - return -1; - } - - // Reassemble the signal - for (int i = 0; i < actualOutLen_left; i++) - { - samplesOut[i * 2] = out_left[i]; - samplesOut[i * 2 + 1] = out_right[i]; - } - outLen = 2 * actualOutLen_left; - - free(left); - free(right); - free(out_left); - free(out_right); - - return 0; - } - - // Containers for temp samples - int16_t* tmp; - int16_t* tmp_2; - // tmp data for resampling routines - int32_t* tmp_mem; - - switch (my_mode_) - { - case kResamplerMode1To1: - memcpy(samplesOut, samplesIn, lengthIn * sizeof(int16_t)); - outLen = lengthIn; - break; - case kResamplerMode1To2: - if (maxLen < (lengthIn * 2)) - { - return -1; - } - WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut, (int32_t*)state1_); - outLen = lengthIn * 2; - return 0; - case kResamplerMode1To3: - - // We can only handle blocks of 160 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 160) != 0) - { - return -1; - } - if (maxLen < (lengthIn * 3)) - { - return -1; - } - tmp_mem = (int32_t*)malloc(336 * sizeof(int32_t)); - - for (int i = 0; i < lengthIn; i += 160) - { - WebRtcSpl_Resample16khzTo48khz(samplesIn + i, samplesOut + i * 3, - (WebRtcSpl_State16khzTo48khz *)state1_, - tmp_mem); - } - outLen = lengthIn * 3; - free(tmp_mem); - return 0; - case kResamplerMode1To4: - if (maxLen < (lengthIn * 4)) - { - return -1; - } - - tmp = (int16_t*)malloc(sizeof(int16_t) * 2 * lengthIn); - // 1:2 - WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_); - // 2:4 - WebRtcSpl_UpsampleBy2(tmp, lengthIn * 2, samplesOut, (int32_t*)state2_); - outLen = lengthIn * 4; - free(tmp); - return 0; - case kResamplerMode1To6: - // We can only handle blocks of 80 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 80) != 0) - { - return -1; - } - if (maxLen < (lengthIn * 6)) - { - return -1; - } - - //1:2 - - tmp_mem = (int32_t*)malloc(336 * sizeof(int32_t)); - tmp = (int16_t*)malloc(sizeof(int16_t) * 2 * lengthIn); - - WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_); - outLen = lengthIn * 2; - - for (int i = 0; i < outLen; i += 160) - { - WebRtcSpl_Resample16khzTo48khz(tmp + i, samplesOut + i * 3, - (WebRtcSpl_State16khzTo48khz *)state2_, - tmp_mem); - } - outLen = outLen * 3; - free(tmp_mem); - free(tmp); - - return 0; - case kResamplerMode1To12: - // We can only handle blocks of 40 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 40) != 0) { - return -1; - } - if (maxLen < (lengthIn * 12)) { - return -1; - } - - tmp_mem = (int32_t*) malloc(336 * sizeof(int32_t)); - tmp = (int16_t*) malloc(sizeof(int16_t) * 4 * lengthIn); - //1:2 - WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut, - (int32_t*) state1_); - outLen = lengthIn * 2; - //2:4 - WebRtcSpl_UpsampleBy2(samplesOut, outLen, tmp, (int32_t*) state2_); - outLen = outLen * 2; - // 4:12 - for (int i = 0; i < outLen; i += 160) { - // WebRtcSpl_Resample16khzTo48khz() takes a block of 160 samples - // as input and outputs a resampled block of 480 samples. The - // data is now actually in 32 kHz sampling rate, despite the - // function name, and with a resampling factor of three becomes - // 96 kHz. - WebRtcSpl_Resample16khzTo48khz(tmp + i, samplesOut + i * 3, - (WebRtcSpl_State16khzTo48khz*) state3_, - tmp_mem); - } - outLen = outLen * 3; - free(tmp_mem); - free(tmp); - - return 0; - case kResamplerMode2To3: - if (maxLen < (lengthIn * 3 / 2)) - { - return -1; - } - // 2:6 - // We can only handle blocks of 160 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 160) != 0) - { - return -1; - } - tmp = static_cast (malloc(sizeof(int16_t) * lengthIn * 3)); - tmp_mem = (int32_t*)malloc(336 * sizeof(int32_t)); - for (int i = 0; i < lengthIn; i += 160) - { - WebRtcSpl_Resample16khzTo48khz(samplesIn + i, tmp + i * 3, - (WebRtcSpl_State16khzTo48khz *)state1_, - tmp_mem); - } - lengthIn = lengthIn * 3; - // 6:3 - WebRtcSpl_DownsampleBy2(tmp, lengthIn, samplesOut, (int32_t*)state2_); - outLen = lengthIn / 2; - free(tmp); - free(tmp_mem); - return 0; - case kResamplerMode2To11: - - // We can only handle blocks of 80 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 80) != 0) - { - return -1; - } - if (maxLen < ((lengthIn * 11) / 2)) - { - return -1; - } - tmp = (int16_t*)malloc(sizeof(int16_t) * 2 * lengthIn); - // 1:2 - WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_); - lengthIn *= 2; - - tmp_mem = (int32_t*)malloc(98 * sizeof(int32_t)); - - for (int i = 0; i < lengthIn; i += 80) - { - WebRtcSpl_Resample8khzTo22khz(tmp + i, samplesOut + (i * 11) / 4, - (WebRtcSpl_State8khzTo22khz *)state2_, - tmp_mem); - } - outLen = (lengthIn * 11) / 4; - free(tmp_mem); - free(tmp); - return 0; - case kResamplerMode4To11: - - // We can only handle blocks of 80 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 80) != 0) - { - return -1; - } - if (maxLen < ((lengthIn * 11) / 4)) - { - return -1; - } - tmp_mem = (int32_t*)malloc(98 * sizeof(int32_t)); - - for (int i = 0; i < lengthIn; i += 80) - { - WebRtcSpl_Resample8khzTo22khz(samplesIn + i, samplesOut + (i * 11) / 4, - (WebRtcSpl_State8khzTo22khz *)state1_, - tmp_mem); - } - outLen = (lengthIn * 11) / 4; - free(tmp_mem); - return 0; - case kResamplerMode8To11: - // We can only handle blocks of 160 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 160) != 0) - { - return -1; - } - if (maxLen < ((lengthIn * 11) / 8)) - { - return -1; - } - tmp_mem = (int32_t*)malloc(88 * sizeof(int32_t)); - - for (int i = 0; i < lengthIn; i += 160) - { - WebRtcSpl_Resample16khzTo22khz(samplesIn + i, samplesOut + (i * 11) / 8, - (WebRtcSpl_State16khzTo22khz *)state1_, - tmp_mem); - } - outLen = (lengthIn * 11) / 8; - free(tmp_mem); - return 0; - - case kResamplerMode11To16: - // We can only handle blocks of 110 samples - if ((lengthIn % 110) != 0) - { - return -1; - } - if (maxLen < ((lengthIn * 16) / 11)) - { - return -1; - } - - tmp_mem = (int32_t*)malloc(104 * sizeof(int32_t)); - tmp = (int16_t*)malloc((sizeof(int16_t) * lengthIn * 2)); - - WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_); - - for (int i = 0; i < (lengthIn * 2); i += 220) - { - WebRtcSpl_Resample22khzTo16khz(tmp + i, samplesOut + (i / 220) * 160, - (WebRtcSpl_State22khzTo16khz *)state2_, - tmp_mem); - } - - outLen = (lengthIn * 16) / 11; - - free(tmp_mem); - free(tmp); - return 0; - - case kResamplerMode11To32: - - // We can only handle blocks of 110 samples - if ((lengthIn % 110) != 0) - { - return -1; - } - if (maxLen < ((lengthIn * 32) / 11)) - { - return -1; - } - - tmp_mem = (int32_t*)malloc(104 * sizeof(int32_t)); - tmp = (int16_t*)malloc((sizeof(int16_t) * lengthIn * 2)); - - // 11 -> 22 kHz in samplesOut - WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut, (int32_t*)state1_); - - // 22 -> 16 in tmp - for (int i = 0; i < (lengthIn * 2); i += 220) - { - WebRtcSpl_Resample22khzTo16khz(samplesOut + i, tmp + (i / 220) * 160, - (WebRtcSpl_State22khzTo16khz *)state2_, - tmp_mem); - } - - // 16 -> 32 in samplesOut - WebRtcSpl_UpsampleBy2(tmp, (lengthIn * 16) / 11, samplesOut, - (int32_t*)state3_); - - outLen = (lengthIn * 32) / 11; - - free(tmp_mem); - free(tmp); - return 0; - - case kResamplerMode2To1: - if (maxLen < (lengthIn / 2)) - { - return -1; - } - WebRtcSpl_DownsampleBy2(samplesIn, lengthIn, samplesOut, (int32_t*)state1_); - outLen = lengthIn / 2; - return 0; - case kResamplerMode3To1: - // We can only handle blocks of 480 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 480) != 0) - { - return -1; - } - if (maxLen < (lengthIn / 3)) - { - return -1; - } - tmp_mem = (int32_t*)malloc(496 * sizeof(int32_t)); - - for (int i = 0; i < lengthIn; i += 480) - { - WebRtcSpl_Resample48khzTo16khz(samplesIn + i, samplesOut + i / 3, - (WebRtcSpl_State48khzTo16khz *)state1_, - tmp_mem); - } - outLen = lengthIn / 3; - free(tmp_mem); - return 0; - case kResamplerMode4To1: - if (maxLen < (lengthIn / 4)) - { - return -1; - } - tmp = (int16_t*)malloc(sizeof(int16_t) * lengthIn / 2); - // 4:2 - WebRtcSpl_DownsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_); - // 2:1 - WebRtcSpl_DownsampleBy2(tmp, lengthIn / 2, samplesOut, (int32_t*)state2_); - outLen = lengthIn / 4; - free(tmp); - return 0; - - case kResamplerMode6To1: - // We can only handle blocks of 480 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 480) != 0) - { - return -1; - } - if (maxLen < (lengthIn / 6)) - { - return -1; - } - - tmp_mem = (int32_t*)malloc(496 * sizeof(int32_t)); - tmp = (int16_t*)malloc((sizeof(int16_t) * lengthIn) / 3); - - for (int i = 0; i < lengthIn; i += 480) - { - WebRtcSpl_Resample48khzTo16khz(samplesIn + i, tmp + i / 3, - (WebRtcSpl_State48khzTo16khz *)state1_, - tmp_mem); - } - outLen = lengthIn / 3; - free(tmp_mem); - WebRtcSpl_DownsampleBy2(tmp, outLen, samplesOut, (int32_t*)state2_); - free(tmp); - outLen = outLen / 2; - return 0; - case kResamplerMode12To1: - // We can only handle blocks of 480 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 480) != 0) { - return -1; - } - if (maxLen < (lengthIn / 12)) { - return -1; - } - - tmp_mem = (int32_t*) malloc(496 * sizeof(int32_t)); - tmp = (int16_t*) malloc((sizeof(int16_t) * lengthIn) / 3); - tmp_2 = (int16_t*) malloc((sizeof(int16_t) * lengthIn) / 6); - // 12:4 - for (int i = 0; i < lengthIn; i += 480) { - // WebRtcSpl_Resample48khzTo16khz() takes a block of 480 samples - // as input and outputs a resampled block of 160 samples. The - // data is now actually in 96 kHz sampling rate, despite the - // function name, and with a resampling factor of 1/3 becomes - // 32 kHz. - WebRtcSpl_Resample48khzTo16khz(samplesIn + i, tmp + i / 3, - (WebRtcSpl_State48khzTo16khz*) state1_, - tmp_mem); - } - outLen = lengthIn / 3; - free(tmp_mem); - // 4:2 - WebRtcSpl_DownsampleBy2(tmp, outLen, tmp_2, - (int32_t*) state2_); - outLen = outLen / 2; - free(tmp); - // 2:1 - WebRtcSpl_DownsampleBy2(tmp_2, outLen, samplesOut, - (int32_t*) state3_); - free(tmp_2); - outLen = outLen / 2; - return 0; - case kResamplerMode3To2: - if (maxLen < (lengthIn * 2 / 3)) - { - return -1; - } - // 3:6 - tmp = static_cast (malloc(sizeof(int16_t) * lengthIn * 2)); - WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_); - lengthIn *= 2; - // 6:2 - // We can only handle blocks of 480 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 480) != 0) - { - free(tmp); - return -1; - } - tmp_mem = (int32_t*)malloc(496 * sizeof(int32_t)); - for (int i = 0; i < lengthIn; i += 480) - { - WebRtcSpl_Resample48khzTo16khz(tmp + i, samplesOut + i / 3, - (WebRtcSpl_State48khzTo16khz *)state2_, - tmp_mem); - } - outLen = lengthIn / 3; - free(tmp); - free(tmp_mem); - return 0; - case kResamplerMode11To2: - // We can only handle blocks of 220 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 220) != 0) - { - return -1; - } - if (maxLen < ((lengthIn * 2) / 11)) - { - return -1; - } - tmp_mem = (int32_t*)malloc(126 * sizeof(int32_t)); - tmp = (int16_t*)malloc((lengthIn * 4) / 11 * sizeof(int16_t)); - - for (int i = 0; i < lengthIn; i += 220) - { - WebRtcSpl_Resample22khzTo8khz(samplesIn + i, tmp + (i * 4) / 11, - (WebRtcSpl_State22khzTo8khz *)state1_, - tmp_mem); - } - lengthIn = (lengthIn * 4) / 11; - - WebRtcSpl_DownsampleBy2(tmp, lengthIn, samplesOut, (int32_t*)state2_); - outLen = lengthIn / 2; - - free(tmp_mem); - free(tmp); - return 0; - case kResamplerMode11To4: - // We can only handle blocks of 220 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 220) != 0) - { - return -1; - } - if (maxLen < ((lengthIn * 4) / 11)) - { - return -1; - } - tmp_mem = (int32_t*)malloc(126 * sizeof(int32_t)); - - for (int i = 0; i < lengthIn; i += 220) - { - WebRtcSpl_Resample22khzTo8khz(samplesIn + i, samplesOut + (i * 4) / 11, - (WebRtcSpl_State22khzTo8khz *)state1_, - tmp_mem); - } - outLen = (lengthIn * 4) / 11; - free(tmp_mem); - return 0; - case kResamplerMode11To8: - // We can only handle blocks of 160 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 220) != 0) - { - return -1; - } - if (maxLen < ((lengthIn * 8) / 11)) - { - return -1; - } - tmp_mem = (int32_t*)malloc(104 * sizeof(int32_t)); - - for (int i = 0; i < lengthIn; i += 220) - { - WebRtcSpl_Resample22khzTo16khz(samplesIn + i, samplesOut + (i * 8) / 11, - (WebRtcSpl_State22khzTo16khz *)state1_, - tmp_mem); - } - outLen = (lengthIn * 8) / 11; - free(tmp_mem); - return 0; - break; - + // Since we initialize to a non-Fixed type, Push() will fail + // until Reset() is called + return -1; } + // Fixed-rate, same-freq "resample" - use memcpy, which avoids + // filtering and delay. For non-fixed rates, where we might tweak + // from 48000->48000 to 48000->48001 for drift, we need to resample + // (and filter) all the time to avoid glitches on rate changes. + memcpy(samples_out, samples_in, length_in*sizeof(*samples_in)); + out_len = length_in; return 0; -} - -// Asynchronous resampling, input -int Resampler::Insert(int16_t * samplesIn, int lengthIn) -{ - if (my_type_ != kResamplerAsynchronous) - { - return -1; - } - int sizeNeeded, tenMsblock; - - // Determine need for size of outBuffer - sizeNeeded = out_buffer_size_ + ((lengthIn + in_buffer_size_) * my_out_frequency_khz_) - / my_in_frequency_khz_; - if (sizeNeeded > out_buffer_size_max_) - { - // Round the value upwards to complete 10 ms blocks - tenMsblock = my_out_frequency_khz_ * 10; - sizeNeeded = (sizeNeeded / tenMsblock + 1) * tenMsblock; - out_buffer_ = (int16_t*)realloc(out_buffer_, sizeNeeded * sizeof(int16_t)); - out_buffer_size_max_ = sizeNeeded; - } - - // If we need to use inBuffer, make sure all input data fits there. - - tenMsblock = my_in_frequency_khz_ * 10; - if (in_buffer_size_ || (lengthIn % tenMsblock)) - { - // Check if input buffer size is enough - if ((in_buffer_size_ + lengthIn) > in_buffer_size_max_) - { - // Round the value upwards to complete 10 ms blocks - sizeNeeded = ((in_buffer_size_ + lengthIn) / tenMsblock + 1) * tenMsblock; - in_buffer_ = (int16_t*)realloc(in_buffer_, - sizeNeeded * sizeof(int16_t)); - in_buffer_size_max_ = sizeNeeded; - } - // Copy in data to input buffer - memcpy(in_buffer_ + in_buffer_size_, samplesIn, lengthIn * sizeof(int16_t)); - - // Resample all available 10 ms blocks - int lenOut; - int dataLenToResample = (in_buffer_size_ / tenMsblock) * tenMsblock; - Push(in_buffer_, dataLenToResample, out_buffer_ + out_buffer_size_, - out_buffer_size_max_ - out_buffer_size_, lenOut); - out_buffer_size_ += lenOut; - - // Save the rest - memmove(in_buffer_, in_buffer_ + dataLenToResample, - (in_buffer_size_ - dataLenToResample) * sizeof(int16_t)); - in_buffer_size_ -= dataLenToResample; - } else - { - // Just resample - int lenOut; - Push(in_buffer_, lengthIn, out_buffer_ + out_buffer_size_, - out_buffer_size_max_ - out_buffer_size_, lenOut); - out_buffer_size_ += lenOut; - } - - return 0; -} - -// Asynchronous resampling output, remaining samples are buffered -int Resampler::Pull(int16_t* samplesOut, int desiredLen, int &outLen) -{ - if (my_type_ != kResamplerAsynchronous) - { - return -1; - } - - // Check that we have enough data - if (desiredLen <= out_buffer_size_) - { - // Give out the date - memcpy(samplesOut, out_buffer_, desiredLen * sizeof(int32_t)); - - // Shuffle down remaining - memmove(out_buffer_, out_buffer_ + desiredLen, - (out_buffer_size_ - desiredLen) * sizeof(int16_t)); - - // Update remaining size - out_buffer_size_ -= desiredLen; - - return 0; - } else - { - return -1; - } + } + assert(channels_ == 1 || channels_ == 2); + spx_uint32_t len = length_in = (length_in >> (channels_ - 1)); + spx_uint32_t out = (spx_uint32_t) (max_len >> (channels_ - 1)); + if ((speex_resampler_process_interleaved_int(state_, samples_in, &len, + samples_out, &out) != RESAMPLER_ERR_SUCCESS) || + len != (spx_uint32_t) length_in) + { + return -1; + } + out_len = (int) (channels_ * out); + return 0; } } // namespace webrtc diff --git a/media/webrtc/trunk/webrtc/common_audio/resampler/resampler.gypi b/media/webrtc/trunk/webrtc/common_audio/resampler/resampler.gypi index ac429eef5ef9..90a14fc4b3cc 100644 --- a/media/webrtc/trunk/webrtc/common_audio/resampler/resampler.gypi +++ b/media/webrtc/trunk/webrtc/common_audio/resampler/resampler.gypi @@ -17,10 +17,24 @@ 'include_dirs': [ 'include', ], + 'conditions': [ + ['build_with_mozilla==1', { + 'include_dirs': [ + '$(DEPTH)/dist/include', + ], + }], + ], 'direct_dependent_settings': { 'include_dirs': [ 'include', ], + 'conditions': [ + ['build_with_mozilla==1', { + 'include_dirs': [ + '$(DEPTH)/dist/include', + ], + }], + ], }, 'sources': [ 'include/resampler.h', diff --git a/media/webrtc/trunk/webrtc/common_audio/resampler/resampler_unittest.cc b/media/webrtc/trunk/webrtc/common_audio/resampler/resampler_unittest.cc index 9b1061ae1e0c..a7cfd68babb6 100644 --- a/media/webrtc/trunk/webrtc/common_audio/resampler/resampler_unittest.cc +++ b/media/webrtc/trunk/webrtc/common_audio/resampler/resampler_unittest.cc @@ -8,6 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include + #include "gtest/gtest.h" #include "common_audio/resampler/include/resampler.h" @@ -18,10 +20,7 @@ namespace webrtc { namespace { const ResamplerType kTypes[] = { kResamplerSynchronous, - kResamplerAsynchronous, kResamplerSynchronousStereo, - kResamplerAsynchronousStereo - // kResamplerInvalid excluded }; const size_t kTypesSize = sizeof(kTypes) / sizeof(*kTypes); @@ -31,7 +30,7 @@ const int kRates[] = { 8000, 16000, 32000, - 44000, + 44100, 48000, kMaxRate }; @@ -39,26 +38,19 @@ const size_t kRatesSize = sizeof(kRates) / sizeof(*kRates); const int kMaxChannels = 2; const size_t kDataSize = static_cast (kMaxChannels * kMaxRate / 100); -// TODO(andrew): should we be supporting these combinations? -bool ValidRates(int in_rate, int out_rate) { - // Not the most compact notation, for clarity. - if ((in_rate == 44000 && (out_rate == 48000 || out_rate == 96000)) || - (out_rate == 44000 && (in_rate == 48000 || in_rate == 96000))) { - return false; - } - - return true; -} - class ResamplerTest : public testing::Test { protected: ResamplerTest(); virtual void SetUp(); virtual void TearDown(); + void RunResampleTest(int channels, + int src_sample_rate_hz, + int dst_sample_rate_hz); Resampler rs_; int16_t data_in_[kDataSize]; int16_t data_out_[kDataSize]; + int16_t data_reference_[kDataSize]; }; ResamplerTest::ResamplerTest() {} @@ -83,34 +75,119 @@ TEST_F(ResamplerTest, Reset) { ss << "Input rate: " << kRates[i] << ", output rate: " << kRates[j] << ", type: " << kTypes[k]; SCOPED_TRACE(ss.str()); - if (ValidRates(kRates[i], kRates[j])) - EXPECT_EQ(0, rs_.Reset(kRates[i], kRates[j], kTypes[k])); - else - EXPECT_EQ(-1, rs_.Reset(kRates[i], kRates[j], kTypes[k])); + EXPECT_EQ(0, rs_.Reset(kRates[i], kRates[j], kTypes[k])); } } } } -// TODO(tlegrand): Replace code inside the two tests below with a function -// with number of channels and ResamplerType as input. -TEST_F(ResamplerTest, Synchronous) { - for (size_t i = 0; i < kRatesSize; ++i) { - for (size_t j = 0; j < kRatesSize; ++j) { - std::ostringstream ss; - ss << "Input rate: " << kRates[i] << ", output rate: " << kRates[j]; - SCOPED_TRACE(ss.str()); +// Sets the signal value to increase by |data| with every sample. Floats are +// used so non-integer values result in rounding error, but not an accumulating +// error. +void SetMonoFrame(int16_t* buffer, float data, int sample_rate_hz) { + for (int i = 0; i < sample_rate_hz / 100; i++) { + buffer[i] = data * i; + } +} - if (ValidRates(kRates[i], kRates[j])) { - int in_length = kRates[i] / 100; - int out_length = 0; - EXPECT_EQ(0, rs_.Reset(kRates[i], kRates[j], kResamplerSynchronous)); - EXPECT_EQ(0, rs_.Push(data_in_, in_length, data_out_, kDataSize, - out_length)); - EXPECT_EQ(kRates[j] / 100, out_length); - } else { - EXPECT_EQ(-1, rs_.Reset(kRates[i], kRates[j], kResamplerSynchronous)); - } +// Sets the signal value to increase by |left| and |right| with every sample in +// each channel respectively. +void SetStereoFrame(int16_t* buffer, float left, float right, + int sample_rate_hz) { + for (int i = 0; i < sample_rate_hz / 100; i++) { + buffer[i * 2] = left * i; + buffer[i * 2 + 1] = right * i; + } +} + +// Computes the best SNR based on the error between |ref_frame| and +// |test_frame|. It allows for a sample delay between the signals to +// compensate for the resampling delay. +float ComputeSNR(const int16_t* reference, const int16_t* test, + int sample_rate_hz, int channels, int max_delay) { + float best_snr = 0; + int best_delay = 0; + int samples_per_channel = sample_rate_hz/100; + for (int delay = 0; delay < max_delay; delay++) { + float mse = 0; + float variance = 0; + for (int i = 0; i < samples_per_channel * channels - delay; i++) { + int error = reference[i] - test[i + delay]; + mse += error * error; + variance += reference[i] * reference[i]; + } + float snr = 100; // We assign 100 dB to the zero-error case. + if (mse > 0) + snr = 10 * log10(variance / mse); + if (snr > best_snr) { + best_snr = snr; + best_delay = delay; + } + } + printf("SNR=%.1f dB at delay=%d\n", best_snr, best_delay); + return best_snr; +} + +void ResamplerTest::RunResampleTest(int channels, + int src_sample_rate_hz, + int dst_sample_rate_hz) { + Resampler resampler; // Create a new one with every test. + const int16_t kSrcLeft = 60; // Shouldn't overflow for any used sample rate. + const int16_t kSrcRight = 30; + const float kResamplingFactor = (1.0 * src_sample_rate_hz) / + dst_sample_rate_hz; + const float kDstLeft = kResamplingFactor * kSrcLeft; + const float kDstRight = kResamplingFactor * kSrcRight; + if (channels == 1) + SetMonoFrame(data_in_, kSrcLeft, src_sample_rate_hz); + else + SetStereoFrame(data_in_, kSrcLeft, kSrcRight, src_sample_rate_hz); + + if (channels == 1) { + SetMonoFrame(data_out_, 0, dst_sample_rate_hz); + SetMonoFrame(data_reference_, kDstLeft, dst_sample_rate_hz); + } else { + SetStereoFrame(data_out_, 0, 0, dst_sample_rate_hz); + SetStereoFrame(data_reference_, kDstLeft, kDstRight, dst_sample_rate_hz); + } + + // The speex resampler has a known delay dependent on quality and rates, + // which we approximate here. Multiplying by two gives us a crude maximum + // for any resampling, as the old resampler typically (but not always) + // has lower delay. The actual delay is calculated internally based on the + // filter length in the QualityMap. + static const int kInputKernelDelaySamples = 16*3; + const int max_delay = std::min(1.0f, 1/kResamplingFactor) * + kInputKernelDelaySamples * channels * 2; + printf("(%d, %d Hz) -> (%d, %d Hz) ", // SNR reported on the same line later. + channels, src_sample_rate_hz, channels, dst_sample_rate_hz); + + int in_length = channels * src_sample_rate_hz / 100; + int out_length = 0; + EXPECT_EQ(0, rs_.Reset(src_sample_rate_hz, dst_sample_rate_hz, + (channels == 1 ? + kResamplerSynchronous : + kResamplerSynchronousStereo))); + EXPECT_EQ(0, rs_.Push(data_in_, in_length, data_out_, kDataSize, + out_length)); + EXPECT_EQ(channels * dst_sample_rate_hz / 100, out_length); + + // EXPECT_EQ(0, Resample(src_frame_, &resampler, &dst_frame_)); + EXPECT_GT(ComputeSNR(data_reference_, data_out_, dst_sample_rate_hz, + channels, max_delay), 40.0f); +} + +TEST_F(ResamplerTest, Synchronous) { + // Number of channels is 1, mono mode. + const int kChannels = 1; + // We don't attempt to be exhaustive here, but just get good coverage. Some + // combinations of rates will not be resampled, and some give an odd + // resampling factor which makes it more difficult to evaluate. + const int kSampleRates[] = {16000, 32000, 44100, 48000}; + const int kSampleRatesSize = sizeof(kSampleRates) / sizeof(*kSampleRates); + for (int src_rate = 0; src_rate < kSampleRatesSize; src_rate++) { + for (int dst_rate = 0; dst_rate < kSampleRatesSize; dst_rate++) { + RunResampleTest(kChannels, kSampleRates[src_rate], kSampleRates[dst_rate]); } } } @@ -118,24 +195,14 @@ TEST_F(ResamplerTest, Synchronous) { TEST_F(ResamplerTest, SynchronousStereo) { // Number of channels is 2, stereo mode. const int kChannels = 2; - for (size_t i = 0; i < kRatesSize; ++i) { - for (size_t j = 0; j < kRatesSize; ++j) { - std::ostringstream ss; - ss << "Input rate: " << kRates[i] << ", output rate: " << kRates[j]; - SCOPED_TRACE(ss.str()); - - if (ValidRates(kRates[i], kRates[j])) { - int in_length = kChannels * kRates[i] / 100; - int out_length = 0; - EXPECT_EQ(0, rs_.Reset(kRates[i], kRates[j], - kResamplerSynchronousStereo)); - EXPECT_EQ(0, rs_.Push(data_in_, in_length, data_out_, kDataSize, - out_length)); - EXPECT_EQ(kChannels * kRates[j] / 100, out_length); - } else { - EXPECT_EQ(-1, rs_.Reset(kRates[i], kRates[j], - kResamplerSynchronousStereo)); - } + // We don't attempt to be exhaustive here, but just get good coverage. Some + // combinations of rates will not be resampled, and some give an odd + // resampling factor which makes it more difficult to evaluate. + const int kSampleRates[] = {16000, 32000, 44100, 48000}; + const int kSampleRatesSize = sizeof(kSampleRates) / sizeof(*kSampleRates); + for (int src_rate = 0; src_rate < kSampleRatesSize; src_rate++) { + for (int dst_rate = 0; dst_rate < kSampleRatesSize; dst_rate++) { + RunResampleTest(kChannels, kSampleRates[src_rate], kSampleRates[dst_rate]); } } } diff --git a/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_resampler.cc b/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_resampler.cc index 261864956bbe..ff4b4a13706f 100644 --- a/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_resampler.cc +++ b/media/webrtc/trunk/webrtc/modules/audio_coding/main/source/acm_resampler.cc @@ -47,8 +47,8 @@ int16_t ACMResampler::Resample10Msec(const int16_t* in_audio, int32_t ret; ResamplerType type; - type = (num_audio_channels == 1) ? kResamplerSynchronous : - kResamplerSynchronousStereo; + type = (num_audio_channels == 1) ? kResamplerFixedSynchronous : + kResamplerFixedSynchronousStereo; ret = resampler_.ResetIfNeeded(in_freq_hz, out_freq_hz, type); if (ret < 0) { diff --git a/media/webrtc/trunk/webrtc/voice_engine/output_mixer_internal.cc b/media/webrtc/trunk/webrtc/voice_engine/output_mixer_internal.cc index dfa7d95b8ffa..74e25bb76f60 100644 --- a/media/webrtc/trunk/webrtc/voice_engine/output_mixer_internal.cc +++ b/media/webrtc/trunk/webrtc/voice_engine/output_mixer_internal.cc @@ -35,7 +35,7 @@ int RemixAndResample(const AudioFrame& src_frame, } const ResamplerType resampler_type = audio_ptr_num_channels == 1 ? - kResamplerSynchronous : kResamplerSynchronousStereo; + kResamplerFixedSynchronous : kResamplerFixedSynchronousStereo; if (resampler->ResetIfNeeded(src_frame.sample_rate_hz_, dst_frame->sample_rate_hz_, resampler_type) == -1) { diff --git a/media/webrtc/trunk/webrtc/voice_engine/output_mixer_unittest.cc b/media/webrtc/trunk/webrtc/voice_engine/output_mixer_unittest.cc index dbcb25103ae3..4ca538e8f84f 100644 --- a/media/webrtc/trunk/webrtc/voice_engine/output_mixer_unittest.cc +++ b/media/webrtc/trunk/webrtc/voice_engine/output_mixer_unittest.cc @@ -80,13 +80,14 @@ void VerifyParams(const AudioFrame& ref_frame, const AudioFrame& test_frame) { } // Computes the best SNR based on the error between |ref_frame| and -// |test_frame|. It allows for up to a 30 sample delay between the signals to +// |test_frame|. It allows for a sample delay between the signals to // compensate for the resampling delay. -float ComputeSNR(const AudioFrame& ref_frame, const AudioFrame& test_frame) { +float ComputeSNR(const AudioFrame& ref_frame, const AudioFrame& test_frame, + int max_delay) { VerifyParams(ref_frame, test_frame); float best_snr = 0; int best_delay = 0; - for (int delay = 0; delay < 30; delay++) { + for (int delay = 0; delay < max_delay; delay++) { float mse = 0; float variance = 0; for (int i = 0; i < ref_frame.samples_per_channel_ * @@ -147,18 +148,23 @@ void OutputMixerTest::RunResampleTest(int src_channels, SetStereoFrame(&golden_frame_, kDstLeft, kDstRight, dst_sample_rate_hz); } + // The speex resampler has a known delay dependent on quality and rates, + // which we approximate here. Multiplying by two gives us a crude maximum + // for any resampling, as the old resampler typically (but not always) + // has lower delay. The actual delay is calculated internally based on the + // filter length in the QualityMap. + static const int kInputKernelDelaySamples = 16*3; + const int max_delay = std::min(1.0f, 1/kResamplingFactor) * + kInputKernelDelaySamples * dst_channels * 2; printf("(%d, %d Hz) -> (%d, %d Hz) ", // SNR reported on the same line later. src_channels, src_sample_rate_hz, dst_channels, dst_sample_rate_hz); EXPECT_EQ(0, RemixAndResample(src_frame_, &resampler, &dst_frame_)); - EXPECT_GT(ComputeSNR(golden_frame_, dst_frame_), 40.0f); -} - -TEST_F(OutputMixerTest, RemixAndResampleFailsWithBadSampleRate) { - SetMonoFrame(&dst_frame_, 10, 44100); - EXPECT_EQ(-1, RemixAndResample(src_frame_, &resampler_, &dst_frame_)); - VerifyFramesAreEqual(src_frame_, dst_frame_); + EXPECT_GT(ComputeSNR(golden_frame_, dst_frame_, max_delay), 40.0f); } +// These two tests assume memcpy() (no delay and no filtering) for input +// freq == output freq && same channels. RemixAndResample uses 'Fixed' +// resamplers to enable this behavior TEST_F(OutputMixerTest, RemixAndResampleCopyFrameSucceeds) { // Stereo -> stereo. SetStereoFrame(&src_frame_, 10, 10); @@ -193,7 +199,7 @@ TEST_F(OutputMixerTest, RemixAndResampleSucceeds) { // We don't attempt to be exhaustive here, but just get good coverage. Some // combinations of rates will not be resampled, and some give an odd // resampling factor which makes it more difficult to evaluate. - const int kSampleRates[] = {16000, 32000, 48000}; + const int kSampleRates[] = {16000, 32000, 44100, 48000}; const int kSampleRatesSize = sizeof(kSampleRates) / sizeof(*kSampleRates); const int kChannels[] = {1, 2}; const int kChannelsSize = sizeof(kChannels) / sizeof(*kChannels); diff --git a/media/webrtc/trunk/webrtc/voice_engine/transmit_mixer.cc b/media/webrtc/trunk/webrtc/voice_engine/transmit_mixer.cc index 4e6e6d6796fc..24f9ada7b06f 100644 --- a/media/webrtc/trunk/webrtc/voice_engine/transmit_mixer.cc +++ b/media/webrtc/trunk/webrtc/voice_engine/transmit_mixer.cc @@ -1152,6 +1152,8 @@ bool TransmitMixer::IsRecordingMic() } // TODO(andrew): use RemixAndResample for this. +// Note that if drift compensation is done here, a buffering stage will be +// needed and this will need to switch to non-fixed resamples. int TransmitMixer::GenerateAudioFrame(const int16_t audio[], int samples_per_channel, int num_channels, @@ -1179,7 +1181,7 @@ int TransmitMixer::GenerateAudioFrame(const int16_t audio[], } ResamplerType resampler_type = (num_channels == 1) ? - kResamplerSynchronous : kResamplerSynchronousStereo; + kResamplerFixedSynchronous : kResamplerFixedSynchronousStereo; if (_audioResampler.ResetIfNeeded(sample_rate_hz, destination_rate,