diff options
author | fbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-20 19:30:33 +0000 |
---|---|---|
committer | fbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-20 19:30:33 +0000 |
commit | 8511b2c7075a3fbc9b27652deaab575f9d5ea49d (patch) | |
tree | cc343886f722dd57ca1d060c95f8b0194cad894e /media | |
parent | 00e3f152083aea092353b3d284c368b48136f33c (diff) | |
download | chromium_src-8511b2c7075a3fbc9b27652deaab575f9d5ea49d.zip chromium_src-8511b2c7075a3fbc9b27652deaab575f9d5ea49d.tar.gz chromium_src-8511b2c7075a3fbc9b27652deaab575f9d5ea49d.tar.bz2 |
Fold first 3 channels of multichannel instead of 5. Use fixed point.
BUG=none
TEST=play a multichannel video, such as trek6.ogv. Voices should sound centered and music stereo.
Review URL: http://codereview.chromium.org/304006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29550 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/audio/audio_util.cc | 183 | ||||
-rw-r--r-- | media/audio/audio_util_unittest.cc | 24 |
2 files changed, 113 insertions, 94 deletions
diff --git a/media/audio/audio_util.cc b/media/audio/audio_util.cc index c1c1bc8..8774a72 100644 --- a/media/audio/audio_util.cc +++ b/media/audio/audio_util.cc @@ -5,24 +5,90 @@ // Software adjust volume of samples, allows each audio stream its own // volume without impacting master volume for chrome and other applications. +// Implemented as templates to allow 8, 16 and 32 bit implementations. +// 8 bit is unsigned and biased by 128. + #include "base/basictypes.h" #include "base/logging.h" #include "media/audio/audio_util.h" namespace media { -// Done as C for future assembly optimization and/or to move to different class. -// TODO(fbarchard): add channels_in to allow number of channels to be reduced. +namespace { + +// TODO(fbarchard): Convert to intrinsics for better efficiency. -template<class Format> -void AdjustVolumeInternal(Format* buf_out, - int sample_count, - float volume) { +template<class Fixed> +static int ScaleChannel(int channel, int volume) { + return static_cast<int>((static_cast<Fixed>(channel) * volume) >> 16); +} + +template<class Format, class Fixed, int bias> +void AdjustVolume(Format* buf_out, + int sample_count, + int fixed_volume) { for (int i = 0; i < sample_count; ++i) { - buf_out[i] = static_cast<Format>(buf_out[i] * volume); + buf_out[i] = static_cast<Format>(ScaleChannel<Fixed>(buf_out[i] - bias, + fixed_volume) + bias); } } +// Channel order for AAC +// From http://www.hydrogenaudio.org/forums/lofiversion/index.php/t40046.html + +static const int kChannel_C = 0; +static const int kChannel_L = 1; +static const int kChannel_R = 2; + +template<class Fixed, int min_value, int max_value> +static int AddChannel(int val, + int adder) { + Fixed sum = static_cast<Fixed>(val) + static_cast<Fixed>(adder); + if (sum > max_value) + return max_value; + if (sum < min_value) + return min_value; + return static_cast<int>(sum); +} + +// FoldChannels() downmixes multichannel (ie 5.1 Surround Sound) to Stereo. +// Left and Right channels are preserved asis, and Center channel is +// distributed equally to both sides. To be perceptually 1/2 volume on +// both channels, 1/sqrt(2) is used instead of 1/2. +// Fixed point math is used for efficiency. 16 bits of fraction and 8,16 or 32 +// bits of integer are used. +// 8 bit samples are unsigned and 128 represents 0, so a bias is removed before +// doing calculations, then readded for the final output. + +template<class Format, class Fixed, int min_value, int max_value, int bias> +static void FoldChannels(Format* buf_out, + int sample_count, + const float volume, + int channels) { + Format* buf_in = buf_out; + const int center_volume = static_cast<int>(volume * 0.707f * 65536); + const int fixed_volume = static_cast<int>(volume * 65536); + + for (int i = 0; i < sample_count; ++i) { + int center = static_cast<int>(buf_in[kChannel_C] - bias); + int left = static_cast<int>(buf_in[kChannel_L] - bias); + int right = static_cast<int>(buf_in[kChannel_R] - bias); + + center = ScaleChannel<Fixed>(center, center_volume); + left = ScaleChannel<Fixed>(left, fixed_volume); + right = ScaleChannel<Fixed>(right, fixed_volume); + + buf_out[0] = static_cast<Format>( + AddChannel<Fixed, min_value, max_value>(left, center) + bias); + buf_out[1] = static_cast<Format>( + AddChannel<Fixed, min_value, max_value>(right, center) + bias); + + buf_out += 2; + buf_in += channels; + } +} +} // namespace + // AdjustVolume() does an in place audio sample change. bool AdjustVolume(void* buf, size_t buflen, @@ -39,78 +105,27 @@ bool AdjustVolume(void* buf, } if (channels > 0 && channels <= 6 && bytes_per_sample > 0) { int sample_count = buflen / bytes_per_sample; + const int fixed_volume = static_cast<int>(volume * 65536); if (bytes_per_sample == 1) { - AdjustVolumeInternal(reinterpret_cast<uint8*>(buf), - sample_count, - volume); + AdjustVolume<uint8, int32, 128>(reinterpret_cast<uint8*>(buf), + sample_count, + fixed_volume); return true; } else if (bytes_per_sample == 2) { - AdjustVolumeInternal(reinterpret_cast<int16*>(buf), - sample_count, - volume); + AdjustVolume<int16, int32, 0>(reinterpret_cast<int16*>(buf), + sample_count, + fixed_volume); return true; } else if (bytes_per_sample == 4) { - AdjustVolumeInternal(reinterpret_cast<int32*>(buf), - sample_count, - volume); + AdjustVolume<int32, int64, 0>(reinterpret_cast<int32*>(buf), + sample_count, + fixed_volume); return true; } } return false; } -// Channel order for AAC -// From http://www.hydrogenaudio.org/forums/lofiversion/index.php/t40046.html -// And Quicktime Pro, Movie Inspector -const int kChannel_C = 0; -const int kChannel_L = 1; -const int kChannel_R = 2; -const int kChannel_SL = 3; -const int kChannel_SR = 4; -const int kChannel_LFE = 5; - -template<class Format> -Format ChannelsClampInternal(float val, - const Format min_value, - const Format max_value) { - if (val > static_cast<float>(max_value)) { - return max_value; - } - if (val < static_cast<float>(min_value)) { - return min_value; - } - return static_cast<Format>(val); -} - -template<class Format> -void FoldChannelsInternal(Format* buf_out, - int sample_count, - float volume, - int channels, - const int min_value, - const int max_value) { - Format* buf_in = buf_out; - // mid_value is to adjust excess 128 notation in unsigned 8 bit samples - // to signed before adding channels. - const int mid_value = (max_value + min_value + 1) / 2; - const float kHalfPerceived = 0.707f; // 1/sqrt(2) - for (int i = 0; i < sample_count; ++i) { - float center_half = static_cast<float>(buf_in[kChannel_C] - mid_value) * kHalfPerceived; - float left = static_cast<float>(buf_in[kChannel_L] - mid_value); - float right = static_cast<float>(buf_in[kChannel_R] - mid_value); - float surround_left = static_cast<float>(buf_in[kChannel_SL] - mid_value); - float surround_right = static_cast<float>(buf_in[kChannel_SR] - mid_value); - buf_out[0] = ChannelsClampInternal( - (left + surround_left + center_half) * volume + mid_value, - static_cast<Format>(min_value), static_cast<Format>(max_value)); - buf_out[1] = ChannelsClampInternal( - (right + surround_right + center_half) * volume + mid_value, - static_cast<Format>(min_value), static_cast<Format>(max_value)); - buf_out += 2; - buf_in += channels; - } -} - bool FoldChannels(void* buf, size_t buflen, int channels, @@ -121,27 +136,25 @@ bool FoldChannels(void* buf, if (channels >= 5 && channels <= 6 && bytes_per_sample > 0) { int sample_count = buflen / (channels * bytes_per_sample); if (bytes_per_sample == 1) { - FoldChannelsInternal(reinterpret_cast<uint8*>(buf), - sample_count, - volume, - channels, - 0, 255); + FoldChannels<uint8, int32, -128, 127, 128>( + reinterpret_cast<uint8*>(buf), + sample_count, + volume, + channels); return true; } else if (bytes_per_sample == 2) { - FoldChannelsInternal(reinterpret_cast<int16*>(buf), - sample_count, - volume, - channels, - -32768, - 32767); + FoldChannels<int16, int32, -32768, 32767, 0>( + reinterpret_cast<int16*>(buf), + sample_count, + volume, + channels); return true; } else if (bytes_per_sample == 4) { - FoldChannelsInternal(reinterpret_cast<int32*>(buf), - sample_count, - volume, - channels, - 0x80000000, - 0x7fffffff); + FoldChannels<int32, int64, 0x80000000, 0x7fffffff, 0>( + reinterpret_cast<int32*>(buf), + sample_count, + volume, + channels); return true; } } diff --git a/media/audio/audio_util_unittest.cc b/media/audio/audio_util_unittest.cc index 4554f6e..c0ed0fc 100644 --- a/media/audio/audio_util_unittest.cc +++ b/media/audio/audio_util_unittest.cc @@ -14,7 +14,10 @@ namespace media { TEST(AudioUtilTest, AdjustVolume_u8) { // Test AdjustVolume() on 8 bit samples. uint8 samples_u8[kNumberOfSamples] = { 4, 0x40, 0x80, 0xff }; - uint8 expected_u8[kNumberOfSamples] = { 2, 0x20, 0x40, 0x7f }; + uint8 expected_u8[kNumberOfSamples] = { (4 - 128) / 2 + 128, + (0x40 - 128) / 2 + 128, + (0x80 - 128) / 2 + 128, + (0xff - 128) / 2 + 128 }; bool result_u8 = media::AdjustVolume(samples_u8, sizeof(samples_u8), 1, // channels. sizeof(samples_u8[0]), @@ -77,9 +80,12 @@ TEST(AudioUtilTest, AdjustVolume_s32) { } TEST(AudioUtilTest, FoldChannels_u8) { - // Test AdjustVolume() on 16 bit samples. + // Test FoldChannels() on 8 bit samples. uint8 samples_u8[6] = { 130, 100, 150, 70, 130, 170 }; - uint8 expected_u8[2] = { 43, 153 }; + uint8 expected_u8[2] = { static_cast<uint8>((130 - 128) * 0.707 + + (100 - 128) + 128), + static_cast<uint8>((130 - 128) * 0.707 + + (150 - 128) + 128) }; bool result_u8 = media::FoldChannels(samples_u8, sizeof(samples_u8), 6, // channels. sizeof(samples_u8[0]), @@ -90,10 +96,10 @@ TEST(AudioUtilTest, FoldChannels_u8) { } TEST(AudioUtilTest, FoldChannels_s16) { - // Test AdjustVolume() on 16 bit samples. + // Test FoldChannels() on 16 bit samples. int16 samples_s16[6] = { 12, 1, 3, 7, 13, 17 }; - int16 expected_s16[2] = { static_cast<int16>(12 * .707 + 1 + 7), - static_cast<int16>(12 * .707 + 3 + 13) }; + int16 expected_s16[2] = { static_cast<int16>(12 * .707 + 1), + static_cast<int16>(12 * .707 + 3) }; bool result_s16 = media::FoldChannels(samples_s16, sizeof(samples_s16), 6, // channels. sizeof(samples_s16[0]), @@ -104,10 +110,10 @@ TEST(AudioUtilTest, FoldChannels_s16) { } TEST(AudioUtilTest, FoldChannels_s32) { - // Test AdjustVolume() on 16 bit samples. + // Test FoldChannels() on 32 bit samples. int32 samples_s32[6] = { 12, 1, 3, 7, 13, 17 }; - int32 expected_s32[2] = { static_cast<int16>(12 * .707 + 1 + 7), - static_cast<int16>(12 * .707 + 3 + 13) }; + int32 expected_s32[2] = { static_cast<int16>(12 * .707 + 1), + static_cast<int16>(12 * .707 + 3) }; bool result_s32 = media::FoldChannels(samples_s32, sizeof(samples_s32), 6, // channels. sizeof(samples_s32[0]), |