Chris@756: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@756: Chris@756: /* Chris@756: Sonic Visualiser Chris@756: An audio file viewer and annotation editor. Chris@756: Centre for Digital Music, Queen Mary, University of London. Chris@756: This file copyright 2013 Chris Cannam. Chris@756: Chris@756: This program is free software; you can redistribute it and/or Chris@756: modify it under the terms of the GNU General Public License as Chris@756: published by the Free Software Foundation; either version 2 of the Chris@756: License, or (at your option) any later version. See the file Chris@756: COPYING included with this distribution for more information. Chris@756: */ Chris@756: Chris@756: #ifndef TEST_AUDIO_FILE_READER_H Chris@756: #define TEST_AUDIO_FILE_READER_H Chris@756: Chris@756: #include "../AudioFileReaderFactory.h" Chris@756: #include "../AudioFileReader.h" Chris@756: Chris@756: #include "AudioTestData.h" Chris@756: Chris@756: #include Chris@756: Chris@756: #include Chris@756: #include Chris@756: #include Chris@756: Chris@756: #include Chris@756: Chris@756: using namespace std; Chris@756: Chris@756: static QString audioDir = "testfiles"; Chris@756: Chris@756: class AudioFileReaderTest : public QObject Chris@756: { Chris@756: Q_OBJECT Chris@756: Chris@756: const char *strOf(QString s) { Chris@756: return strdup(s.toLocal8Bit().data()); Chris@756: } Chris@756: Chris@756: private slots: Chris@756: void init() Chris@756: { Chris@756: if (!QDir(audioDir).exists()) { Chris@756: cerr << "ERROR: Audio test file directory \"" << audioDir << "\" does not exist" << endl; Chris@756: QVERIFY2(QDir(audioDir).exists(), "Audio test file directory not found"); Chris@756: } Chris@756: } Chris@756: Chris@756: void read_data() Chris@756: { Chris@756: QTest::addColumn("audiofile"); Chris@756: QStringList files = QDir(audioDir).entryList(QDir::Files); Chris@756: foreach (QString filename, files) { Chris@756: QTest::newRow(strOf(filename)) << filename; Chris@756: } Chris@756: } Chris@756: Chris@756: void read() Chris@756: { Chris@756: QFETCH(QString, audiofile); Chris@756: Chris@1040: sv_samplerate_t readRate = 48000; Chris@757: Chris@756: AudioFileReader *reader = Chris@756: AudioFileReaderFactory::createReader Chris@757: (audioDir + "/" + audiofile, readRate); Chris@757: Chris@757: QStringList fileAndExt = audiofile.split("."); Chris@757: QStringList bits = fileAndExt[0].split("-"); Chris@757: QString extension = fileAndExt[1]; Chris@1040: sv_samplerate_t nominalRate = bits[0].toInt(); Chris@757: int nominalChannels = bits[1].toInt(); Chris@757: int nominalDepth = 16; Chris@757: if (bits.length() > 2) nominalDepth = bits[2].toInt(); Chris@756: Chris@756: if (!reader) { Chris@820: #if ( QT_VERSION >= 0x050000 ) Chris@763: QSKIP("Unsupported file, skipping"); Chris@820: #else Chris@820: QSKIP("Unsupported file, skipping", SkipSingle); Chris@820: #endif Chris@756: } Chris@756: Chris@757: QCOMPARE((int)reader->getChannelCount(), nominalChannels); Chris@1040: QCOMPARE(reader->getNativeRate(), nominalRate); Chris@1040: QCOMPARE(reader->getSampleRate(), readRate); Chris@757: Chris@756: int channels = reader->getChannelCount(); Chris@757: AudioTestData tdata(readRate, channels); Chris@756: Chris@756: float *reference = tdata.getInterleavedData(); Chris@1040: sv_frame_t refFrames = tdata.getFrameCount(); Chris@756: Chris@756: // The reader should give us exactly the expected number of Chris@759: // frames, except for mp3/aac files. We ask for quite a lot Chris@759: // more, though, so we can (a) check that we only get the Chris@759: // expected number back (if this is not mp3/aac) or (b) take Chris@759: // into account silence at beginning and end (if it is). Chris@1041: vector test = reader->getInterleavedFrames(0, refFrames + 5000); Chris@1040: sv_frame_t read = test.size() / channels; Chris@756: Chris@759: if (extension == "mp3" || extension == "aac" || extension == "m4a") { Chris@759: // mp3s and aacs can have silence at start and end Chris@759: QVERIFY(read >= refFrames); Chris@757: } else { Chris@759: QCOMPARE(read, refFrames); Chris@757: } Chris@757: Chris@757: // Our limits are pretty relaxed -- we're not testing decoder Chris@757: // or resampler quality here, just whether the results are Chris@757: // plainly wrong (e.g. at wrong samplerate or with an offset) Chris@757: Chris@1040: double limit = 0.01; Chris@1040: double edgeLimit = limit * 10; // in first or final edgeSize frames Chris@759: int edgeSize = 100; Chris@759: Chris@757: if (nominalDepth < 16) { Chris@757: limit = 0.02; Chris@757: } Chris@759: if (extension == "ogg" || extension == "mp3" || Chris@759: extension == "aac" || extension == "m4a") { Chris@759: limit = 0.2; Chris@759: edgeLimit = limit * 3; Chris@757: } Chris@757: Chris@759: // And we ignore completely the last few frames when upsampling Chris@1040: int discard = 1 + int(round(readRate / nominalRate)); Chris@759: Chris@759: int offset = 0; Chris@759: Chris@759: if (extension == "aac" || extension == "m4a") { Chris@759: // our m4a file appears to have a fixed offset of 1024 (at Chris@759: // file sample rate) Chris@1040: offset = int(round((1024 / nominalRate) * readRate)); Chris@759: } Chris@759: Chris@759: if (extension == "mp3") { Chris@759: // while mp3s appear to vary Chris@759: for (int i = 0; i < read; ++i) { Chris@759: bool any = false; Chris@1040: double thresh = 0.01; Chris@759: for (int c = 0; c < channels; ++c) { Chris@1040: if (fabs(test[i * channels + c]) > thresh) { Chris@759: any = true; Chris@759: break; Chris@759: } Chris@759: } Chris@759: if (any) { Chris@759: offset = i; Chris@759: break; Chris@759: } Chris@759: } Chris@759: // std::cerr << "offset = " << offset << std::endl; Chris@759: } Chris@756: Chris@756: for (int c = 0; c < channels; ++c) { Chris@756: float maxdiff = 0.f; Chris@756: int maxAt = 0; Chris@756: float totdiff = 0.f; Chris@759: for (int i = 0; i < read - offset - discard && i < refFrames; ++i) { Chris@759: float diff = fabsf(test[(i + offset) * channels + c] - Chris@756: reference[i * channels + c]); Chris@756: totdiff += diff; Chris@757: // in edge areas, record this only if it exceeds edgeLimit Chris@759: if (i < edgeSize || i + edgeSize >= read - offset) { Chris@968: if (diff > edgeLimit && diff > maxdiff) { Chris@757: maxdiff = diff; Chris@757: maxAt = i; Chris@757: } Chris@757: } else { Chris@757: if (diff > maxdiff) { Chris@757: maxdiff = diff; Chris@757: maxAt = i; Chris@757: } Chris@756: } Chris@756: } Chris@1040: float meandiff = totdiff / float(read); Chris@756: // cerr << "meandiff on channel " << c << ": " << meandiff << endl; Chris@756: // cerr << "maxdiff on channel " << c << ": " << maxdiff << " at " << maxAt << endl; Chris@759: if (meandiff >= limit) { Chris@759: cerr << "ERROR: for audiofile " << audiofile << ": mean diff = " << meandiff << " for channel " << c << endl; Chris@759: QVERIFY(meandiff < limit); Chris@759: } Chris@756: if (maxdiff >= limit) { Chris@759: cerr << "ERROR: for audiofile " << audiofile << ": max diff = " << maxdiff << " at frame " << maxAt << " of " << read << " on channel " << c << " (mean diff = " << meandiff << ")" << endl; Chris@756: QVERIFY(maxdiff < limit); Chris@756: } Chris@756: } Chris@756: } Chris@756: }; Chris@756: Chris@756: #endif