Now that we have a test unit – an Arduino Uno Rev 3 clone with the Arty Bridge MIDI out – we can start looking at what makes aleatory tick. As we discussed, the word aleatory comes from the Latin for 'dice player'. So, we need to teach the Arduino (or any computer that will generate music) how to throw the dice.
Unfortunately, that's a bit more difficult than one might imagine.
Simply put, a random number sequence is supposed to be a group of numbers in which the order is not predictable. So a sequence of, say {2,4,6,8,10} would not be considered as being random since there is a common difference of 2 between each number. The set {1,8,13,14,34,99} is more than likely a random set of numbers. However, it could be that there is a relationship: it just may not be readily apparent. That is, by and large, one of the problems with randomness. When you don't want it, it rears its ugly head. When you want it, it makes itself scarce. I'm not exactly a math whiz, but I'm pretty sure there's a point at which a sequence of numbers can be considered as 'random enough'.
In our case, we have to settle with what the Arduino has, which is actually a pseudo-random number generator. For our purposes, it is 'random enough'. And if it isn't, we will pretend it is.
One aspect of the pseudo-random number is called the random seed number. We're going to follow the simple suggestion found on the Arduino reference pages and use an initializer based on looking at the value of an unconnected analog port.
I've added some things to the previous test_MIDI code. First, a global variable called noteVal, to hold our randomly chosen MIDI note. Secondly, in setup(), a call to seed the random number generator. Finally, the loop() code now sends out random notes with a random loudness at a random interval over the MIDI cable, rather than the linear 0 to 127 notes at a constant velocity (loudness) and tempo. You can spend half a day playing with all these random settings and get some output that almost entirely but not quite exactly sound like something that may approach being a distant cousin to something akin to a musical melody.
Unfortunately, that's a bit more difficult than one might imagine.
Simply put, a random number sequence is supposed to be a group of numbers in which the order is not predictable. So a sequence of, say {2,4,6,8,10} would not be considered as being random since there is a common difference of 2 between each number. The set {1,8,13,14,34,99} is more than likely a random set of numbers. However, it could be that there is a relationship: it just may not be readily apparent. That is, by and large, one of the problems with randomness. When you don't want it, it rears its ugly head. When you want it, it makes itself scarce. I'm not exactly a math whiz, but I'm pretty sure there's a point at which a sequence of numbers can be considered as 'random enough'.
In our case, we have to settle with what the Arduino has, which is actually a pseudo-random number generator. For our purposes, it is 'random enough'. And if it isn't, we will pretend it is.
One aspect of the pseudo-random number is called the random seed number. We're going to follow the simple suggestion found on the Arduino reference pages and use an initializer based on looking at the value of an unconnected analog port.
I've added some things to the previous test_MIDI code. First, a global variable called noteVal, to hold our randomly chosen MIDI note. Secondly, in setup(), a call to seed the random number generator. Finally, the loop() code now sends out random notes with a random loudness at a random interval over the MIDI cable, rather than the linear 0 to 127 notes at a constant velocity (loudness) and tempo. You can spend half a day playing with all these random settings and get some output that almost entirely but not quite exactly sound like something that may approach being a distant cousin to something akin to a musical melody.
/*
+-------------------------------------------------+
| Arty The Aleatoric Arduino: random gaze 0 |
| Part I of the Arty series |
| Perambulations in the Field |
| of Artifical Music Generation |
| Brought to you by "Junkbox Junkie, the Musical" |
+-------------------------------------------------+
Copyright © 2015 by Bill L. Behrendt
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdarg.h>
#include <stdio.h>
#include <SoftwareSerial.h>
//software serial port pins for MIDI
#define MIDI_OUT 11
#define MIDI_IN 12 //unused
//MIDI cmd
#define CMD_NOTE_ON 0x90 //channel 1
#define CMD_NOTE_OFF 0x80 //channel 1
//global variables
SoftwareSerial MIDI(MIDI_IN, MIDI_OUT); // RX, TX
byte noteVal;
void flashWorkLED(char start) {
if (start == 1) {
digitalWrite(LED_BUILTIN, HIGH);
delay(250);
digitalWrite(LED_BUILTIN, LOW);
delay(250);
}
for (char tm = 0; tm < 5; tm++) {
digitalWrite(LED_BUILTIN, HIGH);
delay(125);
digitalWrite(LED_BUILTIN, LOW);
delay(75);
}
}
void sendMIDI(unsigned char cmd, unsigned char data1, unsigned char data2)
{
MIDI.write(cmd); //note on or note off
MIDI.write(data1); //MIDI note 0-127
MIDI.write(data2); //velocity (loudness) of note
}
void audioBootTest(void) {
sendMIDI(CMD_NOTE_ON,36,64);
sendMIDI(CMD_NOTE_ON,60,64);
sendMIDI(CMD_NOTE_ON,64,64);
sendMIDI(CMD_NOTE_ON,67,64);
sendMIDI(CMD_NOTE_ON,72,64);
flashWorkLED(1);
delay(1000);
flashWorkLED(0);
sendMIDI(CMD_NOTE_OFF,36,64);
sendMIDI(CMD_NOTE_OFF,60,64);
sendMIDI(CMD_NOTE_OFF,64,64);
sendMIDI(CMD_NOTE_OFF,67,64);
sendMIDI(CMD_NOTE_OFF,72,64);
}
void allNotesOff(void) {
for (int anoff = 0; anoff < 16; anoff++) {
sendMIDI(0xB0 + anoff, 0x7B, 0x00);
}
}
void setup() {
Serial.begin(115200); // for looking at debug things
MIDI.begin(31250); // our software serial MIDI connection
//turn all notes off, all channels
allNotesOff();
audioBootTest();
//get a random seed from an unattached analog pin
randomSeed(analogRead(0));
}
void loop() {
//trying out the Arduino pseudo-random nature
noteVal=random(12,96); //change range to limit spread of notes do things like random(30,72)%random(60,96)
Serial.println(noteVal);
sendMIDI(CMD_NOTE_ON,noteVal,32+random(64)); // or use random(32,96)
//delay(130+random(130)); //a tempo of sorts or use random(130,260)
delay(random(130,260)); //another way to limit the tempo variation
sendMIDI(CMD_NOTE_OFF,noteVal,64);
}
+-------------------------------------------------+
| Arty The Aleatoric Arduino: random gaze 0 |
| Part I of the Arty series |
| Perambulations in the Field |
| of Artifical Music Generation |
| Brought to you by "Junkbox Junkie, the Musical" |
+-------------------------------------------------+
Copyright © 2015 by Bill L. Behrendt
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdarg.h>
#include <stdio.h>
#include <SoftwareSerial.h>
//software serial port pins for MIDI
#define MIDI_OUT 11
#define MIDI_IN 12 //unused
//MIDI cmd
#define CMD_NOTE_ON 0x90 //channel 1
#define CMD_NOTE_OFF 0x80 //channel 1
//global variables
SoftwareSerial MIDI(MIDI_IN, MIDI_OUT); // RX, TX
byte noteVal;
void flashWorkLED(char start) {
if (start == 1) {
digitalWrite(LED_BUILTIN, HIGH);
delay(250);
digitalWrite(LED_BUILTIN, LOW);
delay(250);
}
for (char tm = 0; tm < 5; tm++) {
digitalWrite(LED_BUILTIN, HIGH);
delay(125);
digitalWrite(LED_BUILTIN, LOW);
delay(75);
}
}
void sendMIDI(unsigned char cmd, unsigned char data1, unsigned char data2)
{
MIDI.write(cmd); //note on or note off
MIDI.write(data1); //MIDI note 0-127
MIDI.write(data2); //velocity (loudness) of note
}
void audioBootTest(void) {
sendMIDI(CMD_NOTE_ON,36,64);
sendMIDI(CMD_NOTE_ON,60,64);
sendMIDI(CMD_NOTE_ON,64,64);
sendMIDI(CMD_NOTE_ON,67,64);
sendMIDI(CMD_NOTE_ON,72,64);
flashWorkLED(1);
delay(1000);
flashWorkLED(0);
sendMIDI(CMD_NOTE_OFF,36,64);
sendMIDI(CMD_NOTE_OFF,60,64);
sendMIDI(CMD_NOTE_OFF,64,64);
sendMIDI(CMD_NOTE_OFF,67,64);
sendMIDI(CMD_NOTE_OFF,72,64);
}
void allNotesOff(void) {
for (int anoff = 0; anoff < 16; anoff++) {
sendMIDI(0xB0 + anoff, 0x7B, 0x00);
}
}
void setup() {
Serial.begin(115200); // for looking at debug things
MIDI.begin(31250); // our software serial MIDI connection
//turn all notes off, all channels
allNotesOff();
audioBootTest();
//get a random seed from an unattached analog pin
randomSeed(analogRead(0));
}
void loop() {
//trying out the Arduino pseudo-random nature
noteVal=random(12,96); //change range to limit spread of notes do things like random(30,72)%random(60,96)
Serial.println(noteVal);
sendMIDI(CMD_NOTE_ON,noteVal,32+random(64)); // or use random(32,96)
//delay(130+random(130)); //a tempo of sorts or use random(130,260)
delay(random(130,260)); //another way to limit the tempo variation
sendMIDI(CMD_NOTE_OFF,noteVal,64);
}
It's worth mentioning that the sound source of your destination MIDI device may not handle all notes from 0 to 127. So, in some cases, you may experience 'drop out' which means that you sent a command to turn on a note that your sound source doesn't support. Just write it off as a musical rest. Generally, you can use random(36,96) which I would think is a range supported by most MIDI devices of the last 100 years or so.
I am a proponent of modularizing code whenever possible, so I am going to present a second code listing. This will also be a framework for the future as we discover more and more ways to make Arty sing. The main change is adding a function called getARand(). This function will replace having to type in random(0,127) and allow you to experiment more freely with randomness of your own choosing. I've added three random functions and used #defines to name them RND_0 through RND_2. I've added a global variable array to hold a previous value for each random function. So, if you add your own RND_X functions, you'll also need to add a #define entry and a value in the initialized array for each addition.
The parameters passed to getARand() are a required 'rType' – which is where the #defined names come in handy. There are also two optional parameters to allow any of the functions added to be more flexible. So, getARand(RND_0), getARand(RND_0,36) and getARand(RND_0,36,96) all work because the RND_0 function is created with 'random(loVal, hiVal)'. So, in the first case, the end result of getARand(RND_0) is the same as random(0,127), because the default values are used when no values are supplied. getARand(RND_0,36,96), however, is the same as random(36,96) because both of the optional values were supplied. Finally, getARand(RND_0,36) is the same as random(36,127), with the supplied 36 overriding the default 0. Sadly, getARand(RND_0,,55) will not work and will generate an 'expected primary-expression' compiler error. The rule being, you must supply optional values from the first to the last (from left to right) without skipping over any of them.
I am a proponent of modularizing code whenever possible, so I am going to present a second code listing. This will also be a framework for the future as we discover more and more ways to make Arty sing. The main change is adding a function called getARand(). This function will replace having to type in random(0,127) and allow you to experiment more freely with randomness of your own choosing. I've added three random functions and used #defines to name them RND_0 through RND_2. I've added a global variable array to hold a previous value for each random function. So, if you add your own RND_X functions, you'll also need to add a #define entry and a value in the initialized array for each addition.
The parameters passed to getARand() are a required 'rType' – which is where the #defined names come in handy. There are also two optional parameters to allow any of the functions added to be more flexible. So, getARand(RND_0), getARand(RND_0,36) and getARand(RND_0,36,96) all work because the RND_0 function is created with 'random(loVal, hiVal)'. So, in the first case, the end result of getARand(RND_0) is the same as random(0,127), because the default values are used when no values are supplied. getARand(RND_0,36,96), however, is the same as random(36,96) because both of the optional values were supplied. Finally, getARand(RND_0,36) is the same as random(36,127), with the supplied 36 overriding the default 0. Sadly, getARand(RND_0,,55) will not work and will generate an 'expected primary-expression' compiler error. The rule being, you must supply optional values from the first to the last (from left to right) without skipping over any of them.
/*
+-------------------------------------------------+
| Arty The Aleatoric Arduino: random gaze 1 |
| Part I of the Arty series |
| Perambulations in the Field |
| of Artifical Music Generation |
| Brought to you by "Junkbox Junkie, the Musical" |
+-------------------------------------------------+
Copyright © 2015 by Bill L. Behrendt
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdarg.h>
#include <stdio.h>
#include <SoftwareSerial.h>
//software serial port pins for MIDI
#define MIDI_OUT 11
#define MIDI_IN 12 //unused
//MIDI cmd
#define CMD_NOTE_ON 0x90 //channel 1
#define CMD_NOTE_OFF 0x80 //channel 1
//random types
#define RND_0 0
#define RND_1 1
#define RND_2 2
//global variables
SoftwareSerial MIDI(MIDI_IN, MIDI_OUT); // RX, TX
byte prevVal[] = {72, 60, 96}; // for use in getARand() function
byte noteVal;
byte getARand(byte rType, byte loVal=0, byte hiVal=127) {
//get random stuffs
byte retVal = 0;
switch (rType) {
case RND_0:
retVal = random(loVal,hiVal);
break;
case RND_1:
retVal = random(loVal,hiVal)+prevVal[rType];
break;
case RND_2:
retVal = random(loVal,hiVal)%(prevVal[rType]*3);
break;
default:
retVal=random(0,127);
}
prevVal[rType] = (retVal & 127);
return prevVal[rType];
}
//----- end random functions
void flashWorkLED(char start) {
if (start == 1) {
digitalWrite(LED_BUILTIN, HIGH);
delay(250);
digitalWrite(LED_BUILTIN, LOW);
delay(250);
}
for (char tm = 0; tm < 5; tm++) {
digitalWrite(LED_BUILTIN, HIGH);
delay(125);
digitalWrite(LED_BUILTIN, LOW);
delay(75);
}
}
void sendMIDI(unsigned char cmd, unsigned char data1, unsigned char data2)
{
MIDI.write(cmd); //note on or note off
MIDI.write(data1); //MIDI note 0-127
MIDI.write(data2); //velocity (loudness) of note
}
void audioBootTest(void) {
sendMIDI(CMD_NOTE_ON,36,64);
sendMIDI(CMD_NOTE_ON,60,64);
sendMIDI(CMD_NOTE_ON,64,64);
sendMIDI(CMD_NOTE_ON,67,64);
sendMIDI(CMD_NOTE_ON,72,64);
flashWorkLED(1);
delay(1000);
flashWorkLED(0);
sendMIDI(CMD_NOTE_OFF,36,64);
sendMIDI(CMD_NOTE_OFF,60,64);
sendMIDI(CMD_NOTE_OFF,64,64);
sendMIDI(CMD_NOTE_OFF,67,64);
sendMIDI(CMD_NOTE_OFF,72,64);
}
void allNotesOff(void) {
for (int anoff = 0; anoff < 16; anoff++) {
sendMIDI(0xB0 + anoff, 0x7B, 0x00);
}
}
void setup() {
Serial.begin(115200); // for looking at debug things
MIDI.begin(31250); // our software serial MIDI connection
//turn all notes off, all channels
allNotesOff();
audioBootTest();
//get a random seed from an unattached analog pin
randomSeed(analogRead(0));
}
void loop() {
//rather than changing the random functions here, use getARand()
// RND_0, RND_1 and RND_2
noteVal=getARand(RND_2);
sendMIDI(CMD_NOTE_ON,noteVal,64);
delay(200); //a tempo of sorts
sendMIDI(CMD_NOTE_OFF,noteVal,64);
}
+-------------------------------------------------+
| Arty The Aleatoric Arduino: random gaze 1 |
| Part I of the Arty series |
| Perambulations in the Field |
| of Artifical Music Generation |
| Brought to you by "Junkbox Junkie, the Musical" |
+-------------------------------------------------+
Copyright © 2015 by Bill L. Behrendt
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdarg.h>
#include <stdio.h>
#include <SoftwareSerial.h>
//software serial port pins for MIDI
#define MIDI_OUT 11
#define MIDI_IN 12 //unused
//MIDI cmd
#define CMD_NOTE_ON 0x90 //channel 1
#define CMD_NOTE_OFF 0x80 //channel 1
//random types
#define RND_0 0
#define RND_1 1
#define RND_2 2
//global variables
SoftwareSerial MIDI(MIDI_IN, MIDI_OUT); // RX, TX
byte prevVal[] = {72, 60, 96}; // for use in getARand() function
byte noteVal;
byte getARand(byte rType, byte loVal=0, byte hiVal=127) {
//get random stuffs
byte retVal = 0;
switch (rType) {
case RND_0:
retVal = random(loVal,hiVal);
break;
case RND_1:
retVal = random(loVal,hiVal)+prevVal[rType];
break;
case RND_2:
retVal = random(loVal,hiVal)%(prevVal[rType]*3);
break;
default:
retVal=random(0,127);
}
prevVal[rType] = (retVal & 127);
return prevVal[rType];
}
//----- end random functions
void flashWorkLED(char start) {
if (start == 1) {
digitalWrite(LED_BUILTIN, HIGH);
delay(250);
digitalWrite(LED_BUILTIN, LOW);
delay(250);
}
for (char tm = 0; tm < 5; tm++) {
digitalWrite(LED_BUILTIN, HIGH);
delay(125);
digitalWrite(LED_BUILTIN, LOW);
delay(75);
}
}
void sendMIDI(unsigned char cmd, unsigned char data1, unsigned char data2)
{
MIDI.write(cmd); //note on or note off
MIDI.write(data1); //MIDI note 0-127
MIDI.write(data2); //velocity (loudness) of note
}
void audioBootTest(void) {
sendMIDI(CMD_NOTE_ON,36,64);
sendMIDI(CMD_NOTE_ON,60,64);
sendMIDI(CMD_NOTE_ON,64,64);
sendMIDI(CMD_NOTE_ON,67,64);
sendMIDI(CMD_NOTE_ON,72,64);
flashWorkLED(1);
delay(1000);
flashWorkLED(0);
sendMIDI(CMD_NOTE_OFF,36,64);
sendMIDI(CMD_NOTE_OFF,60,64);
sendMIDI(CMD_NOTE_OFF,64,64);
sendMIDI(CMD_NOTE_OFF,67,64);
sendMIDI(CMD_NOTE_OFF,72,64);
}
void allNotesOff(void) {
for (int anoff = 0; anoff < 16; anoff++) {
sendMIDI(0xB0 + anoff, 0x7B, 0x00);
}
}
void setup() {
Serial.begin(115200); // for looking at debug things
MIDI.begin(31250); // our software serial MIDI connection
//turn all notes off, all channels
allNotesOff();
audioBootTest();
//get a random seed from an unattached analog pin
randomSeed(analogRead(0));
}
void loop() {
//rather than changing the random functions here, use getARand()
// RND_0, RND_1 and RND_2
noteVal=getARand(RND_2);
sendMIDI(CMD_NOTE_ON,noteVal,64);
delay(200); //a tempo of sorts
sendMIDI(CMD_NOTE_OFF,noteVal,64);
}
There are a wealth of things you can do, aside from adding your own functions. Initially, it might help to get a feel for the random numbers by listening to the differences in the default sounds. Do this by simply changing the parameter to getARand(). First, noteVal=getARand(RND_0); then noteVal=getARand(RND_1); and finally noteVal=getARand(RND_2);.
We will be adding other functions soon. In the meantime, here are a few ideas to play with.
noteVal = getARand(RND_0,12,36)+getARand(RND_1,36,60);
Recursive calls:
noteVal=getARand(RND_0, getARand(RND_1), getARand(RND_2));
Change the velocity (loudness):
sendMIDI(CMD_NOTE_ON,noteVal,64+getARand(RND_2, 0, 32));
sendMIDI(CMD_NOTE_ON,noteVal,getARand(RND_0, 64, 96));
Change the tempo / interval between notes:
delay(getARand(RND_0) + 150);
Have fun until next time! :)
We will be adding other functions soon. In the meantime, here are a few ideas to play with.
noteVal = getARand(RND_0,12,36)+getARand(RND_1,36,60);
Recursive calls:
noteVal=getARand(RND_0, getARand(RND_1), getARand(RND_2));
Change the velocity (loudness):
sendMIDI(CMD_NOTE_ON,noteVal,64+getARand(RND_2, 0, 32));
sendMIDI(CMD_NOTE_ON,noteVal,getARand(RND_0, 64, 96));
Change the tempo / interval between notes:
delay(getARand(RND_0) + 150);
Have fun until next time! :)
Copyright © 2015 by Bill L. Behrendt