1 //=============================================================================
2 //  Zerberus
3 //  Zample player
4 //
5 //  Copyright (C) 2013 Werner Schweer
6 //
7 //  This program is free software; you can redistribute it and/or modify
8 //  it under the terms of the GNU General Public License version 2
9 //  as published by the Free Software Foundation and appearing in
10 //  the file LICENCE.GPL
11 //=============================================================================
12 
13 #include <stdio.h>
14 
15 #include "zerberus.h"
16 #include "zerberusgui.h"
17 #include "voice.h"
18 #include "channel.h"
19 #include "instrument.h"
20 #include "zone.h"
21 
22 #include "midi/event.h"
23 #include "midi/midipatch.h"
24 
25 #include "mscore/preferences.h"
26 
27 bool Zerberus::initialized = false;
28 // instruments can be shared between several zerberus instances
29 std::list<ZInstrument*> Zerberus::globalInstruments;
30 
31 //---------------------------------------------------------
32 //   createZerberus
33 //---------------------------------------------------------
34 
createZerberus()35 Ms::Synthesizer* createZerberus()
36       {
37       return new Zerberus();
38       }
39 
40 //---------------------------------------------------------
41 //   Zerberus
42 //---------------------------------------------------------
43 
Zerberus()44 Zerberus::Zerberus()
45    : Synthesizer()
46       {
47       if (!initialized) {
48             initialized = true;
49             Voice::init();
50             }
51 
52       freeVoices.init(this);
53       for (int i = 0; i < MAX_CHANNELS; ++i)
54             _channel[i] = new Channel(this, i);
55       busy = true;      // no sf loaded yet
56       }
57 
58 //---------------------------------------------------------
59 //   ~Zerberus
60 //---------------------------------------------------------
61 
~Zerberus()62 Zerberus::~Zerberus()
63       {
64       busy = true;
65       while (!instruments.empty()) {
66             auto i  = instruments.front();
67             auto it = instruments.begin();
68             instruments.erase(it);
69 
70             i->setRefCount(i->refCount() - 1);
71             if (i->refCount() <= 0) {
72                   delete i;
73                   auto it1 = find(globalInstruments.begin(), globalInstruments.end(), i);
74                   if (it1 != globalInstruments.end())
75                         globalInstruments.erase(it1);
76                   }
77             }
78       for (Channel* c : _channel)
79             delete c;
80 
81       qDeleteAll(patches);
82       }
83 
84 //---------------------------------------------------------
85 //   programChange
86 //---------------------------------------------------------
87 
programChange(int channel,int program)88 void Zerberus::programChange(int channel, int program)
89       {
90       qDebug("Zerberus programChange %d %d", channel, program);
91       }
92 
93 //---------------------------------------------------------
94 //   trigger
95 //    gui
96 //---------------------------------------------------------
97 
trigger(Channel * channel,int key,int velo,Trigger trigger,int cc,int ccVal,double durSinceNoteOn)98 void Zerberus::trigger(Channel* channel, int key, int velo, Trigger trigger, int cc, int ccVal, double durSinceNoteOn)
99       {
100       ZInstrument* i = channel->instrument();
101       double random = (double) rand() / (double) RAND_MAX;
102       for (Zone* z : i->zones()) {
103             if (z->match(channel, key, velo, trigger, random, cc, ccVal)) {
104                   //
105                   // handle offBy voices
106                   //
107                   if (z->group) {
108                         for (Voice* v = activeVoices; v; v = v->next()) {
109                               if (v->offBy() == z->group) {
110                                     if (v->offMode() == OffMode::FAST)
111                                           v->stop(1);
112                                     else
113                                           v->stop();
114                                     }
115                               }
116                         }
117 
118                   if (freeVoices.empty()) {
119                         qDebug("Zerberus: out of voices...");
120                         return;
121                         }
122 
123                   Voice* voice = freeVoices.pop();
124                   Q_ASSERT(voice->isOff());
125                   voice->start(channel, key, velo, z, durSinceNoteOn);
126                   voice->setNext(activeVoices);
127                   activeVoices = voice;
128                   }
129             }
130       }
131 
132 //---------------------------------------------------------
133 //   processNoteOff
134 //---------------------------------------------------------
135 
processNoteOff(Channel * cp,int key)136 void Zerberus::processNoteOff(Channel* cp, int key)
137       {
138       for (Voice* v = activeVoices; v; v = v->next()) {
139             if ((v->channel() == cp)
140                && (v->key() == key)
141                && (v->loopMode() != LoopMode::ONE_SHOT)
142                ) {
143                   if (cp->sustain() < 0x40 && !v->isStopped()) {
144                         v->stop();
145                         double durSinceNoteOn = v->getSamplesSinceStart() / sampleRate();
146                         trigger(cp, key, v->velocity(), Trigger::RELEASE, -1, -1, durSinceNoteOn);
147                         }
148                   else {
149                         if (v->isPlaying())
150                               v->sustained();
151                         }
152                   }
153             }
154       }
155 
156 //---------------------------------------------------------
157 //   processNoteOn
158 //---------------------------------------------------------
159 
processNoteOn(Channel * cp,int key,int velo)160 void Zerberus::processNoteOn(Channel* cp, int key, int velo)
161       {
162       for (Voice* v = activeVoices; v; v = v->next()) {
163             if (v->channel() == cp && v->key() == key) {
164                   if (v->isSustained()) {
165 //if (v->isPlaying())
166 //printf("retrigger (stop) %p\n", v);
167                         v->stop(100);     // fast stop
168                         }
169                   }
170             }
171       trigger(cp, key, velo, Trigger::ATTACK, -1, -1, 0);
172       }
173 
174 //---------------------------------------------------------
175 //   process
176 //---------------------------------------------------------
177 
play(const Ms::PlayEvent & event)178 void Zerberus::play(const Ms::PlayEvent& event)
179       {
180       if (busy)
181             return;
182       static_assert(MAX_CHANNELS - 1 >= std::numeric_limits<decltype(event.channel())>::max(), "need to add a check for a channel number range");
183       //if (event.channel() >= MAX_CHANNELS)
184       //      return;
185       Channel* cp = _channel[event.channel()];
186       if (cp->instrument() == 0) {
187             // qDebug("Zerberus::play(): no instrument for channel %d", event.channel());
188             return;
189             }
190 
191       switch(event.type()) {
192             case Ms::ME_NOTEOFF:
193                   processNoteOff(cp, event.dataA());
194                   break;
195 
196             case Ms::ME_NOTEON: {
197                   int key = event.dataA();
198                   int vel = event.dataB();
199                   if (vel)
200                         processNoteOn(cp, key, vel);
201                   else
202                         processNoteOff(cp, key);
203                   }
204                   break;
205 
206             case Ms::ME_CONTROLLER:
207                   cp->controller(event.dataA(), event.dataB());
208                   trigger(cp, -1, -1, Trigger::CC, event.dataA(), event.dataB(), 0);
209                   break;
210 
211             default:
212                   qDebug("Zerberus: event type 0x%02x", event.type());
213                   break;
214             }
215       }
216 
217 //---------------------------------------------------------
218 //   process
219 //    realtime
220 //---------------------------------------------------------
221 
process(unsigned frames,float * p,float *,float *)222 void Zerberus::process(unsigned frames, float* p, float*, float*)
223       {
224       if (busy)
225             return;
226       Voice* v = activeVoices;
227       Voice* pv = 0;
228       while (v) {
229             v->process(frames, p);
230             if (v->isOff()) {
231                   if (pv)
232                         pv->setNext(v->next());
233                   else
234                         activeVoices = v->next();
235                   freeVoices.push(v);
236                   }
237             else
238                   pv = v;
239             v = v->next();
240             }
241       }
242 
243 //---------------------------------------------------------
244 //   name
245 //---------------------------------------------------------
246 
name() const247 const char* Zerberus::name() const
248       {
249       return "Zerberus";
250       }
251 
252 //---------------------------------------------------------
253 //   getPatchInfo
254 //---------------------------------------------------------
255 
updatePatchList()256 void Zerberus::updatePatchList()
257       {
258       qDeleteAll(patches);
259       patches.clear();
260       int idx = 0;
261       for (ZInstrument* i : instruments) {
262             Ms::MidiPatch* p = new Ms::MidiPatch { false, name(), 0, idx, 0, i->name() };
263             patches.append(p);
264             ++idx;
265             }
266       }
267 
268 //---------------------------------------------------------
269 //   allSoundsOff
270 //---------------------------------------------------------
271 
allSoundsOff(int channel)272 void Zerberus::allSoundsOff(int channel)
273       {
274       allNotesOff(channel);
275       }
276 
277 //---------------------------------------------------------
278 //   allNotesOff
279 //---------------------------------------------------------
280 
allNotesOff(int channel)281 void Zerberus::allNotesOff(int channel)
282       {
283       busy = true;
284       for (Voice* v = activeVoices; v; v = v->next()) {
285             if (channel == -1 || (v->channel()->idx() == channel))
286                   v->stop();
287             }
288       busy = false;
289       }
290 
291 //---------------------------------------------------------
292 //   loadSoundFonts
293 //---------------------------------------------------------
294 
loadSoundFonts(const QStringList & sl)295 bool Zerberus::loadSoundFonts(const QStringList& sl)
296       {
297       for (const QString& s : sl) {
298             if (!loadInstrument(s))
299                   return false;
300             }
301       updatePatchList();
302       return true;
303       }
304 
305 //---------------------------------------------------------
306 //   removeSoundFonts
307 //---------------------------------------------------------
308 
removeSoundFonts(const QStringList & fileNames)309 bool Zerberus::removeSoundFonts(const QStringList& fileNames)
310       {
311       for (auto fileName : fileNames) {
312             if (!removeSoundFont(QFileInfo(fileName).absoluteFilePath()))
313                   return false;
314             }
315       updatePatchList();
316       return true;
317       }
318 
319 //---------------------------------------------------------
320 //   soundFonts
321 //---------------------------------------------------------
322 
soundFonts() const323 QStringList Zerberus::soundFonts() const
324       {
325       QStringList sl;
326       for (ZInstrument* i : instruments)
327             sl.append(i->path());
328       return sl;
329       }
330 
331 //---------------------------------------------------------
332 //   soundFontsInfo
333 //---------------------------------------------------------
334 
soundFontsInfo() const335 std::vector<Ms::SoundFontInfo> Zerberus::soundFontsInfo() const
336       {
337       std::vector<Ms::SoundFontInfo> sl;
338       sl.reserve(instruments.size());
339       for (ZInstrument* i : instruments)
340             sl.emplace_back(i->path());
341       return sl;
342       }
343 
344 //---------------------------------------------------------
345 //   addSoundFont
346 //---------------------------------------------------------
347 
addSoundFont(const QString & s)348 bool Zerberus::addSoundFont(const QString& s)
349       {
350       QMutexLocker locker(&mutex);
351       bool res = loadInstrument(s);
352       updatePatchList();
353       return res;
354       }
355 
356 //---------------------------------------------------------
357 //   removeSoundFont
358 //---------------------------------------------------------
359 
removeSoundFont(const QString & s)360 bool Zerberus::removeSoundFont(const QString& s)
361       {
362       for (ZInstrument* i : instruments) {
363             if (i->path() == s) {
364                   auto it = find(instruments.begin(), instruments.end(), i);
365                   if (it == instruments.end())
366                         return false;
367                   instruments.erase(it);
368                   for (int k = 0; k < MAX_CHANNELS; ++k) {
369                         if (_channel[k]->instrument() == i)
370                               _channel[k]->setInstrument(0);
371                         }
372                   if (!instruments.empty()) {
373                         for (int ii = 0; ii < MAX_CHANNELS; ++ii) {
374                               if (_channel[ii]->instrument() == 0)
375                                     _channel[ii]->setInstrument(instruments.front());
376                               }
377                         }
378                   i->setRefCount(i->refCount() - 1);
379                   if (i->refCount() <= 0) {
380                         auto it1 = find(globalInstruments.begin(), globalInstruments.end(), i);
381                         if (it1 == globalInstruments.end())
382                               return false;
383                         globalInstruments.erase(it1);
384                         delete i;
385                         }
386 
387                   updatePatchList();
388                   return true;
389                   }
390             }
391 
392       updatePatchList();
393       return false;
394       }
395 
396 //---------------------------------------------------------
397 //   state
398 //---------------------------------------------------------
399 
state() const400 Ms::SynthesizerGroup Zerberus::state() const
401       {
402       Ms::SynthesizerGroup g;
403       g.setName(name());
404 
405       QStringList sfl = soundFonts();
406       foreach(QString sf, sfl)
407             g.push_back(Ms::IdValue(0, sf));
408       return g;
409       }
410 
411 //---------------------------------------------------------
412 //   setState
413 //---------------------------------------------------------
414 
setState(const Ms::SynthesizerGroup & sp)415 bool Zerberus::setState(const Ms::SynthesizerGroup& sp)
416       {
417       QStringList sfs;
418       for (const Ms::IdValue& v : sp)
419             sfs.append(v.data);
420       return loadSoundFonts(sfs);
421       }
422 
423 //---------------------------------------------------------
424 //   instrument
425 //---------------------------------------------------------
426 
instrument(int n) const427 ZInstrument* Zerberus::instrument(int n) const
428       {
429       int idx = 0;
430       for (auto i = instruments.begin(); i != instruments.end(); ++i) {
431             if (idx == n)
432                   return *i;
433             ++idx;
434             }
435       return 0;
436       }
437 
438 //---------------------------------------------------------
439 //   loadInstrument
440 //    return true on success
441 //---------------------------------------------------------
442 
loadInstrument(const QString & s)443 bool Zerberus::loadInstrument(const QString& s)
444       {
445       if (s.isEmpty())
446             return false;
447       QFileInfo fis(s);
448       QString fileName = fis.fileName();
449       for (ZInstrument* instr : instruments) {
450             if (QFileInfo(instr->path()).fileName() == fileName) {   // already loaded?
451                   return true;
452                   }
453             }
454       for (ZInstrument* instr : globalInstruments) {
455             if (QFileInfo(instr->path()).fileName() == fileName) {
456                   instruments.push_back(instr);
457                   instr->setRefCount(instr->refCount() + 1);
458                   if (instruments.size() == 1) {
459                         for (int i = 0; i < MAX_CHANNELS; ++i)
460                               _channel[i]->setInstrument(instr);
461                         }
462                   busy = false;
463                   return true;
464                   }
465             }
466 
467       QFileInfoList l = Zerberus::sfzFiles();
468       QString path;
469       foreach (const QFileInfo& fi, l) {
470             if (fi.fileName() == fileName) {
471                   path = fi.absoluteFilePath();
472                   break;
473                   }
474             }
475       busy = true;
476       ZInstrument* instr = new ZInstrument(this);
477 
478       try {
479             if (instr->load(path)) {
480                   globalInstruments.push_back(instr);
481                   instruments.push_back(instr);
482                   instr->setRefCount(1);
483                   //
484                   // set default instrument for all channels:
485                   //
486                   if (instruments.size() == 1) {
487                         for (int i = 0; i < MAX_CHANNELS; ++i)
488                               _channel[i]->setInstrument(instr);
489                         }
490                   busy = false;
491                   return true;
492                   }
493             }
494       catch (std::bad_alloc& a) {
495             qDebug("Unable to allocate memory when loading Zerberus soundfont %s", qPrintable(s));
496 
497             // Prevent "Unreferenced local variable" warning for a
498             Q_UNUSED(a);
499             }
500       catch (...) {
501             }
502       qDebug("Zerberus::loadInstrument failed");
503       busy = false;
504       delete instr;
505       return false;
506       }
507 
508