Add tests and modify tools for new float deinterleaved interface.

- Add an Initialize() overload to allow specification of format
parameters. This is mainly useful for testing, but could be used in
the cases where a consumer knows the format before the streams arrive.
- Add a reverse_sample_rate_hz_ parameter to prepare for mismatched
capture and render rates. There is no functional change as it is
currently constrained to match the capture rate.
- Fix a bug in the float dump: we need to use add_ rather than set_.
- Add a debug dump test for both int and float interfaces.
- Enable unpacking of float dumps.
- Enable audioproc to read float dumps.
- Move more shared functionality to test_utils.h, and generally tidy up
a bit by consolidating repeated code.

BUG=2894
TESTED=Verified that the output produced by the float debug dump test is
correct. Processed the resulting debug dump file with audioproc and
ensured that we get identical output. (This is crucial, as we need to
be able to exactly reproduce online results offline.)

R=aluebs@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5676 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
andrew@webrtc.org
2014-03-10 22:26:12 +00:00
parent 3046b843b2
commit a8b97373d5
12 changed files with 666 additions and 442 deletions

View File

@ -17,22 +17,22 @@
#include "gflags/gflags.h"
#include "webrtc/audio_processing/debug.pb.h"
#include "webrtc/modules/audio_processing/test/test_utils.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/typedefs.h"
using webrtc::scoped_array;
using webrtc::audioproc::Event;
using webrtc::audioproc::ReverseStream;
using webrtc::audioproc::Stream;
using webrtc::audioproc::Init;
// TODO(andrew): unpack more of the data.
DEFINE_string(input_file, "input.pcm", "The name of the input stream file.");
DEFINE_string(float_input_file, "input.float",
"The name of the float input stream file.");
DEFINE_string(output_file, "ref_out.pcm",
"The name of the reference output stream file.");
DEFINE_string(float_output_file, "ref_out.float",
"The name of the float reference output stream file.");
DEFINE_string(reverse_file, "reverse.pcm",
"The name of the reverse input stream file.");
DEFINE_string(float_reverse_file, "reverse.float",
"The name of the float reverse input stream file.");
DEFINE_string(delay_file, "delay.int32", "The name of the delay file.");
DEFINE_string(drift_file, "drift.int32", "The name of the drift file.");
DEFINE_string(level_file, "level.int32", "The name of the level file.");
@ -41,31 +41,22 @@ DEFINE_string(settings_file, "settings.txt", "The name of the settings file.");
DEFINE_bool(full, false,
"Unpack the full set of files (normally not needed).");
// TODO(andrew): move this to a helper class to share with process_test.cc?
// Returns true on success, false on error or end-of-file.
bool ReadMessageFromFile(FILE* file,
::google::protobuf::MessageLite* msg) {
// The "wire format" for the size is little-endian.
// Assume process_test is running on a little-endian machine.
int32_t size = 0;
if (fread(&size, sizeof(int32_t), 1, file) != 1) {
return false;
}
if (size <= 0) {
return false;
}
const size_t usize = static_cast<size_t>(size);
namespace webrtc {
scoped_array<char> array(new char[usize]);
if (fread(array.get(), sizeof(char), usize, file) != usize) {
return false;
}
using audioproc::Event;
using audioproc::ReverseStream;
using audioproc::Stream;
using audioproc::Init;
msg->Clear();
return msg->ParseFromArray(array.get(), usize);
void WriteData(const void* data, size_t size, FILE* file,
const std::string& filename) {
if (fwrite(data, size, 1, file) != 1) {
printf("Error when writing to %s\n", filename.c_str());
exit(1);
}
}
int main(int argc, char* argv[]) {
int do_main(int argc, char* argv[]) {
std::string program_name = argv[0];
std::string usage = "Commandline tool to unpack audioproc debug files.\n"
"Example usage:\n" + program_name + " debug_dump.pb\n";
@ -77,139 +68,99 @@ int main(int argc, char* argv[]) {
return 1;
}
FILE* debug_file = fopen(argv[1], "rb");
if (debug_file == NULL) {
printf("Unable to open %s\n", argv[1]);
return 1;
}
FILE* input_file = fopen(FLAGS_input_file.c_str(), "wb");
if (input_file == NULL) {
printf("Unable to open %s\n", FLAGS_input_file.c_str());
return 1;
}
FILE* output_file = fopen(FLAGS_output_file.c_str(), "wb");
if (output_file == NULL) {
printf("Unable to open %s\n", FLAGS_output_file.c_str());
return 1;
}
FILE* reverse_file = fopen(FLAGS_reverse_file.c_str(), "wb");
if (reverse_file == NULL) {
printf("Unable to open %s\n", FLAGS_reverse_file.c_str());
return 1;
}
FILE* settings_file = fopen(FLAGS_settings_file.c_str(), "wb");
if (settings_file == NULL) {
printf("Unable to open %s\n", FLAGS_settings_file.c_str());
return 1;
}
FILE* delay_file = NULL;
FILE* drift_file = NULL;
FILE* level_file = NULL;
FILE* keypress_file = NULL;
if (FLAGS_full) {
delay_file = fopen(FLAGS_delay_file.c_str(), "wb");
if (delay_file == NULL) {
printf("Unable to open %s\n", FLAGS_delay_file.c_str());
return 1;
}
drift_file = fopen(FLAGS_drift_file.c_str(), "wb");
if (drift_file == NULL) {
printf("Unable to open %s\n", FLAGS_drift_file.c_str());
return 1;
}
level_file = fopen(FLAGS_level_file.c_str(), "wb");
if (level_file == NULL) {
printf("Unable to open %s\n", FLAGS_level_file.c_str());
return 1;
}
keypress_file = fopen(FLAGS_keypress_file.c_str(), "wb");
if (keypress_file == NULL) {
printf("Unable to open %s\n", FLAGS_keypress_file.c_str());
return 1;
}
}
FILE* debug_file = OpenFile(argv[1], "rb");
Event event_msg;
int frame_count = 0;
while (ReadMessageFromFile(debug_file, &event_msg)) {
while (ReadMessageFromFile(debug_file, &event_msg)) {
if (event_msg.type() == Event::REVERSE_STREAM) {
if (!event_msg.has_reverse_stream()) {
printf("Corrupted input file: ReverseStream missing.\n");
printf("Corrupt input file: ReverseStream missing.\n");
return 1;
}
const ReverseStream msg = event_msg.reverse_stream();
if (msg.has_data()) {
if (fwrite(msg.data().data(), msg.data().size(), 1, reverse_file) !=
1) {
printf("Error when writing to %s\n", FLAGS_reverse_file.c_str());
return 1;
}
static FILE* reverse_file = OpenFile(FLAGS_reverse_file, "wb");
WriteData(msg.data().data(), msg.data().size(), reverse_file,
FLAGS_reverse_file);
} else if (msg.channel_size() > 0) {
static FILE* float_reverse_file = OpenFile(FLAGS_float_reverse_file,
"wb");
// TODO(ajm): Interleave multiple channels.
assert(msg.channel_size() == 1);
WriteData(msg.channel(0).data(), msg.channel(0).size(),
float_reverse_file, FLAGS_reverse_file);
}
} else if (event_msg.type() == Event::STREAM) {
frame_count++;
if (!event_msg.has_stream()) {
printf("Corrupted input file: Stream missing.\n");
printf("Corrupt input file: Stream missing.\n");
return 1;
}
const Stream msg = event_msg.stream();
if (msg.has_input_data()) {
if (fwrite(msg.input_data().data(), msg.input_data().size(), 1,
input_file) != 1) {
printf("Error when writing to %s\n", FLAGS_input_file.c_str());
return 1;
}
static FILE* input_file = OpenFile(FLAGS_input_file, "wb");
WriteData(msg.input_data().data(), msg.input_data().size(),
input_file, FLAGS_input_file);
} else if (msg.input_channel_size() > 0) {
static FILE* float_input_file = OpenFile(FLAGS_float_input_file, "wb");
// TODO(ajm): Interleave multiple channels.
assert(msg.input_channel_size() == 1);
WriteData(msg.input_channel(0).data(), msg.input_channel(0).size(),
float_input_file, FLAGS_float_input_file);
}
if (msg.has_output_data()) {
if (fwrite(msg.output_data().data(), msg.output_data().size(), 1,
output_file) != 1) {
printf("Error when writing to %s\n", FLAGS_output_file.c_str());
return 1;
}
static FILE* output_file = OpenFile(FLAGS_output_file, "wb");
WriteData(msg.output_data().data(), msg.output_data().size(),
output_file, FLAGS_output_file);
} else if (msg.output_channel_size() > 0) {
static FILE* float_output_file = OpenFile(FLAGS_float_output_file,
"wb");
// TODO(ajm): Interleave multiple channels.
assert(msg.output_channel_size() == 1);
WriteData(msg.output_channel(0).data(), msg.output_channel(0).size(),
float_output_file, FLAGS_float_output_file);
}
if (FLAGS_full) {
if (msg.has_delay()) {
static FILE* delay_file = OpenFile(FLAGS_delay_file, "wb");
int32_t delay = msg.delay();
if (fwrite(&delay, sizeof(int32_t), 1, delay_file) != 1) {
printf("Error when writing to %s\n", FLAGS_delay_file.c_str());
return 1;
}
WriteData(&delay, sizeof(delay), delay_file, FLAGS_delay_file);
}
if (msg.has_drift()) {
static FILE* drift_file = OpenFile(FLAGS_drift_file, "wb");
int32_t drift = msg.drift();
if (fwrite(&drift, sizeof(int32_t), 1, drift_file) != 1) {
printf("Error when writing to %s\n", FLAGS_drift_file.c_str());
return 1;
}
WriteData(&drift, sizeof(drift), drift_file, FLAGS_drift_file);
}
if (msg.has_level()) {
static FILE* level_file = OpenFile(FLAGS_level_file, "wb");
int32_t level = msg.level();
if (fwrite(&level, sizeof(int32_t), 1, level_file) != 1) {
printf("Error when writing to %s\n", FLAGS_level_file.c_str());
return 1;
}
WriteData(&level, sizeof(level), level_file, FLAGS_level_file);
}
if (msg.has_keypress()) {
static FILE* keypress_file = OpenFile(FLAGS_keypress_file, "wb");
bool keypress = msg.keypress();
if (fwrite(&keypress, sizeof(bool), 1, keypress_file) != 1) {
printf("Error when writing to %s\n", FLAGS_keypress_file.c_str());
return 1;
}
WriteData(&keypress, sizeof(keypress), keypress_file,
FLAGS_keypress_file);
}
}
} else if (event_msg.type() == Event::INIT) {
if (!event_msg.has_init()) {
printf("Corrupted input file: Init missing.\n");
printf("Corrupt input file: Init missing.\n");
return 1;
}
static FILE* settings_file = OpenFile(FLAGS_settings_file, "wb");
const Init msg = event_msg.init();
// These should print out zeros if they're missing.
fprintf(settings_file, "Init at frame: %d\n", frame_count);
@ -229,3 +180,9 @@ int main(int argc, char* argv[]) {
return 0;
}
} // namespace webrtc
int main(int argc, char* argv[]) {
return webrtc::do_main(argc, argv);
}