Update audio_processing module
Corresponds to upstream commit 524e9b043e7e86fd72353b987c9d5f6a1ebf83e1 Update notes: * Pull in third party license file * Replace .gypi files with BUILD.gn to keep track of what changes upstream * Bunch of new filse pulled in as dependencies * Won't build yet due to changes needed on top of these
This commit is contained in:
@ -1,6 +1,18 @@
|
||||
noinst_LTLIBRARIES = libcommon_audio.la
|
||||
|
||||
libcommon_audio_la_SOURCES = signal_processing/include/real_fft.h \
|
||||
libcommon_audio_la_SOURCES = \
|
||||
resampler/include/push_resampler.h \
|
||||
resampler/include/resampler.h \
|
||||
resampler/push_sinc_resampler.h \
|
||||
resampler/sinc_resampler.h \
|
||||
resampler/sinusoidal_linear_chirp_source.h \
|
||||
resampler/push_resampler.cc \
|
||||
resampler/push_sinc_resampler.cc \
|
||||
resampler/resampler.cc \
|
||||
resampler/sinc_resampler.cc \
|
||||
resampler/sinc_resampler_sse.cc \
|
||||
resampler/sinusoidal_linear_chirp_source.cc \
|
||||
signal_processing/include/real_fft.h \
|
||||
signal_processing/include/signal_processing_library.h \
|
||||
signal_processing/include/spl_inl.h \
|
||||
signal_processing/include/spl_inl_armv7.h \
|
||||
@ -51,24 +63,59 @@ libcommon_audio_la_SOURCES = signal_processing/include/real_fft.h \
|
||||
vad/vad_gmm.h \
|
||||
vad/vad_sp.c \
|
||||
vad/vad_sp.h \
|
||||
vad/webrtc_vad.c
|
||||
vad/webrtc_vad.c \
|
||||
audio_converter.cc \
|
||||
audio_converter.h \
|
||||
audio_ring_buffer.cc \
|
||||
audio_ring_buffer.h \
|
||||
blocker.cc \
|
||||
blocker.h \
|
||||
channel_buffer.cc \
|
||||
channel_buffer.h \
|
||||
fft4g.c \
|
||||
fft4g.h \
|
||||
fir_filter.cc \
|
||||
fir_filter.h \
|
||||
fir_filter_sse.cc \
|
||||
fir_filter_sse.h \
|
||||
lapped_transform.cc \
|
||||
lapped_transform.h \
|
||||
real_fourier.cc \
|
||||
real_fourier.h \
|
||||
real_fourier_ooura.cc \
|
||||
real_fourier_ooura.h \
|
||||
real_fourier_openmax.h \
|
||||
ring_buffer.h \
|
||||
ring_buffer.c \
|
||||
sparse_fir_filter.cc \
|
||||
sparse_fir_filter.h \
|
||||
wav_file.h \
|
||||
wav_file.cc \
|
||||
wav_header.h \
|
||||
wav_header.cc \
|
||||
window_generator.h \
|
||||
window_generator.cc
|
||||
|
||||
libcommon_audio_la_CFLAGS = $(AM_CFLAGS) $(COMMON_CFLAGS)
|
||||
libcommon_audio_la_CXXFLAGS = $(AM_CXXFLAGS) $(COMMON_CXXFLAGS)
|
||||
|
||||
# FIXME:
|
||||
# if ARM - signal_processing/complex_bit_reverse_arm.S
|
||||
# signal_processing/spl_sqrt_floor_arm.S
|
||||
# ARM7 - signal_processing/filter_ar_fast_q12_armv7.S
|
||||
# NEON - signal_processing/cross_correlation_neon.c
|
||||
# signal_processing/downsample_fast_neon.c
|
||||
# signal_processing/min_max_operations_neon.c
|
||||
# if MIPS - signal_processing/complex_bit_reverse_mips.c
|
||||
# signal_processing/complex_fft_mips.c
|
||||
# signal_processing/cross_correlation_mips.c
|
||||
# signal_processing/downsample_fast_mips.c
|
||||
# signal_processing/filter_ar_fast_q12_mips.c
|
||||
# signal_processing/min_max_operations_mips.c
|
||||
# signal_processing/resample_by_2_mips.c
|
||||
# signal_processing/spl_sqrt_floor_mips.c
|
||||
# signal_processing/vector_scaling_operations_mips.c
|
||||
# x86 - resampler/sinc_resampler_sse.cc
|
||||
# fir_filter_sse.cc
|
||||
# ARM - signal_processing/complex_bit_reverse_arm.S
|
||||
# signal_processing/spl_sqrt_floor_arm.S
|
||||
# ARM7 - signal_processing/filter_ar_fast_q12_armv7.S
|
||||
# NEON - resampler/sinc_resampler_neon.cc \
|
||||
# signal_processing/cross_correlation_neon.c
|
||||
# signal_processing/downsample_fast_neon.c
|
||||
# signal_processing/min_max_operations_neon.c
|
||||
# fir_filter_neon.c
|
||||
# MIPS - signal_processing/complex_bit_reverse_mips.c
|
||||
# signal_processing/complex_fft_mips.c
|
||||
# signal_processing/cross_correlation_mips.c
|
||||
# signal_processing/downsample_fast_mips.c
|
||||
# signal_processing/filter_ar_fast_q12_mips.c
|
||||
# signal_processing/min_max_operations_mips.c
|
||||
# signal_processing/resample_by_2_mips.c
|
||||
# signal_processing/spl_sqrt_floor_mips.c
|
||||
# signal_processing/vector_scaling_operations_mips.c
|
||||
|
200
webrtc/common_audio/audio_converter.cc
Normal file
200
webrtc/common_audio/audio_converter.cc
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/common_audio/audio_converter.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/base/safe_conversions.h"
|
||||
#include "webrtc/common_audio/channel_buffer.h"
|
||||
#include "webrtc/common_audio/resampler/push_sinc_resampler.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_vector.h"
|
||||
|
||||
using rtc::checked_cast;
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class CopyConverter : public AudioConverter {
|
||||
public:
|
||||
CopyConverter(int src_channels, size_t src_frames, int dst_channels,
|
||||
size_t dst_frames)
|
||||
: AudioConverter(src_channels, src_frames, dst_channels, dst_frames) {}
|
||||
~CopyConverter() override {};
|
||||
|
||||
void Convert(const float* const* src, size_t src_size, float* const* dst,
|
||||
size_t dst_capacity) override {
|
||||
CheckSizes(src_size, dst_capacity);
|
||||
if (src != dst) {
|
||||
for (int i = 0; i < src_channels(); ++i)
|
||||
std::memcpy(dst[i], src[i], dst_frames() * sizeof(*dst[i]));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class UpmixConverter : public AudioConverter {
|
||||
public:
|
||||
UpmixConverter(int src_channels, size_t src_frames, int dst_channels,
|
||||
size_t dst_frames)
|
||||
: AudioConverter(src_channels, src_frames, dst_channels, dst_frames) {}
|
||||
~UpmixConverter() override {};
|
||||
|
||||
void Convert(const float* const* src, size_t src_size, float* const* dst,
|
||||
size_t dst_capacity) override {
|
||||
CheckSizes(src_size, dst_capacity);
|
||||
for (size_t i = 0; i < dst_frames(); ++i) {
|
||||
const float value = src[0][i];
|
||||
for (int j = 0; j < dst_channels(); ++j)
|
||||
dst[j][i] = value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class DownmixConverter : public AudioConverter {
|
||||
public:
|
||||
DownmixConverter(int src_channels, size_t src_frames, int dst_channels,
|
||||
size_t dst_frames)
|
||||
: AudioConverter(src_channels, src_frames, dst_channels, dst_frames) {
|
||||
}
|
||||
~DownmixConverter() override {};
|
||||
|
||||
void Convert(const float* const* src, size_t src_size, float* const* dst,
|
||||
size_t dst_capacity) override {
|
||||
CheckSizes(src_size, dst_capacity);
|
||||
float* dst_mono = dst[0];
|
||||
for (size_t i = 0; i < src_frames(); ++i) {
|
||||
float sum = 0;
|
||||
for (int j = 0; j < src_channels(); ++j)
|
||||
sum += src[j][i];
|
||||
dst_mono[i] = sum / src_channels();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ResampleConverter : public AudioConverter {
|
||||
public:
|
||||
ResampleConverter(int src_channels, size_t src_frames, int dst_channels,
|
||||
size_t dst_frames)
|
||||
: AudioConverter(src_channels, src_frames, dst_channels, dst_frames) {
|
||||
resamplers_.reserve(src_channels);
|
||||
for (int i = 0; i < src_channels; ++i)
|
||||
resamplers_.push_back(new PushSincResampler(src_frames, dst_frames));
|
||||
}
|
||||
~ResampleConverter() override {};
|
||||
|
||||
void Convert(const float* const* src, size_t src_size, float* const* dst,
|
||||
size_t dst_capacity) override {
|
||||
CheckSizes(src_size, dst_capacity);
|
||||
for (size_t i = 0; i < resamplers_.size(); ++i)
|
||||
resamplers_[i]->Resample(src[i], src_frames(), dst[i], dst_frames());
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedVector<PushSincResampler> resamplers_;
|
||||
};
|
||||
|
||||
// Apply a vector of converters in serial, in the order given. At least two
|
||||
// converters must be provided.
|
||||
class CompositionConverter : public AudioConverter {
|
||||
public:
|
||||
CompositionConverter(ScopedVector<AudioConverter> converters)
|
||||
: converters_(converters.Pass()) {
|
||||
RTC_CHECK_GE(converters_.size(), 2u);
|
||||
// We need an intermediate buffer after every converter.
|
||||
for (auto it = converters_.begin(); it != converters_.end() - 1; ++it)
|
||||
buffers_.push_back(new ChannelBuffer<float>((*it)->dst_frames(),
|
||||
(*it)->dst_channels()));
|
||||
}
|
||||
~CompositionConverter() override {};
|
||||
|
||||
void Convert(const float* const* src, size_t src_size, float* const* dst,
|
||||
size_t dst_capacity) override {
|
||||
converters_.front()->Convert(src, src_size, buffers_.front()->channels(),
|
||||
buffers_.front()->size());
|
||||
for (size_t i = 2; i < converters_.size(); ++i) {
|
||||
auto src_buffer = buffers_[i - 2];
|
||||
auto dst_buffer = buffers_[i - 1];
|
||||
converters_[i]->Convert(src_buffer->channels(),
|
||||
src_buffer->size(),
|
||||
dst_buffer->channels(),
|
||||
dst_buffer->size());
|
||||
}
|
||||
converters_.back()->Convert(buffers_.back()->channels(),
|
||||
buffers_.back()->size(), dst, dst_capacity);
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedVector<AudioConverter> converters_;
|
||||
ScopedVector<ChannelBuffer<float>> buffers_;
|
||||
};
|
||||
|
||||
rtc::scoped_ptr<AudioConverter> AudioConverter::Create(int src_channels,
|
||||
size_t src_frames,
|
||||
int dst_channels,
|
||||
size_t dst_frames) {
|
||||
rtc::scoped_ptr<AudioConverter> sp;
|
||||
if (src_channels > dst_channels) {
|
||||
if (src_frames != dst_frames) {
|
||||
ScopedVector<AudioConverter> converters;
|
||||
converters.push_back(new DownmixConverter(src_channels, src_frames,
|
||||
dst_channels, src_frames));
|
||||
converters.push_back(new ResampleConverter(dst_channels, src_frames,
|
||||
dst_channels, dst_frames));
|
||||
sp.reset(new CompositionConverter(converters.Pass()));
|
||||
} else {
|
||||
sp.reset(new DownmixConverter(src_channels, src_frames, dst_channels,
|
||||
dst_frames));
|
||||
}
|
||||
} else if (src_channels < dst_channels) {
|
||||
if (src_frames != dst_frames) {
|
||||
ScopedVector<AudioConverter> converters;
|
||||
converters.push_back(new ResampleConverter(src_channels, src_frames,
|
||||
src_channels, dst_frames));
|
||||
converters.push_back(new UpmixConverter(src_channels, dst_frames,
|
||||
dst_channels, dst_frames));
|
||||
sp.reset(new CompositionConverter(converters.Pass()));
|
||||
} else {
|
||||
sp.reset(new UpmixConverter(src_channels, src_frames, dst_channels,
|
||||
dst_frames));
|
||||
}
|
||||
} else if (src_frames != dst_frames) {
|
||||
sp.reset(new ResampleConverter(src_channels, src_frames, dst_channels,
|
||||
dst_frames));
|
||||
} else {
|
||||
sp.reset(new CopyConverter(src_channels, src_frames, dst_channels,
|
||||
dst_frames));
|
||||
}
|
||||
|
||||
return sp.Pass();
|
||||
}
|
||||
|
||||
// For CompositionConverter.
|
||||
AudioConverter::AudioConverter()
|
||||
: src_channels_(0),
|
||||
src_frames_(0),
|
||||
dst_channels_(0),
|
||||
dst_frames_(0) {}
|
||||
|
||||
AudioConverter::AudioConverter(int src_channels, size_t src_frames,
|
||||
int dst_channels, size_t dst_frames)
|
||||
: src_channels_(src_channels),
|
||||
src_frames_(src_frames),
|
||||
dst_channels_(dst_channels),
|
||||
dst_frames_(dst_frames) {
|
||||
RTC_CHECK(dst_channels == src_channels || dst_channels == 1 ||
|
||||
src_channels == 1);
|
||||
}
|
||||
|
||||
void AudioConverter::CheckSizes(size_t src_size, size_t dst_capacity) const {
|
||||
RTC_CHECK_EQ(src_size, src_channels() * src_frames());
|
||||
RTC_CHECK_GE(dst_capacity, dst_channels() * dst_frames());
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
66
webrtc/common_audio/audio_converter.h
Normal file
66
webrtc/common_audio/audio_converter.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_AUDIO_CONVERTER_H_
|
||||
#define WEBRTC_COMMON_AUDIO_AUDIO_CONVERTER_H_
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Format conversion (remixing and resampling) for audio. Only simple remixing
|
||||
// conversions are supported: downmix to mono (i.e. |dst_channels| == 1) or
|
||||
// upmix from mono (i.e. |src_channels == 1|).
|
||||
//
|
||||
// The source and destination chunks have the same duration in time; specifying
|
||||
// the number of frames is equivalent to specifying the sample rates.
|
||||
class AudioConverter {
|
||||
public:
|
||||
// Returns a new AudioConverter, which will use the supplied format for its
|
||||
// lifetime. Caller is responsible for the memory.
|
||||
static rtc::scoped_ptr<AudioConverter> Create(int src_channels,
|
||||
size_t src_frames,
|
||||
int dst_channels,
|
||||
size_t dst_frames);
|
||||
virtual ~AudioConverter() {};
|
||||
|
||||
// Convert |src|, containing |src_size| samples, to |dst|, having a sample
|
||||
// capacity of |dst_capacity|. Both point to a series of buffers containing
|
||||
// the samples for each channel. The sizes must correspond to the format
|
||||
// passed to Create().
|
||||
virtual void Convert(const float* const* src, size_t src_size,
|
||||
float* const* dst, size_t dst_capacity) = 0;
|
||||
|
||||
int src_channels() const { return src_channels_; }
|
||||
size_t src_frames() const { return src_frames_; }
|
||||
int dst_channels() const { return dst_channels_; }
|
||||
size_t dst_frames() const { return dst_frames_; }
|
||||
|
||||
protected:
|
||||
AudioConverter();
|
||||
AudioConverter(int src_channels, size_t src_frames, int dst_channels,
|
||||
size_t dst_frames);
|
||||
|
||||
// Helper to RTC_CHECK that inputs are correctly sized.
|
||||
void CheckSizes(size_t src_size, size_t dst_capacity) const;
|
||||
|
||||
private:
|
||||
const int src_channels_;
|
||||
const size_t src_frames_;
|
||||
const int dst_channels_;
|
||||
const size_t dst_frames_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(AudioConverter);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_AUDIO_CONVERTER_H_
|
75
webrtc/common_audio/audio_ring_buffer.cc
Normal file
75
webrtc/common_audio/audio_ring_buffer.cc
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/common_audio/audio_ring_buffer.h"
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/common_audio/ring_buffer.h"
|
||||
|
||||
// This is a simple multi-channel wrapper over the ring_buffer.h C interface.
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
AudioRingBuffer::AudioRingBuffer(size_t channels, size_t max_frames) {
|
||||
buffers_.reserve(channels);
|
||||
for (size_t i = 0; i < channels; ++i)
|
||||
buffers_.push_back(WebRtc_CreateBuffer(max_frames, sizeof(float)));
|
||||
}
|
||||
|
||||
AudioRingBuffer::~AudioRingBuffer() {
|
||||
for (auto buf : buffers_)
|
||||
WebRtc_FreeBuffer(buf);
|
||||
}
|
||||
|
||||
void AudioRingBuffer::Write(const float* const* data, size_t channels,
|
||||
size_t frames) {
|
||||
RTC_DCHECK_EQ(buffers_.size(), channels);
|
||||
for (size_t i = 0; i < channels; ++i) {
|
||||
const size_t written = WebRtc_WriteBuffer(buffers_[i], data[i], frames);
|
||||
RTC_CHECK_EQ(written, frames);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioRingBuffer::Read(float* const* data, size_t channels, size_t frames) {
|
||||
RTC_DCHECK_EQ(buffers_.size(), channels);
|
||||
for (size_t i = 0; i < channels; ++i) {
|
||||
const size_t read =
|
||||
WebRtc_ReadBuffer(buffers_[i], nullptr, data[i], frames);
|
||||
RTC_CHECK_EQ(read, frames);
|
||||
}
|
||||
}
|
||||
|
||||
size_t AudioRingBuffer::ReadFramesAvailable() const {
|
||||
// All buffers have the same amount available.
|
||||
return WebRtc_available_read(buffers_[0]);
|
||||
}
|
||||
|
||||
size_t AudioRingBuffer::WriteFramesAvailable() const {
|
||||
// All buffers have the same amount available.
|
||||
return WebRtc_available_write(buffers_[0]);
|
||||
}
|
||||
|
||||
void AudioRingBuffer::MoveReadPositionForward(size_t frames) {
|
||||
for (auto buf : buffers_) {
|
||||
const size_t moved =
|
||||
static_cast<size_t>(WebRtc_MoveReadPtr(buf, static_cast<int>(frames)));
|
||||
RTC_CHECK_EQ(moved, frames);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioRingBuffer::MoveReadPositionBackward(size_t frames) {
|
||||
for (auto buf : buffers_) {
|
||||
const size_t moved = static_cast<size_t>(
|
||||
-WebRtc_MoveReadPtr(buf, -static_cast<int>(frames)));
|
||||
RTC_CHECK_EQ(moved, frames);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
56
webrtc/common_audio/audio_ring_buffer.h
Normal file
56
webrtc/common_audio/audio_ring_buffer.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#ifndef WEBRTC_COMMON_AUDIO_AUDIO_RING_BUFFER_H_
|
||||
#define WEBRTC_COMMON_AUDIO_AUDIO_RING_BUFFER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <vector>
|
||||
|
||||
struct RingBuffer;
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// A ring buffer tailored for float deinterleaved audio. Any operation that
|
||||
// cannot be performed as requested will cause a crash (e.g. insufficient data
|
||||
// in the buffer to fulfill a read request.)
|
||||
class AudioRingBuffer final {
|
||||
public:
|
||||
// Specify the number of channels and maximum number of frames the buffer will
|
||||
// contain.
|
||||
AudioRingBuffer(size_t channels, size_t max_frames);
|
||||
~AudioRingBuffer();
|
||||
|
||||
// Copies |data| to the buffer and advances the write pointer. |channels| must
|
||||
// be the same as at creation time.
|
||||
void Write(const float* const* data, size_t channels, size_t frames);
|
||||
|
||||
// Copies from the buffer to |data| and advances the read pointer. |channels|
|
||||
// must be the same as at creation time.
|
||||
void Read(float* const* data, size_t channels, size_t frames);
|
||||
|
||||
size_t ReadFramesAvailable() const;
|
||||
size_t WriteFramesAvailable() const;
|
||||
|
||||
// Moves the read position. The forward version advances the read pointer
|
||||
// towards the write pointer and the backward verison withdraws the read
|
||||
// pointer away from the write pointer (i.e. flushing and stuffing the buffer
|
||||
// respectively.)
|
||||
void MoveReadPositionForward(size_t frames);
|
||||
void MoveReadPositionBackward(size_t frames);
|
||||
|
||||
private:
|
||||
// We don't use a ScopedVector because it doesn't support a specialized
|
||||
// deleter (like scoped_ptr for instance.)
|
||||
std::vector<RingBuffer*> buffers_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_AUDIO_RING_BUFFER_H_
|
236
webrtc/common_audio/blocker.cc
Normal file
236
webrtc/common_audio/blocker.cc
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/common_audio/blocker.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Adds |a| and |b| frame by frame into |result| (basically matrix addition).
|
||||
void AddFrames(const float* const* a,
|
||||
size_t a_start_index,
|
||||
const float* const* b,
|
||||
int b_start_index,
|
||||
size_t num_frames,
|
||||
int num_channels,
|
||||
float* const* result,
|
||||
size_t result_start_index) {
|
||||
for (int i = 0; i < num_channels; ++i) {
|
||||
for (size_t j = 0; j < num_frames; ++j) {
|
||||
result[i][j + result_start_index] =
|
||||
a[i][j + a_start_index] + b[i][j + b_start_index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copies |src| into |dst| channel by channel.
|
||||
void CopyFrames(const float* const* src,
|
||||
size_t src_start_index,
|
||||
size_t num_frames,
|
||||
int num_channels,
|
||||
float* const* dst,
|
||||
size_t dst_start_index) {
|
||||
for (int i = 0; i < num_channels; ++i) {
|
||||
memcpy(&dst[i][dst_start_index],
|
||||
&src[i][src_start_index],
|
||||
num_frames * sizeof(dst[i][dst_start_index]));
|
||||
}
|
||||
}
|
||||
|
||||
// Moves |src| into |dst| channel by channel.
|
||||
void MoveFrames(const float* const* src,
|
||||
size_t src_start_index,
|
||||
size_t num_frames,
|
||||
int num_channels,
|
||||
float* const* dst,
|
||||
size_t dst_start_index) {
|
||||
for (int i = 0; i < num_channels; ++i) {
|
||||
memmove(&dst[i][dst_start_index],
|
||||
&src[i][src_start_index],
|
||||
num_frames * sizeof(dst[i][dst_start_index]));
|
||||
}
|
||||
}
|
||||
|
||||
void ZeroOut(float* const* buffer,
|
||||
size_t starting_idx,
|
||||
size_t num_frames,
|
||||
int num_channels) {
|
||||
for (int i = 0; i < num_channels; ++i) {
|
||||
memset(&buffer[i][starting_idx], 0,
|
||||
num_frames * sizeof(buffer[i][starting_idx]));
|
||||
}
|
||||
}
|
||||
|
||||
// Pointwise multiplies each channel of |frames| with |window|. Results are
|
||||
// stored in |frames|.
|
||||
void ApplyWindow(const float* window,
|
||||
size_t num_frames,
|
||||
int num_channels,
|
||||
float* const* frames) {
|
||||
for (int i = 0; i < num_channels; ++i) {
|
||||
for (size_t j = 0; j < num_frames; ++j) {
|
||||
frames[i][j] = frames[i][j] * window[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t gcd(size_t a, size_t b) {
|
||||
size_t tmp;
|
||||
while (b) {
|
||||
tmp = a;
|
||||
a = b;
|
||||
b = tmp % b;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
Blocker::Blocker(size_t chunk_size,
|
||||
size_t block_size,
|
||||
int num_input_channels,
|
||||
int num_output_channels,
|
||||
const float* window,
|
||||
size_t shift_amount,
|
||||
BlockerCallback* callback)
|
||||
: chunk_size_(chunk_size),
|
||||
block_size_(block_size),
|
||||
num_input_channels_(num_input_channels),
|
||||
num_output_channels_(num_output_channels),
|
||||
initial_delay_(block_size_ - gcd(chunk_size, shift_amount)),
|
||||
frame_offset_(0),
|
||||
input_buffer_(num_input_channels_, chunk_size_ + initial_delay_),
|
||||
output_buffer_(chunk_size_ + initial_delay_, num_output_channels_),
|
||||
input_block_(block_size_, num_input_channels_),
|
||||
output_block_(block_size_, num_output_channels_),
|
||||
window_(new float[block_size_]),
|
||||
shift_amount_(shift_amount),
|
||||
callback_(callback) {
|
||||
RTC_CHECK_LE(num_output_channels_, num_input_channels_);
|
||||
RTC_CHECK_LE(shift_amount_, block_size_);
|
||||
|
||||
memcpy(window_.get(), window, block_size_ * sizeof(*window_.get()));
|
||||
input_buffer_.MoveReadPositionBackward(initial_delay_);
|
||||
}
|
||||
|
||||
// When block_size < chunk_size the input and output buffers look like this:
|
||||
//
|
||||
// delay* chunk_size chunk_size + delay*
|
||||
// buffer: <-------------|---------------------|---------------|>
|
||||
// _a_ _b_ _c_
|
||||
//
|
||||
// On each call to ProcessChunk():
|
||||
// 1. New input gets read into sections _b_ and _c_ of the input buffer.
|
||||
// 2. We block starting from frame_offset.
|
||||
// 3. We block until we reach a block |bl| that doesn't contain any frames
|
||||
// from sections _a_ or _b_ of the input buffer.
|
||||
// 4. We window the current block, fire the callback for processing, window
|
||||
// again, and overlap/add to the output buffer.
|
||||
// 5. We copy sections _a_ and _b_ of the output buffer into output.
|
||||
// 6. For both the input and the output buffers, we copy section _c_ into
|
||||
// section _a_.
|
||||
// 7. We set the new frame_offset to be the difference between the first frame
|
||||
// of |bl| and the border between sections _b_ and _c_.
|
||||
//
|
||||
// When block_size > chunk_size the input and output buffers look like this:
|
||||
//
|
||||
// chunk_size delay* chunk_size + delay*
|
||||
// buffer: <-------------|---------------------|---------------|>
|
||||
// _a_ _b_ _c_
|
||||
//
|
||||
// On each call to ProcessChunk():
|
||||
// The procedure is the same as above, except for:
|
||||
// 1. New input gets read into section _c_ of the input buffer.
|
||||
// 3. We block until we reach a block |bl| that doesn't contain any frames
|
||||
// from section _a_ of the input buffer.
|
||||
// 5. We copy section _a_ of the output buffer into output.
|
||||
// 6. For both the input and the output buffers, we copy sections _b_ and _c_
|
||||
// into section _a_ and _b_.
|
||||
// 7. We set the new frame_offset to be the difference between the first frame
|
||||
// of |bl| and the border between sections _a_ and _b_.
|
||||
//
|
||||
// * delay here refers to inintial_delay_
|
||||
//
|
||||
// TODO(claguna): Look at using ring buffers to eliminate some copies.
|
||||
void Blocker::ProcessChunk(const float* const* input,
|
||||
size_t chunk_size,
|
||||
int num_input_channels,
|
||||
int num_output_channels,
|
||||
float* const* output) {
|
||||
RTC_CHECK_EQ(chunk_size, chunk_size_);
|
||||
RTC_CHECK_EQ(num_input_channels, num_input_channels_);
|
||||
RTC_CHECK_EQ(num_output_channels, num_output_channels_);
|
||||
|
||||
input_buffer_.Write(input, num_input_channels, chunk_size_);
|
||||
size_t first_frame_in_block = frame_offset_;
|
||||
|
||||
// Loop through blocks.
|
||||
while (first_frame_in_block < chunk_size_) {
|
||||
input_buffer_.Read(input_block_.channels(), num_input_channels,
|
||||
block_size_);
|
||||
input_buffer_.MoveReadPositionBackward(block_size_ - shift_amount_);
|
||||
|
||||
ApplyWindow(window_.get(),
|
||||
block_size_,
|
||||
num_input_channels_,
|
||||
input_block_.channels());
|
||||
callback_->ProcessBlock(input_block_.channels(),
|
||||
block_size_,
|
||||
num_input_channels_,
|
||||
num_output_channels_,
|
||||
output_block_.channels());
|
||||
ApplyWindow(window_.get(),
|
||||
block_size_,
|
||||
num_output_channels_,
|
||||
output_block_.channels());
|
||||
|
||||
AddFrames(output_buffer_.channels(),
|
||||
first_frame_in_block,
|
||||
output_block_.channels(),
|
||||
0,
|
||||
block_size_,
|
||||
num_output_channels_,
|
||||
output_buffer_.channels(),
|
||||
first_frame_in_block);
|
||||
|
||||
first_frame_in_block += shift_amount_;
|
||||
}
|
||||
|
||||
// Copy output buffer to output
|
||||
CopyFrames(output_buffer_.channels(),
|
||||
0,
|
||||
chunk_size_,
|
||||
num_output_channels_,
|
||||
output,
|
||||
0);
|
||||
|
||||
// Copy output buffer [chunk_size_, chunk_size_ + initial_delay]
|
||||
// to output buffer [0, initial_delay], zero the rest.
|
||||
MoveFrames(output_buffer_.channels(),
|
||||
chunk_size,
|
||||
initial_delay_,
|
||||
num_output_channels_,
|
||||
output_buffer_.channels(),
|
||||
0);
|
||||
ZeroOut(output_buffer_.channels(),
|
||||
initial_delay_,
|
||||
chunk_size_,
|
||||
num_output_channels_);
|
||||
|
||||
// Calculate new starting frames.
|
||||
frame_offset_ = first_frame_in_block - chunk_size_;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
123
webrtc/common_audio/blocker.h
Normal file
123
webrtc/common_audio/blocker.h
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_INTERNAL_BEAMFORMER_BLOCKER_H_
|
||||
#define WEBRTC_INTERNAL_BEAMFORMER_BLOCKER_H_
|
||||
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/common_audio/audio_ring_buffer.h"
|
||||
#include "webrtc/common_audio/channel_buffer.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// The callback function to process audio in the time domain. Input has already
|
||||
// been windowed, and output will be windowed. The number of input channels
|
||||
// must be >= the number of output channels.
|
||||
class BlockerCallback {
|
||||
public:
|
||||
virtual ~BlockerCallback() {}
|
||||
|
||||
virtual void ProcessBlock(const float* const* input,
|
||||
size_t num_frames,
|
||||
int num_input_channels,
|
||||
int num_output_channels,
|
||||
float* const* output) = 0;
|
||||
};
|
||||
|
||||
// The main purpose of Blocker is to abstract away the fact that often we
|
||||
// receive a different number of audio frames than our transform takes. For
|
||||
// example, most FFTs work best when the fft-size is a power of 2, but suppose
|
||||
// we receive 20ms of audio at a sample rate of 48000. That comes to 960 frames
|
||||
// of audio, which is not a power of 2. Blocker allows us to specify the
|
||||
// transform and all other necessary processing via the Process() callback
|
||||
// function without any constraints on the transform-size
|
||||
// (read: |block_size_|) or received-audio-size (read: |chunk_size_|).
|
||||
// We handle this for the multichannel audio case, allowing for different
|
||||
// numbers of input and output channels (for example, beamforming takes 2 or
|
||||
// more input channels and returns 1 output channel). Audio signals are
|
||||
// represented as deinterleaved floats in the range [-1, 1].
|
||||
//
|
||||
// Blocker is responsible for:
|
||||
// - blocking audio while handling potential discontinuities on the edges
|
||||
// of chunks
|
||||
// - windowing blocks before sending them to Process()
|
||||
// - windowing processed blocks, and overlap-adding them together before
|
||||
// sending back a processed chunk
|
||||
//
|
||||
// To use blocker:
|
||||
// 1. Impelment a BlockerCallback object |bc|.
|
||||
// 2. Instantiate a Blocker object |b|, passing in |bc|.
|
||||
// 3. As you receive audio, call b.ProcessChunk() to get processed audio.
|
||||
//
|
||||
// A small amount of delay is added to the first received chunk to deal with
|
||||
// the difference in chunk/block sizes. This delay is <= chunk_size.
|
||||
//
|
||||
// Ownership of window is retained by the caller. That is, Blocker makes a
|
||||
// copy of window and does not attempt to delete it.
|
||||
class Blocker {
|
||||
public:
|
||||
Blocker(size_t chunk_size,
|
||||
size_t block_size,
|
||||
int num_input_channels,
|
||||
int num_output_channels,
|
||||
const float* window,
|
||||
size_t shift_amount,
|
||||
BlockerCallback* callback);
|
||||
|
||||
void ProcessChunk(const float* const* input,
|
||||
size_t chunk_size,
|
||||
int num_input_channels,
|
||||
int num_output_channels,
|
||||
float* const* output);
|
||||
|
||||
private:
|
||||
const size_t chunk_size_;
|
||||
const size_t block_size_;
|
||||
const int num_input_channels_;
|
||||
const int num_output_channels_;
|
||||
|
||||
// The number of frames of delay to add at the beginning of the first chunk.
|
||||
const size_t initial_delay_;
|
||||
|
||||
// The frame index into the input buffer where the first block should be read
|
||||
// from. This is necessary because shift_amount_ is not necessarily a
|
||||
// multiple of chunk_size_, so blocks won't line up at the start of the
|
||||
// buffer.
|
||||
size_t frame_offset_;
|
||||
|
||||
// Since blocks nearly always overlap, there are certain blocks that require
|
||||
// frames from the end of one chunk and the beginning of the next chunk. The
|
||||
// input and output buffers are responsible for saving those frames between
|
||||
// calls to ProcessChunk().
|
||||
//
|
||||
// Both contain |initial delay| + |chunk_size| frames. The input is a fairly
|
||||
// standard FIFO, but due to the overlap-add it's harder to use an
|
||||
// AudioRingBuffer for the output.
|
||||
AudioRingBuffer input_buffer_;
|
||||
ChannelBuffer<float> output_buffer_;
|
||||
|
||||
// Space for the input block (can't wrap because of windowing).
|
||||
ChannelBuffer<float> input_block_;
|
||||
|
||||
// Space for the output block (can't wrap because of overlap/add).
|
||||
ChannelBuffer<float> output_block_;
|
||||
|
||||
rtc::scoped_ptr<float[]> window_;
|
||||
|
||||
// The amount of frames between the start of contiguous blocks. For example,
|
||||
// |shift_amount_| = |block_size_| / 2 for a Hann window.
|
||||
size_t shift_amount_;
|
||||
|
||||
BlockerCallback* callback_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_INTERNAL_BEAMFORMER_BLOCKER_H_
|
73
webrtc/common_audio/channel_buffer.cc
Normal file
73
webrtc/common_audio/channel_buffer.cc
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/common_audio/channel_buffer.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
IFChannelBuffer::IFChannelBuffer(size_t num_frames,
|
||||
int num_channels,
|
||||
size_t num_bands)
|
||||
: ivalid_(true),
|
||||
ibuf_(num_frames, num_channels, num_bands),
|
||||
fvalid_(true),
|
||||
fbuf_(num_frames, num_channels, num_bands) {}
|
||||
|
||||
ChannelBuffer<int16_t>* IFChannelBuffer::ibuf() {
|
||||
RefreshI();
|
||||
fvalid_ = false;
|
||||
return &ibuf_;
|
||||
}
|
||||
|
||||
ChannelBuffer<float>* IFChannelBuffer::fbuf() {
|
||||
RefreshF();
|
||||
ivalid_ = false;
|
||||
return &fbuf_;
|
||||
}
|
||||
|
||||
const ChannelBuffer<int16_t>* IFChannelBuffer::ibuf_const() const {
|
||||
RefreshI();
|
||||
return &ibuf_;
|
||||
}
|
||||
|
||||
const ChannelBuffer<float>* IFChannelBuffer::fbuf_const() const {
|
||||
RefreshF();
|
||||
return &fbuf_;
|
||||
}
|
||||
|
||||
void IFChannelBuffer::RefreshF() const {
|
||||
if (!fvalid_) {
|
||||
assert(ivalid_);
|
||||
const int16_t* const* int_channels = ibuf_.channels();
|
||||
float* const* float_channels = fbuf_.channels();
|
||||
for (int i = 0; i < ibuf_.num_channels(); ++i) {
|
||||
for (size_t j = 0; j < ibuf_.num_frames(); ++j) {
|
||||
float_channels[i][j] = int_channels[i][j];
|
||||
}
|
||||
}
|
||||
fvalid_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void IFChannelBuffer::RefreshI() const {
|
||||
if (!ivalid_) {
|
||||
assert(fvalid_);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
169
webrtc/common_audio/channel_buffer.h
Normal file
169
webrtc/common_audio/channel_buffer.h
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_CHANNEL_BUFFER_H_
|
||||
#define WEBRTC_MODULES_AUDIO_PROCESSING_CHANNEL_BUFFER_H_
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/base/scoped_ptr.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, 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(size_t num_frames,
|
||||
int num_channels,
|
||||
size_t 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) {
|
||||
for (int i = 0; i < num_channels_; ++i) {
|
||||
for (size_t 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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(size_t band) const {
|
||||
RTC_DCHECK_LT(band, num_bands_);
|
||||
return &channels_[band * num_channels_];
|
||||
}
|
||||
T* const* channels(size_t band) {
|
||||
const ChannelBuffer<T>* t = this;
|
||||
return const_cast<T* const*>(t->channels(band));
|
||||
}
|
||||
|
||||
// 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 {
|
||||
RTC_DCHECK_LT(channel, num_channels_);
|
||||
RTC_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, size_t start_frame) const {
|
||||
RTC_DCHECK_LT(start_frame, num_frames_);
|
||||
for (int i = 0; i < num_channels_; ++i)
|
||||
slice[i] = &channels_[i][start_frame];
|
||||
return slice;
|
||||
}
|
||||
T** Slice(T** slice, size_t start_frame) {
|
||||
const ChannelBuffer<T>* t = this;
|
||||
return const_cast<T**>(t->Slice(slice, start_frame));
|
||||
}
|
||||
|
||||
size_t num_frames() const { return num_frames_; }
|
||||
size_t num_frames_per_band() const { return num_frames_per_band_; }
|
||||
int num_channels() const { return num_channels_; }
|
||||
size_t num_bands() const { return num_bands_; }
|
||||
size_t size() const {return num_frames_ * num_channels_; }
|
||||
|
||||
void SetDataForTesting(const T* data, size_t size) {
|
||||
RTC_CHECK_EQ(size, this->size());
|
||||
memcpy(data_.get(), data, size * sizeof(*data));
|
||||
}
|
||||
|
||||
private:
|
||||
rtc::scoped_ptr<T[]> data_;
|
||||
rtc::scoped_ptr<T* []> channels_;
|
||||
rtc::scoped_ptr<T* []> bands_;
|
||||
const size_t num_frames_;
|
||||
const size_t num_frames_per_band_;
|
||||
const int num_channels_;
|
||||
const size_t num_bands_;
|
||||
};
|
||||
|
||||
// One int16_t and one float ChannelBuffer that are kept in sync. The sync is
|
||||
// broken when someone requests write access to either ChannelBuffer, and
|
||||
// reestablished when someone requests the outdated ChannelBuffer. It is
|
||||
// therefore safe to use the return value of ibuf_const() and fbuf_const()
|
||||
// until the next call to ibuf() or fbuf(), and the return value of ibuf() and
|
||||
// fbuf() until the next call to any of the other functions.
|
||||
class IFChannelBuffer {
|
||||
public:
|
||||
IFChannelBuffer(size_t num_frames, int num_channels, size_t num_bands = 1);
|
||||
|
||||
ChannelBuffer<int16_t>* ibuf();
|
||||
ChannelBuffer<float>* fbuf();
|
||||
const ChannelBuffer<int16_t>* ibuf_const() const;
|
||||
const ChannelBuffer<float>* fbuf_const() const;
|
||||
|
||||
size_t num_frames() const { return ibuf_.num_frames(); }
|
||||
size_t num_frames_per_band() const { return ibuf_.num_frames_per_band(); }
|
||||
int num_channels() const { return ibuf_.num_channels(); }
|
||||
size_t num_bands() const { return ibuf_.num_bands(); }
|
||||
|
||||
private:
|
||||
void RefreshF() const;
|
||||
void RefreshI() const;
|
||||
|
||||
mutable bool ivalid_;
|
||||
mutable ChannelBuffer<int16_t> ibuf_;
|
||||
mutable bool fvalid_;
|
||||
mutable ChannelBuffer<float> fbuf_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_CHANNEL_BUFFER_H_
|
1332
webrtc/common_audio/fft4g.c
Normal file
1332
webrtc/common_audio/fft4g.c
Normal file
File diff suppressed because it is too large
Load Diff
25
webrtc/common_audio/fft4g.h
Normal file
25
webrtc/common_audio/fft4g.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_FFT4G_H_
|
||||
#define WEBRTC_COMMON_AUDIO_FFT4G_H_
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Refer to fft4g.c for documentation.
|
||||
void WebRtc_rdft(size_t n, int isgn, float *a, size_t *ip, float *w);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_FFT4G_H_
|
116
webrtc/common_audio/fir_filter.cc
Normal file
116
webrtc/common_audio/fir_filter.cc
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/common_audio/fir_filter.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/common_audio/fir_filter_neon.h"
|
||||
#include "webrtc/common_audio/fir_filter_sse.h"
|
||||
#include "webrtc/system_wrappers/interface/cpu_features_wrapper.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class FIRFilterC : public FIRFilter {
|
||||
public:
|
||||
FIRFilterC(const float* coefficients,
|
||||
size_t coefficients_length);
|
||||
|
||||
void Filter(const float* in, size_t length, float* out) override;
|
||||
|
||||
private:
|
||||
size_t coefficients_length_;
|
||||
size_t state_length_;
|
||||
rtc::scoped_ptr<float[]> coefficients_;
|
||||
rtc::scoped_ptr<float[]> state_;
|
||||
};
|
||||
|
||||
FIRFilter* FIRFilter::Create(const float* coefficients,
|
||||
size_t coefficients_length,
|
||||
size_t max_input_length) {
|
||||
if (!coefficients || coefficients_length <= 0 || max_input_length <= 0) {
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FIRFilter* filter = NULL;
|
||||
// If we know the minimum architecture at compile time, avoid CPU detection.
|
||||
#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
#if defined(__SSE2__)
|
||||
filter =
|
||||
new FIRFilterSSE2(coefficients, coefficients_length, max_input_length);
|
||||
#else
|
||||
// x86 CPU detection required.
|
||||
if (WebRtc_GetCPUInfo(kSSE2)) {
|
||||
filter =
|
||||
new FIRFilterSSE2(coefficients, coefficients_length, max_input_length);
|
||||
} else {
|
||||
filter = new FIRFilterC(coefficients, coefficients_length);
|
||||
}
|
||||
#endif
|
||||
#elif defined(WEBRTC_HAS_NEON)
|
||||
filter =
|
||||
new FIRFilterNEON(coefficients, coefficients_length, max_input_length);
|
||||
#elif defined(WEBRTC_DETECT_NEON)
|
||||
if (WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) {
|
||||
filter =
|
||||
new FIRFilterNEON(coefficients, coefficients_length, max_input_length);
|
||||
} else {
|
||||
filter = new FIRFilterC(coefficients, coefficients_length);
|
||||
}
|
||||
#else
|
||||
filter = new FIRFilterC(coefficients, coefficients_length);
|
||||
#endif
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
FIRFilterC::FIRFilterC(const float* coefficients, size_t coefficients_length)
|
||||
: coefficients_length_(coefficients_length),
|
||||
state_length_(coefficients_length - 1),
|
||||
coefficients_(new float[coefficients_length_]),
|
||||
state_(new float[state_length_]) {
|
||||
for (size_t i = 0; i < coefficients_length_; ++i) {
|
||||
coefficients_[i] = coefficients[coefficients_length_ - i - 1];
|
||||
}
|
||||
memset(state_.get(), 0, state_length_ * sizeof(state_[0]));
|
||||
}
|
||||
|
||||
void FIRFilterC::Filter(const float* in, size_t length, float* out) {
|
||||
assert(length > 0);
|
||||
|
||||
// Convolves the input signal |in| with the filter kernel |coefficients_|
|
||||
// taking into account the previous state.
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
out[i] = 0.f;
|
||||
size_t j;
|
||||
for (j = 0; state_length_ > i && j < state_length_ - i; ++j) {
|
||||
out[i] += state_[i + j] * coefficients_[j];
|
||||
}
|
||||
for (; j < coefficients_length_; ++j) {
|
||||
out[i] += in[j + i - state_length_] * coefficients_[j];
|
||||
}
|
||||
}
|
||||
|
||||
// Update current state.
|
||||
if (length >= state_length_) {
|
||||
memcpy(
|
||||
state_.get(), &in[length - state_length_], state_length_ * sizeof(*in));
|
||||
} else {
|
||||
memmove(state_.get(),
|
||||
&state_[length],
|
||||
(state_length_ - length) * sizeof(state_[0]));
|
||||
memcpy(&state_[state_length_ - length], in, length * sizeof(*in));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
40
webrtc/common_audio/fir_filter.h
Normal file
40
webrtc/common_audio/fir_filter.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_FIR_FILTER_H_
|
||||
#define WEBRTC_COMMON_AUDIO_FIR_FILTER_H_
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Finite Impulse Response filter using floating-point arithmetic.
|
||||
class FIRFilter {
|
||||
public:
|
||||
// Creates a filter with the given coefficients. All initial state values will
|
||||
// be zeros.
|
||||
// The length of the chunks fed to the filter should never be greater than
|
||||
// |max_input_length|. This is needed because, when vectorizing it is
|
||||
// necessary to concatenate the input after the state, and resizing this array
|
||||
// dynamically is expensive.
|
||||
static FIRFilter* Create(const float* coefficients,
|
||||
size_t coefficients_length,
|
||||
size_t max_input_length);
|
||||
|
||||
virtual ~FIRFilter() {}
|
||||
|
||||
// Filters the |in| data supplied.
|
||||
// |out| must be previously allocated and it must be at least of |length|.
|
||||
virtual void Filter(const float* in, size_t length, float* out) = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_FIR_FILTER_H_
|
72
webrtc/common_audio/fir_filter_neon.cc
Normal file
72
webrtc/common_audio/fir_filter_neon.cc
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/common_audio/fir_filter_neon.h"
|
||||
|
||||
#include <arm_neon.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "webrtc/system_wrappers/interface/aligned_malloc.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
FIRFilterNEON::FIRFilterNEON(const float* coefficients,
|
||||
size_t coefficients_length,
|
||||
size_t max_input_length)
|
||||
: // Closest higher multiple of four.
|
||||
coefficients_length_((coefficients_length + 3) & ~0x03),
|
||||
state_length_(coefficients_length_ - 1),
|
||||
coefficients_(static_cast<float*>(
|
||||
AlignedMalloc(sizeof(float) * coefficients_length_, 16))),
|
||||
state_(static_cast<float*>(
|
||||
AlignedMalloc(sizeof(float) * (max_input_length + state_length_),
|
||||
16))) {
|
||||
// Add zeros at the end of the coefficients.
|
||||
size_t padding = coefficients_length_ - coefficients_length;
|
||||
memset(coefficients_.get(), 0.f, padding * sizeof(coefficients_[0]));
|
||||
// The coefficients are reversed to compensate for the order in which the
|
||||
// input samples are acquired (most recent last).
|
||||
for (size_t i = 0; i < coefficients_length; ++i) {
|
||||
coefficients_[i + padding] = coefficients[coefficients_length - i - 1];
|
||||
}
|
||||
memset(state_.get(),
|
||||
0.f,
|
||||
(max_input_length + state_length_) * sizeof(state_[0]));
|
||||
}
|
||||
|
||||
void FIRFilterNEON::Filter(const float* in, size_t length, float* out) {
|
||||
assert(length > 0);
|
||||
|
||||
memcpy(&state_[state_length_], in, length * sizeof(*in));
|
||||
|
||||
// Convolves the input signal |in| with the filter kernel |coefficients_|
|
||||
// taking into account the previous state.
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
float* in_ptr = &state_[i];
|
||||
float* coef_ptr = coefficients_.get();
|
||||
|
||||
float32x4_t m_sum = vmovq_n_f32(0);
|
||||
float32x4_t m_in;
|
||||
|
||||
for (size_t j = 0; j < coefficients_length_; j += 4) {
|
||||
m_in = vld1q_f32(in_ptr + j);
|
||||
m_sum = vmlaq_f32(m_sum, m_in, vld1q_f32(coef_ptr + j));
|
||||
}
|
||||
|
||||
float32x2_t m_half = vadd_f32(vget_high_f32(m_sum), vget_low_f32(m_sum));
|
||||
out[i] = vget_lane_f32(vpadd_f32(m_half, m_half), 0);
|
||||
}
|
||||
|
||||
// Update current state.
|
||||
memmove(state_.get(), &state_[length], state_length_ * sizeof(state_[0]));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
37
webrtc/common_audio/fir_filter_neon.h
Normal file
37
webrtc/common_audio/fir_filter_neon.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_FIR_FILTER_NEON_H_
|
||||
#define WEBRTC_COMMON_AUDIO_FIR_FILTER_NEON_H_
|
||||
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/common_audio/fir_filter.h"
|
||||
#include "webrtc/system_wrappers/interface/aligned_malloc.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class FIRFilterNEON : public FIRFilter {
|
||||
public:
|
||||
FIRFilterNEON(const float* coefficients,
|
||||
size_t coefficients_length,
|
||||
size_t max_input_length);
|
||||
|
||||
void Filter(const float* in, size_t length, float* out) override;
|
||||
|
||||
private:
|
||||
size_t coefficients_length_;
|
||||
size_t state_length_;
|
||||
rtc::scoped_ptr<float[], AlignedFreeDeleter> coefficients_;
|
||||
rtc::scoped_ptr<float[], AlignedFreeDeleter> state_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_FIR_FILTER_NEON_H_
|
80
webrtc/common_audio/fir_filter_sse.cc
Normal file
80
webrtc/common_audio/fir_filter_sse.cc
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/common_audio/fir_filter_sse.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <xmmintrin.h>
|
||||
|
||||
#include "webrtc/system_wrappers/interface/aligned_malloc.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
FIRFilterSSE2::FIRFilterSSE2(const float* coefficients,
|
||||
size_t coefficients_length,
|
||||
size_t max_input_length)
|
||||
: // Closest higher multiple of four.
|
||||
coefficients_length_((coefficients_length + 3) & ~0x03),
|
||||
state_length_(coefficients_length_ - 1),
|
||||
coefficients_(static_cast<float*>(
|
||||
AlignedMalloc(sizeof(float) * coefficients_length_, 16))),
|
||||
state_(static_cast<float*>(
|
||||
AlignedMalloc(sizeof(float) * (max_input_length + state_length_),
|
||||
16))) {
|
||||
// Add zeros at the end of the coefficients.
|
||||
size_t padding = coefficients_length_ - coefficients_length;
|
||||
memset(coefficients_.get(), 0, padding * sizeof(coefficients_[0]));
|
||||
// The coefficients are reversed to compensate for the order in which the
|
||||
// input samples are acquired (most recent last).
|
||||
for (size_t i = 0; i < coefficients_length; ++i) {
|
||||
coefficients_[i + padding] = coefficients[coefficients_length - i - 1];
|
||||
}
|
||||
memset(state_.get(),
|
||||
0,
|
||||
(max_input_length + state_length_) * sizeof(state_[0]));
|
||||
}
|
||||
|
||||
void FIRFilterSSE2::Filter(const float* in, size_t length, float* out) {
|
||||
assert(length > 0);
|
||||
|
||||
memcpy(&state_[state_length_], in, length * sizeof(*in));
|
||||
|
||||
// Convolves the input signal |in| with the filter kernel |coefficients_|
|
||||
// taking into account the previous state.
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
float* in_ptr = &state_[i];
|
||||
float* coef_ptr = coefficients_.get();
|
||||
|
||||
__m128 m_sum = _mm_setzero_ps();
|
||||
__m128 m_in;
|
||||
|
||||
// Depending on if the pointer is aligned with 16 bytes or not it is loaded
|
||||
// differently.
|
||||
if (reinterpret_cast<uintptr_t>(in_ptr) & 0x0F) {
|
||||
for (size_t j = 0; j < coefficients_length_; j += 4) {
|
||||
m_in = _mm_loadu_ps(in_ptr + j);
|
||||
m_sum = _mm_add_ps(m_sum, _mm_mul_ps(m_in, _mm_load_ps(coef_ptr + j)));
|
||||
}
|
||||
} else {
|
||||
for (size_t j = 0; j < coefficients_length_; j += 4) {
|
||||
m_in = _mm_load_ps(in_ptr + j);
|
||||
m_sum = _mm_add_ps(m_sum, _mm_mul_ps(m_in, _mm_load_ps(coef_ptr + j)));
|
||||
}
|
||||
}
|
||||
m_sum = _mm_add_ps(_mm_movehl_ps(m_sum, m_sum), m_sum);
|
||||
_mm_store_ss(out + i, _mm_add_ss(m_sum, _mm_shuffle_ps(m_sum, m_sum, 1)));
|
||||
}
|
||||
|
||||
// Update current state.
|
||||
memmove(state_.get(), &state_[length], state_length_ * sizeof(state_[0]));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
37
webrtc/common_audio/fir_filter_sse.h
Normal file
37
webrtc/common_audio/fir_filter_sse.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_FIR_FILTER_SSE_H_
|
||||
#define WEBRTC_COMMON_AUDIO_FIR_FILTER_SSE_H_
|
||||
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/common_audio/fir_filter.h"
|
||||
#include "webrtc/system_wrappers/interface/aligned_malloc.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class FIRFilterSSE2 : public FIRFilter {
|
||||
public:
|
||||
FIRFilterSSE2(const float* coefficients,
|
||||
size_t coefficients_length,
|
||||
size_t max_input_length);
|
||||
|
||||
void Filter(const float* in, size_t length, float* out) override;
|
||||
|
||||
private:
|
||||
size_t coefficients_length_;
|
||||
size_t state_length_;
|
||||
rtc::scoped_ptr<float[], AlignedFreeDeleter> coefficients_;
|
||||
rtc::scoped_ptr<float[], AlignedFreeDeleter> state_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_FIR_FILTER_SSE_H_
|
188
webrtc/common_audio/include/audio_util.h
Normal file
188
webrtc/common_audio/include/audio_util.h
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_INCLUDE_AUDIO_UTIL_H_
|
||||
#define WEBRTC_COMMON_AUDIO_INCLUDE_AUDIO_UTIL_H_
|
||||
|
||||
#include <limits>
|
||||
#include <cstring>
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
typedef std::numeric_limits<int16_t> limits_int16;
|
||||
|
||||
// The conversion functions use the following naming convention:
|
||||
// S16: int16_t [-32768, 32767]
|
||||
// Float: float [-1.0, 1.0]
|
||||
// FloatS16: float [-32768.0, 32767.0]
|
||||
static inline int16_t FloatToS16(float v) {
|
||||
if (v > 0)
|
||||
return v >= 1 ? limits_int16::max()
|
||||
: static_cast<int16_t>(v * limits_int16::max() + 0.5f);
|
||||
return v <= -1 ? limits_int16::min()
|
||||
: static_cast<int16_t>(-v * limits_int16::min() - 0.5f);
|
||||
}
|
||||
|
||||
static inline float S16ToFloat(int16_t v) {
|
||||
static const float kMaxInt16Inverse = 1.f / limits_int16::max();
|
||||
static const float kMinInt16Inverse = 1.f / limits_int16::min();
|
||||
return v * (v > 0 ? kMaxInt16Inverse : -kMinInt16Inverse);
|
||||
}
|
||||
|
||||
static inline int16_t FloatS16ToS16(float v) {
|
||||
static const float kMaxRound = limits_int16::max() - 0.5f;
|
||||
static const float kMinRound = limits_int16::min() + 0.5f;
|
||||
if (v > 0)
|
||||
return v >= kMaxRound ? limits_int16::max()
|
||||
: static_cast<int16_t>(v + 0.5f);
|
||||
return v <= kMinRound ? limits_int16::min() : static_cast<int16_t>(v - 0.5f);
|
||||
}
|
||||
|
||||
static inline float FloatToFloatS16(float v) {
|
||||
return v * (v > 0 ? limits_int16::max() : -limits_int16::min());
|
||||
}
|
||||
|
||||
static inline float FloatS16ToFloat(float v) {
|
||||
static const float kMaxInt16Inverse = 1.f / limits_int16::max();
|
||||
static const float kMinInt16Inverse = 1.f / limits_int16::min();
|
||||
return v * (v > 0 ? kMaxInt16Inverse : -kMinInt16Inverse);
|
||||
}
|
||||
|
||||
void FloatToS16(const float* src, size_t size, int16_t* dest);
|
||||
void S16ToFloat(const int16_t* src, size_t size, float* dest);
|
||||
void FloatS16ToS16(const float* src, size_t size, int16_t* dest);
|
||||
void FloatToFloatS16(const float* src, size_t size, float* dest);
|
||||
void FloatS16ToFloat(const float* src, size_t size, float* dest);
|
||||
|
||||
// Copy audio from |src| channels to |dest| channels unless |src| and |dest|
|
||||
// point to the same address. |src| and |dest| must have the same number of
|
||||
// channels, and there must be sufficient space allocated in |dest|.
|
||||
template <typename T>
|
||||
void CopyAudioIfNeeded(const T* const* src,
|
||||
int num_frames,
|
||||
int num_channels,
|
||||
T* const* dest) {
|
||||
for (int i = 0; i < num_channels; ++i) {
|
||||
if (src[i] != dest[i]) {
|
||||
std::copy(src[i], src[i] + num_frames, dest[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deinterleave audio from |interleaved| to the channel buffers pointed to
|
||||
// by |deinterleaved|. There must be sufficient space allocated in the
|
||||
// |deinterleaved| buffers (|num_channel| buffers with |samples_per_channel|
|
||||
// per buffer).
|
||||
template <typename T>
|
||||
void Deinterleave(const T* interleaved,
|
||||
size_t samples_per_channel,
|
||||
int num_channels,
|
||||
T* const* deinterleaved) {
|
||||
for (int i = 0; i < num_channels; ++i) {
|
||||
T* channel = deinterleaved[i];
|
||||
int interleaved_idx = i;
|
||||
for (size_t j = 0; j < samples_per_channel; ++j) {
|
||||
channel[j] = interleaved[interleaved_idx];
|
||||
interleaved_idx += num_channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Interleave audio from the channel buffers pointed to by |deinterleaved| to
|
||||
// |interleaved|. There must be sufficient space allocated in |interleaved|
|
||||
// (|samples_per_channel| * |num_channels|).
|
||||
template <typename T>
|
||||
void Interleave(const T* const* deinterleaved,
|
||||
size_t samples_per_channel,
|
||||
int num_channels,
|
||||
T* interleaved) {
|
||||
for (int i = 0; i < num_channels; ++i) {
|
||||
const T* channel = deinterleaved[i];
|
||||
int interleaved_idx = i;
|
||||
for (size_t j = 0; j < samples_per_channel; ++j) {
|
||||
interleaved[interleaved_idx] = channel[j];
|
||||
interleaved_idx += num_channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copies audio from a single channel buffer pointed to by |mono| to each
|
||||
// channel of |interleaved|. There must be sufficient space allocated in
|
||||
// |interleaved| (|samples_per_channel| * |num_channels|).
|
||||
template <typename T>
|
||||
void UpmixMonoToInterleaved(const T* mono,
|
||||
int num_frames,
|
||||
int num_channels,
|
||||
T* interleaved) {
|
||||
int interleaved_idx = 0;
|
||||
for (int i = 0; i < num_frames; ++i) {
|
||||
for (int j = 0; j < num_channels; ++j) {
|
||||
interleaved[interleaved_idx++] = mono[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Intermediate>
|
||||
void DownmixToMono(const T* const* input_channels,
|
||||
size_t num_frames,
|
||||
int num_channels,
|
||||
T* out) {
|
||||
for (size_t i = 0; i < num_frames; ++i) {
|
||||
Intermediate value = input_channels[0][i];
|
||||
for (int j = 1; j < num_channels; ++j) {
|
||||
value += input_channels[j][i];
|
||||
}
|
||||
out[i] = value / num_channels;
|
||||
}
|
||||
}
|
||||
|
||||
// Downmixes an interleaved multichannel signal to a single channel by averaging
|
||||
// all channels.
|
||||
template <typename T, typename Intermediate>
|
||||
void DownmixInterleavedToMonoImpl(const T* interleaved,
|
||||
size_t num_frames,
|
||||
int num_channels,
|
||||
T* deinterleaved) {
|
||||
RTC_DCHECK_GT(num_channels, 0);
|
||||
RTC_DCHECK_GT(num_frames, 0u);
|
||||
|
||||
const T* const end = interleaved + num_frames * num_channels;
|
||||
|
||||
while (interleaved < end) {
|
||||
const T* const frame_end = interleaved + num_channels;
|
||||
|
||||
Intermediate value = *interleaved++;
|
||||
while (interleaved < frame_end) {
|
||||
value += *interleaved++;
|
||||
}
|
||||
|
||||
*deinterleaved++ = value / num_channels;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DownmixInterleavedToMono(const T* interleaved,
|
||||
size_t num_frames,
|
||||
int num_channels,
|
||||
T* deinterleaved);
|
||||
|
||||
template <>
|
||||
void DownmixInterleavedToMono<int16_t>(const int16_t* interleaved,
|
||||
size_t num_frames,
|
||||
int num_channels,
|
||||
int16_t* deinterleaved);
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_INCLUDE_AUDIO_UTIL_H_
|
101
webrtc/common_audio/lapped_transform.cc
Normal file
101
webrtc/common_audio/lapped_transform.cc
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/common_audio/lapped_transform.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/common_audio/real_fourier.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
void LappedTransform::BlockThunk::ProcessBlock(const float* const* input,
|
||||
size_t num_frames,
|
||||
int num_input_channels,
|
||||
int num_output_channels,
|
||||
float* const* output) {
|
||||
RTC_CHECK_EQ(num_input_channels, parent_->num_in_channels_);
|
||||
RTC_CHECK_EQ(num_output_channels, parent_->num_out_channels_);
|
||||
RTC_CHECK_EQ(parent_->block_length_, num_frames);
|
||||
|
||||
for (int i = 0; i < num_input_channels; ++i) {
|
||||
memcpy(parent_->real_buf_.Row(i), input[i],
|
||||
num_frames * sizeof(*input[0]));
|
||||
parent_->fft_->Forward(parent_->real_buf_.Row(i),
|
||||
parent_->cplx_pre_.Row(i));
|
||||
}
|
||||
|
||||
size_t block_length = RealFourier::ComplexLength(
|
||||
RealFourier::FftOrder(num_frames));
|
||||
RTC_CHECK_EQ(parent_->cplx_length_, block_length);
|
||||
parent_->block_processor_->ProcessAudioBlock(parent_->cplx_pre_.Array(),
|
||||
num_input_channels,
|
||||
parent_->cplx_length_,
|
||||
num_output_channels,
|
||||
parent_->cplx_post_.Array());
|
||||
|
||||
for (int i = 0; i < num_output_channels; ++i) {
|
||||
parent_->fft_->Inverse(parent_->cplx_post_.Row(i),
|
||||
parent_->real_buf_.Row(i));
|
||||
memcpy(output[i], parent_->real_buf_.Row(i),
|
||||
num_frames * sizeof(*input[0]));
|
||||
}
|
||||
}
|
||||
|
||||
LappedTransform::LappedTransform(int num_in_channels,
|
||||
int num_out_channels,
|
||||
size_t chunk_length,
|
||||
const float* window,
|
||||
size_t block_length,
|
||||
size_t shift_amount,
|
||||
Callback* callback)
|
||||
: blocker_callback_(this),
|
||||
num_in_channels_(num_in_channels),
|
||||
num_out_channels_(num_out_channels),
|
||||
block_length_(block_length),
|
||||
chunk_length_(chunk_length),
|
||||
block_processor_(callback),
|
||||
blocker_(chunk_length_,
|
||||
block_length_,
|
||||
num_in_channels_,
|
||||
num_out_channels_,
|
||||
window,
|
||||
shift_amount,
|
||||
&blocker_callback_),
|
||||
fft_(RealFourier::Create(RealFourier::FftOrder(block_length_))),
|
||||
cplx_length_(RealFourier::ComplexLength(fft_->order())),
|
||||
real_buf_(num_in_channels,
|
||||
block_length_,
|
||||
RealFourier::kFftBufferAlignment),
|
||||
cplx_pre_(num_in_channels,
|
||||
cplx_length_,
|
||||
RealFourier::kFftBufferAlignment),
|
||||
cplx_post_(num_out_channels,
|
||||
cplx_length_,
|
||||
RealFourier::kFftBufferAlignment) {
|
||||
RTC_CHECK(num_in_channels_ > 0 && num_out_channels_ > 0);
|
||||
RTC_CHECK_GT(block_length_, 0u);
|
||||
RTC_CHECK_GT(chunk_length_, 0u);
|
||||
RTC_CHECK(block_processor_);
|
||||
|
||||
// block_length_ power of 2?
|
||||
RTC_CHECK_EQ(0u, block_length_ & (block_length_ - 1));
|
||||
}
|
||||
|
||||
void LappedTransform::ProcessChunk(const float* const* in_chunk,
|
||||
float* const* out_chunk) {
|
||||
blocker_.ProcessChunk(in_chunk, chunk_length_, num_in_channels_,
|
||||
num_out_channels_, out_chunk);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
123
webrtc/common_audio/lapped_transform.h
Normal file
123
webrtc/common_audio/lapped_transform.h
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_LAPPED_TRANSFORM_H_
|
||||
#define WEBRTC_COMMON_AUDIO_LAPPED_TRANSFORM_H_
|
||||
|
||||
#include <complex>
|
||||
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/common_audio/blocker.h"
|
||||
#include "webrtc/common_audio/real_fourier.h"
|
||||
#include "webrtc/system_wrappers/interface/aligned_array.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Helper class for audio processing modules which operate on frequency domain
|
||||
// input derived from the windowed time domain audio stream.
|
||||
//
|
||||
// The input audio chunk is sliced into possibly overlapping blocks, multiplied
|
||||
// by a window and transformed with an FFT implementation. The transformed data
|
||||
// is supplied to the given callback for processing. The processed output is
|
||||
// then inverse transformed into the time domain and spliced back into a chunk
|
||||
// which constitutes the final output of this processing module.
|
||||
class LappedTransform {
|
||||
public:
|
||||
class Callback {
|
||||
public:
|
||||
virtual ~Callback() {}
|
||||
|
||||
virtual void ProcessAudioBlock(const std::complex<float>* const* in_block,
|
||||
int num_in_channels, size_t frames,
|
||||
int num_out_channels,
|
||||
std::complex<float>* const* out_block) = 0;
|
||||
};
|
||||
|
||||
// Construct a transform instance. |chunk_length| is the number of samples in
|
||||
// each channel. |window| defines the window, owned by the caller (a copy is
|
||||
// made internally); |window| should have length equal to |block_length|.
|
||||
// |block_length| defines the length of a block, in samples.
|
||||
// |shift_amount| is in samples. |callback| is the caller-owned audio
|
||||
// processing function called for each block of the input chunk.
|
||||
LappedTransform(int num_in_channels,
|
||||
int num_out_channels,
|
||||
size_t chunk_length,
|
||||
const float* window,
|
||||
size_t block_length,
|
||||
size_t shift_amount,
|
||||
Callback* callback);
|
||||
~LappedTransform() {}
|
||||
|
||||
// Main audio processing helper method. Internally slices |in_chunk| into
|
||||
// blocks, transforms them to frequency domain, calls the callback for each
|
||||
// block and returns a de-blocked time domain chunk of audio through
|
||||
// |out_chunk|. Both buffers are caller-owned.
|
||||
void ProcessChunk(const float* const* in_chunk, float* const* out_chunk);
|
||||
|
||||
// Get the chunk length.
|
||||
//
|
||||
// The chunk length is the number of samples per channel that must be passed
|
||||
// to ProcessChunk via the parameter in_chunk.
|
||||
//
|
||||
// Returns the same chunk_length passed to the LappedTransform constructor.
|
||||
size_t chunk_length() const { return chunk_length_; }
|
||||
|
||||
// Get the number of input channels.
|
||||
//
|
||||
// This is the number of arrays that must be passed to ProcessChunk via
|
||||
// in_chunk.
|
||||
//
|
||||
// Returns the same num_in_channels passed to the LappedTransform constructor.
|
||||
int num_in_channels() const { return num_in_channels_; }
|
||||
|
||||
// Get the number of output channels.
|
||||
//
|
||||
// This is the number of arrays that must be passed to ProcessChunk via
|
||||
// out_chunk.
|
||||
//
|
||||
// Returns the same num_out_channels passed to the LappedTransform
|
||||
// constructor.
|
||||
int num_out_channels() const { return num_out_channels_; }
|
||||
|
||||
private:
|
||||
// Internal middleware callback, given to the blocker. Transforms each block
|
||||
// and hands it over to the processing method given at construction time.
|
||||
class BlockThunk : public BlockerCallback {
|
||||
public:
|
||||
explicit BlockThunk(LappedTransform* parent) : parent_(parent) {}
|
||||
|
||||
virtual void ProcessBlock(const float* const* input, size_t num_frames,
|
||||
int num_input_channels, int num_output_channels,
|
||||
float* const* output);
|
||||
|
||||
private:
|
||||
LappedTransform* const parent_;
|
||||
} blocker_callback_;
|
||||
|
||||
const int num_in_channels_;
|
||||
const int num_out_channels_;
|
||||
|
||||
const size_t block_length_;
|
||||
const size_t chunk_length_;
|
||||
|
||||
Callback* const block_processor_;
|
||||
Blocker blocker_;
|
||||
|
||||
rtc::scoped_ptr<RealFourier> fft_;
|
||||
const size_t cplx_length_;
|
||||
AlignedArray<float> real_buf_;
|
||||
AlignedArray<std::complex<float> > cplx_pre_;
|
||||
AlignedArray<std::complex<float> > cplx_post_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_LAPPED_TRANSFORM_H_
|
||||
|
57
webrtc/common_audio/real_fourier.cc
Normal file
57
webrtc/common_audio/real_fourier.cc
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/common_audio/real_fourier.h"
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/common_audio/real_fourier_ooura.h"
|
||||
#include "webrtc/common_audio/real_fourier_openmax.h"
|
||||
#include "webrtc/common_audio/signal_processing/include/spl_inl.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
using std::complex;
|
||||
|
||||
const int RealFourier::kFftBufferAlignment = 32;
|
||||
|
||||
rtc::scoped_ptr<RealFourier> RealFourier::Create(int fft_order) {
|
||||
#if defined(RTC_USE_OPENMAX_DL)
|
||||
return rtc::scoped_ptr<RealFourier>(new RealFourierOpenmax(fft_order));
|
||||
#else
|
||||
return rtc::scoped_ptr<RealFourier>(new RealFourierOoura(fft_order));
|
||||
#endif
|
||||
}
|
||||
|
||||
int RealFourier::FftOrder(size_t length) {
|
||||
RTC_CHECK_GT(length, 0U);
|
||||
return WebRtcSpl_GetSizeInBits(static_cast<uint32_t>(length - 1));
|
||||
}
|
||||
|
||||
size_t RealFourier::FftLength(int order) {
|
||||
RTC_CHECK_GE(order, 0);
|
||||
return static_cast<size_t>(1 << order);
|
||||
}
|
||||
|
||||
size_t RealFourier::ComplexLength(int order) {
|
||||
return FftLength(order) / 2 + 1;
|
||||
}
|
||||
|
||||
RealFourier::fft_real_scoper RealFourier::AllocRealBuffer(int count) {
|
||||
return fft_real_scoper(static_cast<float*>(
|
||||
AlignedMalloc(sizeof(float) * count, kFftBufferAlignment)));
|
||||
}
|
||||
|
||||
RealFourier::fft_cplx_scoper RealFourier::AllocCplxBuffer(int count) {
|
||||
return fft_cplx_scoper(static_cast<complex<float>*>(
|
||||
AlignedMalloc(sizeof(complex<float>) * count, kFftBufferAlignment)));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
75
webrtc/common_audio/real_fourier.h
Normal file
75
webrtc/common_audio/real_fourier.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_REAL_FOURIER_H_
|
||||
#define WEBRTC_COMMON_AUDIO_REAL_FOURIER_H_
|
||||
|
||||
#include <complex>
|
||||
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/system_wrappers/interface/aligned_malloc.h"
|
||||
|
||||
// Uniform interface class for the real DFT and its inverse, for power-of-2
|
||||
// input lengths. Also contains helper functions for buffer allocation, taking
|
||||
// care of any memory alignment requirements the underlying library might have.
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RealFourier {
|
||||
public:
|
||||
// Shorthand typenames for the scopers used by the buffer allocation helpers.
|
||||
typedef rtc::scoped_ptr<float[], AlignedFreeDeleter> fft_real_scoper;
|
||||
typedef rtc::scoped_ptr<std::complex<float>[], AlignedFreeDeleter>
|
||||
fft_cplx_scoper;
|
||||
|
||||
// The alignment required for all input and output buffers, in bytes.
|
||||
static const int kFftBufferAlignment;
|
||||
|
||||
// Construct a wrapper instance for the given input order, which must be
|
||||
// between 1 and kMaxFftOrder, inclusively.
|
||||
static rtc::scoped_ptr<RealFourier> Create(int fft_order);
|
||||
virtual ~RealFourier() {};
|
||||
|
||||
// Helper to compute the smallest FFT order (a power of 2) which will contain
|
||||
// the given input length.
|
||||
static int FftOrder(size_t length);
|
||||
|
||||
// Helper to compute the input length from the FFT order.
|
||||
static size_t FftLength(int order);
|
||||
|
||||
// Helper to compute the exact length, in complex floats, of the transform
|
||||
// output (i.e. |2^order / 2 + 1|).
|
||||
static size_t ComplexLength(int order);
|
||||
|
||||
// Buffer allocation helpers. The buffers are large enough to hold |count|
|
||||
// floats/complexes and suitably aligned for use by the implementation.
|
||||
// The returned scopers are set up with proper deleters; the caller owns
|
||||
// the allocated memory.
|
||||
static fft_real_scoper AllocRealBuffer(int count);
|
||||
static fft_cplx_scoper AllocCplxBuffer(int count);
|
||||
|
||||
// Main forward transform interface. The output array need only be big
|
||||
// enough for |2^order / 2 + 1| elements - the conjugate pairs are not
|
||||
// returned. Input and output must be properly aligned (e.g. through
|
||||
// AllocRealBuffer and AllocCplxBuffer) and input length must be
|
||||
// |2^order| (same as given at construction time).
|
||||
virtual void Forward(const float* src, std::complex<float>* dest) const = 0;
|
||||
|
||||
// Inverse transform. Same input format as output above, conjugate pairs
|
||||
// not needed.
|
||||
virtual void Inverse(const std::complex<float>* src, float* dest) const = 0;
|
||||
|
||||
virtual int order() const = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_REAL_FOURIER_H_
|
||||
|
85
webrtc/common_audio/real_fourier_ooura.cc
Normal file
85
webrtc/common_audio/real_fourier_ooura.cc
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/common_audio/real_fourier_ooura.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/common_audio/fft4g.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
using std::complex;
|
||||
|
||||
namespace {
|
||||
|
||||
void Conjugate(complex<float>* array, size_t complex_length) {
|
||||
std::for_each(array, array + complex_length,
|
||||
[=](complex<float>& v) { v = std::conj(v); });
|
||||
}
|
||||
|
||||
size_t ComputeWorkIpSize(size_t fft_length) {
|
||||
return static_cast<size_t>(2 + std::ceil(std::sqrt(
|
||||
static_cast<float>(fft_length))));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RealFourierOoura::RealFourierOoura(int fft_order)
|
||||
: order_(fft_order),
|
||||
length_(FftLength(order_)),
|
||||
complex_length_(ComplexLength(order_)),
|
||||
// Zero-initializing work_ip_ will cause rdft to initialize these work
|
||||
// arrays on the first call.
|
||||
work_ip_(new size_t[ComputeWorkIpSize(length_)]()),
|
||||
work_w_(new float[complex_length_]()) {
|
||||
RTC_CHECK_GE(fft_order, 1);
|
||||
}
|
||||
|
||||
void RealFourierOoura::Forward(const float* src, complex<float>* dest) const {
|
||||
{
|
||||
// This cast is well-defined since C++11. See "Non-static data members" at:
|
||||
// http://en.cppreference.com/w/cpp/numeric/complex
|
||||
auto dest_float = reinterpret_cast<float*>(dest);
|
||||
std::copy(src, src + length_, dest_float);
|
||||
WebRtc_rdft(length_, 1, dest_float, work_ip_.get(), work_w_.get());
|
||||
}
|
||||
|
||||
// Ooura places real[n/2] in imag[0].
|
||||
dest[complex_length_ - 1] = complex<float>(dest[0].imag(), 0.0f);
|
||||
dest[0] = complex<float>(dest[0].real(), 0.0f);
|
||||
// Ooura returns the conjugate of the usual Fourier definition.
|
||||
Conjugate(dest, complex_length_);
|
||||
}
|
||||
|
||||
void RealFourierOoura::Inverse(const complex<float>* src, float* dest) const {
|
||||
{
|
||||
auto dest_complex = reinterpret_cast<complex<float>*>(dest);
|
||||
// The real output array is shorter than the input complex array by one
|
||||
// complex element.
|
||||
const size_t dest_complex_length = complex_length_ - 1;
|
||||
std::copy(src, src + dest_complex_length, dest_complex);
|
||||
// Restore Ooura's conjugate definition.
|
||||
Conjugate(dest_complex, dest_complex_length);
|
||||
// Restore real[n/2] to imag[0].
|
||||
dest_complex[0] = complex<float>(dest_complex[0].real(),
|
||||
src[complex_length_ - 1].real());
|
||||
}
|
||||
|
||||
WebRtc_rdft(length_, -1, dest, work_ip_.get(), work_w_.get());
|
||||
|
||||
// Ooura returns a scaled version.
|
||||
const float scale = 2.0f / length_;
|
||||
std::for_each(dest, dest + length_, [scale](float& v) { v *= scale; });
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
45
webrtc/common_audio/real_fourier_ooura.h
Normal file
45
webrtc/common_audio/real_fourier_ooura.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_REAL_FOURIER_OOURA_H_
|
||||
#define WEBRTC_COMMON_AUDIO_REAL_FOURIER_OOURA_H_
|
||||
|
||||
#include <complex>
|
||||
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/common_audio/real_fourier.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RealFourierOoura : public RealFourier {
|
||||
public:
|
||||
explicit RealFourierOoura(int fft_order);
|
||||
|
||||
void Forward(const float* src, std::complex<float>* dest) const override;
|
||||
void Inverse(const std::complex<float>* src, float* dest) const override;
|
||||
|
||||
int order() const override {
|
||||
return order_;
|
||||
}
|
||||
|
||||
private:
|
||||
const int order_;
|
||||
const size_t length_;
|
||||
const size_t complex_length_;
|
||||
// These are work arrays for Ooura. The names are based on the comments in
|
||||
// fft4g.c.
|
||||
const rtc::scoped_ptr<size_t[]> work_ip_;
|
||||
const rtc::scoped_ptr<float[]> work_w_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_REAL_FOURIER_OOURA_H_
|
||||
|
44
webrtc/common_audio/real_fourier_openmax.h
Normal file
44
webrtc/common_audio/real_fourier_openmax.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_REAL_FOURIER_OPENMAX_H_
|
||||
#define WEBRTC_COMMON_AUDIO_REAL_FOURIER_OPENMAX_H_
|
||||
|
||||
#include <complex>
|
||||
|
||||
#include "webrtc/common_audio/real_fourier.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RealFourierOpenmax : public RealFourier {
|
||||
public:
|
||||
explicit RealFourierOpenmax(int fft_order);
|
||||
~RealFourierOpenmax() override;
|
||||
|
||||
void Forward(const float* src, std::complex<float>* dest) const override;
|
||||
void Inverse(const std::complex<float>* src, float* dest) const override;
|
||||
|
||||
int order() const override {
|
||||
return order_;
|
||||
}
|
||||
|
||||
private:
|
||||
// Basically a forward declare of OMXFFTSpec_R_F32. To get rid of the
|
||||
// dependency on openmax.
|
||||
typedef void OMXFFTSpec_R_F32_;
|
||||
const int order_;
|
||||
|
||||
OMXFFTSpec_R_F32_* const omx_spec_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_REAL_FOURIER_OPENMAX_H_
|
||||
|
52
webrtc/common_audio/resampler/include/push_resampler.h
Normal file
52
webrtc/common_audio/resampler/include/push_resampler.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_
|
||||
#define WEBRTC_COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_
|
||||
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class PushSincResampler;
|
||||
|
||||
// Wraps PushSincResampler to provide stereo support.
|
||||
// TODO(ajm): add support for an arbitrary number of channels.
|
||||
template <typename T>
|
||||
class PushResampler {
|
||||
public:
|
||||
PushResampler();
|
||||
virtual ~PushResampler();
|
||||
|
||||
// Must be called whenever the parameters change. Free to be called at any
|
||||
// time as it is a no-op if parameters have not changed since the last call.
|
||||
int InitializeIfNeeded(int src_sample_rate_hz, int dst_sample_rate_hz,
|
||||
int num_channels);
|
||||
|
||||
// Returns the total number of samples provided in destination (e.g. 32 kHz,
|
||||
// 2 channel audio gives 640 samples).
|
||||
int Resample(const T* src, size_t src_length, T* dst, size_t dst_capacity);
|
||||
|
||||
private:
|
||||
rtc::scoped_ptr<PushSincResampler> sinc_resampler_;
|
||||
rtc::scoped_ptr<PushSincResampler> sinc_resampler_right_;
|
||||
int src_sample_rate_hz_;
|
||||
int dst_sample_rate_hz_;
|
||||
int num_channels_;
|
||||
rtc::scoped_ptr<T[]> src_left_;
|
||||
rtc::scoped_ptr<T[]> src_right_;
|
||||
rtc::scoped_ptr<T[]> dst_left_;
|
||||
rtc::scoped_ptr<T[]> dst_right_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_
|
95
webrtc/common_audio/resampler/include/resampler.h
Normal file
95
webrtc/common_audio/resampler/include/resampler.h
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* A wrapper for resampling a numerous amount of sampling combinations.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_RESAMPLER_RESAMPLER_H_
|
||||
#define WEBRTC_RESAMPLER_RESAMPLER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// All methods return 0 on success and -1 on failure.
|
||||
class Resampler
|
||||
{
|
||||
|
||||
public:
|
||||
Resampler();
|
||||
Resampler(int inFreq, int outFreq, int num_channels);
|
||||
~Resampler();
|
||||
|
||||
// Reset all states
|
||||
int Reset(int inFreq, int outFreq, int num_channels);
|
||||
|
||||
// Reset all states if any parameter has changed
|
||||
int ResetIfNeeded(int inFreq, int outFreq, int num_channels);
|
||||
|
||||
// Resample samplesIn to samplesOut.
|
||||
int Push(const int16_t* samplesIn, size_t lengthIn, int16_t* samplesOut,
|
||||
size_t maxLen, size_t &outLen);
|
||||
|
||||
private:
|
||||
enum ResamplerMode
|
||||
{
|
||||
kResamplerMode1To1,
|
||||
kResamplerMode1To2,
|
||||
kResamplerMode1To3,
|
||||
kResamplerMode1To4,
|
||||
kResamplerMode1To6,
|
||||
kResamplerMode1To12,
|
||||
kResamplerMode2To3,
|
||||
kResamplerMode2To11,
|
||||
kResamplerMode4To11,
|
||||
kResamplerMode8To11,
|
||||
kResamplerMode11To16,
|
||||
kResamplerMode11To32,
|
||||
kResamplerMode2To1,
|
||||
kResamplerMode3To1,
|
||||
kResamplerMode4To1,
|
||||
kResamplerMode6To1,
|
||||
kResamplerMode12To1,
|
||||
kResamplerMode3To2,
|
||||
kResamplerMode11To2,
|
||||
kResamplerMode11To4,
|
||||
kResamplerMode11To8
|
||||
};
|
||||
|
||||
// Generic pointers since we don't know what states we'll need
|
||||
void* state1_;
|
||||
void* state2_;
|
||||
void* state3_;
|
||||
|
||||
// Storage if needed
|
||||
int16_t* in_buffer_;
|
||||
int16_t* out_buffer_;
|
||||
size_t in_buffer_size_;
|
||||
size_t out_buffer_size_;
|
||||
size_t in_buffer_size_max_;
|
||||
size_t out_buffer_size_max_;
|
||||
|
||||
int my_in_frequency_khz_;
|
||||
int my_out_frequency_khz_;
|
||||
ResamplerMode my_mode_;
|
||||
int num_channels_;
|
||||
|
||||
// Extra instance for stereo
|
||||
Resampler* slave_left_;
|
||||
Resampler* slave_right_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_RESAMPLER_RESAMPLER_H_
|
110
webrtc/common_audio/resampler/push_resampler.cc
Normal file
110
webrtc/common_audio/resampler/push_resampler.cc
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/common_audio/resampler/include/push_resampler.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "webrtc/common_audio/include/audio_util.h"
|
||||
#include "webrtc/common_audio/resampler/include/resampler.h"
|
||||
#include "webrtc/common_audio/resampler/push_sinc_resampler.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
template <typename T>
|
||||
PushResampler<T>::PushResampler()
|
||||
: src_sample_rate_hz_(0),
|
||||
dst_sample_rate_hz_(0),
|
||||
num_channels_(0) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
PushResampler<T>::~PushResampler() {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int PushResampler<T>::InitializeIfNeeded(int src_sample_rate_hz,
|
||||
int dst_sample_rate_hz,
|
||||
int num_channels) {
|
||||
if (src_sample_rate_hz == src_sample_rate_hz_ &&
|
||||
dst_sample_rate_hz == dst_sample_rate_hz_ &&
|
||||
num_channels == num_channels_)
|
||||
// No-op if settings haven't changed.
|
||||
return 0;
|
||||
|
||||
if (src_sample_rate_hz <= 0 || dst_sample_rate_hz <= 0 ||
|
||||
num_channels <= 0 || num_channels > 2)
|
||||
return -1;
|
||||
|
||||
src_sample_rate_hz_ = src_sample_rate_hz;
|
||||
dst_sample_rate_hz_ = dst_sample_rate_hz;
|
||||
num_channels_ = num_channels;
|
||||
|
||||
const size_t src_size_10ms_mono =
|
||||
static_cast<size_t>(src_sample_rate_hz / 100);
|
||||
const size_t dst_size_10ms_mono =
|
||||
static_cast<size_t>(dst_sample_rate_hz / 100);
|
||||
sinc_resampler_.reset(new PushSincResampler(src_size_10ms_mono,
|
||||
dst_size_10ms_mono));
|
||||
if (num_channels_ == 2) {
|
||||
src_left_.reset(new T[src_size_10ms_mono]);
|
||||
src_right_.reset(new T[src_size_10ms_mono]);
|
||||
dst_left_.reset(new T[dst_size_10ms_mono]);
|
||||
dst_right_.reset(new T[dst_size_10ms_mono]);
|
||||
sinc_resampler_right_.reset(new PushSincResampler(src_size_10ms_mono,
|
||||
dst_size_10ms_mono));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int PushResampler<T>::Resample(const T* src, size_t src_length, T* dst,
|
||||
size_t dst_capacity) {
|
||||
const size_t src_size_10ms =
|
||||
static_cast<size_t>(src_sample_rate_hz_ * num_channels_ / 100);
|
||||
const size_t dst_size_10ms =
|
||||
static_cast<size_t>(dst_sample_rate_hz_ * num_channels_ / 100);
|
||||
if (src_length != src_size_10ms || dst_capacity < dst_size_10ms)
|
||||
return -1;
|
||||
|
||||
if (src_sample_rate_hz_ == dst_sample_rate_hz_) {
|
||||
// The old resampler provides this memcpy facility in the case of matching
|
||||
// sample rates, so reproduce it here for the sinc resampler.
|
||||
memcpy(dst, src, src_length * sizeof(T));
|
||||
return static_cast<int>(src_length);
|
||||
}
|
||||
if (num_channels_ == 2) {
|
||||
const size_t src_length_mono = src_length / num_channels_;
|
||||
const size_t dst_capacity_mono = dst_capacity / num_channels_;
|
||||
T* deinterleaved[] = {src_left_.get(), src_right_.get()};
|
||||
Deinterleave(src, src_length_mono, num_channels_, deinterleaved);
|
||||
|
||||
size_t dst_length_mono =
|
||||
sinc_resampler_->Resample(src_left_.get(), src_length_mono,
|
||||
dst_left_.get(), dst_capacity_mono);
|
||||
sinc_resampler_right_->Resample(src_right_.get(), src_length_mono,
|
||||
dst_right_.get(), dst_capacity_mono);
|
||||
|
||||
deinterleaved[0] = dst_left_.get();
|
||||
deinterleaved[1] = dst_right_.get();
|
||||
Interleave(deinterleaved, dst_length_mono, num_channels_, dst);
|
||||
return static_cast<int>(dst_length_mono * num_channels_);
|
||||
} else {
|
||||
return static_cast<int>(
|
||||
sinc_resampler_->Resample(src, src_length, dst, dst_capacity));
|
||||
}
|
||||
}
|
||||
|
||||
// Explictly generate required instantiations.
|
||||
template class PushResampler<int16_t>;
|
||||
template class PushResampler<float>;
|
||||
|
||||
} // namespace webrtc
|
103
webrtc/common_audio/resampler/push_sinc_resampler.cc
Normal file
103
webrtc/common_audio/resampler/push_sinc_resampler.cc
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/common_audio/resampler/push_sinc_resampler.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/common_audio/include/audio_util.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
PushSincResampler::PushSincResampler(size_t source_frames,
|
||||
size_t destination_frames)
|
||||
: resampler_(new SincResampler(source_frames * 1.0 / destination_frames,
|
||||
source_frames,
|
||||
this)),
|
||||
source_ptr_(nullptr),
|
||||
source_ptr_int_(nullptr),
|
||||
destination_frames_(destination_frames),
|
||||
first_pass_(true),
|
||||
source_available_(0) {}
|
||||
|
||||
PushSincResampler::~PushSincResampler() {
|
||||
}
|
||||
|
||||
size_t PushSincResampler::Resample(const int16_t* source,
|
||||
size_t source_length,
|
||||
int16_t* destination,
|
||||
size_t destination_capacity) {
|
||||
if (!float_buffer_.get())
|
||||
float_buffer_.reset(new float[destination_frames_]);
|
||||
|
||||
source_ptr_int_ = source;
|
||||
// Pass nullptr as the float source to have Run() read from the int16 source.
|
||||
Resample(nullptr, source_length, float_buffer_.get(), destination_frames_);
|
||||
FloatS16ToS16(float_buffer_.get(), destination_frames_, destination);
|
||||
source_ptr_int_ = nullptr;
|
||||
return destination_frames_;
|
||||
}
|
||||
|
||||
size_t PushSincResampler::Resample(const float* source,
|
||||
size_t source_length,
|
||||
float* destination,
|
||||
size_t destination_capacity) {
|
||||
RTC_CHECK_EQ(source_length, resampler_->request_frames());
|
||||
RTC_CHECK_GE(destination_capacity, destination_frames_);
|
||||
// Cache the source pointer. Calling Resample() will immediately trigger
|
||||
// the Run() callback whereupon we provide the cached value.
|
||||
source_ptr_ = source;
|
||||
source_available_ = source_length;
|
||||
|
||||
// On the first pass, we call Resample() twice. During the first call, we
|
||||
// provide dummy input and discard the output. This is done to prime the
|
||||
// SincResampler buffer with the correct delay (half the kernel size), thereby
|
||||
// ensuring that all later Resample() calls will only result in one input
|
||||
// request through Run().
|
||||
//
|
||||
// If this wasn't done, SincResampler would call Run() twice on the first
|
||||
// pass, and we'd have to introduce an entire |source_frames| of delay, rather
|
||||
// than the minimum half kernel.
|
||||
//
|
||||
// It works out that ChunkSize() is exactly the amount of output we need to
|
||||
// request in order to prime the buffer with a single Run() request for
|
||||
// |source_frames|.
|
||||
if (first_pass_)
|
||||
resampler_->Resample(resampler_->ChunkSize(), destination);
|
||||
|
||||
resampler_->Resample(destination_frames_, destination);
|
||||
source_ptr_ = nullptr;
|
||||
return destination_frames_;
|
||||
}
|
||||
|
||||
void PushSincResampler::Run(size_t frames, float* destination) {
|
||||
// Ensure we are only asked for the available samples. This would fail if
|
||||
// Run() was triggered more than once per Resample() call.
|
||||
RTC_CHECK_EQ(source_available_, frames);
|
||||
|
||||
if (first_pass_) {
|
||||
// Provide dummy input on the first pass, the output of which will be
|
||||
// discarded, as described in Resample().
|
||||
std::memset(destination, 0, frames * sizeof(*destination));
|
||||
first_pass_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (source_ptr_) {
|
||||
std::memcpy(destination, source_ptr_, frames * sizeof(*destination));
|
||||
} else {
|
||||
for (size_t i = 0; i < frames; ++i)
|
||||
destination[i] = static_cast<float>(source_ptr_int_[i]);
|
||||
}
|
||||
source_available_ -= frames;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
76
webrtc/common_audio/resampler/push_sinc_resampler.h
Normal file
76
webrtc/common_audio/resampler/push_sinc_resampler.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_
|
||||
#define WEBRTC_COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/common_audio/resampler/sinc_resampler.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// A thin wrapper over SincResampler to provide a push-based interface as
|
||||
// required by WebRTC. SincResampler uses a pull-based interface, and will
|
||||
// use SincResamplerCallback::Run() to request data upon a call to Resample().
|
||||
// These Run() calls will happen on the same thread Resample() is called on.
|
||||
class PushSincResampler : public SincResamplerCallback {
|
||||
public:
|
||||
// Provide the size of the source and destination blocks in samples. These
|
||||
// must correspond to the same time duration (typically 10 ms) as the sample
|
||||
// ratio is inferred from them.
|
||||
PushSincResampler(size_t source_frames, size_t destination_frames);
|
||||
~PushSincResampler() override;
|
||||
|
||||
// Perform the resampling. |source_frames| must always equal the
|
||||
// |source_frames| provided at construction. |destination_capacity| must be
|
||||
// at least as large as |destination_frames|. Returns the number of samples
|
||||
// provided in destination (for convenience, since this will always be equal
|
||||
// to |destination_frames|).
|
||||
size_t Resample(const int16_t* source, size_t source_frames,
|
||||
int16_t* destination, size_t destination_capacity);
|
||||
size_t Resample(const float* source,
|
||||
size_t source_frames,
|
||||
float* destination,
|
||||
size_t destination_capacity);
|
||||
|
||||
// Delay due to the filter kernel. Essentially, the time after which an input
|
||||
// sample will appear in the resampled output.
|
||||
static float AlgorithmicDelaySeconds(int source_rate_hz) {
|
||||
return 1.f / source_rate_hz * SincResampler::kKernelSize / 2;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Implements SincResamplerCallback.
|
||||
void Run(size_t frames, float* destination) override;
|
||||
|
||||
private:
|
||||
friend class PushSincResamplerTest;
|
||||
SincResampler* get_resampler_for_testing() { return resampler_.get(); }
|
||||
|
||||
rtc::scoped_ptr<SincResampler> resampler_;
|
||||
rtc::scoped_ptr<float[]> float_buffer_;
|
||||
const float* source_ptr_;
|
||||
const int16_t* source_ptr_int_;
|
||||
const size_t destination_frames_;
|
||||
|
||||
// True on the first call to Resample(), to prime the SincResampler buffer.
|
||||
bool first_pass_;
|
||||
|
||||
// Used to assert we are only requested for as much data as is available.
|
||||
size_t source_available_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(PushSincResampler);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_
|
959
webrtc/common_audio/resampler/resampler.cc
Normal file
959
webrtc/common_audio/resampler/resampler.cc
Normal file
@ -0,0 +1,959 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* A wrapper for resampling a numerous amount of sampling combinations.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "webrtc/common_audio/resampler/include/resampler.h"
|
||||
#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
Resampler::Resampler()
|
||||
: state1_(nullptr),
|
||||
state2_(nullptr),
|
||||
state3_(nullptr),
|
||||
in_buffer_(nullptr),
|
||||
out_buffer_(nullptr),
|
||||
in_buffer_size_(0),
|
||||
out_buffer_size_(0),
|
||||
in_buffer_size_max_(0),
|
||||
out_buffer_size_max_(0),
|
||||
my_in_frequency_khz_(0),
|
||||
my_out_frequency_khz_(0),
|
||||
my_mode_(kResamplerMode1To1),
|
||||
num_channels_(0),
|
||||
slave_left_(nullptr),
|
||||
slave_right_(nullptr) {
|
||||
}
|
||||
|
||||
Resampler::Resampler(int inFreq, int outFreq, int num_channels)
|
||||
: Resampler() {
|
||||
Reset(inFreq, outFreq, num_channels);
|
||||
}
|
||||
|
||||
Resampler::~Resampler()
|
||||
{
|
||||
if (state1_)
|
||||
{
|
||||
free(state1_);
|
||||
}
|
||||
if (state2_)
|
||||
{
|
||||
free(state2_);
|
||||
}
|
||||
if (state3_)
|
||||
{
|
||||
free(state3_);
|
||||
}
|
||||
if (in_buffer_)
|
||||
{
|
||||
free(in_buffer_);
|
||||
}
|
||||
if (out_buffer_)
|
||||
{
|
||||
free(out_buffer_);
|
||||
}
|
||||
if (slave_left_)
|
||||
{
|
||||
delete slave_left_;
|
||||
}
|
||||
if (slave_right_)
|
||||
{
|
||||
delete slave_right_;
|
||||
}
|
||||
}
|
||||
|
||||
int Resampler::ResetIfNeeded(int inFreq, int outFreq, int num_channels)
|
||||
{
|
||||
int tmpInFreq_kHz = inFreq / 1000;
|
||||
int tmpOutFreq_kHz = outFreq / 1000;
|
||||
|
||||
if ((tmpInFreq_kHz != my_in_frequency_khz_) || (tmpOutFreq_kHz != my_out_frequency_khz_)
|
||||
|| (num_channels != num_channels_))
|
||||
{
|
||||
return Reset(inFreq, outFreq, num_channels);
|
||||
} else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int Resampler::Reset(int inFreq, int outFreq, int num_channels)
|
||||
{
|
||||
if (num_channels != 1 && num_channels != 2) {
|
||||
return -1;
|
||||
}
|
||||
num_channels_ = num_channels;
|
||||
|
||||
if (state1_)
|
||||
{
|
||||
free(state1_);
|
||||
state1_ = NULL;
|
||||
}
|
||||
if (state2_)
|
||||
{
|
||||
free(state2_);
|
||||
state2_ = NULL;
|
||||
}
|
||||
if (state3_)
|
||||
{
|
||||
free(state3_);
|
||||
state3_ = NULL;
|
||||
}
|
||||
if (in_buffer_)
|
||||
{
|
||||
free(in_buffer_);
|
||||
in_buffer_ = NULL;
|
||||
}
|
||||
if (out_buffer_)
|
||||
{
|
||||
free(out_buffer_);
|
||||
out_buffer_ = NULL;
|
||||
}
|
||||
if (slave_left_)
|
||||
{
|
||||
delete slave_left_;
|
||||
slave_left_ = NULL;
|
||||
}
|
||||
if (slave_right_)
|
||||
{
|
||||
delete slave_right_;
|
||||
slave_right_ = NULL;
|
||||
}
|
||||
|
||||
in_buffer_size_ = 0;
|
||||
out_buffer_size_ = 0;
|
||||
in_buffer_size_max_ = 0;
|
||||
out_buffer_size_max_ = 0;
|
||||
|
||||
// Start with a math exercise, Euclid's algorithm to find the gcd:
|
||||
int a = inFreq;
|
||||
int b = outFreq;
|
||||
int c = a % b;
|
||||
while (c != 0)
|
||||
{
|
||||
a = b;
|
||||
b = c;
|
||||
c = a % b;
|
||||
}
|
||||
// b is now the gcd;
|
||||
|
||||
// We need to track what domain we're in.
|
||||
my_in_frequency_khz_ = inFreq / 1000;
|
||||
my_out_frequency_khz_ = outFreq / 1000;
|
||||
|
||||
// Scale with GCD
|
||||
inFreq = inFreq / b;
|
||||
outFreq = outFreq / b;
|
||||
|
||||
if (num_channels_ == 2)
|
||||
{
|
||||
// Create two mono resamplers.
|
||||
slave_left_ = new Resampler(inFreq, outFreq, 1);
|
||||
slave_right_ = new Resampler(inFreq, outFreq, 1);
|
||||
}
|
||||
|
||||
if (inFreq == outFreq)
|
||||
{
|
||||
my_mode_ = kResamplerMode1To1;
|
||||
} else if (inFreq == 1)
|
||||
{
|
||||
switch (outFreq)
|
||||
{
|
||||
case 2:
|
||||
my_mode_ = kResamplerMode1To2;
|
||||
break;
|
||||
case 3:
|
||||
my_mode_ = kResamplerMode1To3;
|
||||
break;
|
||||
case 4:
|
||||
my_mode_ = kResamplerMode1To4;
|
||||
break;
|
||||
case 6:
|
||||
my_mode_ = kResamplerMode1To6;
|
||||
break;
|
||||
case 12:
|
||||
my_mode_ = kResamplerMode1To12;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
} else if (outFreq == 1)
|
||||
{
|
||||
switch (inFreq)
|
||||
{
|
||||
case 2:
|
||||
my_mode_ = kResamplerMode2To1;
|
||||
break;
|
||||
case 3:
|
||||
my_mode_ = kResamplerMode3To1;
|
||||
break;
|
||||
case 4:
|
||||
my_mode_ = kResamplerMode4To1;
|
||||
break;
|
||||
case 6:
|
||||
my_mode_ = kResamplerMode6To1;
|
||||
break;
|
||||
case 12:
|
||||
my_mode_ = kResamplerMode12To1;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
} else if ((inFreq == 2) && (outFreq == 3))
|
||||
{
|
||||
my_mode_ = kResamplerMode2To3;
|
||||
} else if ((inFreq == 2) && (outFreq == 11))
|
||||
{
|
||||
my_mode_ = kResamplerMode2To11;
|
||||
} else if ((inFreq == 4) && (outFreq == 11))
|
||||
{
|
||||
my_mode_ = kResamplerMode4To11;
|
||||
} else if ((inFreq == 8) && (outFreq == 11))
|
||||
{
|
||||
my_mode_ = kResamplerMode8To11;
|
||||
} else if ((inFreq == 3) && (outFreq == 2))
|
||||
{
|
||||
my_mode_ = kResamplerMode3To2;
|
||||
} else if ((inFreq == 11) && (outFreq == 2))
|
||||
{
|
||||
my_mode_ = kResamplerMode11To2;
|
||||
} else if ((inFreq == 11) && (outFreq == 4))
|
||||
{
|
||||
my_mode_ = kResamplerMode11To4;
|
||||
} else if ((inFreq == 11) && (outFreq == 16))
|
||||
{
|
||||
my_mode_ = kResamplerMode11To16;
|
||||
} else if ((inFreq == 11) && (outFreq == 32))
|
||||
{
|
||||
my_mode_ = kResamplerMode11To32;
|
||||
} else if ((inFreq == 11) && (outFreq == 8))
|
||||
{
|
||||
my_mode_ = kResamplerMode11To8;
|
||||
} else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Now create the states we need
|
||||
switch (my_mode_)
|
||||
{
|
||||
case kResamplerMode1To1:
|
||||
// No state needed;
|
||||
break;
|
||||
case kResamplerMode1To2:
|
||||
state1_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state1_, 0, 8 * sizeof(int32_t));
|
||||
break;
|
||||
case kResamplerMode1To3:
|
||||
state1_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz));
|
||||
WebRtcSpl_ResetResample16khzTo48khz((WebRtcSpl_State16khzTo48khz *)state1_);
|
||||
break;
|
||||
case kResamplerMode1To4:
|
||||
// 1:2
|
||||
state1_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state1_, 0, 8 * sizeof(int32_t));
|
||||
// 2:4
|
||||
state2_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state2_, 0, 8 * sizeof(int32_t));
|
||||
break;
|
||||
case kResamplerMode1To6:
|
||||
// 1:2
|
||||
state1_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state1_, 0, 8 * sizeof(int32_t));
|
||||
// 2:6
|
||||
state2_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz));
|
||||
WebRtcSpl_ResetResample16khzTo48khz((WebRtcSpl_State16khzTo48khz *)state2_);
|
||||
break;
|
||||
case kResamplerMode1To12:
|
||||
// 1:2
|
||||
state1_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state1_, 0, 8 * sizeof(int32_t));
|
||||
// 2:4
|
||||
state2_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state2_, 0, 8 * sizeof(int32_t));
|
||||
// 4:12
|
||||
state3_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz));
|
||||
WebRtcSpl_ResetResample16khzTo48khz(
|
||||
(WebRtcSpl_State16khzTo48khz*) state3_);
|
||||
break;
|
||||
case kResamplerMode2To3:
|
||||
// 2:6
|
||||
state1_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz));
|
||||
WebRtcSpl_ResetResample16khzTo48khz((WebRtcSpl_State16khzTo48khz *)state1_);
|
||||
// 6:3
|
||||
state2_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state2_, 0, 8 * sizeof(int32_t));
|
||||
break;
|
||||
case kResamplerMode2To11:
|
||||
state1_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state1_, 0, 8 * sizeof(int32_t));
|
||||
|
||||
state2_ = malloc(sizeof(WebRtcSpl_State8khzTo22khz));
|
||||
WebRtcSpl_ResetResample8khzTo22khz((WebRtcSpl_State8khzTo22khz *)state2_);
|
||||
break;
|
||||
case kResamplerMode4To11:
|
||||
state1_ = malloc(sizeof(WebRtcSpl_State8khzTo22khz));
|
||||
WebRtcSpl_ResetResample8khzTo22khz((WebRtcSpl_State8khzTo22khz *)state1_);
|
||||
break;
|
||||
case kResamplerMode8To11:
|
||||
state1_ = malloc(sizeof(WebRtcSpl_State16khzTo22khz));
|
||||
WebRtcSpl_ResetResample16khzTo22khz((WebRtcSpl_State16khzTo22khz *)state1_);
|
||||
break;
|
||||
case kResamplerMode11To16:
|
||||
state1_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state1_, 0, 8 * sizeof(int32_t));
|
||||
|
||||
state2_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz));
|
||||
WebRtcSpl_ResetResample22khzTo16khz((WebRtcSpl_State22khzTo16khz *)state2_);
|
||||
break;
|
||||
case kResamplerMode11To32:
|
||||
// 11 -> 22
|
||||
state1_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state1_, 0, 8 * sizeof(int32_t));
|
||||
|
||||
// 22 -> 16
|
||||
state2_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz));
|
||||
WebRtcSpl_ResetResample22khzTo16khz((WebRtcSpl_State22khzTo16khz *)state2_);
|
||||
|
||||
// 16 -> 32
|
||||
state3_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state3_, 0, 8 * sizeof(int32_t));
|
||||
|
||||
break;
|
||||
case kResamplerMode2To1:
|
||||
state1_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state1_, 0, 8 * sizeof(int32_t));
|
||||
break;
|
||||
case kResamplerMode3To1:
|
||||
state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz));
|
||||
WebRtcSpl_ResetResample48khzTo16khz((WebRtcSpl_State48khzTo16khz *)state1_);
|
||||
break;
|
||||
case kResamplerMode4To1:
|
||||
// 4:2
|
||||
state1_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state1_, 0, 8 * sizeof(int32_t));
|
||||
// 2:1
|
||||
state2_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state2_, 0, 8 * sizeof(int32_t));
|
||||
break;
|
||||
case kResamplerMode6To1:
|
||||
// 6:2
|
||||
state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz));
|
||||
WebRtcSpl_ResetResample48khzTo16khz((WebRtcSpl_State48khzTo16khz *)state1_);
|
||||
// 2:1
|
||||
state2_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state2_, 0, 8 * sizeof(int32_t));
|
||||
break;
|
||||
case kResamplerMode12To1:
|
||||
// 12:4
|
||||
state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz));
|
||||
WebRtcSpl_ResetResample48khzTo16khz(
|
||||
(WebRtcSpl_State48khzTo16khz*) state1_);
|
||||
// 4:2
|
||||
state2_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state2_, 0, 8 * sizeof(int32_t));
|
||||
// 2:1
|
||||
state3_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state3_, 0, 8 * sizeof(int32_t));
|
||||
break;
|
||||
case kResamplerMode3To2:
|
||||
// 3:6
|
||||
state1_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state1_, 0, 8 * sizeof(int32_t));
|
||||
// 6:2
|
||||
state2_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz));
|
||||
WebRtcSpl_ResetResample48khzTo16khz((WebRtcSpl_State48khzTo16khz *)state2_);
|
||||
break;
|
||||
case kResamplerMode11To2:
|
||||
state1_ = malloc(sizeof(WebRtcSpl_State22khzTo8khz));
|
||||
WebRtcSpl_ResetResample22khzTo8khz((WebRtcSpl_State22khzTo8khz *)state1_);
|
||||
|
||||
state2_ = malloc(8 * sizeof(int32_t));
|
||||
memset(state2_, 0, 8 * sizeof(int32_t));
|
||||
|
||||
break;
|
||||
case kResamplerMode11To4:
|
||||
state1_ = malloc(sizeof(WebRtcSpl_State22khzTo8khz));
|
||||
WebRtcSpl_ResetResample22khzTo8khz((WebRtcSpl_State22khzTo8khz *)state1_);
|
||||
break;
|
||||
case kResamplerMode11To8:
|
||||
state1_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz));
|
||||
WebRtcSpl_ResetResample22khzTo16khz((WebRtcSpl_State22khzTo16khz *)state1_);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Synchronous resampling, all output samples are written to samplesOut
|
||||
int Resampler::Push(const int16_t * samplesIn, size_t lengthIn,
|
||||
int16_t* samplesOut, size_t maxLen, size_t &outLen)
|
||||
{
|
||||
if (num_channels_ == 2)
|
||||
{
|
||||
// Split up the signal and call the slave object for each channel
|
||||
int16_t* left = (int16_t*)malloc(lengthIn * sizeof(int16_t) / 2);
|
||||
int16_t* right = (int16_t*)malloc(lengthIn * sizeof(int16_t) / 2);
|
||||
int16_t* out_left = (int16_t*)malloc(maxLen / 2 * sizeof(int16_t));
|
||||
int16_t* out_right =
|
||||
(int16_t*)malloc(maxLen / 2 * sizeof(int16_t));
|
||||
int res = 0;
|
||||
for (size_t i = 0; i < lengthIn; i += 2)
|
||||
{
|
||||
left[i >> 1] = samplesIn[i];
|
||||
right[i >> 1] = samplesIn[i + 1];
|
||||
}
|
||||
|
||||
// It's OK to overwrite the local parameter, since it's just a copy
|
||||
lengthIn = lengthIn / 2;
|
||||
|
||||
size_t actualOutLen_left = 0;
|
||||
size_t actualOutLen_right = 0;
|
||||
// Do resampling for right channel
|
||||
res |= slave_left_->Push(left, lengthIn, out_left, maxLen / 2, actualOutLen_left);
|
||||
res |= slave_right_->Push(right, lengthIn, out_right, maxLen / 2, actualOutLen_right);
|
||||
if (res || (actualOutLen_left != actualOutLen_right))
|
||||
{
|
||||
free(left);
|
||||
free(right);
|
||||
free(out_left);
|
||||
free(out_right);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Reassemble the signal
|
||||
for (size_t i = 0; i < actualOutLen_left; i++)
|
||||
{
|
||||
samplesOut[i * 2] = out_left[i];
|
||||
samplesOut[i * 2 + 1] = out_right[i];
|
||||
}
|
||||
outLen = 2 * actualOutLen_left;
|
||||
|
||||
free(left);
|
||||
free(right);
|
||||
free(out_left);
|
||||
free(out_right);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Containers for temp samples
|
||||
int16_t* tmp;
|
||||
int16_t* tmp_2;
|
||||
// tmp data for resampling routines
|
||||
int32_t* tmp_mem;
|
||||
|
||||
switch (my_mode_)
|
||||
{
|
||||
case kResamplerMode1To1:
|
||||
memcpy(samplesOut, samplesIn, lengthIn * sizeof(int16_t));
|
||||
outLen = lengthIn;
|
||||
break;
|
||||
case kResamplerMode1To2:
|
||||
if (maxLen < (lengthIn * 2))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut, (int32_t*)state1_);
|
||||
outLen = lengthIn * 2;
|
||||
return 0;
|
||||
case kResamplerMode1To3:
|
||||
|
||||
// We can only handle blocks of 160 samples
|
||||
// Can be fixed, but I don't think it's needed
|
||||
if ((lengthIn % 160) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (maxLen < (lengthIn * 3))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
tmp_mem = (int32_t*)malloc(336 * sizeof(int32_t));
|
||||
|
||||
for (size_t i = 0; i < lengthIn; i += 160)
|
||||
{
|
||||
WebRtcSpl_Resample16khzTo48khz(samplesIn + i, samplesOut + i * 3,
|
||||
(WebRtcSpl_State16khzTo48khz *)state1_,
|
||||
tmp_mem);
|
||||
}
|
||||
outLen = lengthIn * 3;
|
||||
free(tmp_mem);
|
||||
return 0;
|
||||
case kResamplerMode1To4:
|
||||
if (maxLen < (lengthIn * 4))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
tmp = (int16_t*)malloc(sizeof(int16_t) * 2 * lengthIn);
|
||||
// 1:2
|
||||
WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_);
|
||||
// 2:4
|
||||
WebRtcSpl_UpsampleBy2(tmp, lengthIn * 2, samplesOut, (int32_t*)state2_);
|
||||
outLen = lengthIn * 4;
|
||||
free(tmp);
|
||||
return 0;
|
||||
case kResamplerMode1To6:
|
||||
// We can only handle blocks of 80 samples
|
||||
// Can be fixed, but I don't think it's needed
|
||||
if ((lengthIn % 80) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (maxLen < (lengthIn * 6))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
//1:2
|
||||
|
||||
tmp_mem = (int32_t*)malloc(336 * sizeof(int32_t));
|
||||
tmp = (int16_t*)malloc(sizeof(int16_t) * 2 * lengthIn);
|
||||
|
||||
WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_);
|
||||
outLen = lengthIn * 2;
|
||||
|
||||
for (size_t i = 0; i < outLen; i += 160)
|
||||
{
|
||||
WebRtcSpl_Resample16khzTo48khz(tmp + i, samplesOut + i * 3,
|
||||
(WebRtcSpl_State16khzTo48khz *)state2_,
|
||||
tmp_mem);
|
||||
}
|
||||
outLen = outLen * 3;
|
||||
free(tmp_mem);
|
||||
free(tmp);
|
||||
|
||||
return 0;
|
||||
case kResamplerMode1To12:
|
||||
// We can only handle blocks of 40 samples
|
||||
// Can be fixed, but I don't think it's needed
|
||||
if ((lengthIn % 40) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (maxLen < (lengthIn * 12)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
tmp_mem = (int32_t*) malloc(336 * sizeof(int32_t));
|
||||
tmp = (int16_t*) malloc(sizeof(int16_t) * 4 * lengthIn);
|
||||
//1:2
|
||||
WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut,
|
||||
(int32_t*) state1_);
|
||||
outLen = lengthIn * 2;
|
||||
//2:4
|
||||
WebRtcSpl_UpsampleBy2(samplesOut, outLen, tmp, (int32_t*) state2_);
|
||||
outLen = outLen * 2;
|
||||
// 4:12
|
||||
for (size_t i = 0; i < outLen; i += 160) {
|
||||
// WebRtcSpl_Resample16khzTo48khz() takes a block of 160 samples
|
||||
// as input and outputs a resampled block of 480 samples. The
|
||||
// data is now actually in 32 kHz sampling rate, despite the
|
||||
// function name, and with a resampling factor of three becomes
|
||||
// 96 kHz.
|
||||
WebRtcSpl_Resample16khzTo48khz(tmp + i, samplesOut + i * 3,
|
||||
(WebRtcSpl_State16khzTo48khz*) state3_,
|
||||
tmp_mem);
|
||||
}
|
||||
outLen = outLen * 3;
|
||||
free(tmp_mem);
|
||||
free(tmp);
|
||||
|
||||
return 0;
|
||||
case kResamplerMode2To3:
|
||||
if (maxLen < (lengthIn * 3 / 2))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
// 2:6
|
||||
// We can only handle blocks of 160 samples
|
||||
// Can be fixed, but I don't think it's needed
|
||||
if ((lengthIn % 160) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
tmp = static_cast<int16_t*> (malloc(sizeof(int16_t) * lengthIn * 3));
|
||||
tmp_mem = (int32_t*)malloc(336 * sizeof(int32_t));
|
||||
for (size_t i = 0; i < lengthIn; i += 160)
|
||||
{
|
||||
WebRtcSpl_Resample16khzTo48khz(samplesIn + i, tmp + i * 3,
|
||||
(WebRtcSpl_State16khzTo48khz *)state1_,
|
||||
tmp_mem);
|
||||
}
|
||||
lengthIn = lengthIn * 3;
|
||||
// 6:3
|
||||
WebRtcSpl_DownsampleBy2(tmp, lengthIn, samplesOut, (int32_t*)state2_);
|
||||
outLen = lengthIn / 2;
|
||||
free(tmp);
|
||||
free(tmp_mem);
|
||||
return 0;
|
||||
case kResamplerMode2To11:
|
||||
|
||||
// We can only handle blocks of 80 samples
|
||||
// Can be fixed, but I don't think it's needed
|
||||
if ((lengthIn % 80) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (maxLen < ((lengthIn * 11) / 2))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
tmp = (int16_t*)malloc(sizeof(int16_t) * 2 * lengthIn);
|
||||
// 1:2
|
||||
WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_);
|
||||
lengthIn *= 2;
|
||||
|
||||
tmp_mem = (int32_t*)malloc(98 * sizeof(int32_t));
|
||||
|
||||
for (size_t i = 0; i < lengthIn; i += 80)
|
||||
{
|
||||
WebRtcSpl_Resample8khzTo22khz(tmp + i, samplesOut + (i * 11) / 4,
|
||||
(WebRtcSpl_State8khzTo22khz *)state2_,
|
||||
tmp_mem);
|
||||
}
|
||||
outLen = (lengthIn * 11) / 4;
|
||||
free(tmp_mem);
|
||||
free(tmp);
|
||||
return 0;
|
||||
case kResamplerMode4To11:
|
||||
|
||||
// We can only handle blocks of 80 samples
|
||||
// Can be fixed, but I don't think it's needed
|
||||
if ((lengthIn % 80) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (maxLen < ((lengthIn * 11) / 4))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
tmp_mem = (int32_t*)malloc(98 * sizeof(int32_t));
|
||||
|
||||
for (size_t i = 0; i < lengthIn; i += 80)
|
||||
{
|
||||
WebRtcSpl_Resample8khzTo22khz(samplesIn + i, samplesOut + (i * 11) / 4,
|
||||
(WebRtcSpl_State8khzTo22khz *)state1_,
|
||||
tmp_mem);
|
||||
}
|
||||
outLen = (lengthIn * 11) / 4;
|
||||
free(tmp_mem);
|
||||
return 0;
|
||||
case kResamplerMode8To11:
|
||||
// We can only handle blocks of 160 samples
|
||||
// Can be fixed, but I don't think it's needed
|
||||
if ((lengthIn % 160) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (maxLen < ((lengthIn * 11) / 8))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
tmp_mem = (int32_t*)malloc(88 * sizeof(int32_t));
|
||||
|
||||
for (size_t i = 0; i < lengthIn; i += 160)
|
||||
{
|
||||
WebRtcSpl_Resample16khzTo22khz(samplesIn + i, samplesOut + (i * 11) / 8,
|
||||
(WebRtcSpl_State16khzTo22khz *)state1_,
|
||||
tmp_mem);
|
||||
}
|
||||
outLen = (lengthIn * 11) / 8;
|
||||
free(tmp_mem);
|
||||
return 0;
|
||||
|
||||
case kResamplerMode11To16:
|
||||
// We can only handle blocks of 110 samples
|
||||
if ((lengthIn % 110) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (maxLen < ((lengthIn * 16) / 11))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
tmp_mem = (int32_t*)malloc(104 * sizeof(int32_t));
|
||||
tmp = (int16_t*)malloc((sizeof(int16_t) * lengthIn * 2));
|
||||
|
||||
WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_);
|
||||
|
||||
for (size_t i = 0; i < (lengthIn * 2); i += 220)
|
||||
{
|
||||
WebRtcSpl_Resample22khzTo16khz(tmp + i, samplesOut + (i / 220) * 160,
|
||||
(WebRtcSpl_State22khzTo16khz *)state2_,
|
||||
tmp_mem);
|
||||
}
|
||||
|
||||
outLen = (lengthIn * 16) / 11;
|
||||
|
||||
free(tmp_mem);
|
||||
free(tmp);
|
||||
return 0;
|
||||
|
||||
case kResamplerMode11To32:
|
||||
|
||||
// We can only handle blocks of 110 samples
|
||||
if ((lengthIn % 110) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (maxLen < ((lengthIn * 32) / 11))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
tmp_mem = (int32_t*)malloc(104 * sizeof(int32_t));
|
||||
tmp = (int16_t*)malloc((sizeof(int16_t) * lengthIn * 2));
|
||||
|
||||
// 11 -> 22 kHz in samplesOut
|
||||
WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut, (int32_t*)state1_);
|
||||
|
||||
// 22 -> 16 in tmp
|
||||
for (size_t i = 0; i < (lengthIn * 2); i += 220)
|
||||
{
|
||||
WebRtcSpl_Resample22khzTo16khz(samplesOut + i, tmp + (i / 220) * 160,
|
||||
(WebRtcSpl_State22khzTo16khz *)state2_,
|
||||
tmp_mem);
|
||||
}
|
||||
|
||||
// 16 -> 32 in samplesOut
|
||||
WebRtcSpl_UpsampleBy2(tmp, (lengthIn * 16) / 11, samplesOut,
|
||||
(int32_t*)state3_);
|
||||
|
||||
outLen = (lengthIn * 32) / 11;
|
||||
|
||||
free(tmp_mem);
|
||||
free(tmp);
|
||||
return 0;
|
||||
|
||||
case kResamplerMode2To1:
|
||||
if (maxLen < (lengthIn / 2))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
WebRtcSpl_DownsampleBy2(samplesIn, lengthIn, samplesOut, (int32_t*)state1_);
|
||||
outLen = lengthIn / 2;
|
||||
return 0;
|
||||
case kResamplerMode3To1:
|
||||
// We can only handle blocks of 480 samples
|
||||
// Can be fixed, but I don't think it's needed
|
||||
if ((lengthIn % 480) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (maxLen < (lengthIn / 3))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
tmp_mem = (int32_t*)malloc(496 * sizeof(int32_t));
|
||||
|
||||
for (size_t i = 0; i < lengthIn; i += 480)
|
||||
{
|
||||
WebRtcSpl_Resample48khzTo16khz(samplesIn + i, samplesOut + i / 3,
|
||||
(WebRtcSpl_State48khzTo16khz *)state1_,
|
||||
tmp_mem);
|
||||
}
|
||||
outLen = lengthIn / 3;
|
||||
free(tmp_mem);
|
||||
return 0;
|
||||
case kResamplerMode4To1:
|
||||
if (maxLen < (lengthIn / 4))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
tmp = (int16_t*)malloc(sizeof(int16_t) * lengthIn / 2);
|
||||
// 4:2
|
||||
WebRtcSpl_DownsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_);
|
||||
// 2:1
|
||||
WebRtcSpl_DownsampleBy2(tmp, lengthIn / 2, samplesOut, (int32_t*)state2_);
|
||||
outLen = lengthIn / 4;
|
||||
free(tmp);
|
||||
return 0;
|
||||
|
||||
case kResamplerMode6To1:
|
||||
// We can only handle blocks of 480 samples
|
||||
// Can be fixed, but I don't think it's needed
|
||||
if ((lengthIn % 480) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (maxLen < (lengthIn / 6))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
tmp_mem = (int32_t*)malloc(496 * sizeof(int32_t));
|
||||
tmp = (int16_t*)malloc((sizeof(int16_t) * lengthIn) / 3);
|
||||
|
||||
for (size_t i = 0; i < lengthIn; i += 480)
|
||||
{
|
||||
WebRtcSpl_Resample48khzTo16khz(samplesIn + i, tmp + i / 3,
|
||||
(WebRtcSpl_State48khzTo16khz *)state1_,
|
||||
tmp_mem);
|
||||
}
|
||||
outLen = lengthIn / 3;
|
||||
free(tmp_mem);
|
||||
WebRtcSpl_DownsampleBy2(tmp, outLen, samplesOut, (int32_t*)state2_);
|
||||
free(tmp);
|
||||
outLen = outLen / 2;
|
||||
return 0;
|
||||
case kResamplerMode12To1:
|
||||
// We can only handle blocks of 480 samples
|
||||
// Can be fixed, but I don't think it's needed
|
||||
if ((lengthIn % 480) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (maxLen < (lengthIn / 12)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
tmp_mem = (int32_t*) malloc(496 * sizeof(int32_t));
|
||||
tmp = (int16_t*) malloc((sizeof(int16_t) * lengthIn) / 3);
|
||||
tmp_2 = (int16_t*) malloc((sizeof(int16_t) * lengthIn) / 6);
|
||||
// 12:4
|
||||
for (size_t i = 0; i < lengthIn; i += 480) {
|
||||
// WebRtcSpl_Resample48khzTo16khz() takes a block of 480 samples
|
||||
// as input and outputs a resampled block of 160 samples. The
|
||||
// data is now actually in 96 kHz sampling rate, despite the
|
||||
// function name, and with a resampling factor of 1/3 becomes
|
||||
// 32 kHz.
|
||||
WebRtcSpl_Resample48khzTo16khz(samplesIn + i, tmp + i / 3,
|
||||
(WebRtcSpl_State48khzTo16khz*) state1_,
|
||||
tmp_mem);
|
||||
}
|
||||
outLen = lengthIn / 3;
|
||||
free(tmp_mem);
|
||||
// 4:2
|
||||
WebRtcSpl_DownsampleBy2(tmp, outLen, tmp_2, (int32_t*) state2_);
|
||||
outLen = outLen / 2;
|
||||
free(tmp);
|
||||
// 2:1
|
||||
WebRtcSpl_DownsampleBy2(tmp_2, outLen, samplesOut,
|
||||
(int32_t*) state3_);
|
||||
free(tmp_2);
|
||||
outLen = outLen / 2;
|
||||
return 0;
|
||||
case kResamplerMode3To2:
|
||||
if (maxLen < (lengthIn * 2 / 3))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
// 3:6
|
||||
tmp = static_cast<int16_t*> (malloc(sizeof(int16_t) * lengthIn * 2));
|
||||
WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_);
|
||||
lengthIn *= 2;
|
||||
// 6:2
|
||||
// We can only handle blocks of 480 samples
|
||||
// Can be fixed, but I don't think it's needed
|
||||
if ((lengthIn % 480) != 0)
|
||||
{
|
||||
free(tmp);
|
||||
return -1;
|
||||
}
|
||||
tmp_mem = (int32_t*)malloc(496 * sizeof(int32_t));
|
||||
for (size_t i = 0; i < lengthIn; i += 480)
|
||||
{
|
||||
WebRtcSpl_Resample48khzTo16khz(tmp + i, samplesOut + i / 3,
|
||||
(WebRtcSpl_State48khzTo16khz *)state2_,
|
||||
tmp_mem);
|
||||
}
|
||||
outLen = lengthIn / 3;
|
||||
free(tmp);
|
||||
free(tmp_mem);
|
||||
return 0;
|
||||
case kResamplerMode11To2:
|
||||
// We can only handle blocks of 220 samples
|
||||
// Can be fixed, but I don't think it's needed
|
||||
if ((lengthIn % 220) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (maxLen < ((lengthIn * 2) / 11))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
tmp_mem = (int32_t*)malloc(126 * sizeof(int32_t));
|
||||
tmp = (int16_t*)malloc((lengthIn * 4) / 11 * sizeof(int16_t));
|
||||
|
||||
for (size_t i = 0; i < lengthIn; i += 220)
|
||||
{
|
||||
WebRtcSpl_Resample22khzTo8khz(samplesIn + i, tmp + (i * 4) / 11,
|
||||
(WebRtcSpl_State22khzTo8khz *)state1_,
|
||||
tmp_mem);
|
||||
}
|
||||
lengthIn = (lengthIn * 4) / 11;
|
||||
|
||||
WebRtcSpl_DownsampleBy2(tmp, lengthIn, samplesOut,
|
||||
(int32_t*)state2_);
|
||||
outLen = lengthIn / 2;
|
||||
|
||||
free(tmp_mem);
|
||||
free(tmp);
|
||||
return 0;
|
||||
case kResamplerMode11To4:
|
||||
// We can only handle blocks of 220 samples
|
||||
// Can be fixed, but I don't think it's needed
|
||||
if ((lengthIn % 220) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (maxLen < ((lengthIn * 4) / 11))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
tmp_mem = (int32_t*)malloc(126 * sizeof(int32_t));
|
||||
|
||||
for (size_t i = 0; i < lengthIn; i += 220)
|
||||
{
|
||||
WebRtcSpl_Resample22khzTo8khz(samplesIn + i, samplesOut + (i * 4) / 11,
|
||||
(WebRtcSpl_State22khzTo8khz *)state1_,
|
||||
tmp_mem);
|
||||
}
|
||||
outLen = (lengthIn * 4) / 11;
|
||||
free(tmp_mem);
|
||||
return 0;
|
||||
case kResamplerMode11To8:
|
||||
// We can only handle blocks of 160 samples
|
||||
// Can be fixed, but I don't think it's needed
|
||||
if ((lengthIn % 220) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (maxLen < ((lengthIn * 8) / 11))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
tmp_mem = (int32_t*)malloc(104 * sizeof(int32_t));
|
||||
|
||||
for (size_t i = 0; i < lengthIn; i += 220)
|
||||
{
|
||||
WebRtcSpl_Resample22khzTo16khz(samplesIn + i, samplesOut + (i * 8) / 11,
|
||||
(WebRtcSpl_State22khzTo16khz *)state1_,
|
||||
tmp_mem);
|
||||
}
|
||||
outLen = (lengthIn * 8) / 11;
|
||||
free(tmp_mem);
|
||||
return 0;
|
||||
break;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
378
webrtc/common_audio/resampler/sinc_resampler.cc
Normal file
378
webrtc/common_audio/resampler/sinc_resampler.cc
Normal file
@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Modified from the Chromium original:
|
||||
// src/media/base/sinc_resampler.cc
|
||||
|
||||
// Initial input buffer layout, dividing into regions r0_ to r4_ (note: r0_, r3_
|
||||
// and r4_ will move after the first load):
|
||||
//
|
||||
// |----------------|-----------------------------------------|----------------|
|
||||
//
|
||||
// request_frames_
|
||||
// <--------------------------------------------------------->
|
||||
// r0_ (during first load)
|
||||
//
|
||||
// kKernelSize / 2 kKernelSize / 2 kKernelSize / 2 kKernelSize / 2
|
||||
// <---------------> <---------------> <---------------> <--------------->
|
||||
// r1_ r2_ r3_ r4_
|
||||
//
|
||||
// block_size_ == r4_ - r2_
|
||||
// <--------------------------------------->
|
||||
//
|
||||
// request_frames_
|
||||
// <------------------ ... ----------------->
|
||||
// r0_ (during second load)
|
||||
//
|
||||
// On the second request r0_ slides to the right by kKernelSize / 2 and r3_, r4_
|
||||
// and block_size_ are reinitialized via step (3) in the algorithm below.
|
||||
//
|
||||
// These new regions remain constant until a Flush() occurs. While complicated,
|
||||
// this allows us to reduce jitter by always requesting the same amount from the
|
||||
// provided callback.
|
||||
//
|
||||
// The algorithm:
|
||||
//
|
||||
// 1) Allocate input_buffer of size: request_frames_ + kKernelSize; this ensures
|
||||
// there's enough room to read request_frames_ from the callback into region
|
||||
// r0_ (which will move between the first and subsequent passes).
|
||||
//
|
||||
// 2) Let r1_, r2_ each represent half the kernel centered around r0_:
|
||||
//
|
||||
// r0_ = input_buffer_ + kKernelSize / 2
|
||||
// r1_ = input_buffer_
|
||||
// r2_ = r0_
|
||||
//
|
||||
// r0_ is always request_frames_ in size. r1_, r2_ are kKernelSize / 2 in
|
||||
// size. r1_ must be zero initialized to avoid convolution with garbage (see
|
||||
// step (5) for why).
|
||||
//
|
||||
// 3) Let r3_, r4_ each represent half the kernel right aligned with the end of
|
||||
// r0_ and choose block_size_ as the distance in frames between r4_ and r2_:
|
||||
//
|
||||
// r3_ = r0_ + request_frames_ - kKernelSize
|
||||
// r4_ = r0_ + request_frames_ - kKernelSize / 2
|
||||
// block_size_ = r4_ - r2_ = request_frames_ - kKernelSize / 2
|
||||
//
|
||||
// 4) Consume request_frames_ frames into r0_.
|
||||
//
|
||||
// 5) Position kernel centered at start of r2_ and generate output frames until
|
||||
// the kernel is centered at the start of r4_ or we've finished generating
|
||||
// all the output frames.
|
||||
//
|
||||
// 6) Wrap left over data from the r3_ to r1_ and r4_ to r2_.
|
||||
//
|
||||
// 7) If we're on the second load, in order to avoid overwriting the frames we
|
||||
// just wrapped from r4_ we need to slide r0_ to the right by the size of
|
||||
// r4_, which is kKernelSize / 2:
|
||||
//
|
||||
// r0_ = r0_ + kKernelSize / 2 = input_buffer_ + kKernelSize
|
||||
//
|
||||
// r3_, r4_, and block_size_ then need to be reinitialized, so goto (3).
|
||||
//
|
||||
// 8) Else, if we're not on the second load, goto (4).
|
||||
//
|
||||
// Note: we're glossing over how the sub-sample handling works with
|
||||
// |virtual_source_idx_|, etc.
|
||||
|
||||
// MSVC++ requires this to be set before any other includes to get M_PI.
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
#include "webrtc/common_audio/resampler/sinc_resampler.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "webrtc/system_wrappers/interface/cpu_features_wrapper.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
double SincScaleFactor(double io_ratio) {
|
||||
// |sinc_scale_factor| is basically the normalized cutoff frequency of the
|
||||
// low-pass filter.
|
||||
double sinc_scale_factor = io_ratio > 1.0 ? 1.0 / io_ratio : 1.0;
|
||||
|
||||
// The sinc function is an idealized brick-wall filter, but since we're
|
||||
// windowing it the transition from pass to stop does not happen right away.
|
||||
// So we should adjust the low pass filter cutoff slightly downward to avoid
|
||||
// some aliasing at the very high-end.
|
||||
// TODO(crogers): this value is empirical and to be more exact should vary
|
||||
// depending on kKernelSize.
|
||||
sinc_scale_factor *= 0.9;
|
||||
|
||||
return sinc_scale_factor;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// If we know the minimum architecture at compile time, avoid CPU detection.
|
||||
#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
#if defined(__SSE2__)
|
||||
#define CONVOLVE_FUNC Convolve_SSE
|
||||
void SincResampler::InitializeCPUSpecificFeatures() {}
|
||||
#else
|
||||
// x86 CPU detection required. Function will be set by
|
||||
// InitializeCPUSpecificFeatures().
|
||||
// TODO(dalecurtis): Once Chrome moves to an SSE baseline this can be removed.
|
||||
#define CONVOLVE_FUNC convolve_proc_
|
||||
|
||||
void SincResampler::InitializeCPUSpecificFeatures() {
|
||||
convolve_proc_ = WebRtc_GetCPUInfo(kSSE2) ? Convolve_SSE : Convolve_C;
|
||||
}
|
||||
#endif
|
||||
#elif defined(WEBRTC_HAS_NEON)
|
||||
#define CONVOLVE_FUNC Convolve_NEON
|
||||
void SincResampler::InitializeCPUSpecificFeatures() {}
|
||||
#elif defined(WEBRTC_DETECT_NEON)
|
||||
#define CONVOLVE_FUNC convolve_proc_
|
||||
void SincResampler::InitializeCPUSpecificFeatures() {
|
||||
convolve_proc_ = WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON ?
|
||||
Convolve_NEON : Convolve_C;
|
||||
}
|
||||
#else
|
||||
// Unknown architecture.
|
||||
#define CONVOLVE_FUNC Convolve_C
|
||||
void SincResampler::InitializeCPUSpecificFeatures() {}
|
||||
#endif
|
||||
|
||||
SincResampler::SincResampler(double io_sample_rate_ratio,
|
||||
size_t request_frames,
|
||||
SincResamplerCallback* read_cb)
|
||||
: io_sample_rate_ratio_(io_sample_rate_ratio),
|
||||
read_cb_(read_cb),
|
||||
request_frames_(request_frames),
|
||||
input_buffer_size_(request_frames_ + kKernelSize),
|
||||
// Create input buffers with a 16-byte alignment for SSE optimizations.
|
||||
kernel_storage_(static_cast<float*>(
|
||||
AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))),
|
||||
kernel_pre_sinc_storage_(static_cast<float*>(
|
||||
AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))),
|
||||
kernel_window_storage_(static_cast<float*>(
|
||||
AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))),
|
||||
input_buffer_(static_cast<float*>(
|
||||
AlignedMalloc(sizeof(float) * input_buffer_size_, 16))),
|
||||
#if defined(WEBRTC_CPU_DETECTION)
|
||||
convolve_proc_(NULL),
|
||||
#endif
|
||||
r1_(input_buffer_.get()),
|
||||
r2_(input_buffer_.get() + kKernelSize / 2) {
|
||||
#if defined(WEBRTC_CPU_DETECTION)
|
||||
InitializeCPUSpecificFeatures();
|
||||
assert(convolve_proc_);
|
||||
#endif
|
||||
assert(request_frames_ > 0);
|
||||
Flush();
|
||||
assert(block_size_ > kKernelSize);
|
||||
|
||||
memset(kernel_storage_.get(), 0,
|
||||
sizeof(*kernel_storage_.get()) * kKernelStorageSize);
|
||||
memset(kernel_pre_sinc_storage_.get(), 0,
|
||||
sizeof(*kernel_pre_sinc_storage_.get()) * kKernelStorageSize);
|
||||
memset(kernel_window_storage_.get(), 0,
|
||||
sizeof(*kernel_window_storage_.get()) * kKernelStorageSize);
|
||||
|
||||
InitializeKernel();
|
||||
}
|
||||
|
||||
SincResampler::~SincResampler() {}
|
||||
|
||||
void SincResampler::UpdateRegions(bool second_load) {
|
||||
// Setup various region pointers in the buffer (see diagram above). If we're
|
||||
// on the second load we need to slide r0_ to the right by kKernelSize / 2.
|
||||
r0_ = input_buffer_.get() + (second_load ? kKernelSize : kKernelSize / 2);
|
||||
r3_ = r0_ + request_frames_ - kKernelSize;
|
||||
r4_ = r0_ + request_frames_ - kKernelSize / 2;
|
||||
block_size_ = r4_ - r2_;
|
||||
|
||||
// r1_ at the beginning of the buffer.
|
||||
assert(r1_ == input_buffer_.get());
|
||||
// r1_ left of r2_, r4_ left of r3_ and size correct.
|
||||
assert(r2_ - r1_ == r4_ - r3_);
|
||||
// r2_ left of r3.
|
||||
assert(r2_ < r3_);
|
||||
}
|
||||
|
||||
void SincResampler::InitializeKernel() {
|
||||
// Blackman window parameters.
|
||||
static const double kAlpha = 0.16;
|
||||
static const double kA0 = 0.5 * (1.0 - kAlpha);
|
||||
static const double kA1 = 0.5;
|
||||
static const double kA2 = 0.5 * kAlpha;
|
||||
|
||||
// Generates a set of windowed sinc() kernels.
|
||||
// We generate a range of sub-sample offsets from 0.0 to 1.0.
|
||||
const double sinc_scale_factor = SincScaleFactor(io_sample_rate_ratio_);
|
||||
for (size_t offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) {
|
||||
const float subsample_offset =
|
||||
static_cast<float>(offset_idx) / kKernelOffsetCount;
|
||||
|
||||
for (size_t i = 0; i < kKernelSize; ++i) {
|
||||
const size_t idx = i + offset_idx * kKernelSize;
|
||||
const float pre_sinc = static_cast<float>(M_PI *
|
||||
(static_cast<int>(i) - static_cast<int>(kKernelSize / 2) -
|
||||
subsample_offset));
|
||||
kernel_pre_sinc_storage_[idx] = pre_sinc;
|
||||
|
||||
// Compute Blackman window, matching the offset of the sinc().
|
||||
const float x = (i - subsample_offset) / kKernelSize;
|
||||
const float window = static_cast<float>(kA0 - kA1 * cos(2.0 * M_PI * x) +
|
||||
kA2 * cos(4.0 * M_PI * x));
|
||||
kernel_window_storage_[idx] = window;
|
||||
|
||||
// Compute the sinc with offset, then window the sinc() function and store
|
||||
// at the correct offset.
|
||||
kernel_storage_[idx] = static_cast<float>(window *
|
||||
((pre_sinc == 0) ?
|
||||
sinc_scale_factor :
|
||||
(sin(sinc_scale_factor * pre_sinc) / pre_sinc)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SincResampler::SetRatio(double io_sample_rate_ratio) {
|
||||
if (fabs(io_sample_rate_ratio_ - io_sample_rate_ratio) <
|
||||
std::numeric_limits<double>::epsilon()) {
|
||||
return;
|
||||
}
|
||||
|
||||
io_sample_rate_ratio_ = io_sample_rate_ratio;
|
||||
|
||||
// Optimize reinitialization by reusing values which are independent of
|
||||
// |sinc_scale_factor|. Provides a 3x speedup.
|
||||
const double sinc_scale_factor = SincScaleFactor(io_sample_rate_ratio_);
|
||||
for (size_t offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) {
|
||||
for (size_t i = 0; i < kKernelSize; ++i) {
|
||||
const size_t idx = i + offset_idx * kKernelSize;
|
||||
const float window = kernel_window_storage_[idx];
|
||||
const float pre_sinc = kernel_pre_sinc_storage_[idx];
|
||||
|
||||
kernel_storage_[idx] = static_cast<float>(window *
|
||||
((pre_sinc == 0) ?
|
||||
sinc_scale_factor :
|
||||
(sin(sinc_scale_factor * pre_sinc) / pre_sinc)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SincResampler::Resample(size_t frames, float* destination) {
|
||||
size_t remaining_frames = frames;
|
||||
|
||||
// Step (1) -- Prime the input buffer at the start of the input stream.
|
||||
if (!buffer_primed_ && remaining_frames) {
|
||||
read_cb_->Run(request_frames_, r0_);
|
||||
buffer_primed_ = true;
|
||||
}
|
||||
|
||||
// Step (2) -- Resample! const what we can outside of the loop for speed. It
|
||||
// actually has an impact on ARM performance. See inner loop comment below.
|
||||
const double current_io_ratio = io_sample_rate_ratio_;
|
||||
const float* const kernel_ptr = kernel_storage_.get();
|
||||
while (remaining_frames) {
|
||||
// |i| may be negative if the last Resample() call ended on an iteration
|
||||
// that put |virtual_source_idx_| over the limit.
|
||||
//
|
||||
// Note: The loop construct here can severely impact performance on ARM
|
||||
// or when built with clang. See https://codereview.chromium.org/18566009/
|
||||
for (int i = static_cast<int>(
|
||||
ceil((block_size_ - virtual_source_idx_) / current_io_ratio));
|
||||
i > 0; --i) {
|
||||
assert(virtual_source_idx_ < block_size_);
|
||||
|
||||
// |virtual_source_idx_| lies in between two kernel offsets so figure out
|
||||
// what they are.
|
||||
const int source_idx = static_cast<int>(virtual_source_idx_);
|
||||
const double subsample_remainder = virtual_source_idx_ - source_idx;
|
||||
|
||||
const double virtual_offset_idx =
|
||||
subsample_remainder * kKernelOffsetCount;
|
||||
const int offset_idx = static_cast<int>(virtual_offset_idx);
|
||||
|
||||
// We'll compute "convolutions" for the two kernels which straddle
|
||||
// |virtual_source_idx_|.
|
||||
const float* const k1 = kernel_ptr + offset_idx * kKernelSize;
|
||||
const float* const k2 = k1 + kKernelSize;
|
||||
|
||||
// Ensure |k1|, |k2| are 16-byte aligned for SIMD usage. Should always be
|
||||
// true so long as kKernelSize is a multiple of 16.
|
||||
assert(0u == (reinterpret_cast<uintptr_t>(k1) & 0x0F));
|
||||
assert(0u == (reinterpret_cast<uintptr_t>(k2) & 0x0F));
|
||||
|
||||
// Initialize input pointer based on quantized |virtual_source_idx_|.
|
||||
const float* const input_ptr = r1_ + source_idx;
|
||||
|
||||
// Figure out how much to weight each kernel's "convolution".
|
||||
const double kernel_interpolation_factor =
|
||||
virtual_offset_idx - offset_idx;
|
||||
*destination++ = CONVOLVE_FUNC(
|
||||
input_ptr, k1, k2, kernel_interpolation_factor);
|
||||
|
||||
// Advance the virtual index.
|
||||
virtual_source_idx_ += current_io_ratio;
|
||||
|
||||
if (!--remaining_frames)
|
||||
return;
|
||||
}
|
||||
|
||||
// Wrap back around to the start.
|
||||
virtual_source_idx_ -= block_size_;
|
||||
|
||||
// Step (3) -- Copy r3_, r4_ to r1_, r2_.
|
||||
// This wraps the last input frames back to the start of the buffer.
|
||||
memcpy(r1_, r3_, sizeof(*input_buffer_.get()) * kKernelSize);
|
||||
|
||||
// Step (4) -- Reinitialize regions if necessary.
|
||||
if (r0_ == r2_)
|
||||
UpdateRegions(true);
|
||||
|
||||
// Step (5) -- Refresh the buffer with more input.
|
||||
read_cb_->Run(request_frames_, r0_);
|
||||
}
|
||||
}
|
||||
|
||||
#undef CONVOLVE_FUNC
|
||||
|
||||
size_t SincResampler::ChunkSize() const {
|
||||
return static_cast<size_t>(block_size_ / io_sample_rate_ratio_);
|
||||
}
|
||||
|
||||
void SincResampler::Flush() {
|
||||
virtual_source_idx_ = 0;
|
||||
buffer_primed_ = false;
|
||||
memset(input_buffer_.get(), 0,
|
||||
sizeof(*input_buffer_.get()) * input_buffer_size_);
|
||||
UpdateRegions(false);
|
||||
}
|
||||
|
||||
float SincResampler::Convolve_C(const float* input_ptr, const float* k1,
|
||||
const float* k2,
|
||||
double kernel_interpolation_factor) {
|
||||
float sum1 = 0;
|
||||
float sum2 = 0;
|
||||
|
||||
// Generate a single output sample. Unrolling this loop hurt performance in
|
||||
// local testing.
|
||||
size_t n = kKernelSize;
|
||||
while (n--) {
|
||||
sum1 += *input_ptr * *k1++;
|
||||
sum2 += *input_ptr++ * *k2++;
|
||||
}
|
||||
|
||||
// Linearly interpolate the two "convolutions".
|
||||
return static_cast<float>((1.0 - kernel_interpolation_factor) * sum1 +
|
||||
kernel_interpolation_factor * sum2);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
170
webrtc/common_audio/resampler/sinc_resampler.h
Normal file
170
webrtc/common_audio/resampler/sinc_resampler.h
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Modified from the Chromium original here:
|
||||
// src/media/base/sinc_resampler.h
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_
|
||||
#define WEBRTC_COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/system_wrappers/interface/aligned_malloc.h"
|
||||
#include "webrtc/test/testsupport/gtest_prod_util.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Callback class for providing more data into the resampler. Expects |frames|
|
||||
// of data to be rendered into |destination|; zero padded if not enough frames
|
||||
// are available to satisfy the request.
|
||||
class SincResamplerCallback {
|
||||
public:
|
||||
virtual ~SincResamplerCallback() {}
|
||||
virtual void Run(size_t frames, float* destination) = 0;
|
||||
};
|
||||
|
||||
// SincResampler is a high-quality single-channel sample-rate converter.
|
||||
class SincResampler {
|
||||
public:
|
||||
// The kernel size can be adjusted for quality (higher is better) at the
|
||||
// expense of performance. Must be a multiple of 32.
|
||||
// TODO(dalecurtis): Test performance to see if we can jack this up to 64+.
|
||||
static const size_t kKernelSize = 32;
|
||||
|
||||
// Default request size. Affects how often and for how much SincResampler
|
||||
// calls back for input. Must be greater than kKernelSize.
|
||||
static const size_t kDefaultRequestSize = 512;
|
||||
|
||||
// The kernel offset count is used for interpolation and is the number of
|
||||
// sub-sample kernel shifts. Can be adjusted for quality (higher is better)
|
||||
// at the expense of allocating more memory.
|
||||
static const size_t kKernelOffsetCount = 32;
|
||||
static const size_t kKernelStorageSize =
|
||||
kKernelSize * (kKernelOffsetCount + 1);
|
||||
|
||||
// Constructs a SincResampler with the specified |read_cb|, which is used to
|
||||
// acquire audio data for resampling. |io_sample_rate_ratio| is the ratio
|
||||
// of input / output sample rates. |request_frames| controls the size in
|
||||
// frames of the buffer requested by each |read_cb| call. The value must be
|
||||
// greater than kKernelSize. Specify kDefaultRequestSize if there are no
|
||||
// request size constraints.
|
||||
SincResampler(double io_sample_rate_ratio,
|
||||
size_t request_frames,
|
||||
SincResamplerCallback* read_cb);
|
||||
virtual ~SincResampler();
|
||||
|
||||
// Resample |frames| of data from |read_cb_| into |destination|.
|
||||
void Resample(size_t frames, float* destination);
|
||||
|
||||
// The maximum size in frames that guarantees Resample() will only make a
|
||||
// single call to |read_cb_| for more data.
|
||||
size_t ChunkSize() const;
|
||||
|
||||
size_t request_frames() const { return request_frames_; }
|
||||
|
||||
// Flush all buffered data and reset internal indices. Not thread safe, do
|
||||
// not call while Resample() is in progress.
|
||||
void Flush();
|
||||
|
||||
// Update |io_sample_rate_ratio_|. SetRatio() will cause a reconstruction of
|
||||
// the kernels used for resampling. Not thread safe, do not call while
|
||||
// Resample() is in progress.
|
||||
//
|
||||
// TODO(ajm): Use this in PushSincResampler rather than reconstructing
|
||||
// SincResampler. We would also need a way to update |request_frames_|.
|
||||
void SetRatio(double io_sample_rate_ratio);
|
||||
|
||||
float* get_kernel_for_testing() { return kernel_storage_.get(); }
|
||||
|
||||
private:
|
||||
FRIEND_TEST_ALL_PREFIXES(SincResamplerTest, Convolve);
|
||||
FRIEND_TEST_ALL_PREFIXES(SincResamplerTest, ConvolveBenchmark);
|
||||
|
||||
void InitializeKernel();
|
||||
void UpdateRegions(bool second_load);
|
||||
|
||||
// Selects runtime specific CPU features like SSE. Must be called before
|
||||
// using SincResampler.
|
||||
// TODO(ajm): Currently managed by the class internally. See the note with
|
||||
// |convolve_proc_| below.
|
||||
void InitializeCPUSpecificFeatures();
|
||||
|
||||
// Compute convolution of |k1| and |k2| over |input_ptr|, resultant sums are
|
||||
// linearly interpolated using |kernel_interpolation_factor|. On x86 and ARM
|
||||
// the underlying implementation is chosen at run time.
|
||||
static float Convolve_C(const float* input_ptr, const float* k1,
|
||||
const float* k2, double kernel_interpolation_factor);
|
||||
#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
static float Convolve_SSE(const float* input_ptr, const float* k1,
|
||||
const float* k2,
|
||||
double kernel_interpolation_factor);
|
||||
#elif defined(WEBRTC_DETECT_NEON) || defined(WEBRTC_HAS_NEON)
|
||||
static float Convolve_NEON(const float* input_ptr, const float* k1,
|
||||
const float* k2,
|
||||
double kernel_interpolation_factor);
|
||||
#endif
|
||||
|
||||
// The ratio of input / output sample rates.
|
||||
double io_sample_rate_ratio_;
|
||||
|
||||
// An index on the source input buffer with sub-sample precision. It must be
|
||||
// double precision to avoid drift.
|
||||
double virtual_source_idx_;
|
||||
|
||||
// The buffer is primed once at the very beginning of processing.
|
||||
bool buffer_primed_;
|
||||
|
||||
// Source of data for resampling.
|
||||
SincResamplerCallback* read_cb_;
|
||||
|
||||
// The size (in samples) to request from each |read_cb_| execution.
|
||||
const size_t request_frames_;
|
||||
|
||||
// The number of source frames processed per pass.
|
||||
size_t block_size_;
|
||||
|
||||
// The size (in samples) of the internal buffer used by the resampler.
|
||||
const size_t input_buffer_size_;
|
||||
|
||||
// Contains kKernelOffsetCount kernels back-to-back, each of size kKernelSize.
|
||||
// The kernel offsets are sub-sample shifts of a windowed sinc shifted from
|
||||
// 0.0 to 1.0 sample.
|
||||
rtc::scoped_ptr<float[], AlignedFreeDeleter> kernel_storage_;
|
||||
rtc::scoped_ptr<float[], AlignedFreeDeleter> kernel_pre_sinc_storage_;
|
||||
rtc::scoped_ptr<float[], AlignedFreeDeleter> kernel_window_storage_;
|
||||
|
||||
// Data from the source is copied into this buffer for each processing pass.
|
||||
rtc::scoped_ptr<float[], AlignedFreeDeleter> input_buffer_;
|
||||
|
||||
// Stores the runtime selection of which Convolve function to use.
|
||||
// TODO(ajm): Move to using a global static which must only be initialized
|
||||
// once by the user. We're not doing this initially, because we don't have
|
||||
// e.g. a LazyInstance helper in webrtc.
|
||||
#if defined(WEBRTC_CPU_DETECTION)
|
||||
typedef float (*ConvolveProc)(const float*, const float*, const float*,
|
||||
double);
|
||||
ConvolveProc convolve_proc_;
|
||||
#endif
|
||||
|
||||
// Pointers to the various regions inside |input_buffer_|. See the diagram at
|
||||
// the top of the .cc file for more information.
|
||||
float* r0_;
|
||||
float* const r1_;
|
||||
float* const r2_;
|
||||
float* r3_;
|
||||
float* r4_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(SincResampler);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_
|
47
webrtc/common_audio/resampler/sinc_resampler_neon.cc
Normal file
47
webrtc/common_audio/resampler/sinc_resampler_neon.cc
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Modified from the Chromium original:
|
||||
// src/media/base/sinc_resampler.cc
|
||||
|
||||
#include "webrtc/common_audio/resampler/sinc_resampler.h"
|
||||
|
||||
#include <arm_neon.h>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
float SincResampler::Convolve_NEON(const float* input_ptr, const float* k1,
|
||||
const float* k2,
|
||||
double kernel_interpolation_factor) {
|
||||
float32x4_t m_input;
|
||||
float32x4_t m_sums1 = vmovq_n_f32(0);
|
||||
float32x4_t m_sums2 = vmovq_n_f32(0);
|
||||
|
||||
const float* upper = input_ptr + kKernelSize;
|
||||
for (; input_ptr < upper; ) {
|
||||
m_input = vld1q_f32(input_ptr);
|
||||
input_ptr += 4;
|
||||
m_sums1 = vmlaq_f32(m_sums1, m_input, vld1q_f32(k1));
|
||||
k1 += 4;
|
||||
m_sums2 = vmlaq_f32(m_sums2, m_input, vld1q_f32(k2));
|
||||
k2 += 4;
|
||||
}
|
||||
|
||||
// Linearly interpolate the two "convolutions".
|
||||
m_sums1 = vmlaq_f32(
|
||||
vmulq_f32(m_sums1, vmovq_n_f32(1.0 - kernel_interpolation_factor)),
|
||||
m_sums2, vmovq_n_f32(kernel_interpolation_factor));
|
||||
|
||||
// Sum components together.
|
||||
float32x2_t m_half = vadd_f32(vget_high_f32(m_sums1), vget_low_f32(m_sums1));
|
||||
return vget_lane_f32(vpadd_f32(m_half, m_half), 0);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
59
webrtc/common_audio/resampler/sinc_resampler_sse.cc
Normal file
59
webrtc/common_audio/resampler/sinc_resampler_sse.cc
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Modified from the Chromium original:
|
||||
// src/media/base/simd/sinc_resampler_sse.cc
|
||||
|
||||
#include "webrtc/common_audio/resampler/sinc_resampler.h"
|
||||
|
||||
#include <xmmintrin.h>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
float SincResampler::Convolve_SSE(const float* input_ptr, const float* k1,
|
||||
const float* k2,
|
||||
double kernel_interpolation_factor) {
|
||||
__m128 m_input;
|
||||
__m128 m_sums1 = _mm_setzero_ps();
|
||||
__m128 m_sums2 = _mm_setzero_ps();
|
||||
|
||||
// Based on |input_ptr| alignment, we need to use loadu or load. Unrolling
|
||||
// these loops hurt performance in local testing.
|
||||
if (reinterpret_cast<uintptr_t>(input_ptr) & 0x0F) {
|
||||
for (size_t i = 0; i < kKernelSize; i += 4) {
|
||||
m_input = _mm_loadu_ps(input_ptr + i);
|
||||
m_sums1 = _mm_add_ps(m_sums1, _mm_mul_ps(m_input, _mm_load_ps(k1 + i)));
|
||||
m_sums2 = _mm_add_ps(m_sums2, _mm_mul_ps(m_input, _mm_load_ps(k2 + i)));
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < kKernelSize; i += 4) {
|
||||
m_input = _mm_load_ps(input_ptr + i);
|
||||
m_sums1 = _mm_add_ps(m_sums1, _mm_mul_ps(m_input, _mm_load_ps(k1 + i)));
|
||||
m_sums2 = _mm_add_ps(m_sums2, _mm_mul_ps(m_input, _mm_load_ps(k2 + i)));
|
||||
}
|
||||
}
|
||||
|
||||
// Linearly interpolate the two "convolutions".
|
||||
m_sums1 = _mm_mul_ps(m_sums1, _mm_set_ps1(
|
||||
static_cast<float>(1.0 - kernel_interpolation_factor)));
|
||||
m_sums2 = _mm_mul_ps(m_sums2, _mm_set_ps1(
|
||||
static_cast<float>(kernel_interpolation_factor)));
|
||||
m_sums1 = _mm_add_ps(m_sums1, m_sums2);
|
||||
|
||||
// Sum components together.
|
||||
float result;
|
||||
m_sums2 = _mm_add_ps(_mm_movehl_ps(m_sums1, m_sums1), m_sums1);
|
||||
_mm_store_ss(&result, _mm_add_ss(m_sums2, _mm_shuffle_ps(
|
||||
m_sums2, m_sums2, 1)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// MSVC++ requires this to be set before any other includes to get M_PI.
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
#include "webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
SinusoidalLinearChirpSource::SinusoidalLinearChirpSource(int sample_rate,
|
||||
size_t samples,
|
||||
double max_frequency,
|
||||
double delay_samples)
|
||||
: sample_rate_(sample_rate),
|
||||
total_samples_(samples),
|
||||
max_frequency_(max_frequency),
|
||||
current_index_(0),
|
||||
delay_samples_(delay_samples) {
|
||||
// Chirp rate.
|
||||
double duration = static_cast<double>(total_samples_) / sample_rate_;
|
||||
k_ = (max_frequency_ - kMinFrequency) / duration;
|
||||
}
|
||||
|
||||
void SinusoidalLinearChirpSource::Run(size_t frames, float* destination) {
|
||||
for (size_t i = 0; i < frames; ++i, ++current_index_) {
|
||||
// Filter out frequencies higher than Nyquist.
|
||||
if (Frequency(current_index_) > 0.5 * sample_rate_) {
|
||||
destination[i] = 0;
|
||||
} else {
|
||||
// Calculate time in seconds.
|
||||
if (current_index_ < delay_samples_) {
|
||||
destination[i] = 0;
|
||||
} else {
|
||||
// Sinusoidal linear chirp.
|
||||
double t = (current_index_ - delay_samples_) / sample_rate_;
|
||||
destination[i] =
|
||||
sin(2 * M_PI * (kMinFrequency * t + (k_ / 2) * t * t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double SinusoidalLinearChirpSource::Frequency(size_t position) {
|
||||
return kMinFrequency + (position - delay_samples_) *
|
||||
(max_frequency_ - kMinFrequency) / total_samples_;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Modified from the Chromium original here:
|
||||
// src/media/base/sinc_resampler_unittest.cc
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_
|
||||
#define WEBRTC_COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/common_audio/resampler/sinc_resampler.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Fake audio source for testing the resampler. Generates a sinusoidal linear
|
||||
// chirp (http://en.wikipedia.org/wiki/Chirp) which can be tuned to stress the
|
||||
// resampler for the specific sample rate conversion being used.
|
||||
class SinusoidalLinearChirpSource : public SincResamplerCallback {
|
||||
public:
|
||||
// |delay_samples| can be used to insert a fractional sample delay into the
|
||||
// source. It will produce zeros until non-negative time is reached.
|
||||
SinusoidalLinearChirpSource(int sample_rate, size_t samples,
|
||||
double max_frequency, double delay_samples);
|
||||
|
||||
virtual ~SinusoidalLinearChirpSource() {}
|
||||
|
||||
void Run(size_t frames, float* destination) override;
|
||||
|
||||
double Frequency(size_t position);
|
||||
|
||||
private:
|
||||
enum {
|
||||
kMinFrequency = 5
|
||||
};
|
||||
|
||||
int sample_rate_;
|
||||
size_t total_samples_;
|
||||
double max_frequency_;
|
||||
double k_;
|
||||
size_t current_index_;
|
||||
double delay_samples_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(SinusoidalLinearChirpSource);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_
|
247
webrtc/common_audio/ring_buffer.c
Normal file
247
webrtc/common_audio/ring_buffer.c
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// A ring buffer to hold arbitrary data. Provides no thread safety. Unless
|
||||
// otherwise specified, functions return 0 on success and -1 on error.
|
||||
|
||||
#include "webrtc/common_audio/ring_buffer.h"
|
||||
|
||||
#include <stddef.h> // size_t
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
enum Wrap {
|
||||
SAME_WRAP,
|
||||
DIFF_WRAP
|
||||
};
|
||||
|
||||
struct RingBuffer {
|
||||
size_t read_pos;
|
||||
size_t write_pos;
|
||||
size_t element_count;
|
||||
size_t element_size;
|
||||
enum Wrap rw_wrap;
|
||||
char* data;
|
||||
};
|
||||
|
||||
// Get address of region(s) from which we can read data.
|
||||
// If the region is contiguous, |data_ptr_bytes_2| will be zero.
|
||||
// If non-contiguous, |data_ptr_bytes_2| will be the size in bytes of the second
|
||||
// region. Returns room available to be read or |element_count|, whichever is
|
||||
// smaller.
|
||||
static size_t GetBufferReadRegions(RingBuffer* buf,
|
||||
size_t element_count,
|
||||
void** data_ptr_1,
|
||||
size_t* data_ptr_bytes_1,
|
||||
void** data_ptr_2,
|
||||
size_t* data_ptr_bytes_2) {
|
||||
|
||||
const size_t readable_elements = WebRtc_available_read(buf);
|
||||
const size_t read_elements = (readable_elements < element_count ?
|
||||
readable_elements : element_count);
|
||||
const size_t margin = buf->element_count - buf->read_pos;
|
||||
|
||||
// Check to see if read is not contiguous.
|
||||
if (read_elements > margin) {
|
||||
// Write data in two blocks that wrap the buffer.
|
||||
*data_ptr_1 = buf->data + buf->read_pos * buf->element_size;
|
||||
*data_ptr_bytes_1 = margin * buf->element_size;
|
||||
*data_ptr_2 = buf->data;
|
||||
*data_ptr_bytes_2 = (read_elements - margin) * buf->element_size;
|
||||
} else {
|
||||
*data_ptr_1 = buf->data + buf->read_pos * buf->element_size;
|
||||
*data_ptr_bytes_1 = read_elements * buf->element_size;
|
||||
*data_ptr_2 = NULL;
|
||||
*data_ptr_bytes_2 = 0;
|
||||
}
|
||||
|
||||
return read_elements;
|
||||
}
|
||||
|
||||
RingBuffer* WebRtc_CreateBuffer(size_t element_count, size_t element_size) {
|
||||
RingBuffer* self = NULL;
|
||||
if (element_count == 0 || element_size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self = malloc(sizeof(RingBuffer));
|
||||
if (!self) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->data = malloc(element_count * element_size);
|
||||
if (!self->data) {
|
||||
free(self);
|
||||
self = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->element_count = element_count;
|
||||
self->element_size = element_size;
|
||||
WebRtc_InitBuffer(self);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void WebRtc_InitBuffer(RingBuffer* self) {
|
||||
self->read_pos = 0;
|
||||
self->write_pos = 0;
|
||||
self->rw_wrap = SAME_WRAP;
|
||||
|
||||
// Initialize buffer to zeros
|
||||
memset(self->data, 0, self->element_count * self->element_size);
|
||||
}
|
||||
|
||||
void WebRtc_FreeBuffer(void* handle) {
|
||||
RingBuffer* self = (RingBuffer*)handle;
|
||||
if (!self) {
|
||||
return;
|
||||
}
|
||||
|
||||
free(self->data);
|
||||
free(self);
|
||||
}
|
||||
|
||||
size_t WebRtc_ReadBuffer(RingBuffer* self,
|
||||
void** data_ptr,
|
||||
void* data,
|
||||
size_t element_count) {
|
||||
|
||||
if (self == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (data == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
{
|
||||
void* buf_ptr_1 = NULL;
|
||||
void* buf_ptr_2 = NULL;
|
||||
size_t buf_ptr_bytes_1 = 0;
|
||||
size_t buf_ptr_bytes_2 = 0;
|
||||
const size_t read_count = GetBufferReadRegions(self,
|
||||
element_count,
|
||||
&buf_ptr_1,
|
||||
&buf_ptr_bytes_1,
|
||||
&buf_ptr_2,
|
||||
&buf_ptr_bytes_2);
|
||||
|
||||
if (buf_ptr_bytes_2 > 0) {
|
||||
// We have a wrap around when reading the buffer. Copy the buffer data to
|
||||
// |data| and point to it.
|
||||
memcpy(data, buf_ptr_1, buf_ptr_bytes_1);
|
||||
memcpy(((char*) data) + buf_ptr_bytes_1, buf_ptr_2, buf_ptr_bytes_2);
|
||||
buf_ptr_1 = data;
|
||||
} else if (!data_ptr) {
|
||||
// No wrap, but a memcpy was requested.
|
||||
memcpy(data, buf_ptr_1, buf_ptr_bytes_1);
|
||||
}
|
||||
if (data_ptr) {
|
||||
// |buf_ptr_1| == |data| in the case of a wrap.
|
||||
*data_ptr = buf_ptr_1;
|
||||
}
|
||||
|
||||
// Update read position
|
||||
WebRtc_MoveReadPtr(self, (int) read_count);
|
||||
|
||||
return read_count;
|
||||
}
|
||||
}
|
||||
|
||||
size_t WebRtc_WriteBuffer(RingBuffer* self,
|
||||
const void* data,
|
||||
size_t element_count) {
|
||||
if (!self) {
|
||||
return 0;
|
||||
}
|
||||
if (!data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
{
|
||||
const size_t free_elements = WebRtc_available_write(self);
|
||||
const size_t write_elements = (free_elements < element_count ? free_elements
|
||||
: element_count);
|
||||
size_t n = write_elements;
|
||||
const size_t margin = self->element_count - self->write_pos;
|
||||
|
||||
if (write_elements > margin) {
|
||||
// Buffer wrap around when writing.
|
||||
memcpy(self->data + self->write_pos * self->element_size,
|
||||
data, margin * self->element_size);
|
||||
self->write_pos = 0;
|
||||
n -= margin;
|
||||
self->rw_wrap = DIFF_WRAP;
|
||||
}
|
||||
memcpy(self->data + self->write_pos * self->element_size,
|
||||
((const char*) data) + ((write_elements - n) * self->element_size),
|
||||
n * self->element_size);
|
||||
self->write_pos += n;
|
||||
|
||||
return write_elements;
|
||||
}
|
||||
}
|
||||
|
||||
int WebRtc_MoveReadPtr(RingBuffer* self, int element_count) {
|
||||
if (!self) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
{
|
||||
// We need to be able to take care of negative changes, hence use "int"
|
||||
// instead of "size_t".
|
||||
const int free_elements = (int) WebRtc_available_write(self);
|
||||
const int readable_elements = (int) WebRtc_available_read(self);
|
||||
int read_pos = (int) self->read_pos;
|
||||
|
||||
if (element_count > readable_elements) {
|
||||
element_count = readable_elements;
|
||||
}
|
||||
if (element_count < -free_elements) {
|
||||
element_count = -free_elements;
|
||||
}
|
||||
|
||||
read_pos += element_count;
|
||||
if (read_pos > (int) self->element_count) {
|
||||
// Buffer wrap around. Restart read position and wrap indicator.
|
||||
read_pos -= (int) self->element_count;
|
||||
self->rw_wrap = SAME_WRAP;
|
||||
}
|
||||
if (read_pos < 0) {
|
||||
// Buffer wrap around. Restart read position and wrap indicator.
|
||||
read_pos += (int) self->element_count;
|
||||
self->rw_wrap = DIFF_WRAP;
|
||||
}
|
||||
|
||||
self->read_pos = (size_t) read_pos;
|
||||
|
||||
return element_count;
|
||||
}
|
||||
}
|
||||
|
||||
size_t WebRtc_available_read(const RingBuffer* self) {
|
||||
if (!self) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (self->rw_wrap == SAME_WRAP) {
|
||||
return self->write_pos - self->read_pos;
|
||||
} else {
|
||||
return self->element_count - self->read_pos + self->write_pos;
|
||||
}
|
||||
}
|
||||
|
||||
size_t WebRtc_available_write(const RingBuffer* self) {
|
||||
if (!self) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return self->element_count - WebRtc_available_read(self);
|
||||
}
|
66
webrtc/common_audio/ring_buffer.h
Normal file
66
webrtc/common_audio/ring_buffer.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// A ring buffer to hold arbitrary data. Provides no thread safety. Unless
|
||||
// otherwise specified, functions return 0 on success and -1 on error.
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_RING_BUFFER_H_
|
||||
#define WEBRTC_COMMON_AUDIO_RING_BUFFER_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h> // size_t
|
||||
|
||||
typedef struct RingBuffer RingBuffer;
|
||||
|
||||
// Creates and initializes the buffer. Returns NULL on failure.
|
||||
RingBuffer* WebRtc_CreateBuffer(size_t element_count, size_t element_size);
|
||||
void WebRtc_InitBuffer(RingBuffer* handle);
|
||||
void WebRtc_FreeBuffer(void* handle);
|
||||
|
||||
// Reads data from the buffer. The |data_ptr| will point to the address where
|
||||
// it is located. If all |element_count| data are feasible to read without
|
||||
// buffer wrap around |data_ptr| will point to the location in the buffer.
|
||||
// Otherwise, the data will be copied to |data| (memory allocation done by the
|
||||
// user) and |data_ptr| points to the address of |data|. |data_ptr| is only
|
||||
// guaranteed to be valid until the next call to WebRtc_WriteBuffer().
|
||||
//
|
||||
// To force a copying to |data|, pass a NULL |data_ptr|.
|
||||
//
|
||||
// Returns number of elements read.
|
||||
size_t WebRtc_ReadBuffer(RingBuffer* handle,
|
||||
void** data_ptr,
|
||||
void* data,
|
||||
size_t element_count);
|
||||
|
||||
// Writes |data| to buffer and returns the number of elements written.
|
||||
size_t WebRtc_WriteBuffer(RingBuffer* handle, const void* data,
|
||||
size_t element_count);
|
||||
|
||||
// Moves the buffer read position and returns the number of elements moved.
|
||||
// Positive |element_count| moves the read position towards the write position,
|
||||
// that is, flushing the buffer. Negative |element_count| moves the read
|
||||
// position away from the the write position, that is, stuffing the buffer.
|
||||
// Returns number of elements moved.
|
||||
int WebRtc_MoveReadPtr(RingBuffer* handle, int element_count);
|
||||
|
||||
// Returns number of available elements to read.
|
||||
size_t WebRtc_available_read(const RingBuffer* handle);
|
||||
|
||||
// Returns number of available elements for write.
|
||||
size_t WebRtc_available_write(const RingBuffer* handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_RING_BUFFER_H_
|
60
webrtc/common_audio/sparse_fir_filter.cc
Normal file
60
webrtc/common_audio/sparse_fir_filter.cc
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/common_audio/sparse_fir_filter.h"
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
SparseFIRFilter::SparseFIRFilter(const float* nonzero_coeffs,
|
||||
size_t num_nonzero_coeffs,
|
||||
size_t sparsity,
|
||||
size_t offset)
|
||||
: sparsity_(sparsity),
|
||||
offset_(offset),
|
||||
nonzero_coeffs_(nonzero_coeffs, nonzero_coeffs + num_nonzero_coeffs),
|
||||
state_(sparsity_ * (num_nonzero_coeffs - 1) + offset_, 0.f) {
|
||||
RTC_CHECK_GE(num_nonzero_coeffs, 1u);
|
||||
RTC_CHECK_GE(sparsity, 1u);
|
||||
}
|
||||
|
||||
void SparseFIRFilter::Filter(const float* in, size_t length, float* out) {
|
||||
// Convolves the input signal |in| with the filter kernel |nonzero_coeffs_|
|
||||
// taking into account the previous state.
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
out[i] = 0.f;
|
||||
size_t j;
|
||||
for (j = 0; i >= j * sparsity_ + offset_ &&
|
||||
j < nonzero_coeffs_.size(); ++j) {
|
||||
out[i] += in[i - j * sparsity_ - offset_] * nonzero_coeffs_[j];
|
||||
}
|
||||
for (; j < nonzero_coeffs_.size(); ++j) {
|
||||
out[i] += state_[i + (nonzero_coeffs_.size() - j - 1) * sparsity_] *
|
||||
nonzero_coeffs_[j];
|
||||
}
|
||||
}
|
||||
|
||||
// Update current state.
|
||||
if (state_.size() > 0u) {
|
||||
if (length >= state_.size()) {
|
||||
std::memcpy(&state_[0],
|
||||
&in[length - state_.size()],
|
||||
state_.size() * sizeof(*in));
|
||||
} else {
|
||||
std::memmove(&state_[0],
|
||||
&state_[length],
|
||||
(state_.size() - length) * sizeof(state_[0]));
|
||||
std::memcpy(&state_[state_.size() - length], in, length * sizeof(*in));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
52
webrtc/common_audio/sparse_fir_filter.h
Normal file
52
webrtc/common_audio/sparse_fir_filter.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_SPARSE_FIR_FILTER_H_
|
||||
#define WEBRTC_COMMON_AUDIO_SPARSE_FIR_FILTER_H_
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// A Finite Impulse Response filter implementation which takes advantage of a
|
||||
// sparse structure with uniformly distributed non-zero coefficients.
|
||||
class SparseFIRFilter final {
|
||||
public:
|
||||
// |num_nonzero_coeffs| is the number of non-zero coefficients,
|
||||
// |nonzero_coeffs|. They are assumed to be uniformly distributed every
|
||||
// |sparsity| samples and with an initial |offset|. The rest of the filter
|
||||
// coefficients will be assumed zeros. For example, with sparsity = 3, and
|
||||
// offset = 1 the filter coefficients will be:
|
||||
// B = [0 coeffs[0] 0 0 coeffs[1] 0 0 coeffs[2] ... ]
|
||||
// All initial state values will be zeros.
|
||||
SparseFIRFilter(const float* nonzero_coeffs,
|
||||
size_t num_nonzero_coeffs,
|
||||
size_t sparsity,
|
||||
size_t offset);
|
||||
|
||||
// Filters the |in| data supplied.
|
||||
// |out| must be previously allocated and it must be at least of |length|.
|
||||
void Filter(const float* in, size_t length, float* out);
|
||||
|
||||
private:
|
||||
const size_t sparsity_;
|
||||
const size_t offset_;
|
||||
const std::vector<float> nonzero_coeffs_;
|
||||
std::vector<float> state_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(SparseFIRFilter);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_SPARSE_FIR_FILTER_H_
|
174
webrtc/common_audio/wav_file.cc
Normal file
174
webrtc/common_audio/wav_file.cc
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/common_audio/wav_file.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/base/safe_conversions.h"
|
||||
#include "webrtc/common_audio/include/audio_util.h"
|
||||
#include "webrtc/common_audio/wav_header.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// We write 16-bit PCM WAV files.
|
||||
static const WavFormat kWavFormat = kWavFormatPcm;
|
||||
static const int kBytesPerSample = 2;
|
||||
|
||||
// Doesn't take ownership of the file handle and won't close it.
|
||||
class ReadableWavFile : public ReadableWav {
|
||||
public:
|
||||
explicit ReadableWavFile(FILE* file) : file_(file) {}
|
||||
virtual size_t Read(void* buf, size_t num_bytes) {
|
||||
return fread(buf, 1, num_bytes, file_);
|
||||
}
|
||||
|
||||
private:
|
||||
FILE* file_;
|
||||
};
|
||||
|
||||
WavReader::WavReader(const std::string& filename)
|
||||
: file_handle_(fopen(filename.c_str(), "rb")) {
|
||||
RTC_CHECK(file_handle_ && "Could not open wav file for reading.");
|
||||
|
||||
ReadableWavFile readable(file_handle_);
|
||||
WavFormat format;
|
||||
int bytes_per_sample;
|
||||
RTC_CHECK(ReadWavHeader(&readable, &num_channels_, &sample_rate_, &format,
|
||||
&bytes_per_sample, &num_samples_));
|
||||
num_samples_remaining_ = num_samples_;
|
||||
RTC_CHECK_EQ(kWavFormat, format);
|
||||
RTC_CHECK_EQ(kBytesPerSample, bytes_per_sample);
|
||||
}
|
||||
|
||||
WavReader::~WavReader() {
|
||||
Close();
|
||||
}
|
||||
|
||||
size_t WavReader::ReadSamples(size_t num_samples, int16_t* samples) {
|
||||
#ifndef WEBRTC_ARCH_LITTLE_ENDIAN
|
||||
#error "Need to convert samples to big-endian when reading from WAV file"
|
||||
#endif
|
||||
// There could be metadata after the audio; ensure we don't read it.
|
||||
num_samples = std::min(rtc::checked_cast<uint32_t>(num_samples),
|
||||
num_samples_remaining_);
|
||||
const size_t read =
|
||||
fread(samples, sizeof(*samples), num_samples, file_handle_);
|
||||
// If we didn't read what was requested, ensure we've reached the EOF.
|
||||
RTC_CHECK(read == num_samples || feof(file_handle_));
|
||||
RTC_CHECK_LE(read, num_samples_remaining_);
|
||||
num_samples_remaining_ -= rtc::checked_cast<uint32_t>(read);
|
||||
return read;
|
||||
}
|
||||
|
||||
size_t WavReader::ReadSamples(size_t num_samples, float* samples) {
|
||||
static const size_t kChunksize = 4096 / sizeof(uint16_t);
|
||||
size_t read = 0;
|
||||
for (size_t i = 0; i < num_samples; i += kChunksize) {
|
||||
int16_t isamples[kChunksize];
|
||||
size_t chunk = std::min(kChunksize, num_samples - i);
|
||||
chunk = ReadSamples(chunk, isamples);
|
||||
for (size_t j = 0; j < chunk; ++j)
|
||||
samples[i + j] = isamples[j];
|
||||
read += chunk;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
void WavReader::Close() {
|
||||
RTC_CHECK_EQ(0, fclose(file_handle_));
|
||||
file_handle_ = NULL;
|
||||
}
|
||||
|
||||
WavWriter::WavWriter(const std::string& filename, int sample_rate,
|
||||
int num_channels)
|
||||
: sample_rate_(sample_rate),
|
||||
num_channels_(num_channels),
|
||||
num_samples_(0),
|
||||
file_handle_(fopen(filename.c_str(), "wb")) {
|
||||
RTC_CHECK(file_handle_ && "Could not open wav file for writing.");
|
||||
RTC_CHECK(CheckWavParameters(num_channels_, sample_rate_, kWavFormat,
|
||||
kBytesPerSample, num_samples_));
|
||||
|
||||
// Write a blank placeholder header, since we need to know the total number
|
||||
// of samples before we can fill in the real data.
|
||||
static const uint8_t blank_header[kWavHeaderSize] = {0};
|
||||
RTC_CHECK_EQ(1u, fwrite(blank_header, kWavHeaderSize, 1, file_handle_));
|
||||
}
|
||||
|
||||
WavWriter::~WavWriter() {
|
||||
Close();
|
||||
}
|
||||
|
||||
void WavWriter::WriteSamples(const int16_t* samples, size_t num_samples) {
|
||||
#ifndef WEBRTC_ARCH_LITTLE_ENDIAN
|
||||
#error "Need to convert samples to little-endian when writing to WAV file"
|
||||
#endif
|
||||
const size_t written =
|
||||
fwrite(samples, sizeof(*samples), num_samples, file_handle_);
|
||||
RTC_CHECK_EQ(num_samples, written);
|
||||
num_samples_ += static_cast<uint32_t>(written);
|
||||
RTC_CHECK(written <= std::numeric_limits<uint32_t>::max() ||
|
||||
num_samples_ >= written); // detect uint32_t overflow
|
||||
}
|
||||
|
||||
void WavWriter::WriteSamples(const float* samples, size_t num_samples) {
|
||||
static const size_t kChunksize = 4096 / sizeof(uint16_t);
|
||||
for (size_t i = 0; i < num_samples; i += kChunksize) {
|
||||
int16_t isamples[kChunksize];
|
||||
const size_t chunk = std::min(kChunksize, num_samples - i);
|
||||
FloatS16ToS16(samples + i, chunk, isamples);
|
||||
WriteSamples(isamples, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
void WavWriter::Close() {
|
||||
RTC_CHECK_EQ(0, fseek(file_handle_, 0, SEEK_SET));
|
||||
uint8_t header[kWavHeaderSize];
|
||||
WriteWavHeader(header, num_channels_, sample_rate_, kWavFormat,
|
||||
kBytesPerSample, num_samples_);
|
||||
RTC_CHECK_EQ(1u, fwrite(header, kWavHeaderSize, 1, file_handle_));
|
||||
RTC_CHECK_EQ(0, fclose(file_handle_));
|
||||
file_handle_ = NULL;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
rtc_WavWriter* rtc_WavOpen(const char* filename,
|
||||
int sample_rate,
|
||||
int num_channels) {
|
||||
return reinterpret_cast<rtc_WavWriter*>(
|
||||
new webrtc::WavWriter(filename, sample_rate, num_channels));
|
||||
}
|
||||
|
||||
void rtc_WavClose(rtc_WavWriter* wf) {
|
||||
delete reinterpret_cast<webrtc::WavWriter*>(wf);
|
||||
}
|
||||
|
||||
void rtc_WavWriteSamples(rtc_WavWriter* wf,
|
||||
const float* samples,
|
||||
size_t num_samples) {
|
||||
reinterpret_cast<webrtc::WavWriter*>(wf)->WriteSamples(samples, num_samples);
|
||||
}
|
||||
|
||||
int rtc_WavSampleRate(const rtc_WavWriter* wf) {
|
||||
return reinterpret_cast<const webrtc::WavWriter*>(wf)->sample_rate();
|
||||
}
|
||||
|
||||
int rtc_WavNumChannels(const rtc_WavWriter* wf) {
|
||||
return reinterpret_cast<const webrtc::WavWriter*>(wf)->num_channels();
|
||||
}
|
||||
|
||||
uint32_t rtc_WavNumSamples(const rtc_WavWriter* wf) {
|
||||
return reinterpret_cast<const webrtc::WavWriter*>(wf)->num_samples();
|
||||
}
|
115
webrtc/common_audio/wav_file.h
Normal file
115
webrtc/common_audio/wav_file.h
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_WAV_FILE_H_
|
||||
#define WEBRTC_COMMON_AUDIO_WAV_FILE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Interface to provide access to WAV file parameters.
|
||||
class WavFile {
|
||||
public:
|
||||
virtual ~WavFile() {}
|
||||
|
||||
virtual int sample_rate() const = 0;
|
||||
virtual int num_channels() const = 0;
|
||||
virtual uint32_t num_samples() const = 0;
|
||||
};
|
||||
|
||||
// Simple C++ class for writing 16-bit PCM WAV files. All error handling is
|
||||
// by calls to RTC_CHECK(), making it unsuitable for anything but debug code.
|
||||
class WavWriter final : public WavFile {
|
||||
public:
|
||||
// Open a new WAV file for writing.
|
||||
WavWriter(const std::string& filename, int sample_rate, int num_channels);
|
||||
|
||||
// Close the WAV file, after writing its header.
|
||||
~WavWriter();
|
||||
|
||||
// Write additional samples to the file. Each sample is in the range
|
||||
// [-32768,32767], and there must be the previously specified number of
|
||||
// interleaved channels.
|
||||
void WriteSamples(const float* samples, size_t num_samples);
|
||||
void WriteSamples(const int16_t* samples, size_t num_samples);
|
||||
|
||||
int sample_rate() const override { return sample_rate_; }
|
||||
int num_channels() const override { return num_channels_; }
|
||||
uint32_t num_samples() const override { return num_samples_; }
|
||||
|
||||
private:
|
||||
void Close();
|
||||
const int sample_rate_;
|
||||
const int num_channels_;
|
||||
uint32_t num_samples_; // Total number of samples written to file.
|
||||
FILE* file_handle_; // Output file, owned by this class
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(WavWriter);
|
||||
};
|
||||
|
||||
// Follows the conventions of WavWriter.
|
||||
class WavReader final : public WavFile {
|
||||
public:
|
||||
// Opens an existing WAV file for reading.
|
||||
explicit WavReader(const std::string& filename);
|
||||
|
||||
// Close the WAV file.
|
||||
~WavReader();
|
||||
|
||||
// Returns the number of samples read. If this is less than requested,
|
||||
// verifies that the end of the file was reached.
|
||||
size_t ReadSamples(size_t num_samples, float* samples);
|
||||
size_t ReadSamples(size_t num_samples, int16_t* samples);
|
||||
|
||||
int sample_rate() const override { return sample_rate_; }
|
||||
int num_channels() const override { return num_channels_; }
|
||||
uint32_t num_samples() const override { return num_samples_; }
|
||||
|
||||
private:
|
||||
void Close();
|
||||
int sample_rate_;
|
||||
int num_channels_;
|
||||
uint32_t num_samples_; // Total number of samples in the file.
|
||||
uint32_t num_samples_remaining_;
|
||||
FILE* file_handle_; // Input file, owned by this class.
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(WavReader);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
// C wrappers for the WavWriter class.
|
||||
typedef struct rtc_WavWriter rtc_WavWriter;
|
||||
rtc_WavWriter* rtc_WavOpen(const char* filename,
|
||||
int sample_rate,
|
||||
int num_channels);
|
||||
void rtc_WavClose(rtc_WavWriter* wf);
|
||||
void rtc_WavWriteSamples(rtc_WavWriter* wf,
|
||||
const float* samples,
|
||||
size_t num_samples);
|
||||
int rtc_WavSampleRate(const rtc_WavWriter* wf);
|
||||
int rtc_WavNumChannels(const rtc_WavWriter* wf);
|
||||
uint32_t rtc_WavNumSamples(const rtc_WavWriter* wf);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_WAV_FILE_H_
|
242
webrtc/common_audio/wav_header.cc
Normal file
242
webrtc/common_audio/wav_header.cc
Normal file
@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Based on the WAV file format documentation at
|
||||
// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ and
|
||||
// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
|
||||
|
||||
#include "webrtc/common_audio/wav_header.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/common_audio/include/audio_util.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
struct ChunkHeader {
|
||||
uint32_t ID;
|
||||
uint32_t Size;
|
||||
};
|
||||
static_assert(sizeof(ChunkHeader) == 8, "ChunkHeader size");
|
||||
|
||||
// We can't nest this definition in WavHeader, because VS2013 gives an error
|
||||
// on sizeof(WavHeader::fmt): "error C2070: 'unknown': illegal sizeof operand".
|
||||
struct FmtSubchunk {
|
||||
ChunkHeader header;
|
||||
uint16_t AudioFormat;
|
||||
uint16_t NumChannels;
|
||||
uint32_t SampleRate;
|
||||
uint32_t ByteRate;
|
||||
uint16_t BlockAlign;
|
||||
uint16_t BitsPerSample;
|
||||
};
|
||||
static_assert(sizeof(FmtSubchunk) == 24, "FmtSubchunk size");
|
||||
const uint32_t kFmtSubchunkSize = sizeof(FmtSubchunk) - sizeof(ChunkHeader);
|
||||
|
||||
struct WavHeader {
|
||||
struct {
|
||||
ChunkHeader header;
|
||||
uint32_t Format;
|
||||
} riff;
|
||||
FmtSubchunk fmt;
|
||||
struct {
|
||||
ChunkHeader header;
|
||||
} data;
|
||||
};
|
||||
static_assert(sizeof(WavHeader) == kWavHeaderSize, "no padding in header");
|
||||
|
||||
} // namespace
|
||||
|
||||
bool CheckWavParameters(int num_channels,
|
||||
int sample_rate,
|
||||
WavFormat format,
|
||||
int bytes_per_sample,
|
||||
uint32_t num_samples) {
|
||||
// num_channels, sample_rate, and bytes_per_sample must be positive, must fit
|
||||
// in their respective fields, and their product must fit in the 32-bit
|
||||
// ByteRate field.
|
||||
if (num_channels <= 0 || sample_rate <= 0 || bytes_per_sample <= 0)
|
||||
return false;
|
||||
if (static_cast<uint64_t>(sample_rate) > std::numeric_limits<uint32_t>::max())
|
||||
return false;
|
||||
if (static_cast<uint64_t>(num_channels) >
|
||||
std::numeric_limits<uint16_t>::max())
|
||||
return false;
|
||||
if (static_cast<uint64_t>(bytes_per_sample) * 8 >
|
||||
std::numeric_limits<uint16_t>::max())
|
||||
return false;
|
||||
if (static_cast<uint64_t>(sample_rate) * num_channels * bytes_per_sample >
|
||||
std::numeric_limits<uint32_t>::max())
|
||||
return false;
|
||||
|
||||
// format and bytes_per_sample must agree.
|
||||
switch (format) {
|
||||
case kWavFormatPcm:
|
||||
// Other values may be OK, but for now we're conservative:
|
||||
if (bytes_per_sample != 1 && bytes_per_sample != 2)
|
||||
return false;
|
||||
break;
|
||||
case kWavFormatALaw:
|
||||
case kWavFormatMuLaw:
|
||||
if (bytes_per_sample != 1)
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// The number of bytes in the file, not counting the first ChunkHeader, must
|
||||
// be less than 2^32; otherwise, the ChunkSize field overflows.
|
||||
const uint32_t max_samples =
|
||||
(std::numeric_limits<uint32_t>::max()
|
||||
- (kWavHeaderSize - sizeof(ChunkHeader))) /
|
||||
bytes_per_sample;
|
||||
if (num_samples > max_samples)
|
||||
return false;
|
||||
|
||||
// Each channel must have the same number of samples.
|
||||
if (num_samples % num_channels != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef WEBRTC_ARCH_LITTLE_ENDIAN
|
||||
static inline void WriteLE16(uint16_t* f, uint16_t x) { *f = x; }
|
||||
static inline void WriteLE32(uint32_t* f, uint32_t x) { *f = x; }
|
||||
static inline void WriteFourCC(uint32_t* f, char a, char b, char c, char d) {
|
||||
*f = static_cast<uint32_t>(a)
|
||||
| static_cast<uint32_t>(b) << 8
|
||||
| static_cast<uint32_t>(c) << 16
|
||||
| static_cast<uint32_t>(d) << 24;
|
||||
}
|
||||
|
||||
static inline uint16_t ReadLE16(uint16_t x) { return x; }
|
||||
static inline uint32_t ReadLE32(uint32_t x) { return x; }
|
||||
static inline std::string ReadFourCC(uint32_t x) {
|
||||
return std::string(reinterpret_cast<char*>(&x), 4);
|
||||
}
|
||||
#else
|
||||
#error "Write be-to-le conversion functions"
|
||||
#endif
|
||||
|
||||
static inline uint32_t RiffChunkSize(uint32_t bytes_in_payload) {
|
||||
return bytes_in_payload + kWavHeaderSize - sizeof(ChunkHeader);
|
||||
}
|
||||
|
||||
static inline uint32_t ByteRate(int num_channels, int sample_rate,
|
||||
int bytes_per_sample) {
|
||||
return static_cast<uint32_t>(num_channels) * sample_rate * bytes_per_sample;
|
||||
}
|
||||
|
||||
static inline uint16_t BlockAlign(int num_channels, int bytes_per_sample) {
|
||||
return num_channels * bytes_per_sample;
|
||||
}
|
||||
|
||||
void WriteWavHeader(uint8_t* buf,
|
||||
int num_channels,
|
||||
int sample_rate,
|
||||
WavFormat format,
|
||||
int bytes_per_sample,
|
||||
uint32_t num_samples) {
|
||||
RTC_CHECK(CheckWavParameters(num_channels, sample_rate, format,
|
||||
bytes_per_sample, num_samples));
|
||||
|
||||
WavHeader header;
|
||||
const uint32_t bytes_in_payload = bytes_per_sample * num_samples;
|
||||
|
||||
WriteFourCC(&header.riff.header.ID, 'R', 'I', 'F', 'F');
|
||||
WriteLE32(&header.riff.header.Size, RiffChunkSize(bytes_in_payload));
|
||||
WriteFourCC(&header.riff.Format, 'W', 'A', 'V', 'E');
|
||||
|
||||
WriteFourCC(&header.fmt.header.ID, 'f', 'm', 't', ' ');
|
||||
WriteLE32(&header.fmt.header.Size, kFmtSubchunkSize);
|
||||
WriteLE16(&header.fmt.AudioFormat, format);
|
||||
WriteLE16(&header.fmt.NumChannels, num_channels);
|
||||
WriteLE32(&header.fmt.SampleRate, sample_rate);
|
||||
WriteLE32(&header.fmt.ByteRate, ByteRate(num_channels, sample_rate,
|
||||
bytes_per_sample));
|
||||
WriteLE16(&header.fmt.BlockAlign, BlockAlign(num_channels, bytes_per_sample));
|
||||
WriteLE16(&header.fmt.BitsPerSample, 8 * bytes_per_sample);
|
||||
|
||||
WriteFourCC(&header.data.header.ID, 'd', 'a', 't', 'a');
|
||||
WriteLE32(&header.data.header.Size, bytes_in_payload);
|
||||
|
||||
// Do an extra copy rather than writing everything to buf directly, since buf
|
||||
// might not be correctly aligned.
|
||||
memcpy(buf, &header, kWavHeaderSize);
|
||||
}
|
||||
|
||||
bool ReadWavHeader(ReadableWav* readable,
|
||||
int* num_channels,
|
||||
int* sample_rate,
|
||||
WavFormat* format,
|
||||
int* bytes_per_sample,
|
||||
uint32_t* num_samples) {
|
||||
WavHeader header;
|
||||
if (readable->Read(&header, kWavHeaderSize - sizeof(header.data)) !=
|
||||
kWavHeaderSize - sizeof(header.data))
|
||||
return false;
|
||||
|
||||
const uint32_t fmt_size = ReadLE32(header.fmt.header.Size);
|
||||
if (fmt_size != kFmtSubchunkSize) {
|
||||
// There is an optional two-byte extension field permitted to be present
|
||||
// with PCM, but which must be zero.
|
||||
int16_t ext_size;
|
||||
if (kFmtSubchunkSize + sizeof(ext_size) != fmt_size)
|
||||
return false;
|
||||
if (readable->Read(&ext_size, sizeof(ext_size)) != sizeof(ext_size))
|
||||
return false;
|
||||
if (ext_size != 0)
|
||||
return false;
|
||||
}
|
||||
if (readable->Read(&header.data, sizeof(header.data)) != sizeof(header.data))
|
||||
return false;
|
||||
|
||||
// Parse needed fields.
|
||||
*format = static_cast<WavFormat>(ReadLE16(header.fmt.AudioFormat));
|
||||
*num_channels = ReadLE16(header.fmt.NumChannels);
|
||||
*sample_rate = ReadLE32(header.fmt.SampleRate);
|
||||
*bytes_per_sample = ReadLE16(header.fmt.BitsPerSample) / 8;
|
||||
const uint32_t bytes_in_payload = ReadLE32(header.data.header.Size);
|
||||
if (*bytes_per_sample <= 0)
|
||||
return false;
|
||||
*num_samples = bytes_in_payload / *bytes_per_sample;
|
||||
|
||||
// Sanity check remaining fields.
|
||||
if (ReadFourCC(header.riff.header.ID) != "RIFF")
|
||||
return false;
|
||||
if (ReadFourCC(header.riff.Format) != "WAVE")
|
||||
return false;
|
||||
if (ReadFourCC(header.fmt.header.ID) != "fmt ")
|
||||
return false;
|
||||
if (ReadFourCC(header.data.header.ID) != "data")
|
||||
return false;
|
||||
|
||||
if (ReadLE32(header.riff.header.Size) < RiffChunkSize(bytes_in_payload))
|
||||
return false;
|
||||
if (ReadLE32(header.fmt.ByteRate) !=
|
||||
ByteRate(*num_channels, *sample_rate, *bytes_per_sample))
|
||||
return false;
|
||||
if (ReadLE16(header.fmt.BlockAlign) !=
|
||||
BlockAlign(*num_channels, *bytes_per_sample))
|
||||
return false;
|
||||
|
||||
return CheckWavParameters(*num_channels, *sample_rate, *format,
|
||||
*bytes_per_sample, *num_samples);
|
||||
}
|
||||
|
||||
|
||||
} // namespace webrtc
|
64
webrtc/common_audio/wav_header.h
Normal file
64
webrtc/common_audio/wav_header.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_WAV_HEADER_H_
|
||||
#define WEBRTC_COMMON_AUDIO_WAV_HEADER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
static const size_t kWavHeaderSize = 44;
|
||||
|
||||
class ReadableWav {
|
||||
public:
|
||||
// Returns the number of bytes read.
|
||||
size_t virtual Read(void* buf, size_t num_bytes) = 0;
|
||||
virtual ~ReadableWav() {}
|
||||
};
|
||||
|
||||
enum WavFormat {
|
||||
kWavFormatPcm = 1, // PCM, each sample of size bytes_per_sample
|
||||
kWavFormatALaw = 6, // 8-bit ITU-T G.711 A-law
|
||||
kWavFormatMuLaw = 7, // 8-bit ITU-T G.711 mu-law
|
||||
};
|
||||
|
||||
// Return true if the given parameters will make a well-formed WAV header.
|
||||
bool CheckWavParameters(int num_channels,
|
||||
int sample_rate,
|
||||
WavFormat format,
|
||||
int bytes_per_sample,
|
||||
uint32_t num_samples);
|
||||
|
||||
// Write a kWavHeaderSize bytes long WAV header to buf. The payload that
|
||||
// follows the header is supposed to have the specified number of interleaved
|
||||
// channels and contain the specified total number of samples of the specified
|
||||
// type. CHECKs the input parameters for validity.
|
||||
void WriteWavHeader(uint8_t* buf,
|
||||
int num_channels,
|
||||
int sample_rate,
|
||||
WavFormat format,
|
||||
int bytes_per_sample,
|
||||
uint32_t num_samples);
|
||||
|
||||
// Read a WAV header from an implemented ReadableWav and parse the values into
|
||||
// the provided output parameters. ReadableWav is used because the header can
|
||||
// be variably sized. Returns false if the header is invalid.
|
||||
bool ReadWavHeader(ReadableWav* readable,
|
||||
int* num_channels,
|
||||
int* sample_rate,
|
||||
WavFormat* format,
|
||||
int* bytes_per_sample,
|
||||
uint32_t* num_samples);
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_WAV_HEADER_H_
|
72
webrtc/common_audio/window_generator.cc
Normal file
72
webrtc/common_audio/window_generator.cc
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
#include "webrtc/common_audio/window_generator.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
|
||||
using std::complex;
|
||||
|
||||
namespace {
|
||||
|
||||
// Modified Bessel function of order 0 for complex inputs.
|
||||
complex<float> I0(complex<float> x) {
|
||||
complex<float> y = x / 3.75f;
|
||||
y *= y;
|
||||
return 1.0f + y * (
|
||||
3.5156229f + y * (
|
||||
3.0899424f + y * (
|
||||
1.2067492f + y * (
|
||||
0.2659732f + y * (
|
||||
0.360768e-1f + y * 0.45813e-2f)))));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
void WindowGenerator::Hanning(int length, float* window) {
|
||||
RTC_CHECK_GT(length, 1);
|
||||
RTC_CHECK(window != nullptr);
|
||||
for (int i = 0; i < length; ++i) {
|
||||
window[i] = 0.5f * (1 - cosf(2 * static_cast<float>(M_PI) * i /
|
||||
(length - 1)));
|
||||
}
|
||||
}
|
||||
|
||||
void WindowGenerator::KaiserBesselDerived(float alpha, size_t length,
|
||||
float* window) {
|
||||
RTC_CHECK_GT(length, 1U);
|
||||
RTC_CHECK(window != nullptr);
|
||||
|
||||
const size_t half = (length + 1) / 2;
|
||||
float sum = 0.0f;
|
||||
|
||||
for (size_t i = 0; i <= half; ++i) {
|
||||
complex<float> r = (4.0f * i) / length - 1.0f;
|
||||
sum += I0(static_cast<float>(M_PI) * alpha * sqrt(1.0f - r * r)).real();
|
||||
window[i] = sum;
|
||||
}
|
||||
for (size_t i = length - 1; i >= half; --i) {
|
||||
window[length - i - 1] = sqrtf(window[length - i - 1] / sum);
|
||||
window[i] = window[length - i - 1];
|
||||
}
|
||||
if (length % 2 == 1) {
|
||||
window[half - 1] = sqrtf(window[half - 1] / sum);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
33
webrtc/common_audio/window_generator.h
Normal file
33
webrtc/common_audio/window_generator.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_COMMON_AUDIO_WINDOW_GENERATOR_H_
|
||||
#define WEBRTC_COMMON_AUDIO_WINDOW_GENERATOR_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Helper class with generators for various signal transform windows.
|
||||
class WindowGenerator {
|
||||
public:
|
||||
static void Hanning(int length, float* window);
|
||||
static void KaiserBesselDerived(float alpha, size_t length, float* window);
|
||||
|
||||
private:
|
||||
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(WindowGenerator);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_COMMON_AUDIO_WINDOW_GENERATOR_H_
|
||||
|
Reference in New Issue
Block a user