Skip to content

Feauture additions #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Banks/Bank.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ void Bank::setBankSetting(uint8_t bankSetting)
}
}

void Bank::map(int (*fn)(int))
void Bank::map(int (*fn)(int, int))
{
MIDI_Element_list_node *element = firstMIDI_Element;
while (element != nullptr)
Expand Down
2 changes: 1 addition & 1 deletion src/Banks/Bank.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Bank
}

void setBankSetting(uint8_t bankSetting);
void map(int (*fn)(int));
void map(int (*fn)(int, int));

private:
const uint8_t channelsPerBank;
Expand Down
20 changes: 18 additions & 2 deletions src/MIDI_Outputs/Analog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,26 @@ Analog::Analog(pin_t analogPin, uint8_t controllerNumber, uint8_t channel) // Co
this->channel = channel;
}

void Analog::push() //
{
MIDI_Controller.MIDI()->send(CC, channel + channelOffset * channelsPerBank, controllerNumber + addressOffset * channelsPerBank, 127); // send a Control Change MIDI event
}

void Analog::release() //
{
MIDI_Controller.MIDI()->send(CC, channel + channelOffset * channelsPerBank, controllerNumber + addressOffset * channelsPerBank, 0); // send a Control Change MIDI event
}

void Analog::invert() // Invert the button state (send Note On event when released, Note Off when pressed)
{
invertState = true;
}

void Analog::refresh() // read the analog value, update the average, map it to a MIDI value, check if it changed since last time, if so, send Control Change message over MIDI
{
unsigned int input = ExtIO::analogRead(analogPin); // read the raw analog input value
input = analogMap(input); // apply the analogMap function to the value (identity function f(x) = x by default)
if (invertState) input = 1023-input; // invert the scale
input = analogMap(analogPin, input); // apply the analogMap function to the value (identity function f(x) = x by default)

#ifdef SINGLE_BYTE_AVERAGE // use 8-bit value for averaging
uint8_t value = input >> 2; // map from the 10-bit analog input value [0, 1023] to the 8-bit value [0, 255]
Expand All @@ -31,7 +47,7 @@ void Analog::refresh() // read the analog value, update the average, map it to a
}
}

