RockMusic 25 lines generative music entry


/ Published in: ActionScript 3
Save to your folder(s)

This was create for Keith Peters' now defunct 25lines AS3 competition. This entry fit within all the rules of the competition.

To try it yourself, simply paste this code on the first frame of an empty FLA.

To view it live, check it out here: [http://struct.ca/share/rock](http://struct.ca/share/rock)


Copy this code and paste it in your HTML
  1. [SWF (backgroundColor=0, width=800,height=600, frameRate=60)]
  2.  
  3.  
  4. //////////////////////////////////////////
  5. //ROCK MUSIC featuring THE ROLLING STONE//
  6. //////////////////////////////////////////
  7. //By Matt Rix //
  8. //////////////////////////////////////////
  9. //Hold down your mouse on the left and //
  10. //right sides of the screen to move //
  11. //the stone. The closer you are to the //
  12. //edge of the screen, the more force //
  13. //you'll have. Let go of the mouse to //
  14. //let gravity take over. //
  15. // //
  16. //None of the variables are typed, so //
  17. //I ran into some huge speed problems //
  18. //when doing the audio stuff on //
  19. //thousands of samples. Because of that //
  20. //this toy requires a pretty fast //
  21. //computer to run smoothly. //
  22. //////////////////////////////////////////
  23.  
  24.  
  25. //freebies
  26. stage.align = StageAlign.TOP_LEFT;
  27. stage.scaleMode = StageScaleMode.SHOW_ALL;
  28.  
  29.  
  30. //1: create a config object to store all of our config options as well as creating a handy "scope" reference.
  31. var scope:* = (this.config = {x:400, y:300, width:800, height:600, sceneWidth:10000, segmentWidth:8, terrainFlatness:0.01, musicTempo:150, musicKey:440, acceleration:.4, gravity:.4, maxSpeed:15, lastX:0, lastY:0, scope:this}).scope;
  32. //2: create the stone texture
  33. (scope.wheelBMD = new BitmapData(100,100,false,0)).perlinNoise(20,20,2,123,true,true,7,true);
  34. //3: create the wheel
  35. MovieClip(MovieClip(addChild(scope.level = new MovieClip())).addChild(scope.wheel = new MovieClip())).graphics.beginBitmapFill(scope.wheelBMD,null,true,false);
  36. //4: draw the wheel and init the terrain drawing vars
  37. scope.points = [new Point((scope.speedX = int(scope.wheel.graphics.drawCircle(int(scope.level.graphics.lineStyle(3,0x00FF00,1)),int(scope.level.graphics.moveTo(0,300)),30))), (scope.mountY = (scope.wheel.x = scope.config.height/2)+(scope.mountSpeed = (scope.lineIndex = 0))))];
  38. //5: build an array of terrain points
  39. while((scope.points = scope.points.concat([new Point(scope.points.length*scope.config.segmentWidth, scope.mountY = (scope.config.height/2+(scope.mountY-scope.config.height/2)*.9) + (scope.mountSpeed = (1-scope.config.terrainFlatness)*(scope.mountSpeed + -3 + Math.random()*6)))])).length < Math.ceil(scope.config.sceneWidth/scope.config.segmentWidth));
  40. //6: draw the terrain points
  41. while((scope.lineIndex = scope.lineIndex + 1 + int(scope.level.graphics.lineTo(scope.points[scope.lineIndex].x, scope.points[scope.lineIndex].y))) < scope.points.length);
  42.  
  43. //7: listen for enter frames
  44. addEventListener(Event.ENTER_FRAME, function(e:Event):void
  45. {
  46. //8: move the wheel(the rotation value is the same as the x value)
  47. (scope.wheel.x = Math.max(0, Math.min(scope.config.sceneWidth-1, (scope.wheel.x + .5*((scope.speedX = Math.max(-scope.config.maxSpeed, Math.min(scope.config.maxSpeed, scope.speedX*.99 + int(scope.isMouseDown)*(scope.config.acceleration*2*(scope.mouseX-(scope.level.x + scope.wheel.x))/scope.config.width)))))))));
  48. //9: interpolate the terrain points to figure out what y-value the ground is at, and put the wheel on the ground
  49. scope.wheel.y = -30 + (scope.baseY = scope.points[Math.max(0, Math.min(Math.floor(scope.config.sceneWidth/scope.config.segmentWidth)-1, Math.floor(scope.wheel.x/scope.config.segmentWidth)))].y)+ (scope.deltaY = (scope.points[Math.max(0, Math.min(Math.floor(scope.config.sceneWidth/scope.config.segmentWidth)-1, Math.floor(scope.wheel.x/scope.config.segmentWidth)+1))].y - scope.points[Math.max(0, Math.min(200000, Math.floor(scope.wheel.x/scope.config.segmentWidth)))].y)) * ((scope.wheel.x%scope.config.segmentWidth)/scope.config.segmentWidth);
  50. //10: figure out the ground's angle and apply gravity
  51. scope.speedX += Math.sin(Math.atan2(scope.deltaY,scope.config.segmentWidth))*scope.config.gravity;
  52. //11: rotate the wheel based on our direction and speed
  53. scope.wheel.rotation += scope.speedX/Math.abs(scope.speedX) * Math.sqrt((scope.config.lastX - scope.wheel.x) * (scope.config.lastX - scope.wheel.x) + (scope.config.lastY - scope.wheel.y) * (scope.config.lastY - scope.wheel.y)) + (scope.config.lastX = scope.wheel.x)*0 + (scope.config.lastY = scope.wheel.y)*0;
  54. //12: move and ease the "camera" x
  55. scope.level.x += ((scope.config.width/2 - scope.wheel.x) - scope.level.x)/30;
  56. //13: move and ease the "camera" y
  57. scope.level.y += ((scope.config.height/2 - scope.wheel.y) - scope.level.y)/30;
  58. });
  59.  
  60. //14: listen for mouse downs
  61. stage.addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void { scope.isMouseDown = true});
  62. //15: listen for mouse ups
  63. stage.addEventListener(MouseEvent.MOUSE_UP, function(e:MouseEvent):void { scope.isMouseDown = false});
  64.  
  65. //16: create the music config. This only counts as a single line, because if I took out the line breaks, it would be :)
  66. //it creates a bunch of "instrument" settings that define the 3 instruments that play. Fiddling around with these variables leads to tons of fun stuff
  67. scope.music =
  68. {
  69. numSamples:2048,
  70. scale:[2,2,3,2,3], //major pentatonic scale
  71. sample:0,
  72. instrument:
  73. [
  74. /*MELODY*/{freq:0, position:0, noteLength:Math.round(1/4 *44100*60/scope.config.musicTempo*4), beatOffset:0, phase:0, volume:.4, envVolume:0, percent:0, sample:0, currentNote:0, mouseRange:30, numNotes: 21, startNote:20, baseFreq:scope.config.musicKey/4, freqs:[]},
  75. /*BASS*/{freq:0, position:0, noteLength:Math.round(8/4 *44100*60/scope.config.musicTempo*4), beatOffset:0, phase:0, volume:.3, envVolume:0, percent:0, sample:0, currentNote:0, mouseRange:100, numNotes: 12, startNote:7, baseFreq:scope.config.musicKey/8, freqs:[]},
  76. /*TINKLES*/{freq:0, position:0, noteLength:Math.round(3/8 *44100*60/scope.config.musicTempo*4), beatOffset:0, phase:0, volume:.2, envVolume:0, percent:0, sample:0, currentNote:0, mouseRange:1, numNotes: 6, startNote:5, baseFreq:scope.config.musicKey/1, freqs:[]},
  77. ]
  78. };
  79.  
  80. //17: generate an array of all the needed frequencies in a scale
  81. for each(var instrument:Object in scope.music.instrument) while((instrument.freqs = instrument.freqs.concat([(instrument.baseFreq = instrument.baseFreq * Math.pow(1.05946309435929, scope.music.scale[instrument.freqs.length%scope.music.scale.length]))])).length < instrument.numNotes);
  82.  
  83. //18: create a nwe dynamic sound and start listening to it
  84. (scope.dynamicSound = new Sound()).addEventListener(SampleDataEvent.SAMPLE_DATA, function (e:SampleDataEvent):void
  85. {
  86. //19: loop through the samples
  87. for(var i:int = 0; i<scope.music.numSamples; i++)
  88. {
  89. //20: loop through the instrument information
  90. for each(var instrument:Object in scope.music.instrument)
  91. {
  92. //21: if we're on a beat, get a new frequency for the note based on the ball's y position
  93. if(((instrument.position = instrument.position+1) + instrument.beatOffset)%instrument.noteLength == 0) instrument.freq = Math.min(scope.config.musicKey*4, instrument.freqs[((instrument.currentNote = instrument.startNote-Math.round(scope.wheel.y/instrument.mouseRange))+instrument.freqs.length*100000)%instrument.freqs.length]);
  94.  
  95. //I used to have a ton of sweet volume envelope stuff in here(attack, hold, release), but the untyped references made everything run so slow that I couldn't get more than one instrument at a time without it conking out
  96.  
  97. //22: add this instruments's samples to the full sample
  98. scope.music.sample += ((Math.sin((instrument.phase = instrument.phase + instrument.freq/44100)*Math.PI*2)) * instrument.volume * 0.2 * (0.7+0.3*Math.abs(scope.speedX)/15));
  99. }
  100. //23: write the samples to the audio buffer. I was trying to come up with a way to combine the bytes of these two floats and write them as a double, but it's tricky, and this is much simpler
  101. e.data.writeFloat(scope.music.sample + (scope.music.sample = int(e.data.writeFloat(scope.music.sample))));
  102. }
  103.  
  104. });
  105.  
  106. //24: add this instruments's samples to the full sample
  107. (scope.dynamicSoundChannel = scope.dynamicSound.play());
  108.  
  109.  
  110. //25: I left the 25th line blank in case I accidentally broke a rule somewhere. I know I got creative with chaining, but I made sure to never do any cheap recursive/infinite stuff.

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.