| // Copyright (c) 2012 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. |
| |
| // Implemented as templates to allow 8, 16 and 32 bit implementations. |
| // 8 bit is unsigned and biased by 128. |
| |
| // TODO(vrk): This file has been running pretty wild and free, and it's likely |
| // that a lot of the functions can be simplified and made more elegant. Revisit |
| // after other audio cleanup is done. (crbug.com/120319) |
| |
| #include "media/audio/audio_util.h" |
| |
| #include <algorithm> |
| #include <limits> |
| |
| #include "base/basictypes.h" |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| #include "base/string_number_conversions.h" |
| #include "base/time.h" |
| #include "media/audio/audio_parameters.h" |
| #include "media/base/audio_bus.h" |
| #include "media/base/media_switches.h" |
| |
| #if defined(OS_MACOSX) |
| #include "media/audio/mac/audio_low_latency_input_mac.h" |
| #include "media/audio/mac/audio_low_latency_output_mac.h" |
| #elif defined(OS_WIN) |
| #include "base/win/windows_version.h" |
| #include "media/audio/audio_manager_base.h" |
| #include "media/audio/win/audio_low_latency_input_win.h" |
| #include "media/audio/win/audio_low_latency_output_win.h" |
| #include "media/audio/win/core_audio_util_win.h" |
| #include "media/base/limits.h" |
| #endif |
| |
| namespace media { |
| |
| // Returns user buffer size as specified on the command line or 0 if no buffer |
| // size has been specified. |
| static int GetUserBufferSize() { |
| const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
| int buffer_size = 0; |
| std::string buffer_size_str(cmd_line->GetSwitchValueASCII( |
| switches::kAudioBufferSize)); |
| if (base::StringToInt(buffer_size_str, &buffer_size) && buffer_size > 0) { |
| return buffer_size; |
| } |
| |
| return 0; |
| } |
| |
| // TODO(fbarchard): Convert to intrinsics for better efficiency. |
| 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> |
| static 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>(ScaleChannel<Fixed>(buf_out[i] - bias, |
| fixed_volume) + bias); |
| } |
| } |
| |
| template<class Fixed, int min_value, int max_value> |
| static int AddSaturated(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); |
| } |
| |
| // 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); |
| if (volume < 0.0f || volume > 1.0f) |
| return false; |
| if (volume == 1.0f) { |
| return true; |
| } else if (volume == 0.0f) { |
| memset(buf, 0, buflen); |
| return true; |
| } |
| if (channels > 0 && channels <= 8 && 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) { |
| AdjustVolume<uint8, int32, 128>(reinterpret_cast<uint8*>(buf), |
| sample_count, |
| fixed_volume); |
| return true; |
| } else if (bytes_per_sample == 2) { |
| AdjustVolume<int16, int32, 0>(reinterpret_cast<int16*>(buf), |
| sample_count, |
| fixed_volume); |
| return true; |
| } else if (bytes_per_sample == 4) { |
| AdjustVolume<int32, int64, 0>(reinterpret_cast<int32*>(buf), |
| sample_count, |
| fixed_volume); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| // TODO(enal): use template specialization and size-specific intrinsics. |
| // Call is on the time-critical path, and by using SSE/AVX |
| // instructions we can speed things up by ~4-8x, more for the case |
| // when we have to adjust volume as well. |
| template<class Format, class Fixed, int min_value, int max_value, int bias> |
| static void MixStreams(Format* dst, Format* src, int count, float volume) { |
| if (volume == 0.0f) |
| return; |
| if (volume == 1.0f) { |
| // Most common case -- no need to adjust volume. |
| for (int i = 0; i < count; ++i) { |
| Fixed value = AddSaturated<Fixed, min_value, max_value>(dst[i] - bias, |
| src[i] - bias); |
| dst[i] = static_cast<Format>(value + bias); |
| } |
| } else { |
| // General case -- have to adjust volume before mixing. |
| const int fixed_volume = static_cast<int>(volume * 65536); |
| for (int i = 0; i < count; ++i) { |
| Fixed adjusted_src = ScaleChannel<Fixed>(src[i] - bias, fixed_volume); |
| Fixed value = AddSaturated<Fixed, min_value, max_value>(dst[i] - bias, |
| adjusted_src); |
| dst[i] = static_cast<Format>(value + bias); |
| } |
| } |
| } |
| |
| void MixStreams(void* dst, |
| void* src, |
| size_t buflen, |
| int bytes_per_sample, |
| float volume) { |
| DCHECK(dst); |
| DCHECK(src); |
| DCHECK_GE(volume, 0.0f); |
| DCHECK_LE(volume, 1.0f); |
| switch (bytes_per_sample) { |
| case 1: |
| MixStreams<uint8, int32, kint8min, kint8max, 128>( |
| static_cast<uint8*>(dst), |
| static_cast<uint8*>(src), |
| buflen, |
| volume); |
| break; |
| case 2: |
| DCHECK_EQ(0u, buflen % 2); |
| MixStreams<int16, int32, kint16min, kint16max, 0>( |
| static_cast<int16*>(dst), |
| static_cast<int16*>(src), |
| buflen / 2, |
| volume); |
| break; |
| case 4: |
| DCHECK_EQ(0u, buflen % 4); |
| MixStreams<int32, int64, kint32min, kint32max, 0>( |
| static_cast<int32*>(dst), |
| static_cast<int32*>(src), |
| buflen / 4, |
| volume); |
| break; |
| default: |
| NOTREACHED() << "Illegal bytes per sample"; |
| break; |
| } |
| } |
| |
| int GetAudioHardwareSampleRate() { |
| #if defined(OS_MACOSX) |
| // Hardware sample-rate on the Mac can be configured, so we must query. |
| return AUAudioOutputStream::HardwareSampleRate(); |
| #elif defined(OS_WIN) |
| if (!CoreAudioUtil::IsSupported()) { |
| // Fall back to Windows Wave implementation on Windows XP or lower |
| // and use 48kHz as default input sample rate. |
| return 48000; |
| } |
| |
| // TODO(crogers): tune this rate for best possible WebAudio performance. |
| // WebRTC works well at 48kHz and a buffer size of 480 samples will be used |
| // for this case. Note that exclusive mode is experimental. |
| const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
| if (cmd_line->HasSwitch(switches::kEnableExclusiveAudio)) { |
| // This sample rate will be combined with a buffer size of 256 samples |
| // (see GetAudioHardwareBufferSize()), which corresponds to an output |
| // delay of ~5.33ms. |
| return 48000; |
| } |
| |
| // Hardware sample-rate on Windows can be configured, so we must query. |
| // TODO(henrika): improve possibility to specify an audio endpoint. |
| // Use the default device (same as for Wave) for now to be compatible |
| // or possibly remove the ERole argument completely until it is in use. |
| return WASAPIAudioOutputStream::HardwareSampleRate(eConsole); |
| #elif defined(OS_ANDROID) |
| return 16000; |
| #else |
| // Hardware for Linux is nearly always 48KHz. |
| // TODO(crogers) : return correct value in rare non-48KHz cases. |
| return 48000; |
| #endif |
| } |
| |
| int GetAudioInputHardwareSampleRate(const std::string& device_id) { |
| // TODO(henrika): add support for device selection on all platforms. |
| // Only exists on Windows today. |
| #if defined(OS_MACOSX) |
| return AUAudioInputStream::HardwareSampleRate(); |
| #elif defined(OS_WIN) |
| if (!CoreAudioUtil::IsSupported()) { |
| return 48000; |
| } |
| return WASAPIAudioInputStream::HardwareSampleRate(device_id); |
| #elif defined(OS_ANDROID) |
| return 16000; |
| #else |
| return 48000; |
| #endif |
| } |
| |
| size_t GetAudioHardwareBufferSize() { |
| int user_buffer_size = GetUserBufferSize(); |
| if (user_buffer_size) |
| return user_buffer_size; |
| |
| // The sizes here were determined by experimentation and are roughly |
| // the lowest value (for low latency) that still allowed glitch-free |
| // audio under high loads. |
| // |
| // For Mac OS X and Windows the chromium audio backend uses a low-latency |
| // Core Audio API, so a low buffer size is possible. For Linux, further |
| // tuning may be needed. |
| #if defined(OS_MACOSX) |
| return 128; |
| #elif defined(OS_WIN) |
| // Buffer size to use when a proper size can't be determined from the system. |
| static const int kFallbackBufferSize = 4096; |
| |
| if (!CoreAudioUtil::IsSupported()) { |
| // Fall back to Windows Wave implementation on Windows XP or lower |
| // and assume 48kHz as default sample rate. |
| return kFallbackBufferSize; |
| } |
| |
| // TODO(crogers): tune this size to best possible WebAudio performance. |
| // WebRTC always uses 10ms for Windows and does not call this method. |
| // Note that exclusive mode is experimental. |
| const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
| if (cmd_line->HasSwitch(switches::kEnableExclusiveAudio)) { |
| return 256; |
| } |
| |
| // TODO(henrika): remove when the --enable-webaudio-input flag is no longer |
| // utilized. |
| if (cmd_line->HasSwitch(switches::kEnableWebAudioInput)) { |
| AudioParameters params; |
| HRESULT hr = CoreAudioUtil::GetPreferredAudioParameters(eRender, eConsole, |
| ¶ms); |
| return FAILED(hr) ? kFallbackBufferSize : params.frames_per_buffer(); |
| } |
| |
| // This call must be done on a COM thread configured as MTA. |
| // TODO(tommi): http://code.google.com/p/chromium/issues/detail?id=103835. |
| int mixing_sample_rate = |
| WASAPIAudioOutputStream::HardwareSampleRate(eConsole); |
| |
| // Windows will return a sample rate of 0 when no audio output is available |
| // (i.e. via RemoteDesktop with remote audio disabled), but we should never |
| // return a buffer size of zero. |
| if (mixing_sample_rate == 0) |
| return kFallbackBufferSize; |
| |
| // Use different buffer sizes depening on the sample rate . The existing |
| // WASAPI implementation is tuned to provide the most stable callback |
| // sequence using these combinations. |
| if (mixing_sample_rate % 11025 == 0) |
| // Use buffer size of ~10.15873 ms. |
| return (112 * (mixing_sample_rate / 11025)); |
| |
| if (mixing_sample_rate % 8000 == 0) |
| // Use buffer size of 10ms. |
| return (80 * (mixing_sample_rate / 8000)); |
| |
| // Ensure we always return a buffer size which is somewhat appropriate. |
| LOG(ERROR) << "Unknown sample rate " << mixing_sample_rate << " detected."; |
| if (mixing_sample_rate > limits::kMinSampleRate) |
| return (mixing_sample_rate / 100); |
| return kFallbackBufferSize; |
| #else |
| return 2048; |
| #endif |
| } |
| |
| ChannelLayout GetAudioInputHardwareChannelLayout(const std::string& device_id) { |
| // TODO(henrika): add support for device selection on all platforms. |
| // Only exists on Windows today. |
| #if defined(OS_MACOSX) |
| return CHANNEL_LAYOUT_MONO; |
| #elif defined(OS_WIN) |
| if (!CoreAudioUtil::IsSupported()) { |
| // Fall back to Windows Wave implementation on Windows XP or lower and |
| // use stereo by default. |
| return CHANNEL_LAYOUT_STEREO; |
| } |
| return WASAPIAudioInputStream::HardwareChannelCount(device_id) == 1 ? |
| CHANNEL_LAYOUT_MONO : CHANNEL_LAYOUT_STEREO; |
| #else |
| return CHANNEL_LAYOUT_STEREO; |
| #endif |
| } |
| |
| // Computes a buffer size based on the given |sample_rate|. Must be used in |
| // conjunction with AUDIO_PCM_LINEAR. |
| size_t GetHighLatencyOutputBufferSize(int sample_rate) { |
| int user_buffer_size = GetUserBufferSize(); |
| if (user_buffer_size) |
| return user_buffer_size; |
| |
| // TODO(vrk/crogers): The buffer sizes that this function computes is probably |
| // overly conservative. However, reducing the buffer size to 2048-8192 bytes |
| // caused crbug.com/108396. This computation should be revisited while making |
| // sure crbug.com/108396 doesn't happen again. |
| |
| // The minimum number of samples in a hardware packet. |
| // This value is selected so that we can handle down to 5khz sample rate. |
| static const size_t kMinSamplesPerHardwarePacket = 1024; |
| |
| // The maximum number of samples in a hardware packet. |
| // This value is selected so that we can handle up to 192khz sample rate. |
| static const size_t kMaxSamplesPerHardwarePacket = 64 * 1024; |
| |
| // This constant governs the hardware audio buffer size, this value should be |
| // chosen carefully. |
| // This value is selected so that we have 8192 samples for 48khz streams. |
| static const size_t kMillisecondsPerHardwarePacket = 170; |
| |
| // Select the number of samples that can provide at least |
| // |kMillisecondsPerHardwarePacket| worth of audio data. |
| size_t samples = kMinSamplesPerHardwarePacket; |
| while (samples <= kMaxSamplesPerHardwarePacket && |
| samples * base::Time::kMillisecondsPerSecond < |
| sample_rate * kMillisecondsPerHardwarePacket) { |
| samples *= 2; |
| } |
| return samples; |
| } |
| |
| #if defined(OS_WIN) |
| |
| int NumberOfWaveOutBuffers() { |
| // Use 4 buffers for Vista, 3 for everyone else: |
| // - The entire Windows audio stack was rewritten for Windows Vista and wave |
| // out performance was degraded compared to XP. |
| // - The regression was fixed in Windows 7 and most configurations will work |
| // with 2, but some (e.g., some Sound Blasters) still need 3. |
| // - Some XP configurations (even multi-processor ones) also need 3. |
| return (base::win::GetVersion() == base::win::VERSION_VISTA) ? 4 : 3; |
| } |
| |
| #endif |
| |
| } // namespace media |