Pseudorandom Chorale Generator v1
Markov-chains using data from J.S. Bach's 4-voice chorales.
Data from http://www.jsbchorales.net
/**
* Copyright yonatan ( http://wonderfl.net/user/yonatan )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/ibRH
*/
// forked from yonatan's Bach Chorale Dataset PNG
package {
import flash.display.*;
import flash.filters.*;
import flash.net.*;
import flash.system.*;
import flash.events.*;
import com.bit101.components.*;
import org.si.sion.*;
import org.si.sion.events.*;
import org.si.utils.*;
[SWF(width="465", height="465")]
public class main extends Sprite {
private var bae:ByteArrayExt = new ByteArrayExt;
private var log:TextArea;
private var chorales:Vector.<Vector.<int>> = new Vector.<Vector.<int>>();
private var sion:SiONDriver = new SiONDriver;
private var stepper:NumericStepper;
private var prng:PRNG = new PRNG;
private var chords:Vector.<int> = new Vector.<int>();
function main() {
// Data extracted from http://www.jsbchorales.net MIDI files.
// (c) Copyright 1996-2011 Margaret Greentree, some rights reserved, non-commercial use is allowed.
// NOTE_OFF events are not included (i.e. no rests)
//var url:String = "/home/yonatan/src/play/midi/bin/chorales.png";
var url:String = "http://assets.wonderfl.net/images/related_images/f/fe/fe7f/fe7f67dd23761eab779792993fe971976c13e3b5";
var loader:Loader = new Loader;
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoad);
loader.load(new URLRequest(url), new LoaderContext(true));
}
private function pr(...a):void { log.text += a.join(", ") + "\n"; }
private function cls():void { log.text = ""; }
private function onLoad(e:Event):void {
log = new TextArea(this, 0, 0);
log.setSize(465, 465);
var bae:ByteArrayExt = new ByteArrayExt;
bae.fromBitmapData(Bitmap(e.target.content).bitmapData);
bae.endian="bigEndian";
var choraleCnt:int = bae.readInt();
for(var i:int = 0; i < choraleCnt; i++) {
var chords:Vector.<int> = chorales[i] = new Vector.<int>();
var chordCnt:int = bae.readInt();
pr("Reading chorale " + i, chordCnt + " chords");
for(var j:int = 0; j < chordCnt; j++) chords[j] = bae.readInt();
}
stepper = new NumericStepper(this, 250, 30, onChange);
stepper.filters = [new DropShadowFilter(6, 45, Style.DROPSHADOW, 1, 6, 6, .3, 1, false)];
stepper.scaleX = stepper.scaleY = 2;
stepper.minimum = 1;//0;
stepper.maximum = 0x10000;//choraleCnt - 1;
onChange(null);
}
private function onChange(e:Event):void {
sion.stop();
cls();
chords.length = 0;
// writeChords(stepper.value, 0, chorales[stepper.value].length);
generateChorale(stepper.value);
var mml:String = toMml(chords);
pr(mml);
sion.play(sion.compile(mml));
}
private function clamp(x:int, min:int, max:int):int {
if(x>max) return max;
if(x<min) return min;
return x;
}
private function mmlNote(midiNote:int):String {
var names:Array = ["c","c+","d","d+","e","f","f+","g","g+","a","a+","b"];
return "o" + clamp(midiNote/12, 0, 9) + names[midiNote%12];
}
private function toMml(chords:Vector.<int>, root:int = 45):String {
var voices:Vector.<String> = new <String> ["A","A","A","A"];
var j:int, note:int;
for(var i:int = 0; i < chords.length; i++) {
if(i) for(j=0; j<4; j++) voices[j] += "&";
var c:int = chords[i];
// The lowest byte is bass motion (difference from bass on previous chord),
// the high ones are tenor, alto and soprano intervals.
note = (root += (c << 24 >> 24)); voices[0] += mmlNote(note);
note += (c << 16 >> 24); voices[1] += mmlNote(note);
note += (c << 8 >> 24); voices[2] += mmlNote(note);
note += (c >> 24); voices[3] += mmlNote(note);
}
for(j=0; j<4; j++) voices[j] += ";\n";
return mmlPrelude + voices.join("");
}
private function writeChords(choraleIdx:int, offset:int, cnt:int = 4):void {
while(cnt--) chords.push(chorales[choraleIdx][offset++]);
}
private function generateChorale(seed:int = 1):void {
var ci:int, ni:int, needle:int, i:int;
chords.length = 0;
prng.seed = seed;
prng.random(42); // throw away first random number (it's always 0)
ci = prng.random(chorales.length);
ni = 0;
var limit:int=100; // protect from pathologically repeating patterns (also from ones that are just too long...)
while(--limit) {
// pr(ci, ni);
writeChords(ci, ni, 4);
needle = chorales[ci][ni+3];
ci = prng.random(chorales.length);
ni = prng.random(chorales[ci].length) & (~3);
while(chorales[ci][ni+3] != needle) {
ni += 4;
if(ni >= chorales[ci].length) {
ni = 0;
if(++ci == chorales.length) ci = 0;
}
}
ni += 4;
if(ni == chorales[ci].length) break;
}
if(!limit) pr("Limit reached, emergency exit activated!!!");
}
}
}
// Based on code from http://lab.polygonal.de/?p=162
class PRNG {
// set seed with a 31 bit unsigned integer between 1 and 0X7FFFFFFE inclusive. don't use 0!
public var seed:uint;
private function gen():uint { return seed = (seed * 16807) % 2147483647; }
// Returns a pseudorandom uint smaller than limit.
public function random(limit:int):uint { return gen() / 2147483647 * limit; }
}
var mmlPrelude:String = String(<mml><![CDATA[#EFFECT0{reverb 123,34,85,40};
#OPN@0{6 7 31 00 00 06 09 33 0 04 7 0 31 00 00 06 09 00 0 04 3 0 31 00 00 06 09 00 0 02 3 0 31 00 00 06 09 00 0 01 7 0};
#A=%6 @f60 l16;
t80
]]></mml>);