// 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 "build/build_config.h"
#include "ppapi/shared_impl/ppb_audio_config_shared.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<uint32_t>(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<PPB_AudioConfig_Shared> 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<PP_AudioSampleRate>(
      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<PP_AudioSampleRate>(
      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
