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