1 /*
2   ZynAddSubFX - a software synthesizer
3 
4   NotePool.cpp - Pool of Synthesizer Engines And Note Instances
5   Copyright (C) 2016 Mark McCurry
6 
7   This program is free software; you can redistribute it and/or
8   modify it under the terms of the GNU General Public License
9   as published by the Free Software Foundation; either version 2
10   of the License, or (at your option) any later version.
11 */
12 #include "NotePool.h"
13 #include "../Misc/Allocator.h"
14 #include "../Synth/Portamento.h"
15 #include "../Synth/SynthNote.h"
16 #include <cstring>
17 #include <cassert>
18 #include <iostream>
19 
20 #define SUSTAIN_BIT 0x08
21 #define NOTE_MASK   0x07
22 
23 namespace zyn {
24 
25 enum NoteStatus {
26     KEY_OFF                    = 0x00,
27     KEY_PLAYING                = 0x01,
28     KEY_RELEASED_AND_SUSTAINED = 0x02,
29     KEY_RELEASED               = 0x03,
30     KEY_ENTOMBED               = 0x04,
31     KEY_LATCHED                = 0x05
32 };
33 
getStatus(int status)34 const char *getStatus(int status)
35 {
36     switch((enum NoteStatus)(status&NOTE_MASK))
37     {
38         case KEY_OFF:                     return "OFF ";
39         case KEY_PLAYING:                 return "PLAY";
40         case KEY_RELEASED_AND_SUSTAINED:  return "SUST";
41         case KEY_RELEASED:                return "RELA";
42         case KEY_ENTOMBED:                return "TOMB";
43         case KEY_LATCHED:                 return "LTCH";
44         default:                          return "INVD";
45     }
46 }
47 
NotePool(void)48 NotePool::NotePool(void)
49     :needs_cleaning(0)
50 {
51     memset(ndesc, 0, sizeof(ndesc));
52     memset(sdesc, 0, sizeof(sdesc));
53 }
54 
playing(void) const55 bool NotePool::NoteDescriptor::playing(void) const
56 {
57     return (status&NOTE_MASK) == KEY_PLAYING;
58 }
59 
latched(void) const60 bool NotePool::NoteDescriptor::latched(void) const
61 {
62     return (status&NOTE_MASK) == KEY_LATCHED;
63 }
64 
sustained(void) const65 bool NotePool::NoteDescriptor::sustained(void) const
66 {
67     return (status&NOTE_MASK) == KEY_RELEASED_AND_SUSTAINED;
68 }
69 
released(void) const70 bool NotePool::NoteDescriptor::released(void) const
71 {
72     return (status&NOTE_MASK) == KEY_RELEASED;
73 }
74 
entombed(void) const75 bool NotePool::NoteDescriptor::entombed(void) const
76 {
77     return (status&NOTE_MASK) == KEY_ENTOMBED;
78 }
79 
80 // Notes that are no longer playing, for whatever reason.
dying(void) const81 bool NotePool::NoteDescriptor::dying(void) const
82 {
83     return (status&NOTE_MASK) == KEY_ENTOMBED ||
84            (status&NOTE_MASK) == KEY_RELEASED;
85 }
86 
off(void) const87 bool NotePool::NoteDescriptor::off(void) const
88 {
89     return (status&NOTE_MASK) == KEY_OFF;
90 }
91 
setStatus(uint8_t s)92 void NotePool::NoteDescriptor::setStatus(uint8_t s)
93 {
94     status &= ~NOTE_MASK;
95     status |= (NOTE_MASK&s);
96 }
97 
doSustain(void)98 void NotePool::NoteDescriptor::doSustain(void)
99 {
100     setStatus(KEY_RELEASED_AND_SUSTAINED);
101 }
102 
canSustain(void) const103 bool NotePool::NoteDescriptor::canSustain(void) const
104 {
105     return !(status & SUSTAIN_BIT);
106 }
107 
makeUnsustainable(void)108 void NotePool::NoteDescriptor::makeUnsustainable(void)
109 {
110     status |= SUSTAIN_BIT;
111 }
112 
activeNotes(NoteDescriptor & n)113 NotePool::activeNotesIter NotePool::activeNotes(NoteDescriptor &n)
114 {
115     const int off_d1 = &n-ndesc;
116     int off_d2 = 0;
117     assert(off_d1 <= POLYPHONY);
118     for(int i=0; i<off_d1; ++i)
119         off_d2 += ndesc[i].size;
120     return NotePool::activeNotesIter{sdesc+off_d2,sdesc+off_d2+n.size};
121 }
122 
operator ==(NoteDescriptor nd)123 bool NotePool::NoteDescriptor::operator==(NoteDescriptor nd)
124 {
125     return age == nd.age && note == nd.note && sendto == nd.sendto && size == nd.size && status == nd.status;
126 }
127 
128 //return either the first unused descriptor or the last valid descriptor which
129 //matches note/sendto
getMergeableDescriptor(note_t note,uint8_t sendto,bool legato,NotePool::NoteDescriptor * ndesc)130 static int getMergeableDescriptor(note_t note, uint8_t sendto, bool legato,
131         NotePool::NoteDescriptor *ndesc)
132 {
133     int desc_id;
134 
135     for(desc_id = 0; desc_id != POLYPHONY; ++desc_id) {
136         if(ndesc[desc_id].off())
137             break;
138     }
139 
140     if(desc_id != 0) {
141         auto &nd = ndesc[desc_id-1];
142         if(nd.age == 0 && nd.note == note && nd.sendto == sendto
143                 && nd.playing() && nd.legatoMirror == legato && nd.canSustain())
144             return desc_id-1;
145     }
146 
147     //Out of free descriptors
148     if(desc_id == POLYPHONY || !ndesc[desc_id].off()) {
149         return -1;
150     }
151 
152     return desc_id;
153 }
154 
activeDesc(void)155 NotePool::activeDescIter NotePool::activeDesc(void)
156 {
157     cleanup();
158     return activeDescIter{*this};
159 }
160 
activeDesc(void) const161 NotePool::constActiveDescIter NotePool::activeDesc(void) const
162 {
163     const_cast<NotePool*>(this)->cleanup();
164     return constActiveDescIter{*this};
165 }
166 
usedNoteDesc(void) const167 int NotePool::usedNoteDesc(void) const
168 {
169     if(needs_cleaning)
170         const_cast<NotePool*>(this)->cleanup();
171 
172     int cnt = 0;
173     for(int i=0; i<POLYPHONY; ++i)
174         cnt += (ndesc[i].size != 0);
175     return cnt;
176 }
177 
usedSynthDesc(void) const178 int NotePool::usedSynthDesc(void) const
179 {
180     if(needs_cleaning)
181         const_cast<NotePool*>(this)->cleanup();
182 
183     int cnt = 0;
184     for(int i=0; i<POLYPHONY*EXPECTED_USAGE; ++i)
185         cnt += (bool)sdesc[i].note;
186     return cnt;
187 }
188 
insertNote(note_t note,uint8_t sendto,SynthDescriptor desc,PortamentoRealtime * portamento_realtime,bool legato)189 void NotePool::insertNote(note_t note, uint8_t sendto, SynthDescriptor desc, PortamentoRealtime *portamento_realtime, bool legato)
190 {
191     //Get first free note descriptor
192     int desc_id = getMergeableDescriptor(note, sendto, legato, ndesc);
193     int sdesc_id = 0;
194     if(desc_id < 0)
195         goto error;
196 
197     //Get first free synth descriptor
198     while(1) {
199         if (sdesc_id == POLYPHONY*EXPECTED_USAGE)
200                 goto error;
201         if (sdesc[sdesc_id].note == 0)
202                 break;
203         sdesc_id++;
204     }
205 
206     ndesc[desc_id].note                = note;
207     ndesc[desc_id].sendto              = sendto;
208     ndesc[desc_id].size               += 1;
209     ndesc[desc_id].status              = KEY_PLAYING;
210     ndesc[desc_id].legatoMirror        = legato;
211     ndesc[desc_id].portamentoRealtime  = portamento_realtime;
212 
213     sdesc[sdesc_id] = desc;
214     return;
215 error:
216     //Avoid leaking note
217     desc.note->memory.dealloc(desc.note);
218     //Let caller handle failure
219     throw std::bad_alloc();
220 };
221 
upgradeToLegato(void)222 void NotePool::upgradeToLegato(void)
223 {
224     for(auto &d:activeDesc())
225         if(d.playing())
226             for(auto &s:activeNotes(d))
227                 insertLegatoNote(d, s);
228 }
229 
insertLegatoNote(NoteDescriptor desc,SynthDescriptor sdesc)230 void NotePool::insertLegatoNote(NoteDescriptor desc, SynthDescriptor sdesc)
231 {
232     assert(sdesc.note);
233     try {
234         sdesc.note = sdesc.note->cloneLegato();
235         // No portamentoRealtime for the legatoMirror descriptor
236         insertNote(desc.note, desc.sendto, sdesc, NULL, true);
237     } catch (std::bad_alloc &ba) {
238         std::cerr << "failed to insert legato note: " << ba.what() << std::endl;
239     }
240 };
241 
242 //There should only be one pair of notes which are still playing.
243 //Note however that there can be releasing legato notes already in the
244 //list when we get called, so need to handle that.
applyLegato(note_t note,const LegatoParams & par,PortamentoRealtime * portamento_realtime)245 void NotePool::applyLegato(note_t note, const LegatoParams &par, PortamentoRealtime *portamento_realtime)
246 {
247     for(auto &desc:activeDesc()) {
248         //Currently, there can actually be more than one legato pair, while a
249         //previous legato pair is releasing and a new one is started, and we
250         //don't want to change anything about notes which are releasing.
251         if (desc.dying())
252             continue;
253         desc.note = note;
254         // Only set portamentoRealtime for the primary of the two note
255         // descriptors in legato mode, or we'll get two note descriptors
256         // with the same realtime pointer, causing double updateportamento,
257         // and deallocation crashes.
258         if (!desc.legatoMirror) {
259             //If realtime is already set, we mustn't set it to NULL or we'll
260             //leak the old portamento.
261             if (portamento_realtime)
262                 desc.portamentoRealtime = portamento_realtime;
263         }
264         for(auto &synth:activeNotes(desc))
265             try {
266                 synth.note->legatonote(par);
267             } catch (std::bad_alloc& ba) {
268                 std::cerr << "failed to create legato note: " << ba.what() << std::endl;
269             }
270     }
271 }
272 
makeUnsustainable(note_t note)273 void NotePool::makeUnsustainable(note_t note)
274 {
275     for(auto &desc:activeDesc()) {
276         if(desc.note == note) {
277             desc.makeUnsustainable();
278             if(desc.sustained())
279                 release(desc);
280         }
281     }
282 }
283 
full(void) const284 bool NotePool::full(void) const
285 {
286     for(int i=0; i<POLYPHONY; ++i)
287         if(ndesc[i].off())
288             return false;
289     return true;
290 }
291 
synthFull(int sdesc_count) const292 bool NotePool::synthFull(int sdesc_count) const
293 {
294     int actually_free=sizeof(sdesc)/sizeof(sdesc[0]);
295     for(const auto &desc:activeDesc()) {
296         actually_free -= desc.size;
297     }
298     return actually_free < sdesc_count;
299 }
300 
301 //Note that isn't KEY_PLAYING or KEY_RELEASED_AND_SUSTAINED
existsRunningNote(void) const302 bool NotePool::existsRunningNote(void) const
303 {
304     //printf("running note # =%d\n", getRunningNotes());
305     return getRunningNotes();
306 }
307 
getRunningNotes(void) const308 int NotePool::getRunningNotes(void) const
309 {
310     bool running[256] = {};
311     int running_count = 0;
312 
313     for(auto &desc:activeDesc()) {
314         if(desc.playing() == false && desc.sustained() == false && desc.latched() == false)
315             continue;
316         if(running[desc.note] != false)
317             continue;
318         running[desc.note] = true;
319         running_count++;
320     }
321     return running_count;
322 }
323 
enforceKeyLimit(int limit)324 void NotePool::enforceKeyLimit(int limit)
325 {
326     int notes_to_kill = getRunningNotes() - limit;
327     if(notes_to_kill <= 0)
328         return;
329 
330     NoteDescriptor *to_kill = NULL;
331     unsigned oldest = 0;
332     for(auto &nd : activeDesc()) {
333         if(to_kill == NULL) {
334             //There must be something to kill
335             oldest  = nd.age;
336             to_kill = &nd;
337         } else if(to_kill->dying() && nd.playing()) {
338             //Prefer to kill off a running note
339             oldest = nd.age;
340             to_kill = &nd;
341         } else if(nd.age > oldest && !(to_kill->playing() && nd.dying())) {
342             //Get an older note when it doesn't move from running to
343             //released (or entombed)
344             oldest = nd.age;
345             to_kill = &nd;
346         }
347     }
348 
349     if(to_kill) {
350         auto &tk = *to_kill;
351         if(tk.dying() || tk.sustained())
352             kill(*to_kill);
353         else
354             entomb(*to_kill);
355     }
356 }
357 
getRunningVoices(void) const358 int NotePool::getRunningVoices(void) const
359 {
360     int running_count = 0;
361 
362     for(auto &desc:activeDesc()) {
363         // We don't count entombed voices as they will soon be dropped
364         if (desc.entombed())
365             continue;
366         running_count++;
367     }
368 
369     return running_count;
370 }
371 
372 // Silence one voice, trying to the select the one that will be the least
373 // intrusive, preferably preferred_note if possible..
limitVoice(int preferred_note)374 void NotePool::limitVoice(int preferred_note)
375 {
376     NoteDescriptor *oldest_released = NULL;
377     NoteDescriptor *oldest_released_samenote = NULL;
378     NoteDescriptor *oldest_sustained = NULL;
379     NoteDescriptor *oldest_sustained_samenote = NULL;
380     NoteDescriptor *oldest_latched = NULL;
381     NoteDescriptor *oldest_latched_samenote = NULL;
382     NoteDescriptor *oldest_playing = NULL;
383     NoteDescriptor *oldest_playing_samenote = NULL;
384 
385     for(auto &nd : activeDesc()) {
386         // printf("Scanning %d (%s (%d), age %u)\n", nd.note, getStatus(nd.status), nd.status, nd.age);
387         if (nd.released()) {
388             if (!oldest_released || nd.age > oldest_released->age)
389                 oldest_released = &nd;
390             if (nd.note == preferred_note &&
391                 (!oldest_released_samenote || oldest_released_samenote->age))
392                 oldest_released_samenote = &nd;
393         } else if (nd.sustained()) {
394             if (!oldest_sustained || nd.age > oldest_sustained->age)
395                 oldest_sustained = &nd;
396             if (nd.note == preferred_note &&
397                 (!oldest_sustained_samenote || oldest_sustained_samenote->age))
398                 oldest_sustained_samenote = &nd;
399         } else if (nd.latched()) {
400             if (!oldest_latched || nd.age > oldest_latched->age)
401                 oldest_latched = &nd;
402             if (nd.note == preferred_note &&
403                 (!oldest_latched_samenote || oldest_latched_samenote->age))
404                 oldest_latched_samenote = &nd;
405         } else if (nd.playing()) {
406             if (!oldest_playing || nd.age > oldest_playing->age)
407                 oldest_playing = &nd;
408             if (nd.note == preferred_note &&
409                 (!oldest_playing_samenote || oldest_playing_samenote->age))
410                 oldest_playing_samenote = &nd;
411         }
412     }
413 
414     // Prioritize which note to kill: if a released note exists, take that,
415     // otherwise sustained, latched or playing, in that order.
416     // Within each category, favour a voice that is already playing the
417     // same note, which minimizes stealing notes that are still playing
418     // something significant, especially when there are a lot of repeated
419     // notes being played.
420     // If we don't have anything to kill, there's a logical error somewhere,
421     // but we can't do anything about it here so just silently return.
422 
423     NoteDescriptor *to_kill = NULL;
424 
425     if (oldest_released_samenote)
426         to_kill = oldest_released_samenote;
427     else if (oldest_released)
428         to_kill = oldest_released;
429     else if (oldest_sustained_samenote)
430         to_kill = oldest_sustained_samenote;
431     else if (oldest_sustained)
432         to_kill = oldest_sustained;
433     else if (oldest_latched_samenote)
434         to_kill = oldest_latched_samenote;
435     else if (oldest_latched)
436         to_kill = oldest_latched;
437     else if (oldest_playing_samenote)
438         to_kill = oldest_playing_samenote;
439     else if (oldest_playing)
440         to_kill = oldest_playing;
441 
442     if (to_kill) {
443         // printf("Will kill %d (age %d)\n", to_kill->note, to_kill->age);
444         entomb(*to_kill);
445     }
446 }
447 
enforceVoiceLimit(int limit,int preferred_note)448 void NotePool::enforceVoiceLimit(int limit, int preferred_note)
449 {
450     int notes_to_kill = getRunningVoices() - limit;
451 
452     while (notes_to_kill-- > 0)
453         limitVoice(preferred_note);
454 }
455 
456 
releasePlayingNotes(void)457 void NotePool::releasePlayingNotes(void)
458 {
459     for(auto &d:activeDesc()) {
460         if(d.playing() || d.sustained() || d.latched()) {
461             d.setStatus(KEY_RELEASED);
462             for(auto s:activeNotes(d))
463                 s.note->releasekey();
464         }
465     }
466 }
467 
release(NoteDescriptor & d)468 void NotePool::release(NoteDescriptor &d)
469 {
470     d.setStatus(KEY_RELEASED);
471     for(auto s:activeNotes(d))
472         s.note->releasekey();
473 }
474 
latch(NoteDescriptor & d)475 void NotePool::latch(NoteDescriptor &d)
476 {
477     d.setStatus(KEY_LATCHED);
478 }
479 
releaseLatched()480 void NotePool::releaseLatched()
481 {
482     for(auto &desc:activeDesc())
483         if(desc.latched())
484             for(auto s:activeNotes(desc))
485                 s.note->releasekey();
486 }
487 
killAllNotes(void)488 void NotePool::killAllNotes(void)
489 {
490     for(auto &d:activeDesc())
491         kill(d);
492 }
493 
killNote(note_t note)494 void NotePool::killNote(note_t note)
495 {
496     for(auto &d:activeDesc()) {
497         if(d.note == note)
498             kill(d);
499     }
500 }
501 
kill(NoteDescriptor & d)502 void NotePool::kill(NoteDescriptor &d)
503 {
504     d.setStatus(KEY_OFF);
505     for(auto &s:activeNotes(d))
506         kill(s);
507     if (d.portamentoRealtime)
508         d.portamentoRealtime->memory.dealloc(d.portamentoRealtime);
509 }
510 
kill(SynthDescriptor & s)511 void NotePool::kill(SynthDescriptor &s)
512 {
513     //printf("Kill synth...\n");
514     s.note->memory.dealloc(s.note);
515     needs_cleaning = true;
516 }
517 
entomb(NoteDescriptor & d)518 void NotePool::entomb(NoteDescriptor &d)
519 {
520     d.setStatus(KEY_ENTOMBED);
521     for(auto &s:activeNotes(d))
522         s.note->entomb();
523 }
524 
cleanup(void)525 void NotePool::cleanup(void)
526 {
527     if(!needs_cleaning)
528         return;
529     needs_cleaning = false;
530     int new_length[POLYPHONY] = {};
531     int cur_length[POLYPHONY] = {};
532     //printf("Cleanup Start\n");
533     //dump();
534 
535     //Identify the current length of all segments
536     //and the lengths discarding invalid entries
537 
538     int last_valid_desc = 0;
539     for(int i=0; i<POLYPHONY; ++i)
540         if(!ndesc[i].off())
541             last_valid_desc = i;
542 
543     //Find the real numbers of allocated notes
544     {
545         int cum_old = 0;
546 
547         for(int i=0; i<=last_valid_desc; ++i) {
548             cur_length[i] = ndesc[i].size;
549             for(int j=0; j<ndesc[i].size; ++j)
550                 new_length[i] += (bool)sdesc[cum_old++].note;
551         }
552     }
553 
554 
555     //Move the note descriptors
556     {
557         int cum_new = 0;
558         for(int i=0; i<=last_valid_desc; ++i) {
559             ndesc[i].size = new_length[i];
560             if(new_length[i] != 0)
561                 ndesc[cum_new++] = ndesc[i];
562             else {
563                 ndesc[i].setStatus(KEY_OFF);
564                 if (ndesc[i].portamentoRealtime)
565                     ndesc[i].portamentoRealtime->memory.dealloc(ndesc[i].portamentoRealtime);
566             }
567         }
568         memset(ndesc+cum_new, 0, sizeof(*ndesc)*(POLYPHONY-cum_new));
569     }
570 
571     //Move the synth descriptors
572     {
573         int total_notes=0;
574         for(int i=0; i<=last_valid_desc; ++i)
575             total_notes+=cur_length[i];
576 
577         int cum_new = 0;
578         for(int i=0; i<total_notes; ++i)
579             if(sdesc[i].note)
580                 sdesc[cum_new++] = sdesc[i];
581         memset(sdesc+cum_new, 0, sizeof(*sdesc)*(POLYPHONY*EXPECTED_USAGE-cum_new));
582     }
583     //printf("Cleanup Done\n");
584     //dump();
585 }
586 
dump(void)587 void NotePool::dump(void)
588 {
589     printf("NotePool::dump<\n");
590     const char *format =
591         "    Note %d:%d age(%d) note(%d) sendto(%d) status(%s) legato(%d) type(%d) kit(%d) ptr(%p)\n";
592     int note_id=0;
593     int descriptor_id=0;
594     for(auto &d:activeDesc()) {
595         descriptor_id += 1;
596         for(auto &s:activeNotes(d)) {
597             note_id += 1;
598             printf(format,
599                     note_id, descriptor_id,
600                     d.age, d.note, d.sendto,
601                     getStatus(d.status), d.legatoMirror, s.type, s.kit, s.note);
602         }
603     }
604     printf(">NotePool::dump\n");
605 }
606 
607 }
608