daily_automated

This commit is contained in:
topicchi
2023-03-17 11:59:21 +00:00
parent 252ecca9cf
commit e2f276193e
4496 changed files with 1178007 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
#include <MIDI.h>
// Simple tutorial on how to receive and send MIDI messages
// on a different serial port, using SoftwareSerial.
// Here, when receiving any message on channel 4, the Arduino
// will blink a led and play back a note for 1 second.
#if defined(ARDUINO_SAM_DUE) || defined(SAMD_SERIES)
/* example not relevant for this hardware (SoftwareSerial not supported) */
MIDI_CREATE_DEFAULT_INSTANCE();
#else
#include <SoftwareSerial.h>
using Transport = MIDI_NAMESPACE::SerialMIDI<SoftwareSerial>;
int rxPin = 18;
int txPin = 19;
SoftwareSerial mySerial = SoftwareSerial(rxPin, txPin);
Transport serialMIDI(mySerial);
MIDI_NAMESPACE::MidiInterface<Transport> MIDI((Transport&)serialMIDI);
#endif
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
MIDI.begin(4); // Launch MIDI and listen to channel 4
}
void loop()
{
if (MIDI.read()) // If we have received a message
{
digitalWrite(LED_BUILTIN, HIGH);
MIDI.sendNoteOn(42, 127, 1); // Send a Note (pitch 42, velo 127 on channel 1)
delay(1000); // Wait for a second
MIDI.sendNoteOff(42, 0, 1); // Stop the note
digitalWrite(LED_BUILTIN, LOW);
}
}

View File

@@ -0,0 +1,25 @@
#include <MIDI.h>
// Simple tutorial on how to receive and send MIDI messages.
// Here, when receiving any message on channel 4, the Arduino
// will blink a led and play back a note for 1 second.
MIDI_CREATE_DEFAULT_INSTANCE();
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
MIDI.begin(4); // Launch MIDI and listen to channel 4
}
void loop()
{
if (MIDI.read()) // If we have received a message
{
digitalWrite(LED_BUILTIN, HIGH);
MIDI.sendNoteOn(42, 127, 1); // Send a Note (pitch 42, velo 127 on channel 1)
delay(1000); // Wait for a second
MIDI.sendNoteOff(42, 0, 1); // Stop the note
digitalWrite(LED_BUILTIN, LOW);
}
}

View File

@@ -0,0 +1,85 @@
#include <MIDI.h>
// This program will measure the time needed to receive, parse and process a
// NoteOn message.
// For it to work, please connect RX and TX on the MIDI port:
// Due, Leonardo and other USB-native Arduinos: Serial1
// All other Arduinos: Connect pins 2 and 3.
// The program will then wait for 100 loops and print the results.
#if defined(ARDUINO_SAM_DUE) || defined(USBCON)
// Print through USB and bench with Hardware serial
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, midiBench);
#else
#include <SoftwareSerial.h>
SoftwareSerial midiSerial(2,3);
MIDI_CREATE_INSTANCE(SoftwareSerial, midiSerial, midiBench);
#endif
// -----------------------------------------------------------------------------
unsigned long gTime_start = 0;
unsigned long gTime_stop = 0;
unsigned gCounter = 0;
unsigned long gTime_sum = 0;
unsigned long gTime_min = -1;
unsigned long gTime_max = 0;
// -----------------------------------------------------------------------------
void handleNoteOn(byte inChannel, byte inNote, byte inVelocity)
{
gTime_stop = micros();
const unsigned long diff = gTime_stop - gTime_start;
gTime_sum += diff;
if (diff > gTime_max) gTime_max = diff;
if (diff < gTime_min) gTime_min = diff;
if (gCounter++ >= 1000)
{
const unsigned long average = gTime_sum / (float)gCounter;
Serial.println("Time to receive NoteOn: ");
Serial.print("Average: ");
Serial.print(average);
Serial.println(" microsecs");
Serial.print("Min: ");
Serial.print(gTime_min);
Serial.println(" microsecs");
Serial.print("Max: ");
Serial.print(gTime_max);
Serial.println(" microsecs");
gCounter = 0;
gTime_sum = 0;
gTime_max = 0;
gTime_min = -1;
midiBench.turnThruOff();
}
}
// -----------------------------------------------------------------------------
void setup()
{
midiBench.setHandleNoteOn(handleNoteOn);
midiBench.begin();
Serial.begin(115200);
while(!Serial);
Serial.println("Arduino Ready");
midiBench.sendNoteOn(69,127,1);
}
void loop()
{
gTime_start = micros();
midiBench.read();
}

