• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
12 namespace media {
13 
MultiChannelResampler(int channels,double io_sample_rate_ratio,size_t request_size,const ReadCB & read_cb)14 MultiChannelResampler::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 
~MultiChannelResampler()43 MultiChannelResampler::~MultiChannelResampler() {}
44 
Resample(int frames,AudioBus * audio_bus)45 void 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 
ProvideInput(int channel,int frames,float * destination)81 void 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 
Flush()101 void MultiChannelResampler::Flush() {
102   for (size_t i = 0; i < resamplers_.size(); ++i)
103     resamplers_[i]->Flush();
104 }
105 
SetRatio(double io_sample_rate_ratio)106 void 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 
ChunkSize() const111 int MultiChannelResampler::ChunkSize() const {
112   DCHECK(!resamplers_.empty());
113   return resamplers_[0]->ChunkSize();
114 }
115 
116 }  // namespace media
117