From 2d0538922885a2ec310e2c9a462402d90dcdf512 Mon Sep 17 00:00:00 2001 From: "bjornv@webrtc.org" Date: Wed, 29 Oct 2014 10:29:16 +0000 Subject: common_audio: Removed macro WEBRTC_SPL_RSHIFT_W32 Replaces the trivial macro WEBRTC_SPL_RSHIFT_W32 with >> at various places in common_audio and removes it. BUG=3348,3353 TESTED=locally on linux and trybots R=kwiberg@webrtc.org Review URL: https://webrtc-codereview.appspot.com/26989004 git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@7558 4adac7df-926f-26a2-2b94-8c16560cd09d --- common_audio/signal_processing/complex_fft.c | 69 ++++++++++------------ .../signal_processing/division_operations.c | 15 ++--- .../include/signal_processing_library.h | 1 - common_audio/signal_processing/levinson_durbin.c | 58 ++++++++---------- common_audio/signal_processing/lpc_to_refl_coef.c | 4 +- .../signal_processing_unittest.cc | 1 - common_audio/signal_processing/spl_sqrt.c | 22 ++++--- common_audio/signal_processing/splitting_filter.c | 10 ++-- 8 files changed, 79 insertions(+), 101 deletions(-) (limited to 'common_audio') diff --git a/common_audio/signal_processing/complex_fft.c b/common_audio/signal_processing/complex_fft.c index c8230647..74b4258a 100644 --- a/common_audio/signal_processing/complex_fft.c +++ b/common_audio/signal_processing/complex_fft.c @@ -65,18 +65,16 @@ int WebRtcSpl_ComplexFFT(int16_t frfi[], int stages, int mode) { j = i + l; - tr32 = WEBRTC_SPL_RSHIFT_W32((WEBRTC_SPL_MUL_16_16(wr, frfi[2 * j]) - - WEBRTC_SPL_MUL_16_16(wi, frfi[2 * j + 1])), 15); + tr32 = (wr * frfi[2 * j] - wi * frfi[2 * j + 1]) >> 15; - ti32 = WEBRTC_SPL_RSHIFT_W32((WEBRTC_SPL_MUL_16_16(wr, frfi[2 * j + 1]) - + WEBRTC_SPL_MUL_16_16(wi, frfi[2 * j])), 15); + ti32 = (wr * frfi[2 * j + 1] + wi * frfi[2 * j]) >> 15; qr32 = (int32_t)frfi[2 * i]; qi32 = (int32_t)frfi[2 * i + 1]; - frfi[2 * j] = (int16_t)WEBRTC_SPL_RSHIFT_W32(qr32 - tr32, 1); - frfi[2 * j + 1] = (int16_t)WEBRTC_SPL_RSHIFT_W32(qi32 - ti32, 1); - frfi[2 * i] = (int16_t)WEBRTC_SPL_RSHIFT_W32(qr32 + tr32, 1); - frfi[2 * i + 1] = (int16_t)WEBRTC_SPL_RSHIFT_W32(qi32 + ti32, 1); + frfi[2 * j] = (int16_t)((qr32 - tr32) >> 1); + frfi[2 * j + 1] = (int16_t)((qi32 - ti32) >> 1); + frfi[2 * i] = (int16_t)((qr32 + tr32) >> 1); + frfi[2 * i + 1] = (int16_t)((qi32 + ti32) >> 1); } } @@ -135,20 +133,20 @@ int WebRtcSpl_ComplexFFT(int16_t frfi[], int stages, int mode) + WEBRTC_SPL_MUL_16_16(wi, frfi[2 * j]) + CFFTRND; #endif - tr32 = WEBRTC_SPL_RSHIFT_W32(tr32, 15 - CFFTSFT); - ti32 = WEBRTC_SPL_RSHIFT_W32(ti32, 15 - CFFTSFT); + tr32 >>= 15 - CFFTSFT; + ti32 >>= 15 - CFFTSFT; qr32 = ((int32_t)frfi[2 * i]) << CFFTSFT; qi32 = ((int32_t)frfi[2 * i + 1]) << CFFTSFT; - frfi[2 * j] = (int16_t)WEBRTC_SPL_RSHIFT_W32( - (qr32 - tr32 + CFFTRND2), 1 + CFFTSFT); - frfi[2 * j + 1] = (int16_t)WEBRTC_SPL_RSHIFT_W32( - (qi32 - ti32 + CFFTRND2), 1 + CFFTSFT); - frfi[2 * i] = (int16_t)WEBRTC_SPL_RSHIFT_W32( - (qr32 + tr32 + CFFTRND2), 1 + CFFTSFT); - frfi[2 * i + 1] = (int16_t)WEBRTC_SPL_RSHIFT_W32( - (qi32 + ti32 + CFFTRND2), 1 + CFFTSFT); + frfi[2 * j] = (int16_t)( + (qr32 - tr32 + CFFTRND2) >> (1 + CFFTSFT)); + frfi[2 * j + 1] = (int16_t)( + (qi32 - ti32 + CFFTRND2) >> (1 + CFFTSFT)); + frfi[2 * i] = (int16_t)( + (qr32 + tr32 + CFFTRND2) >> (1 + CFFTSFT)); + frfi[2 * i + 1] = (int16_t)( + (qi32 + ti32 + CFFTRND2) >> (1 + CFFTSFT)); } } @@ -219,19 +217,16 @@ int WebRtcSpl_ComplexIFFT(int16_t frfi[], int stages, int mode) { j = i + l; - tr32 = WEBRTC_SPL_RSHIFT_W32((WEBRTC_SPL_MUL_16_16_RSFT(wr, frfi[2 * j], 0) - - WEBRTC_SPL_MUL_16_16_RSFT(wi, frfi[2 * j + 1], 0)), 15); + tr32 = (wr * frfi[2 * j] - wi * frfi[2 * j + 1]) >> 15; - ti32 = WEBRTC_SPL_RSHIFT_W32( - (WEBRTC_SPL_MUL_16_16_RSFT(wr, frfi[2 * j + 1], 0) - + WEBRTC_SPL_MUL_16_16_RSFT(wi,frfi[2*j],0)), 15); + ti32 = (wr * frfi[2 * j + 1] + wi * frfi[2 * j]) >> 15; qr32 = (int32_t)frfi[2 * i]; qi32 = (int32_t)frfi[2 * i + 1]; - frfi[2 * j] = (int16_t)WEBRTC_SPL_RSHIFT_W32(qr32 - tr32, shift); - frfi[2 * j + 1] = (int16_t)WEBRTC_SPL_RSHIFT_W32(qi32 - ti32, shift); - frfi[2 * i] = (int16_t)WEBRTC_SPL_RSHIFT_W32(qr32 + tr32, shift); - frfi[2 * i + 1] = (int16_t)WEBRTC_SPL_RSHIFT_W32(qi32 + ti32, shift); + frfi[2 * j] = (int16_t)((qr32 - tr32) >> shift); + frfi[2 * j + 1] = (int16_t)((qi32 - ti32) >> shift); + frfi[2 * i] = (int16_t)((qr32 + tr32) >> shift); + frfi[2 * i + 1] = (int16_t)((qi32 + ti32) >> shift); } } } else @@ -281,20 +276,20 @@ int WebRtcSpl_ComplexIFFT(int16_t frfi[], int stages, int mode) ti32 = WEBRTC_SPL_MUL_16_16(wr, frfi[2 * j + 1]) + WEBRTC_SPL_MUL_16_16(wi, frfi[2 * j]) + CIFFTRND; #endif - tr32 = WEBRTC_SPL_RSHIFT_W32(tr32, 15 - CIFFTSFT); - ti32 = WEBRTC_SPL_RSHIFT_W32(ti32, 15 - CIFFTSFT); + tr32 >>= 15 - CIFFTSFT; + ti32 >>= 15 - CIFFTSFT; qr32 = ((int32_t)frfi[2 * i]) << CIFFTSFT; qi32 = ((int32_t)frfi[2 * i + 1]) << CIFFTSFT; - frfi[2 * j] = (int16_t)WEBRTC_SPL_RSHIFT_W32((qr32 - tr32+round2), - shift+CIFFTSFT); - frfi[2 * j + 1] = (int16_t)WEBRTC_SPL_RSHIFT_W32( - (qi32 - ti32 + round2), shift + CIFFTSFT); - frfi[2 * i] = (int16_t)WEBRTC_SPL_RSHIFT_W32((qr32 + tr32 + round2), - shift + CIFFTSFT); - frfi[2 * i + 1] = (int16_t)WEBRTC_SPL_RSHIFT_W32( - (qi32 + ti32 + round2), shift + CIFFTSFT); + frfi[2 * j] = (int16_t)( + (qr32 - tr32 + round2) >> (shift + CIFFTSFT)); + frfi[2 * j + 1] = (int16_t)( + (qi32 - ti32 + round2) >> (shift + CIFFTSFT)); + frfi[2 * i] = (int16_t)( + (qr32 + tr32 + round2) >> (shift + CIFFTSFT)); + frfi[2 * i + 1] = (int16_t)( + (qi32 + ti32 + round2) >> (shift + CIFFTSFT)); } } diff --git a/common_audio/signal_processing/division_operations.c b/common_audio/signal_processing/division_operations.c index e9554f44..6aeb0fb2 100644 --- a/common_audio/signal_processing/division_operations.c +++ b/common_audio/signal_processing/division_operations.c @@ -113,23 +113,20 @@ int32_t WebRtcSpl_DivW32HiLow(int32_t num, int16_t den_hi, int16_t den_low) tmpW32 = (int32_t)0x7fffffffL - tmpW32; // result in Q30 (tmpW32 = 2.0-(den*approx)) // Store tmpW32 in hi and low format - tmp_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmpW32, 16); - tmp_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((tmpW32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)tmp_hi, 16)), 1); + tmp_hi = (int16_t)(tmpW32 >> 16); + tmp_low = (int16_t)((tmpW32 - ((int32_t)tmp_hi << 16)) >> 1); // tmpW32 = 1/den in Q29 tmpW32 = ((WEBRTC_SPL_MUL_16_16(tmp_hi, approx) + (WEBRTC_SPL_MUL_16_16(tmp_low, approx) >> 15)) << 1); // 1/den in hi and low format - tmp_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmpW32, 16); - tmp_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((tmpW32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)tmp_hi, 16)), 1); + tmp_hi = (int16_t)(tmpW32 >> 16); + tmp_low = (int16_t)((tmpW32 - ((int32_t)tmp_hi << 16)) >> 1); // Store num in hi and low format - num_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(num, 16); - num_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((num - - WEBRTC_SPL_LSHIFT_W32((int32_t)num_hi, 16)), 1); + num_hi = (int16_t)(num >> 16); + num_low = (int16_t)((num - ((int32_t)num_hi << 16)) >> 1); // num * (1/den) by 32 bit multiplication (result in Q28) diff --git a/common_audio/signal_processing/include/signal_processing_library.h b/common_audio/signal_processing/include/signal_processing_library.h index d13973ca..2bdfc23c 100644 --- a/common_audio/signal_processing/include/signal_processing_library.h +++ b/common_audio/signal_processing/include/signal_processing_library.h @@ -87,7 +87,6 @@ // Shifting with negative numbers not allowed // We cannot do casting here due to signed/unsigned problem -#define WEBRTC_SPL_RSHIFT_W32(x, c) ((x) >> (c)) #define WEBRTC_SPL_LSHIFT_W32(x, c) ((x) << (c)) #define WEBRTC_SPL_RSHIFT_U32(x, c) ((uint32_t)(x) >> (c)) diff --git a/common_audio/signal_processing/levinson_durbin.c b/common_audio/signal_processing/levinson_durbin.c index 5c5d2246..29f2398d 100644 --- a/common_audio/signal_processing/levinson_durbin.c +++ b/common_audio/signal_processing/levinson_durbin.c @@ -45,9 +45,8 @@ int16_t WebRtcSpl_LevinsonDurbin(int32_t *R, int16_t *A, int16_t *K, { temp1W32 = WEBRTC_SPL_LSHIFT_W32(R[i], norm); // Put R in hi and low format - R_hi[i] = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - R_low[i] = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)R_hi[i], 16)), 1); + R_hi[i] = (int16_t)(temp1W32 >> 16); + R_low[i] = (int16_t)((temp1W32 - ((int32_t)R_hi[i] << 16)) >> 1); } // K = A[1] = -R[1] / R[0] @@ -63,19 +62,17 @@ int16_t WebRtcSpl_LevinsonDurbin(int32_t *R, int16_t *A, int16_t *K, } // Put K in hi and low format - K_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - K_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)K_hi, 16)), 1); + K_hi = (int16_t)(temp1W32 >> 16); + K_low = (int16_t)((temp1W32 - ((int32_t)K_hi << 16)) >> 1); // Store first reflection coefficient K[0] = K_hi; - temp1W32 = WEBRTC_SPL_RSHIFT_W32(temp1W32, 4); // A[1] in Q27 + temp1W32 >>= 4; // A[1] in Q27. // Put A[1] in hi and low format - A_hi[1] = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - A_low[1] = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)A_hi[1], 16)), 1); + A_hi[1] = (int16_t)(temp1W32 >> 16); + A_low[1] = (int16_t)((temp1W32 - ((int32_t)A_hi[1] << 16)) >> 1); // Alpha = R[0] * (1-K^2) @@ -86,9 +83,8 @@ int16_t WebRtcSpl_LevinsonDurbin(int32_t *R, int16_t *A, int16_t *K, temp1W32 = (int32_t)0x7fffffffL - temp1W32; // temp1W32 = (1 - K[0]*K[0]) in Q31 // Store temp1W32 = 1 - K[0]*K[0] on hi and low format - tmp_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - tmp_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)tmp_hi, 16)), 1); + tmp_hi = (int16_t)(temp1W32 >> 16); + tmp_low = (int16_t)((temp1W32 - ((int32_t)tmp_hi << 16)) >> 1); // Calculate Alpha in Q31 temp1W32 = ((WEBRTC_SPL_MUL_16_16(R_hi[0], tmp_hi) @@ -99,9 +95,8 @@ int16_t WebRtcSpl_LevinsonDurbin(int32_t *R, int16_t *A, int16_t *K, Alpha_exp = WebRtcSpl_NormW32(temp1W32); temp1W32 = WEBRTC_SPL_LSHIFT_W32(temp1W32, Alpha_exp); - Alpha_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - Alpha_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)Alpha_hi, 16)), 1); + Alpha_hi = (int16_t)(temp1W32 >> 16); + Alpha_low = (int16_t)((temp1W32 - ((int32_t)Alpha_hi << 16)) >> 1); // Perform the iterative calculations in the Levinson-Durbin algorithm @@ -155,9 +150,8 @@ int16_t WebRtcSpl_LevinsonDurbin(int32_t *R, int16_t *A, int16_t *K, } // Put K on hi and low format - K_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp3W32, 16); - K_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp3W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)K_hi, 16)), 1); + K_hi = (int16_t)(temp3W32 >> 16); + K_low = (int16_t)((temp3W32 - ((int32_t)K_hi << 16)) >> 1); // Store Reflection coefficient in Q15 K[i - 1] = K_hi; @@ -188,18 +182,18 @@ int16_t WebRtcSpl_LevinsonDurbin(int32_t *R, int16_t *A, int16_t *K, + (WEBRTC_SPL_MUL_16_16(K_low, A_hi[i-j]) >> 15)) << 1); // Put Anew in hi and low format - A_upd_hi[j] = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - A_upd_low[j] = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)A_upd_hi[j], 16)), 1); + A_upd_hi[j] = (int16_t)(temp1W32 >> 16); + A_upd_low[j] = (int16_t)( + (temp1W32 - ((int32_t)A_upd_hi[j] << 16)) >> 1); } // temp3W32 = K in Q27 (Convert from Q31 to Q27) - temp3W32 = WEBRTC_SPL_RSHIFT_W32(temp3W32, 4); + temp3W32 >>= 4; // Store Anew in hi and low format - A_upd_hi[i] = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp3W32, 16); - A_upd_low[i] = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp3W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)A_upd_hi[i], 16)), 1); + A_upd_hi[i] = (int16_t)(temp3W32 >> 16); + A_upd_low[i] = (int16_t)( + (temp3W32 - ((int32_t)A_upd_hi[i] << 16)) >> 1); // Alpha = Alpha * (1-K^2) @@ -210,9 +204,8 @@ int16_t WebRtcSpl_LevinsonDurbin(int32_t *R, int16_t *A, int16_t *K, temp1W32 = (int32_t)0x7fffffffL - temp1W32; // 1 - K*K in Q31 // Convert 1- K^2 in hi and low format - tmp_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - tmp_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)tmp_hi, 16)), 1); + tmp_hi = (int16_t)(temp1W32 >> 16); + tmp_low = (int16_t)((temp1W32 - ((int32_t)tmp_hi << 16)) >> 1); // Calculate Alpha = Alpha * (1-K^2) in Q31 temp1W32 = ((WEBRTC_SPL_MUL_16_16(Alpha_hi, tmp_hi) @@ -224,9 +217,8 @@ int16_t WebRtcSpl_LevinsonDurbin(int32_t *R, int16_t *A, int16_t *K, norm = WebRtcSpl_NormW32(temp1W32); temp1W32 = WEBRTC_SPL_LSHIFT_W32(temp1W32, norm); - Alpha_hi = (int16_t)WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - Alpha_low = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32 - - WEBRTC_SPL_LSHIFT_W32((int32_t)Alpha_hi, 16)), 1); + Alpha_hi = (int16_t)(temp1W32 >> 16); + Alpha_low = (int16_t)((temp1W32 - ((int32_t)Alpha_hi << 16)) >> 1); // Update the total normalization of Alpha Alpha_exp = Alpha_exp + norm; @@ -253,7 +245,7 @@ int16_t WebRtcSpl_LevinsonDurbin(int32_t *R, int16_t *A, int16_t *K, temp1W32 = WEBRTC_SPL_LSHIFT_W32((int32_t)A_hi[i], 16) + WEBRTC_SPL_LSHIFT_W32((int32_t)A_low[i], 1); // Round and store upper word - A[i] = (int16_t)WEBRTC_SPL_RSHIFT_W32((temp1W32<<1)+(int32_t)32768, 16); + A[i] = (int16_t)(((temp1W32 << 1) + 32768) >> 16); } return 1; // Stable filters } diff --git a/common_audio/signal_processing/lpc_to_refl_coef.c b/common_audio/signal_processing/lpc_to_refl_coef.c index b1a34d48..5fb4d859 100644 --- a/common_audio/signal_processing/lpc_to_refl_coef.c +++ b/common_audio/signal_processing/lpc_to_refl_coef.c @@ -32,7 +32,7 @@ void WebRtcSpl_LpcToReflCoef(int16_t* a16, int use_order, int16_t* k16) // (1 - k^2) in Q30 tmp_inv_denom32 = ((int32_t)1073741823) - WEBRTC_SPL_MUL_16_16(k16[m], k16[m]); // (1 - k^2) in Q15 - tmp_inv_denom16 = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp_inv_denom32, 15); + tmp_inv_denom16 = (int16_t)(tmp_inv_denom32 >> 15); for (k = 1; k <= m; k++) { @@ -47,7 +47,7 @@ void WebRtcSpl_LpcToReflCoef(int16_t* a16, int use_order, int16_t* k16) for (k = 1; k < m; k++) { - a16[k] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32[k], 1); //Q13>>1 => Q12 + a16[k] = (int16_t)(tmp32[k] >> 1); // Q13>>1 => Q12 } tmp32[m] = WEBRTC_SPL_SAT(8191, tmp32[m], -8191); diff --git a/common_audio/signal_processing/signal_processing_unittest.cc b/common_audio/signal_processing/signal_processing_unittest.cc index aa1f1787..611d2bfa 100644 --- a/common_audio/signal_processing/signal_processing_unittest.cc +++ b/common_audio/signal_processing/signal_processing_unittest.cc @@ -65,7 +65,6 @@ TEST_F(SplTest, MacroTest) { // Shifting with negative numbers not allowed // We cannot do casting here due to signed/unsigned problem - EXPECT_EQ(8191, WEBRTC_SPL_RSHIFT_W32(a, 1)); EXPECT_EQ(32766, WEBRTC_SPL_LSHIFT_W32(a, 1)); EXPECT_EQ(8191u, WEBRTC_SPL_RSHIFT_U32(a, 1)); diff --git a/common_audio/signal_processing/spl_sqrt.c b/common_audio/signal_processing/spl_sqrt.c index fff73c03..1de6ccd7 100644 --- a/common_audio/signal_processing/spl_sqrt.c +++ b/common_audio/signal_processing/spl_sqrt.c @@ -35,11 +35,10 @@ int32_t WebRtcSpl_SqrtLocal(int32_t in) + 0.875*((x_half)^5) */ - B = in; + B = in / 2; - B = WEBRTC_SPL_RSHIFT_W32(B, 1); // B = in/2 B = B - ((int32_t)0x40000000); // B = in/2 - 1/2 - x_half = (int16_t)WEBRTC_SPL_RSHIFT_W32(B, 16);// x_half = x/2 = (in-1)/2 + x_half = (int16_t)(B >> 16); // x_half = x/2 = (in-1)/2 B = B + ((int32_t)0x40000000); // B = 1 + x/2 B = B + ((int32_t)0x40000000); // Add 0.5 twice (since 1.0 does not exist in Q31) @@ -47,19 +46,18 @@ int32_t WebRtcSpl_SqrtLocal(int32_t in) A = -x2; // A = -(x/2)^2 B = B + (A >> 1); // B = 1 + x/2 - 0.5*(x/2)^2 - A = WEBRTC_SPL_RSHIFT_W32(A, 16); + A >>= 16; A = A * A * 2; // A = (x/2)^4 - t16 = (int16_t)WEBRTC_SPL_RSHIFT_W32(A, 16); + t16 = (int16_t)(A >> 16); B = B + WEBRTC_SPL_MUL_16_16(-20480, t16) * 2; // B = B - 0.625*A // After this, B = 1 + x/2 - 0.5*(x/2)^2 - 0.625*(x/2)^4 - t16 = (int16_t)WEBRTC_SPL_RSHIFT_W32(A, 16); A = WEBRTC_SPL_MUL_16_16(x_half, t16) * 2; // A = (x/2)^5 - t16 = (int16_t)WEBRTC_SPL_RSHIFT_W32(A, 16); + t16 = (int16_t)(A >> 16); B = B + WEBRTC_SPL_MUL_16_16(28672, t16) * 2; // B = B + 0.875*A // After this, B = 1 + x/2 - 0.5*(x/2)^2 - 0.625*(x/2)^4 + 0.875*(x/2)^5 - t16 = (int16_t)WEBRTC_SPL_RSHIFT_W32(x2, 16); + t16 = (int16_t)(x2 >> 16); A = WEBRTC_SPL_MUL_16_16(x_half, t16) * 2; // A = x/2^3 B = B + (A >> 1); // B = B + 0.5*A @@ -154,7 +152,7 @@ int32_t WebRtcSpl_Sqrt(int32_t value) A = WEBRTC_SPL_WORD32_MAX; } - x_norm = (int16_t)WEBRTC_SPL_RSHIFT_W32(A, 16); // x_norm = AH + x_norm = (int16_t)(A >> 16); // x_norm = AH nshift = (sh / 2); assert(nshift >= 0); @@ -166,17 +164,17 @@ int32_t WebRtcSpl_Sqrt(int32_t value) if (2 * nshift == sh) { // Even shift value case - t16 = (int16_t)WEBRTC_SPL_RSHIFT_W32(A, 16); // t16 = AH + t16 = (int16_t)(A >> 16); // t16 = AH A = WEBRTC_SPL_MUL_16_16(k_sqrt_2, t16) * 2; // A = 1/sqrt(2)*t16 A = A + ((int32_t)32768); // Round off A = A & ((int32_t)0x7fff0000); // Round off - A = WEBRTC_SPL_RSHIFT_W32(A, 15); // A = A>>16 + A >>= 15; // A = A>>16 } else { - A = WEBRTC_SPL_RSHIFT_W32(A, 16); // A = A>>16 + A >>= 16; // A = A>>16 } A = A & ((int32_t)0x0000ffff); diff --git a/common_audio/signal_processing/splitting_filter.c b/common_audio/signal_processing/splitting_filter.c index 4f6430c2..15c37240 100644 --- a/common_audio/signal_processing/splitting_filter.c +++ b/common_audio/signal_processing/splitting_filter.c @@ -156,12 +156,10 @@ void WebRtcSpl_AnalysisQMF(const int16_t* in_data, int in_data_length, // branches to get upper & lower band. for (i = 0; i < band_length; i++) { - tmp = filter1[i] + filter2[i] + 1024; - tmp = WEBRTC_SPL_RSHIFT_W32(tmp, 11); + tmp = (filter1[i] + filter2[i] + 1024) >> 11; low_band[i] = WebRtcSpl_SatW32ToW16(tmp); - tmp = filter1[i] - filter2[i] + 1024; - tmp = WEBRTC_SPL_RSHIFT_W32(tmp, 11); + tmp = (filter1[i] - filter2[i] + 1024) >> 11; high_band[i] = WebRtcSpl_SatW32ToW16(tmp); } } @@ -200,10 +198,10 @@ void WebRtcSpl_SynthesisQMF(const int16_t* low_band, const int16_t* high_band, // saturation. for (i = 0, k = 0; i < band_length; i++) { - tmp = WEBRTC_SPL_RSHIFT_W32(filter2[i] + 512, 10); + tmp = (filter2[i] + 512) >> 10; out_data[k++] = WebRtcSpl_SatW32ToW16(tmp); - tmp = WEBRTC_SPL_RSHIFT_W32(filter1[i] + 512, 10); + tmp = (filter1[i] + 512) >> 10; out_data[k++] = WebRtcSpl_SatW32ToW16(tmp); } -- cgit v1.2.3 From bce1329490c4cc7c1313cfee1afa41c721daa699 Mon Sep 17 00:00:00 2001 From: "andrew@webrtc.org" Date: Thu, 30 Oct 2014 03:40:10 +0000 Subject: Refactor audio conversion functions. Use a consistent naming scheme that can be understood at the callsite without having to refer to documentation. Remove hacks in AudioBuffer intended to maintain bit-exactness with the float path. The conversions etc. are now all natural, and instead we enforce close but not bit-exact output between the two paths. Output of ApmTest.Process: https://paste.googleplex.com/5931055831842816 R=aluebs@webrtc.org, bjornv@webrtc.org, kwiberg@webrtc.org Review URL: https://webrtc-codereview.appspot.com/13049004 git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@7561 4adac7df-926f-26a2-2b94-8c16560cd09d --- common_audio/audio_util.cc | 22 ++++++--- common_audio/audio_util_unittest.cc | 48 ++++++++++++++----- common_audio/include/audio_util.h | 54 +++++++++++++--------- common_audio/resampler/push_sinc_resampler.cc | 2 +- .../resampler/push_sinc_resampler_unittest.cc | 9 ++-- common_audio/wav_writer.cc | 2 +- 6 files changed, 89 insertions(+), 48 deletions(-) (limited to 'common_audio') diff --git a/common_audio/audio_util.cc b/common_audio/audio_util.cc index f2936b07..2047295c 100644 --- a/common_audio/audio_util.cc +++ b/common_audio/audio_util.cc @@ -14,19 +14,29 @@ namespace webrtc { -void RoundToInt16(const float* src, size_t size, int16_t* dest) { +void FloatToS16(const float* src, size_t size, int16_t* dest) { for (size_t i = 0; i < size; ++i) - dest[i] = RoundToInt16(src[i]); + dest[i] = FloatToS16(src[i]); } -void ScaleAndRoundToInt16(const float* src, size_t size, int16_t* dest) { +void S16ToFloat(const int16_t* src, size_t size, float* dest) { for (size_t i = 0; i < size; ++i) - dest[i] = ScaleAndRoundToInt16(src[i]); + dest[i] = S16ToFloat(src[i]); } -void ScaleToFloat(const int16_t* src, size_t size, float* dest) { +void FloatS16ToS16(const float* src, size_t size, int16_t* dest) { for (size_t i = 0; i < size; ++i) - dest[i] = ScaleToFloat(src[i]); + dest[i] = FloatS16ToS16(src[i]); +} + +void FloatToFloatS16(const float* src, size_t size, float* dest) { + for (size_t i = 0; i < size; ++i) + dest[i] = FloatToFloatS16(src[i]); +} + +void FloatS16ToFloat(const float* src, size_t size, float* dest) { + for (size_t i = 0; i < size; ++i) + dest[i] = FloatS16ToFloat(src[i]); } } // namespace webrtc diff --git a/common_audio/audio_util_unittest.cc b/common_audio/audio_util_unittest.cc index bf9ad812..2cdf5381 100644 --- a/common_audio/audio_util_unittest.cc +++ b/common_audio/audio_util_unittest.cc @@ -26,35 +26,59 @@ void ExpectArraysEq(const float* ref, const float* test, int length) { } } -TEST(AudioUtilTest, RoundToInt16) { +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}; + int16_t output[kSize]; + FloatToS16(kInput, kSize, output); + ExpectArraysEq(kReference, output, kSize); +} + +TEST(AudioUtilTest, S16ToFloat) { + const int kSize = 7; + const int16_t kInput[kSize] = {0, 1, -1, 16384, -16384, 32767, -32768}; + const float kReference[kSize] = { + 0.f, 1.f / 32767.f, -1.f / 32768.f, 16384.f / 32767.f, -0.5f, 1.f, -1.f}; + float output[kSize]; + S16ToFloat(kInput, kSize, output); + ExpectArraysEq(kReference, output, kSize); +} + +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 int16_t kReference[kSize] = {0, 0, 1, 0, -1, 32767, -32768}; int16_t output[kSize]; - RoundToInt16(kInput, kSize, output); + FloatS16ToS16(kInput, kSize, output); ExpectArraysEq(kReference, output, kSize); } -TEST(AudioUtilTest, ScaleAndRoundToInt16) { +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 int16_t kReference[kSize] = { - 0, 0, 1, 0, -1, 32767, -32768, 32767, -32768}; - int16_t output[kSize]; - ScaleAndRoundToInt16(kInput, kSize, output); + 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); } -TEST(AudioUtilTest, ScaleToFloat) { - const int kSize = 7; - const int16_t kInput[kSize] = {0, 1, -1, 16384, -16384, 32767, -32768}; +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, 1.f / 32767.f, -1.f / 32768.f, 16384.f / 32767.f, -0.5f, 1.f, -1.f}; + 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]; - ScaleToFloat(kInput, kSize, output); + FloatS16ToFloat(kInput, kSize, output); ExpectArraysEq(kReference, output, kSize); } diff --git a/common_audio/include/audio_util.h b/common_audio/include/audio_util.h index 0ce034be..5a4e8151 100644 --- a/common_audio/include/audio_util.h +++ b/common_audio/include/audio_util.h @@ -20,18 +20,11 @@ namespace webrtc { typedef std::numeric_limits limits_int16; -static inline int16_t RoundToInt16(float v) { - const float kMaxRound = limits_int16::max() - 0.5f; - const float kMinRound = limits_int16::min() + 0.5f; - if (v > 0) - return v >= kMaxRound ? limits_int16::max() : - static_cast(v + 0.5f); - return v <= kMinRound ? limits_int16::min() : - static_cast(v - 0.5f); -} - -// Scale (from [-1, 1]) and round to full-range int16 with clamping. -static inline int16_t ScaleAndRoundToInt16(float v) { +// The conversion functions use the following naming convention: +// S16: int16_t [-32768, 32767] +// Float: float [-1.0, 1.0] +// FloatS16: float [-32768.0, 32767.0] +static inline int16_t FloatToS16(float v) { if (v > 0) return v >= 1 ? limits_int16::max() : static_cast(v * limits_int16::max() + 0.5f); @@ -39,22 +32,37 @@ static inline int16_t ScaleAndRoundToInt16(float v) { static_cast(-v * limits_int16::min() - 0.5f); } -// Scale to float [-1, 1]. -static inline float ScaleToFloat(int16_t v) { - const float kMaxInt16Inverse = 1.f / limits_int16::max(); - const float kMinInt16Inverse = 1.f / limits_int16::min(); +static inline float S16ToFloat(int16_t v) { + static const float kMaxInt16Inverse = 1.f / limits_int16::max(); + static const float kMinInt16Inverse = 1.f / limits_int16::min(); return v * (v > 0 ? kMaxInt16Inverse : -kMinInt16Inverse); } -// Round |size| elements of |src| to int16 with clamping and write to |dest|. -void RoundToInt16(const float* src, size_t size, int16_t* dest); +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(v + 0.5f); + return v <= kMinRound ? limits_int16::min() : + static_cast(v - 0.5f); +} -// Scale (from [-1, 1]) and round |size| elements of |src| to full-range int16 -// with clamping and write to |dest|. -void ScaleAndRoundToInt16(const float* src, size_t size, int16_t* dest); +static inline float FloatToFloatS16(float v) { + return v > 0 ? v * limits_int16::max() : -v * limits_int16::min(); +} + +static inline float FloatS16ToFloat(float v) { + static const float kMaxInt16Inverse = 1.f / limits_int16::max(); + static const float kMinInt16Inverse = 1.f / limits_int16::min(); + return v * (v > 0 ? kMaxInt16Inverse : -kMinInt16Inverse); +} -// Scale |size| elements of |src| to float [-1, 1] and write to |dest|. -void ScaleToFloat(const int16_t* src, size_t size, float* dest); +void FloatToS16(const float* src, size_t size, int16_t* dest); +void S16ToFloat(const int16_t* src, size_t size, float* dest); +void FloatS16ToS16(const float* src, size_t size, int16_t* dest); +void FloatToFloatS16(const float* src, size_t size, float* dest); +void FloatS16ToFloat(const float* src, size_t size, float* dest); // Deinterleave audio from |interleaved| to the channel buffers pointed to // by |deinterleaved|. There must be sufficient space allocated in the diff --git a/common_audio/resampler/push_sinc_resampler.cc b/common_audio/resampler/push_sinc_resampler.cc index 02755590..49e2e12e 100644 --- a/common_audio/resampler/push_sinc_resampler.cc +++ b/common_audio/resampler/push_sinc_resampler.cc @@ -40,7 +40,7 @@ int PushSincResampler::Resample(const int16_t* source, source_ptr_int_ = source; // Pass NULL as the float source to have Run() read from the int16 source. Resample(NULL, source_length, float_buffer_.get(), destination_frames_); - RoundToInt16(float_buffer_.get(), destination_frames_, destination); + FloatS16ToS16(float_buffer_.get(), destination_frames_, destination); source_ptr_int_ = NULL; return destination_frames_; } diff --git a/common_audio/resampler/push_sinc_resampler_unittest.cc b/common_audio/resampler/push_sinc_resampler_unittest.cc index 1ca4fdf9..90ac0cf0 100644 --- a/common_audio/resampler/push_sinc_resampler_unittest.cc +++ b/common_audio/resampler/push_sinc_resampler_unittest.cc @@ -160,16 +160,15 @@ void PushSincResamplerTest::ResampleTest(bool int_format) { resampler_source.Run(input_samples, source.get()); if (int_format) { for (int i = 0; i < kNumBlocks; ++i) { - ScaleAndRoundToInt16( - &source[i * input_block_size], input_block_size, source_int.get()); + FloatToS16(&source[i * input_block_size], input_block_size, + source_int.get()); EXPECT_EQ(output_block_size, resampler.Resample(source_int.get(), input_block_size, destination_int.get(), output_block_size)); - ScaleToFloat(destination_int.get(), - output_block_size, - &resampled_destination[i * output_block_size]); + S16ToFloat(destination_int.get(), output_block_size, + &resampled_destination[i * output_block_size]); } } else { for (int i = 0; i < kNumBlocks; ++i) { diff --git a/common_audio/wav_writer.cc b/common_audio/wav_writer.cc index 30a220c2..52449789 100644 --- a/common_audio/wav_writer.cc +++ b/common_audio/wav_writer.cc @@ -68,7 +68,7 @@ void WavFile::WriteSamples(const float* samples, size_t num_samples) { for (size_t i = 0; i < num_samples; i += kChunksize) { int16_t isamples[kChunksize]; const size_t chunk = std::min(kChunksize, num_samples - i); - RoundToInt16(samples + i, chunk, isamples); + FloatS16ToS16(samples + i, chunk, isamples); WriteSamples(isamples, chunk); } } -- cgit v1.2.3 From d7062cc078dbb80d775336a4870dbbc750fc9b32 Mon Sep 17 00:00:00 2001 From: "henrik.lundin@webrtc.org" Date: Thu, 30 Oct 2014 13:23:25 +0000 Subject: Creating a C++ wrapper class for VAD Also adding a mock. This work is part of an ongoing effort to encapsulate encoders in AudioEncoder classes. The CNG encoder will also be implemented as an AudioEncoder class, and will also contain a VAD C++ wrapper. BUG=3926 R=bjornv@webrtc.org Review URL: https://webrtc-codereview.appspot.com/27839004 git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@7570 4adac7df-926f-26a2-2b94-8c16560cd09d --- common_audio/BUILD.gn | 2 ++ common_audio/common_audio.gyp | 2 ++ common_audio/vad/include/vad.h | 45 ++++++++++++++++++++++++++++++++++++++++ common_audio/vad/mock/mock_vad.h | 34 ++++++++++++++++++++++++++++++ common_audio/vad/vad.cc | 43 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 126 insertions(+) create mode 100644 common_audio/vad/include/vad.h create mode 100644 common_audio/vad/mock/mock_vad.h create mode 100644 common_audio/vad/vad.cc (limited to 'common_audio') diff --git a/common_audio/BUILD.gn b/common_audio/BUILD.gn index ad49d17f..1338a756 100644 --- a/common_audio/BUILD.gn +++ b/common_audio/BUILD.gn @@ -71,7 +71,9 @@ source_set("common_audio") { "signal_processing/splitting_filter.c", "signal_processing/sqrt_of_one_minus_x_squared.c", "signal_processing/vector_scaling_operations.c", + "vad/include/vad.h", "vad/include/webrtc_vad.h", + "vad/vad.cc", "vad/webrtc_vad.c", "vad/vad_core.c", "vad/vad_core.h", diff --git a/common_audio/common_audio.gyp b/common_audio/common_audio.gyp index 6c1b7960..70d6909c 100644 --- a/common_audio/common_audio.gyp +++ b/common_audio/common_audio.gyp @@ -85,7 +85,9 @@ 'signal_processing/splitting_filter.c', 'signal_processing/sqrt_of_one_minus_x_squared.c', 'signal_processing/vector_scaling_operations.c', + 'vad/include/vad.h', 'vad/include/webrtc_vad.h', + 'vad/vad.cc', 'vad/webrtc_vad.c', 'vad/vad_core.c', 'vad/vad_core.h', diff --git a/common_audio/vad/include/vad.h b/common_audio/vad/include/vad.h new file mode 100644 index 00000000..f1d12123 --- /dev/null +++ b/common_audio/vad/include/vad.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_COMMON_AUDIO_VAD_INCLUDE_VAD_H_ +#define WEBRTC_COMMON_AUDIO_VAD_INCLUDE_VAD_H_ + +#include "webrtc/base/checks.h" +#include "webrtc/common_audio/vad/include/webrtc_vad.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// This is a C++ wrapper class for WebRtcVad. +class Vad { + public: + enum Aggressiveness { + kVadNormal = 0, + kVadLowBitrate = 1, + kVadAggressive = 2, + kVadVeryAggressive = 3 + }; + + enum Activity { kPassive = 0, kActive = 1, kError = -1 }; + + explicit Vad(enum Aggressiveness mode); + + virtual ~Vad(); + + enum Activity VoiceActivity(const int16_t* audio, + size_t num_samples, + int sample_rate_hz); + + private: + VadInst* handle_; +}; + +} // namespace webrtc +#endif // WEBRTC_COMMON_AUDIO_VAD_INCLUDE_VAD_H_ diff --git a/common_audio/vad/mock/mock_vad.h b/common_audio/vad/mock/mock_vad.h new file mode 100644 index 00000000..f1d8c226 --- /dev/null +++ b/common_audio/vad/mock/mock_vad.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_COMMON_AUDIO_VAD_MOCK_MOCK_VAD_H_ +#define WEBRTC_COMMON_AUDIO_VAD_MOCK_MOCK_VAD_H_ + +#include "webrtc/common_audio/vad/include/vad.h" + +#include "testing/gmock/include/gmock/gmock.h" + +namespace webrtc { + +class MockVad : public Vad { + public: + explicit MockVad(enum Aggressiveness mode) {} + virtual ~MockVad() { Die(); } + MOCK_METHOD0(Die, void()); + + MOCK_METHOD3(VoiceActivity, + enum Activity(const int16_t* audio, + size_t num_samples, + int sample_rate_hz)); +}; + +} // namespace webrtc + +#endif // WEBRTC_COMMON_AUDIO_VAD_MOCK_MOCK_VAD_H_ diff --git a/common_audio/vad/vad.cc b/common_audio/vad/vad.cc new file mode 100644 index 00000000..9cc0c198 --- /dev/null +++ b/common_audio/vad/vad.cc @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/common_audio/vad/include/vad.h" + +#include "webrtc/base/checks.h" + +namespace webrtc { + +Vad::Vad(enum Aggressiveness mode) { + CHECK_EQ(WebRtcVad_Create(&handle_), 0); + CHECK_EQ(WebRtcVad_Init(handle_), 0); + CHECK_EQ(WebRtcVad_set_mode(handle_, mode), 0); +} + +Vad::~Vad() { + WebRtcVad_Free(handle_); +} + +enum Vad::Activity Vad::VoiceActivity(const int16_t* audio, + size_t num_samples, + int sample_rate_hz) { + int ret = WebRtcVad_Process( + handle_, sample_rate_hz, audio, static_cast(num_samples)); + switch (ret) { + case 0: + return kPassive; + case 1: + return kActive; + default: + DCHECK(false) << "WebRtcVad_Process returned an error."; + return kError; + } +} + +} // namespace webrtc -- cgit v1.2.3 From a1f6cf7fb2f1cadc61fedd98dd4c8411f27669c9 Mon Sep 17 00:00:00 2001 From: "andrew@webrtc.org" Date: Fri, 31 Oct 2014 04:58:14 +0000 Subject: Revert "Revert part of r7561, "Refactor audio conversion functions."" This restores the conversion changes to AudioProcessing originally added in r7561, with minor alterations to ensure it passes all tests. TBR=kwiberg Review URL: https://webrtc-codereview.appspot.com/28899004 git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@7574 4adac7df-926f-26a2-2b94-8c16560cd09d --- common_audio/include/audio_util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'common_audio') diff --git a/common_audio/include/audio_util.h b/common_audio/include/audio_util.h index 5a4e8151..767b21c5 100644 --- a/common_audio/include/audio_util.h +++ b/common_audio/include/audio_util.h @@ -49,7 +49,7 @@ static inline int16_t FloatS16ToS16(float v) { } static inline float FloatToFloatS16(float v) { - return v > 0 ? v * limits_int16::max() : -v * limits_int16::min(); + return v * (v > 0 ? limits_int16::max() : -limits_int16::min()); } static inline float FloatS16ToFloat(float v) { -- cgit v1.2.3 From f8586ccefc9f27335c4ae464b314062e22ea4381 Mon Sep 17 00:00:00 2001 From: "andrew@webrtc.org" Date: Fri, 31 Oct 2014 16:26:17 +0000 Subject: Build fix for MIPS32R6. Exclude MIPS optimizations for MIPS32R6 build since some of the instructions are not supported. This is temporary fix, until the MIPS32R6 code is added. R=andrew@webrtc.org Review URL: https://webrtc-codereview.appspot.com/25989004 Patch from Ljubomir Papuga . git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@7580 4adac7df-926f-26a2-2b94-8c16560cd09d --- common_audio/common_audio.gyp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'common_audio') diff --git a/common_audio/common_audio.gyp b/common_audio/common_audio.gyp index 70d6909c..0eb238f8 100644 --- a/common_audio/common_audio.gyp +++ b/common_audio/common_audio.gyp @@ -140,7 +140,7 @@ }], ], # conditions }], - ['target_arch=="mipsel"', { + ['target_arch=="mipsel" and mips_arch_variant!="r6"', { 'sources': [ 'signal_processing/include/spl_inl_mips.h', 'signal_processing/complex_bit_reverse_mips.c', -- cgit v1.2.3 From 796056b8fff85d6e85e3717d3598684fc9e893f0 Mon Sep 17 00:00:00 2001 From: "kjellander@webrtc.org" Date: Fri, 31 Oct 2014 18:08:09 +0000 Subject: Update all .isolate files for the new format. R=kjellander@webrtc.org BUG= Review URL: https://webrtc-codereview.appspot.com/27809004 Patch from Marc-Antoine Ruel . git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@7583 4adac7df-926f-26a2-2b94-8c16560cd09d --- common_audio/common_audio.gyp | 1 - common_audio/common_audio_unittests.isolate | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'common_audio') diff --git a/common_audio/common_audio.gyp b/common_audio/common_audio.gyp index 0eb238f8..ca77ba8f 100644 --- a/common_audio/common_audio.gyp +++ b/common_audio/common_audio.gyp @@ -285,7 +285,6 @@ ], 'includes': [ '../build/isolate.gypi', - 'common_audio_unittests.isolate', ], 'sources': [ 'common_audio_unittests.isolate', diff --git a/common_audio/common_audio_unittests.isolate b/common_audio/common_audio_unittests.isolate index cc5e6ab4..80eb0fc4 100644 --- a/common_audio/common_audio_unittests.isolate +++ b/common_audio/common_audio_unittests.isolate @@ -9,7 +9,7 @@ 'conditions': [ ['OS=="android"', { 'variables': { - 'isolate_dependency_untracked': [ + 'files': [ '<(DEPTH)/data/', '<(DEPTH)/resources/', ], @@ -21,13 +21,10 @@ '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/common_audio_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_tracked': [ + 'files': [ '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/common_audio_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '<(DEPTH)/tools/swarming_client/', - ], }, }], ], -- cgit v1.2.3 From 46c5634b0a617aa58e5ee12729709776cb73f26b Mon Sep 17 00:00:00 2001 From: "andrew@webrtc.org" Date: Fri, 31 Oct 2014 21:51:03 +0000 Subject: Add a WavReader counterpart to WavWriter. Don't bother with a C interface as we currently have no need to call this from C code. The first use will be in the audioproc tool. R=kwiberg@webrtc.org Review URL: https://webrtc-codereview.appspot.com/30829004 git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@7585 4adac7df-926f-26a2-2b94-8c16560cd09d --- common_audio/BUILD.gn | 4 +- common_audio/common_audio.gyp | 6 +- common_audio/wav_file.cc | 166 ++++++++++++++++++++++++++++++++++++ common_audio/wav_file.h | 98 +++++++++++++++++++++ common_audio/wav_file_unittest.cc | 163 +++++++++++++++++++++++++++++++++++ common_audio/wav_header.cc | 134 ++++++++++++++++++++++------- common_audio/wav_header.h | 16 +++- common_audio/wav_header_unittest.cc | 92 +++++++++++++++++++- common_audio/wav_writer.cc | 115 ------------------------- common_audio/wav_writer.h | 72 ---------------- common_audio/wav_writer_unittest.cc | 137 ----------------------------- 11 files changed, 639 insertions(+), 364 deletions(-) create mode 100644 common_audio/wav_file.cc create mode 100644 common_audio/wav_file.h create mode 100644 common_audio/wav_file_unittest.cc delete mode 100644 common_audio/wav_writer.cc delete mode 100644 common_audio/wav_writer.h delete mode 100644 common_audio/wav_writer_unittest.cc (limited to 'common_audio') diff --git a/common_audio/BUILD.gn b/common_audio/BUILD.gn index 1338a756..ba1d1795 100644 --- a/common_audio/BUILD.gn +++ b/common_audio/BUILD.gn @@ -85,8 +85,8 @@ source_set("common_audio") { "vad/vad_sp.h", "wav_header.cc", "wav_header.h", - "wav_writer.cc", - "wav_writer.h", + "wav_file.cc", + "wav_file.h", "window_generator.cc", "window_generator.h", ] diff --git a/common_audio/common_audio.gyp b/common_audio/common_audio.gyp index ca77ba8f..8f96674f 100644 --- a/common_audio/common_audio.gyp +++ b/common_audio/common_audio.gyp @@ -99,8 +99,8 @@ 'vad/vad_sp.h', 'wav_header.cc', 'wav_header.h', - 'wav_writer.cc', - 'wav_writer.h', + 'wav_file.cc', + 'wav_file.h', 'window_generator.cc', 'window_generator.h', ], @@ -245,7 +245,7 @@ 'vad/vad_unittest.cc', 'vad/vad_unittest.h', 'wav_header_unittest.cc', - 'wav_writer_unittest.cc', + 'wav_file_unittest.cc', 'window_generator_unittest.cc', ], 'conditions': [ diff --git a/common_audio/wav_file.cc b/common_audio/wav_file.cc new file mode 100644 index 00000000..685f3537 --- /dev/null +++ b/common_audio/wav_file.cc @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/common_audio/wav_file.h" + +#include +#include +#include + +#include "webrtc/base/checks.h" +#include "webrtc/common_audio/include/audio_util.h" +#include "webrtc/common_audio/wav_header.h" + +namespace webrtc { + +// We write 16-bit PCM WAV files. +static const WavFormat kWavFormat = kWavFormatPcm; +static const int kBytesPerSample = 2; + +WavReader::WavReader(const std::string& filename) + : file_handle_(fopen(filename.c_str(), "rb")) { + CHECK(file_handle_); + uint8_t header[kWavHeaderSize]; + const size_t read = + fread(header, sizeof(*header), kWavHeaderSize, file_handle_); + CHECK_EQ(kWavHeaderSize, read); + + WavFormat format; + int bytes_per_sample; + CHECK(ReadWavHeader(header, &num_channels_, &sample_rate_, &format, + &bytes_per_sample, &num_samples_)); + CHECK_EQ(kWavFormat, format); + CHECK_EQ(kBytesPerSample, bytes_per_sample); +} + +WavReader::~WavReader() { + Close(); +} + +size_t WavReader::ReadSamples(size_t num_samples, int16_t* samples) { +#ifndef WEBRTC_ARCH_LITTLE_ENDIAN +#error "Need to convert samples to big-endian when reading from WAV file" +#endif + const size_t read = + fread(samples, sizeof(*samples), num_samples, file_handle_); + // If we didn't read what was requested, ensure we've reached the EOF. + CHECK(read == num_samples || feof(file_handle_)); + return read; +} + +size_t WavReader::ReadSamples(size_t num_samples, float* samples) { + static const size_t kChunksize = 4096 / sizeof(uint16_t); + size_t read = 0; + for (size_t i = 0; i < num_samples; i += kChunksize) { + int16_t isamples[kChunksize]; + size_t chunk = std::min(kChunksize, num_samples - i); + chunk = ReadSamples(chunk, isamples); + for (size_t j = 0; j < chunk; ++j) + samples[i + j] = isamples[j]; + read += chunk; + } + return read; +} + +void WavReader::Close() { + CHECK_EQ(0, fclose(file_handle_)); + file_handle_ = NULL; +} + +WavWriter::WavWriter(const std::string& filename, int sample_rate, + int num_channels) + : sample_rate_(sample_rate), + num_channels_(num_channels), + num_samples_(0), + file_handle_(fopen(filename.c_str(), "wb")) { + CHECK(file_handle_); + CHECK(CheckWavParameters(num_channels_, + sample_rate_, + kWavFormat, + kBytesPerSample, + num_samples_)); + + // Write a blank placeholder header, since we need to know the total number + // of samples before we can fill in the real data. + static const uint8_t blank_header[kWavHeaderSize] = {0}; + CHECK_EQ(1u, fwrite(blank_header, kWavHeaderSize, 1, file_handle_)); +} + +WavWriter::~WavWriter() { + Close(); +} + +void WavWriter::WriteSamples(const int16_t* samples, size_t num_samples) { +#ifndef WEBRTC_ARCH_LITTLE_ENDIAN +#error "Need to convert samples to little-endian when writing to WAV file" +#endif + const size_t written = + fwrite(samples, sizeof(*samples), num_samples, file_handle_); + CHECK_EQ(num_samples, written); + num_samples_ += static_cast(written); + CHECK(written <= std::numeric_limits::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) { + static const size_t kChunksize = 4096 / sizeof(uint16_t); + for (size_t i = 0; i < num_samples; i += kChunksize) { + int16_t isamples[kChunksize]; + const size_t chunk = std::min(kChunksize, num_samples - i); + FloatS16ToS16(samples + i, chunk, isamples); + WriteSamples(isamples, chunk); + } +} + +void WavWriter::Close() { + CHECK_EQ(0, fseek(file_handle_, 0, SEEK_SET)); + uint8_t header[kWavHeaderSize]; + CHECK(WriteWavHeader(header, num_channels_, sample_rate_, kWavFormat, + kBytesPerSample, num_samples_)); + CHECK_EQ(1u, fwrite(header, kWavHeaderSize, 1, file_handle_)); + CHECK_EQ(0, fclose(file_handle_)); + file_handle_ = NULL; +} + +} // namespace webrtc + +rtc_WavWriter* rtc_WavOpen(const char* filename, + int sample_rate, + int num_channels) { + return reinterpret_cast( + new webrtc::WavWriter(filename, sample_rate, num_channels)); +} + +void rtc_WavClose(rtc_WavWriter* wf) { + delete reinterpret_cast(wf); +} + +void rtc_WavWriteSamples(rtc_WavWriter* wf, + const float* samples, + size_t num_samples) { + reinterpret_cast(wf)->WriteSamples(samples, num_samples); +} + +int rtc_WavSampleRate(const rtc_WavWriter* wf) { + return reinterpret_cast(wf)->sample_rate(); +} + +int rtc_WavNumChannels(const rtc_WavWriter* wf) { + return reinterpret_cast(wf)->num_channels(); +} + +uint32_t rtc_WavNumSamples(const rtc_WavWriter* wf) { + return reinterpret_cast(wf)->num_samples(); +} diff --git a/common_audio/wav_file.h b/common_audio/wav_file.h new file mode 100644 index 00000000..c6c5d6b7 --- /dev/null +++ b/common_audio/wav_file.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_COMMON_AUDIO_WAV_FILE_H_ +#define WEBRTC_COMMON_AUDIO_WAV_FILE_H_ + +#ifdef __cplusplus + +#include +#include +#include + +namespace webrtc { + +// Simple C++ class for writing 16-bit PCM WAV files. All error handling is +// by calls to CHECK(), making it unsuitable for anything but debug code. +class WavWriter { + public: + // Open a new WAV file for writing. + WavWriter(const std::string& filename, int sample_rate, int num_channels); + + // Close the WAV file, after writing its header. + ~WavWriter(); + + // Write additional samples to the file. Each sample is in the range + // [-32768,32767], and there must be the previously specified number of + // interleaved channels. + void WriteSamples(const float* samples, size_t num_samples); + void WriteSamples(const int16_t* samples, size_t num_samples); + + int sample_rate() const { return sample_rate_; } + int num_channels() const { return num_channels_; } + uint32_t num_samples() const { return num_samples_; } + + private: + void Close(); + const int sample_rate_; + const int num_channels_; + uint32_t num_samples_; // Total number of samples written to file. + FILE* file_handle_; // Output file, owned by this class +}; + +// Follows the conventions of WavWriter. +class WavReader { + public: + // Opens an existing WAV file for reading. + explicit WavReader(const std::string& filename); + + // Close the WAV file. + ~WavReader(); + + // Returns the number of samples read. If this is less than requested, + // verifies that the end of the file was reached. + size_t ReadSamples(size_t num_samples, float* samples); + size_t ReadSamples(size_t num_samples, int16_t* samples); + + int sample_rate() const { return sample_rate_; } + int num_channels() const { return num_channels_; } + uint32_t num_samples() const { return num_samples_; } + + private: + void Close(); + int sample_rate_; + int num_channels_; + uint32_t num_samples_; // Total number of samples in the file. + FILE* file_handle_; // Input file, owned by this class. +}; + +} // namespace webrtc + +extern "C" { +#endif // __cplusplus + +// C wrappers for the WavWriter class. +typedef struct rtc_WavWriter rtc_WavWriter; +rtc_WavWriter* rtc_WavOpen(const char* filename, + int sample_rate, + int num_channels); +void rtc_WavClose(rtc_WavWriter* wf); +void rtc_WavWriteSamples(rtc_WavWriter* wf, + const float* samples, + size_t num_samples); +int rtc_WavSampleRate(const rtc_WavWriter* wf); +int rtc_WavNumChannels(const rtc_WavWriter* wf); +uint32_t rtc_WavNumSamples(const rtc_WavWriter* wf); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBRTC_COMMON_AUDIO_WAV_FILE_H_ diff --git a/common_audio/wav_file_unittest.cc b/common_audio/wav_file_unittest.cc new file mode 100644 index 00000000..1bdb655d --- /dev/null +++ b/common_audio/wav_file_unittest.cc @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// MSVC++ requires this to be set before any other includes to get M_PI. +#define _USE_MATH_DEFINES + +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/compile_assert.h" +#include "webrtc/common_audio/wav_header.h" +#include "webrtc/common_audio/wav_file.h" +#include "webrtc/test/testsupport/fileutils.h" + +static const float kSamples[] = {0.0, 10.0, 4e4, -1e9}; + +// Write a tiny WAV file with the C++ interface and verify the result. +TEST(WavWriterTest, CPP) { + const std::string outfile = webrtc::test::OutputPath() + "wavtest1.wav"; + static const uint32_t kNumSamples = 3; + { + webrtc::WavWriter w(outfile, 14099, 1); + EXPECT_EQ(14099, w.sample_rate()); + EXPECT_EQ(1, w.num_channels()); + EXPECT_EQ(0u, w.num_samples()); + w.WriteSamples(kSamples, kNumSamples); + EXPECT_EQ(kNumSamples, w.num_samples()); + } + static const uint8_t kExpectedContents[] = { + 'R', 'I', 'F', 'F', + 42, 0, 0, 0, // size of whole file - 8: 6 + 44 - 8 + 'W', 'A', 'V', 'E', + 'f', 'm', 't', ' ', + 16, 0, 0, 0, // size of fmt block - 8: 24 - 8 + 1, 0, // format: PCM (1) + 1, 0, // channels: 1 + 0x13, 0x37, 0, 0, // sample rate: 14099 + 0x26, 0x6e, 0, 0, // byte rate: 2 * 14099 + 2, 0, // block align: NumChannels * BytesPerSample + 16, 0, // bits per sample: 2 * 8 + 'd', 'a', 't', 'a', + 6, 0, 0, 0, // size of payload: 6 + 0, 0, // first sample: 0.0 + 10, 0, // second sample: 10.0 + 0xff, 0x7f, // third sample: 4e4 (saturated) + }; + static const int kContentSize = + webrtc::kWavHeaderSize + kNumSamples * sizeof(int16_t); + COMPILE_ASSERT(sizeof(kExpectedContents) == kContentSize, content_size); + EXPECT_EQ(size_t(kContentSize), webrtc::test::GetFileSize(outfile)); + FILE* f = fopen(outfile.c_str(), "rb"); + ASSERT_TRUE(f); + uint8_t contents[kContentSize]; + ASSERT_EQ(1u, fread(contents, kContentSize, 1, f)); + EXPECT_EQ(0, fclose(f)); + EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize)); + + { + webrtc::WavReader r(outfile); + EXPECT_EQ(14099, r.sample_rate()); + EXPECT_EQ(1, r.num_channels()); + EXPECT_EQ(kNumSamples, r.num_samples()); + static const float kTruncatedSamples[] = {0.0, 10.0, 32767.0}; + float samples[kNumSamples]; + EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples)); + EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples))); + EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples)); + } +} + +// Write a tiny WAV file with the C interface and verify the result. +TEST(WavWriterTest, C) { + const std::string outfile = webrtc::test::OutputPath() + "wavtest2.wav"; + rtc_WavWriter *w = rtc_WavOpen(outfile.c_str(), 11904, 2); + EXPECT_EQ(11904, rtc_WavSampleRate(w)); + EXPECT_EQ(2, rtc_WavNumChannels(w)); + EXPECT_EQ(0u, rtc_WavNumSamples(w)); + static const uint32_t kNumSamples = 4; + rtc_WavWriteSamples(w, &kSamples[0], 2); + EXPECT_EQ(2u, rtc_WavNumSamples(w)); + rtc_WavWriteSamples(w, &kSamples[2], kNumSamples - 2); + EXPECT_EQ(kNumSamples, rtc_WavNumSamples(w)); + rtc_WavClose(w); + static const uint8_t kExpectedContents[] = { + 'R', 'I', 'F', 'F', + 44, 0, 0, 0, // size of whole file - 8: 8 + 44 - 8 + 'W', 'A', 'V', 'E', + 'f', 'm', 't', ' ', + 16, 0, 0, 0, // size of fmt block - 8: 24 - 8 + 1, 0, // format: PCM (1) + 2, 0, // channels: 2 + 0x80, 0x2e, 0, 0, // sample rate: 11904 + 0, 0xba, 0, 0, // byte rate: 2 * 2 * 11904 + 4, 0, // block align: NumChannels * BytesPerSample + 16, 0, // bits per sample: 2 * 8 + 'd', 'a', 't', 'a', + 8, 0, 0, 0, // size of payload: 8 + 0, 0, // first sample: 0.0 + 10, 0, // second sample: 10.0 + 0xff, 0x7f, // third sample: 4e4 (saturated) + 0, 0x80, // fourth sample: -1e9 (saturated) + }; + static const int kContentSize = + webrtc::kWavHeaderSize + kNumSamples * sizeof(int16_t); + COMPILE_ASSERT(sizeof(kExpectedContents) == kContentSize, content_size); + EXPECT_EQ(size_t(kContentSize), webrtc::test::GetFileSize(outfile)); + FILE* f = fopen(outfile.c_str(), "rb"); + ASSERT_TRUE(f); + uint8_t contents[kContentSize]; + ASSERT_EQ(1u, fread(contents, kContentSize, 1, f)); + EXPECT_EQ(0, fclose(f)); + EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize)); +} + +// Write a larger WAV file. You can listen to this file to sanity-check it. +TEST(WavWriterTest, LargeFile) { + std::string outfile = webrtc::test::OutputPath() + "wavtest3.wav"; + static const int kSampleRate = 8000; + static const int kNumChannels = 2; + static const uint32_t kNumSamples = 3 * kSampleRate * kNumChannels; + float samples[kNumSamples]; + for (uint32_t i = 0; i < kNumSamples; i += kNumChannels) { + // A nice periodic beeping sound. + static const double kToneHz = 440; + const double t = static_cast(i) / (kNumChannels * kSampleRate); + const double x = + std::numeric_limits::max() * std::sin(t * kToneHz * 2 * M_PI); + samples[i] = std::pow(std::sin(t * 2 * 2 * M_PI), 10) * x; + samples[i + 1] = std::pow(std::cos(t * 2 * 2 * M_PI), 10) * x; + } + { + webrtc::WavWriter w(outfile, kSampleRate, kNumChannels); + EXPECT_EQ(kSampleRate, w.sample_rate()); + EXPECT_EQ(kNumChannels, w.num_channels()); + EXPECT_EQ(0u, w.num_samples()); + w.WriteSamples(samples, kNumSamples); + EXPECT_EQ(kNumSamples, w.num_samples()); + } + EXPECT_EQ(sizeof(int16_t) * kNumSamples + webrtc::kWavHeaderSize, + webrtc::test::GetFileSize(outfile)); + + { + webrtc::WavReader r(outfile); + EXPECT_EQ(kSampleRate, r.sample_rate()); + EXPECT_EQ(kNumChannels, r.num_channels()); + EXPECT_EQ(kNumSamples, r.num_samples()); + + float read_samples[kNumSamples]; + EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, read_samples)); + for (size_t i = 0; i < kNumSamples; ++i) + EXPECT_NEAR(samples[i], read_samples[i], 1); + + EXPECT_EQ(0u, r.ReadSamples(kNumSamples, read_samples)); + } +} diff --git a/common_audio/wav_header.cc b/common_audio/wav_header.cc index ce43896f..3182b1ff 100644 --- a/common_audio/wav_header.cc +++ b/common_audio/wav_header.cc @@ -18,9 +18,11 @@ #include #include +#include "webrtc/base/checks.h" #include "webrtc/common_audio/include/audio_util.h" namespace webrtc { +namespace { struct ChunkHeader { uint32_t ID; @@ -28,6 +30,34 @@ struct ChunkHeader { }; COMPILE_ASSERT(sizeof(ChunkHeader) == 8, chunk_header_size); +// We can't nest this definition in WavHeader, because VS2013 gives an error +// on sizeof(WavHeader::fmt): "error C2070: 'unknown': illegal sizeof operand". +struct FmtSubchunk { + ChunkHeader header; + uint16_t AudioFormat; + uint16_t NumChannels; + uint32_t SampleRate; + uint32_t ByteRate; + uint16_t BlockAlign; + uint16_t BitsPerSample; +}; +COMPILE_ASSERT(sizeof(FmtSubchunk) == 24, fmt_subchunk_size); +const uint32_t kFmtSubchunkSize = sizeof(FmtSubchunk) - sizeof(ChunkHeader); + +struct WavHeader { + struct { + ChunkHeader header; + uint32_t Format; + } riff; + FmtSubchunk fmt; + struct { + ChunkHeader header; + } data; +}; +COMPILE_ASSERT(sizeof(WavHeader) == kWavHeaderSize, no_padding_in_header); + +} // namespace + bool CheckWavParameters(int num_channels, int sample_rate, WavFormat format, @@ -91,54 +121,54 @@ static inline void WriteFourCC(uint32_t* f, char a, char b, char c, char d) { | static_cast(c) << 16 | static_cast(d) << 24; } + +static inline uint16_t ReadLE16(uint16_t x) { return x; } +static inline uint32_t ReadLE32(uint32_t x) { return x; } +static inline std::string ReadFourCC(uint32_t x) { + return std::string(reinterpret_cast(&x), 4); +} #else #error "Write be-to-le conversion functions" #endif -void WriteWavHeader(uint8_t* buf, +static inline uint32_t RiffChunkSize(uint32_t bytes_in_payload) { + return bytes_in_payload + kWavHeaderSize - sizeof(ChunkHeader); +} + +static inline uint32_t ByteRate(int num_channels, int sample_rate, + int bytes_per_sample) { + return static_cast(num_channels) * sample_rate * bytes_per_sample; +} + +static inline uint16_t BlockAlign(int num_channels, int bytes_per_sample) { + return num_channels * bytes_per_sample; +} + +bool WriteWavHeader(uint8_t* buf, int num_channels, int sample_rate, WavFormat format, int bytes_per_sample, uint32_t num_samples) { - assert(CheckWavParameters(num_channels, sample_rate, format, - bytes_per_sample, num_samples)); - - struct { - struct { - ChunkHeader header; - uint32_t Format; - } riff; - struct { - ChunkHeader header; - uint16_t AudioFormat; - uint16_t NumChannels; - uint32_t SampleRate; - uint32_t ByteRate; - uint16_t BlockAlign; - uint16_t BitsPerSample; - } fmt; - struct { - ChunkHeader header; - } data; - } header; - COMPILE_ASSERT(sizeof(header) == kWavHeaderSize, no_padding_in_header); + if (!CheckWavParameters(num_channels, sample_rate, format, + bytes_per_sample, num_samples)) + return false; + WavHeader header; const uint32_t bytes_in_payload = bytes_per_sample * num_samples; WriteFourCC(&header.riff.header.ID, 'R', 'I', 'F', 'F'); - WriteLE32(&header.riff.header.Size, - bytes_in_payload + kWavHeaderSize - sizeof(ChunkHeader)); + WriteLE32(&header.riff.header.Size, RiffChunkSize(bytes_in_payload)); WriteFourCC(&header.riff.Format, 'W', 'A', 'V', 'E'); WriteFourCC(&header.fmt.header.ID, 'f', 'm', 't', ' '); - WriteLE32(&header.fmt.header.Size, sizeof(header.fmt) - sizeof(ChunkHeader)); + WriteLE32(&header.fmt.header.Size, kFmtSubchunkSize); WriteLE16(&header.fmt.AudioFormat, format); WriteLE16(&header.fmt.NumChannels, num_channels); WriteLE32(&header.fmt.SampleRate, sample_rate); - WriteLE32(&header.fmt.ByteRate, (static_cast(num_channels) - * sample_rate * bytes_per_sample)); - WriteLE16(&header.fmt.BlockAlign, num_channels * bytes_per_sample); + WriteLE32(&header.fmt.ByteRate, ByteRate(num_channels, sample_rate, + bytes_per_sample)); + WriteLE16(&header.fmt.BlockAlign, BlockAlign(num_channels, bytes_per_sample)); WriteLE16(&header.fmt.BitsPerSample, 8 * bytes_per_sample); WriteFourCC(&header.data.header.ID, 'd', 'a', 't', 'a'); @@ -147,6 +177,52 @@ void WriteWavHeader(uint8_t* buf, // Do an extra copy rather than writing everything to buf directly, since buf // might not be correctly aligned. memcpy(buf, &header, kWavHeaderSize); + return true; } +bool ReadWavHeader(const uint8_t* buf, + int* num_channels, + int* sample_rate, + WavFormat* format, + int* bytes_per_sample, + uint32_t* num_samples) { + WavHeader header; + memcpy(&header, buf, kWavHeaderSize); + + // Parse needed fields. + *format = static_cast(ReadLE16(header.fmt.AudioFormat)); + *num_channels = ReadLE16(header.fmt.NumChannels); + *sample_rate = ReadLE32(header.fmt.SampleRate); + *bytes_per_sample = ReadLE16(header.fmt.BitsPerSample) / 8; + const uint32_t bytes_in_payload = ReadLE32(header.data.header.Size); + if (*bytes_per_sample <= 0) + return false; + *num_samples = bytes_in_payload / *bytes_per_sample; + + // Sanity check remaining fields. + if (ReadFourCC(header.riff.header.ID) != "RIFF") + return false; + if (ReadFourCC(header.riff.Format) != "WAVE") + return false; + if (ReadFourCC(header.fmt.header.ID) != "fmt ") + return false; + if (ReadFourCC(header.data.header.ID) != "data") + return false; + + if (ReadLE32(header.riff.header.Size) != RiffChunkSize(bytes_in_payload)) + return false; + if (ReadLE32(header.fmt.header.Size) != kFmtSubchunkSize) + return false; + if (ReadLE32(header.fmt.ByteRate) != + ByteRate(*num_channels, *sample_rate, *bytes_per_sample)) + return false; + if (ReadLE16(header.fmt.BlockAlign) != + BlockAlign(*num_channels, *bytes_per_sample)) + return false; + + return CheckWavParameters(*num_channels, *sample_rate, *format, + *bytes_per_sample, *num_samples); +} + + } // namespace webrtc diff --git a/common_audio/wav_header.h b/common_audio/wav_header.h index f9ed8a57..0901f970 100644 --- a/common_audio/wav_header.h +++ b/common_audio/wav_header.h @@ -11,11 +11,12 @@ #ifndef WEBRTC_COMMON_AUDIO_WAV_HEADER_H_ #define WEBRTC_COMMON_AUDIO_WAV_HEADER_H_ +#include #include namespace webrtc { -static const int kWavHeaderSize = 44; +static const size_t kWavHeaderSize = 44; enum WavFormat { kWavFormatPcm = 1, // PCM, each sample of size bytes_per_sample @@ -33,14 +34,23 @@ bool CheckWavParameters(int num_channels, // Write a kWavHeaderSize bytes long WAV header to buf. The payload that // follows the header is supposed to have the specified number of interleaved // channels and contain the specified total number of samples of the specified -// type. -void WriteWavHeader(uint8_t* buf, +// type. Returns false if any of the input parameters are invalid. +bool WriteWavHeader(uint8_t* buf, int num_channels, int sample_rate, WavFormat format, int bytes_per_sample, uint32_t num_samples); +// Read a kWavHeaderSize bytes long WAV header from buf and parse the values +// into the provided output parameters. Returns false if the header is invalid. +bool ReadWavHeader(const uint8_t* buf, + int* num_channels, + int* sample_rate, + WavFormat* format, + int* bytes_per_sample, + uint32_t* num_samples); + } // namespace webrtc #endif // WEBRTC_COMMON_AUDIO_WAV_HEADER_H_ diff --git a/common_audio/wav_header_unittest.cc b/common_audio/wav_header_unittest.cc index f05160ea..ac79cc16 100644 --- a/common_audio/wav_header_unittest.cc +++ b/common_audio/wav_header_unittest.cc @@ -48,13 +48,85 @@ TEST(WavHeaderTest, CheckWavParameters) { webrtc::CheckWavParameters(3, 8000, webrtc::kWavFormatPcm, 1, 5)); } +TEST(WavHeaderTest, ReadWavHeader) { + int num_channels = 0; + int sample_rate = 0; + webrtc::WavFormat format = webrtc::kWavFormatPcm; + int bytes_per_sample = 0; + uint32_t num_samples = 0; + + // Test a few ways the header can be invalid. We start with the valid header + // used in WriteAndReadWavHeader, and invalidate one field per test. The + // invalid field is indicated in the array name, and in the comments with + // *BAD*. + static const uint8_t kBadRiffID[] = { + 'R', 'i', 'f', 'f', // *BAD* + 0xbd, 0xd0, 0x5b, 0x07, // size of whole file - 8: 123457689 + 44 - 8 + 'W', 'A', 'V', 'E', + 'f', 'm', 't', ' ', + 16, 0, 0, 0, // size of fmt block - 8: 24 - 8 + 6, 0, // format: A-law (6) + 17, 0, // channels: 17 + 0x39, 0x30, 0, 0, // sample rate: 12345 + 0xc9, 0x33, 0x03, 0, // byte rate: 1 * 17 * 12345 + 17, 0, // block align: NumChannels * BytesPerSample + 8, 0, // bits per sample: 1 * 8 + 'd', 'a', 't', 'a', + 0x99, 0xd0, 0x5b, 0x07, // size of payload: 123457689 + 0xa4, 0xa4, 0xa4, 0xa4, // untouched bytes after header + }; + EXPECT_FALSE( + webrtc::ReadWavHeader(kBadRiffID, &num_channels, &sample_rate, + &format, &bytes_per_sample, &num_samples)); + + static const uint8_t kBadBitsPerSample[] = { + 'R', 'I', 'F', 'F', + 0xbd, 0xd0, 0x5b, 0x07, // size of whole file - 8: 123457689 + 44 - 8 + 'W', 'A', 'V', 'E', + 'f', 'm', 't', ' ', + 16, 0, 0, 0, // size of fmt block - 8: 24 - 8 + 6, 0, // format: A-law (6) + 17, 0, // channels: 17 + 0x39, 0x30, 0, 0, // sample rate: 12345 + 0xc9, 0x33, 0x03, 0, // byte rate: 1 * 17 * 12345 + 17, 0, // block align: NumChannels * BytesPerSample + 1, 0, // bits per sample: *BAD* + 'd', 'a', 't', 'a', + 0x99, 0xd0, 0x5b, 0x07, // size of payload: 123457689 + 0xa4, 0xa4, 0xa4, 0xa4, // untouched bytes after header + }; + EXPECT_FALSE( + webrtc::ReadWavHeader(kBadBitsPerSample, &num_channels, &sample_rate, + &format, &bytes_per_sample, &num_samples)); + + static const uint8_t kBadByteRate[] = { + 'R', 'I', 'F', 'F', + 0xbd, 0xd0, 0x5b, 0x07, // size of whole file - 8: 123457689 + 44 - 8 + 'W', 'A', 'V', 'E', + 'f', 'm', 't', ' ', + 16, 0, 0, 0, // size of fmt block - 8: 24 - 8 + 6, 0, // format: A-law (6) + 17, 0, // channels: 17 + 0x39, 0x30, 0, 0, // sample rate: 12345 + 0x00, 0x33, 0x03, 0, // byte rate: *BAD* + 17, 0, // block align: NumChannels * BytesPerSample + 8, 0, // bits per sample: 1 * 8 + 'd', 'a', 't', 'a', + 0x99, 0xd0, 0x5b, 0x07, // size of payload: 123457689 + 0xa4, 0xa4, 0xa4, 0xa4, // untouched bytes after header + }; + EXPECT_FALSE( + webrtc::ReadWavHeader(kBadByteRate, &num_channels, &sample_rate, + &format, &bytes_per_sample, &num_samples)); +} + // Try writing a WAV header and make sure it looks OK. -TEST(WavHeaderTest, WriteWavHeader) { +TEST(WavHeaderTest, WriteAndReadWavHeader) { static const int kSize = 4 + webrtc::kWavHeaderSize + 4; uint8_t buf[kSize]; memset(buf, 0xa4, sizeof(buf)); - webrtc::WriteWavHeader( - buf + 4, 17, 12345, webrtc::kWavFormatALaw, 1, 123457689); + EXPECT_TRUE(webrtc::WriteWavHeader( + buf + 4, 17, 12345, webrtc::kWavFormatALaw, 1, 123457689)); static const uint8_t kExpectedBuf[] = { 0xa4, 0xa4, 0xa4, 0xa4, // untouched bytes before header 'R', 'I', 'F', 'F', @@ -74,4 +146,18 @@ TEST(WavHeaderTest, WriteWavHeader) { }; COMPILE_ASSERT(sizeof(kExpectedBuf) == kSize, buf_size); EXPECT_EQ(0, memcmp(kExpectedBuf, buf, kSize)); + + int num_channels = 0; + int sample_rate = 0; + webrtc::WavFormat format = webrtc::kWavFormatPcm; + int bytes_per_sample = 0; + uint32_t num_samples = 0; + EXPECT_TRUE( + webrtc::ReadWavHeader(buf + 4, &num_channels, &sample_rate, &format, + &bytes_per_sample, &num_samples)); + EXPECT_EQ(17, num_channels); + EXPECT_EQ(12345, sample_rate); + EXPECT_EQ(webrtc::kWavFormatALaw, format); + EXPECT_EQ(1, bytes_per_sample); + EXPECT_EQ(123457689u, num_samples); } diff --git a/common_audio/wav_writer.cc b/common_audio/wav_writer.cc deleted file mode 100644 index 52449789..00000000 --- a/common_audio/wav_writer.cc +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/common_audio/wav_writer.h" - -#include -#include -#include - -#include "webrtc/base/checks.h" -#include "webrtc/common_audio/include/audio_util.h" -#include "webrtc/common_audio/wav_header.h" - -namespace webrtc { - -// We write 16-bit PCM WAV files. -static const WavFormat kWavFormat = kWavFormatPcm; -static const int kBytesPerSample = 2; - -WavFile::WavFile(const std::string& filename, int sample_rate, int num_channels) - : sample_rate_(sample_rate), - num_channels_(num_channels), - num_samples_(0), - file_handle_(fopen(filename.c_str(), "wb")) { - CHECK(file_handle_); - CHECK(CheckWavParameters(num_channels_, - sample_rate_, - kWavFormat, - kBytesPerSample, - num_samples_)); - - // Write a blank placeholder header, since we need to know the total number - // of samples before we can fill in the real data. - static const uint8_t blank_header[kWavHeaderSize] = {0}; - CHECK_EQ(1u, fwrite(blank_header, kWavHeaderSize, 1, file_handle_)); -} - -WavFile::~WavFile() { - Close(); -} - -void WavFile::WriteSamples(const int16_t* samples, size_t num_samples) { -#ifndef WEBRTC_ARCH_LITTLE_ENDIAN -#error "Need to convert samples to little-endian when writing to WAV file" -#endif - const size_t written = - fwrite(samples, sizeof(*samples), num_samples, file_handle_); - CHECK_EQ(num_samples, written); - num_samples_ += static_cast(written); - CHECK(written <= std::numeric_limits::max() || - num_samples_ >= written); // detect uint32_t overflow - CHECK(CheckWavParameters(num_channels_, - sample_rate_, - kWavFormat, - kBytesPerSample, - num_samples_)); -} - -void WavFile::WriteSamples(const float* samples, size_t num_samples) { - static const size_t kChunksize = 4096 / sizeof(uint16_t); - for (size_t i = 0; i < num_samples; i += kChunksize) { - int16_t isamples[kChunksize]; - const size_t chunk = std::min(kChunksize, num_samples - i); - FloatS16ToS16(samples + i, chunk, isamples); - WriteSamples(isamples, chunk); - } -} - -void WavFile::Close() { - CHECK_EQ(0, fseek(file_handle_, 0, SEEK_SET)); - uint8_t header[kWavHeaderSize]; - WriteWavHeader(header, num_channels_, sample_rate_, kWavFormat, - kBytesPerSample, num_samples_); - CHECK_EQ(1u, fwrite(header, kWavHeaderSize, 1, file_handle_)); - CHECK_EQ(0, fclose(file_handle_)); - file_handle_ = NULL; -} - -} // namespace webrtc - -rtc_WavFile* rtc_WavOpen(const char* filename, - int sample_rate, - int num_channels) { - return reinterpret_cast( - new webrtc::WavFile(filename, sample_rate, num_channels)); -} - -void rtc_WavClose(rtc_WavFile* wf) { - delete reinterpret_cast(wf); -} - -void rtc_WavWriteSamples(rtc_WavFile* wf, - const float* samples, - size_t num_samples) { - reinterpret_cast(wf)->WriteSamples(samples, num_samples); -} - -int rtc_WavSampleRate(const rtc_WavFile* wf) { - return reinterpret_cast(wf)->sample_rate(); -} - -int rtc_WavNumChannels(const rtc_WavFile* wf) { - return reinterpret_cast(wf)->num_channels(); -} - -uint32_t rtc_WavNumSamples(const rtc_WavFile* wf) { - return reinterpret_cast(wf)->num_samples(); -} diff --git a/common_audio/wav_writer.h b/common_audio/wav_writer.h deleted file mode 100644 index 09667279..00000000 --- a/common_audio/wav_writer.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_COMMON_AUDIO_WAV_WRITER_H_ -#define WEBRTC_COMMON_AUDIO_WAV_WRITER_H_ - -#ifdef __cplusplus - -#include -#include -#include - -namespace webrtc { - -// Simple C++ class for writing 16-bit PCM WAV files. All error handling is -// by calls to CHECK(), making it unsuitable for anything but debug code. -class WavFile { - public: - // Open a new WAV file for writing. - WavFile(const std::string& filename, int sample_rate, int num_channels); - - // Close the WAV file, after writing its header. - ~WavFile(); - - // Write additional samples to the file. Each sample is in the range - // [-32768,32767], and there must be the previously specified number of - // interleaved channels. - void WriteSamples(const float* samples, size_t num_samples); - void WriteSamples(const int16_t* samples, size_t num_samples); - - int sample_rate() const { return sample_rate_; } - int num_channels() const { return num_channels_; } - uint32_t num_samples() const { return num_samples_; } - - private: - void Close(); - const int sample_rate_; - const int num_channels_; - uint32_t num_samples_; // total number of samples written to file - FILE* file_handle_; // output file, owned by this class -}; - -} // namespace webrtc - -extern "C" { -#endif // __cplusplus - -// C wrappers for the WavFile class. -typedef struct rtc_WavFile rtc_WavFile; -rtc_WavFile* rtc_WavOpen(const char* filename, - int sample_rate, - int num_channels); -void rtc_WavClose(rtc_WavFile* wf); -void rtc_WavWriteSamples(rtc_WavFile* wf, - const float* samples, - size_t num_samples); -int rtc_WavSampleRate(const rtc_WavFile* wf); -int rtc_WavNumChannels(const rtc_WavFile* wf); -uint32_t rtc_WavNumSamples(const rtc_WavFile* wf); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // WEBRTC_COMMON_AUDIO_WAV_WRITER_H_ diff --git a/common_audio/wav_writer_unittest.cc b/common_audio/wav_writer_unittest.cc deleted file mode 100644 index 9c593be6..00000000 --- a/common_audio/wav_writer_unittest.cc +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// MSVC++ requires this to be set before any other includes to get M_PI. -#define _USE_MATH_DEFINES - -#include -#include - -#include "testing/gtest/include/gtest/gtest.h" -#include "webrtc/base/compile_assert.h" -#include "webrtc/common_audio/wav_header.h" -#include "webrtc/common_audio/wav_writer.h" -#include "webrtc/test/testsupport/fileutils.h" - -static const float kSamples[] = {0.0, 10.0, 4e4, -1e9}; - -// Write a tiny WAV file with the C++ interface and verify the result. -TEST(WavWriterTest, CPP) { - const std::string outfile = webrtc::test::OutputPath() + "wavtest1.wav"; - static const uint32_t kNumSamples = 3; - { - webrtc::WavFile w(outfile, 14099, 1); - EXPECT_EQ(14099, w.sample_rate()); - EXPECT_EQ(1, w.num_channels()); - EXPECT_EQ(0u, w.num_samples()); - w.WriteSamples(kSamples, kNumSamples); - EXPECT_EQ(kNumSamples, w.num_samples()); - } - static const uint8_t kExpectedContents[] = { - 'R', 'I', 'F', 'F', - 42, 0, 0, 0, // size of whole file - 8: 6 + 44 - 8 - 'W', 'A', 'V', 'E', - 'f', 'm', 't', ' ', - 16, 0, 0, 0, // size of fmt block - 8: 24 - 8 - 1, 0, // format: PCM (1) - 1, 0, // channels: 1 - 0x13, 0x37, 0, 0, // sample rate: 14099 - 0x26, 0x6e, 0, 0, // byte rate: 2 * 14099 - 2, 0, // block align: NumChannels * BytesPerSample - 16, 0, // bits per sample: 2 * 8 - 'd', 'a', 't', 'a', - 6, 0, 0, 0, // size of payload: 6 - 0, 0, // first sample: 0.0 - 10, 0, // second sample: 10.0 - 0xff, 0x7f, // third sample: 4e4 (saturated) - }; - static const int kContentSize = - webrtc::kWavHeaderSize + kNumSamples * sizeof(int16_t); - COMPILE_ASSERT(sizeof(kExpectedContents) == kContentSize, content_size); - EXPECT_EQ(size_t(kContentSize), webrtc::test::GetFileSize(outfile)); - FILE* f = fopen(outfile.c_str(), "rb"); - ASSERT_TRUE(f); - uint8_t contents[kContentSize]; - ASSERT_EQ(1u, fread(contents, kContentSize, 1, f)); - EXPECT_EQ(0, fclose(f)); - EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize)); -} - -// Write a tiny WAV file with the C interface and verify the result. -TEST(WavWriterTest, C) { - const std::string outfile = webrtc::test::OutputPath() + "wavtest2.wav"; - rtc_WavFile *w = rtc_WavOpen(outfile.c_str(), 11904, 2); - EXPECT_EQ(11904, rtc_WavSampleRate(w)); - EXPECT_EQ(2, rtc_WavNumChannels(w)); - EXPECT_EQ(0u, rtc_WavNumSamples(w)); - static const uint32_t kNumSamples = 4; - rtc_WavWriteSamples(w, &kSamples[0], 2); - EXPECT_EQ(2u, rtc_WavNumSamples(w)); - rtc_WavWriteSamples(w, &kSamples[2], kNumSamples - 2); - EXPECT_EQ(kNumSamples, rtc_WavNumSamples(w)); - rtc_WavClose(w); - static const uint8_t kExpectedContents[] = { - 'R', 'I', 'F', 'F', - 44, 0, 0, 0, // size of whole file - 8: 8 + 44 - 8 - 'W', 'A', 'V', 'E', - 'f', 'm', 't', ' ', - 16, 0, 0, 0, // size of fmt block - 8: 24 - 8 - 1, 0, // format: PCM (1) - 2, 0, // channels: 2 - 0x80, 0x2e, 0, 0, // sample rate: 11904 - 0, 0xba, 0, 0, // byte rate: 2 * 2 * 11904 - 4, 0, // block align: NumChannels * BytesPerSample - 16, 0, // bits per sample: 2 * 8 - 'd', 'a', 't', 'a', - 8, 0, 0, 0, // size of payload: 8 - 0, 0, // first sample: 0.0 - 10, 0, // second sample: 10.0 - 0xff, 0x7f, // third sample: 4e4 (saturated) - 0, 0x80, // fourth sample: -1e9 (saturated) - }; - static const int kContentSize = - webrtc::kWavHeaderSize + kNumSamples * sizeof(int16_t); - COMPILE_ASSERT(sizeof(kExpectedContents) == kContentSize, content_size); - EXPECT_EQ(size_t(kContentSize), webrtc::test::GetFileSize(outfile)); - FILE* f = fopen(outfile.c_str(), "rb"); - ASSERT_TRUE(f); - uint8_t contents[kContentSize]; - ASSERT_EQ(1u, fread(contents, kContentSize, 1, f)); - EXPECT_EQ(0, fclose(f)); - EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize)); -} - -// Write a larger WAV file. You can listen to this file to sanity-check it. -TEST(WavWriterTest, LargeFile) { - std::string outfile = webrtc::test::OutputPath() + "wavtest3.wav"; - static const int kSampleRate = 8000; - static const int kNumChannels = 2; - static const uint32_t kNumSamples = 3 * kSampleRate * kNumChannels; - float samples[kNumSamples]; - for (uint32_t i = 0; i < kNumSamples; i += kNumChannels) { - // A nice periodic beeping sound. - static const double kToneHz = 440; - const double t = static_cast(i) / (kNumChannels * kSampleRate); - const double x = - std::numeric_limits::max() * std::sin(t * kToneHz * 2 * M_PI); - samples[i] = std::pow(std::sin(t * 2 * 2 * M_PI), 10) * x; - samples[i + 1] = std::pow(std::cos(t * 2 * 2 * M_PI), 10) * x; - } - { - webrtc::WavFile w(outfile, kSampleRate, kNumChannels); - EXPECT_EQ(kSampleRate, w.sample_rate()); - EXPECT_EQ(kNumChannels, w.num_channels()); - EXPECT_EQ(0u, w.num_samples()); - w.WriteSamples(samples, kNumSamples); - EXPECT_EQ(kNumSamples, w.num_samples()); - } - EXPECT_EQ(sizeof(int16_t) * kNumSamples + webrtc::kWavHeaderSize, - webrtc::test::GetFileSize(outfile)); -} -- cgit v1.2.3 From 1c755a6308e054fd1c81cea8082515b3ff8639a2 Mon Sep 17 00:00:00 2001 From: "andrew@webrtc.org" Date: Mon, 3 Nov 2014 18:20:06 +0000 Subject: Restore the void return type on WriteWavHeader. Karl pointed out that the user can check the validity of the input parameters with CheckWavParameters prior to calling. TBR=kwiberg Review URL: https://webrtc-codereview.appspot.com/23339004 git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@7597 4adac7df-926f-26a2-2b94-8c16560cd09d --- common_audio/wav_file.cc | 4 ++-- common_audio/wav_header.cc | 8 +++----- common_audio/wav_header.h | 4 ++-- common_audio/wav_header_unittest.cc | 8 ++++---- 4 files changed, 11 insertions(+), 13 deletions(-) (limited to 'common_audio') diff --git a/common_audio/wav_file.cc b/common_audio/wav_file.cc index 685f3537..880e1ec4 100644 --- a/common_audio/wav_file.cc +++ b/common_audio/wav_file.cc @@ -127,8 +127,8 @@ void WavWriter::WriteSamples(const float* samples, size_t num_samples) { void WavWriter::Close() { CHECK_EQ(0, fseek(file_handle_, 0, SEEK_SET)); uint8_t header[kWavHeaderSize]; - CHECK(WriteWavHeader(header, num_channels_, sample_rate_, kWavFormat, - kBytesPerSample, num_samples_)); + WriteWavHeader(header, num_channels_, sample_rate_, kWavFormat, + kBytesPerSample, num_samples_); CHECK_EQ(1u, fwrite(header, kWavHeaderSize, 1, file_handle_)); CHECK_EQ(0, fclose(file_handle_)); file_handle_ = NULL; diff --git a/common_audio/wav_header.cc b/common_audio/wav_header.cc index 3182b1ff..8c781fb4 100644 --- a/common_audio/wav_header.cc +++ b/common_audio/wav_header.cc @@ -144,15 +144,14 @@ static inline uint16_t BlockAlign(int num_channels, int bytes_per_sample) { return num_channels * bytes_per_sample; } -bool WriteWavHeader(uint8_t* buf, +void WriteWavHeader(uint8_t* buf, int num_channels, int sample_rate, WavFormat format, int bytes_per_sample, uint32_t num_samples) { - if (!CheckWavParameters(num_channels, sample_rate, format, - bytes_per_sample, num_samples)) - return false; + CHECK(CheckWavParameters(num_channels, sample_rate, format, + bytes_per_sample, num_samples)); WavHeader header; const uint32_t bytes_in_payload = bytes_per_sample * num_samples; @@ -177,7 +176,6 @@ bool WriteWavHeader(uint8_t* buf, // Do an extra copy rather than writing everything to buf directly, since buf // might not be correctly aligned. memcpy(buf, &header, kWavHeaderSize); - return true; } bool ReadWavHeader(const uint8_t* buf, diff --git a/common_audio/wav_header.h b/common_audio/wav_header.h index 0901f970..37f78a6f 100644 --- a/common_audio/wav_header.h +++ b/common_audio/wav_header.h @@ -34,8 +34,8 @@ bool CheckWavParameters(int num_channels, // Write a kWavHeaderSize bytes long WAV header to buf. The payload that // follows the header is supposed to have the specified number of interleaved // channels and contain the specified total number of samples of the specified -// type. Returns false if any of the input parameters are invalid. -bool WriteWavHeader(uint8_t* buf, +// type. CHECKs the input parameters for validity. +void WriteWavHeader(uint8_t* buf, int num_channels, int sample_rate, WavFormat format, diff --git a/common_audio/wav_header_unittest.cc b/common_audio/wav_header_unittest.cc index ac79cc16..677affa5 100644 --- a/common_audio/wav_header_unittest.cc +++ b/common_audio/wav_header_unittest.cc @@ -48,7 +48,7 @@ TEST(WavHeaderTest, CheckWavParameters) { webrtc::CheckWavParameters(3, 8000, webrtc::kWavFormatPcm, 1, 5)); } -TEST(WavHeaderTest, ReadWavHeader) { +TEST(WavHeaderTest, ReadWavHeaderWithErrors) { int num_channels = 0; int sample_rate = 0; webrtc::WavFormat format = webrtc::kWavFormatPcm; @@ -120,13 +120,13 @@ TEST(WavHeaderTest, ReadWavHeader) { &format, &bytes_per_sample, &num_samples)); } -// Try writing a WAV header and make sure it looks OK. +// Try writing and reading a valid WAV header and make sure it looks OK. TEST(WavHeaderTest, WriteAndReadWavHeader) { static const int kSize = 4 + webrtc::kWavHeaderSize + 4; uint8_t buf[kSize]; memset(buf, 0xa4, sizeof(buf)); - EXPECT_TRUE(webrtc::WriteWavHeader( - buf + 4, 17, 12345, webrtc::kWavFormatALaw, 1, 123457689)); + webrtc::WriteWavHeader( + buf + 4, 17, 12345, webrtc::kWavFormatALaw, 1, 123457689); static const uint8_t kExpectedBuf[] = { 0xa4, 0xa4, 0xa4, 0xa4, // untouched bytes before header 'R', 'I', 'F', 'F', -- cgit v1.2.3 From deb9e492c3db34f9580f6799ac7fdd5bb72e3d1e Mon Sep 17 00:00:00 2001 From: "andrew@webrtc.org" Date: Mon, 3 Nov 2014 21:32:14 +0000 Subject: Add format members to AudioConverter for DCHECKing. And use a std::min. Post-commit fixes after: https://review.webrtc.org/30779004/ TBR=kwiberg Review URL: https://webrtc-codereview.appspot.com/25059004 git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@7600 4adac7df-926f-26a2-2b94-8c16560cd09d --- common_audio/audio_converter.cc | 16 +++++++++++----- common_audio/audio_converter.h | 4 ++++ 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'common_audio') diff --git a/common_audio/audio_converter.cc b/common_audio/audio_converter.cc index 9e18033f..f085ff13 100644 --- a/common_audio/audio_converter.cc +++ b/common_audio/audio_converter.cc @@ -43,10 +43,13 @@ void UpmixFromMono(const float* src, } // namespace AudioConverter::AudioConverter(int src_channels, int src_frames, - int dst_channels, int dst_frames) { + int dst_channels, int dst_frames) + : src_channels_(src_channels), + src_frames_(src_frames), + dst_channels_(dst_channels), + dst_frames_(dst_frames) { CHECK(dst_channels == src_channels || dst_channels == 1 || src_channels == 1); - const int resample_channels = src_channels < dst_channels ? src_channels : - dst_channels; + const int resample_channels = std::min(src_channels, dst_channels); // Prepare buffers as needed for intermediate stages. if (dst_channels < src_channels) @@ -66,8 +69,11 @@ void AudioConverter::Convert(const float* const* src, int dst_channels, int dst_frames, float* const* dst) { - DCHECK(dst_channels == src_channels || dst_channels == 1 || - src_channels == 1); + DCHECK_EQ(src_channels_, src_channels); + DCHECK_EQ(src_frames_, src_frames); + DCHECK_EQ(dst_channels_, dst_channels); + DCHECK_EQ(dst_frames_, dst_frames);; + if (src_channels == dst_channels && src_frames == dst_frames) { // Shortcut copy. if (src != dst) { diff --git a/common_audio/audio_converter.h b/common_audio/audio_converter.h index df31755e..6365f587 100644 --- a/common_audio/audio_converter.h +++ b/common_audio/audio_converter.h @@ -40,6 +40,10 @@ class AudioConverter { float* const* dest); private: + const int src_channels_; + const int src_frames_; + const int dst_channels_; + const int dst_frames_; scoped_ptr> downmix_buffer_; ScopedVector resamplers_; -- cgit v1.2.3