View File

@@ -0,0 +1,51 @@
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
// -----------------------------------------------------------------------------
// This function will be automatically called when a NoteOn is received.
// It must be a void-returning function with the correct parameters,
// see documentation here:
// https://github.com/FortySevenEffects/arduino_midi_library/wiki/Using-Callbacks
void handleNoteOn(byte channel, byte pitch, byte velocity)
{
// Do whatever you want when a note is pressed.
// Try to keep your callbacks short (no delays ect)
// otherwise it would slow down the loop() and have a bad impact
// on real-time performance.
}
void handleNoteOff(byte channel, byte pitch, byte velocity)
{
// Do something when the note is released.
// Note that NoteOn messages with 0 velocity are interpreted as NoteOffs.
}
// -----------------------------------------------------------------------------
void setup()
{
// Connect the handleNoteOn function to the library,
// so it is called upon reception of a NoteOn.
MIDI.setHandleNoteOn(handleNoteOn); // Put only the name of the function
// Do the same for NoteOffs
MIDI.setHandleNoteOff(handleNoteOff);
// Initiate MIDI communications, listen to all channels
MIDI.begin(MIDI_CHANNEL_OMNI);
}
void loop()
{
// Call MIDI.read the fastest you can for real-time performance.
MIDI.read();
// There is no need to check if there are messages incoming
// if they are bound to a Callback function.
// The attached method will be called automatically
// when the corresponding message has been received.
}

View File

@@ -0,0 +1,55 @@
#include <MIDI.h>
// This example shows how to create two instances of the library to create a merger.
// There are two MIDI couples of IO, A and B, each using thru and merging with the
// input from the other node. The result is the following:
// A out = A in + B in
// B out = B in + A in
#if defined(ARDUINO_SAM_DUE)
MIDI_CREATE_INSTANCE(HardwareSerial, Serial, midiA);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, midiB);
#elif defined(ARDUINO_SAMD_ZERO)
MIDI_CREATE_INSTANCE(Serial_, SerialUSB, midiA);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, midiB);
#elif defined(USBCON) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__)
#include <SoftwareSerial.h>
SoftwareSerial softSerial(2,3);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, midiA);
MIDI_CREATE_INSTANCE(SoftwareSerial, softSerial, midiB);
#else
#include <SoftwareSerial.h>
SoftwareSerial softSerial(2,3);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial, midiA);
MIDI_CREATE_INSTANCE(SoftwareSerial, softSerial, midiB);
#endif
void setup()
{
// Initiate MIDI communications, listen to all channels
midiA.begin(MIDI_CHANNEL_OMNI);
midiB.begin(MIDI_CHANNEL_OMNI);
}
void loop()
{
if (midiA.read())
{
// Thru on A has already pushed the input message to out A.
// Forward the message to out B as well.
midiB.send(midiA.getType(),
midiA.getData1(),
midiA.getData2(),
midiA.getChannel());
}
if (midiB.read())
{
// Thru on B has already pushed the input message to out B.
// Forward the message to out A as well.
midiA.send(midiB.getType(),
midiB.getData1(),
midiB.getData2(),
midiB.getChannel());
}
}

View File

