diff --git a/webrtc/modules/audio_processing/audio_processing_tests.gypi b/webrtc/modules/audio_processing/audio_processing_tests.gypi index a535144589..f5d6dae7ef 100644 --- a/webrtc/modules/audio_processing/audio_processing_tests.gypi +++ b/webrtc/modules/audio_processing/audio_processing_tests.gypi @@ -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', diff --git a/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.cc b/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.cc index 8fd6c687f4..a99d3b1f16 100644 --- a/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.cc +++ b/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.cc @@ -294,7 +294,7 @@ void NonlinearBeamformer::InitInterfCovMats() { } void NonlinearBeamformer::ProcessChunk(const ChannelBuffer& input, - ChannelBuffer* output) { + ChannelBuffer* 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& 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); diff --git a/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer_test.cc b/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer_test.cc index 48d7c2b2ae..82a6cb050b 100644 --- a/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer_test.cc +++ b/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer_test.cc @@ -8,75 +8,82 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include #include #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 captured_audio_cb(kChunkSize, FLAGS_num_input_channels); + const size_t num_mics = in_file.num_channels(); + const std::vector 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 in_buf( + rtc::CheckedDivExact(in_file.sample_rate(), kChunksPerSecond), + in_file.num_channels()); + ChannelBuffer out_buf( + rtc::CheckedDivExact(out_file.sample_rate(), kChunksPerSecond), + out_file.num_channels()); + + std::vector 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 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(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); +} diff --git a/webrtc/modules/audio_processing/beamformer/pcm_utils.cc b/webrtc/modules/audio_processing/beamformer/pcm_utils.cc deleted file mode 100644 index 0999de34f8..0000000000 --- a/webrtc/modules/audio_processing/beamformer/pcm_utils.cc +++ /dev/null @@ -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 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(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(length) / num_channels; - rtc::scoped_ptr > deinterleaved_buffer( - new ChannelBuffer(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 interleaved_buffer(new int16_t[length]); - Interleave(buffer, - static_cast(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(length) / num_channels; - rtc::scoped_ptr > deinterleaved_buffer( - new ChannelBuffer(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 diff --git a/webrtc/modules/audio_processing/beamformer/pcm_utils.h b/webrtc/modules/audio_processing/beamformer/pcm_utils.h deleted file mode 100644 index 3a6a3b9057..0000000000 --- a/webrtc/modules/audio_processing/beamformer/pcm_utils.h +++ /dev/null @@ -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 -#include - -// 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_ diff --git a/webrtc/modules/audio_processing/test/audio_processing_unittest.cc b/webrtc/modules/audio_processing/test/audio_processing_unittest.cc index 6546cfae0f..e5c3865c9b 100644 --- a/webrtc/modules/audio_processing/test/audio_processing_unittest.cc +++ b/webrtc/modules/audio_processing/test/audio_processing_unittest.cc @@ -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" diff --git a/webrtc/modules/audio_processing/test/audioproc_float.cc b/webrtc/modules/audio_processing/test/audioproc_float.cc index 6278a21009..b3765c5094 100644 --- a/webrtc/modules/audio_processing/test/audioproc_float.cc +++ b/webrtc/modules/audio_processing/test/audioproc_float.cc @@ -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 parsed from whitespace delimited values in to_parse, -// or an empty vector if the string could not be parsed. -template -std::vector parse_list(std::string to_parse) { - std::vector values; - - std::istringstream str(to_parse); - std::copy( - std::istream_iterator(str), - std::istream_iterator(), - 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 get_array_geometry(size_t num_mics) { - std::vector 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 values = parse_list(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(new ExperimentalNs(FLAGS_ts || FLAGS_all)); if (FLAGS_bf || FLAGS_all) { - const size_t num_mics = c_file.num_channels(); - const std::vector 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 array_geometry = + ParseArrayGeometry(FLAGS_mic_positions, num_mics); + CHECK_EQ(array_geometry.size(), num_mics); config.Set(new Beamforming(true, array_geometry)); } @@ -171,46 +109,49 @@ int main(int argc, char* argv[]) { static_cast(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 c_buf(c_file.sample_rate() / kChunksPerSecond, - c_file.num_channels()); - ChannelBuffer o_buf(o_file.sample_rate() / kChunksPerSecond, - o_file.num_channels()); + ChannelBuffer in_buf( + rtc::CheckedDivExact(in_file.sample_rate(), kChunksPerSecond), + in_file.num_channels()); + ChannelBuffer out_buf( + rtc::CheckedDivExact(out_file.sample_rate(), kChunksPerSecond), + out_file.num_channels()); - const size_t c_length = - static_cast(c_buf.num_channels() * c_buf.num_frames()); - const size_t o_length = - static_cast(o_buf.num_channels() * o_buf.num_frames()); - rtc::scoped_ptr c_interleaved(new float[c_length]); - rtc::scoped_ptr o_interleaved(new float[o_length]); + std::vector in_interleaved(in_buf.size()); + std::vector 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) { diff --git a/webrtc/modules/audio_processing/test/process_test.cc b/webrtc/modules/audio_processing/test/process_test.cc index 3218243359..23d4d18bca 100644 --- a/webrtc/modules/audio_processing/test/process_test.cc +++ b/webrtc/modules/audio_processing/test/process_test.cc @@ -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. diff --git a/webrtc/modules/audio_processing/test/protobuf_utils.cc b/webrtc/modules/audio_processing/test/protobuf_utils.cc new file mode 100644 index 0000000000..37042cdc14 --- /dev/null +++ b/webrtc/modules/audio_processing/test/protobuf_utils.cc @@ -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* 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 bytes; + size_t size = ReadMessageBytesFromFile(file, &bytes); + if (!size) + return false; + + msg->Clear(); + return msg->ParseFromArray(bytes.get(), size); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/test/protobuf_utils.h b/webrtc/modules/audio_processing/test/protobuf_utils.h new file mode 100644 index 0000000000..230fcaad76 --- /dev/null +++ b/webrtc/modules/audio_processing/test/protobuf_utils.h @@ -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* 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_ diff --git a/webrtc/modules/audio_processing/test/test_utils.cc b/webrtc/modules/audio_processing/test/test_utils.cc new file mode 100644 index 0000000000..fe33ec0351 --- /dev/null +++ b/webrtc/modules/audio_processing/test/test_utils.cc @@ -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 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::max() : + -buffer[i] * std::numeric_limits::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 ParseArrayGeometry(const std::string& mic_positions, + size_t num_mics) { + const std::vector values = ParseList(mic_positions); + CHECK_EQ(values.size(), 3 * num_mics) << + "Could not parse mic_positions or incorrect number of points."; + + std::vector 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 diff --git a/webrtc/modules/audio_processing/test/test_utils.h b/webrtc/modules/audio_processing/test/test_utils.h index 52274d7853..7ad462c095 100644 --- a/webrtc/modules/audio_processing/test/test_utils.h +++ b/webrtc/modules/audio_processing/test/test_utils.h @@ -8,13 +8,18 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include -#include +#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 +#include +#include +#include +#include + +#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 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::max() : - -buffer[i] * std::numeric_limits::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 void SetContainerFormat(int sample_rate_hz, @@ -113,47 +72,7 @@ void SetContainerFormat(int sample_rate_hz, cb->reset(new ChannelBuffer(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* 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 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 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 parsed from whitespace delimited values in to_parse, +// or an empty vector if the string could not be parsed. +template +std::vector ParseList(const std::string& to_parse) { + std::vector values; + + std::istringstream str(to_parse); + std::copy( + std::istream_iterator(str), + std::istream_iterator(), + 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 ParseArrayGeometry(const std::string& mic_positions, + size_t num_mics); + } // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TEST_TEST_UTILS_H_ diff --git a/webrtc/modules/audio_processing/test/unpack.cc b/webrtc/modules/audio_processing/test/unpack.cc index 49c8b3c979..2484828bd1 100644 --- a/webrtc/modules/audio_processing/test/unpack.cc +++ b/webrtc/modules/audio_processing/test/unpack.cc @@ -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" diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index 839238175c..3a7e219974 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -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',