1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
|
//=============================================================================
// MuseScore
// Music Composition & Notation
// $Id:$
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENCE.GPL
//=============================================================================
#include <QtTest/QtTest>
#include "mtest/testutils.h"
#include "zerberus/instrument.h"
#include "zerberus/zerberus.h"
#include "zerberus/zone.h"
#include "mscore/preferences.h"
#include "synthesizer/event.h"
#include <sndfile.h>
using namespace Ms;
//---------------------------------------------------------
// TestSfzEnvelopes
//---------------------------------------------------------
class TestSfzLoop : public QObject, public MTest
{
Q_OBJECT
float samplerate = 44100;
Zerberus* synth;
private slots:
void initTestCase();
void testEnvelopesParsing();
void testLoopAudio();
public:
~TestSfzLoop();
};
//---------------------------------------------------------
// initTestCase
//---------------------------------------------------------
void TestSfzLoop::initTestCase()
{
initMTest();
synth = new Zerberus();
synth->init(samplerate);
Ms::preferences.mySoundfontsPath += ";" + root;
synth->loadInstrument("loopTest.sfz");
}
//---------------------------------------------------------
// testglobal
//---------------------------------------------------------
void TestSfzLoop::testEnvelopesParsing()
{
QCOMPARE(synth->instrument(0)->zones().size(), (unsigned long) 4);
std::list<Zone *>::iterator curZone = synth->instrument(0)->zones().begin();
QCOMPARE((*curZone)->keyLo, (char) 20);
QCOMPARE((*curZone)->keyHi, (char) 20);
QCOMPARE((*curZone)->keyBase, (char) 20);
QCOMPARE((*curZone)->loopMode, LoopMode::NO_LOOP);
curZone++;
QCOMPARE((*curZone)->keyLo, (char) 21);
QCOMPARE((*curZone)->keyHi, (char) 21);
QCOMPARE((*curZone)->keyBase, (char) 21);
QCOMPARE((*curZone)->loopMode, LoopMode::ONE_SHOT);
curZone++;
QCOMPARE((*curZone)->keyLo, (char) 22);
QCOMPARE((*curZone)->keyHi, (char) 22);
QCOMPARE((*curZone)->keyBase, (char) 22);
QCOMPARE((*curZone)->loopMode, LoopMode::CONTINUOUS);
curZone++;
QCOMPARE((*curZone)->keyLo, (char) 23);
QCOMPARE((*curZone)->keyHi, (char) 23);
QCOMPARE((*curZone)->keyBase, (char) 23);
QCOMPARE((*curZone)->loopMode, LoopMode::SUSTAIN);
curZone++;
}
void TestSfzLoop::testLoopAudio()
{
synth->play(Ms::PlayEvent(ME_PROGRAM, 0, 0, 0));
float data[2 * 110 * 3]; // 2 Channel 110 Sample duration - enough space for at least one loop
memset(data, 0, sizeof(data));
// test no_loop
synth->play(Ms::PlayEvent(ME_NOTEON, 0, 20, 127)); // play note - no_loop mode
synth->process(50, data, nullptr, nullptr); // process - stop in middle of ones
synth->play(Ms::PlayEvent(ME_NOTEON, 0, 20, 0)); // send note off and check whether voice is actually turned off
synth->process(50 , data + (50*2), nullptr, nullptr); // process until end of sustain stage
QCOMPARE(data[0], 0.0f);
QCOMPARE(data[1], 0.0f);
QCOMPARE(data[10 * 2] != 0.0f, true);
QCOMPARE(data[10 * 2 + 1] != 0.0f, true);
QCOMPARE(data[50 * 2], 0.0f);
QCOMPARE(data[50 * 2 + 1], 0.0f);
memset(data, 0, sizeof(data)); // clear data because each voice gets added to it!
// test one_shot
synth->play(Ms::PlayEvent(ME_NOTEON, 0, 21, 127)); // play note - one_shot
synth->process(50, data, nullptr, nullptr); // process - stop in middle of ones
synth->play(Ms::PlayEvent(ME_NOTEON, 0, 21, 0)); // send note off - sample should still play to end!
synth->process(60 , data + (50*2), nullptr, nullptr); // process until end of sample
QCOMPARE(data[0], 0.0f);
QCOMPARE(data[1], 0.0f);
QVERIFY(data[10 * 2] != 0.0f);
QVERIFY(data[10 * 2 + 1] != 0.0f);
QVERIFY(data[50 * 2] != 0.0f);
QVERIFY(data[50 * 2 + 1] != 0.0f);
QVERIFY(data[60 * 2] > pow(10, -30.0f/20.0f)); // it is not zero due to the filter - assume at least -30dB right after stop
QVERIFY(data[60 * 2 + 1] > pow(10, -30.0f/20.0f));
QVERIFY(data[70 * 2] < pow(10, -85.0f/20.0f)); // and -85dB 10 samples later
QVERIFY(data[70 * 2 + 1] < pow(10, -85.0f/20.0f));
memset(data, 0, sizeof(data)); // clear data because each voice gets added to it!
// test loop_continuous
synth->play(Ms::PlayEvent(ME_NOTEON, 0, 22, 127)); // play note
synth->process(109, data, nullptr, nullptr); // process - it should loop (loop from 10 to 59)
synth->play(Ms::PlayEvent(ME_NOTEON, 0, 22, 0)); // send note off - sample should not switch to the zero part
synth->process(60 , data + (109*2), nullptr, nullptr); // process until end of release
QCOMPARE(data[0], 0.0f);
QCOMPARE(data[1], 0.0f);
QVERIFY(data[10 * 2] != 0.0f);
QVERIFY(data[10 * 2 + 1] != 0.0f);
QVERIFY(data[50 * 2] != 0.0f);
QVERIFY(data[50 * 2 + 1] != 0.0f);
QVERIFY(data[60 * 2] > pow(10, -15.0f/20.0f)); // it should not stop make sure it is loud enough!
QVERIFY(data[60 * 2 + 1] > pow(10, -15.0f/20.0f));
QVERIFY(data[70 * 2] > pow(10, -15.0f/20.0f)); // it should not stop make sure it is loud enough!
QVERIFY(data[70 * 2 + 1] > pow(10, -15.0f/20.0f));
QVERIFY(data[110 * 2] > pow(10, -20.0f/20.0f)); // it should not stop make sure it is loud enough!
QVERIFY(data[110 * 2 + 1] > pow(10, -20.0f/20.0f));
QVERIFY(data[120 * 2] > pow(10, -35.0f/20.0f)); // it should not stop make sure it is loud enough!
QVERIFY(data[120 * 2 + 1] > pow(10, -35.0f/20.0f));
memset(data, 0, sizeof(data)); // clear data because each voice gets added to it!
// test loop_sustain
synth->play(Ms::PlayEvent(ME_NOTEON, 0, 23, 127)); // play note
synth->process(109, data, nullptr, nullptr); // process - it should loop (loop from 10 to 59)
synth->play(Ms::PlayEvent(ME_NOTEON, 0, 23, 0)); // send note off - sample should not switch to the zero part
synth->process(60 , data + (109*2), nullptr, nullptr); // process until end of release
QCOMPARE(data[0], 0.0f);
QCOMPARE(data[1], 0.0f);
QVERIFY(data[10 * 2] != 0.0f);
QVERIFY(data[10 * 2 + 1] != 0.0f);
QVERIFY(data[50 * 2] != 0.0f);
QVERIFY(data[50 * 2 + 1] != 0.0f);
QVERIFY(data[60 * 2] > pow(10, -15.0f/20.0f)); // it should not stop make sure it is loud enough!
QVERIFY(data[60 * 2 + 1] > pow(10, -15.0f/20.0f));
QVERIFY(data[70 * 2] > pow(10, -15.0f/20.0f)); // it should not stop make sure it is loud enough!
QVERIFY(data[70 * 2 + 1] > pow(10, -15.0f/20.0f));
QVERIFY(data[110 * 2] < pow(10, -20.0f/20.0f)); // it should play zeros after leaving sustain -> drastic volume reduce
QVERIFY(data[110 * 2 + 1] < pow(10, -20.0f/20.0f));
QVERIFY(data[120 * 2] < pow(10, -85.0f/20.0f));
QVERIFY(data[120 * 2 + 1] < pow(10, -85.0f/20.0f));
}
TestSfzLoop::~TestSfzLoop()
{
delete synth;
}
QTEST_MAIN(TestSfzLoop)
#include "tst_sfzloop.moc"
|