annotate data/fileio/test/AudioFileReaderTest.h @ 1008:d9e0e59a1581

When using an aggregate model to pass data to a transform, zero-pad the shorter input to the duration of the longer rather than truncating the longer. (This is better behaviour for e.g. MATCH, and in any case the code was previously truncating incorrectly and ending up with garbage data at the end.)
author Chris Cannam
date Fri, 14 Nov 2014 13:51:33 +0000
parents 6b931eeba385
children a1cd5abcb38b
rev   line source
Chris@756 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@756 2
Chris@756 3 /*
Chris@756 4 Sonic Visualiser
Chris@756 5 An audio file viewer and annotation editor.
Chris@756 6 Centre for Digital Music, Queen Mary, University of London.
Chris@756 7 This file copyright 2013 Chris Cannam.
Chris@756 8
Chris@756 9 This program is free software; you can redistribute it and/or
Chris@756 10 modify it under the terms of the GNU General Public License as
Chris@756 11 published by the Free Software Foundation; either version 2 of the
Chris@756 12 License, or (at your option) any later version. See the file
Chris@756 13 COPYING included with this distribution for more information.
Chris@756 14 */
Chris@756 15
Chris@756 16 #ifndef TEST_AUDIO_FILE_READER_H
Chris@756 17 #define TEST_AUDIO_FILE_READER_H
Chris@756 18
Chris@756 19 #include "../AudioFileReaderFactory.h"
Chris@756 20 #include "../AudioFileReader.h"
Chris@756 21
Chris@756 22 #include "AudioTestData.h"
Chris@756 23
Chris@756 24 #include <cmath>
Chris@756 25
Chris@756 26 #include <QObject>
Chris@756 27 #include <QtTest>
Chris@756 28 #include <QDir>
Chris@756 29
Chris@756 30 #include <iostream>
Chris@756 31
Chris@756 32 using namespace std;
Chris@756 33
Chris@756 34 static QString audioDir = "testfiles";
Chris@756 35
Chris@756 36 class AudioFileReaderTest : public QObject
Chris@756 37 {
Chris@756 38 Q_OBJECT
Chris@756 39
Chris@756 40 const char *strOf(QString s) {
Chris@756 41 return strdup(s.toLocal8Bit().data());
Chris@756 42 }
Chris@756 43
Chris@756 44 private slots:
Chris@756 45 void init()
Chris@756 46 {
Chris@756 47 if (!QDir(audioDir).exists()) {
Chris@756 48 cerr << "ERROR: Audio test file directory \"" << audioDir << "\" does not exist" << endl;
Chris@756 49 QVERIFY2(QDir(audioDir).exists(), "Audio test file directory not found");
Chris@756 50 }
Chris@756 51 }
Chris@756 52
Chris@756 53 void read_data()
Chris@756 54 {
Chris@756 55 QTest::addColumn<QString>("audiofile");
Chris@756 56 QStringList files = QDir(audioDir).entryList(QDir::Files);
Chris@756 57 foreach (QString filename, files) {
Chris@756 58 QTest::newRow(strOf(filename)) << filename;
Chris@756 59 }
Chris@756 60 }
Chris@756 61
Chris@756 62 void read()
Chris@756 63 {
Chris@756 64 QFETCH(QString, audiofile);
Chris@756 65
Chris@757 66 int readRate = 48000;
Chris@757 67
Chris@756 68 AudioFileReader *reader =
Chris@756 69 AudioFileReaderFactory::createReader
Chris@757 70 (audioDir + "/" + audiofile, readRate);
Chris@757 71
Chris@757 72 QStringList fileAndExt = audiofile.split(".");
Chris@757 73 QStringList bits = fileAndExt[0].split("-");
Chris@757 74 QString extension = fileAndExt[1];
Chris@757 75 int nominalRate = bits[0].toInt();
Chris@757 76 int nominalChannels = bits[1].toInt();
Chris@757 77 int nominalDepth = 16;
Chris@757 78 if (bits.length() > 2) nominalDepth = bits[2].toInt();
Chris@756 79
Chris@756 80 if (!reader) {
Chris@820 81 #if ( QT_VERSION >= 0x050000 )
Chris@763 82 QSKIP("Unsupported file, skipping");
Chris@820 83 #else
Chris@820 84 QSKIP("Unsupported file, skipping", SkipSingle);
Chris@820 85 #endif
Chris@756 86 }
Chris@756 87
Chris@757 88 QCOMPARE((int)reader->getChannelCount(), nominalChannels);
Chris@757 89 QCOMPARE((int)reader->getNativeRate(), nominalRate);
Chris@757 90 QCOMPARE((int)reader->getSampleRate(), readRate);
Chris@757 91
Chris@756 92 int channels = reader->getChannelCount();
Chris@757 93 AudioTestData tdata(readRate, channels);
Chris@756 94
Chris@756 95 float *reference = tdata.getInterleavedData();
Chris@759 96 int refFrames = tdata.getFrameCount();
Chris@756 97
Chris@756 98 vector<float> test;
Chris@756 99
Chris@756 100 // The reader should give us exactly the expected number of
Chris@759 101 // frames, except for mp3/aac files. We ask for quite a lot
Chris@759 102 // more, though, so we can (a) check that we only get the
Chris@759 103 // expected number back (if this is not mp3/aac) or (b) take
Chris@759 104 // into account silence at beginning and end (if it is).
Chris@759 105 reader->getInterleavedFrames(0, refFrames + 5000, test);
Chris@756 106 int read = test.size() / channels;
Chris@756 107
Chris@759 108 if (extension == "mp3" || extension == "aac" || extension == "m4a") {
Chris@759 109 // mp3s and aacs can have silence at start and end
Chris@759 110 QVERIFY(read >= refFrames);
Chris@757 111 } else {
Chris@759 112 QCOMPARE(read, refFrames);
Chris@757 113 }
Chris@757 114
Chris@757 115 // Our limits are pretty relaxed -- we're not testing decoder
Chris@757 116 // or resampler quality here, just whether the results are
Chris@757 117 // plainly wrong (e.g. at wrong samplerate or with an offset)
Chris@757 118
Chris@757 119 float limit = 0.01;
Chris@759 120 float edgeLimit = limit * 10; // in first or final edgeSize frames
Chris@759 121 int edgeSize = 100;
Chris@759 122
Chris@757 123 if (nominalDepth < 16) {
Chris@757 124 limit = 0.02;
Chris@757 125 }
Chris@759 126 if (extension == "ogg" || extension == "mp3" ||
Chris@759 127 extension == "aac" || extension == "m4a") {
Chris@759 128 limit = 0.2;
Chris@759 129 edgeLimit = limit * 3;
Chris@757 130 }
Chris@757 131
Chris@759 132 // And we ignore completely the last few frames when upsampling
Chris@759 133 int discard = 1 + readRate / nominalRate;
Chris@759 134
Chris@759 135 int offset = 0;
Chris@759 136
Chris@759 137 if (extension == "aac" || extension == "m4a") {
Chris@759 138 // our m4a file appears to have a fixed offset of 1024 (at
Chris@759 139 // file sample rate)
Chris@759 140 offset = (1024 / float(nominalRate)) * readRate;
Chris@759 141 }
Chris@759 142
Chris@759 143 if (extension == "mp3") {
Chris@759 144 // while mp3s appear to vary
Chris@759 145 for (int i = 0; i < read; ++i) {
Chris@759 146 bool any = false;
Chris@759 147 float thresh = 0.01;
Chris@759 148 for (int c = 0; c < channels; ++c) {
Chris@759 149 if (fabsf(test[i * channels + c]) > thresh) {
Chris@759 150 any = true;
Chris@759 151 break;
Chris@759 152 }
Chris@759 153 }
Chris@759 154 if (any) {
Chris@759 155 offset = i;
Chris@759 156 break;
Chris@759 157 }
Chris@759 158 }
Chris@759 159 // std::cerr << "offset = " << offset << std::endl;
Chris@759 160 }
Chris@756 161
Chris@756 162 for (int c = 0; c < channels; ++c) {
Chris@756 163 float maxdiff = 0.f;
Chris@756 164 int maxAt = 0;
Chris@756 165 float totdiff = 0.f;
Chris@759 166 for (int i = 0; i < read - offset - discard && i < refFrames; ++i) {
Chris@759 167 float diff = fabsf(test[(i + offset) * channels + c] -
Chris@756 168 reference[i * channels + c]);
Chris@756 169 totdiff += diff;
Chris@757 170 // in edge areas, record this only if it exceeds edgeLimit
Chris@759 171 if (i < edgeSize || i + edgeSize >= read - offset) {
Chris@968 172 if (diff > edgeLimit && diff > maxdiff) {
Chris@757 173 maxdiff = diff;
Chris@757 174 maxAt = i;
Chris@757 175 }
Chris@757 176 } else {
Chris@757 177 if (diff > maxdiff) {
Chris@757 178 maxdiff = diff;
Chris@757 179 maxAt = i;
Chris@757 180 }
Chris@756 181 }
Chris@756 182 }
Chris@756 183 float meandiff = totdiff / read;
Chris@756 184 // cerr << "meandiff on channel " << c << ": " << meandiff << endl;
Chris@756 185 // cerr << "maxdiff on channel " << c << ": " << maxdiff << " at " << maxAt << endl;
Chris@759 186 if (meandiff >= limit) {
Chris@759 187 cerr << "ERROR: for audiofile " << audiofile << ": mean diff = " << meandiff << " for channel " << c << endl;
Chris@759 188 QVERIFY(meandiff < limit);
Chris@759 189 }
Chris@756 190 if (maxdiff >= limit) {
Chris@759 191 cerr << "ERROR: for audiofile " << audiofile << ": max diff = " << maxdiff << " at frame " << maxAt << " of " << read << " on channel " << c << " (mean diff = " << meandiff << ")" << endl;
Chris@756 192 QVERIFY(maxdiff < limit);
Chris@756 193 }
Chris@756 194 }
Chris@756 195 }
Chris@756 196 };
Chris@756 197
Chris@756 198 #endif