Pipe is now a class!
This commit is contained in:
parent
e565fb35b6
commit
5eea748715
|
@ -50,7 +50,7 @@
|
||||||
#define ALG_DX9_2(feedback) \
|
#define ALG_DX9_2(feedback) \
|
||||||
{ \
|
{ \
|
||||||
{0, 1, 0, 0, 1}, \
|
{0, 1, 0, 0, 1}, \
|
||||||
{0, 0, 1, 1, 0}, \
|
{0, 0, 1, 1, 0}, \
|
||||||
{0, 0, 0, 0, 0}, \
|
{0, 0, 0, 0, 0}, \
|
||||||
{0, 0, 0, feedback, 0}, \
|
{0, 0, 0, feedback, 0}, \
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
#pragma once
|
||||||
|
#include "notes.h"
|
||||||
|
|
||||||
#define CCCC NOTE_CS5, NOTE_CS5, NOTE_CS5, NOTE_CS5
|
#define CCCC NOTE_CS5, NOTE_CS5, NOTE_CS5, NOTE_CS5
|
||||||
#define CCDD NOTE_CS5, NOTE_CS5, NOTE_D5, NOTE_D5
|
#define CCDD NOTE_CS5, NOTE_CS5, NOTE_D5, NOTE_D5
|
||||||
#define CDCD NOTE_CS5, NOTE_D5, NOTE_CS5, NOTE_D5
|
#define CDCD NOTE_CS5, NOTE_D5, NOTE_CS5, NOTE_D5
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "algorithms.h"
|
#include "algorithms.h"
|
||||||
|
#include "synth.h"
|
||||||
|
|
||||||
// Waveform, offset, multiplier, delay, attack, holdAmp, hold, decay, sustainAmp, release
|
// Waveform, offset, multiplier, delay, attack, holdAmp, hold, decay, sustainAmp, release
|
||||||
FMPatch Bank[] = {
|
FMPatch Bank[] = {
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
#include "pipe.h"
|
||||||
|
#include "fingering.h"
|
||||||
|
|
||||||
|
// Kludge time: this is something I did just to make my breadboard look nicer.
|
||||||
|
#define KEY_OFFSET 2
|
||||||
|
|
||||||
|
#define CLOSEDVAL 0x30
|
||||||
|
#define OPENVAL 0x70
|
||||||
|
#define GLISSANDO_STEPS (OPENVAL - CLOSEDVAL)
|
||||||
|
|
||||||
|
Pipe::Pipe() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Pipe::Init() {
|
||||||
|
// Capacative touch sensor
|
||||||
|
if (!capSensor.begin(0x5A)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proximity sensor
|
||||||
|
if (paj7620Init()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bag button
|
||||||
|
bagSensor.begin();
|
||||||
|
// This library takes the entire program out if you poll it 5-40 times without anything connected
|
||||||
|
bag_enabled = bagSensor.isConnected();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pipe::Update() {
|
||||||
|
uint8_t glissandoKeys = 0;
|
||||||
|
|
||||||
|
// Read the bag state, if there's a bag.
|
||||||
|
// if there isn't a bag, don't try, or this library will crash the program.
|
||||||
|
if (bag_enabled) {
|
||||||
|
bag = bagSensor.isPressed();
|
||||||
|
} else {
|
||||||
|
bag = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0x6c is actually 8 bytes, but all 8 are always the same...
|
||||||
|
paj7620ReadReg(0x6c, 1, &kneeClosedness);
|
||||||
|
|
||||||
|
keys = 0;
|
||||||
|
glissandoKeys = 0;
|
||||||
|
for (int i=0; i<8; i++) {
|
||||||
|
uint16_t val = max(capSensor.filteredData(i+KEY_OFFSET), CLOSEDVAL);
|
||||||
|
float openness = ((val - CLOSEDVAL) / float(GLISSANDO_STEPS));
|
||||||
|
|
||||||
|
// keys = all keys which are at least touched
|
||||||
|
// glissandoKeys = all keys which are fully closed
|
||||||
|
// The glissando operation computes the difference.
|
||||||
|
if (openness < 1.0) {
|
||||||
|
glissandoOpenness = max(glissandoOpenness, openness);
|
||||||
|
bitSet(keys, i);
|
||||||
|
}
|
||||||
|
if (openness == 0.0) {
|
||||||
|
bitSet(glissandoKeys, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up notes in the big table
|
||||||
|
note = uilleann_matrix[keys];
|
||||||
|
glissandoNote = uilleann_matrix[glissandoKeys];
|
||||||
|
|
||||||
|
// Was the high bit set? That indicates "alternate fingering", which sounds different.
|
||||||
|
altFingering = (note & 0x80);
|
||||||
|
|
||||||
|
note &= 0x7f;
|
||||||
|
glissandoNote &= 0x7f;
|
||||||
|
|
||||||
|
// If the bag is squished, jump up an octave
|
||||||
|
// But only if the left thumb is down!
|
||||||
|
if (bag && (keys & bit(7))) {
|
||||||
|
note += 12;
|
||||||
|
glissandoNote += 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All keys closed + knee = no sound
|
||||||
|
silent = ((kneeClosedness > 240) && (keys == 0xff));
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <SparkFun_Qwiic_Button.h>
|
||||||
|
#include <Adafruit_MPR121.h>
|
||||||
|
#include <paj7620.h>
|
||||||
|
|
||||||
|
class Pipe {
|
||||||
|
public:
|
||||||
|
// kneeClosedness indicates how "closed" the knee sensor is. 0 = wide open.
|
||||||
|
uint8_t kneeClosedness;
|
||||||
|
|
||||||
|
// keys are which keys are being pressed.
|
||||||
|
uint8_t keys;
|
||||||
|
|
||||||
|
// note holds the note being played, according to the fingering chart.
|
||||||
|
uint8_t note;
|
||||||
|
|
||||||
|
// silent is true if all keys and the knee are closed.
|
||||||
|
bool silent;
|
||||||
|
|
||||||
|
// bag is true if the bag is being squished.
|
||||||
|
bool bag;
|
||||||
|
|
||||||
|
// altFingering is true if the "alternate fingering" is being played.
|
||||||
|
// This should sound different than the standard fingering.
|
||||||
|
bool altFingering;
|
||||||
|
|
||||||
|
// glissandoNote is the note that would be played if partially open keys were fully open.
|
||||||
|
uint8_t glissandoNote;
|
||||||
|
|
||||||
|
// glissandoOpenness is how "open" the holes are in the direction of the glissandoNote.
|
||||||
|
float glissandoOpenness;
|
||||||
|
|
||||||
|
Pipe();
|
||||||
|
|
||||||
|
// Init initializes everything.
|
||||||
|
//
|
||||||
|
// Returns true if it all worked. You can run it again if it didn't.
|
||||||
|
bool Init();
|
||||||
|
|
||||||
|
// Update reads sensors and updates pipe state.
|
||||||
|
//
|
||||||
|
// It should be run once per loop.
|
||||||
|
void Update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Adafruit_MPR121 capSensor;
|
||||||
|
QwiicButton bagSensor;
|
||||||
|
bool bag_enabled;
|
||||||
|
};
|
2
synth.h
2
synth.h
|
@ -73,7 +73,7 @@ typedef struct FMOperator {
|
||||||
* can be accomplished by patching an operator into itself.
|
* can be accomplished by patching an operator into itself.
|
||||||
*/
|
*/
|
||||||
typedef struct FMPatch {
|
typedef struct FMPatch {
|
||||||
char *name;
|
const char *name;
|
||||||
float gains[NUM_OPERATORS][NUM_OPERATORS+1];
|
float gains[NUM_OPERATORS][NUM_OPERATORS+1];
|
||||||
FMOperator operators[NUM_OPERATORS];
|
FMOperator operators[NUM_OPERATORS];
|
||||||
} FMPatch;
|
} FMPatch;
|
||||||
|
|
172
uilleann.ino
172
uilleann.ino
|
@ -8,11 +8,14 @@
|
||||||
#include "synth.h"
|
#include "synth.h"
|
||||||
#include "patches.h"
|
#include "patches.h"
|
||||||
#include "notes.h"
|
#include "notes.h"
|
||||||
#include "fingering.h"
|
#include "pipe.h"
|
||||||
|
|
||||||
#define DRONES
|
#define DRONES
|
||||||
#define DEBUG
|
#define DEBUG
|
||||||
#define KEY_OFFSET 2
|
|
||||||
|
Pipe pipe;
|
||||||
|
Adafruit_SSD1306 display(128, 32, &Wire, -1);
|
||||||
|
int currentPatch = 0;
|
||||||
|
|
||||||
FMVoice Chanter;
|
FMVoice Chanter;
|
||||||
FMVoice Drones[3];
|
FMVoice Drones[3];
|
||||||
|
@ -61,13 +64,6 @@ AudioConnection FMVoicePatchCords[] = {
|
||||||
FMVoiceWiring(Regulators[2]),
|
FMVoiceWiring(Regulators[2]),
|
||||||
};
|
};
|
||||||
|
|
||||||
int currentPatch = 0;
|
|
||||||
|
|
||||||
Adafruit_MPR121 cap = Adafruit_MPR121();
|
|
||||||
Adafruit_SSD1306 display(128, 32, &Wire, -1);
|
|
||||||
QwiicButton bag;
|
|
||||||
bool use_bag;
|
|
||||||
|
|
||||||
void blink(bool forever) {
|
void blink(bool forever) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
digitalWrite(LED_BUILTIN, true);
|
digitalWrite(LED_BUILTIN, true);
|
||||||
|
@ -88,12 +84,7 @@ void setup() {
|
||||||
delay(100);
|
delay(100);
|
||||||
Wire.begin();
|
Wire.begin();
|
||||||
|
|
||||||
// Initialize gesture/proximity sensor
|
// Initialize display
|
||||||
if (paj7620Init()) {
|
|
||||||
// XXX: Error handling
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize display display
|
|
||||||
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3c)) {
|
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3c)) {
|
||||||
blink(true);
|
blink(true);
|
||||||
}
|
}
|
||||||
|
@ -103,13 +94,8 @@ void setup() {
|
||||||
display.print("Starting");
|
display.print("Starting");
|
||||||
display.display();
|
display.display();
|
||||||
|
|
||||||
// Initialize bag
|
while (!pipe.Init()) {
|
||||||
bag.begin();
|
display.clearDisplay();
|
||||||
use_bag = bag.isConnected();
|
|
||||||
|
|
||||||
// Initialize touch sensor
|
|
||||||
while (!cap.begin(0x5A)) {
|
|
||||||
display.clearDisplay();
|
|
||||||
display.setCursor(0, 0);
|
display.setCursor(0, 0);
|
||||||
display.print("Pipe?");
|
display.print("Pipe?");
|
||||||
display.display();
|
display.display();
|
||||||
|
@ -117,7 +103,7 @@ void setup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set aside some memory for the audio library
|
// Set aside some memory for the audio library
|
||||||
AudioMemory(120);
|
AudioMemory(20);
|
||||||
|
|
||||||
// initialize tunables
|
// initialize tunables
|
||||||
updateTunables(3, 0);
|
updateTunables(3, 0);
|
||||||
|
@ -143,6 +129,11 @@ void setup() {
|
||||||
mixL.gain(3, 0.1);
|
mixL.gain(3, 0.1);
|
||||||
mixR.gain(3, 0.1);
|
mixR.gain(3, 0.1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
display.print("Done!");
|
||||||
|
display.display();
|
||||||
}
|
}
|
||||||
|
|
||||||
#define INIT_PITCH_ADJUST 0
|
#define INIT_PITCH_ADJUST 0
|
||||||
|
@ -202,88 +193,17 @@ void updateTunables(uint8_t buttons, int note) {
|
||||||
|
|
||||||
FMPatch *p = &Bank[patch];
|
FMPatch *p = &Bank[patch];
|
||||||
Chanter.LoadPatch(p);
|
Chanter.LoadPatch(p);
|
||||||
|
|
||||||
display.clearDisplay();
|
|
||||||
display.setCursor(0, 0);
|
|
||||||
display.print(p->name);
|
|
||||||
display.setCursor(0, 10);
|
|
||||||
display.print("Patch ");
|
|
||||||
display.print(patch);
|
|
||||||
display.display();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t CLOSEDVAL = 0x30;
|
|
||||||
const uint8_t OPENVAL = 0x70;
|
|
||||||
const uint8_t GLISSANDO_STEPS = OPENVAL - CLOSEDVAL;
|
|
||||||
|
|
||||||
uint8_t loopno = 0;
|
|
||||||
uint8_t last_note = 0;
|
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
uint8_t keys = 0;
|
static uint8_t last_note = 0;
|
||||||
uint8_t note;
|
bool updateDisplay = false;
|
||||||
uint8_t glissandoKeys = 0;
|
bool setupMode = false;
|
||||||
uint8_t glissandoNote;
|
|
||||||
float glissandoOpenness = 0;
|
|
||||||
bool silent = false;
|
|
||||||
uint8_t paj_knee = 127;
|
|
||||||
bool knee = false;
|
|
||||||
|
|
||||||
loopno++;
|
pipe.Update();
|
||||||
|
|
||||||
paj7620ReadReg(0x6c, 1, &paj_knee);
|
|
||||||
if (paj_knee > 240) {
|
|
||||||
knee = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
uint16_t val = max(cap.filteredData(i+KEY_OFFSET), CLOSEDVAL);
|
|
||||||
float openness = ((val - CLOSEDVAL) / float(GLISSANDO_STEPS));
|
|
||||||
|
|
||||||
// keys = all keys which are at least touched
|
|
||||||
// glissandoKeys = all keys which are fully closed
|
|
||||||
// The glissando operation computes the difference.
|
|
||||||
if (openness < 1.0) {
|
|
||||||
glissandoOpenness = max(glissandoOpenness, openness);
|
|
||||||
bitSet(keys, i);
|
|
||||||
|
|
||||||
if (openness == 0.0) {
|
|
||||||
bitSet(glissandoKeys, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
note = uilleann_matrix[keys];
|
|
||||||
glissandoNote = uilleann_matrix[glissandoKeys];
|
|
||||||
|
|
||||||
bool alt = note & 0x80;
|
|
||||||
bool galt = glissandoNote & 0x80;
|
|
||||||
note = note & 0x7f;
|
|
||||||
glissandoNote = glissandoNote & 0x7f;
|
|
||||||
|
|
||||||
// All keys closed + knee = no sound
|
|
||||||
if (knee) {
|
|
||||||
if (keys == 0xff) {
|
|
||||||
silent = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Look up the note name
|
|
||||||
char *note_name = NoteNames[note % 12];
|
|
||||||
if (silent) {
|
|
||||||
note_name = "-";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Jump octave if the bag is squished
|
|
||||||
//bag = !digitalRead(BAG);
|
|
||||||
if (use_bag && bag.isPressed()) {
|
|
||||||
if (keys & bit(7)) {
|
|
||||||
note += 12;
|
|
||||||
glissandoNote += 12;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
display.clearDisplay();
|
display.clearDisplay();
|
||||||
display.setCursor(0, 0);
|
display.setCursor(0, 0);
|
||||||
|
@ -300,26 +220,34 @@ void loop() {
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// If we're infinitely (for the sensor) off the knee,
|
||||||
|
// go into setup mode!
|
||||||
|
if (pipe.kneeClosedness == 0) {
|
||||||
|
setupMode = true;
|
||||||
|
updateDisplay = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (silent) {
|
if (pipe.silent) {
|
||||||
Chanter.NoteOff();
|
Chanter.NoteOff();
|
||||||
} else {
|
} else {
|
||||||
// Calculate pitch, and glissando pitch
|
// Calculate pitch, and glissando pitch
|
||||||
uint16_t pitch = JustPitches[note];
|
uint16_t pitch = JustPitches[pipe.note];
|
||||||
uint16_t glissandoPitch = JustPitches[glissandoNote];
|
uint16_t glissandoPitch = JustPitches[pipe.glissandoNote];
|
||||||
|
|
||||||
if (alt) {
|
// Bend pitch if fewer than 3 half steps away
|
||||||
|
if (abs(pipe.glissandoNote - pipe.note) < 3) {
|
||||||
|
float diff = glissandoPitch - pitch;
|
||||||
|
pitch += diff * pipe.glissandoOpenness;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply a low shelf filter if this is the alternate fingering
|
||||||
|
if (pipe.altFingering) {
|
||||||
biquad1.setLowShelf(0, 2000, 0.2, 1);
|
biquad1.setLowShelf(0, 2000, 0.2, 1);
|
||||||
} else {
|
} else {
|
||||||
biquad1.setHighShelf(0, 1000, 1.0, 1);
|
biquad1.setHighShelf(0, 1000, 1.0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bend pitch if fewer than 3 half steps away
|
// We've figured out what pitch to play, now we can play it.
|
||||||
if (abs(glissandoNote - note) < 3) {
|
|
||||||
float diff = glissandoPitch - pitch;
|
|
||||||
pitch += diff * glissandoOpenness;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Chanter.playing) {
|
if (Chanter.playing) {
|
||||||
Chanter.SetPitch(pitch);
|
Chanter.SetPitch(pitch);
|
||||||
} else {
|
} else {
|
||||||
|
@ -327,11 +255,33 @@ void loop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (note != last_note) {
|
// Look up the note name
|
||||||
|
const char *note_name = NoteNames[pipe.note % 12];
|
||||||
|
if (pipe.silent) {
|
||||||
|
note_name = "--";
|
||||||
|
updateDisplay = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pipe.note != last_note) {
|
||||||
|
updateDisplay = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateDisplay) {
|
||||||
display.clearDisplay();
|
display.clearDisplay();
|
||||||
|
display.setTextSize(2);
|
||||||
display.setCursor(0, 0);
|
display.setCursor(0, 0);
|
||||||
display.print(note_name);
|
display.print(Chanter.patch->name);
|
||||||
|
|
||||||
|
if (setupMode) {
|
||||||
|
// THE SETUP DONUT
|
||||||
|
display.fillCircle(128-8, 16+8, 4, SSD1306_WHITE);
|
||||||
|
display.fillCircle(128-8, 16+8, 2, SSD1306_BLACK);
|
||||||
|
} else {
|
||||||
|
display.setCursor(0, 16);
|
||||||
|
display.print(note_name);
|
||||||
|
}
|
||||||
|
|
||||||
display.display();
|
display.display();
|
||||||
last_note = note;
|
last_note = pipe.note;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue