Skip to content

Instantly share code, notes, and snippets.

@wware
Last active September 3, 2016 17:28
Show Gist options
  • Save wware/f52f30df10634ae30f78a1ce4b4af407 to your computer and use it in GitHub Desktop.
Save wware/f52f30df10634ae30f78a1ce4b4af407 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
import os
import re
import time
import subprocess
os.system("ps ax | grep fluidsynth | cut -c -6 | xargs kill -9")
devnull = os.open('/dev/null', os.O_WRONLY)
cmd = [
'fluidsynth',
'--server',
'--no-shell',
'--audio-driver=alsa',
'-o', 'audio.alsa.device=hw:1,0',
'/usr/share/sounds/sf2/FluidR3_GM.sf2',
]
print ' '.join(cmd)
p = subprocess.Popen(
cmd,
shell=False,
stdout=devnull,
stderr=devnull
)
fluid_pid = p.pid
# We might want to kill this process later.
open(os.environ['HOME'] + '/fluidsynth.pid', 'w').write('%d' % fluid_pid)
time.sleep(1)
R = os.popen('aconnect -l').read()
r2 = re.compile(r"client (\d+): 'FLUID Synth").search(R)
assert r2
fluid_num = r2.group(1)
connected = False
while True:
time.sleep(1)
R = os.popen('aconnect -l').read()
r1 = re.compile(r"client (\d+): 'Teensy MIDI'").search(R)
if not r1:
connected = False
continue
if connected:
continue
connect_cmd = [
'aconnect',
r1.group(1),
r2.group(1)
]
print ' '.join(connect_cmd)
subprocess.Popen(
connect_cmd,
shell=False,
stdout=devnull,
stderr=devnull
)
connected = True
#define _KEYBOARD 0
#define NUM_KEYS 40
#define DIAGNOSTICS 0
#define KEYDOWN_HYSTERESIS 10
#define PIANO 0
#define CELESTA 10
#define TUBULAR_BELLS 14
#define CHURCH_ORGAN 19
#define NYLON_GUITAR 24
#define CELLO 42
#define ORCHESTRAL_HARP 46
#define CHOIR_AAHS 52
#define TRUMPET 56
#define SYNTH_BRASS 62
#define ALTO_SAX 65
#define FLUTE 73
#define INH0 2
#define INH1 14
#define INH2 7
#define INH3 6
#define INH4 8
uint32_t inhibits[] = {INH0, INH1, INH2, INH3, INH4};
#define A_SELECT 15
#define B_SELECT 22
#define C_SELECT 23
// Port A, Port C, Port D
uint32_t port_settings[2 * NUM_KEYS];
#define PORT_A 0x400FF000
#define PORT_C 0x400FF080
#define PORT_D 0x400FF0C0
static inline uint32_t read_key(uint32_t X, uint32_t Y, uint32_t portc) {
// offset 4 is set output
// offset 8 is clear output
// offset 16 is read input
asm volatile(
// digitalWrite(9, LOW);
"mov %0, #0x08" "\n"
"str %0, [%2, #8]" "\n"
// X = 20;
"mov %0, #20" "\n"
// while (X > 0) X--;
"1:" "\n"
"subs %0, %0, #1" "\n"
"bne 1b" "\n"
// digitalWrite(9, HIGH);
"mov %0, #0x08" "\n"
"str %0, [%2, #4]" "\n"
// while (Y > 0) Y--;
"2:" "\n"
"subs %1, %1, #1" "\n"
"bne 2b" "\n"
// X = digitalReadFast(10);
"ldr %0, [%2, #16]" "\n"
"ands %0, %0, #0x10" "\n"
// digitalWrite(9, LOW);
"mov %1, #0x08" "\n"
"str %1, [%2, #8]" "\n"
: "+r" (X), "+r" (Y), "+r" (portc)
);
return !X;
}
int h = 0;
class Key
{
public:
uint32_t threshold;
uint32_t successive_approximate(uint32_t lo, uint32_t hi) {
if (hi < lo) return successive_approximate(hi, lo);
if (hi - lo <= 1) return hi;
uint32_t mid = (lo + hi) >> 1;
if (!read_n(mid))
return successive_approximate(lo, mid);
else
return successive_approximate(mid, hi);
}
bool read_n(uint32_t n) {
digitalWrite(A_SELECT, (id & 1) ? HIGH : LOW);
digitalWrite(B_SELECT, (id & 2) ? HIGH : LOW);
digitalWrite(C_SELECT, (id & 4) ? HIGH : LOW);
int i;
for (i = 0; i < 5; i++) {
if (i == (id >> 3))
digitalWrite(inhibits[i], LOW);
else
digitalWrite(inhibits[i], HIGH);
}
return read_key(0, n, PORT_C);
}
/**
* A numerical index of this key, used to identify it for keyboard scanning.
*/
uint32_t id;
/**
* 1 if the key is pressed/touched, 0 otherwise.
*/
uint32_t state;
/**
* This counter is used for hysteresis (debouncing).
*/
uint32_t count;
/**
* An integer, increments for each half-tone in pitch.
*/
int8_t pitch;
Key(uint32_t _id) {
id = _id;
count = state = 0;
}
int fresh_calibrate(void) {
threshold = 0;
return calibrate();
}
int calibrate(void) {
const int _max = 250;
int previous = 0, n;
for (n = 1; n < _max; n <<=1) {
if (!read_n(n)) {
threshold =
max(threshold, successive_approximate(previous, n) + 1);
return threshold;
}
previous = n;
}
threshold = max(threshold, _max); // out of luck, probably
return threshold;
}
/**
* Detect whether a key is being pressed or touched. This is a single
* detection prior to any debouncing logic.
*/
bool read(void) {
return read_n(threshold);
}
/**
* Checks to see if this key is being pressed/touched. Debounces using a
* hysteresis state machine.
*/
void check(void) {
if (read()) {
if (state) {
count = 0;
} else {
if (count < KEYDOWN_HYSTERESIS) {
count++;
if (count == KEYDOWN_HYSTERESIS) {
state = 1;
count = 0;
keydown();
}
}
}
} else {
if (!state) {
count = 0;
} else {
if (count < KEYDOWN_HYSTERESIS) {
count++;
if (count == KEYDOWN_HYSTERESIS) {
state = 0;
count = 0;
keyup();
}
}
}
}
}
virtual void keydown(void) {
usbMIDI.sendNoteOn(pitch, 127, 1);
}
virtual void keyup(void) {
usbMIDI.sendNoteOff(pitch, 0, 1);
}
};
Key *keyboard[NUM_KEYS];
class FunctionKey : public Key
{
void keyup(void) { /* nada */ }
void keydown(void) {
switch (id) {
case 4:
break;
default:
break;
}
}
};
void setup() {
uint8_t j;
pinMode(LED_BUILTIN, OUTPUT);
#if _KEYBOARD
uint8_t i;
usbMIDI.sendProgramChange(CHOIR_AAHS, 1);
pinMode(10, INPUT_PULLUP);
pinMode(9, OUTPUT);
digitalWrite(9, LOW);
pinMode(2, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(14, OUTPUT);
pinMode(15, OUTPUT);
pinMode(22, OUTPUT);
pinMode(23, OUTPUT);
for (i = 0; i < NUM_KEYS; i++) {
keyboard[i] = new Key(i);
keyboard[i]->pitch = 60 + i;
}
for (i = 0; i < NUM_KEYS; i++) {
keyboard[i]->fresh_calibrate();
}
for (j = 0; j < 3; j++) {
for (i = 0; i < NUM_KEYS; i++) {
keyboard[i]->calibrate();
}
}
#endif
}
uint8_t scanned_key = 0;
int chords[4][6] = {
{ 60, 64, 67, 70, 72, 76 }, // C major with B flat
{ 60, 65, 69, 72, 77, 81 }, // F major
{ 62, 65, 67, 71, 74, 77 }, // G major with F
{ 60, 64, 67, 72, 76, 79 } // C major
};
void loop(void) {
static uint8_t led_on = 0;
int i;
static int j = 0;
if (scanned_key == 0) {
// toggle the LED
digitalWrite(LED_BUILTIN, led_on);
led_on = !led_on;
}
#if _KEYBOARD
keyboard[scanned_key]->check();
scanned_key = (scanned_key + 1) % NUM_KEYS;
#else
delayMicroseconds(500000);
switch (j) {
case 0:
usbMIDI.sendProgramChange(NYLON_GUITAR, 1);
break;
case 1:
usbMIDI.sendProgramChange(CELESTA, 1);
break;
case 2:
usbMIDI.sendProgramChange(TRUMPET, 1);
break;
case 3:
usbMIDI.sendProgramChange(FLUTE, 1);
break;
}
for (i = 0; i < 6; i++) {
usbMIDI.sendNoteOn(chords[j][i], 127, 1);
delayMicroseconds(100000);
}
delayMicroseconds(50000);
for (i = 0; i < 6; i++) {
usbMIDI.sendNoteOff(chords[j][i], 0, 1);
delayMicroseconds(100000);
}
j = (j + 1) % 4;
if (j == 0) delayMicroseconds(500000);
#endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment