// 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. #include "ppapi/shared_impl/ppb_audio_config_shared.h" #include #include "build/build_config.h" #include "ppapi/thunk/enter.h" #include "ppapi/thunk/ppb_instance_api.h" namespace ppapi { // Rounds up requested_size to the nearest multiple of minimum_size. static uint32_t CalculateMultipleOfSampleFrameCount(uint32_t minimum_size, uint32_t requested_size) { const uint32_t multiple = (requested_size + minimum_size - 1) / minimum_size; return std::min(minimum_size * multiple, static_cast(PP_AUDIOMAXSAMPLEFRAMECOUNT)); } PPB_AudioConfig_Shared::PPB_AudioConfig_Shared(ResourceObjectType type, PP_Instance instance) : Resource(type, instance), sample_rate_(PP_AUDIOSAMPLERATE_NONE), sample_frame_count_(0) {} PPB_AudioConfig_Shared::~PPB_AudioConfig_Shared() {} PP_Resource PPB_AudioConfig_Shared::Create(ResourceObjectType type, PP_Instance instance, PP_AudioSampleRate sample_rate, uint32_t sample_frame_count) { scoped_refptr object( new PPB_AudioConfig_Shared(type, instance)); if (!object->Init(sample_rate, sample_frame_count)) return 0; return object->GetReference(); } // static uint32_t PPB_AudioConfig_Shared::RecommendSampleFrameCount_1_0( PP_AudioSampleRate sample_rate, uint32_t requested_sample_frame_count) { // Version 1.0: Don't actually query to get a value from the // hardware; instead return the input for in-range values. if (requested_sample_frame_count < PP_AUDIOMINSAMPLEFRAMECOUNT) return PP_AUDIOMINSAMPLEFRAMECOUNT; if (requested_sample_frame_count > PP_AUDIOMAXSAMPLEFRAMECOUNT) return PP_AUDIOMAXSAMPLEFRAMECOUNT; return requested_sample_frame_count; } // static uint32_t PPB_AudioConfig_Shared::RecommendSampleFrameCount_1_1( PP_Instance instance, PP_AudioSampleRate sample_rate, uint32_t sample_frame_count) { // Version 1.1: Query the back-end hardware for sample rate and buffer size, // and recommend a best fit based on request. thunk::EnterInstanceNoLock enter(instance); if (enter.failed()) return 0; // Get the hardware config. PP_AudioSampleRate hardware_sample_rate = static_cast( enter.functions()->GetAudioHardwareOutputSampleRate(instance)); uint32_t hardware_sample_frame_count = enter.functions()->GetAudioHardwareOutputBufferSize(instance); if (sample_frame_count < PP_AUDIOMINSAMPLEFRAMECOUNT) sample_frame_count = PP_AUDIOMINSAMPLEFRAMECOUNT; // If hardware information isn't available we're connected to a fake audio // output stream on the browser side, so we can use whatever sample count the // client wants. if (!hardware_sample_frame_count || !hardware_sample_rate) return sample_frame_count; // Note: All the values below were determined through experimentation to // minimize jitter and back-to-back callbacks from the browser. Please take // care when modifying these values as they impact a large number of users. // TODO(dalecurtis): Land jitter test and add documentation for updating this. // Should track the value reported by XP and ALSA backends. const uint32_t kHighLatencySampleFrameCount = 2048; #if defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY) // TODO(ihf): Remove this once ARM Chromebooks support low latency audio. For // now we classify them as high latency. See crbug.com/289770. Note that // Adobe Flash is affected but not HTML5, WebRTC and WebAudio (they are using // real time threads). const bool kHighLatencyDevice = true; #else const bool kHighLatencyDevice = false; #endif // If client is using same sample rate as audio hardware, then recommend a // multiple of the audio hardware's sample frame count. if (!kHighLatencyDevice && hardware_sample_rate == sample_rate) { return CalculateMultipleOfSampleFrameCount(hardware_sample_frame_count, sample_frame_count); } // If the hardware requires a high latency buffer or we're at a low sample // rate w/ a buffer that's larger than 10ms, choose the nearest multiple of // the high latency sample frame count. An example of too low and too large // is 16kHz and a sample frame count greater than 160 frames. if (kHighLatencyDevice || hardware_sample_frame_count >= kHighLatencySampleFrameCount || (hardware_sample_rate < 44100 && hardware_sample_frame_count > hardware_sample_rate / 100u)) { return CalculateMultipleOfSampleFrameCount( sample_frame_count, std::max(kHighLatencySampleFrameCount, hardware_sample_frame_count)); } // All low latency clients should be able to handle a 512 frame buffer with // resampling from 44.1kHz and 48kHz to higher sample rates. // TODO(dalecurtis): We may need to investigate making the callback thread // high priority to handle buffers at the absolute minimum w/o glitching. const uint32_t kLowLatencySampleFrameCount = 512; // Special case for 48kHz -> 44.1kHz and buffer sizes greater than 10ms. In // testing most buffer sizes > 10ms led to glitching, so we choose a size we // know won't cause jitter. int min_sample_frame_count = kLowLatencySampleFrameCount; if (hardware_sample_rate == 44100 && sample_rate == 48000 && hardware_sample_frame_count > hardware_sample_rate / 100u) { min_sample_frame_count = std::max(2 * kLowLatencySampleFrameCount, hardware_sample_frame_count); } return CalculateMultipleOfSampleFrameCount(min_sample_frame_count, sample_frame_count); } // static PP_AudioSampleRate PPB_AudioConfig_Shared::RecommendSampleRate( PP_Instance instance) { thunk::EnterInstanceNoLock enter(instance); if (enter.failed()) return PP_AUDIOSAMPLERATE_NONE; PP_AudioSampleRate hardware_sample_rate = static_cast( enter.functions()->GetAudioHardwareOutputSampleRate(instance)); return hardware_sample_rate; } thunk::PPB_AudioConfig_API* PPB_AudioConfig_Shared::AsPPB_AudioConfig_API() { return this; } PP_AudioSampleRate PPB_AudioConfig_Shared::GetSampleRate() { return sample_rate_; } uint32_t PPB_AudioConfig_Shared::GetSampleFrameCount() { return sample_frame_count_; } bool PPB_AudioConfig_Shared::Init(PP_AudioSampleRate sample_rate, uint32_t sample_frame_count) { // TODO(brettw): Currently we don't actually check what the hardware // supports, so just allow sample rates of the "guaranteed working" ones. // TODO(dalecurtis): If sample rates are added RecommendSampleFrameCount_1_1() // must be updated to account for the new rates. if (sample_rate != PP_AUDIOSAMPLERATE_44100 && sample_rate != PP_AUDIOSAMPLERATE_48000) return false; // TODO(brettw): Currently we don't actually query to get a value from the // hardware, so just validate the range. if (sample_frame_count > PP_AUDIOMAXSAMPLEFRAMECOUNT || sample_frame_count < PP_AUDIOMINSAMPLEFRAMECOUNT) return false; sample_rate_ = sample_rate; sample_frame_count_ = sample_frame_count; return true; } } // namespace ppapi