Rengga Dev – Math.random() is an API in JavaScript. It is a function that gives you a random number. The number returned will be between 0 (inclusive, as in, it’s possible for an actual 0 to be returned) and 1 (exclusive, as in, it’s not possible for an actual 1 to be returned).
# 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()
This program takes the traditional melody of “Auld Lang Syne” and plays random notes from it in piano. A change package is created from the count data and a random number is generated to select a value. The octave is also randomly selected.







