1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "media/base/multi_channel_resampler.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/logging.h"
10#include "media/base/audio_bus.h"
11
12namespace media {
13
14MultiChannelResampler::MultiChannelResampler(int channels,
15                                             double io_sample_rate_ratio,
16                                             size_t request_size,
17                                             const ReadCB& read_cb)
18    : read_cb_(read_cb),
19      wrapped_resampler_audio_bus_(AudioBus::CreateWrapper(channels)),
20      output_frames_ready_(0) {
21  // Allocate each channel's resampler.
22  resamplers_.reserve(channels);
23  for (int i = 0; i < channels; ++i) {
24    resamplers_.push_back(new SincResampler(
25        io_sample_rate_ratio, request_size, base::Bind(
26            &MultiChannelResampler::ProvideInput, base::Unretained(this), i)));
27  }
28
29  // Setup the wrapped AudioBus for channel data.
30  wrapped_resampler_audio_bus_->set_frames(request_size);
31
32  // Allocate storage for all channels except the first, which will use the
33  // |destination| provided to ProvideInput() directly.
34  if (channels > 1) {
35    resampler_audio_bus_ = AudioBus::Create(channels - 1, request_size);
36    for (int i = 0; i < resampler_audio_bus_->channels(); ++i) {
37      wrapped_resampler_audio_bus_->SetChannelData(
38          i + 1, resampler_audio_bus_->channel(i));
39    }
40  }
41}
42
43MultiChannelResampler::~MultiChannelResampler() {}
44
45void MultiChannelResampler::Resample(int frames, AudioBus* audio_bus) {
46  DCHECK_EQ(static_cast<size_t>(audio_bus->channels()), resamplers_.size());
47
48  // Optimize the single channel case to avoid the chunking process below.
49  if (audio_bus->channels() == 1) {
50    resamplers_[0]->Resample(frames, audio_bus->channel(0));
51    return;
52  }
53
54  // We need to ensure that SincResampler only calls ProvideInput once for each
55  // channel.  To ensure this, we chunk the number of requested frames into
56  // SincResampler::ChunkSize() sized chunks.  SincResampler guarantees it will
57  // only call ProvideInput() once when we resample this way.
58  output_frames_ready_ = 0;
59  while (output_frames_ready_ < frames) {
60    int chunk_size = resamplers_[0]->ChunkSize();
61    int frames_this_time = std::min(frames - output_frames_ready_, chunk_size);
62
63    // Resample each channel.
64    for (size_t i = 0; i < resamplers_.size(); ++i) {
65      DCHECK_EQ(chunk_size, resamplers_[i]->ChunkSize());
66
67      // Depending on the sample-rate scale factor, and the internal buffering
68      // used in a SincResampler kernel, this call to Resample() will only
69      // sometimes call ProvideInput().  However, if it calls ProvideInput() for
70      // the first channel, then it will call it for the remaining channels,
71      // since they all buffer in the same way and are processing the same
72      // number of frames.
73      resamplers_[i]->Resample(
74          frames_this_time, audio_bus->channel(i) + output_frames_ready_);
75    }
76
77    output_frames_ready_ += frames_this_time;
78  }
79}
80
81void MultiChannelResampler::ProvideInput(int channel,
82                                         int frames,
83                                         float* destination) {
84  // Get the data from the multi-channel provider when the first channel asks
85  // for it.  For subsequent channels, we can just dish out the channel data
86  // from that (stored in |resampler_audio_bus_|).
87  if (channel == 0) {
88    wrapped_resampler_audio_bus_->SetChannelData(0, destination);
89    read_cb_.Run(output_frames_ready_, wrapped_resampler_audio_bus_.get());
90  } else {
91    // All channels must ask for the same amount.  This should always be the
92    // case, but let's just make sure.
93    DCHECK_EQ(frames, wrapped_resampler_audio_bus_->frames());
94
95    // Copy the channel data from what we received from |read_cb_|.
96    memcpy(destination, wrapped_resampler_audio_bus_->channel(channel),
97           sizeof(*wrapped_resampler_audio_bus_->channel(channel)) * frames);
98  }
99}
100
101void MultiChannelResampler::Flush() {
102  for (size_t i = 0; i < resamplers_.size(); ++i)
103    resamplers_[i]->Flush();
104}
105
106void MultiChannelResampler::SetRatio(double io_sample_rate_ratio) {
107  for (size_t i = 0; i < resamplers_.size(); ++i)
108    resamplers_[i]->SetRatio(io_sample_rate_ratio);
109}
110
111int MultiChannelResampler::ChunkSize() const {
112  DCHECK(!resamplers_.empty());
113  return resamplers_[0]->ChunkSize();
114}
115
116}  // namespace media
117