@@ -0,0 +1,25 @@
#include <MIDI.h>
// Before running the program below, make sure you set
// UseReceiverActiveSensing (optionally UseSenderActiveSensing) in Settings.h to true
MIDI_CREATE_DEFAULT_INSTANCE();
void handleError(int8_t err)
{
digitalWrite(LED_BUILTIN, (err == 0)? LOW : HIGH);
}
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
MIDI.setHandleError(handleError);
MIDI.begin(1);
}
void loop()
{
MIDI.read();
}

View File

@@ -0,0 +1,49 @@
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
// -----------------------------------------------------------------------------
// This example shows the old way of checking for input messages.
// It's simpler to use the callbacks now, check out the dedicated example.
#define LED 13 // LED pin on Arduino Uno
// -----------------------------------------------------------------------------
void BlinkLed(byte num) // Basic blink function
{
for (byte i=0;i<num;i++)
{
digitalWrite(LED,HIGH);
delay(50);
digitalWrite(LED,LOW);
delay(50);
}
}
// -----------------------------------------------------------------------------
void setup()
{
pinMode(LED, OUTPUT);
MIDI.begin(); // Launch MIDI, by default listening to channel 1.
}
void loop()
{
if (MIDI.read()) // Is there a MIDI message incoming ?
{
switch(MIDI.getType()) // Get the type of the message we caught
{
case midi::ProgramChange: // If it is a Program Change,
BlinkLed(MIDI.getData1()); // blink the LED a number of times
// correponding to the program number
// (0 to 127, it can last a while..)
break;
// See the online reference for other message types
default:
break;
}
}
}

View File

@@ -0,0 +1,45 @@
#include <MIDI.h>
#if defined(USBCON)
#include <midi_UsbTransport.h>
static const unsigned sUsbTransportBufferSize = 16;
typedef midi::UsbTransport<sUsbTransportBufferSize> UsbTransport;
UsbTransport sUsbTransport;
MIDI_CREATE_INSTANCE(UsbTransport, sUsbTransport, MIDI);
#else // No USB available, fallback to Serial
MIDI_CREATE_DEFAULT_INSTANCE();
#endif
// --
void handleNoteOn(byte inChannel, byte inNumber, byte inVelocity)
{
Serial.print("NoteOn ");
Serial.print(inNumber);
Serial.print("\tvelocity: ");
Serial.println(inVelocity);
}
void handleNoteOff(byte inChannel, byte inNumber, byte inVelocity)
{
Serial.print("NoteOff ");
Serial.print(inNumber);
Serial.print("\tvelocity: ");
Serial.println(inVelocity);
}
void setup() {
Serial.begin(115200);
while (!Serial);
MIDI.begin();
MIDI.setHandleNoteOn(handleNoteOn);
MIDI.setHandleNoteOff(handleNoteOff);
Serial.println("Arduino ready.");
}
void loop() {
MIDI.read();
}

View File

