/*! * \file noteList.h * \author Francois Best * \date 24/05/2013 * \brief Linked list of notes, for Low, Last & High playing modes. * \license GPL v3.0 - Copyright Forty Seven Effects 2013 * * 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 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include typedef uint8_t byte; // ----------------------------------------------------------------------------- struct MidiNote { inline MidiNote(); inline MidiNote(byte inPitch, byte inVelocity); inline MidiNote(const MidiNote& inOther); inline MidiNote& operator= (const MidiNote& inOther); byte pitch; byte velocity; }; // ----------------------------------------------------------------------------- template class MidiNoteList { private: struct Cell { inline Cell(); inline Cell(const Cell& inOther); inline Cell& operator= (const Cell& inOther); MidiNote note; bool active; Cell* next; Cell* prev; }; public: inline MidiNoteList(); inline ~MidiNoteList(); public: inline void add(const MidiNote& inNote); inline void remove(byte inPitch); public: inline bool get(byte inIndex, byte& outPitch) const; inline bool getLast(byte& outPitch) const; inline bool getHigh(byte& outPitch) const; inline bool getLow(byte& outPitch) const; public: inline bool empty() const; inline byte size() const; private: inline Cell* getFirstEmptyCell(); inline void print() const; private: Cell mArray[Size]; Cell* mHead; Cell* mTail; byte mSize; }; // ########################################################################## // // Inline implementation inline MidiNote::MidiNote() : pitch(0) , velocity(0) { } inline MidiNote::MidiNote(byte inPitch, byte inVelocity) : pitch(inPitch) , velocity(inVelocity) { } inline MidiNote::MidiNote(const MidiNote& inOther) : pitch(inOther.pitch) , velocity(inOther.velocity) { } inline MidiNote& MidiNote::operator= (const MidiNote& inOther) { pitch = inOther.pitch; velocity = inOther.velocity; return *this; } // ########################################################################## // template inline MidiNoteList::Cell::Cell() : note() , active(false) , next(0) , prev(0) { } template inline MidiNoteList::Cell::Cell(const Cell& inOther) : note(inOther.note) , active(inOther.active) , next(inOther.next) , prev(inOther.prev) { } template inline typename MidiNoteList::Cell& MidiNoteList::Cell::operator= (const Cell& inOther) { note = inOther.note; active = inOther.active; next = inOther.next; prev = inOther.prev; return *this; } // ########################################################################## // template inline MidiNoteList::MidiNoteList() { } template inline MidiNoteList::~MidiNoteList() { } // ----------------------------------------------------------------------------- /*! \brief Add a note, sorting it by time. Call this when receiving a NoteOn event. This will add the new note as the tail of the list. */ template inline void MidiNoteList::add(const MidiNote& inNote) { if (mHead == 0) { mArray[0].note = inNote; mArray[0].active = true; mArray[0].next = 0; mArray[0].prev = 0; mHead = mArray; mTail = mArray; } else { // Find the first inactive cell, and use it as tail. Cell* const oldTail = mTail; Cell* const newTail = getFirstEmptyCell(); newTail->active = true; newTail->note = inNote; oldTail->next = newTail; newTail->prev = oldTail; newTail->next = 0; mTail = newTail; } mSize++; print(); } /*! \brief Remove a note Call this when receiving a NoteOff event. */ template inline void MidiNoteList::remove(byte inPitch) { if (mTail != 0) { for (Cell* it = mTail; it != 0; it = it->prev) { if (it->note.pitch == inPitch) { Cell* const prev = it->prev; Cell* const next = it->next; it->active = false; it->next = 0; it->prev = 0; // Reconnect both ends if (it == mHead) { //AVR_ASSERT(prev == 0); mHead = next; } else { //AVR_ASSERT(prev != 0); prev->next = next; } if (it == mTail) { //AVR_ASSERT(next == 0); mTail = prev; } else { //AVR_ASSERT(next != 0); next->prev = prev; } mSize--; break; } } } print(); } // ----------------------------------------------------------------------------- /*! \brief Get a note at an arbitrary position This can be interesting for duo/multi/polyphony operations. */ template inline bool MidiNoteList::get(byte inIndex, byte& outPitch) const { if (mTail) { const Cell* it = mTail; for (byte i = 0; i < inIndex; ++i) { if (it->prev) { it = it->prev; } } print(); //AVR_LOG("Index " << inIndex << ": " << it->note.pitch); outPitch = it->note.pitch; return true; } return false; } /*! \brief Get the last active note played This implements the Mono Last playing mode. */ template inline bool MidiNoteList::getLast(byte& outPitch) const { if (!mTail) { return false; } outPitch = mTail->note.pitch; return true; } /*! \brief Get the highest pitched active note This implements the Mono High playing mode. */ template inline bool MidiNoteList::getHigh(byte& outPitch) const { if (!mTail) { return false; } outPitch = 0; const Cell* it = mTail; for (byte i = 0; i < mSize; ++i) { if (it->note.pitch > outPitch) { outPitch = it->note.pitch; } if (it->prev) { it = it->prev; } } return true; } /*! \brief Get the lowest pitched active note This implements the Mono Low playing mode. */ template inline bool MidiNoteList::getLow(byte& outPitch) const { if (!mTail) { return false; } outPitch = 0xff; const Cell* it = mTail; for (byte i = 0; i < mSize; ++i) { if (it->note.pitch < outPitch) { outPitch = it->note.pitch; } if (it->prev) { it = it->prev; } } return true; } // ----------------------------------------------------------------------------- template inline bool MidiNoteList::empty() const { return mSize == 0; } /*! \brief Get the number of active notes. */ template inline byte MidiNoteList::size() const { return mSize; } // ----------------------------------------------------------------------------- // Private implementations, for internal use only. template inline typename MidiNoteList::Cell* MidiNoteList::getFirstEmptyCell() { for (byte i = 0; i < Size; ++i) { if (mArray[i].active == false) { return mArray + i; } } return 0; } template inline void MidiNoteList::print() const { //#ifndef NDEBUG // AVR_DBG("Note List: [ "); // if (mHead) // { // for (const Cell* it = mHead; it != 0; it = it->next) // { // AVR_DBG(it->note.pitch); // if (it->next) // AVR_DBG(" -> "); // } // } // AVR_LOG(" ]"); //#endif }