Das “RA”-Instrument

Der Name “RA” ist eine Remineszenz an Sun Ra einem der ersten Musiker der den Einsatz von Live-Elektronik im Kontext improvisierter Musik intensiv ausgelotet hat und ausserdem ein grosser Komponist, Philosoph, Poet und nicht zuletzt Widerstandskämpfer gegen die Rassendiskriminierung.

Das “RA”-Instrument ist eine Verknüpfung  von manuell steuerbaren Drucksensoren sowie einem Blasdrucksensor mit Klangsynthese-Algorithmen. Das Ziel ist die sensible und subtile Steuerung von elektronischem Klang – weniger das Spielen von traditioneller Melodie und Harmonie.

Nach Experimenten mit verschiedenen Formfaktoren kam ich zu einem Instrument auf Basis des Teensy Controllers mit Audio-Interface und intergrierter Tonerzeugung (unteres in der Abbildung).

Allerdings brachte mich die Schwierigkeit und Umständlichkeit der Audioprogrammierung in C ohne Gleitkommazahlen wieder von der Idee einer Eingebauten Tonerzeugung ab und ich entwickelte und baute den “RA”-Controller als momentan finales Modell der “RA”-Serie:

Schaltungstechnische Details zum Anschluss der Sensoren:

  • R 2.2k
  • Drucksensoren der FSR400 Serie – je nach gewünschtem Format

  • R1 2k
  • R2 4k

Als Mikrocontroller kam auch hier der Teensi 3.2 zum Einsatz – hier der Arduino-Quellcode:

#define TSR_MIN 40.0
#define TSR_MAX 2600.0

#define BREATH_MIN 20.0
#define BREATH_MAX 720.0

#define MAXVALUE 16383
#define ADC_Resolution 12