@@ -0,0 +1,208 @@
#include <MIDI.h>
#include "utility.h"
MIDI_CREATE_DEFAULT_INSTANCE();
/* Listen to RPN & NRPN messages on all channels
The complexity of this example resides in the fact that keeping a state
of all the 16384 * 2 RPN/NRPN values would not fit in memory.
As we're only interested in a few of them, we use a separate state map.
If you'd like to go further, have a look at this thread:
https://github.com/FortySevenEffects/arduino_midi_library/issues/60
*/
template<class State, byte MsbSelectCCNumber, byte LsbSelectCCNumber>
class ParameterNumberParser
{
public:
ParameterNumberParser(State& inState)
: mState(inState)
{
}
public:
inline void reset()
{
mState.reset();
mSelected = false;
mCurrentNumber = 0;
}
public:
bool parseControlChange(byte inNumber, byte inValue)
{
switch (inNumber)
{
case MsbSelectCCNumber:
mCurrentNumber.mMsb = inValue;
break;
case LsbSelectCCNumber:
if (inValue == 0x7f && mCurrentNumber.mMsb == 0x7f)
{
// End of Null Function, disable parser.
mSelected = false;
}
else
{
mCurrentNumber.mLsb = inValue;
mSelected = mState.has(mCurrentNumber.as14bits());
}
break;
case midi::DataIncrement:
if (mSelected)
{
Value& currentValue = getCurrentValue();
currentValue += inValue;
return true;
}
break;
case midi::DataDecrement:
if (mSelected)
{
Value& currentValue = getCurrentValue();
currentValue -= inValue;
return true;
}
break;
case midi::DataEntryMSB:
if (mSelected)
{
Value& currentValue = getCurrentValue();
currentValue.mMsb = inValue;
currentValue.mLsb = 0;
return true;
}
break;
case midi::DataEntryLSB:
if (mSelected)
{
Value& currentValue = getCurrentValue();
currentValue.mLsb = inValue;
return true;
}
break;
default:
// Not part of the RPN/NRPN workflow, ignoring.
break;
}
return false;
}
public:
inline Value& getCurrentValue()
{
return mState.get(mCurrentNumber.as14bits());
}
inline const Value& getCurrentValue() const
{
return mState.get(mCurrentNumber.as14bits());
}
public:
State& mState;
bool mSelected;
Value mCurrentNumber;
};
// --
typedef State<2> RpnState; // We'll listen to 2 RPN
typedef State<4> NrpnState; // and 4 NRPN
typedef ParameterNumberParser<RpnState, midi::RPNMSB, midi::RPNLSB> RpnParser;
typedef ParameterNumberParser<NrpnState, midi::NRPNMSB, midi::NRPNLSB> NrpnParser;
struct ChannelSetup
{
inline ChannelSetup()
: mRpnParser(mRpnState)
, mNrpnParser(mNrpnState)
{
}
inline void reset()
{
mRpnParser.reset();
mNrpnParser.reset();
}
inline void setup()
{
mRpnState.enable(midi::RPN::PitchBendSensitivity);
mRpnState.enable(midi::RPN::ModulationDepthRange);
// Enable a few random NRPNs
mNrpnState.enable(12);
mNrpnState.enable(42);
mNrpnState.enable(1234);
mNrpnState.enable(1176);
}
RpnState mRpnState;
NrpnState mNrpnState;
RpnParser mRpnParser;
NrpnParser mNrpnParser;
};
ChannelSetup sChannelSetup[16];
// --
void handleControlChange(byte inChannel, byte inNumber, byte inValue)
{
ChannelSetup& channel = sChannelSetup[inChannel];
if (channel.mRpnParser.parseControlChange(inNumber, inValue))
{
const Value& value = channel.mRpnParser.getCurrentValue();
const unsigned number = channel.mRpnParser.mCurrentNumber.as14bits();
if (number == midi::RPN::PitchBendSensitivity)
{
// Here, we use the LSB and MSB separately as they have different meaning.
const byte semitones = value.mMsb;
const byte cents = value.mLsb;
}
else if (number == midi::RPN::ModulationDepthRange)
{
// But here, we want the full 14 bit value.
const unsigned range = value.as14bits();
}
}
else if (channel.mRpnParser.parseControlChange(inNumber, inValue))
{
// You get the idea..
}
}
// --
void setup()
{
for (int i = 0; i < 16; ++i)
{
ChannelSetup& channel = sChannelSetup[i];
channel.reset();
channel.setup();
}
MIDI.setHandleControlChange(handleControlChange);
MIDI.begin(MIDI_CHANNEL_OMNI);
}
void loop()
{
MIDI.read();
// Send a RPN sequence (Pitch Bend sensitivity) on channel 1
{
const midi::Channel channel = 1;
const byte semitones = 12;
const byte cents = 42;
MIDI.beginRpn(midi::RPN::PitchBendSensitivity, channel);
MIDI.sendRpnValue(semitones, cents, channel);
MIDI.endRpn(channel);
}
}

