/*!
* \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
}