int cont[16]; 
unsigned int OldTSR[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

float breath0;
unsigned int oldBreath, oldSuck;

elapsedMillis msec = 0;
void setup() {
 analogReadResolution(ADC_Resolution);

breath0 = analogRead(A17);
}

void loop() {
 float tmp; 
 unsigned int tmpUnsigned;
 int i, brthTmp;

if (msec >= 4) {
 msec = 0;

/* TSR */ 
 cont[0] = analogRead(A16);
 cont[1] = analogRead(A19);
 cont[2] = analogRead(A13);
 cont[3] = analogRead(A8);
 
 cont[4] = analogRead(A15);
 cont[5] = analogRead(A18);
 cont[6] = analogRead(A20);
 cont[7] = analogRead(A9);
 
 cont[8] = analogRead(A7);
 cont[9] = analogRead(A6);
 cont[10] = analogRead(A5);
 cont[11] = analogRead(A4);
 
 cont[12] = analogRead(A3);
 cont[13] = analogRead(A2);
 cont[14] = analogRead(A1);
 cont[15] = analogRead(A0);

for( i = 0; i < 16; i++ ) {
 tmp = ( max( min( cont[i], TSR_MAX ), TSR_MIN) - TSR_MIN ) / ( TSR_MAX - TSR_MIN );
 tmp *= tmp;
 tmpUnsigned = (unsigned int)(tmp * 16383.);

if(tmpUnsigned != OldTSR[i]) {
 usbMIDI.sendControlChange(i + 53, (tmpUnsigned << 9) >> 9, 7);
 usbMIDI.sendControlChange(i + 3, tmpUnsigned >> 7, 7);
 if(OldTSR[i] == 0) {
 usbMIDI.sendNoteOn(i + 3, 99, 7);
 }
 if(tmpUnsigned == 0) {
 usbMIDI.sendNoteOff(i + 3, 99, 7);
 }
 }
 
 OldTSR[i] = tmpUnsigned;
 }

/* breath */
 brthTmp = analogRead(A17);
 tmp = max (((float)(brthTmp-BREATH_MIN) - breath0), 0);
 tmp = (min(tmp, BREATH_MAX)) / BREATH_MAX;
 tmpUnsigned = (unsigned int)((tmp*tmp) * 16383.);

if(tmpUnsigned!=oldBreath) {
 usbMIDI.sendControlChange(51, (tmpUnsigned << 9) >> 9, 1);
 usbMIDI.sendControlChange(1, tmpUnsigned >> 7, 1);

if( oldBreath == 0) { usbMIDI.sendNoteOn(1, 99, 1); }
 if( tmpUnsigned == 0) { usbMIDI.sendNoteOff(1, 99, 1); }
 
 oldBreath = tmpUnsigned;
 }

tmp = abs( min (((float)(brthTmp+BREATH_MIN) - breath0), 0) );
 tmp = (min(tmp, BREATH_MAX)) / BREATH_MAX;
 tmpUnsigned = (unsigned int)((tmp*tmp) * 16383.);

if(tmpUnsigned!=oldSuck) {
 usbMIDI.sendControlChange(52, (tmpUnsigned << 9) >> 9, 1);
 usbMIDI.sendControlChange(2, tmpUnsigned >> 7, 1);

if( oldSuck == 0) { usbMIDI.sendNoteOn(2, 99, 1); }
 if( tmpUnsigned == 0) { usbMIDI.sendNoteOff(2, 99, 1); }
 
 oldSuck = tmpUnsigned;
 }
 
 } 
 while (usbMIDI.read()) { }
}

Die Klangerzeugung realisierte ich innerhalb von MAX/MSP mit dem csound~ -Objekt. Hier zwei beispielhafte csound-patches:

<CsoundSynthesizer>
<CsOptions>
-+rtmidi=null -M0
</CsOptions>
<CsInstruments>

#include "common.orc"

schedule(100, 0, -1)

instr 1
endin

instr 100

aPitchMod = randh(raTSR(5, 24, 30), 29)
 aPitchMod += randi(raTSR(6, 24, 30), 167)
 aPitchMod += oscili(raTSR(7, 24, 30), 25, giSINUS)
 aPitchMod += vco(raTSR(8, 2, 30), 19, 2, 0.5, giSINUS)
 aPitchMod += raTSRbi(1, 2, 36, 60)
 
 aFreq = 1000 * intv2mul( aPitchMod )

aPM = oscili(raTSR(9, 2, 30), aFreq * 0.345, giSINUS)
 aPM += oscili(raTSR(10, 2, 30), aFreq * 0.045, giSINUS)
 aPM += oscili(raTSR(11, 2, 30), aFreq * 0.164, giSINUS)
 aPM += oscili(raTSR(12, 2, 30), aFreq * 1.045, giSINUS)
 
 as0, as1, as2, as3, as4, as5, as6, as7, as8, as9, as10, as11, as12, as13, as14, as15 raTSR4to16 13, 14, 15, 16, 30

aSig = as0 * tablei:a(phasor:a(aFreq) + aPM, giSINUS, 1, 0, 1) 
 aSig += as1 * tablei:a(phasor:a(aFreq * 1.234) + aPM, giSINUS, 1, 0, 1)
 aSig += as2 * tablei:a(phasor:a(aFreq * 1.345) + aPM, giSINUS, 1, 0, 1)
 aSig += as3 * tablei:a(phasor:a(aFreq * 1.456) + aPM, giSINUS, 1, 0, 1)
 aSig += as4 * tablei:a(phasor:a(aFreq * 0.1) + aPM, giSINUS, 1, 0, 1)
 aSig += as5 * tablei:a(phasor:a(aFreq * 1.04) + aPM, giSINUS, 1, 0, 1)
 aSig += as6 * tablei:a(phasor:a(aFreq * 2) + aPM, giSINUS, 1, 0, 1)
 aSig += as7 * tablei:a(phasor:a(aFreq * 0.333) + aPM, giSINUS, 1, 0, 1) 
 aSig += as8 * tablei:a(phasor:a(aFreq * 0.444) + aPM, giSINUS, 1, 0, 1)
 aSig += as9 * tablei:a(phasor:a(aFreq * 0.555) + aPM, giSINUS, 1, 0, 1)
 aSig += as10 * tablei:a(phasor:a(aFreq * 0.666) + aPM, giSINUS, 1, 0, 1)
 aSig += as11 * tablei:a(phasor:a(aFreq * 0.777) + aPM, giSINUS, 1, 0, 1)
 aSig += as12 * tablei:a(phasor:a(aFreq * 0.888) + aPM, giSINUS, 1, 0, 1)
 aSig += as13 * tablei:a(phasor:a(aFreq * 5) + aPM, giSINUS, 1, 0, 1)
 aSig += as14 * tablei:a(phasor:a(aFreq * 6) + aPM, giSINUS, 1, 0, 1)
 aSig += as15 * tablei:a(phasor:a(aFreq * 7) + aPM, giSINUS, 1, 0, 1)
 
 aAmpMod = 1
 aAmpMod += randh(raTSR(3, 1, 30), 100)
 aAmpMod += randi(raTSR(4, 1, 30), 3000)

aSig = tanh(aSig * raBC(1, 60) * 8 * aAmpMod) * 0.5

;aSigRL, aSigRR convolutionreverb (aSigL+aSigR) * 0.001, "white3000ms.aiff"
 ;outs(aSigRL+aSigL, aSigRR+aSigR)

outs(aSig, aSig)

endin

</CsInstruments>
<CsScore>
</CsScore>
</CsoundSynthesizer>
<CsoundSynthesizer>
<CsOptions>
</CsOptions>
<CsInstruments>

#include "common.orc"

schedule(100, 0, -1)

instr 1
endin

instr 100

aPitchMod = randh(raTSR(5, 40, 30), 79)
 aPitchMod += randi(raTSR(6, 40, 30), 467)
 aPitchMod += oscili(raTSR(7, 40, 30), 250, giSINUS)
 aPitchMod += vco(raTSR(8, 40, 30), 19, 2, 0.5, giSINUS)
 aPitchMod += raTSRbi(1, 2, 62, 60)
 
 aFreq = 400 * intv2mul( aPitchMod )

ashape = 2 / intv2mul(raTSR(3, 80, 60))

aSigL, aSigR sinoise2 aFreq, ashape , 0.2, 0.1, 0.3, 0.4

aSigL, aSigR mono aSigL, aSigR, raTSR(4, 1, 30)

iAMBase = 100
 aAmpMod = 1
 aAmpMod += oscil(raTSR(9, 1, 30), iAMBase * 10, giSINUS)
 aAmpMod += oscil(raTSR(10, 1, 30), iAMBase * 18.70, giSINUS)
 aAmpMod += oscil(raTSR(11, 1, 30), iAMBase * 34.00, giSINUS)
 aAmpMod += oscil(raTSR(12, 1, 30), iAMBase * 28.70, giSINUS)
 aAmpMod += oscil(raTSR(13, 1, 30), iAMBase * 120.00, giSINUS)
 aAmpMod += oscil(raTSR(14, 1, 30), iAMBase * 108.70, giSINUS)
 aAmpMod += oscil(raTSR(15, 1, 30), iAMBase * 0.18, giSINUS)
 aAmpMod += oscil(raTSR(16, 1, 30), iAMBase * 1.08, giSINUS)

aBreath = raBC(1, 60) 
 aSigL = tanh(aSigL * aBreath * 16 * aAmpMod) * 0.5
 aSigR = tanh(aSigR * aBreath * 16 * aAmpMod) * 0.5

;aSigRL, aSigRR convolutionreverb (aSigL+aSigR) * 0.001, "white3000ms.aiff"
 ;outs(aSigRL+aSigL, aSigRR+aSigR)

outs(aSigR, aSigL)

endin

</CsInstruments>
<CsScore>
</CsScore>
</CsoundSynthesizer>

und die datei common.orc

common.orc

Ich habe das “RA” in einigen Konzerten eingesetzt und werde dies weiter tun. Die Chance eines Instruments das zwar die Sensibilität nicht aber die vorgegebene (12-tönig temperierte) Struktur eines traditionellen  Blas- oder sonstigen Instruments mitbringt liegt im Loslassen von konvetionellen Strukturen und der Unmöglichkeit im Augenblick des Spielens wieder in alte bewährte Fahrwasser zurückzufallen.

Meine gegenwärtigen Experimente das RA-Konzept weiterzuentwickeln gehen in die Richtung einen Breath-Controller und den Sensel-Touch mit live-Patching in Pure Data und auf einem Eurorack-System miteinander zu verbinden. Der Sensel hat meines erachtens ohne Overlays die angenehmste Haptik und zudem die grösstmögliche maximale Fläche so das ich ihn momentan sozusagen nackig mir einem selbstgeschrieben Programm, welches Datan über OSC an PD sendet, benutze.