Fix WEBRTC_AEC_DEBUG_DUMP (broken by int16->float conversion)

And in the process, make it dump WAV files instead of raw PCM.

R=andrew@webrtc.org, bjornv@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6959 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
kwiberg@webrtc.org
2014-08-25 06:26:04 +00:00
parent 3740d74106
commit 584cd8da4b
8 changed files with 114 additions and 70 deletions

View File

@@ -101,3 +101,15 @@ void rtc_WavWriteSamples(rtc_WavFile* wf,
size_t num_samples) { size_t num_samples) {
reinterpret_cast<webrtc::WavFile*>(wf)->WriteSamples(samples, num_samples); reinterpret_cast<webrtc::WavFile*>(wf)->WriteSamples(samples, num_samples);
} }
int rtc_WavSampleRate(const rtc_WavFile* wf) {
return reinterpret_cast<const webrtc::WavFile*>(wf)->sample_rate();
}
int rtc_WavNumChannels(const rtc_WavFile* wf) {
return reinterpret_cast<const webrtc::WavFile*>(wf)->num_channels();
}
uint32_t rtc_WavNumSamples(const rtc_WavFile* wf) {
return reinterpret_cast<const webrtc::WavFile*>(wf)->num_samples();
}

View File

@@ -34,6 +34,10 @@ class WavFile {
// interleaved channels. // interleaved channels.
void WriteSamples(const float* samples, size_t num_samples); void WriteSamples(const float* samples, size_t num_samples);
int sample_rate() const { return sample_rate_; }
int num_channels() const { return num_channels_; }
uint32_t num_samples() const { return num_samples_; }
private: private:
void WriteSamples(const int16_t* samples, size_t num_samples); void WriteSamples(const int16_t* samples, size_t num_samples);
void Close(); void Close();
@@ -57,6 +61,9 @@ void rtc_WavClose(rtc_WavFile* wf);
void rtc_WavWriteSamples(rtc_WavFile* wf, void rtc_WavWriteSamples(rtc_WavFile* wf,
const float* samples, const float* samples,
size_t num_samples); size_t num_samples);
int rtc_WavSampleRate(const rtc_WavFile* wf);
int rtc_WavNumChannels(const rtc_WavFile* wf);
uint32_t rtc_WavNumSamples(const rtc_WavFile* wf);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"

View File

@@ -25,10 +25,14 @@ static const float kSamples[] = {0.0, 10.0, 4e4, -1e9};
// Write a tiny WAV file with the C++ interface and verify the result. // Write a tiny WAV file with the C++ interface and verify the result.
TEST(WavWriterTest, CPP) { TEST(WavWriterTest, CPP) {
const std::string outfile = webrtc::test::OutputPath() + "wavtest1.wav"; const std::string outfile = webrtc::test::OutputPath() + "wavtest1.wav";
static const int kNumSamples = 3; static const uint32_t kNumSamples = 3;
{ {
webrtc::WavFile w(outfile, 14099, 1); webrtc::WavFile w(outfile, 14099, 1);
EXPECT_EQ(14099, w.sample_rate());
EXPECT_EQ(1, w.num_channels());
EXPECT_EQ(0u, w.num_samples());
w.WriteSamples(kSamples, kNumSamples); w.WriteSamples(kSamples, kNumSamples);
EXPECT_EQ(kNumSamples, w.num_samples());
} }
static const uint8_t kExpectedContents[] = { static const uint8_t kExpectedContents[] = {
'R', 'I', 'F', 'F', 'R', 'I', 'F', 'F',
@@ -64,9 +68,14 @@ TEST(WavWriterTest, CPP) {
TEST(WavWriterTest, C) { TEST(WavWriterTest, C) {
const std::string outfile = webrtc::test::OutputPath() + "wavtest2.wav"; const std::string outfile = webrtc::test::OutputPath() + "wavtest2.wav";
rtc_WavFile *w = rtc_WavOpen(outfile.c_str(), 11904, 2); rtc_WavFile *w = rtc_WavOpen(outfile.c_str(), 11904, 2);
static const int kNumSamples = 4; EXPECT_EQ(11904, rtc_WavSampleRate(w));
EXPECT_EQ(2, rtc_WavNumChannels(w));
EXPECT_EQ(0u, rtc_WavNumSamples(w));
static const uint32_t kNumSamples = 4;
rtc_WavWriteSamples(w, &kSamples[0], 2); rtc_WavWriteSamples(w, &kSamples[0], 2);
EXPECT_EQ(2u, rtc_WavNumSamples(w));
rtc_WavWriteSamples(w, &kSamples[2], kNumSamples - 2); rtc_WavWriteSamples(w, &kSamples[2], kNumSamples - 2);
EXPECT_EQ(kNumSamples, rtc_WavNumSamples(w));
rtc_WavClose(w); rtc_WavClose(w);
static const uint8_t kExpectedContents[] = { static const uint8_t kExpectedContents[] = {
'R', 'I', 'F', 'F', 'R', 'I', 'F', 'F',
@@ -104,9 +113,9 @@ TEST(WavWriterTest, LargeFile) {
std::string outfile = webrtc::test::OutputPath() + "wavtest3.wav"; std::string outfile = webrtc::test::OutputPath() + "wavtest3.wav";
static const int kSampleRate = 8000; static const int kSampleRate = 8000;
static const int kNumChannels = 2; static const int kNumChannels = 2;
static const int kNumSamples = 3 * kSampleRate * kNumChannels; static const uint32_t kNumSamples = 3 * kSampleRate * kNumChannels;
float samples[kNumSamples]; float samples[kNumSamples];
for (int i = 0; i < kNumSamples; i += kNumChannels) { for (uint32_t i = 0; i < kNumSamples; i += kNumChannels) {
// A nice periodic beeping sound. // A nice periodic beeping sound.
static const double kToneHz = 440; static const double kToneHz = 440;
const double t = static_cast<double>(i) / (kNumChannels * kSampleRate); const double t = static_cast<double>(i) / (kNumChannels * kSampleRate);
@@ -117,7 +126,11 @@ TEST(WavWriterTest, LargeFile) {
} }
{ {
webrtc::WavFile w(outfile, kSampleRate, kNumChannels); webrtc::WavFile w(outfile, kSampleRate, kNumChannels);
EXPECT_EQ(kSampleRate, w.sample_rate());
EXPECT_EQ(kNumChannels, w.num_channels());
EXPECT_EQ(0u, w.num_samples());
w.WriteSamples(samples, kNumSamples); w.WriteSamples(samples, kNumSamples);
EXPECT_EQ(kNumSamples, w.num_samples());
} }
EXPECT_EQ(sizeof(int16_t) * kNumSamples + webrtc::kWavHeaderSize, EXPECT_EQ(sizeof(int16_t) * kNumSamples + webrtc::kWavHeaderSize,
webrtc::test::GetFileSize(outfile)); webrtc::test::GetFileSize(outfile));

View File

@@ -14,6 +14,10 @@
#include "webrtc/modules/audio_processing/aec/aec_core.h" #include "webrtc/modules/audio_processing/aec/aec_core.h"
#ifdef WEBRTC_AEC_DEBUG_DUMP
#include <stdio.h>
#endif
#include <assert.h> #include <assert.h>
#include <math.h> #include <math.h>
#include <stddef.h> // size_t #include <stddef.h> // size_t
@@ -1054,11 +1058,11 @@ static void ProcessBlock(AecCore* aec) {
#ifdef WEBRTC_AEC_DEBUG_DUMP #ifdef WEBRTC_AEC_DEBUG_DUMP
{ {
int16_t farend[PART_LEN]; float farend[PART_LEN];
int16_t* farend_ptr = NULL; float* farend_ptr = NULL;
WebRtc_ReadBuffer(aec->far_time_buf, (void**)&farend_ptr, farend, 1); WebRtc_ReadBuffer(aec->far_time_buf, (void**)&farend_ptr, farend, 1);
(void)fwrite(farend_ptr, sizeof(int16_t), PART_LEN, aec->farFile); rtc_WavWriteSamples(aec->farFile, farend_ptr, PART_LEN);
(void)fwrite(nearend_ptr, sizeof(int16_t), PART_LEN, aec->nearFile); rtc_WavWriteSamples(aec->nearFile, nearend_ptr, PART_LEN);
} }
#endif #endif
@@ -1208,16 +1212,8 @@ static void ProcessBlock(AecCore* aec) {
} }
#ifdef WEBRTC_AEC_DEBUG_DUMP #ifdef WEBRTC_AEC_DEBUG_DUMP
{ rtc_WavWriteSamples(aec->outLinearFile, e, PART_LEN);
int16_t eInt16[PART_LEN]; rtc_WavWriteSamples(aec->outFile, output, PART_LEN);
for (i = 0; i < PART_LEN; i++) {
eInt16[i] = (int16_t)WEBRTC_SPL_SAT(
WEBRTC_SPL_WORD16_MAX, e[i], WEBRTC_SPL_WORD16_MIN);
}
(void)fwrite(eInt16, sizeof(int16_t), PART_LEN, aec->outLinearFile);
(void)fwrite(output, sizeof(int16_t), PART_LEN, aec->outFile);
}
#endif #endif
} }
@@ -1272,24 +1268,16 @@ int WebRtcAec_CreateAec(AecCore** aecInst) {
return -1; return -1;
} }
#ifdef WEBRTC_AEC_DEBUG_DUMP #ifdef WEBRTC_AEC_DEBUG_DUMP
aec->instance_index = webrtc_aec_instance_count;
aec->far_time_buf = aec->far_time_buf =
WebRtc_CreateBuffer(kBufSizePartitions, sizeof(int16_t) * PART_LEN); WebRtc_CreateBuffer(kBufSizePartitions, sizeof(float) * PART_LEN);
if (!aec->far_time_buf) { if (!aec->far_time_buf) {
WebRtcAec_FreeAec(aec); WebRtcAec_FreeAec(aec);
aec = NULL; aec = NULL;
return -1; return -1;
} }
{ aec->farFile = aec->nearFile = aec->outFile = aec->outLinearFile = NULL;
char filename[64]; aec->debug_dump_count = 0;
sprintf(filename, "aec_far%d.pcm", webrtc_aec_instance_count);
aec->farFile = fopen(filename, "wb");
sprintf(filename, "aec_near%d.pcm", webrtc_aec_instance_count);
aec->nearFile = fopen(filename, "wb");
sprintf(filename, "aec_out%d.pcm", webrtc_aec_instance_count);
aec->outFile = fopen(filename, "wb");
sprintf(filename, "aec_out_linear%d.pcm", webrtc_aec_instance_count);
aec->outLinearFile = fopen(filename, "wb");
}
#endif #endif
aec->delay_estimator_farend = aec->delay_estimator_farend =
WebRtc_CreateDelayEstimatorFarend(PART_LEN1, kHistorySizeBlocks); WebRtc_CreateDelayEstimatorFarend(PART_LEN1, kHistorySizeBlocks);
@@ -1348,10 +1336,10 @@ int WebRtcAec_FreeAec(AecCore* aec) {
WebRtc_FreeBuffer(aec->far_buf_windowed); WebRtc_FreeBuffer(aec->far_buf_windowed);
#ifdef WEBRTC_AEC_DEBUG_DUMP #ifdef WEBRTC_AEC_DEBUG_DUMP
WebRtc_FreeBuffer(aec->far_time_buf); WebRtc_FreeBuffer(aec->far_time_buf);
fclose(aec->farFile); rtc_WavClose(aec->farFile);
fclose(aec->nearFile); rtc_WavClose(aec->nearFile);
fclose(aec->outFile); rtc_WavClose(aec->outFile);
fclose(aec->outLinearFile); rtc_WavClose(aec->outLinearFile);
#endif #endif
WebRtc_FreeDelayEstimator(aec->delay_estimator); WebRtc_FreeDelayEstimator(aec->delay_estimator);
WebRtc_FreeDelayEstimatorFarend(aec->delay_estimator_farend); WebRtc_FreeDelayEstimatorFarend(aec->delay_estimator_farend);
@@ -1360,6 +1348,29 @@ int WebRtcAec_FreeAec(AecCore* aec) {
return 0; return 0;
} }
#ifdef WEBRTC_AEC_DEBUG_DUMP
// Open a new Wav file for writing. If it was already open with a different
// sample frequency, close it first.
static void ReopenWav(rtc_WavFile** wav_file,
const char* name,
int seq1,
int seq2,
int sample_rate) {
int written UNUSED;
char filename[64];
if (*wav_file) {
if (rtc_WavSampleRate(*wav_file) == sample_rate)
return;
rtc_WavClose(*wav_file);
}
written = snprintf(filename, sizeof(filename), "%s%d-%d.wav",
name, seq1, seq2);
assert(written >= 0); // no output error
assert((size_t)written < sizeof(filename)); // buffer was large enough
*wav_file = rtc_WavOpen(filename, sample_rate, 1);
}
#endif // WEBRTC_AEC_DEBUG_DUMP
int WebRtcAec_InitAec(AecCore* aec, int sampFreq) { int WebRtcAec_InitAec(AecCore* aec, int sampFreq) {
int i; int i;
@@ -1400,6 +1411,15 @@ int WebRtcAec_InitAec(AecCore* aec, int sampFreq) {
if (WebRtc_InitBuffer(aec->far_time_buf) == -1) { if (WebRtc_InitBuffer(aec->far_time_buf) == -1) {
return -1; return -1;
} }
ReopenWav(&aec->farFile, "aec_far",
aec->instance_index, aec->debug_dump_count, sampFreq);
ReopenWav(&aec->nearFile, "aec_near",
aec->instance_index, aec->debug_dump_count, sampFreq);
ReopenWav(&aec->outFile, "aec_out",
aec->instance_index, aec->debug_dump_count, sampFreq);
ReopenWav(&aec->outLinearFile, "aec_out_linear",
aec->instance_index, aec->debug_dump_count, sampFreq);
++aec->debug_dump_count;
#endif #endif
aec->system_delay = 0; aec->system_delay = 0;

View File

@@ -11,10 +11,7 @@
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_INTERNAL_H_ #ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_INTERNAL_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_INTERNAL_H_ #define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_INTERNAL_H_
#ifdef WEBRTC_AEC_DEBUG_DUMP #include "webrtc/common_audio/wav_writer.h"
#include <stdio.h>
#endif
#include "webrtc/modules/audio_processing/aec/aec_common.h" #include "webrtc/modules/audio_processing/aec/aec_common.h"
#include "webrtc/modules/audio_processing/aec/aec_core.h" #include "webrtc/modules/audio_processing/aec/aec_core.h"
#include "webrtc/modules/audio_processing/utility/ring_buffer.h" #include "webrtc/modules/audio_processing/utility/ring_buffer.h"
@@ -141,11 +138,19 @@ struct AecCore {
int num_partitions; int num_partitions;
#ifdef WEBRTC_AEC_DEBUG_DUMP #ifdef WEBRTC_AEC_DEBUG_DUMP
// Sequence number of this AEC instance, so that different instances can
// choose different dump file names.
int instance_index;
// Number of times we've restarted dumping; used to pick new dump file names
// each time.
int debug_dump_count;
RingBuffer* far_time_buf; RingBuffer* far_time_buf;
FILE* farFile; rtc_WavFile* farFile;
FILE* nearFile; rtc_WavFile* nearFile;
FILE* outFile; rtc_WavFile* outFile;
FILE* outLinearFile; rtc_WavFile* outLinearFile;
#endif #endif
}; };

View File

@@ -158,13 +158,6 @@ int32_t WebRtcAec_Create(void** aecInst) {
aecpc->lastError = 0; aecpc->lastError = 0;
#ifdef WEBRTC_AEC_DEBUG_DUMP #ifdef WEBRTC_AEC_DEBUG_DUMP
aecpc->far_pre_buf_s16 =
WebRtc_CreateBuffer(PART_LEN2 + kResamplerBufferSize, sizeof(int16_t));
if (!aecpc->far_pre_buf_s16) {
WebRtcAec_Free(aecpc);
aecpc = NULL;
return -1;
}
{ {
char filename[64]; char filename[64];
sprintf(filename, "aec_buf%d.dat", webrtc_aec_instance_count); sprintf(filename, "aec_buf%d.dat", webrtc_aec_instance_count);
@@ -190,7 +183,6 @@ int32_t WebRtcAec_Free(void* aecInst) {
WebRtc_FreeBuffer(aecpc->far_pre_buf); WebRtc_FreeBuffer(aecpc->far_pre_buf);
#ifdef WEBRTC_AEC_DEBUG_DUMP #ifdef WEBRTC_AEC_DEBUG_DUMP
WebRtc_FreeBuffer(aecpc->far_pre_buf_s16);
fclose(aecpc->bufFile); fclose(aecpc->bufFile);
fclose(aecpc->skewFile); fclose(aecpc->skewFile);
fclose(aecpc->delayFile); fclose(aecpc->delayFile);
@@ -281,14 +273,6 @@ int32_t WebRtcAec_Init(void* aecInst, int32_t sampFreq, int32_t scSampFreq) {
return -1; return -1;
} }
#ifdef WEBRTC_AEC_DEBUG_DUMP
if (WebRtc_InitBuffer(aecpc->far_pre_buf_s16) == -1) {
aecpc->lastError = AEC_UNSPECIFIED_ERROR;
return -1;
}
WebRtc_MoveReadPtr(aecpc->far_pre_buf_s16, -PART_LEN); // Start overlap.
#endif
return 0; return 0;
} }
@@ -332,10 +316,6 @@ int32_t WebRtcAec_BufferFarend(void* aecInst,
WebRtcAec_SetSystemDelay(aecpc->aec, WebRtcAec_SetSystemDelay(aecpc->aec,
WebRtcAec_system_delay(aecpc->aec) + newNrOfSamples); WebRtcAec_system_delay(aecpc->aec) + newNrOfSamples);
#ifdef WEBRTC_AEC_DEBUG_DUMP
WebRtc_WriteBuffer(
aecpc->far_pre_buf_s16, farend_ptr, (size_t)newNrOfSamples);
#endif
// Write the time-domain data to |far_pre_buf|. // Write the time-domain data to |far_pre_buf|.
WebRtc_WriteBuffer(aecpc->far_pre_buf, farend_ptr, (size_t)newNrOfSamples); WebRtc_WriteBuffer(aecpc->far_pre_buf, farend_ptr, (size_t)newNrOfSamples);
@@ -347,17 +327,14 @@ int32_t WebRtcAec_BufferFarend(void* aecInst,
float tmp[PART_LEN2]; float tmp[PART_LEN2];
WebRtc_ReadBuffer(aecpc->far_pre_buf, (void**)&ptmp, tmp, PART_LEN2); WebRtc_ReadBuffer(aecpc->far_pre_buf, (void**)&ptmp, tmp, PART_LEN2);
WebRtcAec_BufferFarendPartition(aecpc->aec, ptmp); WebRtcAec_BufferFarendPartition(aecpc->aec, ptmp);
#ifdef WEBRTC_AEC_DEBUG_DUMP
WebRtc_WriteBuffer(
WebRtcAec_far_time_buf(aecpc->aec), &ptmp[PART_LEN], 1);
#endif
} }
// Rewind |far_pre_buf| PART_LEN samples for overlap before continuing. // Rewind |far_pre_buf| PART_LEN samples for overlap before continuing.
WebRtc_MoveReadPtr(aecpc->far_pre_buf, -PART_LEN); WebRtc_MoveReadPtr(aecpc->far_pre_buf, -PART_LEN);
#ifdef WEBRTC_AEC_DEBUG_DUMP
WebRtc_ReadBuffer(
aecpc->far_pre_buf_s16, (void**)&farend_ptr, new_farend, PART_LEN2);
WebRtc_WriteBuffer(
WebRtcAec_far_time_buf(aecpc->aec), &farend_ptr[PART_LEN], 1);
WebRtc_MoveReadPtr(aecpc->far_pre_buf_s16, -PART_LEN);
#endif
} }
return 0; return 0;

View File

@@ -42,7 +42,6 @@ typedef struct {
short lastDelayDiff; short lastDelayDiff;
#ifdef WEBRTC_AEC_DEBUG_DUMP #ifdef WEBRTC_AEC_DEBUG_DUMP
RingBuffer* far_pre_buf_s16; // Time domain far-end pre-buffer in int16_t.
FILE* bufFile; FILE* bufFile;
FILE* delayFile; FILE* delayFile;
FILE* skewFile; FILE* skewFile;

View File

@@ -109,4 +109,15 @@ typedef unsigned __int64 uint64_t;
#endif #endif
#endif // WARN_UNUSED_RESULT #endif // WARN_UNUSED_RESULT
// Put after a variable that might not be used, to prevent compiler warnings:
// int result UNUSED = DoSomething();
// assert(result == 17);
#ifndef UNUSED
#ifdef __GNUC__
#define UNUSED __attribute__((unused))
#else
#define UNUSED
#endif
#endif
#endif // WEBRTC_TYPEDEFS_H_ #endif // WEBRTC_TYPEDEFS_H_