<header>
<h1>Random Music</h1>
<p>Using the characteristics of <em>Auld Lang Syne</em></p>
</header>
<section id="staff">
</section>
<div class="triggers">
<a id="play">
<span class="play"></span>
<span class="stop"></span>
</a>
<a id="new">New Song</a>
</div>
<article>
<h1>Why this exists</h1>
<p>After trying my hand at <a href="//codepen.io/jakealbaugh/pen/EaypRL" target="blank">random beats</a>, I finally summoned the courage to attempt random music. This is a program through which you can pass fairly arbitrary statistical data about a piece of music and then get a response with a random composition based on the provided data. The result can often seem patternless, but occasionally it creates peieces with a clear theme. The dots above are in fact two different clef's, with three octaves worth of intervals distributed vertically inside.</p>
<h1>How it works</h1>
<h2>Interpretting a written piece of music</h2>
<p>This program relies on source data. I decided that the source material for this demo would be <a href="http://www.8notes.com/scores/1476.asp" target="blank">Auld Lang Syne</a> for the sake of the new year. I decided the best way to interpret the source material would be to do it as abstractly as possible in an effort to achieve a truly unique result. It is for that reason, I chose to measure it in the following ways.</p>
<h3>Piece Structure</h3>
<p>The global values captured for the piece of music are fed in to the program in the following format:</p>
<pre>piece = {
measures: 16 <em># measures in analyzed piece</em>
beats: 4 <em># beats per measure in analyzed piece</em>
treble_clef: {} <em># all the data for the treble clef</em>
bass_clef: {} <em># all the data for the bass clef</em>
composition: {} <em># all the data for our output</em>
}</pre>
<h3>Clef-specific</h3>
<p>To maintain abstract specificity, I measure the following data per clef and store it in <code>piece.[clefname]_clef</code>.</p>
<h3>Clef Values (note length)</h3>
<p>Values are the number of occurances of each <code>value</code> (note length) in the piece. Note: I chose to disregard triplets for now.</p>
<pre>values: {
1: 0 <em># whole (occurs 0 times)</em>
1.5: 4 <em># dotted half (occurs 4 times)</em>
2: 5 <em># half</em>
3: 12 <em># dotted quarter</em>
4: 28 <em># quarter</em>
6: 0 <em># dotted</em>
8: 12 <em># eighth</em>
12: 0 <em># dotted sixteenth</em>
16: 0 <em># sixteenth</em>
32: 0 <em># thirty-second</em>
}</pre>
<h3>Clef Intervals</h3>
<p><code>Intervals</code> are the number of occurances of each <code>interval</code> relative to the key. For example, if the key was F, the first interval <code>0</code> would be F, the second interval <code>1</code> would be F#, and so on. In an effort to stay abstract, this measurement does not care about octaves. A low and a high F would both count towards <code>0</code>. In the same way, a <code>chord</code> (multiple notes) would count for each note in the chord's interval.</p>
<pre>intervals: {
0: 32 <em># root / perfect unison (F)</em>
1: 0 <em># minor second (F#)</em>
2: 9 <em># major second (G)</em>
3: 0 <em># minor third (G#)</em>
4: 13 <em># major third (A)</em>
5: 0 <em># perfect fourth (A#)</em>
6: 6 <em># tritone (B)</em>
7: 13 <em># perfect fifth (C)</em>
8: 0 <em># minor sixth (C#)</em>
9: 15 <em># major sixth (D)</em>
10: 0 <em># minor seventh (D#)</em>
11: 12 <em># major seventh (E)</em>
}</pre>
<h3>Clef Octaves</h3>
<p><code>Octaves</code> are the number of occurances of each <code>octave</code> relative to the key. For example, in the key of F the F on the clef is the start of the first octave (filed below as <code>2</code>), anything over twelve intervals away would be classified as an octave up (<code>3</code>) and anything below it would be an octave down (<code>1</code>).</p>
<pre>octaves: {
1: 37 <em># clef - 1</em>
2: 67 <em># clef</em>
3: 1 <em># clef + 1</em>
}</pre>
<h3>Clef Chords</h3>
<p>In this program, any note(s) occuring in the clef are classified as a <code>chord</code> (even if a single note). <code>Chords</code> are the number of occurances of each <code>chord</code> size.</p>
<pre>chords: {
1: 9 <em># 1 note</em>
2: 47 <em># 2 notes</em>
3: 0 <em># 3 notes</em>
4: 0 <em># 4 notes</em>
5: 0 <em># 5 notes</em>
}</pre>
<h2>Asking for a unique composition</h2>
<p>Once we have all the data ready for processing, we must tell the program what we want in return. We can specify <code>measures</code>, <code>beats</code> per measure, <code>tempo</code>, <code>resolution</code>, and the <code>root</code> of the key (relative to C).</p>
<pre>composition: {
measures: 32 <em># bars to generate</em>
beats: 4 <em># beats per measure</em>
tempo: 120 <em># tempo</em>
resolution: 16 <em># resolution scale of piece </em>
root: 5 <em># root of key (0-11), 0 is 'C'</em>
}</pre>
<h2>Silence / Noise</h2>
<p>The first thing that must be done when creating a <code>sequence</code> is to calculate a <code>sound_ratio</code>, which I am defining as the <code>amount of sound</code> <code>/</code> <code>(amount of sound + amount of silence)</code>. This allows us to randomly choose to play a note at each step of the grid before we can select <code>chords</code>, <code>values</code>, <code>intervals</code>, and <code>octaves</code>.</p>
<h2>Selecting Anything Randomly</h2>
<p>The program takes our count data and creates <code>chance_packages</code> which are used to select each variable. An example of a <code>chance_package</code> output looks something like this:</p>
<pre>{
chances: [16000, 100000]
vals: [1, 2]
}</pre>
<p>A random number between 0 and 100000 is generated. Then using that number, the program finds the first <code>chance</code> in the package's <code>chances</code> that is greater than our number, grabs its index in the array, and then selects the <code>val</code> in the <code>vals</code> array with that index. In the above case, a random number <code>17000</code> would return <code>2</code> from the <code>vals</code> array and <code>15000</code> would return with a <code>1</code>.</p>
<h2>Building a chord</h2>
<p>First, we randomly calculate the size of <code>chord</code> to use. Say that we got a <code>2</code>. We would then randomly select a <code>value</code> (note length) and two <code>intervals</code>. When each <code>interval</code> is generated, we randomly select an <code>octave</code> for that <code>interval</code>, relative to the key. From there, we need a <code>frequency</code> value (measured in Hz) for the <code>Oscillator</code> to play.</p>
<p>Frequency values can be calculated using the speed of sound and a constant, but I elected to just find the values online and turn the data into an array of arrays, one for each <code>octave</code> (10). Then, using the clef's <code>base_octave</code> value, we can select the right <code>octave</code> array in the <code>frequency</code> array and then select the right <code>interval</code> from that <code>octave</code> array.</p>
<h2>The Performance</h2>
<p>I already went into detail on the same general concept for how the step sequencer works in my <a href="//codepen.io/jakealbaugh/pen/EaypRL" target="blank">random beats</a> pen, and I feel like it would not make sense to copy and paste most of it here.</p>
<h1>Improvements</h1>
<p>Right now this thing requires a lot of input if you want to have it interpret another song. It would be awesome if it could read MIDI data and perform its randomization. I'm also planning on building another version that takes influence inputs via the DOM instead of a hard-coded analysis of an existing piece of music.</p>
<p>I'm looking into how to optimize these web Oscillators more effectively, specifically with layering, as well as tone parameters like attack, decay, etc. Any hints there would be fantastic.</p>
<p>The graphic representation isn't fully accurate yet. There are a few cases in which the drawn notation is improperly placed.</p>
</article>
@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,300italic|Inconsolata);
$c-primary: #545454;
$c-dark: darken($c-primary,10%);
$c-black: darken($c-dark,20%);
$c-med: lighten($c-primary,10%);
$c-gray: #c0c0c0;
$c-light: #f0f0f0;
$c-white: #ffffff;
$c-off-white: transparentize($c-white, 0.8);
$c-barely-white: transparentize($c-white, 0.9);
$clef_h: 100px;
#staff {
height: $clef_h * 2;
width: 95%;
margin: 4em auto;
position: relative;
.clef {
height: $clef_h;
width: 100%;
position: relative;
margin-bottom: 24px;
.beat {
display: block;
position: absolute;
top: 0; bottom: 0;
transition: background 200ms;
&.sus {
z-index: 1;
}
&.chord {
z-index: 2;
font-size: 10px;
$note-di: 4px;
.note {
transition: transform 500ms, background 100ms;
position: absolute;
left: 50%;
transform: translate3d(-50%, -50%, 0) scale(1);
background-color: $c-white;
width: $note-di;
height: $note-di;
border-radius: 50%;
@for $i from 0 through 36 {
&.note-#{$i} {
$ratio: $i / 36;
top: $ratio * 100%;
background-color: hsl($ratio * 360, 50%, 40%);
}
}
}
}
&.active {
transition-duration: 50ms;
/* background: $c-barely-white; */
z-index: 9;
.note {
transform: translate3d(-50%, -50%, 0) scale(2);
@for $i from 0 through 36 {
&.note-#{$i} {
$ratio: $i / 36;
background-color: hsl($ratio * 360, 100%, 60%);
}
}
}
}
}
}
}
.triggers {
width: 100%;
text-align: center;
$button-di: 40px;
a {
color: $c-white;
font-weight: 100;
cursor: pointer;
display: block;
margin: 0 auto;
text-transform: uppercase;
opacity: 0.8;
width: 100px;
transition: opacity 200ms;
font-size: 2em;
&.inactive {
opacity: 0.3;
pointer-events: none;
}
&:hover, &:active {
opacity: 1;
}
&#play {
width: $button-di;
height: $button-di;
line-height: $button-di;
border-radius: 50%;
background-color: $c-off-white;
margin-bottom: 12px;
position: relative;
.play, .stop {
display: block;
position: absolute;
top: 50%;
left: 50%;
}
.play {
border-left: 8px solid $c-white;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
transform: translate3d(-40%, -50%, 0);
}
.stop {
background-color: $c-white;
height: 10px;
width: 10px;
transform: translate3d(-50%, -50%, 0);
display: none;
}
&.playing {
.play { display: none;}
.stop { display: block;}
}
}
&#new {
font-size: 0.875em;
}
}
}
body {
background-color: $c-black;
font-family: 'Source Sans Pro';
font-weight: 300;
color: $c-white;
header, article, #staff {
transition: opacity 300ms;
}
#staff {
opacity: 0.5;
}
&.playing {
header, article {
opacity: 0.4;
}
#staff {
opacity: 1;
}
}
}
pre {
font-family: 'Inconsolata';
font-size: 0.9em;
background-color: $c-barely-white;
box-sizing: border-box;
padding: 12px;
em {
font-style: normal;
color: darken($c-white,70%);
}
}
code {
font-family: 'Inconsolata';
font-size: 0.9em;
padding: 6px;
white-space: nowrap;
background-color: $c-off-white;
}
h1, h2, h3 {
font-weight: 300;
text-transform: uppercase;
letter-spacing: 0.125em;
}
h1 {
margin-bottom: 0.25em;
}
h1 { font-size: 1.4em; }
h2 { font-size: 1.1em; }
h3 { font-size: 0.9em; }
h1, h2 {
text-align: center;
}
header {
text-align: center;
margin-top: 4em;
h1, p {
margin: 0;
}
p {
margin-top: 0.5em;
color: $c-light;
}
}
article {
width: 90%;
max-width: 600px;
margin: 8em auto;
line-height: 1.8;
text-align: justify;
text-justify: inter-word;
h1,h2 {
margin-top: 1em;
}
h1, h2, h3 {
margin-bottom: 0;
& + p {
margin-top: 0;
}
}
a {
color: $c-white;
}
}
View Compiled
###
///
random music generator
jake albaugh
///
###
###
# globals
###
# generate a package of chances and values based on an object containing multiple possible zero values
ChancePkg = (vals) ->
# generating vals and their chances of occuring
new_vals = []
# get total number of vals
vals_count = 0
for value of vals
if vals.hasOwnProperty(value) && vals[value] > 0
vals_count += vals[value]
new_vals.push [value, vals[value]]
# go through new vals and generate chance for each value
new_vals_chances = []
total = 0
for val, i in new_vals
# relative amount for random selection
amount = total + ( (val[1] / vals_count ) * 100000)
# push amount into chances array
new_vals_chances.push amount
# set value to be the value, no longer an array of data
new_vals[i] = parseInt(val[0])
# increase base total
total = amount
# return package which has array for both non-zero vals and their corrosponding chance
return {chances: new_vals_chances, vals: new_vals}
# get the sound to silence ratio at the smallest scale
getSoundRatio = (measures, beats, values) ->
# generating vals and their chances of occuring
new_values = []
# get total number of vals
values_count = 0
for value of values
if values.hasOwnProperty(value) && values[value] > 0
values_count += values[value]
new_values.push [value, values[value]]
# resolution is the smallest sized note
resolution = (beats * (new_values[new_values.length - 1][1] / 4))
# possible is how many resolution sized notes are in the piece
possible = measures * (beats * (resolution / 4))
# usage to be used in the loop
usage = 0
for value in new_values
# how many resolution values are in a single value? (ie. how many sixteenth notes are in a quarter note)
res_value = (1 / value[0]) * resolution
# use resolution value * occurances
usage += res_value * value[1]
return usage / possible
# generate clef values based on data
Piece = (piece) ->
{measures, beats, treble_clef, bass_clef, composition} = piece
this.treble_clef = {
# sound ratio is the presence of sound divided by the presenece of silence in the analyzed piece
sound_ratio: getSoundRatio(measures, beats, treble_clef.values)
# value package has array for both non-zero values and their corrosponding chance
values_pkg: new ChancePkg(treble_clef.values)
# interval package has array for both non-zero intervals and their corrosponding chance
intervals_pkg: new ChancePkg(treble_clef.intervals)
# octaves package has array for both non-zero octaves and their corrosponding chance
octaves_pkg: new ChancePkg(treble_clef.octaves)
# chords package has array for both non-zero chords and their corrosponding chance
chords_pkg: new ChancePkg(treble_clef.chords)
# setting the base octave
base_octave: 5
# tone
wave: 'sine'
# gain
gain: 0.3
}
this.bass_clef = {
# sound ratio is the presence of sound divided by the presenece of silence in the analyzed piece
sound_ratio: getSoundRatio(measures, beats, bass_clef.values)
# value package has array for both non-zero values and their corrosponding chance
values_pkg: new ChancePkg(bass_clef.values)
# interval package has array for both non-zero intervals and their corrosponding chance
intervals_pkg: new ChancePkg(bass_clef.intervals)
# octaves package has array for both non-zero octaves and their corrosponding chance
octaves_pkg: new ChancePkg(bass_clef.octaves)
# chords package has array for both non-zero chords and their corrosponding chance
chords_pkg: new ChancePkg(bass_clef.chords)
# setting the base octave
base_octave: 3
# tone
wave: 'sine'
# gain
gain: 0.3
}
this.composition = composition
return
# initiate audio context
audio_context = undefined
(init = (g) ->
try
# "crossbrowser" audio context.
audio_context = new (g.AudioContext or g.webkitAudioContext)
catch e
console.log "No web audio oscillator support in this browser"
return
) window
# oscillator prototype
Oscillator = (tone) ->
max_gain = tone.gain
this.tone = tone
this.play = () ->
# capturing current time for play start and stop
current_time = audio_context.currentTime
# create oscillator
o = audio_context.createOscillator()
# create gain
gn = audio_context.createGain()
# set waveform
o.type = this.tone.wave
# set frequency
if this.tone.frequency
o.frequency.value = this.tone.frequency
# connect oscillator to gain
o.connect gn
# connect gain to output
gn.connect audio_context.destination
# set gain amount
gn.gain.value = (max_gain / this.tone.vol) / 1
# play it
o.start(current_time)
# stop after sustain
o.stop(current_time + this.tone.sustain)
return this
# note frequencies array of octave arrays that start on c (our root note)
freqs = [
[16.351, 17.324, 18.354, 19.445, 20.601, 21.827, 23.124, 24.499, 25.956, 27.5, 29.135, 30.868]
[32.703, 34.648, 36.708, 38.891, 41.203, 43.654, 46.249, 48.999, 51.913, 55, 58.27, 61.735]
[65.406, 69.296, 73.416, 77.782, 82.407, 87.307, 92.499, 97.999, 103.826, 110, 116.541, 123.471]
[130.813, 138.591, 146.832, 155.563, 164.814, 174.614, 184.997, 195.998, 207.652, 220, 233.082, 246.942]
[261.626, 277.183, 293.665, 311.127, 329.628, 349.228, 369.994, 391.995, 415.305, 440, 466.164, 493.883]
[523.251, 554.365, 587.33, 622.254, 659.255, 698.456, 739.989, 783.991, 830.609, 880, 932.328, 987.767]
[1046.502, 1108.731, 1174.659, 1244.508, 1318.51, 1396.913, 1479.978, 1567.982, 1661.219, 1760, 1864.655, 1975.533]
[2093.005, 2217.461, 2349.318, 2489.016, 2637.021, 2793.826, 2959.955, 3135.964, 3322.438, 3520, 3729.31, 3951.066]
[4186.009, 4434.922, 4698.636, 4978.032, 5274.042, 5587.652, 5919.91, 6271.928, 6644.876, 7040, 7458.62, 7902.132]
[8372.018, 8869.844, 9397.272, 9956.064, 10548.084, 11175.304, 11839.82, 12543.856, 13289.752, 14080, 14917.24, 15804.264]
]
# get random val from chance package
randomVal = (chance_pkg) ->
random = Math.random() * 100000
for chance, i in chance_pkg.chances
return chance_pkg.vals[i] if random < chance
return
# get sequence function
getSequence = (clef, composition) ->
# get the duration in smallest resolution amount
duration = composition.measures * (composition.beats * (composition.resolution / 4))
# note sequence
sequence = []
# while there is still duration
while duration > 0
# random hit
random = Math.random()
# if a hit
if random < clef.sound_ratio
# random chord note count
chord = randomVal(clef.chords_pkg)
# random length for the chord
value = randomVal(clef.values_pkg)
# if there isnt enough space
if ((1 / value) / (1 / composition.resolution)) >= duration
# make it the length of remaining
value = ((1 / duration) / (1 / composition.resolution))
# the new chord
new_chord = {length: (1 / value), notes: []}
# for each note in the chord
for note in [1..chord]
# get a random interval
interval = randomVal(clef.intervals_pkg)
# get the random octave
octave = randomVal(clef.octaves_pkg)
# make intervale relative to key
interval += composition.root
# if key pushes interval into new octave
if interval > 12
interval -= 12
# make the octave relative to the clef's octave
new_octave = clef.base_octave + ((2 - octave) * -1)
# the frequency of the note
note = freqs[new_octave - 1][interval]
# if note doesnt already exist in chord
if new_chord.notes.indexOf(note) == -1
# push the frequency into the chord's notes
new_chord.notes.push {freq: note, int: interval, octave: octave}
else
# duplicate note in chord, ignoring it for now.
console.log 'duplicate note in chord, ignoring it for now.'
# push the chord into the sequence
sequence.push new_chord
# get values resolution-relative value
res_value = Math.floor((1 / value) / (1 / composition.resolution))
# if we need to add sustain notes
if res_value > 1
# add blank values
for blank in [1..res_value - 1]
sequence.push 'sus'
# subtract from the duration
duration -= res_value
else
# it was a miss, add a zero to the sequence
sequence.push 0
# subtract tick from duration
duration--
return sequence
Note = (tone) ->
this.osc = () ->
return new Oscillator(tone)
return
###
# data (to be derived from an analyzed piece of music)
###
piece = {
# piece data (auld lang syne)
measures: 16 # measures in analyzed piece
beats: 4 # beats per measure in analyzed piece
# all our analysis will be relative to each clef
treble_clef: {
# note values are the duration of the note / chord.
# this is a map of how many times each value appears in the analyzed data
values: {
1: 0 # whole
1.5: 4 # dotted half
2: 5 # half
3: 12 # dotted quarter
4: 28 # quarter
6: 0 # dotted eighth
8: 12 # eighth
12: 0 # dotted sixteenth
16: 0 # sixteenth
32: 0 # thirty-second
}
# relative to the key, the intervals are steps between notes and the root (no octaves)
# this includes all single notes and instances of a note in a chord
# this is a map of how many times each interval appears in the analyzed data
intervals: {
0: 32 # root / perfect unison (F)
1: 0 # minor second (F#)
2: 9 # major second (G)
3: 0 # minor third (G#)
4: 13 # major third (A)
5: 0 # perfect fourth (A#)
6: 6 # tritone (B)
7: 13 # perfect fifth (C)
8: 0 # minor sixth (C#)
9: 15 # major sixth (D)
10: 0 # minor seventh (D#)
11: 12 # major seventh (E)
}
# octaves are how many times a note appears in each octave (relative to the key)
octaves: {
1: 37 # clef - 1
2: 67 # clef
3: 1 # clef + 1
}
# instead of using proper chords (dyad, triad, 7th, 9th, and 11th), we only analyze how many notes are in the chord
# this is a map of how many times each chord size appears in the analyzed data
chords: {
1: 9 # 1 note
2: 47 # 2 notes
3: 0 # 3 notes
4: 0 # 4 notes
5: 0 # 5 notes
}
}
bass_clef: {
# note values are the duration of the note / chord.
# this is a map of how many times each value appears in the analyzed data
values: {
1: 0 # whole
1.5: 4 # dotted half
2: 5 # half
3: 12 # dotted quarter
4: 28 # quarter
6: 0 # dotted eighth
8: 12 # eighth
12: 0 # dotted sixteenth
16: 0 # sixteenth
32: 0 # thirty-second
}
# relative to the key, the intervals are steps between notes and the root (no octaves)
# this includes all single notes and instances of a note in a chord
# this is a map of how many times each interval appears in the analyzed data
intervals: {
0: 25 # root / perfect unison (F)
1: 0 # minor second (F#)
2: 1 # major second (G)
3: 0 # minor third (G#)
4: 17 # major third (A)
5: 2 # perfect fourth (A#)
6: 0 # tritone (B)
7: 38 # perfect fifth (C)
8: 1 # minor sixth (C#)
9: 4 # major sixth (D)
10: 0 # minor seventh (D#)
11: 2 # major seventh (E)
}
# octaves are how many times a note appears in each octave (relative to the key)
octaves: {
1: 2 # clef - 1
2: 53 # clef
3: 50 # clef + 1
}
# instead of using proper chords (dyad, triad, 7th, 9th, and 11th), we only analyze how many notes are in the chord
# this is a map of how many times each chord size appears in the analyzed data
chords: {
1: 9 # 1 note
2: 46 # 2 notes
3: 0 # 3 notes
4: 0 # 4 notes
5: 0 # 5 notes
}
}
# defining the desired output composition data
composition: {
measures: 32 # bars to generate
beats: 4 # beats per measure
tempo: 120 # tempo
resolution: 16 # resolution scale of piece
root: 5 # root of key (0-11), 0 is 'C'
}
}
# lets analyze our piece data!
p = new Piece(piece)
# sequence stores
trebleSequence = undefined
bassSequence = undefined
# get sequences
getSequences = () ->
# creating our treble clef
trebleSequence = getSequence(p.treble_clef, p.composition)
bassSequence = getSequence(p.bass_clef, p.composition)
draw_sequences()
getSequenceHtml = (name, composition, sequence) ->
$seq_html = $('<div class="' + name + ' clef"></div>')
beats = composition.measures * (composition.beats * (composition.resolution / 4))
width_increment = 1 / beats * 100
left = 0
for chord in sequence
if typeof chord == "object"
width = width_increment * (chord.length / (1 / composition.resolution))
width = width_increment
classname = 'chord value-' + Math.round( (1 / chord.length) * 100) / 100
classname = classname.replace('.', '-')
notes = ''
for note in chord.notes
out_of_36 = 36 - (note.int + ((note.octave - 1) * 12))
notes += '<span class="note note-' + out_of_36 + '"></span>'
else if chord == 'sus'
classname = 'sus'
width = width_increment
notes = ''
else
width = width_increment
classname = 'blank'
notes = ''
$seq_html.append '<span class="beat ' + classname + '" style="width: ' + width + '%; left: ' + left + '%">' + notes + '</span>'
left += width
return $seq_html
draw_sequences = () ->
composition = p.composition
$staffs = $('<div class="staffs-wrapper"></div>')
$staffs.append getSequenceHtml('treble', composition, trebleSequence)
$staffs.append getSequenceHtml('bass', composition, bassSequence)
$('#staff').html $staffs
return
performance_interval = undefined
# play sequences
playSequences = () ->
composition = p.composition
sequences = [trebleSequence, bassSequence]
waves = [p.treble_clef.wave, p.bass_clef.wave]
gains = [p.treble_clef.gain, p.bass_clef.gain]
# total beat count
beats = composition.measures * (composition.beats * (composition.resolution / 4))
# css width of beat
beat_width = 100 / beats
# relative index
index = 0
# tempo to ms
tempo_time = 60000 / composition.tempo
# single beat instance
next_beat = () ->
for sequence, i in sequences
chord = sequence[index]
# if beat in any rhythm array has value
if typeof chord == "object"
# beats per second
bps = composition.tempo / 60
# how much of a beat is the length
beat_count = chord.length / 0.25
# sustain of the note in seconds
chord_length_secs = beat_count * bps / 2
sustain = (chord_length_secs / bps) - 0.1
for note in chord.notes
# new note
n = new Note({frequency: note.freq, sustain: sustain, wave: waves[i], gain: gains[i], vol: chord.notes.length})
# new oscillator
o = n.osc()
# play oscillator
o.play()
if i == 0 then clef = 'treble' else clef = 'bass'
if typeof chord == "object"
$('.' + clef + ' .beat.active').removeClass 'active'
$('.' + clef + ' .beat.go').removeClass clef + '-go'
$('.' + clef + ' .beat:nth-child(' + (index + 1) + ')').addClass clef + '-go'
else if chord != 'sus'
$('.' + clef + ' .beat.active').removeClass 'active'
$('.' + clef + ' .beat.go').removeClass clef + '-go'
$('.beat:nth-child(' + (index + 1) + ')').addClass 'active'
# update index
index = (index + 1) % beats
# first call of next beat
next_beat()
# ms to relative speed (based on resolution)
time = tempo_time / (composition.resolution / 4)
# set interval for next beat to occur at approriate time
performance_interval = window.setInterval(next_beat, time)
# stop button
stopSequences = () ->
window.clearInterval(performance_interval)
# get sequences
getSequences()
playing = false
play_handler = (p) ->
if p == true
playSequences()
else
stopSequences()
playing = p
$('#play').click () ->
$(this).toggleClass 'playing'
$('body').toggleClass 'playing'
$('#new').toggleClass 'inactive'
play_handler(!playing)
$('#new').click () ->
getSequences()
View Compiled
This Pen doesn't use any external CSS resources.