After the short experiment the other day in Processing, I decided i wanted to do it a bit funnier, using something better than true-false pixels. So I put yellow blocks and also made use of the easy to use functionalities in Processing’s third-party libs to make some noise.
I didn’t elaborate too far, but I feel this could become a serious sine wave noise generator. My simple goal in the beginning, was just to make a sonic footprint of the little universe’s evolution.
The looks of the thing now is like this, a bit nicer than the pixels.
It’s easy to modify the code if you want to have a bigger or smaller grid, the only gotchas being that the side size of the squares has to be an even number, and the height and width of the board have to yield zero if %’ed to the square side size. So basically I mean width % _sqSize = 0 and height % _sqSize = 0. I didn’t bother to perform this kind of checking. This is a quick and dirty test.
I think you guys are able to follow the Processing code easily and modify as wanted.
The code as it is will also record the noise generated for you. Exactly as for generating the animated gif file, click on the mouse whenever you want to generate both the gif and the sound bite.
Alternatively you could use the keyreleased event to have separate keys triggering each event (see here). The gif library is the same as the other example. Grab it from here. The Minim library (better be the beta) get from here. Thanks to the authors for their excellent work.
I put an array of floats with the tones of 8 octaves, which I got from here, although I must say I didn’t make much use. I guess you could introduce a lot of random variations in the algorithm. The idea is to get the closest tone to the count of live and dead cells respectively (multiplying by 200 since otherwise we would get very low numbers, therefore very low tones). Even so I commented out the lowest tones.
I don’t want to cause headaches, so I put just a short mp3. You play with the rest if you want more.
So this is the code.
rock on!
import ddf.minim.signals.*;
import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.effects.*;
import ddf.minim.ugens.*;
/* above import is for noise gen */
/* http://code.compartmental.net/tools/minim/manual-music-programming-intro/ */
import gifAnimation.*;
/* Every time you use the Minim library, you need to instantiate a
Minim object and call its constructor with the this keyword. (in setup())*/
Minim minim;
/* we need this class for minimum audio output */
AudioOutput audioout;
SineWave sinW;
SineWave sinW2;
SineWave sinW3; // this will signal the spawning of new cells
boolean activateMouseOverNoise =false;
Oscil osc;
AudioInput audioin;
AudioRecorder recorder;
int freq1 = 440;
int freq2 = 440;
GifMaker gifExport = null;
/* declarations of love... */
int _sqSize = 12;
int _width = 288; // _width % _sqSize should be 0
int _height = 288; // _height % _sqSize should be 0
float frames = 2;
int genCounter;
int rowLength = _width / _sqSize;
int rowHeight = _height / _sqSize;
/* hold our universes here */
float startpop;
/* we decide this is the live color */
color _liveColor = color(235, 235, 0);
/* death gradient intervals, indicates how the cell will turn of slowly
losing its color in certain intervals; these values will be added or
substracted to the values that make up the live color */
/* not done yet */
int _gradIntervalR = 10;
int _gradIntervalG = 10;
int _gradIntervalB = 10;
// float of tones - see here http://www.phy.mtu.edu/~suits/notefreqs.html
float tones[] = new float [] {
/*16.35, 17.32, 18.35, 19.45, 20.6, 21.83, 23.12, 24.5, 25.96, 27.5, 29.14, 30.87, 32.7, 34.65, 36.71, 38.89, 41.2, 43.65, 46.25, 49, 51.91, 55, 58.27, */
61.74, 65.41, 69.3, 73.42, 77.78, 82.41, 87.31, 92.5, 98, 103.83, 110, 116.54, 123.47, 130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185,
196, 207.65, 220, 233.08, 246.94, 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392, 415.3, 440, 466.16, 493.88, 523.25,
554.37, 587.33, 622.25, 659.26, 698.46, 739.99, 783.99, 830.61, 880, 932.33, 987.77, 1046.5, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1479.98,
1567.98, 1661.22, 1760, 1864.66, 1975.53, 2093, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2959.96, 3135.96, 3322.44, 3520, 3729.31,
3951.07, 4186.01, 4434.92, 4698.64, 4978.03
};
void setup()
{
size(_width, _height);
frameRate(frames);
startpop = .18 * rowLength * (rowHeight/1.2);
setupAudioShite();
println("----------------------------------------------------------------------------------------------");
println("STARTPOP " + startpop);
background(0); // set bg to black
smooth();
gifExport = new GifMaker(this, "life.gif");
gifExport.setRepeat(0); // make it an "endless" animation
}
void draw()
{
println(" --------- GENERATION: " + genCounter + " --------------------------------------------------");
if (genCounter == 0)
{
for (int i = 0; i < startpop; i++ )
{
drawCell(int(random(rowLength))*_sqSize, int(random(rowHeight))*_sqSize, 255);
}
recorder.beginRecord();
}
for (int x = 0; x < rowHeight * _sqSize; x+=_sqSize ) {
for (int y = 0; y < rowLength * _sqSize; y+=_sqSize ) {
//println("X:" + x + "Y:" + y);
int _neighs = getNeighbours(x+(_sqSize/2), y+(_sqSize/2));
//println("NUMBER OF NEIGHS: " + _neighs);
// get color first
boolean islive = isPixelAlive(x+(_sqSize/2), y+(_sqSize/2)); // get color of cell center
// -----------------------------------
if (islive)
{
if (_neighs < 2 || _neighs > 3) {
freq1++;
killCell(x, y);
}
if (_neighs == 2) { // keep alive
drawCell(x, y, _liveColor);
freq2++;
}
// if(genCounter % 3 == 0)
// killCell(int(random(rowLength))*_sqSize, int(random(rowHeight))*_sqSize);
}
else
{
if (_neighs == 3) {
drawCell(x, y, _liveColor);
}
}
// -----------------------------------
// uncomment if you want further chaos
// if(genCounter % 3 == 0)
// drawCell(int(random(rowLength))*_sqSize, int(random(rowHeight))*_sqSize, _liveColor);
} // inner for
} // outer for
++genCounter;
sinW.setFreq(findNearestTone(freq2*200) / genCounter);
sinW2.setFreq(findNearestTone(freq2*200) / genCounter);
gifExport.setDelay(500);
gifExport.addFrame();
} // method
void mouseMoved() {
if (activateMouseOverNoise) {
sinW.setFreq(map(mouseX/2, mouseY, height, 500, 60));
sinW2.setFreq(map(mouseX/2, mouseY, height, 500, 60));
}
}
void mousePressed() {
if ( recorder.isRecording() )
{
recorder.endRecord();
recorder.save();
}
gifExport.finish(); // write file
}
int getNeighbours(int x, int y)
{
int _n = (y + height - _sqSize) % height;
int _s = (y + _sqSize) % height;
int _e = (x + _sqSize) % width;
int _w = (x + height - _sqSize) % width;
/* we must exclude from evaluating as alive those nearby cells
that are in the process of losing color */
return (isPixelAlive(x, _n) ? 1 : 0) +
(isPixelAlive(_e, _n) ? 1 : 0) +
(isPixelAlive(_e, y) ? 1 : 0) +
(isPixelAlive(_e, _s) ? 1 : 0) +
(isPixelAlive(x, _s) ? 1 : 0) +
(isPixelAlive(_w, _s) ? 1 : 0) +
(isPixelAlive(_w, y) ? 1 : 0) +
(isPixelAlive(_w, _n) ? 1 : 0);
}
/* this func returns whether a pixel is exactly of the color defined as
alive for our purposes here or not. the color considered valid, or live,
is defined globally (see declaration block) */
boolean isPixelAlive(int x, int y)
{
color _c = get(x, y);
// print("Color for " + x + "," + y + " is " + _c);
return (_c == _liveColor);
}
/* x and y are the center coordinates */
void drawCell(int x, int y, color c)
{
// println("ENTERING drawCell " + x + ", " + y);
stroke(235, 235, 0, 0);
fill(235, 235, 0);
rect(x, y, _sqSize, _sqSize);
}
void killCell(int x, int y)
{
color _c = get(x, y);
stroke(0, 0, 0, 0);
fill(0, 0, 0);
rect(x, y, _sqSize, _sqSize);
}
void stop()
{
// always close Minim audio classes when done
audioout.close();
audioin.close();
minim.stop();
super.stop();
}
/* ------------------------------------------------------------ */
void setupAudioShite()
{
minim = new Minim(this);
/* Without calling the getLineOut() method, none of the audio data generated by the
xWave classes will be routed correctly to the sound card */
audioout = minim.getLineOut(Minim.STEREO, 2048);
audioin = minim.getLineIn(Minim.STEREO, 2048);
recorder = minim.createRecorder(audioin, "lifesound.wav", true);
sinW = new SineWave(0, 1, 44100);
sinW2 = new SineWave(0, 1, 44100);
// sinW3 = new SineWave(0, 1, 44100);
sinW.portamento(300);
sinW2.portamento(300);
// sinW3.portamento(300);
/* add the waves to the audio output channel */
audioout.addSignal(sinW);
audioout.addSignal(sinW2);
}
float findNearestTone(int freq)
{
/* taken from here http://stackoverflow.com/questions/519881/finding-closest-number-in-an-array
hey I just ran 12 kms...too tired for this */
float nearest = 0.0;
float bestDistanceFoundYet = -1;
// We iterate on the array...
for (int i = 0; i < tones.length; i++) {
// if we found the desired number, we return it.
if (tones[i] == freq) {
return tones[i];
}
else {
// else, we consider the difference between the desired number and the current number in the array.
float d = Math.abs(freq - tones[i]);
if (d < bestDistanceFoundYet) {
// For the moment, this value is the nearest to the desired number...
nearest = tones[i];
}
}
}
return nearest;
}