From 036dcf592eca0b41e6911a044b5cec83fc8b892a Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Tue, 24 Nov 2020 21:33:55 -0700 Subject: [PATCH] Now you can adjust pitch and intonation --- .clang-format | 3 + fingering.h | 140 ++++++++++++++++----------------- pipe.cpp | 126 +++++++++++++++--------------- pipe.h | 76 +++++++++--------- tuning.cpp | 51 ++++++++---- tuning.h | 210 ++++++++++++++++++++++++++++++++++++++------------ uilleann.ino | 109 +++++++++++++++++--------- 7 files changed, 448 insertions(+), 267 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..ce4f900 --- /dev/null +++ b/.clang-format @@ -0,0 +1,3 @@ +BasedOnStyle: Chromium +ColumnLimit: 0 +PointerAlignment: Right diff --git a/fingering.h b/fingering.h index e8feab9..ac85cef 100644 --- a/fingering.h +++ b/fingering.h @@ -3,11 +3,13 @@ struct Fingering { Note note; - bool alt; // Alternate fingering: sounds more choked + bool alt; // Alternate fingering: sounds more choked }; -#define n(note) {note, false} -#define P(note) {note, true} +#define n(note) \ + { note, false } +#define P(note) \ + { note, true } #define CCCC n(NOTE_CS5), n(NOTE_CS5), n(NOTE_CS5), n(NOTE_CS5) #define CCDD n(NOTE_CS5), n(NOTE_CS5), n(NOTE_D5), n(NOTE_D5) @@ -15,71 +17,71 @@ struct Fingering { #define DDDD n(NOTE_D5), n(NOTE_D5), n(NOTE_D5), n(NOTE_D5) struct Fingering uilleann_matrix[] = { - // Open Back D - n(NOTE_CS5), n(NOTE_CS5), n(NOTE_CS5), n(NOTE_D5), // OOO OO.. - CCDD, // OOO OX.. - CDCD, // OOO XO.. - DDDD, // OOO XX.. - CDCD, // OOX OO.. - DDDD, // OOX OX.. - CDCD, // OOX XO.. - DDDD, // OOX XX.. - CCDD, // OXO OO.. - CCDD, // OXO OX.. - DDDD, // OXO XO.. - DDDD, // OXO XX.. - DDDD, // OXX OO.. - DDDD, // OXX OX.. - DDDD, // OXX XO.. - DDDD, // OXX XX.. - CDCD, // XOO OO.. - DDDD, // XOO OX.. - CDCD, // XOO XO.. - DDDD, // XOO XX.. - CDCD, // XOX OO.. - DDDD, // XOX OX.. - CDCD, // XOX XO.. - DDDD, // XOX XX.. - DDDD, // XXO OO.. - DDDD, // XXO OX.. - DDDD, // XXO XO.. - DDDD, // XXO XX.. - DDDD, // XXX OO.. - DDDD, // XXX OX.. - n(NOTE_D5), n(NOTE_D5), n(NOTE_D5), P(NOTE_D5), // XXX XO.. - DDDD, // XXX XX.. + // Open Back D + n(NOTE_CS5), n(NOTE_CS5), n(NOTE_CS5), n(NOTE_D5), // OOO OO.. + CCDD, // OOO OX.. + CDCD, // OOO XO.. + DDDD, // OOO XX.. + CDCD, // OOX OO.. + DDDD, // OOX OX.. + CDCD, // OOX XO.. + DDDD, // OOX XX.. + CCDD, // OXO OO.. + CCDD, // OXO OX.. + DDDD, // OXO XO.. + DDDD, // OXO XX.. + DDDD, // OXX OO.. + DDDD, // OXX OX.. + DDDD, // OXX XO.. + DDDD, // OXX XX.. + CDCD, // XOO OO.. + DDDD, // XOO OX.. + CDCD, // XOO XO.. + DDDD, // XOO XX.. + CDCD, // XOX OO.. + DDDD, // XOX OX.. + CDCD, // XOX XO.. + DDDD, // XOX XX.. + DDDD, // XXO OO.. + DDDD, // XXO OX.. + DDDD, // XXO XO.. + DDDD, // XXO XX.. + DDDD, // XXX OO.. + DDDD, // XXX OX.. + n(NOTE_D5), n(NOTE_D5), n(NOTE_D5), P(NOTE_D5), // XXX XO.. + DDDD, // XXX XX.. - // Closed Back D - CCCC, // OOO OO... - n(NOTE_CS5), n(NOTE_CS5), n(NOTE_CS5), P(NOTE_CS5), // OOO OX.. - CCCC, // OOO XO.. - CCCC, // OOO XX.. - CCCC, // OOX OO.. - n(NOTE_CS5), P(NOTE_CS5), n(NOTE_CS5), P(NOTE_CS5), // OOX OX.. - CCCC, // OOX XO.. - CCCC, // OOX XX.. - CCCC, // OXO OO.. - n(NOTE_CS5), P(NOTE_CS5), n(NOTE_CS5), P(NOTE_CS5), // OXO OX.. - CCCC, // OXO XO.. - CCCC, // OXO XX.. - P(NOTE_C5), P(NOTE_C5), P(NOTE_C5), P(NOTE_C5), // OXX OO.. - n(NOTE_C5), n(NOTE_C5), n(NOTE_C5), n(NOTE_C5), // OXX OX.. - n(NOTE_C5), n(NOTE_C5), n(NOTE_C5), P(NOTE_C5), // OXX XO.. - n(NOTE_C5), n(NOTE_C5), n(NOTE_C5), n(NOTE_CS5), // OXX XX.. - n(NOTE_B4), n(NOTE_B4), n(NOTE_B4), n(NOTE_B4), // XOO OO.. - P(NOTE_B4), P(NOTE_B4), n(NOTE_B4), P(NOTE_B4), // XOO OX.. - n(NOTE_AS4), n(NOTE_B4), n(NOTE_AS4), n(NOTE_B4), // XOO XO.. - n(NOTE_B4), n(NOTE_B4), n(NOTE_B4), n(NOTE_B4), // XOO XX.. - P(NOTE_B4), P(NOTE_B4), P(NOTE_B4), P(NOTE_B4), // XOX OO.. - P(NOTE_B4), P(NOTE_B4), P(NOTE_B4), P(NOTE_B4), // XOX OX.. - P(NOTE_B4), P(NOTE_B4), P(NOTE_B4), P(NOTE_B4), // XOX XO.. - P(NOTE_B4), P(NOTE_B4), P(NOTE_B4), P(NOTE_B4), // XOX XX.. - n(NOTE_A4), n(NOTE_A4), P(NOTE_A4), n(NOTE_A4), // XXO OO.. - P(NOTE_A4), P(NOTE_A4), P(NOTE_A4), P(NOTE_A4), // XXO OX.. - n(NOTE_GS4), P(NOTE_GS4), n(NOTE_A4), n(NOTE_A4), // XXO XO.. - P(NOTE_A4), P(NOTE_A4), P(NOTE_A4), n(NOTE_A4), // XXO XX.. - n(NOTE_G4), n(NOTE_G4), P(NOTE_G4), n(NOTE_G4), // XXX OO.. - P(NOTE_G4), P(NOTE_G4), P(NOTE_G4), P(NOTE_G4), // XXX OX.. - n(NOTE_FS4), n(NOTE_FS4), n(NOTE_F4), P(NOTE_FS4), // XXX XO.. - n(NOTE_E4), P(NOTE_E4), n(NOTE_DS4), n(NOTE_D4), // XXX XX.. + // Closed Back D + CCCC, // OOO OO... + n(NOTE_CS5), n(NOTE_CS5), n(NOTE_CS5), P(NOTE_CS5), // OOO OX.. + CCCC, // OOO XO.. + CCCC, // OOO XX.. + CCCC, // OOX OO.. + n(NOTE_CS5), P(NOTE_CS5), n(NOTE_CS5), P(NOTE_CS5), // OOX OX.. + CCCC, // OOX XO.. + CCCC, // OOX XX.. + CCCC, // OXO OO.. + n(NOTE_CS5), P(NOTE_CS5), n(NOTE_CS5), P(NOTE_CS5), // OXO OX.. + CCCC, // OXO XO.. + CCCC, // OXO XX.. + P(NOTE_C5), P(NOTE_C5), P(NOTE_C5), P(NOTE_C5), // OXX OO.. + n(NOTE_C5), n(NOTE_C5), n(NOTE_C5), n(NOTE_C5), // OXX OX.. + n(NOTE_C5), n(NOTE_C5), n(NOTE_C5), P(NOTE_C5), // OXX XO.. + n(NOTE_C5), n(NOTE_C5), n(NOTE_C5), n(NOTE_CS5), // OXX XX.. + n(NOTE_B4), n(NOTE_B4), n(NOTE_B4), n(NOTE_B4), // XOO OO.. + P(NOTE_B4), P(NOTE_B4), n(NOTE_B4), P(NOTE_B4), // XOO OX.. + n(NOTE_AS4), n(NOTE_B4), n(NOTE_AS4), n(NOTE_B4), // XOO XO.. + n(NOTE_B4), n(NOTE_B4), n(NOTE_B4), n(NOTE_B4), // XOO XX.. + P(NOTE_B4), P(NOTE_B4), P(NOTE_B4), P(NOTE_B4), // XOX OO.. + P(NOTE_B4), P(NOTE_B4), P(NOTE_B4), P(NOTE_B4), // XOX OX.. + P(NOTE_B4), P(NOTE_B4), P(NOTE_B4), P(NOTE_B4), // XOX XO.. + P(NOTE_B4), P(NOTE_B4), P(NOTE_B4), P(NOTE_B4), // XOX XX.. + n(NOTE_A4), n(NOTE_A4), P(NOTE_A4), n(NOTE_A4), // XXO OO.. + P(NOTE_A4), P(NOTE_A4), P(NOTE_A4), P(NOTE_A4), // XXO OX.. + n(NOTE_GS4), P(NOTE_GS4), n(NOTE_A4), n(NOTE_A4), // XXO XO.. + P(NOTE_A4), P(NOTE_A4), P(NOTE_A4), n(NOTE_A4), // XXO XX.. + n(NOTE_G4), n(NOTE_G4), P(NOTE_G4), n(NOTE_G4), // XXX OO.. + P(NOTE_G4), P(NOTE_G4), P(NOTE_G4), P(NOTE_G4), // XXX OX.. + n(NOTE_FS4), n(NOTE_FS4), n(NOTE_F4), P(NOTE_FS4), // XXX XO.. + n(NOTE_E4), P(NOTE_E4), n(NOTE_DS4), n(NOTE_D4), // XXX XX.. }; diff --git a/pipe.cpp b/pipe.cpp index 67a93d4..0ded6b1 100644 --- a/pipe.cpp +++ b/pipe.cpp @@ -1,6 +1,6 @@ #include "pipe.h" -#include "tuning.h" #include "fingering.h" +#include "tuning.h" // Kludge time: this is something I did just to make my breadboard look nicer. #define KEY_OFFSET 2 @@ -10,89 +10,89 @@ #define GLISSANDO_STEPS (OPENVAL - CLOSEDVAL) Pipe::Pipe() { - keysLast = 0; + keysLast = 0; } bool Pipe::Init() { - // Capacative touch sensor - if (!capSensor.begin(0x5A)) { - return false; - } + // Capacative touch sensor + if (!capSensor.begin(0x5A)) { + return false; + } - // Proximity sensor - if (paj7620Init()) { - 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(); + // 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; + return true; } void Pipe::Update() { - uint8_t glissandoKeys = 0; + 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; + // 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); + + keysLast = keys; + 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); } - - // 0x6c is actually 8 bytes, but all 8 are always the same... - paj7620ReadReg(0x6c, 1, &kneeClosedness); - - keysLast = keys; - 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); - } + if (openness == 0.0) { + bitSet(glissandoKeys, i); } + } - // Look up notes in the big table - struct Fingering f = uilleann_matrix[keys]; - struct Fingering gf = uilleann_matrix[glissandoKeys]; + // Look up notes in the big table + struct Fingering f = uilleann_matrix[keys]; + struct Fingering gf = uilleann_matrix[glissandoKeys]; - note = f.note; - glissandoNote = gf.note; + note = f.note; + glissandoNote = gf.note; - // Was the high bit set? That indicates "alternate fingering", which sounds different. - altFingering = f.alt; + // Was the high bit set? That indicates "alternate fingering", which sounds different. + altFingering = f.alt; - // If the bag is squished, jump up an octave - // But only if the left thumb is down! - if (bag && (keys & bit(7))) { - note += NOTE_OCTAVE; - glissandoNote += NOTE_OCTAVE; - } + // If the bag is squished, jump up an octave + // But only if the left thumb is down! + if (bag && (keys & bit(7))) { + note += NOTE_OCTAVE; + glissandoNote += NOTE_OCTAVE; + } - // All keys closed + knee = no sound - silent = ((kneeClosedness > 240) && (keys == 0xff)); + // All keys closed + knee = no sound + silent = ((kneeClosedness > 240) && (keys == 0xff)); } bool Pipe::Pressed(uint8_t key) { - return bitRead(keys, key); + return bitRead(keys, key); } bool Pipe::JustPressed(uint8_t key) { - if (bitRead(keys, key)) { - return !bitRead(keysLast, key); - } - return false; + if (bitRead(keys, key)) { + return !bitRead(keysLast, key); + } + return false; } diff --git a/pipe.h b/pipe.h index 055895c..44c4862 100644 --- a/pipe.h +++ b/pipe.h @@ -1,59 +1,59 @@ #pragma once -#include -#include #include +#include #include +#include #include "tuning.h" class Pipe { -public: - // kneeClosedness indicates how "closed" the knee sensor is. 0 = wide open. - uint8_t kneeClosedness; + 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; - uint8_t keysLast; + // keys are which keys are being pressed. + uint8_t keys; + uint8_t keysLast; - // note holds the note being played, according to the fingering chart. - Note note; + // note holds the note being played, according to the fingering chart. + Note note; - // glissandoNote is the note that would be played if partially open keys were fully open. - Note glissandoNote; + // glissandoNote is the note that would be played if partially open keys were fully open. + Note glissandoNote; - // glissandoOpenness is how "open" the holes are in the direction of the glissandoNote. - float glissandoOpenness; + // glissandoOpenness is how "open" the holes are in the direction of the glissandoNote. + float glissandoOpenness; - // silent is true if all keys and the knee are closed. - bool silent; + // silent is true if all keys and the knee are closed. + bool silent; - // bag is true if the bag is being squished. - bool bag; + // 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; + // altFingering is true if the "alternate fingering" is being played. + // This should sound different than the standard fingering. + bool altFingering; - Pipe(); + Pipe(); - // Init initializes everything. - // - // Returns true if it all worked. You can run it again if it didn't. - bool Init(); + // 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(); + // Update reads sensors and updates pipe state. + // + // It should be run once per loop. + void Update(); - // Pressed returns whether the given key is pressed. - bool Pressed(uint8_t key); + // Pressed returns whether the given key is pressed. + bool Pressed(uint8_t key); - // JustPressed returns whether the given key was just pressed. - bool JustPressed(uint8_t key); + // JustPressed returns whether the given key was just pressed. + bool JustPressed(uint8_t key); -private: - Adafruit_MPR121 capSensor; - QwiicButton bagSensor; - bool bag_enabled; + private: + Adafruit_MPR121 capSensor; + QwiicButton bagSensor; + bool bag_enabled; }; diff --git a/tuning.cpp b/tuning.cpp index d56f8b5..a605f41 100644 --- a/tuning.cpp +++ b/tuning.cpp @@ -2,9 +2,6 @@ #include -// 12th root of 2, used for Twelvetone Equal Temperament -#define TET_CONST 1.059463 - Tuning::Tuning(Note base, float pitch, TuningSystem system) { Setup(base, pitch, system); } @@ -14,18 +11,20 @@ Tuning::Tuning(Note base, float pitch) { // I like just Intonation. Tuning::Tuning() { -#if 0 Tuning(NOTE_D4, PITCH_CONCERT_D4, TUNINGSYSTEM_JUST); -#endif } -Note Tuning::GetBaseNote() { return baseNote; } +Note Tuning::GetBaseNote() { + return baseNote; +} void Tuning::SetTuningSystem(TuningSystem system) { Setup(baseNote, GetPitch(baseNote), system); } -TuningSystem Tuning::GetSystem() { return system; } +TuningSystem Tuning::GetTuningSystem() { + return system; +} // setupOctaves computes the entire tuning frequency chart. // @@ -53,7 +52,7 @@ void Tuning::setupOctaves(Note base) { void Tuning::setupEqual(Note base, float pitch) { pitches[base] = pitch; for (int i = 1; i < 12; i++) { - pitches[base + i] = pitches[base + i - 1] * TET_CONST; + pitches[base + i] = pitches[base + i - 1] * TET_SEMITONE_MULTIPLIER; } } @@ -95,17 +94,43 @@ void Tuning::Setup(Note base, float pitch, TuningSystem system) { setupOctaves(base); } -void Tuning::Setup(Note base, float pitch) { Setup(base, pitch, system); } +void Tuning::Setup(Note base, float pitch) { + Setup(base, pitch, system); +} -float Tuning::GetPitch(Note note) { return pitches[note]; } +float Tuning::GetPitch(Note note) { + return pitches[note]; +} Note NearestNote(float pitch) { - return Note(round(log2(pitch / PITCH_CONCERT_C0))); + return Note(round(log(pitch / PITCH_CONCERT_C0) / log(TET_SEMITONE_MULTIPLIER))); } const char *noteNames[]{ - "C ", "C#", "D ", "Eb", "E ", "F ", "F#", "G ", "Ab", "A ", "Bb", "B ", + "C", + "C#", + "D", + "Eb", + "E", + "F", + "F#", + "G", + "Ab", + "A", + "Bb", + "B", }; -const char *NoteName(Note note) { return noteNames[note % 12]; } +const char *NoteName(Note note) { + return noteNames[note % 12]; +} +const char *TuningSystemName(TuningSystem system) { + switch (system) { + case TUNINGSYSTEM_EQUAL: + return "Equal"; + case TUNINGSYSTEM_JUST: + default: + return "Just"; + } +} diff --git a/tuning.h b/tuning.h index 5483cdc..bcb54a5 100644 --- a/tuning.h +++ b/tuning.h @@ -2,21 +2,120 @@ #include enum TuningSystem { - TUNINGSYSTEM_JUST, - TUNINGSYSTEM_EQUAL, + TUNINGSYSTEM_JUST, + TUNINGSYSTEM_EQUAL, + TUNINGSYSTEM_MAX = TUNINGSYSTEM_EQUAL, }; -#define TUNINGSYSTEM_MAX TUNINGSYSTEM_EQUAL enum Note { - NOTE_C0, NOTE_CS0, NOTE_D0, NOTE_DS0, NOTE_E0, NOTE_F0, NOTE_FS0, NOTE_G0, NOTE_GS0, NOTE_A0, NOTE_AS0, 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, NOTE_E8, NOTE_F8, NOTE_FS8, NOTE_G8, NOTE_GS8, NOTE_A8, NOTE_AS8, NOTE_B8, + NOTE_C0, + NOTE_CS0, + NOTE_D0, + NOTE_DS0, + NOTE_E0, + NOTE_F0, + NOTE_FS0, + NOTE_G0, + NOTE_GS0, + NOTE_A0, + NOTE_AS0, + 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, + NOTE_E8, + NOTE_F8, + NOTE_FS8, + NOTE_G8, + NOTE_GS8, + NOTE_A8, + NOTE_AS8, + NOTE_B8, NOTE_ZERO = 0, NOTE_SEMITONE = 1, NOTE_WHOLETONE = 2, @@ -25,75 +124,88 @@ enum Note { }; #define PITCH_CONCERT_C0 16.35 -#define PITCH_CONCERT_A4 440.00 +#define PITCH_CONCERT_A4 440.00 #define PITCH_CONCERT_D4 293.66 +// Twelvetone Equal Temperament semitone multiplier +// Take any frequency and multiply it by this magic number to get a semitone higher! +// Divide to get a semitone lower! +// This is an approximation of exp(2, 1/12), +// which was worked out in around the 1500s. +#define TET_SEMITONE_MULTIPLIER 1.059463 + class Tuning { -public: - // name contains the name of the current tuning system - const char *name; + public: + // name contains the name of the current tuning system + const char *name; - Tuning(Note base, float pitch, TuningSystem system); - Tuning(Note base, float pitch); - Tuning(); - void Setup(Note base, float pitch, TuningSystem system); - void Setup(Note base, float pitch); - void SetTuningSystem(TuningSystem system); - TuningSystem GetSystem(); - Note GetBaseNote(); - float GetPitch(Note note); + Tuning(Note base, float pitch, TuningSystem system); + Tuning(Note base, float pitch); + Tuning(); + void Setup(Note base, float pitch, TuningSystem system); + void Setup(Note base, float pitch); + void SetTuningSystem(TuningSystem system); + TuningSystem GetTuningSystem(); + Note GetBaseNote(); + float GetPitch(Note note); -private: - TuningSystem system; - Note baseNote; - float pitches[NOTE_MAX]; + private: + TuningSystem system; + Note baseNote; + float pitches[NOTE_MAX]; - void setupOctaves(Note base); - void setupJust(Note base, float pitch); - void setupEqual(Note base, float pitch); + void setupOctaves(Note base); + void setupJust(Note base, float pitch); + void setupEqual(Note base, float pitch); }; // NearestNote returns the note nearest to pitch. Note NearestNote(float pitch); -// NoteName return the name of a note (without octave). +// NoteName returns the name of a note (without octave). const char *NoteName(Note note); +// TuningSystemName returns the name of a tuning system. +const char *TuningSystemName(TuningSystem system); // Make notes support some arithmetic inline Note toNote(int a) { - if (a < NOTE_ZERO) { - return NOTE_ZERO; - } else if (a > NOTE_MAX) { - return NOTE_MAX; - } else { - return Note(a); - } + if (a < NOTE_ZERO) { + return NOTE_ZERO; + } else if (a > NOTE_MAX) { + return NOTE_MAX; + } else { + return Note(a); + } } inline Note operator+(const Note &a, const Note b) { - return toNote(int(a) + int(b)); + return toNote(int(a) + int(b)); } inline Note &operator+=(Note &a, const Note b) { - return a = a + b; + return a = a + b; } inline Note &operator++(Note &a) { - return a += NOTE_SEMITONE; + return a += NOTE_SEMITONE; } inline Note operator-(const Note a, const Note b) { - return toNote(int(a) - int(b)); + return toNote(int(a) - int(b)); } inline Note &operator-=(Note &a, const Note b) { - return a = a - b; + return a = a - b; } inline Note &operator--(Note &a) { - return a -= NOTE_SEMITONE; + return a -= NOTE_SEMITONE; } inline Note operator*(const Note a, const int b) { - return toNote(int(a) * b); + return toNote(int(a) * b); } inline int operator/(const Note a, const int b) { - return int(a)/b; + return int(a) / b; } inline int operator/(const Note a, const Note b) { - return int(a)/b; + return int(a) / b; +} + +inline TuningSystem operator++(TuningSystem &a) { + return a = TuningSystem((int(a) + 1) % int(TUNINGSYSTEM_MAX + 1)); } diff --git a/uilleann.ino b/uilleann.ino index 37a3eff..89616fa 100644 --- a/uilleann.ino +++ b/uilleann.ino @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -22,14 +21,12 @@ Pipe pipe; Tuning tuning = Tuning(NOTE_D4, PITCH_CONCERT_D4, TUNINGSYSTEM_JUST); Adafruit_SSD1306 display(128, 32, &Wire, -1); -int currentPatch = 0; // Settings -uint8_t intonation = 0; -int16_t pitchAdjust = 0; uint8_t patch[4] = {0}; float volume[4] = {0}; const char *settingNames[4] = {"c", "r", "d", "*"}; +const char *buildDate = __DATE__; // Pipes FMVoice Chanter; @@ -100,7 +97,7 @@ void diag(const char *fmt, ...) { char s[80]; va_start(args, fmt); - vsnprintf(s, sizeof(s)-1, fmt, args); + vsnprintf(s, sizeof(s) - 1, fmt, args); va_end(args); display.clearDisplay(); @@ -109,7 +106,7 @@ void diag(const char *fmt, ...) { display.setTextSize(1); display.setCursor(56, 24); - display.print(__DATE__); + display.print(buildDate); #if 0 display.setCursor(0, 16); @@ -171,8 +168,7 @@ void setup() { // Turn on drones for (int i = 0; i < 3; i++) { Note note = NOTE_D4 - (NOTE_OCTAVE * i); - float pitch = - tuning.GetPitch(note) * (0.01 * (i - 1)); // Detune just a touch + float pitch = tuning.GetPitch(note) * (0.01 * (i - 1)); // Detune just a touch Drones[i].LoadPatch(&Bank[0]); Drones[i].NoteOn(pitch); } @@ -187,25 +183,22 @@ void setup() { } void loop() { + static bool forceDisplayUpdate = true; + pipe.Update(); - diag("loop %d", millis()); #if defined(ADAFRUIT_TRELLIS_M4_EXPRESS) trellis.tick(); - - trellis.setPixelColor(1, trellis.ColorHSV(millis(), 255, 120)); - { - uint16_t color = trellis.ColorHSV(64 * pipe.kneeClosedness, 255, 120); - trellis.setPixelColor(0, color); - } #endif // If we're infinitely (for the sensor) off the knee, // go into setup mode! if (pipe.kneeClosedness == 0) { doSetup(); + forceDisplayUpdate = true; } else { - doPlay(); + doPlay(forceDisplayUpdate); + forceDisplayUpdate = false; } } @@ -225,12 +218,21 @@ void loop() { * */ void doSetup() { + static unsigned long quietUntil = 0; + + // Stuff can set quietUntil to stop responding to keys for a bit +#define quiet() quietUntil = millis() + 200 + if (millis() < quietUntil) { + return; + } + display.clearDisplay(); bool alt = pipe.Pressed(7); // Draw indicator bar display.fillRect(126, 0, 2, 32, SSD1306_WHITE); + display.setFont(0); display.setTextSize(1); display.setCursor(0, 0); @@ -238,7 +240,7 @@ void doSetup() { // Show settings for each of Chanter, Regulators, Drones for (int i = 0; i < 3; i++) { int p = patch[i]; - int16_t x = 0; + int16_t x = 1; int16_t y = i * 8; display.setCursor(x, y); @@ -265,31 +267,71 @@ void doSetup() { display.print(Bank[p].name); } } else { - if (pipe.Pressed(6)) { - float freq = PITCH_CONCERT_D4 + pitchAdjust; - Note note = NearestNote(freq); - display.setCursor(0, 0); + if (pipe.Pressed(6)) { + TuningSystem system = tuning.GetTuningSystem(); + float freq = tuning.GetPitch(NOTE_D4); + Note note = NearestNote(freq); + int octave = int(note) / 12; + + if (pipe.JustPressed(3) || pipe.JustPressed(2)) { + tuning.Setup(NOTE_A4, PITCH_CONCERT_A4, TUNINGSYSTEM_EQUAL); + if (pipe.Pressed(3) && pipe.Pressed(2)) { + ++system; + } else if (pipe.Pressed(3)) { + freq = tuning.GetPitch(++note); + } else if (pipe.Pressed(2)) { + freq = tuning.GetPitch(--note); + } + tuning.Setup(NOTE_D4, freq, system); + } + + if (pipe.Pressed(1) || pipe.Pressed(0)) { + if (pipe.Pressed(1) && pipe.Pressed(0)) { + freq = PITCH_CONCERT_D4; + quiet(); + } else if (pipe.Pressed(1)) { + freq *= 1.001; + } else if (pipe.Pressed(0)) { + freq /= 1.001; + } + tuning.Setup(NOTE_D4, freq); + note = NearestNote(freq); + } + + display.setFont(&FreeSans9pt7b); + display.setCursor(0, 12); display.print(NoteName(note)); - display.setCursor(6 + 6, 0); + display.print(octave); + display.setCursor(48, 12); display.print(freq); + + display.setCursor(0, 27); + display.print(TuningSystemName(system)); } else if (pipe.Pressed(5)) { display.print("fn2"); } else if (pipe.Pressed(4)) { display.print("fn3"); } else { - display.setCursor(56, 8); - display.setTextSize(2); + display.setFont(&FreeSans9pt7b); + display.setCursor(64, 18); display.print("Setup"); + + display.setFont(); + display.setTextSize(1); + display.setCursor(0, 16); + display.print("build"); + display.setCursor(0, 24); + display.print(buildDate); } } display.display(); } -void doPlay() { +void doPlay(bool forceUpdate) { static uint8_t last_note = 0; - bool updateDisplay = false; + bool updateDisplay = forceUpdate; if (pipe.silent) { Chanter.NoteOff(); @@ -332,19 +374,16 @@ void doPlay() { if (updateDisplay) { display.clearDisplay(); + display.setFont(&FreeSans9pt7b); + + if (Chanter.patch) { + display.setCursor(0, 32); + display.print(Chanter.patch->name); + } display.setCursor(0, 16); - display.setTextSize(2); - display.print(Chanter.patch->name); - - display.setCursor(0, 0); - display.setTextSize(2); display.print(note_name); - display.setCursor(40, 0); - display.setTextSize(1); - display.print(pipe.kneeClosedness); - display.display(); last_note = pipe.note; }