/* 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 #include #include #define SUSTAIN_BIT 0x04 #define NOTE_MASK 0x03 namespace zyncarla { 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= 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(this)->cleanup(); return constActiveDescIter{*this}; } int NotePool::usedNoteDesc(void) const { if(needs_cleaning) const_cast(this)->cleanup(); int cnt = 0; for(int i=0; i(this)->cleanup(); int cnt = 0; for(int i=0; icloneLegato(); 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; ireleased() && 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; iNotePool::dump\n"); } }