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