Chris@1345
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@1345
|
2
|
Chris@1345
|
3 /*
|
Chris@1345
|
4 Sonic Visualiser
|
Chris@1345
|
5 An audio file viewer and annotation editor.
|
Chris@1345
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@1345
|
7
|
Chris@1345
|
8 This program is free software; you can redistribute it and/or
|
Chris@1345
|
9 modify it under the terms of the GNU General Public License as
|
Chris@1345
|
10 published by the Free Software Foundation; either version 2 of the
|
Chris@1345
|
11 License, or (at your option) any later version. See the file
|
Chris@1345
|
12 COPYING included with this distribution for more information.
|
Chris@1345
|
13 */
|
Chris@1345
|
14
|
Chris@1345
|
15 #ifndef TEST_AUDIO_ENCODINGS_H
|
Chris@1345
|
16 #define TEST_AUDIO_ENCODINGS_H
|
Chris@1345
|
17
|
Chris@1345
|
18 // Quick tests for filename encodings and encoding of ID3 data. Not a
|
Chris@1345
|
19 // test of audio codecs.
|
Chris@1345
|
20
|
Chris@1345
|
21 #include "../AudioFileReaderFactory.h"
|
Chris@1345
|
22 #include "../AudioFileReader.h"
|
Chris@1359
|
23 #include "../WavFileWriter.h"
|
Chris@1345
|
24
|
Chris@1698
|
25 #include "UnsupportedFormat.h"
|
Chris@1698
|
26
|
Chris@1345
|
27 #include <cmath>
|
Chris@1345
|
28
|
Chris@1345
|
29 #include <QObject>
|
Chris@1345
|
30 #include <QtTest>
|
Chris@1345
|
31 #include <QDir>
|
Chris@1345
|
32
|
Chris@1345
|
33 #include <iostream>
|
Chris@1345
|
34
|
Chris@1345
|
35 using namespace std;
|
Chris@1345
|
36
|
Chris@1346
|
37 const char utf8_name_cdp_1[] = "Caf\303\251 de Paris";
|
Chris@1346
|
38 const char utf8_name_cdp_2[] = "Caf\303\251 de \351\207\215\345\272\206";
|
Chris@1347
|
39 const char utf8_name_tsprk[] = "T\303\253mple of Sp\303\266rks";
|
Chris@1346
|
40 const char utf8_name_sprkt[] = "\343\202\271\343\203\235\343\203\274\343\202\257\343\201\256\345\257\272\351\231\242";
|
Chris@1345
|
41
|
Chris@1359
|
42 // Mapping between filename and expected title metadata field
|
Chris@1345
|
43 static const char *mapping[][2] = {
|
Chris@1346
|
44 { "id3v2-iso-8859-1", utf8_name_cdp_1 },
|
Chris@1346
|
45 { "id3v2-ucs-2", utf8_name_cdp_2 },
|
Chris@1346
|
46 { utf8_name_tsprk, utf8_name_tsprk },
|
Chris@1346
|
47 { utf8_name_sprkt, utf8_name_sprkt },
|
Chris@1345
|
48 };
|
Chris@1345
|
49 static const int mappingCount = 4;
|
Chris@1345
|
50
|
Chris@1597
|
51 #ifdef Q_OS_MAC
|
Chris@1597
|
52 // see note in addAudioFiles below
|
Chris@1593
|
53 static const char *testFiles[][2] = {
|
Chris@1593
|
54 { "id3v2-iso-8859-1", "mp3" },
|
Chris@1593
|
55 { "id3v2-ucs-2", "mp3" },
|
Chris@1593
|
56 { utf8_name_tsprk, "flac" },
|
Chris@1593
|
57 { utf8_name_tsprk, "m4a" },
|
Chris@1593
|
58 { utf8_name_tsprk, "mp3" },
|
Chris@1593
|
59 { utf8_name_tsprk, "ogg" },
|
Chris@1594
|
60 { utf8_name_tsprk, "opus" },
|
Chris@1593
|
61 { utf8_name_sprkt, "mp3" },
|
Chris@1593
|
62 { utf8_name_sprkt, "ogg" },
|
Chris@1593
|
63 };
|
Chris@1593
|
64 static const int testFileCount = 8;
|
Chris@1597
|
65 #endif
|
Chris@1593
|
66
|
Chris@1345
|
67 class EncodingTest : public QObject
|
Chris@1345
|
68 {
|
Chris@1345
|
69 Q_OBJECT
|
Chris@1345
|
70
|
Chris@1346
|
71 private:
|
Chris@1346
|
72 QString testDirBase;
|
Chris@1346
|
73 QString encodingDir;
|
Chris@1359
|
74 QString outDir;
|
Chris@1346
|
75
|
Chris@1346
|
76 public:
|
Chris@1346
|
77 EncodingTest(QString base) {
|
Chris@1346
|
78 if (base == "") {
|
Chris@1346
|
79 base = "svcore/data/fileio/test";
|
Chris@1346
|
80 }
|
Chris@1346
|
81 testDirBase = base;
|
Chris@1346
|
82 encodingDir = base + "/encodings";
|
Chris@1359
|
83 outDir = base + "/outfiles";
|
Chris@1346
|
84 }
|
Chris@1346
|
85
|
Chris@1346
|
86 private:
|
Chris@1596
|
87 const char *strOf(QString s) {
|
Chris@1596
|
88 return strdup(s.toLocal8Bit().data());
|
Chris@1596
|
89 }
|
Chris@1596
|
90
|
Chris@1359
|
91 void addAudioFiles() {
|
Chris@1596
|
92 QTest::addColumn<QString>("audiofile");
|
Chris@1596
|
93 #ifndef Q_OS_MAC
|
Chris@1596
|
94 // The normal case - populate the file list from the files
|
Chris@1596
|
95 // actually present in the encodings directory
|
Chris@1596
|
96 QStringList files = QDir(encodingDir).entryList(QDir::Files);
|
Chris@1596
|
97 foreach (QString filename, files) {
|
Chris@1596
|
98 QTest::newRow(strOf(filename)) << filename;
|
Chris@1596
|
99 }
|
Chris@1596
|
100 #else
|
Chris@1596
|
101 // Deviant case for Mac - populate the file list from the
|
Chris@1596
|
102 // hard-coded list of expected files in testFiles. This is
|
Chris@1596
|
103 // because QDir::entryList is currently broken on APFS (as of
|
Chris@1596
|
104 // Qt 5.12) because of variant Unicode normalisations.
|
Chris@1596
|
105 for (int i = 0; i < testFileCount; ++i) {
|
Chris@1596
|
106 std::string s = testFiles[i][0];
|
Chris@1596
|
107 s += ".";
|
Chris@1596
|
108 s += testFiles[i][1];
|
Chris@1596
|
109 QTest::newRow(strdup(s.c_str())) << QString::fromStdString(s);
|
Chris@1596
|
110 }
|
Chris@1596
|
111 #endif
|
Chris@1359
|
112 }
|
Chris@1359
|
113
|
Chris@1345
|
114 private slots:
|
Chris@1345
|
115 void init()
|
Chris@1345
|
116 {
|
Chris@1345
|
117 if (!QDir(encodingDir).exists()) {
|
Chris@1428
|
118 SVCERR << "ERROR: Audio encoding file directory \"" << encodingDir << "\" does not exist" << endl;
|
Chris@1345
|
119 QVERIFY2(QDir(encodingDir).exists(), "Audio encoding file directory not found");
|
Chris@1593
|
120 }
|
Chris@1359
|
121 if (!QDir(outDir).exists() && !QDir().mkpath(outDir)) {
|
Chris@1428
|
122 SVCERR << "ERROR: Audio out directory \"" << outDir << "\" does not exist and could not be created" << endl;
|
Chris@1359
|
123 QVERIFY2(QDir(outDir).exists(), "Audio out directory not found and could not be created");
|
Chris@1345
|
124 }
|
Chris@1345
|
125 }
|
Chris@1345
|
126
|
Chris@1359
|
127 void readAudio_data() {
|
Chris@1359
|
128 addAudioFiles();
|
Chris@1359
|
129 }
|
Chris@1359
|
130
|
Chris@1359
|
131 void readAudio() {
|
Chris@1359
|
132
|
Chris@1359
|
133 // Ensure that we can open all the files
|
Chris@1359
|
134
|
Chris@1359
|
135 QFETCH(QString, audiofile);
|
Chris@1359
|
136
|
Chris@1698
|
137 QStringList fileAndExt = audiofile.split(".");
|
Chris@1698
|
138 QString extension = fileAndExt[1];
|
Chris@1698
|
139
|
Chris@1592
|
140 if (!AudioFileReaderFactory::isSupported(encodingDir + "/" +
|
Chris@1592
|
141 audiofile)) {
|
Chris@1698
|
142 if (isLegitimatelyUnsupported(extension)) {
|
Chris@1592
|
143 #if ( QT_VERSION >= 0x050000 )
|
Chris@1698
|
144 QSKIP("Known unsupported file, skipping");
|
Chris@1592
|
145 #else
|
Chris@1698
|
146 QSKIP("Known unsupported file, skipping", SkipSingle);
|
Chris@1592
|
147 #endif
|
Chris@1698
|
148 }
|
Chris@1592
|
149 }
|
Chris@1592
|
150
|
Chris@1359
|
151 AudioFileReaderFactory::Parameters params;
|
Chris@1359
|
152 AudioFileReader *reader =
|
Chris@1359
|
153 AudioFileReaderFactory::createReader
|
Chris@1359
|
154 (encodingDir + "/" + audiofile, params);
|
Chris@1359
|
155
|
Chris@1359
|
156 QVERIFY(reader != nullptr);
|
Chris@1402
|
157
|
Chris@1402
|
158 delete reader;
|
Chris@1359
|
159 }
|
Chris@1359
|
160
|
Chris@1359
|
161 void readMetadata_data() {
|
Chris@1359
|
162 addAudioFiles();
|
Chris@1359
|
163 }
|
Chris@1359
|
164
|
Chris@1359
|
165 void readMetadata() {
|
Chris@1359
|
166
|
Chris@1359
|
167 // All files other than WAVs should have title metadata; check
|
Chris@1359
|
168 // that the title matches whatever is in our mapping structure
|
Chris@1359
|
169 // defined at the top
|
Chris@1359
|
170
|
Chris@1345
|
171 QFETCH(QString, audiofile);
|
Chris@1345
|
172
|
Chris@1698
|
173 QStringList fileAndExt = audiofile.split(".");
|
Chris@1698
|
174 QString file = fileAndExt[0];
|
Chris@1698
|
175 QString extension = fileAndExt[1];
|
Chris@1698
|
176
|
Chris@1345
|
177 AudioFileReaderFactory::Parameters params;
|
Chris@1346
|
178 AudioFileReader *reader =
|
Chris@1346
|
179 AudioFileReaderFactory::createReader
|
Chris@1346
|
180 (encodingDir + "/" + audiofile, params);
|
Chris@1345
|
181
|
Chris@1592
|
182 if (!reader) {
|
Chris@1698
|
183 if (isLegitimatelyUnsupported(extension)) {
|
Chris@1592
|
184 #if ( QT_VERSION >= 0x050000 )
|
Chris@1698
|
185 QSKIP("Unsupported file, skipping");
|
Chris@1592
|
186 #else
|
Chris@1698
|
187 QSKIP("Unsupported file, skipping", SkipSingle);
|
Chris@1592
|
188 #endif
|
Chris@1698
|
189 }
|
Chris@1592
|
190 }
|
Chris@1345
|
191
|
Chris@1698
|
192 QVERIFY(reader != nullptr);
|
Chris@1345
|
193
|
Chris@1402
|
194 if (extension == "wav") {
|
Chris@1402
|
195
|
Chris@1402
|
196 // Nothing
|
Chris@1402
|
197
|
Chris@1402
|
198 delete reader;
|
Chris@1402
|
199
|
Chris@1402
|
200 } else {
|
Chris@1345
|
201
|
Chris@1359
|
202 auto blah = reader->getInterleavedFrames(0, 10);
|
Chris@1359
|
203
|
Chris@1345
|
204 QString title = reader->getTitle();
|
Chris@1345
|
205 QVERIFY(title != QString());
|
Chris@1345
|
206
|
Chris@1402
|
207 delete reader;
|
Chris@1402
|
208
|
Chris@1345
|
209 bool found = false;
|
Chris@1345
|
210 for (int m = 0; m < mappingCount; ++m) {
|
Chris@1345
|
211 if (file == QString::fromUtf8(mapping[m][0])) {
|
Chris@1345
|
212 found = true;
|
Chris@1346
|
213 QString expected = QString::fromUtf8(mapping[m][1]);
|
Chris@1346
|
214 if (title != expected) {
|
Chris@1428
|
215 SVCERR << "Title does not match expected: codepoints are" << endl;
|
Chris@1428
|
216 SVCERR << "Title (" << title.length() << "ch): ";
|
Chris@1346
|
217 for (int i = 0; i < title.length(); ++i) {
|
Chris@1428
|
218 SVCERR << title[i].unicode() << " ";
|
Chris@1346
|
219 }
|
Chris@1428
|
220 SVCERR << endl;
|
Chris@1428
|
221 SVCERR << "Expected (" << expected.length() << "ch): ";
|
Chris@1346
|
222 for (int i = 0; i < expected.length(); ++i) {
|
Chris@1428
|
223 SVCERR << expected[i].unicode() << " ";
|
Chris@1346
|
224 }
|
Chris@1428
|
225 SVCERR << endl;
|
Chris@1346
|
226 }
|
Chris@1346
|
227 QCOMPARE(title, expected);
|
Chris@1345
|
228 break;
|
Chris@1345
|
229 }
|
Chris@1345
|
230 }
|
Chris@1345
|
231
|
Chris@1345
|
232 if (!found) {
|
Chris@1359
|
233 // Note that this can happen legitimately on Windows,
|
Chris@1359
|
234 // where (for annoying VCS-related reasons) the test
|
Chris@1359
|
235 // files may have a different filename encoding from
|
Chris@1359
|
236 // the expected UTF-16. We check this properly in
|
Chris@1359
|
237 // readWriteAudio below, by saving out the file to a
|
Chris@1359
|
238 // name matching the metadata
|
Chris@1428
|
239 SVCERR << "Couldn't find filename \""
|
Chris@1345
|
240 << file << "\" in title mapping array" << endl;
|
Chris@1346
|
241 QSKIP("Couldn't find filename in title mapping array");
|
Chris@1345
|
242 }
|
Chris@1345
|
243 }
|
Chris@1345
|
244 }
|
Chris@1359
|
245
|
Chris@1359
|
246 void readWriteAudio_data() {
|
Chris@1359
|
247 addAudioFiles();
|
Chris@1359
|
248 }
|
Chris@1359
|
249
|
Chris@1359
|
250 void readWriteAudio()
|
Chris@1359
|
251 {
|
Chris@1359
|
252 // For those files that have title metadata (i.e. all of them
|
Chris@1359
|
253 // except the WAVs), read the title metadata and write a wav
|
Chris@1359
|
254 // file (of arbitrary content) whose name matches that. Then
|
Chris@1359
|
255 // check that we can re-read it. This is intended to exercise
|
Chris@1359
|
256 // systems on which the original test filename is miscoded (as
|
Chris@1359
|
257 // can happen on Windows).
|
Chris@1359
|
258
|
Chris@1359
|
259 QFETCH(QString, audiofile);
|
Chris@1359
|
260
|
Chris@1359
|
261 QStringList fileAndExt = audiofile.split(".");
|
Chris@1359
|
262 QString file = fileAndExt[0];
|
Chris@1359
|
263 QString extension = fileAndExt[1];
|
Chris@1359
|
264
|
Chris@1359
|
265 if (extension == "wav") {
|
Chris@1359
|
266 return;
|
Chris@1359
|
267 }
|
Chris@1359
|
268
|
Chris@1359
|
269 AudioFileReaderFactory::Parameters params;
|
Chris@1359
|
270 AudioFileReader *reader =
|
Chris@1359
|
271 AudioFileReaderFactory::createReader
|
Chris@1359
|
272 (encodingDir + "/" + audiofile, params);
|
Chris@1592
|
273
|
Chris@1592
|
274 if (!reader) {
|
Chris@1698
|
275 if (isLegitimatelyUnsupported(extension)) {
|
Chris@1592
|
276 #if ( QT_VERSION >= 0x050000 )
|
Chris@1698
|
277 QSKIP("Unsupported file, skipping");
|
Chris@1592
|
278 #else
|
Chris@1698
|
279 QSKIP("Unsupported file, skipping", SkipSingle);
|
Chris@1592
|
280 #endif
|
Chris@1698
|
281 }
|
Chris@1592
|
282 }
|
Chris@1359
|
283
|
Chris@1698
|
284 QVERIFY(reader != nullptr);
|
Chris@1698
|
285
|
Chris@1359
|
286 QString title = reader->getTitle();
|
Chris@1359
|
287 QVERIFY(title != QString());
|
Chris@1359
|
288
|
Chris@1359
|
289 for (int useTemporary = 0; useTemporary <= 1; ++useTemporary) {
|
Chris@1359
|
290
|
Chris@1359
|
291 QString outfile = outDir + "/" + file + ".wav";
|
Chris@1359
|
292 WavFileWriter writer(outfile,
|
Chris@1359
|
293 reader->getSampleRate(),
|
Chris@1359
|
294 1,
|
Chris@1359
|
295 useTemporary ?
|
Chris@1359
|
296 WavFileWriter::WriteToTemporary :
|
Chris@1359
|
297 WavFileWriter::WriteToTarget);
|
Chris@1359
|
298
|
Chris@1359
|
299 QVERIFY(writer.isOK());
|
Chris@1359
|
300
|
Chris@1359
|
301 floatvec_t data { 0.0, 1.0, 0.0, -1.0, 0.0, 1.0, 0.0, -1.0 };
|
Chris@1359
|
302 const float *samples = data.data();
|
Chris@1359
|
303 bool ok = writer.writeSamples(&samples, 8);
|
Chris@1359
|
304 QVERIFY(ok);
|
Chris@1359
|
305
|
Chris@1359
|
306 ok = writer.close();
|
Chris@1359
|
307 QVERIFY(ok);
|
Chris@1359
|
308
|
Chris@1359
|
309 AudioFileReader *rereader =
|
Chris@1359
|
310 AudioFileReaderFactory::createReader(outfile, params);
|
Chris@1359
|
311 QVERIFY(rereader != nullptr);
|
Chris@1359
|
312
|
Chris@1359
|
313 floatvec_t readFrames = rereader->getInterleavedFrames(0, 8);
|
Chris@1359
|
314 QCOMPARE(readFrames, data);
|
Chris@1359
|
315
|
Chris@1359
|
316 delete rereader;
|
Chris@1359
|
317 }
|
Chris@1359
|
318
|
Chris@1359
|
319 delete reader;
|
Chris@1359
|
320 }
|
Chris@1345
|
321 };
|
Chris@1345
|
322
|
Chris@1345
|
323 #endif
|