Code-Outlet von Andreas Bahr, Zürich
Blinksy ist eine Beatbox-Erweiterung für die von Jonas Guggenheim & den Sporthorses geschaffene "Guggenheim Box", eine Telefonkabinen-artige Installation mit Lichtschranken. Vereinfacht formuliert erlaubt die Box und ihre Erweiterungen, einen Modul-Synthsizer zu "spielen".
The Sporthoses sind eine Live-Performance-Band deren Auftritte nicht nur die Ohren, sondern auch die Augen erfreuen.
Kern ist ein Arduino Nano. Über einen Drehschalter mit 4 Positionen können 4 verschiedene Pattern ausgewählt werden, die maximal 4 LED-Lampen direkt ansteuern. Diese triggern die Lichtschranken anstelle eines Sporthorse-Musikers.
In der ersten Version der Hardware nach meinem Entwurf waren 2 kräftige LEDs pro Kanal vorgesehen, meine Idee war, mehr LED = mehr Licht = weniger Anfällig auf Streulicht. Die Musiker haben aber Dank ihrer Erfahrung im Instrumentenbau eine bessere Lösung gefunden: der Einsatz von einfachen Optiken. So konnten mit einzelnen LEDs perfekte Resultate erzielt werden.
In der Arduino-Welt muss mit C/C++ programmiert werden. Ich mag C nicht, muss aber zugeben, es ist wesentlich effizienter damit zu arbeiten, als mit Maschinensprache/Assembler...
Die aktuelle Version erlaubt das Task-Synchrone umschalten der Pattern während der Live-Performance. Die Pattern selbst sind im Header des Scripts definiert.
Die hier angebotene Ino-Datei können Sie mit der Arduino-IDE verwenden. Sie lässt sich aber auch mit einem normalen Text-Editor lesen.
Die etwas einfachere erste Version eignet sich sehr gut um die Funktionsweise der Software zu erkennen: Der HW-Timer des Prozessors wird verwendet für das korrekte Timing, er meldet via einen Interupt das vergehen der Zeit zurück an die Software...
/* blinksy v0.4
* music sequencer with lights
* for the Guggenheim-Box
* copyleft 2017 button3 & the sporthorses
* -----------------------------------------
* sequencer plays a note by switching
* a light off for some milliseconds.
* in the musical setup, write any character
* for a note, leave blank otherwise.
* -----------------------------------------
* 4 sets with 4 tracks on 4 led-lamps
* BPM values from 20 to 180
*/
// MUSIC SETUP ==========================================
// SET 1 ------------------------------------------------
int set1bpm = 40;
// TICK RULER "1...2...3...4...5...6...7...8..."
char set1track1[33] = "x x x x ";
char set1track2[33] = " x x x x ";
char set1track3[33] = " ";
char set1track4[33] = " ";
// SET 2 ------------------------------------------------
int set2bpm = 80;
// TICK RULER "1...2...3...4...5...6...7...8..."
char set2track1[33] = "x x x x ";
char set2track2[33] = "x x x x x x x x ";
char set2track3[33] = " x x x x ";
char set2track4[33] = " ";
// SET 3 ------------------------------------------------
int set3bpm = 60;
// TICK RULER "1...2...3...4...5...6...7...8..."
char set3track1[33] = " ";
char set3track2[33] = " ";
char set3track3[33] = " ";
char set3track4[33] = " ";
// SET 4 ------------------------------------------------
int set4bpm = 60;
// TICK RULER "1...2...3...4...5...6...7...8..."
char set4track1[33] = " ";
char set4track2[33] = " ";
char set4track3[33] = " ";
char set4track4[33] = " ";
// ======================================================
// internal setup
volatile int loopCount = 0;
const unsigned long CLOCK_FREQ = 250000;
unsigned int impulseMiliSec = 10;
unsigned int compareON = 20000;
unsigned int compareOFF = 10000;
int bpm = 60;
char track1[33];
char track2[33];
char track3[33];
char track4[33];
// system initialisation
void setup() {
Serial.begin(9600);
noInterrupts();
// BPM flash
pinMode(LED_BUILTIN, OUTPUT); // on-board led
// 4 lights
pinMode(12, OUTPUT); // light 1
pinMode(11, OUTPUT); // light 2
pinMode(10, OUTPUT); // light 3
pinMode(9, OUTPUT); // light 4
digitalWrite(12, HIGH); // switch them OFF
digitalWrite(11, HIGH);
digitalWrite(10, HIGH);
digitalWrite(9, HIGH);
// set-switch
pinMode(5, INPUT_PULLUP); // prg 1
pinMode(6, INPUT_PULLUP); // prg 2
pinMode(7, INPUT_PULLUP); // prg 3
pinMode(8, INPUT_PULLUP); // prg 4
// load music data
if(digitalRead(8) == LOW) {
bpm = set4bpm;
strcpy(track1, set4track1);
strcpy(track2, set4track2);
strcpy(track3, set4track3);
strcpy(track4, set4track4);
} else {
if(digitalRead(7) == LOW) {
bpm = set3bpm;
strcpy(track1, set3track1);
strcpy(track2, set3track2);
strcpy(track3, set3track3);
strcpy(track4, set3track4);
} else {
if(digitalRead(6) == LOW) {
bpm = set2bpm;
strcpy(track1, set2track1);
strcpy(track2, set2track2);
strcpy(track3, set2track3);
strcpy(track4, set2track4);
} else {
bpm = set1bpm;
strcpy(track1, set1track1);
strcpy(track2, set1track2);
strcpy(track3, set1track3);
strcpy(track4, set1track4);
}
}
}
// calculate timer-compare-values
compareON = (CLOCK_FREQ / (bpm * 32 / 60)) - 1;
compareOFF = (int)(((float)impulseMiliSec / 1000) / (1 / (float)CLOCK_FREQ) - 1);
// timer 1 setup
TCCR1A = 0;
TCCR1B = 0;
OCR1A = compareON;
OCR1B = compareOFF;
TCCR1B |= _BV(CS10); // clock ON
TCCR1B |= _BV(CS11); // prescale 64
TCCR1B |= _BV(WGM12); // mode: ctc
TIMSK1 |= _BV(OCIE1A); // enable interrupt
TIMSK1 |= _BV(OCIE1B); // enable interrupt
interrupts();
}
// impulse ON
ISR(TIMER1_COMPA_vect) {
// metronome BPM flash
if(loopCount == 0) digitalWrite(LED_BUILTIN, HIGH);
else digitalWrite(LED_BUILTIN, LOW);
// play sequence
if(track1[loopCount] != ' ') digitalWrite(12, HIGH);
if(track2[loopCount] != ' ') digitalWrite(11, HIGH);
if(track3[loopCount] != ' ') digitalWrite(10, HIGH);
if(track4[loopCount] != ' ') digitalWrite(9, HIGH);
// ticks gear
if(loopCount == 31) loopCount = 0;
else loopCount++;
}
// impulse OFF
ISR(TIMER1_COMPB_vect) {
digitalWrite(12, LOW);
digitalWrite(11, LOW);
digitalWrite(10, LOW);
digitalWrite(9, LOW);
}
// status infos
void loop() {
Serial.println(bpm);
Serial.println(compareON);
Serial.println(compareOFF);
Serial.println("---------");
delay(1000);
}