summaryrefslogtreecommitdiff
path: root/test/analysis
diff options
context:
space:
mode:
authorpepper <peppersclothescult@gmail.com>2015-01-10 21:32:32 -0800
committerpepper <peppersclothescult@gmail.com>2015-01-10 21:32:32 -0800
commitd53fa8a169832563c62262078b8d2ffe5cab8473 (patch)
treeb911d06d357d009c976709780f10e92ce915228a /test/analysis
first
Diffstat (limited to 'test/analysis')
-rw-r--r--test/analysis/AnalysisClipping.c25
-rw-r--r--test/analysis/AnalysisClipping.h3
-rw-r--r--test/analysis/AnalysisClippingTest.c37
-rw-r--r--test/analysis/AnalysisDistortion.c33
-rw-r--r--test/analysis/AnalysisDistortion.h3
-rw-r--r--test/analysis/AnalysisDistortionTest.c37
-rw-r--r--test/analysis/AnalysisSilence.c25
-rw-r--r--test/analysis/AnalysisSilence.h3
-rw-r--r--test/analysis/AnalysisSilenceTest.c57
-rw-r--r--test/analysis/AnalyzeFile.c123
-rw-r--r--test/analysis/AnalyzeFile.h37
11 files changed, 383 insertions, 0 deletions
diff --git a/test/analysis/AnalysisClipping.c b/test/analysis/AnalysisClipping.c
new file mode 100644
index 0000000..55229ae
--- /dev/null
+++ b/test/analysis/AnalysisClipping.c
@@ -0,0 +1,25 @@
+#include <math.h>
+#include "AnalysisClipping.h"
+
+boolByte analysisClipping(const SampleBuffer sampleBuffer, AnalysisFunctionData data)
+{
+ for (ChannelCount i = 0; i < sampleBuffer->numChannels; i++) {
+ for (SampleCount j = 0; j < sampleBuffer->blocksize; j++) {
+ if (fabs(sampleBuffer->samples[i][j]) >= 1.0f) {
+ if (data->consecutiveFailCounter > data->failTolerance) {
+ data->failedChannel = i;
+ data->failedSample = j;
+ return false;
+ } else {
+ data->consecutiveFailCounter++;
+ }
+ } else {
+ if (data->consecutiveFailCounter > 0) {
+ data->consecutiveFailCounter--;
+ }
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/test/analysis/AnalysisClipping.h b/test/analysis/AnalysisClipping.h
new file mode 100644
index 0000000..6dc56ef
--- /dev/null
+++ b/test/analysis/AnalysisClipping.h
@@ -0,0 +1,3 @@
+#include "AnalyzeFile.h"
+
+boolByte analysisClipping(const SampleBuffer sampleBuffer, AnalysisFunctionData data);
diff --git a/test/analysis/AnalysisClippingTest.c b/test/analysis/AnalysisClippingTest.c
new file mode 100644
index 0000000..bd062a3
--- /dev/null
+++ b/test/analysis/AnalysisClippingTest.c
@@ -0,0 +1,37 @@
+#include "unit/TestRunner.h"
+#include "AnalysisClipping.h"
+
+static int _testAnalysisClipping(void)
+{
+ SampleBuffer s = newSampleBuffer(1, 128);
+ AnalysisFunctionData d = newAnalysisFunctionData();
+ unsigned long i;
+
+ for (i = 0; i < s->blocksize; i++) {
+ s->samples[0][i] = 1.0f;
+ }
+
+ assertFalse(analysisClipping(s, d));
+ freeAnalysisFunctionData(d);
+ freeSampleBuffer(s);
+ return 0;
+}
+
+static int _testAnalysisNotClipping(void)
+{
+ SampleBuffer s = newSampleBuffer(1, 8);
+ AnalysisFunctionData d = newAnalysisFunctionData();
+ assert(analysisClipping(s, d));
+ freeAnalysisFunctionData(d);
+ freeSampleBuffer(s);
+ return 0;
+}
+
+TestSuite addAnalysisClippingTests(void);
+TestSuite addAnalysisClippingTests(void)
+{
+ TestSuite testSuite = newTestSuite("AnalysisClipping", NULL, NULL);
+ addTest(testSuite, "AnalysisClipping", _testAnalysisClipping);
+ addTest(testSuite, "AnalysisNotClipping", _testAnalysisNotClipping);
+ return testSuite;
+}
diff --git a/test/analysis/AnalysisDistortion.c b/test/analysis/AnalysisDistortion.c
new file mode 100644
index 0000000..27b8492
--- /dev/null
+++ b/test/analysis/AnalysisDistortion.c
@@ -0,0 +1,33 @@
+#include <stdlib.h>
+#include "AnalysisDistortion.h"
+
+// If two samples differ by more than this amount, then we call it distortion
+static const Sample kAnalysisDistortionTolerance = 0.5f;
+
+boolByte analysisDistortion(const SampleBuffer sampleBuffer, AnalysisFunctionData data)
+{
+ Sample difference;
+
+ for (ChannelCount channelIndex = 0; channelIndex < sampleBuffer->numChannels; channelIndex++) {
+ for (SampleCount sampleIndex = 0; sampleIndex < sampleBuffer->blocksize; sampleIndex++) {
+ if (sampleBuffer->samples[channelIndex][sampleIndex] > data->lastSample[channelIndex]) {
+ difference = sampleBuffer->samples[channelIndex][sampleIndex] - data->lastSample[channelIndex];
+ } else {
+ difference = data->lastSample[channelIndex] - sampleBuffer->samples[channelIndex][sampleIndex];
+ }
+
+ if (difference >= kAnalysisDistortionTolerance) {
+ // In this test, we don't care about the consecutive sample count. That is because
+ // we also want to detect harsh clicks which occur by a jump in the amplitude, which
+ // is a common error in many plugins.
+ data->failedChannel = channelIndex;
+ data->failedSample = sampleIndex;
+ return false;
+ }
+
+ data->lastSample[channelIndex] = sampleBuffer->samples[channelIndex][sampleIndex];
+ }
+ }
+
+ return true;
+}
diff --git a/test/analysis/AnalysisDistortion.h b/test/analysis/AnalysisDistortion.h
new file mode 100644
index 0000000..a517777
--- /dev/null
+++ b/test/analysis/AnalysisDistortion.h
@@ -0,0 +1,3 @@
+#include "AnalyzeFile.h"
+
+boolByte analysisDistortion(const SampleBuffer sampleBuffer, AnalysisFunctionData data);
diff --git a/test/analysis/AnalysisDistortionTest.c b/test/analysis/AnalysisDistortionTest.c
new file mode 100644
index 0000000..cb9441c
--- /dev/null
+++ b/test/analysis/AnalysisDistortionTest.c
@@ -0,0 +1,37 @@
+#include "AnalysisDistortion.h"
+#include "unit/TestRunner.h"
+
+static int _testAnalysisDistortion(void)
+{
+ SampleBuffer s = newSampleBuffer(1, 8);
+ AnalysisFunctionData d = newAnalysisFunctionData();
+ unsigned int i;
+
+ for (i = 0; i < s->blocksize; i++) {
+ s->samples[0][i] = 0.9f * (i % 2 ? 1.0f : -1.0f);
+ }
+
+ assertFalse(analysisDistortion(s, d));
+ freeAnalysisFunctionData(d);
+ freeSampleBuffer(s);
+ return 0;
+}
+
+static int _testAnalysisNotDistortion(void)
+{
+ SampleBuffer s = newSampleBuffer(1, 8);
+ AnalysisFunctionData d = newAnalysisFunctionData();
+ assert(analysisDistortion(s, d));
+ freeAnalysisFunctionData(d);
+ freeSampleBuffer(s);
+ return 0;
+}
+
+TestSuite addAnalysisDistortionTests(void);
+TestSuite addAnalysisDistortionTests(void)
+{
+ TestSuite testSuite = newTestSuite("AnalysisDistortion", NULL, NULL);
+ addTest(testSuite, "AnalysisDistortion", _testAnalysisDistortion);
+ addTest(testSuite, "AnalysisNotDistortion", _testAnalysisNotDistortion);
+ return testSuite;
+}
diff --git a/test/analysis/AnalysisSilence.c b/test/analysis/AnalysisSilence.c
new file mode 100644
index 0000000..0876245
--- /dev/null
+++ b/test/analysis/AnalysisSilence.c
@@ -0,0 +1,25 @@
+#include "AnalysisSilence.h"
+#include "AnalyzeFile.h"
+
+boolByte analysisSilence(const SampleBuffer sampleBuffer, AnalysisFunctionData data)
+{
+ for (ChannelCount i = 0; i < sampleBuffer->numChannels; ++i) {
+ for (SampleCount j = 0; j < sampleBuffer->blocksize; ++j) {
+ if (sampleBuffer->samples[i][j] == 0.0f) {
+ data->consecutiveFailCounter++;
+
+ if (data->consecutiveFailCounter > data->failTolerance) {
+ data->failedChannel = i;
+ data->failedSample = j;
+ return false;
+ }
+ } else {
+ if (data->consecutiveFailCounter > 0) {
+ data->consecutiveFailCounter = 0;
+ }
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/test/analysis/AnalysisSilence.h b/test/analysis/AnalysisSilence.h
new file mode 100644
index 0000000..4e56c6c
--- /dev/null
+++ b/test/analysis/AnalysisSilence.h
@@ -0,0 +1,3 @@
+#include "AnalyzeFile.h"
+
+boolByte analysisSilence(const SampleBuffer sampleBuffer, AnalysisFunctionData data);
diff --git a/test/analysis/AnalysisSilenceTest.c b/test/analysis/AnalysisSilenceTest.c
new file mode 100644
index 0000000..968bc37
--- /dev/null
+++ b/test/analysis/AnalysisSilenceTest.c
@@ -0,0 +1,57 @@
+#include "AnalysisSilence.h"
+#include "unit/TestRunner.h"
+
+static int _testAnalysisSilence(void)
+{
+ SampleBuffer s = newSampleBuffer(1, 64);
+ AnalysisFunctionData d = newAnalysisFunctionData();
+ assertFalse(analysisSilence(s, d));
+ freeAnalysisFunctionData(d);
+ freeSampleBuffer(s);
+ return 0;
+}
+
+static int _testAnalysisNotSilence(void)
+{
+ SampleBuffer s = newSampleBuffer(2, 64);
+ AnalysisFunctionData d = newAnalysisFunctionData();
+ unsigned long i;
+ unsigned int j;
+
+ for (i = 0; i < s->blocksize; i++) {
+ for (j = 0; j < s->numChannels; j++) {
+ s->samples[j][i] = 32767.0;
+ }
+ }
+
+ assert(analysisSilence(s, d));
+ freeAnalysisFunctionData(d);
+ freeSampleBuffer(s);
+ return 0;
+}
+
+static int _testAnalysisNotSilenceInOneChannel(void)
+{
+ SampleBuffer s = newSampleBuffer(1, 64);
+ AnalysisFunctionData d = newAnalysisFunctionData();
+ unsigned long i;
+
+ for (i = 0; i < s->blocksize; i++) {
+ s->samples[0][i] = 32767.0;
+ }
+
+ assert(analysisSilence(s, d));
+ freeAnalysisFunctionData(d);
+ freeSampleBuffer(s);
+ return 0;
+}
+
+TestSuite addAnalysisSilenceTests(void);
+TestSuite addAnalysisSilenceTests(void)
+{
+ TestSuite testSuite = newTestSuite("AnalysisSilence", NULL, NULL);
+ addTest(testSuite, "AnalysisSilence", _testAnalysisSilence);
+ addTest(testSuite, "AnalysisNotSilence", _testAnalysisNotSilence);
+ addTest(testSuite, "AnalysisNotSilenceInOneChannel", _testAnalysisNotSilenceInOneChannel);
+ return testSuite;
+}
diff --git a/test/analysis/AnalyzeFile.c b/test/analysis/AnalyzeFile.c
new file mode 100644
index 0000000..11e020f
--- /dev/null
+++ b/test/analysis/AnalyzeFile.c
@@ -0,0 +1,123 @@
+#include <stdlib.h>
+#include "audio/AudioSettings.h"
+#include "io/SampleSource.h"
+#include "AnalysisClipping.h"
+#include "AnalysisDistortion.h"
+#include "AnalysisSilence.h"
+
+// Number of consecutive samples which need to fail in order for the test to fail
+static const int kAnalysisDefaultFailTolerance = 16;
+// Use a blocksize of the default * 2 in order to avoid false positives of the
+// silence detection algorithm, since the last block is likely to be silent.
+static const int kAnalysisBlocksize = DEFAULT_BLOCKSIZE * 2;
+
+static LinkedList _getAnalysisFunctions(void)
+{
+ AnalysisFunctionData data;
+ LinkedList functionsList = newLinkedList();
+
+ data = newAnalysisFunctionData();
+ data->analysisName = "clipping";
+ data->functionPtr = (void *)analysisClipping;
+ linkedListAppend(functionsList, data);
+
+ data = newAnalysisFunctionData();
+ data->analysisName = "distortion";
+ data->functionPtr = (void *)analysisDistortion;
+ linkedListAppend(functionsList, data);
+
+ data = newAnalysisFunctionData();
+ data->analysisName = "silence";
+ data->functionPtr = (void *)analysisSilence;
+ data->failTolerance = kAnalysisBlocksize;
+ linkedListAppend(functionsList, data);
+
+ return functionsList;
+}
+
+static void _runAnalysisFunction(void *item, void *userData)
+{
+ AnalysisFunctionData functionData = (AnalysisFunctionData)item;
+ AnalysisFuncPtr analysisFuncPtr = (AnalysisFuncPtr)(functionData->functionPtr);
+ AnalysisData analysisData = (AnalysisData)userData;
+
+ if (!analysisFuncPtr(analysisData->sampleBuffer, functionData)) {
+ charStringCopyCString(analysisData->failedAnalysisFunctionName, functionData->analysisName);
+ *(analysisData->failedAnalysisFrame) = *(analysisData->currentFrame) + functionData->failedSample;
+ *(analysisData->failedAnalysisChannel) = functionData->failedChannel;
+ *(analysisData->result) = false;
+ }
+}
+
+boolByte analyzeFile(const char *filename, CharString failedAnalysisFunctionName,
+ ChannelCount *failedAnalysisChannel, SampleCount *failedAnalysisFrame)
+{
+ boolByte result;
+ CharString analysisFilename;
+ SampleSource sampleSource;
+ LinkedList analysisFunctions;
+ AnalysisData analysisData = (AnalysisData)malloc(sizeof(AnalysisDataMembers));
+ SampleCount currentFrame = 0;
+
+ // Needed to initialize new sample sources
+ initAudioSettings();
+ analysisFunctions = _getAnalysisFunctions();
+ analysisFilename = newCharStringWithCString(filename);
+ sampleSource = sampleSourceFactory(analysisFilename);
+
+ if (sampleSource == NULL) {
+ freeCharString(analysisFilename);
+ free(analysisData);
+ freeAudioSettings();
+ return false;
+ }
+
+ result = sampleSource->openSampleSource(sampleSource, SAMPLE_SOURCE_OPEN_READ);
+
+ if (!result) {
+ free(analysisData);
+ return result;
+ }
+
+ analysisData->failedAnalysisFunctionName = failedAnalysisFunctionName;
+ analysisData->failedAnalysisChannel = failedAnalysisChannel;
+ analysisData->failedAnalysisFrame = failedAnalysisFrame;
+ analysisData->sampleBuffer = newSampleBuffer(DEFAULT_NUM_CHANNELS, kAnalysisBlocksize);
+ analysisData->currentFrame = &currentFrame;
+ analysisData->result = &result;
+
+ while (sampleSource->readSampleBlock(sampleSource, analysisData->sampleBuffer) && result) {
+ linkedListForeach(analysisFunctions, _runAnalysisFunction, analysisData);
+ currentFrame += kAnalysisBlocksize;
+ }
+
+ freeSampleSource(sampleSource);
+ freeCharString(analysisFilename);
+ freeAudioSettings();
+ freeSampleBuffer(analysisData->sampleBuffer);
+ freeLinkedListAndItems(analysisFunctions, (LinkedListFreeItemFunc)freeAnalysisFunctionData);
+ free(analysisData);
+ return result;
+}
+
+void freeAnalysisFunctionData(AnalysisFunctionData self)
+{
+ free(self->lastSample);
+ free(self);
+}
+
+AnalysisFunctionData newAnalysisFunctionData(void)
+{
+ AnalysisFunctionData result = (AnalysisFunctionData)malloc(sizeof(AnalysisFunctionDataMembers));
+ result->analysisName = NULL;
+ result->consecutiveFailCounter = 0;
+ result->failedSample = 0;
+ result->functionPtr = NULL;
+ // TODO: Should use max channels, when we get that
+ result->lastSample = (Sample*)malloc(sizeof(Sample) * 2);
+ result->lastSample[0] = 0.0f;
+ result->lastSample[1] = 0.0f;
+ result->failTolerance = kAnalysisDefaultFailTolerance;
+ return result;
+}
+
diff --git a/test/analysis/AnalyzeFile.h b/test/analysis/AnalyzeFile.h
new file mode 100644
index 0000000..d47b3ba
--- /dev/null
+++ b/test/analysis/AnalyzeFile.h
@@ -0,0 +1,37 @@
+#include <stdio.h>
+#include "base/Types.h"
+#include "audio/SampleBuffer.h"
+#include "base/CharString.h"
+
+#ifndef MrsWatson_AnalyzeFile_h
+#define MrsWatson_AnalyzeFile_h
+
+typedef struct {
+ const char *analysisName;
+ void *functionPtr;
+ int consecutiveFailCounter;
+ Sample *lastSample;
+ SampleCount failedSample;
+ ChannelCount failedChannel;
+ int failTolerance;
+} AnalysisFunctionDataMembers;
+typedef AnalysisFunctionDataMembers *AnalysisFunctionData;
+typedef boolByte (*AnalysisFuncPtr)(const SampleBuffer sampleBuffer, AnalysisFunctionData data);
+
+typedef struct {
+ CharString failedAnalysisFunctionName;
+ SampleCount *failedAnalysisFrame;
+ ChannelCount *failedAnalysisChannel;
+ SampleCount *currentFrame;
+ boolByte *result;
+ SampleBuffer sampleBuffer;
+ AnalysisFunctionData functionData;
+} AnalysisDataMembers;
+typedef AnalysisDataMembers *AnalysisData;
+
+AnalysisFunctionData newAnalysisFunctionData(void);
+boolByte analyzeFile(const char *filename, CharString failedAnalysisFunctionName,
+ ChannelCount *failedAnalysisChannel, SampleCount *failedAnalysisFrame);
+void freeAnalysisFunctionData(AnalysisFunctionData self);
+
+#endif