Home | History | Annotate | Download | only in base
      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 
     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 
     43 MultiChannelResampler::~MultiChannelResampler() {}
     44 
     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 
     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 
    101 void MultiChannelResampler::Flush() {
    102   for (size_t i = 0; i < resamplers_.size(); ++i)
    103     resamplers_[i]->Flush();
    104 }
    105 
    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 
    111 }  // namespace media
    112