summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorfbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-20 20:20:47 +0000
committerfbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-20 20:20:47 +0000
commit762b625b3de00f106016a42bc9cdf774af4e1027 (patch)
treebf234d8459e12d671207e2341cccde9e6877da54 /media
parent3b28936e5f88399fd4256df0ad02f117da38c8cb (diff)
downloadchromium_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.cc62
-rw-r--r--media/audio/audio_util.h30
-rw-r--r--media/audio/audio_util_unittest.cc66
-rw-r--r--media/audio/win/waveout_output_win.cc36
-rw-r--r--media/audio/win/waveout_output_win.h10
-rw-r--r--media/media.gyp5
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',