diff options
author | fbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-20 20:20:47 +0000 |
---|---|---|
committer | fbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-20 20:20:47 +0000 |
commit | 762b625b3de00f106016a42bc9cdf774af4e1027 (patch) | |
tree | bf234d8459e12d671207e2341cccde9e6877da54 /media | |
parent | 3b28936e5f88399fd4256df0ad02f117da38c8cb (diff) | |
download | chromium_src-762b625b3de00f106016a42bc9cdf774af4e1027.zip chromium_src-762b625b3de00f106016a42bc9cdf774af4e1027.tar.gz chromium_src-762b625b3de00f106016a42bc9cdf774af4e1027.tar.bz2 |
Software volume adjustment by changing samples.
BUG=16500
TEST=play youtube in different window. play another video with video tag and mute sound and make sure it does not affect youtube.
Review URL: http://codereview.chromium.org/159092
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@21091 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/audio/audio_util.cc | 62 | ||||
-rw-r--r-- | media/audio/audio_util.h | 30 | ||||
-rw-r--r-- | media/audio/audio_util_unittest.cc | 66 | ||||
-rw-r--r-- | media/audio/win/waveout_output_win.cc | 36 | ||||
-rw-r--r-- | media/audio/win/waveout_output_win.h | 10 | ||||
-rw-r--r-- | media/media.gyp | 5 |
6 files changed, 182 insertions, 27 deletions
diff --git a/media/audio/audio_util.cc b/media/audio/audio_util.cc new file mode 100644 index 0000000..dabe448 --- /dev/null +++ b/media/audio/audio_util.cc @@ -0,0 +1,62 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Software adjust volume of samples, allows each audio stream its own +// volume without impacting master volume for chrome and other applications. + +#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. + +template<class Format> +void AdjustVolumeInternal(Format* buf_out, + int sample_count, + float volume) { + for (int i = 0; i < sample_count; ++i) { + buf_out[i] = static_cast<Format>(buf_out[i] * volume); + } +} + +// AdjustVolume() does an in place audio sample change. +bool AdjustVolume(void* buf, + size_t buflen, + int channels, + int bytes_per_sample, + float volume) { + DCHECK(buf); + DCHECK(volume >= 0.0f && volume <= 1.0f); + if (volume == 1.0f) { + return true; + } else if (volume == 0.0f) { + memset(buf, 0, buflen); + return true; + } + if (channels > 0 && channels <= 6 && bytes_per_sample > 0) { + int sample_count = buflen / bytes_per_sample; + if (bytes_per_sample == 1) { + AdjustVolumeInternal(reinterpret_cast<uint8*>(buf), + sample_count, + volume); + return true; + } else if (bytes_per_sample == 2) { + AdjustVolumeInternal(reinterpret_cast<int16*>(buf), + sample_count, + volume); + return true; + } else if (bytes_per_sample == 4) { + AdjustVolumeInternal(reinterpret_cast<int32*>(buf), + sample_count, + volume); + return true; + } + } + return false; +} + +} // namespace media diff --git a/media/audio/audio_util.h b/media/audio/audio_util.h new file mode 100644 index 0000000..14fe169 --- /dev/null +++ b/media/audio/audio_util.h @@ -0,0 +1,30 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_AUDIO_AUDIO_UTIL_H_ +#define MEDIA_AUDIO_AUDIO_UTIL_H_ + +#include "base/basictypes.h" + +namespace media { + +// 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). +// Using software allows each audio and video to have its own volume without +// affecting the master volume. +// In the future the function may be used to adjust the sample format to +// simplify hardware requirements and to support a wider variety of input +// formats. +// The buffer is modified in-place to avoid memory management, as this +// function may be called in performance critical code. +bool AdjustVolume(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 new file mode 100644 index 0000000..415a108 --- /dev/null +++ b/media/audio/audio_util_unittest.cc @@ -0,0 +1,66 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "media/audio/audio_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +// Number of samples in each audio array. +static const size_t kNumberOfSamples = 4; + +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 }; + bool result_u8 = media::AdjustVolume(samples_u8, sizeof(samples_u8), + 1, // channels. + sizeof(samples_u8[0]), + 0.5f); + EXPECT_EQ(true, result_u8); + int expected_test = memcmp(samples_u8, expected_u8, sizeof(expected_u8)); + EXPECT_EQ(0, expected_test); +} + +TEST(AudioUtilTest, AdjustVolume_s16) { + // Test AdjustVolume() on 16 bit samples. + int16 samples_s16[kNumberOfSamples] = { -4, 0x40, -32768, 123 }; + int16 expected_s16[kNumberOfSamples] = { -1, 0x10, -8192, 30 }; + bool result_s16 = media::AdjustVolume(samples_s16, sizeof(samples_s16), + 2, // channels. + sizeof(samples_s16[0]), + 0.25f); + EXPECT_EQ(true, result_s16); + int expected_test = memcmp(samples_s16, expected_s16, sizeof(expected_s16)); + EXPECT_EQ(0, expected_test); +} + +TEST(AudioUtilTest, AdjustVolume_s16_zero) { + // Test AdjustVolume() on 16 bit samples. + int16 samples_s16[kNumberOfSamples] = { -4, 0x40, -32768, 123 }; + int16 expected_s16[kNumberOfSamples] = { 0, 0, 0, 0 }; + bool result_s16 = media::AdjustVolume(samples_s16, sizeof(samples_s16), + 2, // channels. + sizeof(samples_s16[0]), + 0.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 }; + int32 expected_s32[kNumberOfSamples] = { -1, 0x10, -8192, 30 }; + bool result_s32 = media::AdjustVolume(samples_s32, sizeof(samples_s32), + 4, // channels. + sizeof(samples_s32[0]), + 0.25f); + EXPECT_EQ(true, result_s32); + int expected_test = memcmp(samples_s32, expected_s32, sizeof(expected_s32)); + EXPECT_EQ(0, expected_test); +} + +} // namespace media diff --git a/media/audio/win/waveout_output_win.cc b/media/audio/win/waveout_output_win.cc index 77b2d36..d8d2ebdd 100644 --- a/media/audio/win/waveout_output_win.cc +++ b/media/audio/win/waveout_output_win.cc @@ -11,6 +11,7 @@ #include "base/basictypes.h" #include "base/logging.h" #include "media/audio/audio_output.h" +#include "media/audio/audio_util.h" #include "media/audio/win/audio_manager_win.h" // Some general thoughts about the waveOut API which is badly documented : @@ -34,10 +35,6 @@ namespace { // between how fast data needs to be provided versus memory usage. const size_t kNumBuffers = 2; -// The maximum volume for a PCM device on windows. Volume is logarithmic so the -// perceived volume increase sounds linear. -const double kMaxVolumeLevel = 65535.0; - // Sixty four MB is the maximum buffer size per AudioOutputStream. const size_t kMaxOpenBufferSize = 1024 * 1024 * 64; @@ -58,7 +55,8 @@ PCMWaveOutAudioOutputStream::PCMWaveOutAudioOutputStream( waveout_(NULL), callback_(NULL), buffer_(NULL), - buffer_size_(0) { + buffer_size_(0), + volume_(1) { format_.wFormatTag = WAVE_FORMAT_PCM; format_.nChannels = channels; format_.nSamplesPerSec = sampling_rate; @@ -143,10 +141,11 @@ void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) { state_ = PCMA_PLAYING; WAVEHDR* buffer = buffer_; for (int ix = 0; ix != kNumBuffers; ++ix) { - QueueNextPacket(buffer); + QueueNextPacket(buffer); // Read more data. buffer = GetNextBuffer(buffer); } buffer = buffer_; + // Send the buffers to the audio driver. for (int ix = 0; ix != kNumBuffers; ++ix) { MMRESULT result = ::waveOutWrite(waveout_, buffer, sizeof(WAVEHDR)); @@ -202,31 +201,18 @@ void PCMWaveOutAudioOutputStream::Close() { } void PCMWaveOutAudioOutputStream::SetVolume(double left_level, - double right_level) { + double ) { if (!waveout_) return; - uint16 left = static_cast<uint16>(left_level * kMaxVolumeLevel); - uint16 right = static_cast<uint16>(right_level * kMaxVolumeLevel); - DWORD volume_packed = MAKELONG(left, right); - MMRESULT res = ::waveOutSetVolume(waveout_, volume_packed); - if (res != MMSYSERR_NOERROR) { - HandleError(res); - return; - } + volume_ = static_cast<float>(left_level); } void PCMWaveOutAudioOutputStream::GetVolume(double* left_level, double* right_level) { if (!waveout_) return; - DWORD volume_packed = 0; - MMRESULT res = ::waveOutGetVolume(waveout_, &volume_packed); - if (res != MMSYSERR_NOERROR) { - HandleError(res); - return; - } - *left_level = static_cast<double>(LOWORD(volume_packed)) / kMaxVolumeLevel; - *right_level = static_cast<double>(HIWORD(volume_packed)) / kMaxVolumeLevel; + *left_level = volume_; + *right_level = volume_; } size_t PCMWaveOutAudioOutputStream::GetNumBuffers() { @@ -245,6 +231,9 @@ void PCMWaveOutAudioOutputStream::QueueNextPacket(WAVEHDR *buffer) { 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_); } else { HandleError(0); return; @@ -279,6 +268,7 @@ void PCMWaveOutAudioOutputStream::WaveCallback(HWAVEOUT hwo, UINT msg, return; } obj->QueueNextPacket(buffer); + // Time to send the buffer to the audio driver. Since we are reusing // the same buffers we can get away without calling waveOutPrepareHeader. MMRESULT result = ::waveOutWrite(hwo, buffer, sizeof(WAVEHDR)); diff --git a/media/audio/win/waveout_output_win.h b/media/audio/win/waveout_output_win.h index 8b90e69..ea9bdef 100644 --- a/media/audio/win/waveout_output_win.h +++ b/media/audio/win/waveout_output_win.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MEDIA_AUDIO_WAVEOUT_OUTPUT_WIN_H_ -#define MEDIA_AUDIO_WAVEOUT_OUTPUT_WIN_H_ +#ifndef MEDIA_AUDIO_WIN_WAVEOUT_OUTPUT_WIN_H_ +#define MEDIA_AUDIO_WIN_WAVEOUT_OUTPUT_WIN_H_ #include <windows.h> #include <mmsystem.h> @@ -84,6 +84,9 @@ class PCMWaveOutAudioOutputStream : public AudioOutputStream { // The size in bytes of each audio buffer, we usually have two of these. size_t buffer_size_; + // Volume level from 0 to 1. + float volume_; + // 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_; @@ -103,4 +106,5 @@ class PCMWaveOutAudioOutputStream : public AudioOutputStream { DISALLOW_COPY_AND_ASSIGN(PCMWaveOutAudioOutputStream); }; -#endif // MEDIA_AUDIO_WAVEOUT_OUTPUT_WIN_H_ +#endif // MEDIA_AUDIO_WIN_WAVEOUT_OUTPUT_WIN_H_ + diff --git a/media/media.gyp b/media/media.gyp index 5b4b96a..33a17c6 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -30,6 +30,8 @@ 'msvs_guid': '6AE76406-B03B-11DD-94B1-80B556D89593', 'sources': [ 'audio/audio_output.h', + 'audio/audio_util.cc', + 'audio/audio_util.h', 'audio/linux/audio_manager_linux.cc', 'audio/linux/audio_manager_linux.h', 'audio/linux/alsa_output.cc', @@ -147,9 +149,10 @@ '../third_party/ffmpeg/ffmpeg.gyp:ffmpeg', ], 'sources': [ - 'audio/win/audio_output_win_unittest.cc', + 'audio/audio_util_unittest.cc', 'audio/mac/audio_output_mac_unittest.cc', 'audio/simple_sources_unittest.cc', + 'audio/win/audio_output_win_unittest.cc', 'base/buffer_queue_unittest.cc', 'base/data_buffer_unittest.cc', 'base/mock_ffmpeg.cc', |