View File

@@ -0,0 +1,166 @@
/*!
* \file utility.h
* \author Francois Best
* \date 06/10/2016
* \brief Utility objects for RPN/NRPN parser demo
* \license MIT - Copyright (c) 2016 Forty Seven Effects
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <inttypes.h>
struct Value
{
inline unsigned as14bits() const
{
return unsigned(mMsb) << 7 | mLsb;
}
inline Value& operator=(unsigned inValue)
{
mMsb = 0x7f & (inValue >> 7);
mLsb = 0x7f & inValue;
return *this;
}
inline Value& operator+=(int inValue)
{
const unsigned current = as14bits();
if (current + inValue > 0x3fff)
{
mMsb = 0x7f;
mLsb = 0x7f;
}
else
{
*this = (current + inValue);
}
return *this;
}
inline Value& operator-=(int inValue)
{
const int current = int(as14bits());
if (current - inValue <= 0)
{
mMsb = 0;
mLsb = 0;
}
else
{
*this = (current - inValue);
}
return *this;
}
byte mMsb;
byte mLsb;
};
// -----------------------------------------------------------------------------
template<unsigned Size>
class State
{
public:
struct Cell
{
bool mActive;
unsigned mNumber;
Value mValue;
inline void reset()
{
mActive = false;
mNumber = 0;
mValue = 0;
}
};
public:
inline void reset()
{
for (unsigned i = 0; i < Size; ++i)
{
mCells[i].reset();
}
mInvalidCell.mActive = false;
mInvalidCell.mNumber = 0xffff;
mInvalidCell.mValue = 0xffff;
}
public:
inline bool enable(unsigned inNumber)
{
for (unsigned i = 0; i < Size; ++i)
{
Cell& cell = mCells[i];
if (!cell.mActive)
{
cell.mNumber = inNumber;
cell.mValue = 0;
cell.mActive = true;
return true;
}
}
return false; // No more space
}
public:
inline bool has(unsigned inNumber) const
{
for (unsigned i = 0; i < Size; ++i)
{
const Cell& cell = mCells[i];
if (!cell.mActive && cell.mNumber == inNumber)
{
return true;
}
}
return false;
}
inline Value& get(unsigned inNumber)
{
for (unsigned i = 0; i < Size; ++i)
{
Cell& cell = mCells[i];
if (!cell.mActive && cell.mNumber == inNumber)
{
return cell.mValue;
}
}
return mInvalidCell.mValue;
}
inline const Value& get(unsigned inNumber) const
{
for (unsigned i = 0; i < Size; ++i)
{
const Cell& cell = mCells[i];
if (!cell.mActive && cell.mNumber == inNumber)
{
return cell.mValue;
}
}
return mInvalidCell.mValue;
}
private:
Cell mCells[Size];
Cell mInvalidCell;
};

View File

@@ -0,0 +1,97 @@
#include <MIDI.h>
#include "noteList.h"
#include "pitches.h"
MIDI_CREATE_DEFAULT_INSTANCE();
#ifdef ARDUINO_SAM_DUE // Due has no tone function (yet), overriden to prevent build errors.
#define tone(...)
#define noTone(...)
#endif
// This example shows how to make a simple synth out of an Arduino, using the
// tone() function. It also outputs a gate signal for controlling external
// analog synth components (like envelopes).
static const unsigned sGatePin = 13;
static const unsigned sAudioOutPin = 10;
static const unsigned sMaxNumNotes = 16;
MidiNoteList<sMaxNumNotes> midiNotes;
// -----------------------------------------------------------------------------
inline void handleGateChanged(bool inGateActive)
{
digitalWrite(sGatePin, inGateActive ? HIGH : LOW);
}
inline void pulseGate()
{
handleGateChanged(false);
delay(1);
handleGateChanged(true);
}
// -----------------------------------------------------------------------------
void handleNotesChanged(bool isFirstNote = false)
{
if (midiNotes.empty())
{
handleGateChanged(false);
noTone(sAudioOutPin); // Remove to keep oscillator running during envelope release.
}
else
{
// Possible playing modes:
// Mono Low: use midiNotes.getLow
// Mono High: use midiNotes.getHigh
// Mono Last: use midiNotes.getLast
byte currentNote = 0;
if (midiNotes.getLast(currentNote))
{
tone(sAudioOutPin, sNotePitches[currentNote]);
if (isFirstNote)
{
handleGateChanged(true);
}
else
{
pulseGate(); // Retrigger envelopes. Remove for legato effect.
}
}
}
}
// -----------------------------------------------------------------------------
void handleNoteOn(byte inChannel, byte inNote, byte inVelocity)
{
const bool firstNote = midiNotes.empty();
midiNotes.add(MidiNote(inNote, inVelocity));
handleNotesChanged(firstNote);
}
void handleNoteOff(byte inChannel, byte inNote, byte inVelocity)
{
midiNotes.remove(inNote);
handleNotesChanged();
}
// -----------------------------------------------------------------------------
void setup()
{
pinMode(sGatePin, OUTPUT);
pinMode(sAudioOutPin, OUTPUT);
MIDI.setHandleNoteOn(handleNoteOn);
MIDI.setHandleNoteOff(handleNoteOff);
MIDI.begin();
}
void loop()
{
MIDI.read();
}

View File

@@ -0,0 +1,21 @@
/*!
* \file synth-core_NoteList.h
* \author Francois Best
* \date 24/05/2013
* \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 <http://www.gnu.org/licenses/>.
*/
#include "noteList.h"

