| 
							- /*
 -   ZynAddSubFX - a software synthesizer
 - 
 -   NotePool.cpp - Pool of Synthesizer Engines And Note Instances
 -   Copyright (C) 2016 Mark McCurry
 - 
 -   This program is free software; you can redistribute it and/or
 -   modify it under the terms of the GNU General Public License
 -   as published by the Free Software Foundation; either version 2
 -   of the License, or (at your option) any later version.
 - */
 - #include "NotePool.h"
 - #include "../Misc/Allocator.h"
 - #include "../Synth/SynthNote.h"
 - #include <cstring>
 - #include <cassert>
 - #include <iostream>
 - 
 - #define SUSTAIN_BIT 0x04
 - #define NOTE_MASK   0x03
 - 
 - enum NoteStatus {
 -     KEY_OFF                    = 0x00,
 -     KEY_PLAYING                = 0x01,
 -     KEY_RELEASED_AND_SUSTAINED = 0x02,
 -     KEY_RELEASED               = 0x03
 - };
 - 
 - 
 - NotePool::NotePool(void)
 -     :needs_cleaning(0)
 - {
 -     memset(ndesc, 0, sizeof(ndesc));
 -     memset(sdesc, 0, sizeof(sdesc));
 - }
 - 
 - bool NotePool::NoteDescriptor::playing(void) const
 - {
 -     return (status&NOTE_MASK) == KEY_PLAYING;
 - }
 - 
 - bool NotePool::NoteDescriptor::sustained(void) const
 - {
 -     return (status&NOTE_MASK) == KEY_RELEASED_AND_SUSTAINED;
 - }
 - 
 - bool NotePool::NoteDescriptor::released(void) const
 - {
 -     return (status&NOTE_MASK) == KEY_RELEASED;
 - }
 - 
 - bool NotePool::NoteDescriptor::off(void) const
 - {
 -     return (status&NOTE_MASK) == KEY_OFF;
 - }
 - 
 - void NotePool::NoteDescriptor::setStatus(uint8_t s)
 - {
 -     status &= ~NOTE_MASK;
 -     status |= (NOTE_MASK&s);
 - }
 - 
 - void NotePool::NoteDescriptor::doSustain(void)
 - {
 -     setStatus(KEY_RELEASED_AND_SUSTAINED);
 - }
 - 
 - bool NotePool::NoteDescriptor::canSustain(void) const
 - {
 -     return !(status & SUSTAIN_BIT);
 - }
 - 
 - void NotePool::NoteDescriptor::makeUnsustainable(void)
 - {
 -     status |= SUSTAIN_BIT;
 - }
 - 
 - NotePool::activeNotesIter NotePool::activeNotes(NoteDescriptor &n)
 - {
 -     const int off_d1 = &n-ndesc;
 -     int off_d2 = 0;
 -     assert(off_d1 <= POLYPHONY);
 -     for(int i=0; i<off_d1; ++i)
 -         off_d2 += ndesc[i].size;
 -     return NotePool::activeNotesIter{sdesc+off_d2,sdesc+off_d2+n.size};
 - }
 - 
 - bool NotePool::NoteDescriptor::operator==(NoteDescriptor nd)
 - {
 -     return age == nd.age && note == nd.note && sendto == nd.sendto && size == nd.size && status == nd.status;
 - }
 - 
 - //return either the first unused descriptor or the last valid descriptor which
 - //matches note/sendto
 - static int getMergeableDescriptor(uint8_t note, uint8_t sendto, bool legato,
 -         NotePool::NoteDescriptor *ndesc)
 - {
 -     int desc_id = 0;
 -     for(int i=0; i<POLYPHONY; ++i, ++desc_id)
 -         if(ndesc[desc_id].off())
 -             break;
 - 
 -     if(desc_id != 0) {
 -         auto &nd = ndesc[desc_id-1];
 -         if(nd.age == 0 && nd.note == note && nd.sendto == sendto
 -                 && nd.playing() && nd.legatoMirror == legato && nd.canSustain())
 -             return desc_id-1;
 -     }
 - 
 -     //Out of free descriptors
 -     if(desc_id >= POLYPHONY || !ndesc[desc_id].off()) {
 -         return -1;
 -     }
 - 
 -     return desc_id;
 - }
 - 
 - NotePool::activeDescIter      NotePool::activeDesc(void)
 - {
 -     cleanup();
 -     return activeDescIter{*this};
 - }
 - 
 - NotePool::constActiveDescIter NotePool::activeDesc(void) const
 - {
 -     const_cast<NotePool*>(this)->cleanup();
 -     return constActiveDescIter{*this};
 - }
 - 
 - int NotePool::usedNoteDesc(void) const
 - {
 -     if(needs_cleaning)
 -         const_cast<NotePool*>(this)->cleanup();
 - 
 -     int cnt = 0;
 -     for(int i=0; i<POLYPHONY; ++i)
 -         cnt += (ndesc[i].size != 0);
 -     return cnt;
 - }
 - 
 - int NotePool::usedSynthDesc(void) const
 - {
 -     if(needs_cleaning)
 -         const_cast<NotePool*>(this)->cleanup();
 - 
 -     int cnt = 0;
 -     for(int i=0; i<POLYPHONY*EXPECTED_USAGE; ++i)
 -         cnt += (bool)sdesc[i].note;
 -     return cnt;
 - }
 - 
 - void NotePool::insertNote(uint8_t note, uint8_t sendto, SynthDescriptor desc, bool legato)
 - {
 -     //Get first free note descriptor
 -     int desc_id = getMergeableDescriptor(note, sendto, legato, ndesc);
 -     assert(desc_id != -1);
 - 
 -     ndesc[desc_id].note         = note;
 -     ndesc[desc_id].sendto       = sendto;
 -     ndesc[desc_id].size        += 1;
 -     ndesc[desc_id].status       = KEY_PLAYING;
 -     ndesc[desc_id].legatoMirror = legato;
 - 
 -     //Get first free synth descriptor
 -     int sdesc_id = 0;
 -     while(sdesc[sdesc_id].note && sdesc_id < POLYPHONY*EXPECTED_USAGE)
 -         sdesc_id++;
 - 
 -     assert(sdesc_id < POLYPHONY*EXPECTED_USAGE);
 - 
 -     sdesc[sdesc_id] = desc;
 - };
 - 
 - void NotePool::upgradeToLegato(void)
 - {
 -     for(auto &d:activeDesc())
 -         if(d.playing())
 -             for(auto &s:activeNotes(d))
 -                 insertLegatoNote(d.note, d.sendto, s);
 - }
 - 
 - void NotePool::insertLegatoNote(uint8_t note, uint8_t sendto, SynthDescriptor desc)
 - {
 -     assert(desc.note);
 -     try {
 -         desc.note = desc.note->cloneLegato();
 -         insertNote(note, sendto, desc, true);
 -     } catch (std::bad_alloc &ba) {
 -         std::cerr << "failed to insert legato note: " << ba.what() << std::endl;
 -     }
 - };
 - 
 - //There should only be one pair of notes which are still playing
 - void NotePool::applyLegato(LegatoParams &par)
 - {
 -     for(auto &desc:activeDesc()) {
 -         desc.note = par.midinote;
 -         for(auto &synth:activeNotes(desc))
 -             try {
 -                 synth.note->legatonote(par);
 -             } catch (std::bad_alloc& ba) {
 -                 std::cerr << "failed to create legato note: " << ba.what() << std::endl;
 -             }
 -     }
 - }
 - 
 - void NotePool::makeUnsustainable(uint8_t note)
 - {
 -     for(auto &desc:activeDesc()) {
 -         if(desc.note == note) {
 -             desc.makeUnsustainable();
 -             if(desc.sustained())
 -                 release(desc);
 -         }
 -     }
 - }
 - 
 - bool NotePool::full(void) const
 - {
 -     for(int i=0; i<POLYPHONY; ++i)
 -         if(ndesc[i].off())
 -             return false;
 -     return true;
 - }
 - 
 - bool NotePool::synthFull(int sdesc_count) const
 - {
 -     int actually_free=sizeof(sdesc)/sizeof(sdesc[0]);
 -     for(const auto &desc:activeDesc()) {
 -         actually_free -= desc.size;
 -     }
 -     return actually_free < sdesc_count;
 - }
 - 
 - //Note that isn't KEY_PLAYING or KEY_RELASED_AND_SUSTAINING
 - bool NotePool::existsRunningNote(void) const
 - {
 -     //printf("runing note # =%d\n", getRunningNotes());
 -     return getRunningNotes();
 - }
 - 
 - int NotePool::getRunningNotes(void) const
 - {
 -     bool running[256] = {0};
 -     for(auto &desc:activeDesc()) {
 -         //printf("note!(%d)\n", desc.note);
 -         if(desc.playing() || desc.sustained())
 -             running[desc.note] = true;
 -     }
 - 
 -     int running_count = 0;
 -     for(int i=0; i<256; ++i)
 -         running_count += running[i];
 - 
 -     return running_count;
 - }
 - void NotePool::enforceKeyLimit(int limit)
 - {
 -     int notes_to_kill = getRunningNotes() - limit;
 -     if(notes_to_kill <= 0)
 -         return;
 - 
 -     NoteDescriptor *to_kill = NULL;
 -     unsigned oldest = 0;
 -     for(auto &nd : activeDesc()) {
 -         if(to_kill == NULL) {
 -             //There must be something to kill
 -             oldest  = nd.age;
 -             to_kill = &nd;
 -         } else if(to_kill->released() && nd.playing()) {
 -             //Prefer to kill off a running note
 -             oldest = nd.age;
 -             to_kill = &nd;
 -         } else if(nd.age > oldest && !(to_kill->playing() && nd.released())) {
 -             //Get an older note when it doesn't move from running to released
 -             oldest = nd.age;
 -             to_kill = &nd;
 -         }
 -     }
 - 
 -     if(to_kill) {
 -         auto &tk = *to_kill;
 -         if(tk.released() || tk.sustained())
 -             kill(*to_kill);
 -         else
 -             entomb(*to_kill);
 -     }
 - }
 - 
 - void NotePool::releasePlayingNotes(void)
 - {
 -     for(auto &d:activeDesc()) {
 -         if(d.playing() || d.sustained()) {
 -             d.setStatus(KEY_RELEASED);
 -             for(auto s:activeNotes(d))
 -                 s.note->releasekey();
 -         }
 -     }
 - }
 - 
 - void NotePool::release(NoteDescriptor &d)
 - {
 -     d.setStatus(KEY_RELEASED);
 -     for(auto s:activeNotes(d))
 -         s.note->releasekey();
 - }
 - 
 - void NotePool::killAllNotes(void)
 - {
 -     for(auto &d:activeDesc())
 -         kill(d);
 - }
 - 
 - void NotePool::killNote(uint8_t note)
 - {
 -     for(auto &d:activeDesc()) {
 -         if(d.note == note)
 -             kill(d);
 -     }
 - }
 - 
 - void NotePool::kill(NoteDescriptor &d)
 - {
 -     d.setStatus(KEY_OFF);
 -     for(auto &s:activeNotes(d))
 -         kill(s);
 - }
 - 
 - void NotePool::kill(SynthDescriptor &s)
 - {
 -     //printf("Kill synth...\n");
 -     s.note->memory.dealloc(s.note);
 -     needs_cleaning = true;
 - }
 - 
 - void NotePool::entomb(NoteDescriptor &d)
 - {
 -     d.setStatus(KEY_RELEASED);
 -     for(auto &s:activeNotes(d))
 -         s.note->entomb();
 - }
 - 
 - const char *getStatus(int status_bits)
 - {
 -     switch(status_bits)
 -     {
 -         case 0:  return "OFF ";
 -         case 1:  return "PLAY";
 -         case 2:  return "SUST";
 -         case 3:  return "RELA";
 -         default: return "INVD";
 -     }
 - }
 - 
 - void NotePool::cleanup(void)
 - {
 -     if(!needs_cleaning)
 -         return;
 -     needs_cleaning = false;
 -     int new_length[POLYPHONY] = {0};
 -     int cur_length[POLYPHONY] = {0};
 -     //printf("Cleanup Start\n");
 -     //dump();
 - 
 -     //Identify the current length of all segments
 -     //and the lengths discarding invalid entries
 - 
 -     int last_valid_desc = 0;
 -     for(int i=0; i<POLYPHONY; ++i)
 -         if(!ndesc[i].off())
 -             last_valid_desc = i;
 - 
 -     //Find the real numbers of allocated notes
 -     {
 -         int cum_old = 0;
 - 
 -         for(int i=0; i<=last_valid_desc; ++i) {
 -             cur_length[i] = ndesc[i].size;
 -             for(int j=0; j<ndesc[i].size; ++j)
 -                 new_length[i] += (bool)sdesc[cum_old++].note;
 -         }
 -     }
 - 
 - 
 -     //Move the note descriptors
 -     {
 -         int cum_new = 0;
 -         for(int i=0; i<=last_valid_desc; ++i) {
 -             ndesc[i].size = new_length[i];
 -             if(new_length[i] != 0)
 -                 ndesc[cum_new++] = ndesc[i];
 -             else
 -                 ndesc[i].setStatus(KEY_OFF);
 -         }
 -         memset(ndesc+cum_new, 0, sizeof(*ndesc)*(POLYPHONY-cum_new));
 -     }
 - 
 -     //Move the synth descriptors
 -     {
 -         int total_notes=0;
 -         for(int i=0; i<=last_valid_desc; ++i)
 -             total_notes+=cur_length[i];
 - 
 -         int cum_new = 0;
 -         for(int i=0; i<total_notes; ++i)
 -             if(sdesc[i].note)
 -                 sdesc[cum_new++] = sdesc[i];
 -         memset(sdesc+cum_new, 0, sizeof(*sdesc)*(POLYPHONY*EXPECTED_USAGE-cum_new));
 -     }
 -     //printf("Cleanup Done\n");
 -     //dump();
 - }
 - 
 - void NotePool::dump(void)
 - {
 -     printf("NotePool::dump<\n");
 -     const char *format =
 -         "    Note %d:%d age(%d) note(%d) sendto(%d) status(%s) legato(%d) type(%d) kit(%d) ptr(%p)\n";
 -     int note_id=0;
 -     int descriptor_id=0;
 -     for(auto &d:activeDesc()) {
 -         descriptor_id += 1;
 -         for(auto &s:activeNotes(d)) {
 -             note_id += 1;
 -             printf(format,
 -                     note_id, descriptor_id,
 -                     d.age, d.note, d.sendto,
 -                     getStatus(d.status), d.legatoMirror, s.type, s.kit, s.note);
 -         }
 -     }
 -     printf(">NotePool::dump\n");
 - }
 
 
  |