void Analog::map(int (*fn)(int)) // change the function pointer for analogMap to a new function. It will be applied to the raw analog input value in Analog::refresh()
void Analog::map(int (*fn)(int, int)) // change the function pointer for analogMap to a new function. It will be applied to the raw analog input value in Analog::refresh()
{
analogMap = fn;
}
Expand Down
11 changes: 8 additions & 3 deletions src/MIDI_Outputs/Analog.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,24 @@ class Analog : public MIDI_Control_Element
{
public:
Analog(pin_t analogPin, uint8_t controllerNumber, uint8_t channel); // Constructor
void map(int (*fn)(int)); // Change the function pointer for analogMap to a new function. It will be applied to the raw analog input value in Analog::refresh()
void push();
void release();
void invert(); // Invert the analog scale
void map(int (*fn)(int, int)); // Change the function pointer for analogMap to a new function. It will be applied to the raw analog input value in Analog::refresh()

private:
void refresh(); // Read the analog input value, update the average, map it to a MIDI value, check if it changed since last time, if so, send Control Change message over MIDI

pin_t analogPin;
uint8_t controllerNumber, channel, oldVal = -1;
int (*analogMap)(int) = identity; // function pointer to identity function f(x) → x
int (*analogMap)(int, int) = identity; // function pointer to identity function f(x) → x

static int identity(int x)
static int identity(int p, int x)
{ // identity function f(x) → x
return x;
}

bool invertState = false;

#ifdef SINGLE_BYTE_AVERAGE
uint8_t runningAverage(uint8_t value); // http://playground.arduino.cc/Main/RunningAverage
Expand Down
69 changes: 69 additions & 0 deletions src/MIDI_Outputs/AnalogResponsive.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include "Arduino.h"
#include "AnalogResponsive.h"
#include "MIDI_Controller.h"

using namespace ExtIO;

AnalogResponsive::AnalogResponsive(pin_t analogPin, uint8_t msg, uint8_t controllerNumber, uint8_t channel) // Constructor
{
this->analogPin = analogPin;
this->msg = msg;
this->controllerNumber = controllerNumber;
this->channel = channel;
this->respAnalog = new ResponsiveAnalogRead(analogPin, true);
}

AnalogResponsive::~AnalogResponsive() // Destructor
{
delete respAnalog;
}

void AnalogResponsive::push(uint16_t value) //
{
switch (msg) {
case NOTE_ON:
MIDI_Controller.MIDI()->send(NOTE_ON, channel + channelOffset * channelsPerBank, value, 127);
break;
case CONTROL_CHANGE:
MIDI_Controller.MIDI()->send(CONTROL_CHANGE, channel + channelOffset * channelsPerBank, controllerNumber + addressOffset * channelsPerBank, value);
break;
case PROGRAM_CHANGE:
MIDI_Controller.MIDI()->send(PROGRAM_CHANGE, channel + channelOffset * channelsPerBank, value);
break;
case PITCH_BEND:
MIDI_Controller.MIDI()->send(PITCH_BEND, channel + channelOffset * channelsPerBank, value, value >> 7);
break;
}
}

void AnalogResponsive::release(uint16_t value) //
{
this->push(value);
}

void AnalogResponsive::invert() // Invert the button state (send Note On event when released, Note Off when pressed)
{
invertState = true;
}

void AnalogResponsive::refresh() // read the analog value, update the average, map it to a MIDI value, check if it changed since last time, if so, send Control Change message over MIDI
{
unsigned int input = ExtIO::analogRead(analogPin); // read the raw analog input value
if (invertState) input = 1023-input; // invert the scale
uint16_t value = analogMap(analogPin, input); // apply the analogMap function to the value (identity function f(x) = x by default)
respAnalog->update(value); // update the responsive analog average
if (respAnalog->hasChanged()) // if the value changed since last time
{
value = respAnalog->getValue(); // get the responsive analog average value
if ( msg == PITCH_BEND)
{
value = value << 4; // make it a 14-bit number (pad with 4 zeros)
}
this->push(value); // send a MIDI event
}
}

void AnalogResponsive::map(int (*fn)(int, int)) // change the function pointer for analogMap to a new function. It will be applied to the raw analog input value in Analog::refresh()
{
analogMap = fn;
}
36 changes: 36 additions & 0 deletions src/MIDI_Outputs/AnalogResponsive.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#ifndef AnalogResponsive_h_
#define AnalogResponsive_h_

#include "Arduino.h"
#include "../Settings/Settings.h"
#include "MIDI_Control_Element.h"
#include "../ExtendedInputOutput/ExtendedInputOutput.h"
#include <ResponsiveAnalogRead.h>

class AnalogResponsive : public MIDI_Control_Element
{
public:
AnalogResponsive(pin_t analogPin, uint8_t msg, uint8_t controllerNumber, uint8_t channel); // Constructor
~AnalogResponsive(); // Destructor
void push(uint16_t value = 0x4000);
void release(uint16_t value = 0);
void invert(); // Invert the analog scale
void map(int (*fn)(int, int)); // Change the function pointer for analogMap to a new function. It will be applied to the raw analog input value in Analog::refresh()

private:
void refresh(); // Read the analog input value, update the average, map it to a MIDI value, check if it changed since last time, if so, send Control Change message over MIDI

ResponsiveAnalogRead *respAnalog;
pin_t analogPin;
uint8_t msg, controllerNumber, channel, oldVal = -1;
int (*analogMap)(int, int) = identity; // function pointer to identity function f(x) → x

static int identity(int p, int x)
{ // identity function f(x) → x
return x;
}

bool invertState = false;
};

#endif // AnalogResponsive_h_
52 changes: 48 additions & 4 deletions src/MIDI_Outputs/Digital.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@

using namespace ExtIO;

Digital::Digital(pin_t pin, uint8_t note, uint8_t channel, uint8_t velocity) // Constructor
Digital::Digital(pin_t pin, uint8_t msg, uint8_t note, uint8_t channel, uint8_t velocity) // Constructor
{
ExtIO::pinMode(pin, INPUT_PULLUP); // Enable the internal pull-up resistor on the pin with the button/switch
this->pin = pin;
this->msg = msg;
this->note = note;
this->channel = channel;
this->velocity = velocity;
Expand All @@ -17,6 +18,44 @@ Digital::~Digital() // Destructor
ExtIO::pinMode(pin, INPUT); // make it a normal input again, without the internal pullup resistor.
}

void Digital::push() //
{
switch (msg) {
case NOTE_ON:
MIDI_Controller.MIDI()->send(NOTE_ON, channel + channelOffset * channelsPerBank, note + addressOffset * channelsPerBank, velocity);
break;
case CONTROL_CHANGE:
MIDI_Controller.MIDI()->send(CONTROL_CHANGE, channel + channelOffset * channelsPerBank, note + addressOffset * channelsPerBank, 127);
break;
case PROGRAM_CHANGE:
if (!invertState)
MIDI_Controller.MIDI()->send(PROGRAM_CHANGE, channel + channelOffset * channelsPerBank, note + addressOffset * channelsPerBank);
break;
case PITCH_BEND:
MIDI_Controller.MIDI()->send(PITCH_BEND, channel + channelOffset * channelsPerBank, 127, note + addressOffset * channelsPerBank);
break;
}
}

void Digital::release() //
{
switch (msg) {
case NOTE_ON:
MIDI_Controller.MIDI()->send(NOTE_OFF, channel + channelOffset * channelsPerBank, note + addressOffset * channelsPerBank, velocity);
break;
case CONTROL_CHANGE:
MIDI_Controller.MIDI()->send(CONTROL_CHANGE, channel + channelOffset * channelsPerBank, note + addressOffset * channelsPerBank, 0);
break;
case PROGRAM_CHANGE:
if (invertState)
MIDI_Controller.MIDI()->send(PROGRAM_CHANGE, channel + channelOffset * channelsPerBank, note + addressOffset * channelsPerBank);
break;
case PITCH_BEND:
MIDI_Controller.MIDI()->send(PITCH_BEND, channel + channelOffset * channelsPerBank, 0, note + addressOffset * channelsPerBank);
break;
}
}

void Digital::invert() // Invert the button state (send Note On event when released, Note Off when pressed)
{
invertState = true;
Expand All @@ -28,23 +67,28 @@ void Digital::refresh() // Check if the button state changed, and send a MIDI No

if (millis() - prevBounceTime > debounceTime)
{
int8_t stateChange = state - buttonState;
int8_t stateChange = digitalMap(pin, state) - buttonState;

if (stateChange == falling)
{ // Button is pushed
buttonState = state;
MIDI_Controller.MIDI()->send(NOTE_ON, channel + channelOffset * channelsPerBank, note + addressOffset * channelsPerBank, velocity);
this->push();
}

if (stateChange == rising)
{ // Button is released
buttonState = state;
MIDI_Controller.MIDI()->send(NOTE_OFF, channel + channelOffset * channelsPerBank, note + addressOffset * channelsPerBank, velocity);
this->release();
}
}
if (state != prevState)
{
prevBounceTime = millis();
prevState = state;
}
}

void Digital::map(int (*fn)(int, int)) // change the function pointer for digitalMap to a new function. It will be applied to the raw digital input value in Digital::refresh()
{
digitalMap = fn;
}
17 changes: 13 additions & 4 deletions src/MIDI_Outputs/Digital.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,24 @@
class Digital : public MIDI_Control_Element
{
public:
Digital(pin_t pin, uint8_t note, uint8_t channel, uint8_t velocity = 127); // Constructor
~Digital(); // Destructor
void invert(); // Invert the button state (send Note On event when released, Note Off when pressed)
Digital(pin_t pin, uint8_t msg, uint8_t note, uint8_t channel, uint8_t velocity = 127); // Constructor
~Digital(); // Destructor
void push();
void release();
void invert(); // Invert the button state (send Note On event when released, Note Off when pressed)
void map(int (*fn)(int, int)); // Change the function pointer for digitalMap to a new function. It will be applied to the raw digital input value in Digital::refresh()

private:
void refresh(); // Check if the button state changed, and send a MIDI Note On or Off accordingly
int (*digitalMap)(int, int) = identity; // function pointer to identity function f(x) → x

static int identity(int p, int x)
{ // identity function f(x) → x
return x;
}

pin_t pin;
uint8_t note, channel, velocity;
uint8_t msg, note, channel, velocity;
bool prevState = HIGH, buttonState = HIGH;
unsigned long prevBounceTime = 0;

Expand Down
Loading