Pipe is now a class!

This commit is contained in:
Neale Pickett 2020-11-11 20:20:21 -07:00
parent e565fb35b6
commit 5eea748715
7 changed files with 202 additions and 113 deletions

View File

@ -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

View File

@ -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[] = {

84
pipe.cpp Normal file
View File

@ -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));
}

51
pipe.h Normal file
View File

@ -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;
};

View File

@ -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;

View File

@ -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,87 +193,16 @@ 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();
@ -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;
} }
} }