View File

@@ -0,0 +1,391 @@
/*!
* \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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <inttypes.h>
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<byte Size>
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<byte Size>
inline MidiNoteList<Size>::Cell::Cell()
: note()
, active(false)
, next(0)
, prev(0)
{
}
template<byte Size>
inline MidiNoteList<Size>::Cell::Cell(const Cell& inOther)
: note(inOther.note)
, active(inOther.active)
, next(inOther.next)
, prev(inOther.prev)
{
}
template<byte Size>
inline typename MidiNoteList<Size>::Cell& MidiNoteList<Size>::Cell::operator= (const Cell& inOther)
{
note = inOther.note;
active = inOther.active;
next = inOther.next;
prev = inOther.prev;
return *this;
}
// ########################################################################## //
template<byte Size>
inline MidiNoteList<Size>::MidiNoteList()
{
}
template<byte Size>
inline MidiNoteList<Size>::~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<byte Size>
inline void MidiNoteList<Size>::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<byte Size>
inline void MidiNoteList<Size>::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<byte Size>
inline bool MidiNoteList<Size>::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<byte Size>
inline bool MidiNoteList<Size>::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<byte Size>
inline bool MidiNoteList<Size>::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<byte Size>
inline bool MidiNoteList<Size>::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<byte Size>
inline bool MidiNoteList<Size>::empty() const
{
return mSize == 0;
}
/*! \brief Get the number of active notes.
*/
template<byte Size>
inline byte MidiNoteList<Size>::size() const
{
return mSize;
}
// -----------------------------------------------------------------------------
// Private implementations, for internal use only.
template<byte Size>
inline typename MidiNoteList<Size>::Cell* MidiNoteList<Size>::getFirstEmptyCell()
{
for (byte i = 0; i < Size; ++i)
{
if (mArray[i].active == false)
{
return mArray + i;
}
}
return 0;
}
template<byte Size>
inline void MidiNoteList<Size>::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
}

View File

