Electromagnetic Monochords

La ley de los movimientos en el cosmos es la misma que la de los sonidos en la escala musical [...] tal es la tesis original del pitagorismo. – José Vasconcelos

Fig. 1. Playing the Monokords

Abstract

Los monocordios electromagnéticos son un instrumento capaz de demostrar el fenómeno de los armónicos naturales en la vibración de una cuerda, just a knob turn away from each other! ...Wow!

Discussion

Cuatro cuerdas tensadas son excitadas con un electroimán de 5V que oscila en frecuencias simpáticas a la frecuencia fundamental de la cuerda. El intérprete navega un rango electromagnético en busca de armónicos naturales, propios de la vibración de la cuerda.

Acompañando las cuerdas tensadas, los electroimanes y pastillas de guitarra eléctrica, diseñamos un control box; un control center para mandar señales eléctricas a los imanes –así ofreciendo una interface análoga divertida e intuitiva de usar.

Fig. 2. Innards

La caja de control está equipada con dos diferentes presets: "free" [continuous integers] y "harmonic" [multiples of a fundamental]. Dependiendo del humor del intérprete: navegará el espacio armónico de manera controlada, o de manera libre? También integrados en la caja hay un on/off switch for each electroimán, y control de volumen y tono de las pastillas.

Un laboratorio de armónicos, capaz de generar un canto polifónico entre las resonancias naturales de cuatro cuerdas. Con esta herramienta, el armónico pasa de ser concepto, o número, a resonancia en un cuerpo.

Source Code & Specifications

En la versión actual, usamos un (1) Arduino Nano™ para controlar dos (2) monocordios. (O, con más precisión, se usa un Arduino para controlar the rate at which two electromagnets are cycling on & off, which in turn sympathetically vibrate the respective chords under which they are mounted.)

Below, you'll find the current source code.


/*
DUAL MONOCHORD CODE v3 :: 24 IV 14
by Dra. 8000
*/

#include <Encoder.h>
#include <Tone.h>
#include <TM1637Display.h>

// for best performance tie the first pin of each encoder
// to an interrupt pin
// [ nano only has interrupts on pins 2 & 3 ]   
// so we'll give one to each encoder
Encoder enc1(2, 4);
Encoder enc2(3, 5);

// ENCODER VARS
long enc1Old  = -999;
long enc2Old = -999;
long enc1Curr;
long enc2Curr;
long encOffset = 79;
long encSteps = 4;

TM1637Display display1(6, 7); // clk, dio
TM1637Display display2(11, 12); //clk, dio
uint8_t blank[] = { 0x00, 0x00, 0x00, 0x00 };

Tone tone1;
Tone tone2;

// power switches
const int sPow1 = A0;
const int sPow2 = A1;

// rotary function switches
const int sFun1 = A2;
const int sFun2 = A3;

// pots
const int potPin1 = A6;
const int potPin2 = A7;
int pot1 = 0;
int pot2 = 0;

// set fundamentals
const int f1 = 82;
const int f2 = 82;

// frequencies
long freq1;
long freq2;

// track rot vs pot
bool chaos1 = false;
bool chaos2 = false;

int hStep1 = 1;
int hStep2 = 1;

void setup() {
Serial.begin(9600);
// init tones
tone1.begin(10);
tone2.begin(9);
// init power switches
pinMode(sPow1, INPUT);
pinMode(sPow2, INPUT);
digitalWrite(sPow1, HIGH);
digitalWrite(sPow2, HIGH);
// init function switches [HIGH = rotary]
pinMode(sFun1, INPUT);
pinMode(sFun2, INPUT);
digitalWrite(sFun1, HIGH);
digitalWrite(sFun2, HIGH);
// init frequencies to fundamental
freq1 = f1;
freq2 = f2;
enc1.write(freq1 * encSteps);
enc2.write(freq2 * encSteps);
}

void loop() {
if (digitalRead(sPow1) == LOW) {
    // Serial.println("on");
    updateDisplay(display1, freq1);
    if (digitalRead(sFun1) == LOW) {
    potFreq1();
    chaos1 = true;
    } else {
    rotFreq1();
    chaos1 = false;
    }
    // send frequency to tone
    tone1.play(freq1);
} else {
    blankDisplay(display1);
    tone1.stop();
}
if (digitalRead(sPow2) == LOW) {
    updateDisplay(display2, freq2);
    if (digitalRead(sFun2) == LOW) {
    potFreq2();
    chaos2 = true;
    } else {
    rotFreq2();
    chaos2 = false;
    }
    // send frequency to tone
    tone2.play(freq2);
} else {
    blankDisplay(display2);
    tone2.stop();
}
}

void potFreq1() {
// freq1 = random(82, 328);

// first time (back) in pot mode
if (chaos1 == false) {
    Serial.println("(back) in pot mode");
    // set enc to last harmonic step
    enc1.write(hStep1);
}
if (getEncPosition(enc1) != hStep1) {
    hStep1 = getEncPosition(enc1);
}
// map pot value between 0 - fundamental
pot1 = map(analogRead(potPin1), 0, 1023, 0, f1);
// scale pot value by harmonic step
pot1 += f1 * hStep1;
freq1 = pot1;
updateDisplay(display1, freq1);
}

void potFreq2() {
// first time (back) in pot mode
if (chaos2 == false) {
    // set enc to last harmonic step
    enc2.write(hStep2);
}
if (getEncPosition(enc2) != hStep2) {
    hStep2 = getEncPosition(enc2);
}
// map pot value between 0 - fundamental
pot2 = map(analogRead(potPin2), 0, 1023, 0, f2);
// scale pot value by harmonic step
pot2 += f2 * hStep2;
freq2 = pot2;
updateDisplay(display2, freq2);
}

void rotFreq1() {
enc1Curr = getEncPosition(enc1);
// reset encoder to current freq if switiching back to Hz-by-Hz mode
if (chaos1 == true) {
    enc1Curr = freq1;
    enc1.write(freq1 * encSteps);
} else if (enc1Curr != freq1) {
    freq1 = enc1Curr;
}
updateDisplay(display1, freq1);
}

void rotFreq2() {
enc2Curr = getEncPosition(enc2);
// reset encoder to current freq if switiching back to Hz-by-Hz mode
if (chaos2 == true) {
    enc2Curr = freq2;
    enc2.write(freq2 * encSteps);
} else if (enc2Curr != freq2) {
    freq2 = enc2Curr;
}
updateDisplay(display2, freq2);
}

long getEncPosition(Encoder enc) {
// scale for 4 steps/detent encoder
long val = enc.read()/encSteps;
// lowest possible value = 1
if (val < 1) {
    val = 1;
    enc.write(1);
}
return val;
}

void updateDisplay(TM1637Display display, long val) {
int nDigits = floor(log10(abs(val))) + 1;
uint8_t disp[] = { 0x00, 0x00, 0x00, 0x00 };
for (int i = 0; i < nDigits; i++) {
    // get current encoder value digit-by-digit
    char cdigit = nthdigit(val, i);
    int digit = cdigit - '0';
    // encode each digit for display
    disp[3-i] = display.encodeDigit(digit);
}
display.setBrightness(1);
display.setSegments(disp);
}

char nthdigit(int x, int n) {
    static int powersof10[] = {1, 10, 100, 1000, 10000, 100000, 1000000};
    return ((x / powersof10[n]) % 10) + '0';
}

void blankDisplay(TM1637Display display) {
uint8_t disp[] = { 0x00, 0x00, 0x00, 0x00 };  
display.setSegments(disp);
}