This piece, written for percussionist Irene Bianco in 2020 (click here for the track on Spotify), comes after a research project about vibration in sound art and music (2014-2016) and is strictly related to a project called Fingerscan (Hackster tutorial here). For this work I used Repaer.fm as a DAW, syncing the commands for 10 motors sent as MIDI controller values with and a scrolling video-score that Irene was reading.
The wooden structure holding the vibration motors is placed on the strings of the piano. The strings are protected by a soft padding under the wooden base.
The custom-made circuit independently activates ten vibration motors. Six of them are controllable in intensity (via PWM). A test function in the provided Max patch allows for fine-tuning of the motor position.
As you can see from the video, the performer operates on the strings by plucking or touching them in different ways. By touching the strings while the motors are making them vibrate, different timbres, articulations, and dynamics are produced.
The piano is amplified with four microphones: two cardioid condenser microphones are pointed towards the strings, while two contact mics are attached on the nails of the performerโs left and right index fingers.
The circuitAn Arduino Nano compatible is translating the serial messages into motor commands. Ten output pins are connected to ten NPN transistors, but only some of them are PWM-controllable. Nano's PWM pins are only six: 2, 3, 6 and 9 to 11, so, according to my code and connections (see the attached Arduino sketch and schematics), motors 5, 6, 7 and 10 are not controllable in intensity.
const int motorPin1 = 11; //PWM
const int motorPin2 = 10; //PWM
const int motorPin3 = 9; //PWM
const int motorPin4 = 6; //PWM
const int motorPin5 = 4;
const int motorPin6 = 8;
const int motorPin7 = 7;
const int motorPin8 = 5; //PWM
const int motorPin9 = 3; //PWM
const int motorPin10 = 2;
I used a step-down to obtain 3.3V, but of course you are free to use a power adaptor that provides that voltage directly (please not that the vibration motors are actually rated 3V).
The Nano is fed by the USB cable, since the motors will be controlled via software by a DAW (in my case Reaper), with MIDI continuous controllers, passing through a Max patch which translates those MIDI values into serial messages.
The softwareThe software is divided into three: a DAW part, a Max part and an Arduino Sketch. Let's start from the last one.
Arduino sketch
You already saw the assignment of the pins to the different motors in the previous paragraph. Now look at this part of the sketch:
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
byte startMarker = 255;
byte endMarker = 250;
byte rc;
// if (Serial.available() > 0) {
while (Serial.available() > 0 && newData == false) {
rc = Serial.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
This whole section of code is used to receive a list of values. The microcontroller will receive, here, the raw MIDI value of a controller, which is between 0 and 127, as well as the controller number (1 to 10). This list of two values (ctl and val) is delimited by a start marker, which is 255, and an end marker (250). This way, Arduino can understand when a list is opened and when it is closed, and can assign to both element of the list its correct place. For example, let's say I send a MIDI value of 100 on the controller number 2 and a value of 90 on the controller number 3, meaning I want motors 2 and 3 to vibrate at a good speed. In this case, the Max patch will format a list made this way:
255 2 100 250
255 3 90 250
Arduino will recognize the start of the list and first assign 100 to motor 2, and then 90 to motor 3.
For each of this assignment, the following piece is software is run:
void writeToMotors() {
if (newData == true) {
if (CTL == 1) {
analogWrite(motorPin1, VAL);
}
if (CTL == 2) {
analogWrite(motorPin2, VAL);
}
if (CTL == 3) {
analogWrite(motorPin3, VAL);
}
Et cetera. This is true for the PWM pins, however. On the digital pins the condition test works like this:
if (CTL == 5) {
if (VAL > 64) {
digitalWrite(motorPin5, HIGH);
}
else {
digitalWrite(motorPin5, LOW);
}
}
Meaning that if the controller value is bigger than 64 (50%), the motor will be on, otherwise it will be off.
Max patch
Copy and paste this to an empty Max patch and the basic patch will appear.
<pre><code>
----------begin_max5_patcher----------
1393.3ocyZk0aaiCD9Y6eEDB8QWCdpi9VvhTfBjhrHI69RQQfrLcpZ0ggN5l
1h9ee4gjsSprLinjQBPDsnH0vuYFNWT+Z9LmU4OxKc.uC7IvrY+Z9rYptjcL
q49YNogOFkDVpFlSTdZJOqxYg9YU7GqT8ewUWA9302c8M2Bt98uu8wY0owYI
7J0bQ66LutpsW3Auon7j7B8pAtL.F3CIAXDy02E6sP1U6MTl3gxd77bodPlu
OlvvAzE.zRH3yMux30pkV9pu9Vra6RZaXUzWhyd39BdTklVdX3R3BP.VdEwT
2H6B7Y4T9874xKKrjAc6k27gKtB72Weycfau7pK+q6d8wjH8vjXDIawiMk7H
kBD3eu3p+4xWeLGXOLGRfmjgP8mTMHdxRvGyqD.70FyA40Cyg5oXNH7jxctH
IAjJYNkf7MaF.G5.3zGZb8Uv3s3QwZQF++DD7O.yZdR3O.DHrSbf6DGnNvwN
qd5wU8isbMJbVEl8fyNQ4wwIwcIaAvUs4WB4wDkaCi9lc.j4cT.dBvojdXe2
FLNEnqHduZ5vzCYtNmDCDLSJgHTqvvp5pp7rWvpsKgAa+psHLkWwKtmmEtJg
eH7FrlXfdmFs85XJqp.PP7K.83NPO4nphJ8.ciA3DST.UecrUJixqyDxkNgJ
qSnR6.p3SA01+MDxHejrwEMEPt9mw.D5EXmgzAhQ8aHcw9VSgLUYRkhsCx0o
q38GNfX7wo0optflpLS8LYqrfZsu5it6dGqoOdRaDBLUCyNM+TdYY3C7+POf
gAD671PYCyaCApcepCd1R3UlDulWXmkZJYPVp6Gj5Pf8BZiGRpiCGYYHTnCa
oLDMLYHCoDd3ffQPFdz3g9lvOjk.DG3Xi7C6hmv8fXFC7FD3MX.lYKNw1gSs
fz2aRv4F6vFZfXCKbp3IBFDgzhu.YjgiN33VBtAZEECUg9MsXascXyeXPC4R
OCxsHKsbNPrgYimXquTs.U42xKhCSdmcIcg5qTFnkX1NO49nW8FIGpPqIkXc
ikFIGibKQvoI2xViJAXkd4jjborLG16W20JIIJP0vljJcDUkD+RjuckfkIIO
aP1UsvkoKYPvT.2j7v05r8rQilvrn1bHsLknpM2zDp1wKek4d5Gn0m15.nxZ
xxBacTOF7s7r0fs4EUVZYhLvDJnGnn5ELIxvUVJ+FXtRB+E3tiloYNOTDtNV
VD+Cn6pGdxwS.kG+.iH+S9KTygTb34OzLCb6TvAPgYN4nIxy1fp+knKzym2l
3jD0buuER6WRcLH0UzKZk8rIa0Z7I7kg7BDlSz9IwdxkeGCntJ+PYR2iZaQt
b6RrvWtZLjfQTUMzNU0Alzq9PMmzXSKUwjBVAH9zibdNs3YSdV0lvnmDRyIK
U2NyOxYWF+S0rQzkvSUh1c7E4DyDwToOFM4p04zQFpi.twA6yCXR8pcDAE77
us.0KT1+S4ik40EQsquFwIXut7ZdYUbVXql2m1a3EbLQkoDx+bQH4QwcRJIO
AEqojIDRVIS.Z2fxKjkTTp0M4TVlQxACZGkQVQY44.dZtqa2jFN8jVVrpI.0
LSPMZDzoXDSnj2XQIjITBYKkvlfowXueqHneJgGCJY.gbmDUQCjZtigPyD60
voXeN0n84Ai.FoDSojspKzyk2OhQxM5HPIrIgNHODnw28mQjdZ7DfMQRRlhs
EFQ4mZ+KMd81bQTnMwFhnpOfErqurgAUUBWc2nykvlykrhPH14ZiEhdtfD4b
QH7Yi2gFFkzY2Dtc624EkMiVQDQ9feUmot+h45uOB8spD7bJ3eOtc7pupFmv
BQ5UUhbqpKzYe8nqtjYNo4BM9r53FkdA7DjTkqoLSsxsMYIpRIc9um++.J1Z
WIB
-----------end_max5_patcher-----------
</code></pre>
On the left side: select your serial port for the Nano, and see the incoming Control-In MIDI values from your DAW.
In the middle: manually test each motor.
On the right side: panic button, send all motors a 0 message.
The original patch used in the performance of the piece also contains reverb and amplification routing for the audio signals, as well as some other features especially programmed for rehearsals and performance.
DAW
The DAW part is mainly used for programming the behavior of the motors in time, synced with a video-score that the performer is reading. In this screenshot you can see the ten lanes containing the MIDI controllers assigned to each motor (bottom lane is motor 1). I use Reaper.fm as a DAW but you can reproduce this in any other daw such as Cubase and Logic to name a few.
More info on my website.
Comments