Make ChannelBuffer aware of frequency bands
Now the ChannelBuffer has 2 separate arrays, one for the full-band data and one for the splitted one. The corresponding accessors are added to the ChannelBuffer. This is done to avoid having to refresh the bands pointers in AudioBuffer. It will also allow us to have a general accessor like data()[band][channel][sample]. All the files using the ChannelBuffer needed to be re-factored. Tested with modules_unittests, common_audio_unittests, audioproc, audioproc_f, voe_cmd_test. R=andrew@webrtc.org, kwiberg@webrtc.org Review URL: https://webrtc-codereview.appspot.com/36999004 Cr-Commit-Position: refs/heads/master@{#8318} git-svn-id: http://webrtc.googlecode.com/svn/trunk@8318 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
d7472b52d6
commit
d35a5c3506
@ -89,7 +89,7 @@ void AudioConverter::Convert(const float* const* src,
|
||||
if (src_frames != dst_frames) {
|
||||
// Downmix to a buffer for subsequent resampling.
|
||||
DCHECK_EQ(downmix_buffer_->num_channels(), dst_channels);
|
||||
DCHECK_EQ(downmix_buffer_->samples_per_channel(), src_frames);
|
||||
DCHECK_EQ(downmix_buffer_->num_frames(), src_frames);
|
||||
dst_ptr = downmix_buffer_->channels();
|
||||
}
|
||||
|
||||
|
@ -28,14 +28,14 @@ ScopedBuffer CreateBuffer(const std::vector<float>& data, int frames) {
|
||||
ScopedBuffer sb(new ChannelBuffer<float>(frames, num_channels));
|
||||
for (int i = 0; i < num_channels; ++i)
|
||||
for (int j = 0; j < frames; ++j)
|
||||
sb->channel(i)[j] = data[i] * j;
|
||||
sb->channels()[i][j] = data[i] * j;
|
||||
return sb;
|
||||
}
|
||||
|
||||
void VerifyParams(const ChannelBuffer<float>& ref,
|
||||
const ChannelBuffer<float>& test) {
|
||||
EXPECT_EQ(ref.num_channels(), test.num_channels());
|
||||
EXPECT_EQ(ref.samples_per_channel(), test.samples_per_channel());
|
||||
EXPECT_EQ(ref.num_frames(), test.num_frames());
|
||||
}
|
||||
|
||||
// Computes the best SNR based on the error between |ref_frame| and
|
||||
@ -50,20 +50,20 @@ float ComputeSNR(const ChannelBuffer<float>& ref,
|
||||
|
||||
// Search within one sample of the expected delay.
|
||||
for (int delay = std::max(expected_delay - 1, 0);
|
||||
delay <= std::min(expected_delay + 1, ref.samples_per_channel());
|
||||
delay <= std::min(expected_delay + 1, ref.num_frames());
|
||||
++delay) {
|
||||
float mse = 0;
|
||||
float variance = 0;
|
||||
float mean = 0;
|
||||
for (int i = 0; i < ref.num_channels(); ++i) {
|
||||
for (int j = 0; j < ref.samples_per_channel() - delay; ++j) {
|
||||
float error = ref.channel(i)[j] - test.channel(i)[j + delay];
|
||||
for (int j = 0; j < ref.num_frames() - delay; ++j) {
|
||||
float error = ref.channels()[i][j] - test.channels()[i][j + delay];
|
||||
mse += error * error;
|
||||
variance += ref.channel(i)[j] * ref.channel(i)[j];
|
||||
mean += ref.channel(i)[j];
|
||||
variance += ref.channels()[i][j] * ref.channels()[i][j];
|
||||
mean += ref.channels()[i][j];
|
||||
}
|
||||
}
|
||||
const int length = ref.num_channels() * (ref.samples_per_channel() - delay);
|
||||
const int length = ref.num_channels() * (ref.num_frames() - delay);
|
||||
mse /= length;
|
||||
variance /= length;
|
||||
mean /= length;
|
||||
|
@ -25,7 +25,7 @@ void ReadAndWriteTest(const ChannelBuffer<float>& input,
|
||||
size_t buffer_frames,
|
||||
ChannelBuffer<float>* output) {
|
||||
const size_t num_channels = input.num_channels();
|
||||
const size_t total_frames = input.samples_per_channel();
|
||||
const size_t total_frames = input.num_frames();
|
||||
AudioRingBuffer buf(num_channels, buffer_frames);
|
||||
scoped_ptr<float*[]> slice(new float*[num_channels]);
|
||||
|
||||
@ -91,17 +91,18 @@ TEST_F(AudioRingBufferTest, MoveReadPosition) {
|
||||
const size_t kNumChannels = 1;
|
||||
const float kInputArray[] = {1, 2, 3, 4};
|
||||
const size_t kNumFrames = sizeof(kInputArray) / sizeof(*kInputArray);
|
||||
ChannelBuffer<float> input(kInputArray, kNumFrames, kNumChannels);
|
||||
ChannelBuffer<float> input(kNumFrames, kNumChannels);
|
||||
input.SetDataForTesting(kInputArray, kNumFrames);
|
||||
AudioRingBuffer buf(kNumChannels, kNumFrames);
|
||||
buf.Write(input.channels(), kNumChannels, kNumFrames);
|
||||
|
||||
buf.MoveReadPosition(3);
|
||||
ChannelBuffer<float> output(1, kNumChannels);
|
||||
buf.Read(output.channels(), kNumChannels, 1);
|
||||
EXPECT_EQ(4, output.data()[0]);
|
||||
EXPECT_EQ(4, output.channels()[0][0]);
|
||||
buf.MoveReadPosition(-3);
|
||||
buf.Read(output.channels(), kNumChannels, 1);
|
||||
EXPECT_EQ(2, output.data()[0]);
|
||||
EXPECT_EQ(2, output.channels()[0][0]);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -132,13 +132,15 @@ TEST_F(BlockerTest, TestBlockerMutuallyPrimeChunkandBlockSize) {
|
||||
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
|
||||
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
|
||||
{3, 3, 3, 3, 3, 3, 3, 3, 3, 3}};
|
||||
const ChannelBuffer<float> input_cb(kInput[0], kNumFrames, kNumInputChannels);
|
||||
ChannelBuffer<float> input_cb(kNumFrames, kNumInputChannels);
|
||||
input_cb.SetDataForTesting(kInput[0], sizeof(kInput) / sizeof(**kInput));
|
||||
|
||||
const float kExpectedOutput[kNumInputChannels][kNumFrames] = {
|
||||
{6, 6, 12, 20, 20, 20, 20, 20, 20, 20},
|
||||
{6, 6, 12, 28, 28, 28, 28, 28, 28, 28}};
|
||||
const ChannelBuffer<float> expected_output_cb(
|
||||
kExpectedOutput[0], kNumFrames, kNumInputChannels);
|
||||
ChannelBuffer<float> expected_output_cb(kNumFrames, kNumInputChannels);
|
||||
expected_output_cb.SetDataForTesting(
|
||||
kExpectedOutput[0], sizeof(kExpectedOutput) / sizeof(**kExpectedOutput));
|
||||
|
||||
const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f};
|
||||
|
||||
@ -183,13 +185,15 @@ TEST_F(BlockerTest, TestBlockerMutuallyPrimeShiftAndBlockSize) {
|
||||
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
|
||||
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
|
||||
{3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}};
|
||||
const ChannelBuffer<float> input_cb(kInput[0], kNumFrames, kNumInputChannels);
|
||||
ChannelBuffer<float> input_cb(kNumFrames, kNumInputChannels);
|
||||
input_cb.SetDataForTesting(kInput[0], sizeof(kInput) / sizeof(**kInput));
|
||||
|
||||
const float kExpectedOutput[kNumOutputChannels][kNumFrames] = {
|
||||
{6, 10, 10, 20, 10, 10, 20, 10, 10, 20, 10, 10},
|
||||
{6, 14, 14, 28, 14, 14, 28, 14, 14, 28, 14, 14}};
|
||||
const ChannelBuffer<float> expected_output_cb(
|
||||
kExpectedOutput[0], kNumFrames, kNumOutputChannels);
|
||||
ChannelBuffer<float> expected_output_cb(kNumFrames, kNumOutputChannels);
|
||||
expected_output_cb.SetDataForTesting(
|
||||
kExpectedOutput[0], sizeof(kExpectedOutput) / sizeof(**kExpectedOutput));
|
||||
|
||||
const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f};
|
||||
|
||||
@ -234,13 +238,15 @@ TEST_F(BlockerTest, TestBlockerNoOverlap) {
|
||||
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
|
||||
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
|
||||
{3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}};
|
||||
const ChannelBuffer<float> input_cb(kInput[0], kNumFrames, kNumInputChannels);
|
||||
ChannelBuffer<float> input_cb(kNumFrames, kNumInputChannels);
|
||||
input_cb.SetDataForTesting(kInput[0], sizeof(kInput) / sizeof(**kInput));
|
||||
|
||||
const float kExpectedOutput[kNumOutputChannels][kNumFrames] = {
|
||||
{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
|
||||
{14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}};
|
||||
const ChannelBuffer<float> expected_output_cb(
|
||||
kExpectedOutput[0], kNumFrames, kNumOutputChannels);
|
||||
ChannelBuffer<float> expected_output_cb(kNumFrames, kNumOutputChannels);
|
||||
expected_output_cb.SetDataForTesting(
|
||||
kExpectedOutput[0], sizeof(kExpectedOutput) / sizeof(**kExpectedOutput));
|
||||
|
||||
const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f};
|
||||
|
||||
@ -292,7 +298,8 @@ TEST_F(BlockerTest, InitialDelaysAreMinimum) {
|
||||
input[i][j] = i + 1;
|
||||
}
|
||||
}
|
||||
const ChannelBuffer<float> input_cb(input[0], kNumFrames, kNumInputChannels);
|
||||
ChannelBuffer<float> input_cb(kNumFrames, kNumInputChannels);
|
||||
input_cb.SetDataForTesting(input[0], sizeof(input) / sizeof(**input));
|
||||
|
||||
ChannelBuffer<float> output_cb(kNumFrames, kNumOutputChannels);
|
||||
|
||||
|
@ -12,11 +12,13 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
IFChannelBuffer::IFChannelBuffer(int samples_per_channel, int num_channels)
|
||||
IFChannelBuffer::IFChannelBuffer(int num_frames,
|
||||
int num_channels,
|
||||
int num_bands)
|
||||
: ivalid_(true),
|
||||
ibuf_(samples_per_channel, num_channels),
|
||||
ibuf_(num_frames, num_channels, num_bands),
|
||||
fvalid_(true),
|
||||
fbuf_(samples_per_channel, num_channels) {}
|
||||
fbuf_(num_frames, num_channels, num_bands) {}
|
||||
|
||||
ChannelBuffer<int16_t>* IFChannelBuffer::ibuf() {
|
||||
RefreshI();
|
||||
@ -43,11 +45,13 @@ const ChannelBuffer<float>* IFChannelBuffer::fbuf_const() const {
|
||||
void IFChannelBuffer::RefreshF() const {
|
||||
if (!fvalid_) {
|
||||
assert(ivalid_);
|
||||
const int16_t* const int_data = ibuf_.data();
|
||||
float* const float_data = fbuf_.data();
|
||||
const int length = fbuf_.length();
|
||||
for (int i = 0; i < length; ++i)
|
||||
float_data[i] = int_data[i];
|
||||
const int16_t* const* int_channels = ibuf_.channels();
|
||||
float* const* float_channels = fbuf_.channels();
|
||||
for (int i = 0; i < ibuf_.num_channels(); ++i) {
|
||||
for (int j = 0; j < ibuf_.num_frames(); ++j) {
|
||||
float_channels[i][j] = int_channels[i][j];
|
||||
}
|
||||
}
|
||||
fvalid_ = true;
|
||||
}
|
||||
}
|
||||
@ -55,7 +59,13 @@ void IFChannelBuffer::RefreshF() const {
|
||||
void IFChannelBuffer::RefreshI() const {
|
||||
if (!ivalid_) {
|
||||
assert(fvalid_);
|
||||
FloatS16ToS16(fbuf_.data(), ibuf_.length(), ibuf_.data());
|
||||
int16_t* const* int_channels = ibuf_.channels();
|
||||
const float* const* float_channels = fbuf_.channels();
|
||||
for (int i = 0; i < ibuf_.num_channels(); ++i) {
|
||||
FloatS16ToS16(float_channels[i],
|
||||
ibuf_.num_frames(),
|
||||
int_channels[i]);
|
||||
}
|
||||
ivalid_ = true;
|
||||
}
|
||||
}
|
||||
|
@ -15,70 +15,96 @@
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/common_audio/include/audio_util.h"
|
||||
#include "webrtc/test/testsupport/gtest_prod_util.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Helper to encapsulate a contiguous data buffer with access to a pointer
|
||||
// array of the deinterleaved channels. The buffer is zero initialized at
|
||||
// creation.
|
||||
// Helper to encapsulate a contiguous data buffer, full or split into frequency
|
||||
// bands, with access to a pointer arrays of the deinterleaved channels and
|
||||
// bands. The buffer is zero initialized at creation.
|
||||
//
|
||||
// The buffer structure is showed below for a 2 channel and 2 bands case:
|
||||
//
|
||||
// |data_|:
|
||||
// { [ --- b1ch1 --- ] [ --- b2ch1 --- ] [ --- b1ch2 --- ] [ --- b2ch2 --- ] }
|
||||
//
|
||||
// The pointer arrays for the same example are as follows:
|
||||
//
|
||||
// |channels_|:
|
||||
// { [ b1ch1* ] [ b1ch2* ] [ b2ch1* ] [ b2ch2* ] }
|
||||
//
|
||||
// |bands_|:
|
||||
// { [ b1ch1* ] [ b2ch1* ] [ b1ch2* ] [ b2ch2* ] }
|
||||
template <typename T>
|
||||
class ChannelBuffer {
|
||||
public:
|
||||
ChannelBuffer(int samples_per_channel, int num_channels)
|
||||
: data_(new T[samples_per_channel * num_channels]),
|
||||
channels_(new T*[num_channels]),
|
||||
samples_per_channel_(samples_per_channel),
|
||||
num_channels_(num_channels) {
|
||||
Initialize();
|
||||
ChannelBuffer(int num_frames,
|
||||
int num_channels,
|
||||
int num_bands = 1)
|
||||
: data_(new T[num_frames * num_channels]),
|
||||
channels_(new T*[num_channels * num_bands]),
|
||||
bands_(new T*[num_channels * num_bands]),
|
||||
num_frames_(num_frames),
|
||||
num_frames_per_band_(num_frames / num_bands),
|
||||
num_channels_(num_channels),
|
||||
num_bands_(num_bands) {
|
||||
memset(data_.get(), 0, size() * sizeof(T));
|
||||
for (int i = 0; i < num_channels_; ++i) {
|
||||
for (int j = 0; j < num_bands_; ++j) {
|
||||
channels_[j * num_channels_ + i] =
|
||||
&data_[i * num_frames_ + j * num_frames_per_band_];
|
||||
bands_[i * num_bands_ + j] = channels_[j * num_channels_ + i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ChannelBuffer(const T* data, int samples_per_channel, int num_channels)
|
||||
: data_(new T[samples_per_channel * num_channels]),
|
||||
channels_(new T*[num_channels]),
|
||||
samples_per_channel_(samples_per_channel),
|
||||
num_channels_(num_channels) {
|
||||
Initialize();
|
||||
memcpy(data_.get(), data, length() * sizeof(T));
|
||||
// Returns a pointer array to the full-band channels (or lower band channels).
|
||||
// Usage:
|
||||
// channels()[channel][sample].
|
||||
// Where:
|
||||
// 0 <= channel < |num_channels_|
|
||||
// 0 <= sample < |num_frames_|
|
||||
T* const* channels() { return channels(0); }
|
||||
const T* const* channels() const { return channels(0); }
|
||||
|
||||
// Returns a pointer array to the channels for a specific band.
|
||||
// Usage:
|
||||
// channels(band)[channel][sample].
|
||||
// Where:
|
||||
// 0 <= band < |num_bands_|
|
||||
// 0 <= channel < |num_channels_|
|
||||
// 0 <= sample < |num_frames_per_band_|
|
||||
const T* const* channels(int band) const {
|
||||
DCHECK_LT(band, num_bands_);
|
||||
DCHECK_GE(band, 0);
|
||||
return &channels_[band * num_channels_];
|
||||
}
|
||||
|
||||
ChannelBuffer(const T* const* channels, int samples_per_channel,
|
||||
int num_channels)
|
||||
: data_(new T[samples_per_channel * num_channels]),
|
||||
channels_(new T*[num_channels]),
|
||||
samples_per_channel_(samples_per_channel),
|
||||
num_channels_(num_channels) {
|
||||
Initialize();
|
||||
for (int i = 0; i < num_channels_; ++i)
|
||||
CopyFrom(channels[i], i);
|
||||
}
|
||||
|
||||
~ChannelBuffer() {}
|
||||
|
||||
void CopyFrom(const void* channel_ptr, int i) {
|
||||
DCHECK_LT(i, num_channels_);
|
||||
memcpy(channels_[i], channel_ptr, samples_per_channel_ * sizeof(T));
|
||||
}
|
||||
|
||||
T* data() { return data_.get(); }
|
||||
const T* data() const { return data_.get(); }
|
||||
|
||||
const T* channel(int i) const {
|
||||
DCHECK_GE(i, 0);
|
||||
DCHECK_LT(i, num_channels_);
|
||||
return channels_[i];
|
||||
}
|
||||
T* channel(int i) {
|
||||
T* const* channels(int band) {
|
||||
const ChannelBuffer<T>* t = this;
|
||||
return const_cast<T*>(t->channel(i));
|
||||
return const_cast<T* const*>(t->channels(band));
|
||||
}
|
||||
|
||||
T* const* channels() { return channels_.get(); }
|
||||
const T* const* channels() const { return channels_.get(); }
|
||||
// Returns a pointer array to the bands for a specific channel.
|
||||
// Usage:
|
||||
// bands(channel)[band][sample].
|
||||
// Where:
|
||||
// 0 <= channel < |num_channels_|
|
||||
// 0 <= band < |num_bands_|
|
||||
// 0 <= sample < |num_frames_per_band_|
|
||||
const T* const* bands(int channel) const {
|
||||
DCHECK_LT(channel, num_channels_);
|
||||
DCHECK_GE(channel, 0);
|
||||
return &bands_[channel * num_bands_];
|
||||
}
|
||||
T* const* bands(int channel) {
|
||||
const ChannelBuffer<T>* t = this;
|
||||
return const_cast<T* const*>(t->bands(channel));
|
||||
}
|
||||
|
||||
// Sets the |slice| pointers to the |start_frame| position for each channel.
|
||||
// Returns |slice| for convenience.
|
||||
const T* const* Slice(T** slice, int start_frame) const {
|
||||
DCHECK_LT(start_frame, samples_per_channel_);
|
||||
DCHECK_LT(start_frame, num_frames_);
|
||||
for (int i = 0; i < num_channels_; ++i)
|
||||
slice[i] = &channels_[i][start_frame];
|
||||
return slice;
|
||||
@ -88,21 +114,25 @@ class ChannelBuffer {
|
||||
return const_cast<T**>(t->Slice(slice, start_frame));
|
||||
}
|
||||
|
||||
int samples_per_channel() const { return samples_per_channel_; }
|
||||
int num_frames() const { return num_frames_; }
|
||||
int num_frames_per_band() const { return num_frames_per_band_; }
|
||||
int num_channels() const { return num_channels_; }
|
||||
int length() const { return samples_per_channel_ * num_channels_; }
|
||||
int num_bands() const { return num_bands_; }
|
||||
size_t size() const {return num_frames_ * num_channels_; }
|
||||
|
||||
private:
|
||||
void Initialize() {
|
||||
memset(data_.get(), 0, sizeof(T) * length());
|
||||
for (int i = 0; i < num_channels_; ++i)
|
||||
channels_[i] = &data_[i * samples_per_channel_];
|
||||
void SetDataForTesting(const T* data, size_t size) {
|
||||
CHECK_EQ(size, this->size());
|
||||
memcpy(data_.get(), data, size * sizeof(*data));
|
||||
}
|
||||
|
||||
private:
|
||||
scoped_ptr<T[]> data_;
|
||||
scoped_ptr<T*[]> channels_;
|
||||
const int samples_per_channel_;
|
||||
scoped_ptr<T*[]> bands_;
|
||||
const int num_frames_;
|
||||
const int num_frames_per_band_;
|
||||
const int num_channels_;
|
||||
const int num_bands_;
|
||||
};
|
||||
|
||||
// One int16_t and one float ChannelBuffer that are kept in sync. The sync is
|
||||
@ -113,15 +143,17 @@ class ChannelBuffer {
|
||||
// fbuf() until the next call to any of the other functions.
|
||||
class IFChannelBuffer {
|
||||
public:
|
||||
IFChannelBuffer(int samples_per_channel, int num_channels);
|
||||
IFChannelBuffer(int num_frames, int num_channels, int num_bands = 1);
|
||||
|
||||
ChannelBuffer<int16_t>* ibuf();
|
||||
ChannelBuffer<float>* fbuf();
|
||||
const ChannelBuffer<int16_t>* ibuf_const() const;
|
||||
const ChannelBuffer<float>* fbuf_const() const;
|
||||
|
||||
int num_frames() const { return ibuf_.num_frames(); }
|
||||
int num_frames_per_band() const { return ibuf_.num_frames_per_band(); }
|
||||
int num_channels() const { return ibuf_.num_channels(); }
|
||||
int samples_per_channel() const { return ibuf_.samples_per_channel(); }
|
||||
int num_bands() const { return ibuf_.num_bands(); }
|
||||
|
||||
private:
|
||||
void RefreshF() const;
|
||||
|
@ -48,92 +48,90 @@ int KeyboardChannelIndex(AudioProcessing::ChannelLayout layout) {
|
||||
|
||||
template <typename T>
|
||||
void StereoToMono(const T* left, const T* right, T* out,
|
||||
int samples_per_channel) {
|
||||
for (int i = 0; i < samples_per_channel; ++i)
|
||||
int num_frames) {
|
||||
for (int i = 0; i < num_frames; ++i)
|
||||
out[i] = (left[i] + right[i]) / 2;
|
||||
}
|
||||
|
||||
int NumBandsFromSamplesPerChannel(int num_frames) {
|
||||
int num_bands = 1;
|
||||
if (num_frames == kSamplesPer32kHzChannel ||
|
||||
num_frames == kSamplesPer48kHzChannel) {
|
||||
num_bands = rtc::CheckedDivExact(num_frames,
|
||||
static_cast<int>(kSamplesPer16kHzChannel));
|
||||
}
|
||||
return num_bands;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AudioBuffer::AudioBuffer(int input_samples_per_channel,
|
||||
AudioBuffer::AudioBuffer(int input_num_frames,
|
||||
int num_input_channels,
|
||||
int process_samples_per_channel,
|
||||
int process_num_frames,
|
||||
int num_process_channels,
|
||||
int output_samples_per_channel)
|
||||
: input_samples_per_channel_(input_samples_per_channel),
|
||||
int output_num_frames)
|
||||
: input_num_frames_(input_num_frames),
|
||||
num_input_channels_(num_input_channels),
|
||||
proc_samples_per_channel_(process_samples_per_channel),
|
||||
proc_num_frames_(process_num_frames),
|
||||
num_proc_channels_(num_process_channels),
|
||||
output_samples_per_channel_(output_samples_per_channel),
|
||||
output_num_frames_(output_num_frames),
|
||||
num_channels_(num_process_channels),
|
||||
num_bands_(1),
|
||||
samples_per_split_channel_(proc_samples_per_channel_),
|
||||
num_bands_(NumBandsFromSamplesPerChannel(proc_num_frames_)),
|
||||
num_split_frames_(rtc::CheckedDivExact(
|
||||
proc_num_frames_, num_bands_)),
|
||||
mixed_low_pass_valid_(false),
|
||||
reference_copied_(false),
|
||||
activity_(AudioFrame::kVadUnknown),
|
||||
keyboard_data_(NULL),
|
||||
channels_(new IFChannelBuffer(proc_samples_per_channel_,
|
||||
num_proc_channels_)) {
|
||||
assert(input_samples_per_channel_ > 0);
|
||||
assert(proc_samples_per_channel_ > 0);
|
||||
assert(output_samples_per_channel_ > 0);
|
||||
data_(new IFChannelBuffer(proc_num_frames_, num_proc_channels_)) {
|
||||
assert(input_num_frames_ > 0);
|
||||
assert(proc_num_frames_ > 0);
|
||||
assert(output_num_frames_ > 0);
|
||||
assert(num_input_channels_ > 0 && num_input_channels_ <= 2);
|
||||
assert(num_proc_channels_ <= num_input_channels_);
|
||||
assert(num_proc_channels_ > 0 && num_proc_channels_ <= num_input_channels_);
|
||||
|
||||
if (num_input_channels_ == 2 && num_proc_channels_ == 1) {
|
||||
input_buffer_.reset(new ChannelBuffer<float>(input_samples_per_channel_,
|
||||
input_buffer_.reset(new ChannelBuffer<float>(input_num_frames_,
|
||||
num_proc_channels_));
|
||||
}
|
||||
|
||||
if (input_samples_per_channel_ != proc_samples_per_channel_ ||
|
||||
output_samples_per_channel_ != proc_samples_per_channel_) {
|
||||
if (input_num_frames_ != proc_num_frames_ ||
|
||||
output_num_frames_ != proc_num_frames_) {
|
||||
// Create an intermediate buffer for resampling.
|
||||
process_buffer_.reset(new ChannelBuffer<float>(proc_samples_per_channel_,
|
||||
process_buffer_.reset(new ChannelBuffer<float>(proc_num_frames_,
|
||||
num_proc_channels_));
|
||||
}
|
||||
|
||||
if (input_samples_per_channel_ != proc_samples_per_channel_) {
|
||||
input_resamplers_.reserve(num_proc_channels_);
|
||||
for (int i = 0; i < num_proc_channels_; ++i) {
|
||||
input_resamplers_.push_back(
|
||||
new PushSincResampler(input_samples_per_channel_,
|
||||
proc_samples_per_channel_));
|
||||
if (input_num_frames_ != proc_num_frames_) {
|
||||
for (int i = 0; i < num_proc_channels_; ++i) {
|
||||
input_resamplers_.push_back(
|
||||
new PushSincResampler(input_num_frames_,
|
||||
proc_num_frames_));
|
||||
}
|
||||
}
|
||||
|
||||
if (output_num_frames_ != proc_num_frames_) {
|
||||
for (int i = 0; i < num_proc_channels_; ++i) {
|
||||
output_resamplers_.push_back(
|
||||
new PushSincResampler(proc_num_frames_,
|
||||
output_num_frames_));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (output_samples_per_channel_ != proc_samples_per_channel_) {
|
||||
output_resamplers_.reserve(num_proc_channels_);
|
||||
for (int i = 0; i < num_proc_channels_; ++i) {
|
||||
output_resamplers_.push_back(
|
||||
new PushSincResampler(proc_samples_per_channel_,
|
||||
output_samples_per_channel_));
|
||||
}
|
||||
}
|
||||
|
||||
if (proc_samples_per_channel_ == kSamplesPer32kHzChannel ||
|
||||
proc_samples_per_channel_ == kSamplesPer48kHzChannel) {
|
||||
samples_per_split_channel_ = kSamplesPer16kHzChannel;
|
||||
num_bands_ = proc_samples_per_channel_ / samples_per_split_channel_;
|
||||
split_channels_.push_back(new IFChannelBuffer(samples_per_split_channel_,
|
||||
num_proc_channels_));
|
||||
split_channels_.push_back(new IFChannelBuffer(samples_per_split_channel_,
|
||||
num_proc_channels_));
|
||||
if (num_bands_ > 1) {
|
||||
split_data_.reset(new IFChannelBuffer(proc_num_frames_,
|
||||
num_proc_channels_,
|
||||
num_bands_));
|
||||
splitting_filter_.reset(new SplittingFilter(num_proc_channels_));
|
||||
if (proc_samples_per_channel_ == kSamplesPer48kHzChannel) {
|
||||
split_channels_.push_back(new IFChannelBuffer(samples_per_split_channel_,
|
||||
num_proc_channels_));
|
||||
}
|
||||
}
|
||||
bands_.reset(new int16_t*[num_proc_channels_ * kMaxNumBands]);
|
||||
bands_f_.reset(new float*[num_proc_channels_ * kMaxNumBands]);
|
||||
}
|
||||
|
||||
AudioBuffer::~AudioBuffer() {}
|
||||
|
||||
void AudioBuffer::CopyFrom(const float* const* data,
|
||||
int samples_per_channel,
|
||||
int num_frames,
|
||||
AudioProcessing::ChannelLayout layout) {
|
||||
assert(samples_per_channel == input_samples_per_channel_);
|
||||
assert(num_frames == input_num_frames_);
|
||||
assert(ChannelsFromLayout(layout) == num_input_channels_);
|
||||
InitForNewData();
|
||||
|
||||
@ -146,53 +144,55 @@ void AudioBuffer::CopyFrom(const float* const* data,
|
||||
if (num_input_channels_ == 2 && num_proc_channels_ == 1) {
|
||||
StereoToMono(data[0],
|
||||
data[1],
|
||||
input_buffer_->channel(0),
|
||||
input_samples_per_channel_);
|
||||
input_buffer_->channels()[0],
|
||||
input_num_frames_);
|
||||
data_ptr = input_buffer_->channels();
|
||||
}
|
||||
|
||||
// Resample.
|
||||
if (input_samples_per_channel_ != proc_samples_per_channel_) {
|
||||
if (input_num_frames_ != proc_num_frames_) {
|
||||
for (int i = 0; i < num_proc_channels_; ++i) {
|
||||
input_resamplers_[i]->Resample(data_ptr[i],
|
||||
input_samples_per_channel_,
|
||||
process_buffer_->channel(i),
|
||||
proc_samples_per_channel_);
|
||||
input_num_frames_,
|
||||
process_buffer_->channels()[i],
|
||||
proc_num_frames_);
|
||||
}
|
||||
data_ptr = process_buffer_->channels();
|
||||
}
|
||||
|
||||
// Convert to the S16 range.
|
||||
for (int i = 0; i < num_proc_channels_; ++i) {
|
||||
FloatToFloatS16(data_ptr[i], proc_samples_per_channel_,
|
||||
channels_->fbuf()->channel(i));
|
||||
FloatToFloatS16(data_ptr[i],
|
||||
proc_num_frames_,
|
||||
data_->fbuf()->channels()[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioBuffer::CopyTo(int samples_per_channel,
|
||||
void AudioBuffer::CopyTo(int num_frames,
|
||||
AudioProcessing::ChannelLayout layout,
|
||||
float* const* data) {
|
||||
assert(samples_per_channel == output_samples_per_channel_);
|
||||
assert(num_frames == output_num_frames_);
|
||||
assert(ChannelsFromLayout(layout) == num_channels_);
|
||||
|
||||
// Convert to the float range.
|
||||
float* const* data_ptr = data;
|
||||
if (output_samples_per_channel_ != proc_samples_per_channel_) {
|
||||
if (output_num_frames_ != proc_num_frames_) {
|
||||
// Convert to an intermediate buffer for subsequent resampling.
|
||||
data_ptr = process_buffer_->channels();
|
||||
}
|
||||
for (int i = 0; i < num_channels_; ++i) {
|
||||
FloatS16ToFloat(channels_->fbuf()->channel(i), proc_samples_per_channel_,
|
||||
FloatS16ToFloat(data_->fbuf()->channels()[i],
|
||||
proc_num_frames_,
|
||||
data_ptr[i]);
|
||||
}
|
||||
|
||||
// Resample.
|
||||
if (output_samples_per_channel_ != proc_samples_per_channel_) {
|
||||
if (output_num_frames_ != proc_num_frames_) {
|
||||
for (int i = 0; i < num_channels_; ++i) {
|
||||
output_resamplers_[i]->Resample(data_ptr[i],
|
||||
proc_samples_per_channel_,
|
||||
proc_num_frames_,
|
||||
data[i],
|
||||
output_samples_per_channel_);
|
||||
output_num_frames_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -205,119 +205,81 @@ void AudioBuffer::InitForNewData() {
|
||||
num_channels_ = num_proc_channels_;
|
||||
}
|
||||
|
||||
const int16_t* AudioBuffer::data_const(int channel) const {
|
||||
return channels_const()[channel];
|
||||
}
|
||||
|
||||
int16_t* AudioBuffer::data(int channel) {
|
||||
return channels()[channel];
|
||||
}
|
||||
|
||||
const int16_t* const* AudioBuffer::channels_const() const {
|
||||
return channels_->ibuf_const()->channels();
|
||||
return data_->ibuf_const()->channels();
|
||||
}
|
||||
|
||||
int16_t* const* AudioBuffer::channels() {
|
||||
mixed_low_pass_valid_ = false;
|
||||
return channels_->ibuf()->channels();
|
||||
return data_->ibuf()->channels();
|
||||
}
|
||||
|
||||
const int16_t* const* AudioBuffer::split_bands_const(int channel) const {
|
||||
// This is necessary to make sure that the int16_t data is up to date in the
|
||||
// IFChannelBuffer.
|
||||
// TODO(aluebs): Having to depend on this to get the updated data is bug
|
||||
// prone. One solution is to have ChannelBuffer track the bands as well.
|
||||
for (int i = 0; i < kMaxNumBands; ++i) {
|
||||
int16_t* const* channels =
|
||||
const_cast<int16_t* const*>(split_channels_const(static_cast<Band>(i)));
|
||||
bands_[kMaxNumBands * channel + i] = channels ? channels[channel] : NULL;
|
||||
}
|
||||
return &bands_[kMaxNumBands * channel];
|
||||
return split_data_.get() ?
|
||||
split_data_->ibuf_const()->bands(channel) :
|
||||
data_->ibuf_const()->bands(channel);
|
||||
}
|
||||
|
||||
int16_t* const* AudioBuffer::split_bands(int channel) {
|
||||
mixed_low_pass_valid_ = false;
|
||||
// This is necessary to make sure that the int16_t data is up to date and the
|
||||
// float data is marked as invalid in the IFChannelBuffer.
|
||||
for (int i = 0; i < kMaxNumBands; ++i) {
|
||||
int16_t* const* channels = split_channels(static_cast<Band>(i));
|
||||
bands_[kMaxNumBands * channel + i] = channels ? channels[channel] : NULL;
|
||||
}
|
||||
return &bands_[kMaxNumBands * channel];
|
||||
return split_data_.get() ?
|
||||
split_data_->ibuf()->bands(channel) :
|
||||
data_->ibuf()->bands(channel);
|
||||
}
|
||||
|
||||
const int16_t* const* AudioBuffer::split_channels_const(Band band) const {
|
||||
if (split_channels_.size() > static_cast<size_t>(band)) {
|
||||
return split_channels_[band]->ibuf_const()->channels();
|
||||
if (split_data_.get()) {
|
||||
return split_data_->ibuf_const()->channels(band);
|
||||
} else {
|
||||
return band == kBand0To8kHz ? channels_->ibuf_const()->channels() : NULL;
|
||||
return band == kBand0To8kHz ? data_->ibuf_const()->channels() : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int16_t* const* AudioBuffer::split_channels(Band band) {
|
||||
mixed_low_pass_valid_ = false;
|
||||
if (split_channels_.size() > static_cast<size_t>(band)) {
|
||||
return split_channels_[band]->ibuf()->channels();
|
||||
if (split_data_.get()) {
|
||||
return split_data_->ibuf()->channels(band);
|
||||
} else {
|
||||
return band == kBand0To8kHz ? channels_->ibuf()->channels() : NULL;
|
||||
return band == kBand0To8kHz ? data_->ibuf()->channels() : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const float* AudioBuffer::data_const_f(int channel) const {
|
||||
return channels_const_f()[channel];
|
||||
}
|
||||
|
||||
float* AudioBuffer::data_f(int channel) {
|
||||
return channels_f()[channel];
|
||||
}
|
||||
|
||||
const float* const* AudioBuffer::channels_const_f() const {
|
||||
return channels_->fbuf_const()->channels();
|
||||
return data_->fbuf_const()->channels();
|
||||
}
|
||||
|
||||
float* const* AudioBuffer::channels_f() {
|
||||
mixed_low_pass_valid_ = false;
|
||||
return channels_->fbuf()->channels();
|
||||
return data_->fbuf()->channels();
|
||||
}
|
||||
|
||||
const float* const* AudioBuffer::split_bands_const_f(int channel) const {
|
||||
// This is necessary to make sure that the float data is up to date in the
|
||||
// IFChannelBuffer.
|
||||
for (int i = 0; i < kMaxNumBands; ++i) {
|
||||
float* const* channels =
|
||||
const_cast<float* const*>(split_channels_const_f(static_cast<Band>(i)));
|
||||
bands_f_[kMaxNumBands * channel + i] = channels ? channels[channel] : NULL;
|
||||
|
||||
}
|
||||
return &bands_f_[kMaxNumBands * channel];
|
||||
return split_data_.get() ?
|
||||
split_data_->fbuf_const()->bands(channel) :
|
||||
data_->fbuf_const()->bands(channel);
|
||||
}
|
||||
|
||||
float* const* AudioBuffer::split_bands_f(int channel) {
|
||||
mixed_low_pass_valid_ = false;
|
||||
// This is necessary to make sure that the float data is up to date and the
|
||||
// int16_t data is marked as invalid in the IFChannelBuffer.
|
||||
for (int i = 0; i < kMaxNumBands; ++i) {
|
||||
float* const* channels = split_channels_f(static_cast<Band>(i));
|
||||
bands_f_[kMaxNumBands * channel + i] = channels ? channels[channel] : NULL;
|
||||
|
||||
}
|
||||
return &bands_f_[kMaxNumBands * channel];
|
||||
return split_data_.get() ?
|
||||
split_data_->fbuf()->bands(channel) :
|
||||
data_->fbuf()->bands(channel);
|
||||
}
|
||||
|
||||
const float* const* AudioBuffer::split_channels_const_f(Band band) const {
|
||||
if (split_channels_.size() > static_cast<size_t>(band)) {
|
||||
return split_channels_[band]->fbuf_const()->channels();
|
||||
if (split_data_.get()) {
|
||||
return split_data_->fbuf_const()->channels(band);
|
||||
} else {
|
||||
return band == kBand0To8kHz ? channels_->fbuf_const()->channels() : NULL;
|
||||
return band == kBand0To8kHz ? data_->fbuf_const()->channels() : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
float* const* AudioBuffer::split_channels_f(Band band) {
|
||||
mixed_low_pass_valid_ = false;
|
||||
if (split_channels_.size() > static_cast<size_t>(band)) {
|
||||
return split_channels_[band]->fbuf()->channels();
|
||||
if (split_data_.get()) {
|
||||
return split_data_->fbuf()->channels(band);
|
||||
} else {
|
||||
return band == kBand0To8kHz ? channels_->fbuf()->channels() : NULL;
|
||||
return band == kBand0To8kHz ? data_->fbuf()->channels() : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,15 +294,15 @@ const int16_t* AudioBuffer::mixed_low_pass_data() {
|
||||
if (!mixed_low_pass_valid_) {
|
||||
if (!mixed_low_pass_channels_.get()) {
|
||||
mixed_low_pass_channels_.reset(
|
||||
new ChannelBuffer<int16_t>(samples_per_split_channel_, 1));
|
||||
new ChannelBuffer<int16_t>(num_split_frames_, 1));
|
||||
}
|
||||
StereoToMono(split_bands_const(0)[kBand0To8kHz],
|
||||
split_bands_const(1)[kBand0To8kHz],
|
||||
mixed_low_pass_channels_->data(),
|
||||
samples_per_split_channel_);
|
||||
mixed_low_pass_channels_->channels()[0],
|
||||
num_split_frames_);
|
||||
mixed_low_pass_valid_ = true;
|
||||
}
|
||||
return mixed_low_pass_channels_->data();
|
||||
return mixed_low_pass_channels_->channels()[0];
|
||||
}
|
||||
|
||||
const int16_t* AudioBuffer::low_pass_reference(int channel) const {
|
||||
@ -348,7 +310,7 @@ const int16_t* AudioBuffer::low_pass_reference(int channel) const {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return low_pass_reference_channels_->channel(channel);
|
||||
return low_pass_reference_channels_->channels()[channel];
|
||||
}
|
||||
|
||||
const float* AudioBuffer::keyboard_data() const {
|
||||
@ -371,17 +333,17 @@ void AudioBuffer::set_num_channels(int num_channels) {
|
||||
num_channels_ = num_channels;
|
||||
}
|
||||
|
||||
int AudioBuffer::samples_per_channel() const {
|
||||
return proc_samples_per_channel_;
|
||||
int AudioBuffer::num_frames() const {
|
||||
return proc_num_frames_;
|
||||
}
|
||||
|
||||
int AudioBuffer::samples_per_split_channel() const {
|
||||
return samples_per_split_channel_;
|
||||
int AudioBuffer::num_frames_per_band() const {
|
||||
return num_split_frames_;
|
||||
}
|
||||
|
||||
int AudioBuffer::samples_per_keyboard_channel() const {
|
||||
int AudioBuffer::num_keyboard_frames() const {
|
||||
// We don't resample the keyboard channel.
|
||||
return input_samples_per_channel_;
|
||||
return input_num_frames_;
|
||||
}
|
||||
|
||||
int AudioBuffer::num_bands() const {
|
||||
@ -390,25 +352,25 @@ int AudioBuffer::num_bands() const {
|
||||
|
||||
// TODO(andrew): Do deinterleaving and mixing in one step?
|
||||
void AudioBuffer::DeinterleaveFrom(AudioFrame* frame) {
|
||||
assert(proc_samples_per_channel_ == input_samples_per_channel_);
|
||||
assert(proc_num_frames_ == input_num_frames_);
|
||||
assert(frame->num_channels_ == num_input_channels_);
|
||||
assert(frame->samples_per_channel_ == proc_samples_per_channel_);
|
||||
assert(frame->samples_per_channel_ == proc_num_frames_);
|
||||
InitForNewData();
|
||||
activity_ = frame->vad_activity_;
|
||||
|
||||
if (num_input_channels_ == 2 && num_proc_channels_ == 1) {
|
||||
// Downmix directly; no explicit deinterleaving needed.
|
||||
int16_t* downmixed = channels_->ibuf()->channel(0);
|
||||
for (int i = 0; i < input_samples_per_channel_; ++i) {
|
||||
int16_t* downmixed = data_->ibuf()->channels()[0];
|
||||
for (int i = 0; i < input_num_frames_; ++i) {
|
||||
downmixed[i] = (frame->data_[i * 2] + frame->data_[i * 2 + 1]) / 2;
|
||||
}
|
||||
} else {
|
||||
assert(num_proc_channels_ == num_input_channels_);
|
||||
int16_t* interleaved = frame->data_;
|
||||
for (int i = 0; i < num_proc_channels_; ++i) {
|
||||
int16_t* deinterleaved = channels_->ibuf()->channel(i);
|
||||
int16_t* deinterleaved = data_->ibuf()->channels()[i];
|
||||
int interleaved_idx = i;
|
||||
for (int j = 0; j < proc_samples_per_channel_; ++j) {
|
||||
for (int j = 0; j < proc_num_frames_; ++j) {
|
||||
deinterleaved[j] = interleaved[interleaved_idx];
|
||||
interleaved_idx += num_proc_channels_;
|
||||
}
|
||||
@ -417,10 +379,10 @@ void AudioBuffer::DeinterleaveFrom(AudioFrame* frame) {
|
||||
}
|
||||
|
||||
void AudioBuffer::InterleaveTo(AudioFrame* frame, bool data_changed) const {
|
||||
assert(proc_samples_per_channel_ == output_samples_per_channel_);
|
||||
assert(proc_num_frames_ == output_num_frames_);
|
||||
assert(num_channels_ == num_input_channels_);
|
||||
assert(frame->num_channels_ == num_channels_);
|
||||
assert(frame->samples_per_channel_ == proc_samples_per_channel_);
|
||||
assert(frame->samples_per_channel_ == proc_num_frames_);
|
||||
frame->vad_activity_ = activity_;
|
||||
|
||||
if (!data_changed) {
|
||||
@ -429,9 +391,9 @@ void AudioBuffer::InterleaveTo(AudioFrame* frame, bool data_changed) const {
|
||||
|
||||
int16_t* interleaved = frame->data_;
|
||||
for (int i = 0; i < num_channels_; i++) {
|
||||
int16_t* deinterleaved = channels_->ibuf()->channel(i);
|
||||
int16_t* deinterleaved = data_->ibuf()->channels()[i];
|
||||
int interleaved_idx = i;
|
||||
for (int j = 0; j < proc_samples_per_channel_; j++) {
|
||||
for (int j = 0; j < proc_num_frames_; j++) {
|
||||
interleaved[interleaved_idx] = deinterleaved[j];
|
||||
interleaved_idx += num_channels_;
|
||||
}
|
||||
@ -443,23 +405,23 @@ void AudioBuffer::CopyLowPassToReference() {
|
||||
if (!low_pass_reference_channels_.get() ||
|
||||
low_pass_reference_channels_->num_channels() != num_channels_) {
|
||||
low_pass_reference_channels_.reset(
|
||||
new ChannelBuffer<int16_t>(samples_per_split_channel_,
|
||||
new ChannelBuffer<int16_t>(num_split_frames_,
|
||||
num_proc_channels_));
|
||||
}
|
||||
for (int i = 0; i < num_proc_channels_; i++) {
|
||||
low_pass_reference_channels_->CopyFrom(split_bands_const(i)[kBand0To8kHz],
|
||||
i);
|
||||
memcpy(low_pass_reference_channels_->channels()[i],
|
||||
split_bands_const(i)[kBand0To8kHz],
|
||||
low_pass_reference_channels_->num_frames_per_band() *
|
||||
sizeof(split_bands_const(i)[kBand0To8kHz][0]));
|
||||
}
|
||||
}
|
||||
|
||||
void AudioBuffer::SplitIntoFrequencyBands() {
|
||||
splitting_filter_->Analysis(channels_.get(),
|
||||
split_channels_.get());
|
||||
splitting_filter_->Analysis(data_.get(), split_data_.get());
|
||||
}
|
||||
|
||||
void AudioBuffer::MergeFrequencyBands() {
|
||||
splitting_filter_->Synthesis(split_channels_.get(),
|
||||
channels_.get());
|
||||
splitting_filter_->Synthesis(split_data_.get(), data_.get());
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -27,7 +27,6 @@ namespace webrtc {
|
||||
class PushSincResampler;
|
||||
class IFChannelBuffer;
|
||||
|
||||
static const int kMaxNumBands = 3;
|
||||
enum Band {
|
||||
kBand0To8kHz = 0,
|
||||
kBand8To16kHz = 1,
|
||||
@ -37,25 +36,23 @@ enum Band {
|
||||
class AudioBuffer {
|
||||
public:
|
||||
// TODO(ajm): Switch to take ChannelLayouts.
|
||||
AudioBuffer(int input_samples_per_channel,
|
||||
AudioBuffer(int input_num_frames,
|
||||
int num_input_channels,
|
||||
int process_samples_per_channel,
|
||||
int process_num_frames,
|
||||
int num_process_channels,
|
||||
int output_samples_per_channel);
|
||||
int output_num_frames);
|
||||
virtual ~AudioBuffer();
|
||||
|
||||
int num_channels() const;
|
||||
void set_num_channels(int num_channels);
|
||||
int samples_per_channel() const;
|
||||
int samples_per_split_channel() const;
|
||||
int samples_per_keyboard_channel() const;
|
||||
int num_frames() const;
|
||||
int num_frames_per_band() const;
|
||||
int num_keyboard_frames() const;
|
||||
int num_bands() const;
|
||||
|
||||
// Sample array accessors. Channels are guaranteed to be stored contiguously
|
||||
// in memory. Prefer to use the const variants of each accessor when
|
||||
// possible, since they incur less float<->int16 conversion overhead.
|
||||
int16_t* data(int channel);
|
||||
const int16_t* data_const(int channel) const;
|
||||
int16_t* const* channels();
|
||||
const int16_t* const* channels_const() const;
|
||||
int16_t* const* split_bands(int channel);
|
||||
@ -70,8 +67,6 @@ class AudioBuffer {
|
||||
|
||||
// Float versions of the accessors, with automatic conversion back and forth
|
||||
// as necessary. The range of the numbers are the same as for int16_t.
|
||||
float* data_f(int channel);
|
||||
const float* data_const_f(int channel) const;
|
||||
float* const* channels_f();
|
||||
const float* const* channels_const_f() const;
|
||||
float* const* split_bands_f(int channel);
|
||||
@ -92,9 +87,9 @@ class AudioBuffer {
|
||||
|
||||
// Use for float deinterleaved data.
|
||||
void CopyFrom(const float* const* data,
|
||||
int samples_per_channel,
|
||||
int num_frames,
|
||||
AudioProcessing::ChannelLayout layout);
|
||||
void CopyTo(int samples_per_channel,
|
||||
void CopyTo(int num_frames,
|
||||
AudioProcessing::ChannelLayout layout,
|
||||
float* const* data);
|
||||
void CopyLowPassToReference();
|
||||
@ -110,29 +105,27 @@ class AudioBuffer {
|
||||
|
||||
// The audio is passed into DeinterleaveFrom() or CopyFrom() with input
|
||||
// format (samples per channel and number of channels).
|
||||
const int input_samples_per_channel_;
|
||||
const int input_num_frames_;
|
||||
const int num_input_channels_;
|
||||
// The audio is stored by DeinterleaveFrom() or CopyFrom() with processing
|
||||
// format.
|
||||
const int proc_samples_per_channel_;
|
||||
const int proc_num_frames_;
|
||||
const int num_proc_channels_;
|
||||
// The audio is returned by InterleaveTo() and CopyTo() with output samples
|
||||
// per channels and the current number of channels. This last one can be
|
||||
// changed at any time using set_num_channels().
|
||||
const int output_samples_per_channel_;
|
||||
const int output_num_frames_;
|
||||
int num_channels_;
|
||||
|
||||
int num_bands_;
|
||||
int samples_per_split_channel_;
|
||||
int num_split_frames_;
|
||||
bool mixed_low_pass_valid_;
|
||||
bool reference_copied_;
|
||||
AudioFrame::VADActivity activity_;
|
||||
|
||||
const float* keyboard_data_;
|
||||
scoped_ptr<IFChannelBuffer> channels_;
|
||||
ScopedVector<IFChannelBuffer> split_channels_;
|
||||
scoped_ptr<int16_t*[]> bands_;
|
||||
scoped_ptr<float*[]> bands_f_;
|
||||
scoped_ptr<IFChannelBuffer> data_;
|
||||
scoped_ptr<IFChannelBuffer> split_data_;
|
||||
scoped_ptr<SplittingFilter> splitting_filter_;
|
||||
scoped_ptr<ChannelBuffer<int16_t> > mixed_low_pass_channels_;
|
||||
scoped_ptr<ChannelBuffer<int16_t> > low_pass_reference_channels_;
|
||||
|
@ -598,7 +598,7 @@ int AudioProcessingImpl::ProcessStreamLocked() {
|
||||
|
||||
AudioBuffer* ca = capture_audio_.get(); // For brevity.
|
||||
if (use_new_agc_ && gain_control_->is_enabled()) {
|
||||
agc_manager_->AnalyzePreProcess(ca->data(0),
|
||||
agc_manager_->AnalyzePreProcess(ca->channels()[0],
|
||||
ca->num_channels(),
|
||||
fwd_proc_format_.samples_per_channel());
|
||||
}
|
||||
@ -613,7 +613,7 @@ int AudioProcessingImpl::ProcessStreamLocked() {
|
||||
beamformer_->ProcessChunk(ca->split_channels_const_f(kBand0To8kHz),
|
||||
ca->split_channels_const_f(kBand8To16kHz),
|
||||
ca->num_channels(),
|
||||
ca->samples_per_split_channel(),
|
||||
ca->num_frames_per_band(),
|
||||
ca->split_channels_f(kBand0To8kHz),
|
||||
ca->split_channels_f(kBand8To16kHz));
|
||||
ca->set_num_channels(1);
|
||||
@ -636,7 +636,7 @@ int AudioProcessingImpl::ProcessStreamLocked() {
|
||||
gain_control_->is_enabled() &&
|
||||
(!beamformer_enabled_ || beamformer_->is_target_present())) {
|
||||
agc_manager_->Process(ca->split_bands_const(0)[kBand0To8kHz],
|
||||
ca->samples_per_split_channel(),
|
||||
ca->num_frames_per_band(),
|
||||
split_rate_);
|
||||
}
|
||||
RETURN_ON_ERR(gain_control_->ProcessCaptureAudio(ca));
|
||||
@ -651,13 +651,13 @@ int AudioProcessingImpl::ProcessStreamLocked() {
|
||||
float voice_probability =
|
||||
agc_manager_.get() ? agc_manager_->voice_probability() : 1.f;
|
||||
|
||||
transient_suppressor_->Suppress(ca->data_f(0),
|
||||
ca->samples_per_channel(),
|
||||
transient_suppressor_->Suppress(ca->channels_f()[0],
|
||||
ca->num_frames(),
|
||||
ca->num_channels(),
|
||||
ca->split_bands_const_f(0)[kBand0To8kHz],
|
||||
ca->samples_per_split_channel(),
|
||||
ca->num_frames_per_band(),
|
||||
ca->keyboard_data(),
|
||||
ca->samples_per_keyboard_channel(),
|
||||
ca->num_keyboard_frames(),
|
||||
voice_probability,
|
||||
key_pressed_);
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ size_t PcmReadToFloat(FILE* file,
|
||||
PcmRead(file, length, num_channels, deinterleaved_buffer->channels());
|
||||
|
||||
for (int i = 0; i < num_channels; ++i) {
|
||||
S16ToFloat(deinterleaved_buffer->channel(i), num_frames, buffer[i]);
|
||||
S16ToFloat(deinterleaved_buffer->channels()[i], num_frames, buffer[i]);
|
||||
}
|
||||
return elements_read;
|
||||
}
|
||||
@ -82,7 +82,7 @@ void PcmWriteFromFloat(FILE* file,
|
||||
new ChannelBuffer<int16_t>(num_frames, num_channels));
|
||||
|
||||
for (int i = 0; i < num_channels; ++i) {
|
||||
FloatToS16(buffer[i], num_frames, deinterleaved_buffer->channel(i));
|
||||
FloatToS16(buffer[i], num_frames, deinterleaved_buffer->channels()[i]);
|
||||
}
|
||||
PcmWrite(file, length, num_channels, deinterleaved_buffer->channels());
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ int EchoCancellationImpl::ProcessRenderAudio(const AudioBuffer* audio) {
|
||||
return apm_->kNoError;
|
||||
}
|
||||
|
||||
assert(audio->samples_per_split_channel() <= 160);
|
||||
assert(audio->num_frames_per_band() <= 160);
|
||||
assert(audio->num_channels() == apm_->num_reverse_channels());
|
||||
|
||||
int err = apm_->kNoError;
|
||||
@ -90,7 +90,7 @@ int EchoCancellationImpl::ProcessRenderAudio(const AudioBuffer* audio) {
|
||||
err = WebRtcAec_BufferFarend(
|
||||
my_handle,
|
||||
audio->split_bands_const_f(j)[kBand0To8kHz],
|
||||
static_cast<int16_t>(audio->samples_per_split_channel()));
|
||||
static_cast<int16_t>(audio->num_frames_per_band()));
|
||||
|
||||
if (err != apm_->kNoError) {
|
||||
return GetHandleError(my_handle); // TODO(ajm): warning possible?
|
||||
@ -116,7 +116,7 @@ int EchoCancellationImpl::ProcessCaptureAudio(AudioBuffer* audio) {
|
||||
return apm_->kStreamParameterNotSetError;
|
||||
}
|
||||
|
||||
assert(audio->samples_per_split_channel() <= 160);
|
||||
assert(audio->num_frames_per_band() <= 160);
|
||||
assert(audio->num_channels() == apm_->num_output_channels());
|
||||
|
||||
int err = apm_->kNoError;
|
||||
@ -132,7 +132,7 @@ int EchoCancellationImpl::ProcessCaptureAudio(AudioBuffer* audio) {
|
||||
audio->split_bands_const_f(i),
|
||||
audio->num_bands(),
|
||||
audio->split_bands_f(i),
|
||||
static_cast<int16_t>(audio->samples_per_split_channel()),
|
||||
static_cast<int16_t>(audio->num_frames_per_band()),
|
||||
apm_->stream_delay_ms(),
|
||||
stream_drift_samples_);
|
||||
|
||||
|
@ -83,7 +83,7 @@ int EchoControlMobileImpl::ProcessRenderAudio(const AudioBuffer* audio) {
|
||||
return apm_->kNoError;
|
||||
}
|
||||
|
||||
assert(audio->samples_per_split_channel() <= 160);
|
||||
assert(audio->num_frames_per_band() <= 160);
|
||||
assert(audio->num_channels() == apm_->num_reverse_channels());
|
||||
|
||||
int err = apm_->kNoError;
|
||||
@ -96,7 +96,7 @@ int EchoControlMobileImpl::ProcessRenderAudio(const AudioBuffer* audio) {
|
||||
err = WebRtcAecm_BufferFarend(
|
||||
my_handle,
|
||||
audio->split_bands_const(j)[kBand0To8kHz],
|
||||
static_cast<int16_t>(audio->samples_per_split_channel()));
|
||||
static_cast<int16_t>(audio->num_frames_per_band()));
|
||||
|
||||
if (err != apm_->kNoError) {
|
||||
return GetHandleError(my_handle); // TODO(ajm): warning possible?
|
||||
@ -118,7 +118,7 @@ int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio) {
|
||||
return apm_->kStreamParameterNotSetError;
|
||||
}
|
||||
|
||||
assert(audio->samples_per_split_channel() <= 160);
|
||||
assert(audio->num_frames_per_band() <= 160);
|
||||
assert(audio->num_channels() == apm_->num_output_channels());
|
||||
|
||||
int err = apm_->kNoError;
|
||||
@ -141,7 +141,7 @@ int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio) {
|
||||
noisy,
|
||||
clean,
|
||||
audio->split_bands(i)[kBand0To8kHz],
|
||||
static_cast<int16_t>(audio->samples_per_split_channel()),
|
||||
static_cast<int16_t>(audio->num_frames_per_band()),
|
||||
apm_->stream_delay_ms());
|
||||
|
||||
if (err != apm_->kNoError) {
|
||||
|
@ -57,14 +57,14 @@ int GainControlImpl::ProcessRenderAudio(AudioBuffer* audio) {
|
||||
return apm_->kNoError;
|
||||
}
|
||||
|
||||
assert(audio->samples_per_split_channel() <= 160);
|
||||
assert(audio->num_frames_per_band() <= 160);
|
||||
|
||||
for (int i = 0; i < num_handles(); i++) {
|
||||
Handle* my_handle = static_cast<Handle*>(handle(i));
|
||||
int err = WebRtcAgc_AddFarend(
|
||||
my_handle,
|
||||
audio->mixed_low_pass_data(),
|
||||
static_cast<int16_t>(audio->samples_per_split_channel()));
|
||||
static_cast<int16_t>(audio->num_frames_per_band()));
|
||||
|
||||
if (err != apm_->kNoError) {
|
||||
return GetHandleError(my_handle);
|
||||
@ -79,7 +79,7 @@ int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) {
|
||||
return apm_->kNoError;
|
||||
}
|
||||
|
||||
assert(audio->samples_per_split_channel() <= 160);
|
||||
assert(audio->num_frames_per_band() <= 160);
|
||||
assert(audio->num_channels() == num_handles());
|
||||
|
||||
int err = apm_->kNoError;
|
||||
@ -92,7 +92,7 @@ int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) {
|
||||
my_handle,
|
||||
audio->split_bands(i),
|
||||
audio->num_bands(),
|
||||
static_cast<int16_t>(audio->samples_per_split_channel()));
|
||||
static_cast<int16_t>(audio->num_frames_per_band()));
|
||||
|
||||
if (err != apm_->kNoError) {
|
||||
return GetHandleError(my_handle);
|
||||
@ -108,7 +108,7 @@ int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) {
|
||||
my_handle,
|
||||
audio->split_bands(i),
|
||||
audio->num_bands(),
|
||||
static_cast<int16_t>(audio->samples_per_split_channel()),
|
||||
static_cast<int16_t>(audio->num_frames_per_band()),
|
||||
analog_capture_level_,
|
||||
&capture_level_out);
|
||||
|
||||
@ -133,7 +133,7 @@ int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio) {
|
||||
return apm_->kStreamParameterNotSetError;
|
||||
}
|
||||
|
||||
assert(audio->samples_per_split_channel() <= 160);
|
||||
assert(audio->num_frames_per_band() <= 160);
|
||||
assert(audio->num_channels() == num_handles());
|
||||
|
||||
stream_is_saturated_ = false;
|
||||
@ -146,7 +146,7 @@ int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio) {
|
||||
my_handle,
|
||||
audio->split_bands_const(i),
|
||||
audio->num_bands(),
|
||||
static_cast<int16_t>(audio->samples_per_split_channel()),
|
||||
static_cast<int16_t>(audio->num_frames_per_band()),
|
||||
audio->split_bands(i),
|
||||
capture_levels_[i],
|
||||
&capture_level_out,
|
||||
|
@ -114,13 +114,13 @@ int HighPassFilterImpl::ProcessCaptureAudio(AudioBuffer* audio) {
|
||||
return apm_->kNoError;
|
||||
}
|
||||
|
||||
assert(audio->samples_per_split_channel() <= 160);
|
||||
assert(audio->num_frames_per_band() <= 160);
|
||||
|
||||
for (int i = 0; i < num_handles(); i++) {
|
||||
Handle* my_handle = static_cast<Handle*>(handle(i));
|
||||
err = Filter(my_handle,
|
||||
audio->split_bands(i)[kBand0To8kHz],
|
||||
audio->samples_per_split_channel());
|
||||
audio->num_frames_per_band());
|
||||
|
||||
if (err != apm_->kNoError) {
|
||||
return GetHandleError(my_handle);
|
||||
|
@ -31,8 +31,8 @@ int LevelEstimatorImpl::ProcessStream(AudioBuffer* audio) {
|
||||
|
||||
RMSLevel* rms_level = static_cast<RMSLevel*>(handle(0));
|
||||
for (int i = 0; i < audio->num_channels(); ++i) {
|
||||
rms_level->Process(audio->data_const(i),
|
||||
audio->samples_per_channel());
|
||||
rms_level->Process(audio->channels_const()[i],
|
||||
audio->num_frames());
|
||||
}
|
||||
|
||||
return AudioProcessing::kNoError;
|
||||
|
@ -60,7 +60,7 @@ int NoiseSuppressionImpl::AnalyzeCaptureAudio(AudioBuffer* audio) {
|
||||
if (!is_component_enabled()) {
|
||||
return apm_->kNoError;
|
||||
}
|
||||
assert(audio->samples_per_split_channel() <= 160);
|
||||
assert(audio->num_frames_per_band() <= 160);
|
||||
assert(audio->num_channels() == num_handles());
|
||||
|
||||
for (int i = 0; i < num_handles(); ++i) {
|
||||
@ -76,7 +76,7 @@ int NoiseSuppressionImpl::ProcessCaptureAudio(AudioBuffer* audio) {
|
||||
if (!is_component_enabled()) {
|
||||
return apm_->kNoError;
|
||||
}
|
||||
assert(audio->samples_per_split_channel() <= 160);
|
||||
assert(audio->num_frames_per_band() <= 160);
|
||||
assert(audio->num_channels() == num_handles());
|
||||
|
||||
for (int i = 0; i < num_handles(); ++i) {
|
||||
|
@ -30,59 +30,53 @@ SplittingFilter::SplittingFilter(int channels)
|
||||
}
|
||||
}
|
||||
|
||||
void SplittingFilter::Analysis(const IFChannelBuffer* in_data,
|
||||
const std::vector<IFChannelBuffer*>& bands) {
|
||||
DCHECK(bands.size() == 2 || bands.size() == 3);
|
||||
DCHECK_EQ(channels_, in_data->num_channels());
|
||||
for (size_t i = 0; i < bands.size(); ++i) {
|
||||
DCHECK_EQ(channels_, bands[i]->num_channels());
|
||||
DCHECK_EQ(in_data->samples_per_channel(),
|
||||
static_cast<int>(bands.size()) * bands[i]->samples_per_channel());
|
||||
}
|
||||
if (bands.size() == 2) {
|
||||
TwoBandsAnalysis(in_data, bands[0], bands[1]);
|
||||
} else if (bands.size() == 3) {
|
||||
ThreeBandsAnalysis(in_data, bands[0], bands[1], bands[2]);
|
||||
void SplittingFilter::Analysis(const IFChannelBuffer* data,
|
||||
IFChannelBuffer* bands) {
|
||||
DCHECK(bands->num_bands() == 2 || bands->num_bands() == 3);
|
||||
DCHECK_EQ(channels_, data->num_channels());
|
||||
DCHECK_EQ(channels_, bands->num_channels());
|
||||
DCHECK_EQ(data->num_frames(),
|
||||
bands->num_frames_per_band() * bands->num_bands());
|
||||
if (bands->num_bands() == 2) {
|
||||
TwoBandsAnalysis(data, bands);
|
||||
} else if (bands->num_bands() == 3) {
|
||||
ThreeBandsAnalysis(data, bands);
|
||||
}
|
||||
}
|
||||
|
||||
void SplittingFilter::Synthesis(const std::vector<IFChannelBuffer*>& bands,
|
||||
IFChannelBuffer* out_data) {
|
||||
DCHECK(bands.size() == 2 || bands.size() == 3);
|
||||
DCHECK_EQ(channels_, out_data->num_channels());
|
||||
for (size_t i = 0; i < bands.size(); ++i) {
|
||||
DCHECK_EQ(channels_, bands[i]->num_channels());
|
||||
DCHECK_EQ(out_data->samples_per_channel(),
|
||||
static_cast<int>(bands.size()) * bands[i]->samples_per_channel());
|
||||
}
|
||||
if (bands.size() == 2) {
|
||||
TwoBandsSynthesis(bands[0], bands[1], out_data);
|
||||
} else if (bands.size() == 3) {
|
||||
ThreeBandsSynthesis(bands[0], bands[1], bands[2], out_data);
|
||||
void SplittingFilter::Synthesis(const IFChannelBuffer* bands,
|
||||
IFChannelBuffer* data) {
|
||||
DCHECK(bands->num_bands() == 2 || bands->num_bands() == 3);
|
||||
DCHECK_EQ(channels_, data->num_channels());
|
||||
DCHECK_EQ(channels_, bands->num_channels());
|
||||
DCHECK_EQ(data->num_frames(),
|
||||
bands->num_frames_per_band() * bands->num_bands());
|
||||
if (bands->num_bands() == 2) {
|
||||
TwoBandsSynthesis(bands, data);
|
||||
} else if (bands->num_bands() == 3) {
|
||||
ThreeBandsSynthesis(bands, data);
|
||||
}
|
||||
}
|
||||
|
||||
void SplittingFilter::TwoBandsAnalysis(const IFChannelBuffer* in_data,
|
||||
IFChannelBuffer* band1,
|
||||
IFChannelBuffer* band2) {
|
||||
void SplittingFilter::TwoBandsAnalysis(const IFChannelBuffer* data,
|
||||
IFChannelBuffer* bands) {
|
||||
for (int i = 0; i < channels_; ++i) {
|
||||
WebRtcSpl_AnalysisQMF(in_data->ibuf_const()->channel(i),
|
||||
in_data->samples_per_channel(),
|
||||
band1->ibuf()->channel(i),
|
||||
band2->ibuf()->channel(i),
|
||||
WebRtcSpl_AnalysisQMF(data->ibuf_const()->channels()[i],
|
||||
data->num_frames(),
|
||||
bands->ibuf()->channels(0)[i],
|
||||
bands->ibuf()->channels(1)[i],
|
||||
two_bands_states_[i].analysis_state1,
|
||||
two_bands_states_[i].analysis_state2);
|
||||
}
|
||||
}
|
||||
|
||||
void SplittingFilter::TwoBandsSynthesis(const IFChannelBuffer* band1,
|
||||
const IFChannelBuffer* band2,
|
||||
IFChannelBuffer* out_data) {
|
||||
void SplittingFilter::TwoBandsSynthesis(const IFChannelBuffer* bands,
|
||||
IFChannelBuffer* data) {
|
||||
for (int i = 0; i < channels_; ++i) {
|
||||
WebRtcSpl_SynthesisQMF(band1->ibuf_const()->channel(i),
|
||||
band2->ibuf_const()->channel(i),
|
||||
band1->samples_per_channel(),
|
||||
out_data->ibuf()->channel(i),
|
||||
WebRtcSpl_SynthesisQMF(bands->ibuf_const()->channels(0)[i],
|
||||
bands->ibuf_const()->channels(1)[i],
|
||||
bands->num_frames_per_band(),
|
||||
data->ibuf()->channels()[i],
|
||||
two_bands_states_[i].synthesis_state1,
|
||||
two_bands_states_[i].synthesis_state2);
|
||||
}
|
||||
@ -92,15 +86,13 @@ void SplittingFilter::TwoBandsSynthesis(const IFChannelBuffer* band1,
|
||||
// by a proper 3 band filter bank.
|
||||
// It up-samples from 48kHz to 64kHz, splits twice into 2 bands and discards the
|
||||
// uppermost band, because it is empty anyway.
|
||||
void SplittingFilter::ThreeBandsAnalysis(const IFChannelBuffer* in_data,
|
||||
IFChannelBuffer* band1,
|
||||
IFChannelBuffer* band2,
|
||||
IFChannelBuffer* band3) {
|
||||
void SplittingFilter::ThreeBandsAnalysis(const IFChannelBuffer* data,
|
||||
IFChannelBuffer* bands) {
|
||||
DCHECK_EQ(kSamplesPer48kHzChannel,
|
||||
in_data->samples_per_channel());
|
||||
data->num_frames());
|
||||
InitBuffers();
|
||||
for (int i = 0; i < channels_; ++i) {
|
||||
analysis_resamplers_[i]->Resample(in_data->ibuf_const()->channel(i),
|
||||
analysis_resamplers_[i]->Resample(data->ibuf_const()->channels()[i],
|
||||
kSamplesPer48kHzChannel,
|
||||
int_buffer_.get(),
|
||||
kSamplesPer64kHzChannel);
|
||||
@ -112,14 +104,14 @@ void SplittingFilter::ThreeBandsAnalysis(const IFChannelBuffer* in_data,
|
||||
two_bands_states_[i].analysis_state2);
|
||||
WebRtcSpl_AnalysisQMF(int_buffer_.get(),
|
||||
kSamplesPer32kHzChannel,
|
||||
band1->ibuf()->channel(i),
|
||||
band2->ibuf()->channel(i),
|
||||
bands->ibuf()->channels(0)[i],
|
||||
bands->ibuf()->channels(1)[i],
|
||||
band1_states_[i].analysis_state1,
|
||||
band1_states_[i].analysis_state2);
|
||||
WebRtcSpl_AnalysisQMF(int_buffer_.get() + kSamplesPer32kHzChannel,
|
||||
kSamplesPer32kHzChannel,
|
||||
int_buffer_.get(),
|
||||
band3->ibuf()->channel(i),
|
||||
bands->ibuf()->channels(2)[i],
|
||||
band2_states_[i].analysis_state1,
|
||||
band2_states_[i].analysis_state2);
|
||||
}
|
||||
@ -129,25 +121,23 @@ void SplittingFilter::ThreeBandsAnalysis(const IFChannelBuffer* in_data,
|
||||
// by a proper 3 band filter bank.
|
||||
// Using an empty uppermost band, it merges the 4 bands in 2 steps and
|
||||
// down-samples from 64kHz to 48kHz.
|
||||
void SplittingFilter::ThreeBandsSynthesis(const IFChannelBuffer* band1,
|
||||
const IFChannelBuffer* band2,
|
||||
const IFChannelBuffer* band3,
|
||||
IFChannelBuffer* out_data) {
|
||||
void SplittingFilter::ThreeBandsSynthesis(const IFChannelBuffer* bands,
|
||||
IFChannelBuffer* data) {
|
||||
DCHECK_EQ(kSamplesPer48kHzChannel,
|
||||
out_data->samples_per_channel());
|
||||
data->num_frames());
|
||||
InitBuffers();
|
||||
for (int i = 0; i < channels_; ++i) {
|
||||
memset(int_buffer_.get(),
|
||||
0,
|
||||
kSamplesPer64kHzChannel * sizeof(int_buffer_[0]));
|
||||
WebRtcSpl_SynthesisQMF(band1->ibuf_const()->channel(i),
|
||||
band2->ibuf_const()->channel(i),
|
||||
WebRtcSpl_SynthesisQMF(bands->ibuf_const()->channels(0)[i],
|
||||
bands->ibuf_const()->channels(1)[i],
|
||||
kSamplesPer16kHzChannel,
|
||||
int_buffer_.get(),
|
||||
band1_states_[i].synthesis_state1,
|
||||
band1_states_[i].synthesis_state2);
|
||||
WebRtcSpl_SynthesisQMF(int_buffer_.get() + kSamplesPer32kHzChannel,
|
||||
band3->ibuf_const()->channel(i),
|
||||
bands->ibuf_const()->channels(2)[i],
|
||||
kSamplesPer16kHzChannel,
|
||||
int_buffer_.get() + kSamplesPer32kHzChannel,
|
||||
band2_states_[i].synthesis_state1,
|
||||
@ -160,7 +150,7 @@ void SplittingFilter::ThreeBandsSynthesis(const IFChannelBuffer* band1,
|
||||
two_bands_states_[i].synthesis_state2);
|
||||
synthesis_resamplers_[i]->Resample(int_buffer_.get(),
|
||||
kSamplesPer64kHzChannel,
|
||||
out_data->ibuf()->channel(i),
|
||||
data->ibuf()->channels()[i],
|
||||
kSamplesPer48kHzChannel);
|
||||
}
|
||||
}
|
||||
|
@ -56,28 +56,16 @@ class SplittingFilter {
|
||||
public:
|
||||
SplittingFilter(int channels);
|
||||
|
||||
void Analysis(const IFChannelBuffer* in_data,
|
||||
const std::vector<IFChannelBuffer*>& bands);
|
||||
void Synthesis(const std::vector<IFChannelBuffer*>& bands,
|
||||
IFChannelBuffer* out_data);
|
||||
void Analysis(const IFChannelBuffer* data, IFChannelBuffer* bands);
|
||||
void Synthesis(const IFChannelBuffer* bands, IFChannelBuffer* data);
|
||||
|
||||
private:
|
||||
// These work for 640 samples or less.
|
||||
void TwoBandsAnalysis(const IFChannelBuffer* in_data,
|
||||
IFChannelBuffer* band1,
|
||||
IFChannelBuffer* band2);
|
||||
void TwoBandsSynthesis(const IFChannelBuffer* band1,
|
||||
const IFChannelBuffer* band2,
|
||||
IFChannelBuffer* out_data);
|
||||
void TwoBandsAnalysis(const IFChannelBuffer* data, IFChannelBuffer* bands);
|
||||
void TwoBandsSynthesis(const IFChannelBuffer* bands, IFChannelBuffer* data);
|
||||
// These only work for 480 samples at the moment.
|
||||
void ThreeBandsAnalysis(const IFChannelBuffer* in_data,
|
||||
IFChannelBuffer* band1,
|
||||
IFChannelBuffer* band2,
|
||||
IFChannelBuffer* band3);
|
||||
void ThreeBandsSynthesis(const IFChannelBuffer* band1,
|
||||
const IFChannelBuffer* band2,
|
||||
const IFChannelBuffer* band3,
|
||||
IFChannelBuffer* out_data);
|
||||
void ThreeBandsAnalysis(const IFChannelBuffer* data, IFChannelBuffer* bands);
|
||||
void ThreeBandsSynthesis(const IFChannelBuffer* bands, IFChannelBuffer* data);
|
||||
void InitBuffers();
|
||||
|
||||
int channels_;
|
||||
|
@ -35,36 +35,32 @@ TEST(SplittingFilterTest, SplitsIntoThreeBandsAndReconstructs) {
|
||||
static const float kAmplitude = 8192;
|
||||
static const int kChunks = 8;
|
||||
SplittingFilter splitting_filter(kChannels);
|
||||
IFChannelBuffer in_data(kSamplesPer48kHzChannel, kChannels);
|
||||
IFChannelBuffer out_data(kSamplesPer48kHzChannel, kChannels);
|
||||
ScopedVector<IFChannelBuffer> bands;
|
||||
for (int i = 0; i < kNumBands; ++i) {
|
||||
bands.push_back(new IFChannelBuffer(kSamplesPer16kHzChannel, kChannels));
|
||||
}
|
||||
IFChannelBuffer in_data(kSamplesPer48kHzChannel, kChannels, kNumBands);
|
||||
IFChannelBuffer out_data(kSamplesPer48kHzChannel, kChannels, kNumBands);
|
||||
for (int i = 0; i < kChunks; ++i) {
|
||||
// Input signal generation.
|
||||
bool is_present[kNumBands];
|
||||
memset(in_data.fbuf()->channel(0),
|
||||
memset(in_data.fbuf()->channels()[0],
|
||||
0,
|
||||
kSamplesPer48kHzChannel * sizeof(in_data.fbuf()->channel(0)[0]));
|
||||
kSamplesPer48kHzChannel * sizeof(in_data.fbuf()->channels()[0][0]));
|
||||
for (int j = 0; j < kNumBands; ++j) {
|
||||
is_present[j] = i & (1 << j);
|
||||
float amplitude = is_present[j] ? kAmplitude : 0;
|
||||
for (int k = 0; k < kSamplesPer48kHzChannel; ++k) {
|
||||
in_data.fbuf()->channel(0)[k] +=
|
||||
in_data.fbuf()->channels()[0][k] +=
|
||||
amplitude * sin(2 * M_PI * kFrequenciesHz[j] *
|
||||
(i * kSamplesPer48kHzChannel + k) / kSampleRateHz);
|
||||
}
|
||||
}
|
||||
// Three band splitting filter.
|
||||
splitting_filter.Analysis(&in_data, bands.get());
|
||||
splitting_filter.Analysis(&in_data, &out_data);
|
||||
// Energy calculation.
|
||||
float energy[kNumBands];
|
||||
for (int j = 0; j < kNumBands; ++j) {
|
||||
energy[j] = 0;
|
||||
for (int k = 0; k < kSamplesPer16kHzChannel; ++k) {
|
||||
energy[j] += bands[j]->fbuf_const()->channel(0)[k] *
|
||||
bands[j]->fbuf_const()->channel(0)[k];
|
||||
energy[j] += out_data.fbuf_const()->channels(j)[0][k] *
|
||||
out_data.fbuf_const()->channels(j)[0][k];
|
||||
}
|
||||
energy[j] /= kSamplesPer16kHzChannel;
|
||||
if (is_present[j]) {
|
||||
@ -74,14 +70,14 @@ TEST(SplittingFilterTest, SplitsIntoThreeBandsAndReconstructs) {
|
||||
}
|
||||
}
|
||||
// Three band merge.
|
||||
splitting_filter.Synthesis(bands.get(), &out_data);
|
||||
splitting_filter.Synthesis(&out_data, &out_data);
|
||||
// Delay and cross correlation estimation.
|
||||
float xcorr = 0;
|
||||
for (int delay = 0; delay < kSamplesPer48kHzChannel; ++delay) {
|
||||
float tmpcorr = 0;
|
||||
for (int j = delay; j < kSamplesPer48kHzChannel; ++j) {
|
||||
tmpcorr += in_data.fbuf_const()->channel(0)[j] *
|
||||
out_data.fbuf_const()->channel(0)[j - delay];
|
||||
tmpcorr += in_data.fbuf_const()->channels()[0][j] *
|
||||
out_data.fbuf_const()->channels()[0][j - delay];
|
||||
}
|
||||
tmpcorr /= kSamplesPer48kHzChannel;
|
||||
if (tmpcorr > xcorr) {
|
||||
|
@ -62,15 +62,17 @@ const size_t kProcessSampleRatesSize = sizeof(kProcessSampleRates) /
|
||||
sizeof(*kProcessSampleRates);
|
||||
|
||||
void ConvertToFloat(const int16_t* int_data, ChannelBuffer<float>* cb) {
|
||||
ChannelBuffer<int16_t> cb_int(cb->samples_per_channel(),
|
||||
ChannelBuffer<int16_t> cb_int(cb->num_frames(),
|
||||
cb->num_channels());
|
||||
Deinterleave(int_data,
|
||||
cb->samples_per_channel(),
|
||||
cb->num_frames(),
|
||||
cb->num_channels(),
|
||||
cb_int.channels());
|
||||
S16ToFloat(cb_int.data(),
|
||||
cb->samples_per_channel() * cb->num_channels(),
|
||||
cb->data());
|
||||
for (int i = 0; i < cb->num_channels(); ++i) {
|
||||
S16ToFloat(cb_int.channels()[i],
|
||||
cb->num_frames(),
|
||||
cb->channels()[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void ConvertToFloat(const AudioFrame& frame, ChannelBuffer<float>* cb) {
|
||||
@ -294,7 +296,7 @@ void OpenFileAndReadMessage(const std::string filename,
|
||||
bool ReadChunk(FILE* file, int16_t* int_data, float* float_data,
|
||||
ChannelBuffer<float>* cb) {
|
||||
// The files always contain stereo audio.
|
||||
size_t frame_size = cb->samples_per_channel() * 2;
|
||||
size_t frame_size = cb->num_frames() * 2;
|
||||
size_t read_count = fread(int_data, sizeof(int16_t), frame_size, file);
|
||||
if (read_count != frame_size) {
|
||||
// Check that the file really ended.
|
||||
@ -304,9 +306,9 @@ bool ReadChunk(FILE* file, int16_t* int_data, float* float_data,
|
||||
|
||||
S16ToFloat(int_data, frame_size, float_data);
|
||||
if (cb->num_channels() == 1) {
|
||||
MixStereoToMono(float_data, cb->data(), cb->samples_per_channel());
|
||||
MixStereoToMono(float_data, cb->channels()[0], cb->num_frames());
|
||||
} else {
|
||||
Deinterleave(float_data, cb->samples_per_channel(), 2,
|
||||
Deinterleave(float_data, cb->num_frames(), 2,
|
||||
cb->channels());
|
||||
}
|
||||
|
||||
@ -1250,12 +1252,14 @@ TEST_F(ApmTest, AgcOnlyAdaptsWhenTargetSignalIsPresent) {
|
||||
int_data.get(),
|
||||
float_data.get(),
|
||||
&src_buf));
|
||||
for (int j = 0; j < kNumInputChannels * kSamplesPerChannel; ++j) {
|
||||
src_buf.data()[j] *= kScaleFactor;
|
||||
for (int j = 0; j < kNumInputChannels; ++j) {
|
||||
for (int k = 0; k < kSamplesPerChannel; ++k) {
|
||||
src_buf.channels()[j][k] *= kScaleFactor;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(kNoErr,
|
||||
apm->ProcessStream(src_buf.channels(),
|
||||
src_buf.samples_per_channel(),
|
||||
src_buf.num_frames(),
|
||||
kSampleRateHz,
|
||||
LayoutFromChannels(src_buf.num_channels()),
|
||||
kSampleRateHz,
|
||||
@ -1273,12 +1277,14 @@ TEST_F(ApmTest, AgcOnlyAdaptsWhenTargetSignalIsPresent) {
|
||||
int_data.get(),
|
||||
float_data.get(),
|
||||
&src_buf));
|
||||
for (int j = 0; j < kNumInputChannels * kSamplesPerChannel; ++j) {
|
||||
src_buf.data()[j] *= kScaleFactor;
|
||||
for (int j = 0; j < kNumInputChannels; ++j) {
|
||||
for (int k = 0; k < kSamplesPerChannel; ++k) {
|
||||
src_buf.channels()[j][k] *= kScaleFactor;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(kNoErr,
|
||||
apm->ProcessStream(src_buf.channels(),
|
||||
src_buf.samples_per_channel(),
|
||||
src_buf.num_frames(),
|
||||
kSampleRateHz,
|
||||
LayoutFromChannels(src_buf.num_channels()),
|
||||
kSampleRateHz,
|
||||
@ -1648,7 +1654,8 @@ void ApmTest::ProcessDebugDump(const std::string& in_filename,
|
||||
if (msg.channel_size() > 0) {
|
||||
ASSERT_EQ(revframe_->num_channels_, msg.channel_size());
|
||||
for (int i = 0; i < msg.channel_size(); ++i) {
|
||||
memcpy(revfloat_cb_->channel(i), msg.channel(i).data(),
|
||||
memcpy(revfloat_cb_->channels()[i],
|
||||
msg.channel(i).data(),
|
||||
msg.channel(i).size());
|
||||
}
|
||||
} else {
|
||||
@ -1677,7 +1684,8 @@ void ApmTest::ProcessDebugDump(const std::string& in_filename,
|
||||
if (msg.input_channel_size() > 0) {
|
||||
ASSERT_EQ(frame_->num_channels_, msg.input_channel_size());
|
||||
for (int i = 0; i < msg.input_channel_size(); ++i) {
|
||||
memcpy(float_cb_->channel(i), msg.input_channel(i).data(),
|
||||
memcpy(float_cb_->channels()[i],
|
||||
msg.input_channel(i).data(),
|
||||
msg.input_channel(i).size());
|
||||
}
|
||||
} else {
|
||||
@ -1835,7 +1843,6 @@ TEST_F(ApmTest, FloatAndIntInterfacesGiveSimilarResults) {
|
||||
const int num_output_channels = test->num_output_channels();
|
||||
const int samples_per_channel = test->sample_rate() *
|
||||
AudioProcessing::kChunkSizeMs / 1000;
|
||||
const int output_length = samples_per_channel * num_output_channels;
|
||||
|
||||
Init(test->sample_rate(), test->sample_rate(), test->sample_rate(),
|
||||
num_input_channels, num_output_channels, num_render_channels, true);
|
||||
@ -1876,11 +1883,13 @@ TEST_F(ApmTest, FloatAndIntInterfacesGiveSimilarResults) {
|
||||
test->sample_rate(),
|
||||
LayoutFromChannels(num_output_channels),
|
||||
float_cb_->channels()));
|
||||
|
||||
FloatToS16(float_cb_->data(), output_length, output_cb.data());
|
||||
for (int j = 0; j < num_output_channels; ++j) {
|
||||
FloatToS16(float_cb_->channels()[j],
|
||||
samples_per_channel,
|
||||
output_cb.channels()[j]);
|
||||
float variance = 0;
|
||||
float snr = ComputeSNR(output_int16.channel(j), output_cb.channel(j),
|
||||
float snr = ComputeSNR(output_int16.channels()[j],
|
||||
output_cb.channels()[j],
|
||||
samples_per_channel, &variance);
|
||||
#if defined(WEBRTC_AUDIOPROC_FIXED_PROFILE)
|
||||
// There are a few chunks in the fixed-point profile that give low SNR.
|
||||
@ -2171,7 +2180,7 @@ TEST_F(ApmTest, NoErrorsWithKeyboardChannel) {
|
||||
for (int j = 0; j < 10; ++j) {
|
||||
EXPECT_NOERR(ap->ProcessStream(
|
||||
in_cb.channels(),
|
||||
in_cb.samples_per_channel(),
|
||||
in_cb.num_frames(),
|
||||
in_rate,
|
||||
cf[i].in_layout,
|
||||
out_rate,
|
||||
@ -2313,9 +2322,9 @@ class AudioProcessingTest
|
||||
|
||||
// Temporary buffers.
|
||||
const int max_length =
|
||||
2 * std::max(out_cb.samples_per_channel(),
|
||||
std::max(fwd_cb.samples_per_channel(),
|
||||
rev_cb.samples_per_channel()));
|
||||
2 * std::max(out_cb.num_frames(),
|
||||
std::max(fwd_cb.num_frames(),
|
||||
rev_cb.num_frames()));
|
||||
scoped_ptr<float[]> float_data(new float[max_length]);
|
||||
scoped_ptr<int16_t[]> int_data(new int16_t[max_length]);
|
||||
|
||||
@ -2324,7 +2333,7 @@ class AudioProcessingTest
|
||||
ReadChunk(near_file, int_data.get(), float_data.get(), &fwd_cb)) {
|
||||
EXPECT_NOERR(ap->AnalyzeReverseStream(
|
||||
rev_cb.channels(),
|
||||
rev_cb.samples_per_channel(),
|
||||
rev_cb.num_frames(),
|
||||
reverse_rate,
|
||||
LayoutFromChannels(num_reverse_channels)));
|
||||
|
||||
@ -2334,7 +2343,7 @@ class AudioProcessingTest
|
||||
|
||||
EXPECT_NOERR(ap->ProcessStream(
|
||||
fwd_cb.channels(),
|
||||
fwd_cb.samples_per_channel(),
|
||||
fwd_cb.num_frames(),
|
||||
input_rate,
|
||||
LayoutFromChannels(num_input_channels),
|
||||
output_rate,
|
||||
@ -2342,13 +2351,14 @@ class AudioProcessingTest
|
||||
out_cb.channels()));
|
||||
|
||||
Interleave(out_cb.channels(),
|
||||
out_cb.samples_per_channel(),
|
||||
out_cb.num_frames(),
|
||||
out_cb.num_channels(),
|
||||
float_data.get());
|
||||
// Dump output to file.
|
||||
ASSERT_EQ(static_cast<size_t>(out_cb.length()),
|
||||
int out_length = out_cb.num_channels() * out_cb.num_frames();
|
||||
ASSERT_EQ(static_cast<size_t>(out_length),
|
||||
fwrite(float_data.get(), sizeof(float_data[0]),
|
||||
out_cb.length(), out_file));
|
||||
out_length, out_file));
|
||||
|
||||
analog_level = ap->gain_control()->stream_analog_level();
|
||||
}
|
||||
|
@ -177,27 +177,30 @@ int main(int argc, char* argv[]) {
|
||||
ChannelBuffer<float> o_buf(o_file.sample_rate() / kChunksPerSecond,
|
||||
o_file.num_channels());
|
||||
|
||||
const size_t c_length = static_cast<size_t>(c_buf.length());
|
||||
const size_t c_length =
|
||||
static_cast<size_t>(c_buf.num_channels() * c_buf.num_frames());
|
||||
const size_t o_length =
|
||||
static_cast<size_t>(o_buf.num_channels() * o_buf.num_frames());
|
||||
scoped_ptr<float[]> c_interleaved(new float[c_length]);
|
||||
scoped_ptr<float[]> o_interleaved(new float[o_buf.length()]);
|
||||
scoped_ptr<float[]> o_interleaved(new float[o_length]);
|
||||
while (c_file.ReadSamples(c_length, c_interleaved.get()) == c_length) {
|
||||
FloatS16ToFloat(c_interleaved.get(), c_length, c_interleaved.get());
|
||||
Deinterleave(c_interleaved.get(), c_buf.samples_per_channel(),
|
||||
Deinterleave(c_interleaved.get(), c_buf.num_frames(),
|
||||
c_buf.num_channels(), c_buf.channels());
|
||||
|
||||
CHECK_EQ(kNoErr,
|
||||
ap->ProcessStream(c_buf.channels(),
|
||||
c_buf.samples_per_channel(),
|
||||
c_buf.num_frames(),
|
||||
c_file.sample_rate(),
|
||||
LayoutFromChannels(c_buf.num_channels()),
|
||||
o_file.sample_rate(),
|
||||
LayoutFromChannels(o_buf.num_channels()),
|
||||
o_buf.channels()));
|
||||
|
||||
Interleave(o_buf.channels(), o_buf.samples_per_channel(),
|
||||
Interleave(o_buf.channels(), o_buf.num_frames(),
|
||||
o_buf.num_channels(), o_interleaved.get());
|
||||
FloatToFloatS16(o_interleaved.get(), o_buf.length(), o_interleaved.get());
|
||||
o_file.WriteSamples(o_interleaved.get(), o_buf.length());
|
||||
FloatToFloatS16(o_interleaved.get(), o_length, o_interleaved.get());
|
||||
o_file.WriteSamples(o_interleaved.get(), o_length);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -654,7 +654,10 @@ void void_main(int argc, char* argv[]) {
|
||||
memcpy(far_frame.data_, msg.data().data(), msg.data().size());
|
||||
} else {
|
||||
for (int i = 0; i < msg.channel_size(); ++i) {
|
||||
reverse_cb->CopyFrom(msg.channel(i).data(), i);
|
||||
memcpy(reverse_cb->channels()[i],
|
||||
msg.channel(i).data(),
|
||||
reverse_cb->num_frames() *
|
||||
sizeof(reverse_cb->channels()[i][0]));
|
||||
}
|
||||
}
|
||||
|
||||
@ -704,7 +707,10 @@ void void_main(int argc, char* argv[]) {
|
||||
near_read_bytes += msg.input_data().size();
|
||||
} else {
|
||||
for (int i = 0; i < msg.input_channel_size(); ++i) {
|
||||
primary_cb->CopyFrom(msg.input_channel(i).data(), i);
|
||||
memcpy(primary_cb->channels()[i],
|
||||
msg.input_channel(i).data(),
|
||||
primary_cb->num_frames() *
|
||||
sizeof(primary_cb->channels()[i][0]));
|
||||
near_read_bytes += msg.input_channel(i).size();
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ int VoiceDetectionImpl::ProcessCaptureAudio(AudioBuffer* audio) {
|
||||
using_external_vad_ = false;
|
||||
return apm_->kNoError;
|
||||
}
|
||||
assert(audio->samples_per_split_channel() <= 160);
|
||||
assert(audio->num_frames_per_band() <= 160);
|
||||
|
||||
// TODO(ajm): concatenate data in frame buffer here.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user