In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Pseudorandom Chorale Generator v1

Markov-chains using data from J.S. Bach's 4-voice chorales.
Data from http://www.jsbchorales.net
Get Adobe Flash player
by yonatan 09 Mar 2013

    Talk

    makc3d at 09 Mar 2013 03:10
    I think some sin/cos stuff would work better than random walk
    yonatan at 09 Mar 2013 09:50
    sin/cos? where?
    makc3d at 10 Mar 2013 00:48
    around line 111 :) I mean, have chords(?) function of time e.g. Ī£Asin(Bt+C) and have A, B, C, n selected randomly - if all the Bs are prime, you could have quite long irrepeatable pattern yet with certain periodicity, which may sound better than just randomly jumping from sequence to sequence. Just a speculation, though.

    Tags

    Embed
/**
 * 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>);