Now that you have been introduced to the basis of Arty's aleatoric nature, it's time to get seriously random. While the Arduino's random() function works well, it essentially provides a statistically similar pseudo-pattern without much change. Listen to it for a while and your ear will start picking up a bland sameness as time goes on. In order to get a bigger, spicier palette for generating music by way of non-restricted, semi-restricted and restricted methods, I decided on a handful of different 'tasting' random functions to start with. The getARand() code is open ended enough to have up to 255 types, but we will start off with four basic types, with variations, for eight total functions.
These functions come to us from a PC program I wrote many years ago. I do not remember exactly how they came about, but I had done some heavy research, and they are part of the result. We will be working with a watered-down, mutilated Gaussian-like function and a simplistic approximation of something similar to an almost recognizable Voss Fractal function. In addition, we'll be using two non-random functions, just to mix things up. And for good measure, we will throw in one which relies on the random() function.
To support these new random number types, we now have a Voss_f() and a Gauss_R() function, new #defines for the random types and another global variable. The float pi_eye is for the tan() and sin() functions. Also, the array holding the previous random value, prevVal[], now has 8 initial values.
I'm not a math wizard. I do remember, vaguely, in 1991, getting some books from the local college library and coming up with the Gauss_R() and Voss_f() functions for a program called “Jazzy”. Jazzy was an MS-DOS program, written in Turbo Pascal, and was a menu driven aleatoric MIDI output program. So, this code is known to work in this context, but frankly, I would have to research it all again to explain how it works. If any math types reading this would care to comment on the inner workings of these functions, please feel free.
In this version of the code, we simply pick out an endless melody by continually throwing random MIDI note numbers out, at a constant loudness and tempo. I've included charts of the first 100 numbers which came out of my Arduino for each RND_* function using the default loVal=0 and hiVal=127. This code, random_gazeFinal.ino, allows you to get an 'audio' feel for the functions. Change the line noteVal=getARand(RND_GAUSS); to experiment with different parameters, as we did last time. Although it is easier to get acquainted with the randomness by listening to only the changing MIDI note values, you can also throw in a randomness to the velocity parameter of the sendMIDI() function and vary the tempo by adding a random value to the delay() parameter, as we did last time.
You can, at your discretion, add functions to getARand(). Just remember to add a #define and another element to the 'previous value' array. Also keep in mind the memory limitations of the Arduino. Finally, as silly as it sounds, even though 'random music' seems to rely heavily on good, solid randomness: well... it doesn't. It's all about how the random numbers are used.
There are several techniques for non-restricted composition, one of which we are using here: just throwing out a random sequence of notes. This generates a melody, of course, which, aside from being random, has no other sort of 'key feeling' attributes necessary for well-formed Western style music. We could add a second sendMIDI() and use a noteVal2 to generate a random counterpoint line. You could keep stacking more notes for a triad chord (three notes sounding at once) and so on. That method quickly blows up the number of lines of code and variable space, though, and becomes a monster to control when you want to start semi- or totally restricting the music output by adding musical composition rules.
So, now that we have a good, basic method for getting the aleatory out of our Arduino, we can begin exploring the methods of converting that into musical output in an easier way. Next time, we'll start heading to the future of our music generation by first going back in time to the late 1890's, when I will present the piano roll method of Arty's Aleatoric Abilities.
These functions come to us from a PC program I wrote many years ago. I do not remember exactly how they came about, but I had done some heavy research, and they are part of the result. We will be working with a watered-down, mutilated Gaussian-like function and a simplistic approximation of something similar to an almost recognizable Voss Fractal function. In addition, we'll be using two non-random functions, just to mix things up. And for good measure, we will throw in one which relies on the random() function.
To support these new random number types, we now have a Voss_f() and a Gauss_R() function, new #defines for the random types and another global variable. The float pi_eye is for the tan() and sin() functions. Also, the array holding the previous random value, prevVal[], now has 8 initial values.
I'm not a math wizard. I do remember, vaguely, in 1991, getting some books from the local college library and coming up with the Gauss_R() and Voss_f() functions for a program called “Jazzy”. Jazzy was an MS-DOS program, written in Turbo Pascal, and was a menu driven aleatoric MIDI output program. So, this code is known to work in this context, but frankly, I would have to research it all again to explain how it works. If any math types reading this would care to comment on the inner workings of these functions, please feel free.
In this version of the code, we simply pick out an endless melody by continually throwing random MIDI note numbers out, at a constant loudness and tempo. I've included charts of the first 100 numbers which came out of my Arduino for each RND_* function using the default loVal=0 and hiVal=127. This code, random_gazeFinal.ino, allows you to get an 'audio' feel for the functions. Change the line noteVal=getARand(RND_GAUSS); to experiment with different parameters, as we did last time. Although it is easier to get acquainted with the randomness by listening to only the changing MIDI note values, you can also throw in a randomness to the velocity parameter of the sendMIDI() function and vary the tempo by adding a random value to the delay() parameter, as we did last time.
You can, at your discretion, add functions to getARand(). Just remember to add a #define and another element to the 'previous value' array. Also keep in mind the memory limitations of the Arduino. Finally, as silly as it sounds, even though 'random music' seems to rely heavily on good, solid randomness: well... it doesn't. It's all about how the random numbers are used.
There are several techniques for non-restricted composition, one of which we are using here: just throwing out a random sequence of notes. This generates a melody, of course, which, aside from being random, has no other sort of 'key feeling' attributes necessary for well-formed Western style music. We could add a second sendMIDI() and use a noteVal2 to generate a random counterpoint line. You could keep stacking more notes for a triad chord (three notes sounding at once) and so on. That method quickly blows up the number of lines of code and variable space, though, and becomes a monster to control when you want to start semi- or totally restricting the music output by adding musical composition rules.
So, now that we have a good, basic method for getting the aleatory out of our Arduino, we can begin exploring the methods of converting that into musical output in an easier way. Next time, we'll start heading to the future of our music generation by first going back in time to the late 1890's, when I will present the piano roll method of Arty's Aleatoric Abilities.
/*
+-------------------------------------------------+
| Arty The Aleatoric Arduino: random gaze final |
| 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_GAUSS 0
#define RND_GAUSS2 1
#define RND_LIN 2
#define RND_SIN 3
#define RND_TAN 4
#define RND_VOSSF 5
#define RND_VOSSPREV 6
#define RND_VOSSGAUSS 7
//global variables
SoftwareSerial MIDI(MIDI_IN, MIDI_OUT); // RX, TX
float pi_eye = -3.14159;
byte prevVal[] = {2, 3, 4, 3, 1, 1, 2, 7, 2}; // for use in getARand() function
byte noteVal;
//-----random functions
byte Voss_f(int last) {
float probit, u;
int Nu, J, K, L;
Nu = 0;
K = random(23);
L = last;
probit = 0.029384;
while (K > 0) {
J = L / K;
if (J == 1) L -= K;
u = random(-10, 21);
if (u < probit) J = 1 - J;
Nu += (J * K);
K /= 2;
probit *= random(1.25,2.5);
}
return Nu;
}
byte Gauss_R(int range) {
int count;
float summ, std, mean;
summ = 0.0;
std = 0.2;
mean = 0.5;
for (count = 1; count < 25; count++) {
summ += random(2);
}
return (byte((std * 0.707106781 * (summ - 12) + mean) * range + 1));
}
byte getARand(byte rType, byte loVal=0, byte hiVal=127) {
//get random stuffs
byte retVal = 0;
byte tmpVal;
pi_eye += 0.05;
switch (rType) {
case RND_GAUSS:
retVal = Gauss_R(random(loVal,hiVal+1)); //must be hiVal+1 to include 127 (random is loVal to hiVal-1)
break;
case RND_GAUSS2:
retVal = Gauss_R(loVal) + Gauss_R(hiVal);
break;
case RND_LIN:
retVal = random(loVal, hiVal+1) - prevVal[rType];
break;
case RND_SIN:
retVal = sin(pi_eye / (loVal+1)) * hiVal;
break;
case RND_TAN:
retVal = tan(pi_eye / (loVal+1)) * hiVal;
break;
case RND_VOSSF:
retVal = Voss_f(hiVal*prevVal[rType])+loVal;
break;
case RND_VOSSPREV:
retVal = Voss_f(prevVal[rType]) * hiVal;
break;
case RND_VOSSGAUSS:
retVal = Voss_f(int(Gauss_R((hiVal-loVal) / 2)));
break;
}
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(9600); // 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() {
//random types are:
// RND_GAUSS, RND_GAUSS2, RND_LIN, RND_SIN, RND_TAN, RND_VOSSF, RND_VOSSPREV and RND_VOSSGAUSS
noteVal=getARand(RND_GAUSS);
sendMIDI(CMD_NOTE_ON,noteVal,64);
delay(200); //a tempo of sorts
sendMIDI(CMD_NOTE_OFF,noteVal,64);
}
+-------------------------------------------------+
| Arty The Aleatoric Arduino: random gaze final |
| 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_GAUSS 0
#define RND_GAUSS2 1
#define RND_LIN 2
#define RND_SIN 3
#define RND_TAN 4
#define RND_VOSSF 5
#define RND_VOSSPREV 6
#define RND_VOSSGAUSS 7
//global variables
SoftwareSerial MIDI(MIDI_IN, MIDI_OUT); // RX, TX
float pi_eye = -3.14159;
byte prevVal[] = {2, 3, 4, 3, 1, 1, 2, 7, 2}; // for use in getARand() function
byte noteVal;
//-----random functions
byte Voss_f(int last) {
float probit, u;
int Nu, J, K, L;
Nu = 0;
K = random(23);
L = last;
probit = 0.029384;
while (K > 0) {
J = L / K;
if (J == 1) L -= K;
u = random(-10, 21);
if (u < probit) J = 1 - J;
Nu += (J * K);
K /= 2;
probit *= random(1.25,2.5);
}
return Nu;
}
byte Gauss_R(int range) {
int count;
float summ, std, mean;
summ = 0.0;
std = 0.2;
mean = 0.5;
for (count = 1; count < 25; count++) {
summ += random(2);
}
return (byte((std * 0.707106781 * (summ - 12) + mean) * range + 1));
}
byte getARand(byte rType, byte loVal=0, byte hiVal=127) {
//get random stuffs
byte retVal = 0;
byte tmpVal;
pi_eye += 0.05;
switch (rType) {
case RND_GAUSS:
retVal = Gauss_R(random(loVal,hiVal+1)); //must be hiVal+1 to include 127 (random is loVal to hiVal-1)
break;
case RND_GAUSS2:
retVal = Gauss_R(loVal) + Gauss_R(hiVal);
break;
case RND_LIN:
retVal = random(loVal, hiVal+1) - prevVal[rType];
break;
case RND_SIN:
retVal = sin(pi_eye / (loVal+1)) * hiVal;
break;
case RND_TAN:
retVal = tan(pi_eye / (loVal+1)) * hiVal;
break;
case RND_VOSSF:
retVal = Voss_f(hiVal*prevVal[rType])+loVal;
break;
case RND_VOSSPREV:
retVal = Voss_f(prevVal[rType]) * hiVal;
break;
case RND_VOSSGAUSS:
retVal = Voss_f(int(Gauss_R((hiVal-loVal) / 2)));
break;
}
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(9600); // 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() {
//random types are:
// RND_GAUSS, RND_GAUSS2, RND_LIN, RND_SIN, RND_TAN, RND_VOSSF, RND_VOSSPREV and RND_VOSSGAUSS
noteVal=getARand(RND_GAUSS);
sendMIDI(CMD_NOTE_ON,noteVal,64);
delay(200); //a tempo of sorts
sendMIDI(CMD_NOTE_OFF,noteVal,64);
}
Copyright © 2015 by Bill L. Behrendt