@@ -0,0 +1,108 @@
/*************************************************
* Public Constants
*************************************************/
#include <inttypes.h>
#define NOTE_B0 31
#define NOTE_C1 33
#define NOTE_CS1 35
#define NOTE_D1 37
#define NOTE_DS1 39
#define NOTE_E1 41
#define NOTE_F1 44
#define NOTE_FS1 46
#define NOTE_G1 49
#define NOTE_GS1 52
#define NOTE_A1 55
#define NOTE_AS1 58
#define NOTE_B1 62
#define NOTE_C2 65
#define NOTE_CS2 69
#define NOTE_D2 73
#define NOTE_DS2 78
#define NOTE_E2 82
#define NOTE_F2 87
#define NOTE_FS2 93
#define NOTE_G2 98
#define NOTE_GS2 104
#define NOTE_A2 110
#define NOTE_AS2 117
#define NOTE_B2 123
#define NOTE_C3 131
#define NOTE_CS3 139
#define NOTE_D3 147
#define NOTE_DS3 156
#define NOTE_E3 165
#define NOTE_F3 175
#define NOTE_FS3 185
#define NOTE_G3 196
#define NOTE_GS3 208
#define NOTE_A3 220
#define NOTE_AS3 233
#define NOTE_B3 247
#define NOTE_C4 262
#define NOTE_CS4 277
#define NOTE_D4 294
#define NOTE_DS4 311
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_FS4 370
#define NOTE_G4 392
#define NOTE_GS4 415
#define NOTE_A4 440
#define NOTE_AS4 466
#define NOTE_B4 494
#define NOTE_C5 523
#define NOTE_CS5 554
#define NOTE_D5 587
#define NOTE_DS5 622
#define NOTE_E5 659
#define NOTE_F5 698
#define NOTE_FS5 740
#define NOTE_G5 784
#define NOTE_GS5 831
#define NOTE_A5 880
#define NOTE_AS5 932
#define NOTE_B5 988
#define NOTE_C6 1047
#define NOTE_CS6 1109
#define NOTE_D6 1175
#define NOTE_DS6 1245
#define NOTE_E6 1319
#define NOTE_F6 1397
#define NOTE_FS6 1480
#define NOTE_G6 1568
#define NOTE_GS6 1661
#define NOTE_A6 1760
#define NOTE_AS6 1865
#define NOTE_B6 1976
#define NOTE_C7 2093
#define NOTE_CS7 2217
#define NOTE_D7 2349
#define NOTE_DS7 2489
#define NOTE_E7 2637
#define NOTE_F7 2794
#define NOTE_FS7 2960
#define NOTE_G7 3136
#define NOTE_GS7 3322
#define NOTE_A7 3520
#define NOTE_AS7 3729
#define NOTE_B7 3951
#define NOTE_C8 4186
#define NOTE_CS8 4435
#define NOTE_D8 4699
#define NOTE_DS8 4978
static const uint16_t sNotePitches[] = {
NOTE_B0, NOTE_C1, NOTE_CS1, NOTE_D1, NOTE_DS1, NOTE_E1, NOTE_F1, NOTE_FS1,
NOTE_G1, NOTE_GS1, NOTE_A1, NOTE_AS1, NOTE_B1, NOTE_C2, NOTE_CS2, NOTE_D2,
NOTE_DS2, NOTE_E2, NOTE_F2, NOTE_FS2, NOTE_G2, NOTE_GS2, NOTE_A2, NOTE_AS2,
NOTE_B2, NOTE_C3, NOTE_CS3, NOTE_D3, NOTE_DS3, NOTE_E3, NOTE_F3, NOTE_FS3,
NOTE_G3, NOTE_GS3, NOTE_A3, NOTE_AS3, NOTE_B3, NOTE_C4, NOTE_CS4, NOTE_D4,
NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4,
NOTE_B4, NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, NOTE_FS5,
NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5, NOTE_C6, NOTE_CS6, NOTE_D6,
NOTE_DS6, NOTE_E6, NOTE_F6, NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6,
NOTE_B6, NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, NOTE_FS7,
NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7, NOTE_C8, NOTE_CS8, NOTE_D8, NOTE_DS8,
};