Add WAV and arbitrary geometry support to nlbf test.

This adds functionality from audioproc_float. The geometry parsing code
is now shared from test_utils.h. I removed the "mic_spacing" flag from
audioproc_float because it's a redundancy that I suspect isn't very
useful.

Includes a cleanup of the audio_processing test utils. They're now
packaged in targets, with the protobuf-using ones split out to avoid
requiring users to depend on protobufs.

pcm_utils is no longer needed and removed.

The primary motivation for this CL is that AudioProcessing currently
doesn't support more than two channels and we'd like a way to pass
more channels to the beamformer.

R=aluebs@webrtc.org, mgraczyk@chromium.org

Review URL: https://webrtc-codereview.appspot.com/50899004

Cr-Commit-Position: refs/heads/master@{#9157}
This commit is contained in:
Andrew MacDonald 2015-05-07 22:17:51 -07:00
parent d3ddc1b69e
commit cb05b72eb2
14 changed files with 405 additions and 436 deletions

View File

@ -8,6 +8,18 @@
{
'targets': [
{
'target_name': 'audioproc_test_utils',
'type': 'static_library',
'dependencies': [
'<(webrtc_root)/base/base.gyp:rtc_base_approved',
'<(webrtc_root)/common_audio/common_audio.gyp:common_audio',
],
'sources': [
'test/test_utils.cc',
'test/test_utils.h',
],
},
{
'target_name': 'transient_suppression_test',
'type': 'executable',
@ -39,13 +51,12 @@
'target_name': 'nonlinear_beamformer_test',
'type': 'executable',
'dependencies': [
'audioproc_test_utils',
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
'<(webrtc_root)/modules/modules.gyp:audio_processing',
],
'sources': [
'beamformer/nonlinear_beamformer_test.cc',
'beamformer/pcm_utils.cc',
'beamformer/pcm_utils.h',
],
}, # nonlinear_beamformer_test
],
@ -65,12 +76,25 @@
},
'includes': [ '../../build/protoc.gypi', ],
},
{
'target_name': 'audioproc_protobuf_utils',
'type': 'static_library',
'dependencies': [
'audioproc_debug_proto',
],
'sources': [
'test/protobuf_utils.cc',
'test/protobuf_utils.h',
],
},
{
'target_name': 'audioproc',
'type': 'executable',
'dependencies': [
'audio_processing',
'audioproc_debug_proto',
'audioproc_test_utils',
'audioproc_protobuf_utils',
'<(DEPTH)/testing/gtest.gyp:gtest',
'<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers',
'<(webrtc_root)/test/test.gyp:test_support',
@ -83,6 +107,8 @@
'dependencies': [
'audio_processing',
'audioproc_debug_proto',
'audioproc_test_utils',
'audioproc_protobuf_utils',
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
],
'sources': [ 'test/audioproc_float.cc', ],
@ -92,6 +118,8 @@
'type': 'executable',
'dependencies': [
'audioproc_debug_proto',
'audioproc_test_utils',
'audioproc_protobuf_utils',
'<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers',
'<(webrtc_root)/common_audio/common_audio.gyp:common_audio',
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',

View File

@ -294,7 +294,7 @@ void NonlinearBeamformer::InitInterfCovMats() {
}
void NonlinearBeamformer::ProcessChunk(const ChannelBuffer<float>& input,
ChannelBuffer<float>* output) {
ChannelBuffer<float>* output) {
DCHECK_EQ(input.num_channels(), num_input_channels_);
DCHECK_EQ(input.num_frames_per_band(), chunk_length_);
@ -324,10 +324,10 @@ void NonlinearBeamformer::ProcessChunk(const ChannelBuffer<float>& input,
}
void NonlinearBeamformer::ProcessAudioBlock(const complex_f* const* input,
int num_input_channels,
int num_freq_bins,
int num_output_channels,
complex_f* const* output) {
int num_input_channels,
int num_freq_bins,
int num_output_channels,
complex_f* const* output) {
CHECK_EQ(num_freq_bins, kNumFreqBins);
CHECK_EQ(num_input_channels, num_input_channels_);
CHECK_EQ(num_output_channels, 1);
@ -398,7 +398,7 @@ float NonlinearBeamformer::CalculatePostfilterMask(
}
void NonlinearBeamformer::ApplyMasks(const complex_f* const* input,
complex_f* const* output) {
complex_f* const* output) {
complex_f* output_channel = output[0];
for (int f_ix = 0; f_ix < kNumFreqBins; ++f_ix) {
output_channel[f_ix] = complex_f(0.f, 0.f);

View File

@ -8,75 +8,82 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#include <iostream>
#include <vector>
#include "gflags/gflags.h"
#include "webrtc/base/checks.h"
#include "webrtc/common_audio/channel_buffer.h"
#include "webrtc/common_audio/wav_file.h"
#include "webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.h"
#include "webrtc/modules/audio_processing/beamformer/pcm_utils.h"
#include "webrtc/modules/audio_processing/test/test_utils.h"
DEFINE_int32(sample_rate,
48000,
"The sample rate of the input file. The output"
"file will be of the same sample rate.");
DEFINE_int32(num_input_channels,
2,
"The number of channels in the input file.");
DEFINE_double(mic_spacing,
0.05,
"The spacing between microphones on the chromebook which "
"recorded the input file.");
DEFINE_string(input_file_path,
"input.wav",
"The absolute path to the input file.");
DEFINE_string(output_file_path,
"beamformer_test_output.wav",
"The absolute path to the output file.");
DEFINE_string(i, "", "The name of the input file to read from.");
DEFINE_string(o, "out.wav", "Name of the output file to write to.");
DEFINE_string(mic_positions, "",
"Space delimited cartesian coordinates of microphones in meters. "
"The coordinates of each point are contiguous. "
"For a two element array: \"x1 y1 z1 x2 y2 z2\"");
using webrtc::ChannelBuffer;
namespace webrtc {
namespace {
const int kChunksPerSecond = 100;
const int kChunkSizeMs = 1000 / kChunksPerSecond;
const char kUsage[] =
"Command-line tool to run beamforming on WAV files. The signal is passed\n"
"in as a single band, unlike the audio processing interface which splits\n"
"signals into multiple bands.";
} // namespace
int main(int argc, char* argv[]) {
google::SetUsageMessage(kUsage);
google::ParseCommandLineFlags(&argc, &argv, true);
const float kChunkTimeMilliseconds = 10;
const int kChunkSize = FLAGS_sample_rate / (1000.f / kChunkTimeMilliseconds);
const int kInputSamplesPerChunk = kChunkSize * FLAGS_num_input_channels;
WavReader in_file(FLAGS_i);
WavWriter out_file(FLAGS_o, in_file.sample_rate(), 1);
ChannelBuffer<float> captured_audio_cb(kChunkSize, FLAGS_num_input_channels);
const size_t num_mics = in_file.num_channels();
const std::vector<Point> array_geometry =
ParseArrayGeometry(FLAGS_mic_positions, num_mics);
CHECK_EQ(array_geometry.size(), num_mics);
FILE* read_file = fopen(FLAGS_input_file_path.c_str(), "rb");
if (!read_file) {
std::cerr << "Input file '" << FLAGS_input_file_path << "' not found."
<< std::endl;
return -1;
NonlinearBeamformer bf(array_geometry);
bf.Initialize(kChunkSizeMs, in_file.sample_rate());
printf("Input file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
FLAGS_i.c_str(), in_file.num_channels(), in_file.sample_rate());
printf("Output file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
FLAGS_o.c_str(), out_file.num_channels(), out_file.sample_rate());
ChannelBuffer<float> in_buf(
rtc::CheckedDivExact(in_file.sample_rate(), kChunksPerSecond),
in_file.num_channels());
ChannelBuffer<float> out_buf(
rtc::CheckedDivExact(out_file.sample_rate(), kChunksPerSecond),
out_file.num_channels());
std::vector<float> interleaved(in_buf.size());
while (in_file.ReadSamples(interleaved.size(),
&interleaved[0]) == interleaved.size()) {
FloatS16ToFloat(&interleaved[0], interleaved.size(), &interleaved[0]);
Deinterleave(&interleaved[0], in_buf.num_frames(),
in_buf.num_channels(), in_buf.channels());
bf.ProcessChunk(in_buf, &out_buf);
Interleave(out_buf.channels(), out_buf.num_frames(),
out_buf.num_channels(), &interleaved[0]);
FloatToFloatS16(&interleaved[0], interleaved.size(), &interleaved[0]);
out_file.WriteSamples(&interleaved[0], interleaved.size());
}
// Skipping the .wav header. TODO: Add .wav header parsing.
fseek(read_file, 44, SEEK_SET);
FILE* write_file = fopen(FLAGS_output_file_path.c_str(), "wb");
std::vector<webrtc::Point> array_geometry;
for (int i = 0; i < FLAGS_num_input_channels; ++i) {
array_geometry.push_back(webrtc::Point(i * FLAGS_mic_spacing, 0.f, 0.f));
}
webrtc::NonlinearBeamformer bf(array_geometry);
bf.Initialize(kChunkTimeMilliseconds, FLAGS_sample_rate);
while (true) {
size_t samples_read = webrtc::PcmReadToFloat(read_file,
kInputSamplesPerChunk,
FLAGS_num_input_channels,
captured_audio_cb.channels());
if (static_cast<int>(samples_read) != kInputSamplesPerChunk) {
break;
}
bf.ProcessChunk(captured_audio_cb, &captured_audio_cb);
webrtc::PcmWriteFromFloat(
write_file, kChunkSize, 1, captured_audio_cb.channels());
}
fclose(read_file);
fclose(write_file);
return 0;
}
} // namespace webrtc
int main(int argc, char* argv[]) {
return webrtc::main(argc, argv);
}

View File

@ -1,90 +0,0 @@
/*
* 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/modules/audio_processing/beamformer/pcm_utils.h"
#include "webrtc/base/checks.h"
#include "webrtc/common_audio/include/audio_util.h"
#include "webrtc/common_audio/channel_buffer.h"
namespace webrtc {
size_t PcmRead(FILE* file,
size_t length,
int num_channels,
int16_t* const* buffer) {
CHECK_GE(num_channels, 1);
rtc::scoped_ptr<int16_t[]> interleaved_buffer(new int16_t[length]);
size_t elements_read = fread(interleaved_buffer.get(), sizeof(int16_t),
length, file);
if (elements_read != length) {
// This is only an error if we haven't reached the end of the file.
CHECK_NE(0, feof(file));
}
Deinterleave(interleaved_buffer.get(),
static_cast<int>(elements_read) / num_channels,
num_channels,
buffer);
return elements_read;
}
size_t PcmReadToFloat(FILE* file,
size_t length,
int num_channels,
float* const* buffer) {
CHECK_GE(num_channels, 1);
int num_frames = static_cast<int>(length) / num_channels;
rtc::scoped_ptr<ChannelBuffer<int16_t> > deinterleaved_buffer(
new ChannelBuffer<int16_t>(num_frames, num_channels));
size_t elements_read =
PcmRead(file, length, num_channels, deinterleaved_buffer->channels());
for (int i = 0; i < num_channels; ++i) {
S16ToFloat(deinterleaved_buffer->channels()[i], num_frames, buffer[i]);
}
return elements_read;
}
void PcmWrite(FILE* file,
size_t length,
int num_channels,
const int16_t* const* buffer) {
CHECK_GE(num_channels, 1);
rtc::scoped_ptr<int16_t[]> interleaved_buffer(new int16_t[length]);
Interleave(buffer,
static_cast<int>(length) / num_channels,
num_channels,
interleaved_buffer.get());
CHECK_EQ(length,
fwrite(interleaved_buffer.get(), sizeof(int16_t), length, file));
}
void PcmWriteFromFloat(FILE* file,
size_t length,
int num_channels,
const float* const* buffer) {
CHECK_GE(num_channels, 1);
int num_frames = static_cast<int>(length) / num_channels;
rtc::scoped_ptr<ChannelBuffer<int16_t> > deinterleaved_buffer(
new ChannelBuffer<int16_t>(num_frames, num_channels));
for (int i = 0; i < num_channels; ++i) {
FloatToS16(buffer[i], num_frames, deinterleaved_buffer->channels()[i]);
}
PcmWrite(file, length, num_channels, deinterleaved_buffer->channels());
}
} // namespace webrtc

View File

@ -1,50 +0,0 @@
/*
* 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_BEAMFORMER_PCM_UTILS_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_PCM_UTILS_H_
#include <inttypes.h>
#include <stdio.h>
// Utilities for reading from and writing to multichannel pcm files.
// Assumes a bit depth of 16 and little-endian. Note that in these functions,
// length refers to the number of samples to read from/write to the file,
// such that length / num_channels is the number of frames.
namespace webrtc {
// Reads audio from a pcm into a 2D array: buffer[channel_index][frame_index].
// Returns the number of frames written. If this is less than |length|, it's
// safe to assume the end-of-file was reached, as otherwise this will crash.
// In PcmReadToFloat, the floats are within the range [-1, 1].
size_t PcmRead(FILE* file,
size_t length,
int num_channels,
int16_t* const* buffer);
size_t PcmReadToFloat(FILE* file,
size_t length,
int num_channels,
float* const* buffer);
// Writes to a pcm file. The resulting file contains the channels interleaved.
// Crashes if the correct number of frames aren't written to the file. For
// PcmWriteFromFloat, floats must be within the range [-1, 1].
void PcmWrite(FILE* file,
size_t length,
int num_channels,
const int16_t* const* buffer);
void PcmWriteFromFloat(FILE* file,
size_t length,
int num_channels,
const float* const* buffer);
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_PCM_UTILS_H_

View File

@ -22,6 +22,7 @@
#include "webrtc/modules/audio_processing/beamformer/mock_nonlinear_beamformer.h"
#include "webrtc/modules/audio_processing/common.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/audio_processing/test/protobuf_utils.h"
#include "webrtc/modules/audio_processing/test/test_utils.h"
#include "webrtc/modules/interface/module_common_types.h"
#include "webrtc/system_wrappers/interface/event_wrapper.h"

View File

@ -18,17 +18,16 @@
#include "webrtc/common_audio/channel_buffer.h"
#include "webrtc/common_audio/wav_file.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/audio_processing/test/protobuf_utils.h"
#include "webrtc/modules/audio_processing/test/test_utils.h"
#include "webrtc/system_wrappers/interface/tick_util.h"
DEFINE_string(dump, "", "The name of the debug dump file to read from.");
DEFINE_string(c, "", "The name of the capture input file to read from.");
DEFINE_string(o, "out.wav", "Name of the capture output file to write to.");
DEFINE_int32(o_channels, 0, "Number of output channels. Defaults to input.");
DEFINE_int32(o_sample_rate, 0, "Output sample rate in Hz. Defaults to input.");
DEFINE_double(mic_spacing, 0.0,
"Alternate way to specify mic_positions. "
"Assumes uniform linear array with specified spacings.");
DEFINE_string(i, "", "The name of the input file to read from.");
DEFINE_string(o, "out.wav", "Name of the output file to write to.");
DEFINE_int32(out_channels, 0, "Number of output channels. Defaults to input.");
DEFINE_int32(out_sample_rate, 0,
"Output sample rate in Hz. Defaults to input.");
DEFINE_string(mic_positions, "",
"Space delimited cartesian coordinates of microphones in meters. "
"The coordinates of each point are contiguous. "
@ -46,8 +45,11 @@ DEFINE_int32(ns_level, -1, "Noise suppression level [0 - 3].");
DEFINE_bool(perf, false, "Enable performance tests.");
static const int kChunksPerSecond = 100;
static const char kUsage[] =
namespace webrtc {
namespace {
const int kChunksPerSecond = 100;
const char kUsage[] =
"Command-line tool to run audio processing on WAV files. Accepts either\n"
"an input capture WAV file or protobuf debug dump and writes to an output\n"
"WAV file.\n"
@ -55,76 +57,15 @@ static const char kUsage[] =
"All components are disabled by default. If any bi-directional components\n"
"are enabled, only debug dump files are permitted.";
namespace webrtc {
namespace {
// Returns a vector<T> parsed from whitespace delimited values in to_parse,
// or an empty vector if the string could not be parsed.
template<typename T>
std::vector<T> parse_list(std::string to_parse) {
std::vector<T> values;
std::istringstream str(to_parse);
std::copy(
std::istream_iterator<T>(str),
std::istream_iterator<T>(),
std::back_inserter(values));
return values;
}
// Parses the array geometry from the command line.
//
// If a vector with size != num_mics is returned, an error has occurred and an
// appropriate error message has been printed to stdout.
std::vector<Point> get_array_geometry(size_t num_mics) {
std::vector<Point> result;
result.reserve(num_mics);
if (FLAGS_mic_positions.length()) {
CHECK(FLAGS_mic_spacing == 0.0 &&
"mic_positions and mic_spacing should not both be specified");
const std::vector<float> values = parse_list<float>(FLAGS_mic_positions);
if (values.size() != 3 * num_mics) {
fprintf(stderr,
"Could not parse mic_positions or incorrect number of points.\n");
} else {
for (size_t i = 0; i < values.size(); i += 3) {
double x = values[i + 0];
double y = values[i + 1];
double z = values[i + 2];
result.push_back(Point(x, y, z));
}
}
} else {
if (FLAGS_mic_spacing <= 0) {
fprintf(stderr,
"mic_spacing must a positive value when beamforming is enabled.\n");
} else {
for (size_t i = 0; i < num_mics; ++i) {
result.push_back(Point(i * FLAGS_mic_spacing, 0.f, 0.f));
}
}
}
return result;
}
} // namespace
int main(int argc, char* argv[]) {
{
const std::string program_name = argv[0];
const std::string usage = kUsage;
google::SetUsageMessage(usage);
}
google::SetUsageMessage(kUsage);
google::ParseCommandLineFlags(&argc, &argv, true);
if (!((FLAGS_c == "") ^ (FLAGS_dump == ""))) {
if (!((FLAGS_i == "") ^ (FLAGS_dump == ""))) {
fprintf(stderr,
"An input file must be specified with either -c or -dump.\n");
"An input file must be specified with either -i or -dump.\n");
return 1;
}
if (FLAGS_dump != "") {
@ -132,25 +73,22 @@ int main(int argc, char* argv[]) {
return 1;
}
WavReader c_file(FLAGS_c);
WavReader in_file(FLAGS_i);
// If the output format is uninitialized, use the input format.
int o_channels = FLAGS_o_channels;
if (!o_channels)
o_channels = c_file.num_channels();
int o_sample_rate = FLAGS_o_sample_rate;
if (!o_sample_rate)
o_sample_rate = c_file.sample_rate();
WavWriter o_file(FLAGS_o, o_sample_rate, o_channels);
const int out_channels =
FLAGS_out_channels ? FLAGS_out_channels : in_file.num_channels();
const int out_sample_rate =
FLAGS_out_sample_rate ? FLAGS_out_sample_rate : in_file.sample_rate();
WavWriter out_file(FLAGS_o, out_sample_rate, out_channels);
Config config;
config.Set<ExperimentalNs>(new ExperimentalNs(FLAGS_ts || FLAGS_all));
if (FLAGS_bf || FLAGS_all) {
const size_t num_mics = c_file.num_channels();
const std::vector<Point> array_geometry = get_array_geometry(num_mics);
if (array_geometry.size() != num_mics) {
return 1;
}
const size_t num_mics = in_file.num_channels();
const std::vector<Point> array_geometry =
ParseArrayGeometry(FLAGS_mic_positions, num_mics);
CHECK_EQ(array_geometry.size(), num_mics);
config.Set<Beamforming>(new Beamforming(true, array_geometry));
}
@ -171,46 +109,49 @@ int main(int argc, char* argv[]) {
static_cast<NoiseSuppression::Level>(FLAGS_ns_level)));
printf("Input file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
FLAGS_c.c_str(), c_file.num_channels(), c_file.sample_rate());
FLAGS_i.c_str(), in_file.num_channels(), in_file.sample_rate());
printf("Output file: %s\nChannels: %d, Sample rate: %d Hz\n\n",
FLAGS_o.c_str(), o_file.num_channels(), o_file.sample_rate());
FLAGS_o.c_str(), out_file.num_channels(), out_file.sample_rate());
ChannelBuffer<float> c_buf(c_file.sample_rate() / kChunksPerSecond,
c_file.num_channels());
ChannelBuffer<float> o_buf(o_file.sample_rate() / kChunksPerSecond,
o_file.num_channels());
ChannelBuffer<float> in_buf(
rtc::CheckedDivExact(in_file.sample_rate(), kChunksPerSecond),
in_file.num_channels());
ChannelBuffer<float> out_buf(
rtc::CheckedDivExact(out_file.sample_rate(), kChunksPerSecond),
out_file.num_channels());
const size_t c_length =
static_cast<size_t>(c_buf.num_channels() * c_buf.num_frames());
const size_t o_length =
static_cast<size_t>(o_buf.num_channels() * o_buf.num_frames());
rtc::scoped_ptr<float[]> c_interleaved(new float[c_length]);
rtc::scoped_ptr<float[]> o_interleaved(new float[o_length]);
std::vector<float> in_interleaved(in_buf.size());
std::vector<float> out_interleaved(out_buf.size());
TickTime processing_start_time;
TickInterval accumulated_time;
int num_chunks = 0;
while (c_file.ReadSamples(c_length, c_interleaved.get()) == c_length) {
FloatS16ToFloat(c_interleaved.get(), c_length, c_interleaved.get());
Deinterleave(c_interleaved.get(), c_buf.num_frames(),
c_buf.num_channels(), c_buf.channels());
while (in_file.ReadSamples(in_interleaved.size(),
&in_interleaved[0]) == in_interleaved.size()) {
FloatS16ToFloat(&in_interleaved[0], in_interleaved.size(),
&in_interleaved[0]);
Deinterleave(&in_interleaved[0], in_buf.num_frames(),
in_buf.num_channels(), in_buf.channels());
if (FLAGS_perf) {
processing_start_time = TickTime::Now();
}
CHECK_EQ(kNoErr,
ap->ProcessStream(c_buf.channels(),
c_buf.num_frames(),
c_file.sample_rate(),
LayoutFromChannels(c_buf.num_channels()),
o_file.sample_rate(),
LayoutFromChannels(o_buf.num_channels()),
o_buf.channels()));
ap->ProcessStream(in_buf.channels(),
in_buf.num_frames(),
in_file.sample_rate(),
LayoutFromChannels(in_buf.num_channels()),
out_file.sample_rate(),
LayoutFromChannels(out_buf.num_channels()),
out_buf.channels()));
if (FLAGS_perf) {
accumulated_time += TickTime::Now() - processing_start_time;
}
Interleave(o_buf.channels(), o_buf.num_frames(),
o_buf.num_channels(), o_interleaved.get());
FloatToFloatS16(o_interleaved.get(), o_length, o_interleaved.get());
o_file.WriteSamples(o_interleaved.get(), o_length);
Interleave(out_buf.channels(), out_buf.num_frames(),
out_buf.num_channels(), &out_interleaved[0]);
FloatToFloatS16(&out_interleaved[0], out_interleaved.size(),
&out_interleaved[0]);
out_file.WriteSamples(&out_interleaved[0], out_interleaved.size());
num_chunks++;
}
if (FLAGS_perf) {

View File

@ -20,6 +20,7 @@
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/common.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/audio_processing/test/protobuf_utils.h"
#include "webrtc/modules/audio_processing/test/test_utils.h"
#include "webrtc/modules/interface/module_common_types.h"
#include "webrtc/system_wrappers/interface/cpu_features_wrapper.h"
@ -905,7 +906,7 @@ void void_main(int argc, char* argv[]) {
// not reaching end-of-file.
EXPECT_EQ(0, fseek(near_file, read_count * sizeof(int16_t),
SEEK_CUR));
break; // This is expected.
break; // This is expected.
}
} else {
ASSERT_EQ(size, read_count);
@ -948,7 +949,7 @@ void void_main(int argc, char* argv[]) {
}
if (simulating) {
if (read_count != size) {
break; // This is expected.
break; // This is expected.
}
delay_ms = 0;
@ -1040,8 +1041,7 @@ void void_main(int argc, char* argv[]) {
size,
output_wav_file.get(),
output_raw_file.get());
}
else {
} else {
FAIL() << "Event " << event << " is unrecognized";
}
}
@ -1136,8 +1136,7 @@ void void_main(int argc, char* argv[]) {
} // namespace
} // namespace webrtc
int main(int argc, char* argv[])
{
int main(int argc, char* argv[]) {
webrtc::void_main(argc, argv);
// Optional, but removes memory leak noise from Valgrind.

View File

@ -0,0 +1,42 @@
/*
* 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/modules/audio_processing/test/protobuf_utils.h"
namespace webrtc {
size_t ReadMessageBytesFromFile(FILE* file, rtc::scoped_ptr<uint8_t[]>* bytes) {
// The "wire format" for the size is little-endian. Assume we're running on
// a little-endian machine.
#ifndef WEBRTC_ARCH_LITTLE_ENDIAN
#error "Need to convert messsage from little-endian."
#endif
int32_t size = 0;
if (fread(&size, sizeof(size), 1, file) != 1)
return 0;
if (size <= 0)
return 0;
bytes->reset(new uint8_t[size]);
return fread(bytes->get(), sizeof((*bytes)[0]), size, file);
}
// Returns true on success, false on error or end-of-file.
bool ReadMessageFromFile(FILE* file, ::google::protobuf::MessageLite* msg) {
rtc::scoped_ptr<uint8_t[]> bytes;
size_t size = ReadMessageBytesFromFile(file, &bytes);
if (!size)
return false;
msg->Clear();
return msg->ParseFromArray(bytes.get(), size);
}
} // namespace webrtc

View File

@ -0,0 +1,28 @@
/*
* 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_MODULES_AUDIO_PROCESSING_TEST_PROTOBUF_UTILS_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_TEST_PROTOBUF_UTILS_H_
#include "webrtc/audio_processing/debug.pb.h"
#include "webrtc/base/scoped_ptr.h"
namespace webrtc {
// Allocates new memory in the scoped_ptr to fit the raw message and returns the
// number of bytes read.
size_t ReadMessageBytesFromFile(FILE* file, rtc::scoped_ptr<uint8_t[]>* bytes);
// Returns true on success, false on error or end-of-file.
bool ReadMessageFromFile(FILE* file, ::google::protobuf::MessageLite* msg);
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TEST_PROTOBUF_UTILS_H_

View File

@ -0,0 +1,119 @@
/*
* 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/base/checks.h"
#include "webrtc/modules/audio_processing/test/test_utils.h"
namespace webrtc {
RawFile::RawFile(const std::string& filename)
: file_handle_(fopen(filename.c_str(), "wb")) {}
RawFile::~RawFile() {
fclose(file_handle_);
}
void RawFile::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 PCM file"
#endif
fwrite(samples, sizeof(*samples), num_samples, file_handle_);
}
void RawFile::WriteSamples(const float* samples, size_t num_samples) {
fwrite(samples, sizeof(*samples), num_samples, file_handle_);
}
void WriteIntData(const int16_t* data,
size_t length,
WavWriter* wav_file,
RawFile* raw_file) {
if (wav_file) {
wav_file->WriteSamples(data, length);
}
if (raw_file) {
raw_file->WriteSamples(data, length);
}
}
void WriteFloatData(const float* const* data,
int samples_per_channel,
int num_channels,
WavWriter* wav_file,
RawFile* raw_file) {
size_t length = num_channels * samples_per_channel;
rtc::scoped_ptr<float[]> buffer(new float[length]);
Interleave(data, samples_per_channel, num_channels, buffer.get());
if (raw_file) {
raw_file->WriteSamples(buffer.get(), length);
}
// TODO(aluebs): Use ScaleToInt16Range() from audio_util
for (size_t i = 0; i < length; ++i) {
buffer[i] = buffer[i] > 0 ?
buffer[i] * std::numeric_limits<int16_t>::max() :
-buffer[i] * std::numeric_limits<int16_t>::min();
}
if (wav_file) {
wav_file->WriteSamples(buffer.get(), length);
}
}
FILE* OpenFile(const std::string& filename, const char* mode) {
FILE* file = fopen(filename.c_str(), mode);
if (!file) {
printf("Unable to open file %s\n", filename.c_str());
exit(1);
}
return file;
}
int SamplesFromRate(int rate) {
return AudioProcessing::kChunkSizeMs * rate / 1000;
}
void SetFrameSampleRate(AudioFrame* frame,
int sample_rate_hz) {
frame->sample_rate_hz_ = sample_rate_hz;
frame->samples_per_channel_ = AudioProcessing::kChunkSizeMs *
sample_rate_hz / 1000;
}
AudioProcessing::ChannelLayout LayoutFromChannels(int num_channels) {
switch (num_channels) {
case 1:
return AudioProcessing::kMono;
case 2:
return AudioProcessing::kStereo;
default:
assert(false);
return AudioProcessing::kMono;
}
}
std::vector<Point> ParseArrayGeometry(const std::string& mic_positions,
size_t num_mics) {
const std::vector<float> values = ParseList<float>(mic_positions);
CHECK_EQ(values.size(), 3 * num_mics) <<
"Could not parse mic_positions or incorrect number of points.";
std::vector<Point> result;
result.reserve(num_mics);
for (size_t i = 0; i < values.size(); i += 3) {
double x = values[i + 0];
double y = values[i + 1];
double z = values[i + 2];
result.push_back(Point(x, y, z));
}
return result;
}
} // namespace webrtc

View File

@ -8,13 +8,18 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#include <math.h>
#include <limits>
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TEST_TEST_UTILS_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_TEST_TEST_UTILS_H_
#include "webrtc/audio_processing/debug.pb.h"
#include <math.h>
#include <iterator>
#include <limits>
#include <string>
#include <vector>
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/common_audio/channel_buffer.h"
#include "webrtc/common_audio/include/audio_util.h"
#include "webrtc/common_audio/wav_file.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/interface/module_common_types.h"
@ -24,84 +29,38 @@ namespace webrtc {
static const AudioProcessing::Error kNoErr = AudioProcessing::kNoError;
#define EXPECT_NOERR(expr) EXPECT_EQ(kNoErr, (expr))
class RawFile {
class RawFile final {
public:
RawFile(const std::string& filename)
: file_handle_(fopen(filename.c_str(), "wb")) {}
explicit RawFile(const std::string& filename);
~RawFile();
~RawFile() {
fclose(file_handle_);
}
void 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 PCM file"
#endif
fwrite(samples, sizeof(*samples), num_samples, file_handle_);
}
void WriteSamples(const float* samples, size_t num_samples) {
fwrite(samples, sizeof(*samples), num_samples, file_handle_);
}
void WriteSamples(const int16_t* samples, size_t num_samples);
void WriteSamples(const float* samples, size_t num_samples);
private:
FILE* file_handle_;
DISALLOW_COPY_AND_ASSIGN(RawFile);
};
static inline void WriteIntData(const int16_t* data,
size_t length,
WavWriter* wav_file,
RawFile* raw_file) {
if (wav_file) {
wav_file->WriteSamples(data, length);
}
if (raw_file) {
raw_file->WriteSamples(data, length);
}
}
void WriteIntData(const int16_t* data,
size_t length,
WavWriter* wav_file,
RawFile* raw_file);
static inline void WriteFloatData(const float* const* data,
size_t samples_per_channel,
int num_channels,
WavWriter* wav_file,
RawFile* raw_file) {
size_t length = num_channels * samples_per_channel;
rtc::scoped_ptr<float[]> buffer(new float[length]);
Interleave(data, samples_per_channel, num_channels, buffer.get());
if (raw_file) {
raw_file->WriteSamples(buffer.get(), length);
}
// TODO(aluebs): Use ScaleToInt16Range() from audio_util
for (size_t i = 0; i < length; ++i) {
buffer[i] = buffer[i] > 0 ?
buffer[i] * std::numeric_limits<int16_t>::max() :
-buffer[i] * std::numeric_limits<int16_t>::min();
}
if (wav_file) {
wav_file->WriteSamples(buffer.get(), length);
}
}
void WriteFloatData(const float* const* data,
int samples_per_channel,
int num_channels,
WavWriter* wav_file,
RawFile* raw_file);
// Exits on failure; do not use in unit tests.
static inline FILE* OpenFile(const std::string& filename, const char* mode) {
FILE* file = fopen(filename.c_str(), mode);
if (!file) {
printf("Unable to open file %s\n", filename.c_str());
exit(1);
}
return file;
}
FILE* OpenFile(const std::string& filename, const char* mode);
static inline int SamplesFromRate(int rate) {
return AudioProcessing::kChunkSizeMs * rate / 1000;
}
int SamplesFromRate(int rate);
static inline void SetFrameSampleRate(AudioFrame* frame,
int sample_rate_hz) {
frame->sample_rate_hz_ = sample_rate_hz;
frame->samples_per_channel_ = AudioProcessing::kChunkSizeMs *
sample_rate_hz / 1000;
}
void SetFrameSampleRate(AudioFrame* frame,
int sample_rate_hz);
template <typename T>
void SetContainerFormat(int sample_rate_hz,
@ -113,47 +72,7 @@ void SetContainerFormat(int sample_rate_hz,
cb->reset(new ChannelBuffer<T>(frame->samples_per_channel_, num_channels));
}
static inline AudioProcessing::ChannelLayout LayoutFromChannels(
int num_channels) {
switch (num_channels) {
case 1:
return AudioProcessing::kMono;
case 2:
return AudioProcessing::kStereo;
default:
assert(false);
return AudioProcessing::kMono;
}
}
// Allocates new memory in the scoped_ptr to fit the raw message and returns the
// number of bytes read.
static inline size_t ReadMessageBytesFromFile(
FILE* file,
rtc::scoped_ptr<uint8_t[]>* bytes) {
// The "wire format" for the size is little-endian. Assume we're running on
// a little-endian machine.
int32_t size = 0;
if (fread(&size, sizeof(size), 1, file) != 1)
return 0;
if (size <= 0)
return 0;
bytes->reset(new uint8_t[size]);
return fread(bytes->get(), sizeof((*bytes)[0]), size, file);
}
// Returns true on success, false on error or end-of-file.
static inline bool ReadMessageFromFile(FILE* file,
::google::protobuf::MessageLite* msg) {
rtc::scoped_ptr<uint8_t[]> bytes;
size_t size = ReadMessageBytesFromFile(file, &bytes);
if (!size)
return false;
msg->Clear();
return msg->ParseFromArray(bytes.get(), size);
}
AudioProcessing::ChannelLayout LayoutFromChannels(int num_channels);
template <typename T>
float ComputeSNR(const T* ref, const T* test, int length, float* variance) {
@ -177,4 +96,28 @@ float ComputeSNR(const T* ref, const T* test, int length, float* variance) {
return snr;
}
// Returns a vector<T> parsed from whitespace delimited values in to_parse,
// or an empty vector if the string could not be parsed.
template<typename T>
std::vector<T> ParseList(const std::string& to_parse) {
std::vector<T> values;
std::istringstream str(to_parse);
std::copy(
std::istream_iterator<T>(str),
std::istream_iterator<T>(),
std::back_inserter(values));
return values;
}
// Parses the array geometry from the command line.
//
// If a vector with size != num_mics is returned, an error has occurred and an
// appropriate error message has been printed to stdout.
std::vector<Point> ParseArrayGeometry(const std::string& mic_positions,
size_t num_mics);
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TEST_TEST_UTILS_H_

View File

@ -18,6 +18,7 @@
#include "gflags/gflags.h"
#include "webrtc/audio_processing/debug.pb.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/modules/audio_processing/test/protobuf_utils.h"
#include "webrtc/modules/audio_processing/test/test_utils.h"
#include "webrtc/typedefs.h"

View File

@ -59,6 +59,8 @@
'audio_coding_module',
'audio_device' ,
'audio_processing',
'audioproc_test_utils',
'audioproc_protobuf_utils',
'bitrate_controller',
'bwe_simulator',
'CNG',
@ -173,8 +175,6 @@
'audio_processing/beamformer/matrix_unittest.cc',
'audio_processing/beamformer/mock_nonlinear_beamformer.cc',
'audio_processing/beamformer/mock_nonlinear_beamformer.h',
'audio_processing/beamformer/pcm_utils.cc',
'audio_processing/beamformer/pcm_utils.h',
'audio_processing/echo_cancellation_impl_unittest.cc',
'audio_processing/splitting_filter_unittest.cc',
'audio_processing/transient/dyadic_decimator_unittest.cc',