multi_channel_resampler.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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                                             const ReadCB& read_cb)
17    : last_frame_count_(0),
18      read_cb_(read_cb),
19      output_frames_ready_(0) {
20  // Allocate each channel's resampler.
21  resamplers_.reserve(channels);
22  for (int i = 0; i < channels; ++i) {
23    resamplers_.push_back(new SincResampler(io_sample_rate_ratio, base::Bind(
24        &MultiChannelResampler::ProvideInput, base::Unretained(this), i)));
25  }
26}
27
28MultiChannelResampler::~MultiChannelResampler() {}
29
30void MultiChannelResampler::Resample(AudioBus* audio_bus, int frames) {
31  DCHECK_EQ(static_cast<size_t>(audio_bus->channels()), resamplers_.size());
32
33  // We need to ensure that SincResampler only calls ProvideInput once for each
34  // channel.  To ensure this, we chunk the number of requested frames into
35  // SincResampler::ChunkSize() sized chunks.  SincResampler guarantees it will
36  // only call ProvideInput() once when we resample this way.
37  output_frames_ready_ = 0;
38  int chunk_size = resamplers_[0]->ChunkSize();
39  while (output_frames_ready_ < frames) {
40    int frames_this_time = std::min(frames - output_frames_ready_, chunk_size);
41
42    // Resample each channel.
43    for (size_t i = 0; i < resamplers_.size(); ++i) {
44      DCHECK_EQ(chunk_size, resamplers_[i]->ChunkSize());
45
46      // Depending on the sample-rate scale factor, and the internal buffering
47      // used in a SincResampler kernel, this call to Resample() will only
48      // sometimes call ProvideInput().  However, if it calls ProvideInput() for
49      // the first channel, then it will call it for the remaining channels,
50      // since they all buffer in the same way and are processing the same
51      // number of frames.
52      resamplers_[i]->Resample(
53          audio_bus->channel(i) + output_frames_ready_, frames_this_time);
54    }
55
56    output_frames_ready_ += frames_this_time;
57  }
58}
59
60void MultiChannelResampler::ProvideInput(int channel, float* destination,
61                                         int frames) {
62  // Get the data from the multi-channel provider when the first channel asks
63  // for it.  For subsequent channels, we can just dish out the channel data
64  // from that (stored in |resampler_audio_bus_|).
65  if (channel == 0) {
66    // Allocate staging arrays on the first request and if the frame size or
67    // |destination| changes (should only happen once).
68    if (!resampler_audio_bus_.get() ||
69        resampler_audio_bus_->frames() != frames ||
70        wrapped_resampler_audio_bus_->channel(0) != destination) {
71      resampler_audio_bus_ = AudioBus::Create(resamplers_.size(), frames);
72
73      // Create a channel vector based on |resampler_audio_bus_| but using
74      // |destination| directly for the first channel and then wrap it in a new
75      // AudioBus so we can avoid an extra memcpy later.
76      resampler_audio_data_.clear();
77      resampler_audio_data_.reserve(resampler_audio_bus_->channels());
78      resampler_audio_data_.push_back(destination);
79      for (int i = 1; i < resampler_audio_bus_->channels(); ++i)
80        resampler_audio_data_.push_back(resampler_audio_bus_->channel(i));
81      wrapped_resampler_audio_bus_ = AudioBus::WrapVector(
82          frames, resampler_audio_data_);
83    }
84
85    last_frame_count_ = frames;
86    read_cb_.Run(output_frames_ready_, wrapped_resampler_audio_bus_.get());
87  } else {
88    // All channels must ask for the same amount.  This should always be the
89    // case, but let's just make sure.
90    DCHECK_EQ(frames, last_frame_count_);
91
92    // Copy the channel data from what we received from |read_cb_|.
93    memcpy(destination, resampler_audio_bus_->channel(channel),
94           sizeof(*resampler_audio_bus_->channel(channel)) * frames);
95  }
96}
97
98void MultiChannelResampler::Flush() {
99  last_frame_count_ = 0;
100  for (size_t i = 0; i < resamplers_.size(); ++i)
101    resamplers_[i]->Flush();
102}
103
104}  // namespace media
105