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