diff options
author | fbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-25 03:16:21 +0000 |
---|---|---|
committer | fbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-25 03:16:21 +0000 |
commit | 9787fec1d6a14f3ca72288d277d3049c2c65b85e (patch) | |
tree | 07c5beebcd00439d414831d179cec77d7dfe0c38 /media | |
parent | e435d6b7103f0d0c57133121454458bda6ccb69f (diff) | |
download | chromium_src-9787fec1d6a14f3ca72288d277d3049c2c65b85e.zip chromium_src-9787fec1d6a14f3ca72288d277d3049c2c65b85e.tar.gz chromium_src-9787fec1d6a14f3ca72288d277d3049c2c65b85e.tar.bz2 |
Surround Sound handling by folding 5 channels down to stereo.
BUG=16026
TEST=play a movie trailer with 5.1 audio. It should sound roughly the same as quicktime.
Review URL: http://codereview.chromium.org/155894
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@21613 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/audio/audio_util.cc | 89 | ||||
-rw-r--r-- | media/audio/audio_util.h | 24 | ||||
-rw-r--r-- | media/audio/audio_util_unittest.cc | 71 | ||||
-rw-r--r-- | media/audio/win/waveout_output_win.cc | 25 | ||||
-rw-r--r-- | media/audio/win/waveout_output_win.h | 3 |
5 files changed, 207 insertions, 5 deletions
diff --git a/media/audio/audio_util.cc b/media/audio/audio_util.cc index dabe448..c1c1bc8 100644 --- a/media/audio/audio_util.cc +++ b/media/audio/audio_util.cc @@ -59,4 +59,93 @@ bool AdjustVolume(void* buf, 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, + int bytes_per_sample, + float volume) { + DCHECK(buf); + DCHECK(volume >= 0.0f && volume <= 1.0f); + 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); + return true; + } else if (bytes_per_sample == 2) { + FoldChannelsInternal(reinterpret_cast<int16*>(buf), + sample_count, + volume, + channels, + -32768, + 32767); + return true; + } else if (bytes_per_sample == 4) { + FoldChannelsInternal(reinterpret_cast<int32*>(buf), + sample_count, + volume, + channels, + 0x80000000, + 0x7fffffff); + return true; + } + } + return false; +} + } // namespace media diff --git a/media/audio/audio_util.h b/media/audio/audio_util.h index 14fe169..edfe7ab 100644 --- a/media/audio/audio_util.h +++ b/media/audio/audio_util.h @@ -9,6 +9,11 @@ namespace media { +// For all audio functions 3 audio formats are supported: +// 8 bits unsigned 0 to 255. +// 16 bit signed (little endian). +// 32 bit signed (little endian) + // AdjustVolume() does a software volume adjustment of a sample buffer. // The samples are multiplied by the volume, which should range from // 0.0 (mute) to 1.0 (full volume). @@ -25,6 +30,25 @@ bool AdjustVolume(void* buf, int bytes_per_sample, float volume); +// FoldChannels() does a software multichannel folding down to stereo. +// Channel order is assumed to be 5.1 Dolby standard which is +// front left, front right, center, surround left, surround right. +// The subwoofer is ignored. +// 6.1 adds a rear center speaker, and 7.1 has 2 rear speakers. These +// channels are rare and ignored. +// After summing the channels, volume is adjusted and the samples are +// clipped to the maximum value. +// Volume should normally range from 0.0 (mute) to 1.0 (full volume), but +// since clamping is performed a value of more than 1 is allowed to increase +// volume. +// The buffer is modified in-place to avoid memory management, as this +// function may be called in performance critical code. +bool FoldChannels(void* buf, + size_t buflen, + int channels, + int bytes_per_sample, + float volume); + } // namespace media #endif // MEDIA_AUDIO_AUDIO_UTIL_H_ diff --git a/media/audio/audio_util_unittest.cc b/media/audio/audio_util_unittest.cc index 415a108..aa5df4c 100644 --- a/media/audio/audio_util_unittest.cc +++ b/media/audio/audio_util_unittest.cc @@ -50,6 +50,19 @@ TEST(AudioUtilTest, AdjustVolume_s16_zero) { EXPECT_EQ(0, expected_test); } +TEST(AudioUtilTest, AdjustVolume_s16_one) { + // Test AdjustVolume() on 16 bit samples. + int16 samples_s16[kNumberOfSamples] = { -4, 0x40, -32768, 123 }; + int16 expected_s16[kNumberOfSamples] = { -4, 0x40, -32768, 123 }; + bool result_s16 = media::AdjustVolume(samples_s16, sizeof(samples_s16), + 2, // channels. + sizeof(samples_s16[0]), + 1.0f); + EXPECT_EQ(true, result_s16); + int expected_test = memcmp(samples_s16, expected_s16, sizeof(expected_s16)); + EXPECT_EQ(0, expected_test); +} + TEST(AudioUtilTest, AdjustVolume_s32) { // Test AdjustVolume() on 32 bit samples. int32 samples_s32[kNumberOfSamples] = { -4, 0x40, -32768, 123 }; @@ -63,4 +76,62 @@ TEST(AudioUtilTest, AdjustVolume_s32) { EXPECT_EQ(0, expected_test); } +TEST(AudioUtilTest, FoldChannels_u8) { + // Test AdjustVolume() on 16 bit samples. + uint8 samples_u8[6] = { 130, 100, 150, 70, 130, 170 }; + uint8 expected_u8[2] = { 43, 153 }; + bool result_u8 = media::FoldChannels(samples_u8, sizeof(samples_u8), + 6, // channels. + sizeof(samples_u8[0]), + 1.0f); + EXPECT_EQ(true, result_u8); + int expected_test = memcmp(samples_u8, expected_u8, sizeof(expected_u8)); + EXPECT_EQ(0, expected_test); +} + +TEST(AudioUtilTest, FoldChannels_s16) { + // Test AdjustVolume() 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) }; + bool result_s16 = media::FoldChannels(samples_s16, sizeof(samples_s16), + 6, // channels. + sizeof(samples_s16[0]), + 1.00f); + EXPECT_EQ(true, result_s16); + int expected_test = memcmp(samples_s16, expected_s16, sizeof(expected_s16)); + EXPECT_EQ(0, expected_test); +} + +TEST(AudioUtilTest, FoldChannels_s32) { + // Test AdjustVolume() on 16 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) }; + bool result_s32 = media::FoldChannels(samples_s32, sizeof(samples_s32), + 6, // channels. + sizeof(samples_s32[0]), + 1.00f); + EXPECT_EQ(true, result_s32); + int expected_test = memcmp(samples_s32, expected_s32, sizeof(expected_s32)); + EXPECT_EQ(0, expected_test); +} + +// This mimics 1 second of audio at 48000 samples per second. +// Running the unittest will produce timing. +TEST(AudioUtilTest, FoldChannels_s16_benchmark) { + const int kBufferSize = 1024 * 6; + // Test AdjustVolume() on 16 bit samples. + for (int i = 0; i < 48000; ++i) { + int16 samples_s16[kBufferSize]; + for (int j = 0; j < kBufferSize; ++j) + samples_s16[j] = j; + + bool result_s16 = media::FoldChannels(samples_s16, sizeof(samples_s16), + 6, // channels. + sizeof(samples_s16[0]), + 0.5f); + EXPECT_EQ(true, result_s16); + } +} } // namespace media diff --git a/media/audio/win/waveout_output_win.cc b/media/audio/win/waveout_output_win.cc index 124d8e7..40d17d3 100644 --- a/media/audio/win/waveout_output_win.cc +++ b/media/audio/win/waveout_output_win.cc @@ -29,6 +29,9 @@ // to make sure we are not executing inside the audio source's OnMoreData() // or that we take locks inside WaveCallback() or QueueNextPacket(). +// Enable or disable software folding +#define FOLDING 1 + namespace { // We settled for a double buffering scheme. It seems to strike a good balance @@ -56,9 +59,14 @@ PCMWaveOutAudioOutputStream::PCMWaveOutAudioOutputStream( callback_(NULL), buffer_(NULL), buffer_size_(0), - volume_(1) { + volume_(1), + channels_(channels) { format_.wFormatTag = WAVE_FORMAT_PCM; +#ifdef FOLDING + format_.nChannels = channels > 2 ? 2 : channels; +#else format_.nChannels = channels; +#endif format_.nSamplesPerSec = sampling_rate; format_.wBitsPerSample = bits_per_sample; format_.cbSize = 0; @@ -226,10 +234,17 @@ void PCMWaveOutAudioOutputStream::QueueNextPacket(WAVEHDR *buffer) { // TODO(fbarchard): Handle used 0 by queueing more. size_t used = callback_->OnMoreData(this, buffer->lpData, buffer_size_); if (used <= buffer_size_) { - buffer->dwBufferLength = used; - media::AdjustVolume(buffer->lpData, buffer->dwBufferLength, - format_.nChannels, format_.wBitsPerSample >> 3, - volume_); + buffer->dwBufferLength = used * format_.nChannels / channels_; + if (channels_ > 2 && format_.nChannels == 2) { + media::FoldChannels(buffer->lpData, used, + channels_, format_.wBitsPerSample >> 3, + volume_); + } else { + media::AdjustVolume(buffer->lpData, used, + format_.nChannels, format_.wBitsPerSample >> 3, + volume_); + } + } else { HandleError(0); return; diff --git a/media/audio/win/waveout_output_win.h b/media/audio/win/waveout_output_win.h index 3f822e9..e072851 100644 --- a/media/audio/win/waveout_output_win.h +++ b/media/audio/win/waveout_output_win.h @@ -86,6 +86,9 @@ class PCMWaveOutAudioOutputStream : public AudioOutputStream { // Volume level from 0 to 1. float volume_; + // Channels from 0 to 6. + int channels_; + // The id assigned by the operating system to the selected wave output // hardware device. Usually this is just -1 which means 'default device'. UINT device_id_; |