Spaces:
Running
Running
/** | |
* @license | |
* Copyright (c) 2018, Carlos Toxtli (@ctoxtli). | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
/* | |
* VARIABLES | |
*/ | |
let recorder; | |
let visualizer; | |
let instruments; | |
let notesPerChord; | |
let canvases = []; | |
let nowPlaying = 0; | |
let fullSong = null; | |
let runLocal = false; | |
let visualizers = []; | |
let visualizerArr = []; | |
let isInputVoice = true; | |
let isRecording = false; | |
let supportsMidi = false; | |
let audioRecorded = null; | |
let recordingBroken = false; | |
var midiDrums = [36, 38, 42, 46, 41, 43, 45, 49, 51]; | |
let jazzHits = [51, 53, 54, 42, 59, 44, 46]; | |
var audioCtx = new (AudioContext || webkitAudioContext)(); | |
var tf; | |
var z1, z2; | |
var tsynth; | |
var drumMap; | |
var progSeqs; | |
var chordSeqs; | |
var programMap; | |
var playerMaster; | |
var globalReverb; | |
var globalLimiter; | |
const Z_DIM = 256; | |
const numSteps = 6; | |
const MAX_NOTE = 71; | |
const MIN_NOTE = 20; | |
const MAX_PAN = 0.2; | |
const MIN_DRUM = 35; | |
const MAX_DRUM = 81; | |
var sectionSize = 8; | |
var globalCompressor; | |
let numContainers = 9; | |
const STEPS_PER_QUARTER = 24; | |
const HUMANIZE_SECONDS = 0.01; | |
var chords = []; | |
var numChords = 4; | |
var numTimes = sectionSize / numChords; | |
var onsets_frames_uni = null; | |
var multitrack_chords = null; | |
var drum_kit_rnn = null; | |
var midiRecorder = null; | |
var trio_4bar = null; | |
var vae = null; | |
const pulsePattern = true; | |
const temperature = 1.1; | |
var TRIO_EXAMPLE = { | |
notes: [], | |
quantizationInfo: { stepsPerQuarter: 4 } | |
}; | |
/* | |
* METHODS | |
*/ | |
function init() { | |
initObjects(); | |
initModels(); | |
} | |
function initObjects() { | |
WebMidi.enable(function (err) { | |
if (err) { | |
console.log("WebMidi could not be enabled.", err); | |
return; | |
} | |
console.log("WebMidi enabled!"); | |
if (WebMidi.inputs.length > 0) { | |
console.log("Supports WebMidi"); | |
supportsMidi = true; | |
startStreamBtn.disabled = false; | |
var input = WebMidi.inputs[0]; | |
input.addListener('noteon', 1, function (e) { | |
//let note = e.note.number | |
let note = e.note.name + e.note.octave; | |
tsynth.triggerAttack(note); | |
//nsynthPlayer.noteOn(e.note.number); | |
}); | |
input.addListener('noteoff', 1, function (e) { | |
tsynth.triggerRelease(); | |
//nsynthPlayer.noteOff(e.note.number); | |
}); | |
} | |
}); | |
$('.container').each((index, element) => { | |
var containerId = parseInt(element.id.split('_')[1]); | |
element.addEventListener('click', () => { | |
if (playerMaster.isPlaying()) { | |
stopPlayerNum(containerId); | |
} else { | |
startPlayerNum(containerId); | |
} | |
}); | |
}); | |
$('.playCanvas').each((index, element) => { | |
var containerId = parseInt(element.id.split('_')[1]); | |
element.addEventListener('click', () => { | |
if (playerMaster.isPlaying()) { | |
stopPlayerNum(containerId); | |
element.style.backgroundImage = 'url(assets/images/play.svg)'; | |
} else { | |
startPlayerNum(containerId); | |
} | |
}); | |
}); | |
$('.downloadCanvas').each((index, element) => { | |
var containerId = parseInt(element.id.split('_')[1]); | |
element.addEventListener('click', () => { | |
saveSequence(visualizerArr[containerId].noteSequence); | |
}); | |
}); | |
$('.editCanvas').each((index, element) => { | |
var containerId = parseInt(element.id.split('_')[1]); | |
element.addEventListener('click', () => { | |
exportSong(visualizerArr[containerId].noteSequence); | |
}); | |
}); | |
$('.theme').each((index, element) => { | |
var themeId = parseInt(element.id.split('_')[1]); | |
element.addEventListener('click', () => { | |
document.body.className = 'colorScheme' + themeId; | |
}); | |
}); | |
playIconSimple.addEventListener('click', playFullSong); | |
playIconAdvanced.addEventListener('click', playFullSong); | |
stopIconSimple.addEventListener('click', stopFullSong); | |
stopIconAdvanced.addEventListener('click', stopFullSong); | |
btnSaveScore.addEventListener('click', () => { | |
saveSequence(fullSong); | |
}); | |
btnLoadSimple.addEventListener('click', () => { | |
loadSequence(); | |
}); | |
btnRecord.addEventListener('click', onClickRecord); | |
btnRecordSimple.addEventListener('click', onClickRecord); | |
btnSave.addEventListener('click', () => { | |
saveSequence(fullSong); | |
}); | |
btnLoad.addEventListener('click', () => { | |
loadSequence(); | |
}); | |
startStreamBtn.addEventListener('click', () => { | |
hideOptions(); | |
if (!midiRecorder.isRecording()) { | |
midiRecorder.callbackObject = { | |
run: (seq) => { | |
if (seq) { | |
visualizer = new mm.Visualizer(seq, canvas0, { | |
noteRGB: '255, 255, 255', | |
activeNoteRGB: '232, 69, 164', | |
pixelsPerTimeStep: window.innerWidth < 500 ? null: 80, | |
}); | |
} | |
} | |
}; | |
updateWorkingState([startStreamBtn], [btnUpload, btnRecord, btnRecordSimple]); | |
startStreamBtn.textContent = 'STOP'; | |
midiRecorder.start(); | |
} else { | |
startStreamBtn.textContent = 'PROCESSING...'; | |
const seq = midiRecorder.stop(); | |
if (seq) { | |
let ns = mm.sequences.clone(seq); | |
processNotes(ns).then((ns) => { | |
step0.hidden = true; | |
startStreamBtn.textContent = 'MIDI keyboard'; | |
resetUIState(); | |
}); | |
} | |
} | |
}); | |
fileInput.addEventListener('change', (e) => { | |
hideOptions(); | |
updateWorkingState([btnUpload], [btnRecord, btnRecordSimple, startStreamBtn]); | |
requestAnimationFrame(() => requestAnimationFrame(() => { | |
transcribeFromFile(e.target.files[0]); | |
fileInput.value = null; | |
})); | |
return false; | |
}); | |
} | |
function initModels() { | |
tf = mm.tf; | |
onsets_frames_uni = new mm.OnsetsAndFrames('assets/models/onsets_frames_uni'); | |
trio_4bar = new mm.MusicVAE('assets/models/trio_4bar'); | |
multitrack_chords = new mm.MusicVAE('assets/models/multitrack_chords'); | |
midiRecorder = new mm.Recorder(); | |
loadMultitrack(); | |
tsynth = new Tone.FMSynth({ | |
"modulationIndex" : 12.22, | |
"envelope" : { | |
"attack" : 0.01, | |
"decay" : 0.2 | |
}, | |
"modulation" : { | |
"type" : "square" | |
}, | |
"modulationEnvelope" : { | |
"attack" : 0.2, | |
"decay" : 0.01 | |
} | |
}).toMaster(); | |
let soundFontUrl = 'https://storage.googleapis.com/download.magenta.tensorflow.org/soundfonts_js/sgm_plus'; | |
if (runLocal) { | |
soundFontUrl = 'assets/sounds/sgm_plus'; | |
} | |
playerMaster = new mm.SoundFontPlayer(soundFontUrl, globalCompressor, programMap, drumMap); | |
playerMaster.callbackObject = { | |
run: (note) => { | |
const currentNotePosition = visualizerArr[nowPlaying].redraw(note); | |
// See if we need to scroll the container. | |
let containerObj = window['container_' + nowPlaying]; | |
const containerWidth = containerObj.getBoundingClientRect().width; | |
if (currentNotePosition > (containerObj.scrollLeft + containerWidth)) { | |
containerObj.scrollLeft = currentNotePosition - 20; | |
} | |
let playObj = window['play_' + nowPlaying]; | |
playObj.style.backgroundImage = 'url(assets/images/stop.svg)'; | |
}, | |
stop: () => { | |
let containerObj = window['container_' + nowPlaying]; | |
containerObj.classList.remove('playing'); | |
let playObj = window['play_' + nowPlaying]; | |
playObj.style.backgroundImage = 'url(assets/images/play.svg)'; | |
} | |
}; | |
Promise.all([ | |
onsets_frames_uni.initialize(), | |
trio_4bar.initialize(), | |
midiRecorder.initialize(), | |
multitrack_chords.initialize(), | |
initMultitrack() | |
//vae.initialize(), | |
//drum_kit_rnn.initialize() | |
]).then(() => { | |
resetUIState(); | |
modelLoading.hidden = true; | |
modelReady.hidden = false; | |
modelReadySimple.hidden = false; | |
}); | |
// Things are slow on Safari. | |
if (window.webkitOfflineAudioContext) { | |
safariWarning.hidden = false; | |
} | |
// Things are very broken on ios12. | |
if (navigator.userAgent.indexOf('iPhone OS 12_0') >= 0) { | |
iosError.hidden = false; | |
buttons.hidden = true; | |
} | |
} | |
function toNoteSequence(pattern) { | |
return mm.sequences.quantizeNoteSequence( | |
{ | |
ticksPerQuarter: 220, | |
totalTime: pattern.length / 2, | |
timeSignatures: [ | |
{ | |
time: 0, | |
numerator: 4, | |
denominator: 4 }], | |
tempos: [ | |
{ | |
time: 0, | |
qpm: 120 }], | |
notes: _.flatMap(pattern, function (step, index) {return ( | |
step.map(function (d) {return { | |
pitch: midiDrums[d], | |
startTime: index * 0.5, | |
endTime: (index + 1) * 0.5 };}));}) }, 1); | |
} | |
function detectChord(notes) { | |
notes = notes.map(function (n) {return Tonal.Note.pc(Tonal.Note.fromMidi(n.note));}).sort(); | |
return Tonal.PcSet.modes(notes). | |
map(function (mode, i) { | |
var tonic = Tonal.Note.name(notes[i]); | |
var names = Tonal.Dictionary.chord.names(mode); | |
return names.length ? tonic + names[0] : null; | |
}). | |
filter(function (x) {return x;}); | |
} | |
function buildNoteSequence(seed) { | |
var step = 0; | |
var delayProb = pulsePattern ? 0 : 0.3; | |
var notes = seed.map(function (n) { | |
var dur = 1 + (Math.random() < delayProb ? 1 : 0); | |
var note = { | |
pitch: n.note, | |
quantizedStartStep: step, | |
quantizedEndStep: step + dur }; | |
step += dur; | |
return note; | |
}); | |
return { | |
totalQuantizedSteps: _.last(notes).quantizedEndStep, | |
quantizationInfo: { | |
stepsPerQuarter: 1 }, | |
notes: notes }; | |
} | |
function seqToTickArray(seq) { | |
return _.flatMap(seq.notes, function (n) {return ( | |
[n.pitch].concat( | |
pulsePattern ? | |
[] : | |
_.times(n.quantizedEndStep - n.quantizedStartStep - 1, function () {return null;})));}); | |
} | |
function onClickRecord() { | |
// Things are broken on old ios | |
hideOptions(); | |
if (!navigator.mediaDevices) { | |
recordingBroken = true; | |
btnRecord.disabled = true; | |
btnRecordSimple.disabled = true; | |
startStreamBtn.disabled = true; | |
return; | |
} | |
if (isRecording) { | |
isRecording = false; | |
updateRecordBtn(true); | |
recorder.stop(); | |
} else { | |
isRecording = true; | |
updateRecordBtn(false); | |
// Request permissions to record audio. | |
navigator.mediaDevices.getUserMedia({audio: true}).then(stream => { | |
recorder = new window.MediaRecorder(stream); | |
recorder.addEventListener('dataavailable', (e) => { | |
updateWorkingState([btnRecord, btnRecordSimple], [btnUpload, startStreamBtn]); | |
audioRecorded = e.data; | |
requestAnimationFrame(() => requestAnimationFrame(() => transcribeFromFile(e.data))); | |
}); | |
recorder.start(); | |
}); | |
} | |
} | |
function initMultitrack() { | |
return new Promise((resolve, reject) => { | |
resolve(); | |
}); | |
} | |
async function transcribeFromFile(blob) { | |
audioControl.src = window.URL.createObjectURL(blob); | |
onsets_frames_uni.transcribeFromAudioFile(blob).then((transcribed) => { | |
processNotes(transcribed).then((ns) => { | |
step0.hidden = false; | |
resetUIState(); | |
}); | |
}); | |
} | |
function getNoteNumber(letter, scale) { | |
return allNoteLetters[letter] + 12 * (scale - 1); | |
} | |
function getNoteLetter(number) { | |
return allNotes[number % 12]; | |
} | |
function getNoteScale(number) { | |
return parseInt(number / 12); | |
} | |
function getExactChordsFromNotes(notes) { | |
let finalChord = []; | |
let trie = cloneObject(chordTrie); | |
chordsFromNote[notes[0]].forEach((chord1) => { | |
if (trie.hasOwnProperty(chord1)) { | |
chordsFromNote[notes[1]].forEach((chord2) => { | |
if (trie[chord1].hasOwnProperty(chord2)) { | |
chordsFromNote[notes[2]].forEach((chord3) => { | |
if (trie[chord1][chord2].hasOwnProperty(chord3)) { | |
chordsFromNote[notes[3]].forEach((chord4) => { | |
if (trie[chord1][chord2][chord3].hasOwnProperty(chord4)) { | |
finalChord.push([chord1,chord2,chord3,chord4]); | |
} | |
}); | |
} | |
}); | |
} | |
}); | |
} | |
}); | |
return finalChord; | |
} | |
function getProbableChordsFromNotesOld(notes) { | |
let finalChord = {chords:[],max:0}; | |
let trie = cloneObject(chordTrie); | |
for (let i in trie) { | |
for (let j in trie[i]) { | |
for (let k in trie[i][j]) { | |
for (let l in trie[i][j][k]) { | |
let count = 0; | |
if (chordsFromNote[notes[0]].indexOf(i) != -1) | |
count++; | |
if (chordsFromNote[notes[1]].indexOf(j) != -1) | |
count++; | |
if (chordsFromNote[notes[2]].indexOf(k) != -1) | |
count++; | |
if (chordsFromNote[notes[3]].indexOf(l) != -1) | |
count++; | |
if (count > finalChord.max) { | |
finalChord.max = count; | |
finalChord.chords = []; | |
} | |
if (count == finalChord.max) { | |
finalChord.chords.push([i,j,k,l]) | |
} | |
} | |
} | |
} | |
} | |
return finalChord; | |
} | |
function truncateNumber(decimal) { | |
return Math.floor(decimal * 100) / 100; | |
} | |
function displayPrediction(pred) { | |
refGenre.innerText = pred[0]; | |
refDance.innerText = truncateNumber(pred[1][0])*100 + '%'; | |
refRock.innerText = truncateNumber(pred[1][1])*100 + '%'; | |
refJazz.innerText = truncateNumber(pred[1][2])*100 + '%'; | |
} | |
function displayChords(chordRes) { | |
refScale.innerText = chordRes[1]; | |
refChord.innerText = chordRes[0].join(', '); | |
} | |
function calculateMode(numbers) { | |
// as result can be bimodal or multi-modal, | |
// the returned result is provided as an array | |
// mode of [3, 5, 4, 4, 1, 1, 2, 3] = [1, 3, 4] | |
var modes = [], count = [], i, number, maxIndex = 0; | |
for (i = 0; i < numbers.length; i += 1) { | |
number = numbers[i]; | |
count[number] = (count[number] || 0) + 1; | |
if (count[number] > maxIndex) { | |
maxIndex = count[number]; | |
} | |
} | |
for (i in count) | |
if (count.hasOwnProperty(i)) { | |
if (count[i] === maxIndex) { | |
modes.push(Number(i)); | |
} | |
} | |
return modes; | |
} | |
function normalizeNotes(ns) { | |
let seq = mm.sequences.clone(ns); | |
const notes_scale = 12 | |
const min_pitch = 36 | |
const max_pitch = 71 | |
const min_scale = parseInt(min_pitch / notes_scale) | |
const max_scale = parseInt(max_pitch / notes_scale) | |
const med_scale = Math.round((min_scale + max_scale) / 2) | |
let mod_scales = med_scale; | |
let scales = [] | |
for (let i in seq.notes) { | |
scales.push(parseInt(seq.notes[i].pitch / notes_scale)); | |
} | |
const mods_scales = calculateMode(scales); | |
if (mods_scales.length == 1) { | |
mod_scales = mods_scales[0]; | |
} else { | |
if (mods_scales.indexOf(med_scale) == -1) { | |
mod_scales = med_scale; | |
} else { | |
mod_scales = Math.max(mods_scales); | |
} | |
} | |
const dif_scales = med_scale - mod_scales; | |
const adjust = notes_scale * dif_scales; | |
let toDelete = []; | |
for (let i = 0; i < seq.notes.length; i++) { | |
let note = seq.notes[i].pitch; | |
let new_value = seq.notes[i].pitch + adjust; | |
if (new_value >= MIN_NOTE && new_value <= MAX_NOTE) { | |
seq.notes[i].pitch = new_value | |
} else { | |
toDelete.push(i); | |
} | |
} | |
while (toDelete.length > 0) { | |
let index = toDelete.pop(); | |
seq.notes.splice(index, 1); | |
} | |
return seq; | |
} | |
function playFullSong() { | |
playerMaster.start(fullSong); | |
playIconSimple.style.display = 'none'; | |
stopIconSimple.style.display = 'block'; | |
playIconAdvanced.style.display = 'none'; | |
stopIconAdvanced.style.display = 'block'; | |
} | |
function stopFullSong() { | |
playerMaster.stop(); | |
stopIconSimple.style.display = 'none'; | |
playIconSimple.style.display = 'block'; | |
stopIconAdvanced.style.display = 'none'; | |
playIconAdvanced.style.display = 'block'; | |
} | |
function getInstrumentsFromGenre(genre) { | |
let basses = [32, 33, 34, 35, 36, 37, 38, 39]; | |
let drumKicks = [35, 36]; | |
let melodies = [0]; | |
let drumHits = [42]; | |
let result = { | |
'melody': 0, | |
'bass': 32, | |
'drumHit': 42, | |
'drumKick': 36 | |
} | |
if (genre == 'rock') { | |
melodies = [24, 25, 26, 27, 28, 29, 30, 31]; | |
drumHits = [41, 43, 45, 47, 48]; | |
} else if (genre == 'electronic') { | |
melodies = [80, 81, 82, 83, 84, 85, 86, 87]; | |
drumHits = [42, 44, 46]; | |
} else if (genre == 'jazz') { | |
melodies = [56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67]; | |
drumHits = [51, 53, 54, 59]; | |
} | |
result.bass = _.sample(basses); | |
result.melody = _.sample(melodies); | |
result.drumHit = _.sample(drumHits); | |
result.drumKick = _.sample(drumKicks); | |
return result; | |
} | |
function getDrumFromGenre(genre, genreInst) { | |
let drum = getDrum(genre, genreInst.drumKick, genreInst.drumHit); | |
return mm.sequences.unquantizeSequence(drum); | |
} | |
function processNotes(ns) { | |
return new Promise(async (resolve, reject) => { | |
if (isInputVoice) { | |
let original = mm.sequences.clone(ns); | |
visualizeSequence(original, 0); | |
ns = normalizeNotes(ns); | |
ns = moveToTimeZero(ns); | |
let melody = mm.sequences.clone(ns); | |
melody = removeOverlapped(melody); | |
melody = resizeSequence(melody); | |
visualizeSequence(melody, 1); | |
let dupMelody = duplicateSequence(melody); | |
let seqVector = sequenceToVector(dupMelody); | |
let predGenreArr = await predict_genre([seqVector]); | |
let pred_genre = predGenreArr[0]; | |
let genreInst = getInstrumentsFromGenre(pred_genre); | |
melody = setParamsToNotes(melody, {instrument:0, program: genreInst.melody, isDrum: false}); | |
visualizeSequence(melody, 2); | |
let melodyProc = setParamsToNotes(melody, {instrument:0, program: 0, isDrum: false}); | |
let chordRes = getChordsFromMelody(dupMelody); | |
chords = chordRes[0]; | |
displayChords(chordRes); | |
displayPrediction(predGenreArr); | |
let bass = mm.sequences.clone(melody); | |
bass = convertToPad(bass); | |
bass = setParamsToNotes(bass, {instrument:1, program: genreInst.bass, isDrum: false}); | |
let drum = getDrumFromGenre(pred_genre, genreInst); | |
drum = setParamsToNotes(drum, {instrument:2, isDrum: true}); | |
visualizeSequence(drum, 3); | |
bass = moveOctaves(bass, -1); | |
visualizeSequence(bass, 4); | |
melody = moveOctaves(melody, 1); | |
let genBlock = createBlock(); | |
addBlock(genBlock, melody, 0); | |
addBlock(genBlock, bass, 0); | |
addBlock(genBlock, drum, 0); | |
//let blockVis = prepareToVisualize(genBlock); | |
visualizeSequence(genBlock, 5); | |
let procBlock = createBlock(); | |
addBlock(procBlock, melodyProc, 0); | |
addBlock(procBlock, bass, 0); | |
addBlock(procBlock, drum, 0); | |
createTrio(procBlock).then((accompaniment) => { | |
let generatedAcc = accompaniment[0]; | |
let unqAcc = mm.sequences.unquantizeSequence(generatedAcc); | |
let oriInst = separateInstruments(genBlock); | |
let newInst = separateInstruments(unqAcc); | |
let oriDruNS = oriInst['2']; | |
let newDruNS = newInst['2']; | |
let oriBasNS = oriInst['1']; | |
let newBasNS = newInst['1']; | |
let oriMelNS = oriInst['0']; | |
let newMelNS = newInst['0']; | |
newMelNS = setParamsToNotes(newMelNS, {instrument:0, program: genreInst.melody, isDrum: false}); | |
let newBlock = createBlock(); | |
addBlock(newBlock, newMelNS, 0); | |
addBlock(newBlock, newBasNS, 0); | |
addBlock(newBlock, newDruNS, 0); | |
visualizeSequence(newBlock, 6); | |
let testSong = createBlock(); | |
addBlock(testSong, drum, 0); | |
addBlock(testSong, oriMelNS, 0); | |
addBlock(testSong, newBasNS, 0); | |
visualizeSequence(testSong, 7); | |
let sampleBlock = createBlock(); | |
addBlock(sampleBlock, drum, 0); | |
addBlock(sampleBlock, oriMelNS, 0); | |
addBlock(sampleBlock, newBasNS, 0); | |
encodeSong(sampleBlock).then(z => { | |
z1 = z; | |
generateSample(z => { | |
z2 = z; | |
processSong().then(finalPieces => { | |
fullSong = finalPieces[0]; | |
visualizeSequence(finalPieces[1][0], 8); | |
visualizeSequence(finalPieces[1][finalPieces[1].length-1], 9); | |
visualizeSequence(fullSong, 10); | |
playerMaster.loadSamples(fullSong).then(() => { | |
songOptions.hidden = false; | |
songOptionsAdvanced.hidden = false; | |
playIconSimple.style.display = 'block'; | |
playIconAdvanced.style.display = 'block'; | |
resolve(fullSong); | |
}); | |
}); | |
}); | |
}); | |
}); | |
} else { | |
resolve(ns); | |
} | |
}); | |
} | |
function notesToSequence(notes, time) { | |
let newSeq = new mm.NoteSequence({notes: notes, totalTime: time}) | |
return newSeq; | |
} | |
function separateInstruments(ns) { | |
let drumsId = '2'; | |
let exit = {}; | |
exit[drumsId] = []; | |
for (var i in ns.notes) { | |
if (!ns.notes[i].isDrum) { | |
if (!exit.hasOwnProperty(ns.notes[i].instrument)) { | |
exit[ns.notes[i].instrument] = []; | |
} | |
exit[ns.notes[i].instrument].push(ns.notes[i]); | |
} else { | |
exit[drumsId].push(ns.notes[i]); | |
} | |
} | |
for (var i in exit) { | |
exit[i] = notesToSequence(exit[i], ns.totalTime); | |
} | |
return exit; | |
} | |
function prepareToVisualize(ns) { | |
let seq = mm.sequences.clone(ns); | |
for (let i in seq.notes) { | |
seq.notes[i].instrument = seq.notes[i].program; | |
} | |
return seq; | |
} | |
function getQuantizedSequence(ns, size) { | |
let qns = mm.sequences.clone(ns); | |
if (!mm.sequences.isQuantizedSequence(qns)) { | |
qns = mm.sequences.quantizeNoteSequence(qns, size); | |
} | |
return qns; | |
} | |
function createTrio(ns) { | |
return new Promise((resolve, reject) => { | |
let qns = mm.sequences.quantizeNoteSequence(ns, 4); | |
TRIO_EXAMPLE.notes = qns.notes; | |
TRIO_EXAMPLE.totalQuantizedSteps = 64; | |
let inputs = [TRIO_EXAMPLE]; | |
trio_4bar.encode(inputs).then((z) => { | |
trio_4bar.decode(z).then((recon) => { | |
resolve(recon); | |
}); | |
}); | |
}); | |
} | |
function concatNoteSequences(seqs, individualDuration) { | |
var concatSeq = mm.sequences.clone(seqs[0]); | |
var _loop_1 = function (i) { | |
Array.prototype.push.apply(concatSeq.notes, seqs[i].notes.map(function (n) { | |
var newN = mm.sequences.clone(n); | |
newN.quantizedStartStep += individualDuration * i; | |
newN.quantizedEndStep += individualDuration * i; | |
return newN; | |
})); | |
}; | |
for (var i = 1; i < seqs.length; ++i) { | |
_loop_1(i); | |
} | |
return concatSeq; | |
} | |
function createBlock() { | |
let ns = new mm.NoteSequence({notes: [], totalTime: 0}); | |
return ns; | |
} | |
function addBlock(ns1, ns2, time) { | |
let block = mm.sequences.clone(ns2); | |
var notes = _.sortBy(block.notes, 'startTime'); | |
var lastTime = ns1.totalTime; | |
for (var i in notes) { | |
notes[i].startTime += time; | |
notes[i].endTime += time; | |
if (notes[i].endTime > lastTime) { | |
lastTime = notes[i].endTime; | |
} | |
ns1.notes.push(notes[i]); | |
} | |
ns1.totalTime = lastTime; | |
return ns1; | |
} | |
function mixNotes(ns1, ns2) { | |
for (var i in ns2.notes) { | |
ns1.notes.push(ns2.notes[i]); | |
} | |
return ns1; | |
} | |
function mixNotesAt(ns1, ns2, at) { | |
for (var i in ns2.notes) { | |
ns2.notes[i].startTime += at; | |
ns2.notes[i].endTime += at; | |
ns1.notes.push(ns2.notes[i]); | |
} | |
return ns1; | |
} | |
function removeOverlapped(ns) { | |
if (ns.notes.length == 0) | |
return ns; | |
var notes = _.sortBy( ns.notes, 'startTime' ); | |
let range = notes[0]; | |
let toDelete = []; | |
for (let i = 1; i < notes.length; i++) { | |
if (notes[i].startTime >= range.startTime && notes[i].startTime <= range.endTime) { | |
if (notes[i].endTime <= range.endTime) { | |
toDelete.push(i); | |
} else { | |
notes[i].startTime = range.endTime; | |
range = notes[i]; | |
} | |
} else if (notes[i].startTime < range.startTime) { | |
if (notes[i].endTime > range.endTime) { | |
notes[i].startTime = range.endTime; | |
range = notes[i]; | |
} else if (notes[i].endTime <= range.endTime) { | |
toDelete.push(i); | |
} | |
} else { | |
range = notes[i]; | |
} | |
} | |
ns.totalTime = range.endTime; | |
while (toDelete.length > 0) { | |
notes.splice(toDelete.pop(), 1); | |
} | |
ns.notes = notes; | |
return ns; | |
} | |
function convertToPad(ns) { | |
if (ns.notes.length == 0) | |
return ns; | |
let toDelete = []; | |
var notes = _.sortBy( ns.notes, 'startTime' ); | |
var last = notes[0]; | |
for (let i = 1; i < notes.length; i++) { | |
last.endTime = notes[i].startTime; | |
if (last.pitch != notes[i].pitch) { | |
last = notes[i]; | |
} else { | |
toDelete.push(i); | |
} | |
} | |
last.endTime = ns.totalTime; | |
while (toDelete.length > 0) { | |
notes.splice(toDelete.pop(), 1); | |
} | |
ns.notes = notes; | |
return ns; | |
} | |
function moveOctaves(ns, octaves) { | |
if (ns.notes.length == 0) | |
return ns; | |
let toAdjust = 12 * octaves; | |
for (let i = 0; i < ns.notes.length; i++) { | |
ns.notes[i].pitch += toAdjust | |
} | |
return ns; | |
} | |
function resizeSequence(ns) { | |
let octaves = parseInt(ns.totalTime / 8); | |
let silence = getAverageSilence(ns); | |
let fitToTime = octaves == 0 ? 4 : octaves * 8; | |
let ns2 = expandSequence(ns, fitToTime, silence); | |
ns.totalTime = fitToTime; | |
return ns2; | |
} | |
function duplicateSequence(ns) { | |
let seq = mm.sequences.clone(ns); | |
let octaves = parseInt(seq.totalTime / 8); | |
let fitToTime = octaves == 0? 4 : octaves * 8; | |
let dupSeq = duplicateFrom(seq, fitToTime); | |
return dupSeq; | |
} | |
function getChordsFromMelody(ns) { | |
notesPerChord = getNotesPerChord(ns); | |
return getChordFromNotes(notesPerChord); | |
} | |
function getNotesPerChord(ns) { | |
var notes = [{},{},{},{}]; | |
var notesGlobal = {}; | |
let exitLocal = []; | |
let exitGlobal = []; | |
let cutEverySecs = 2; | |
let pitchKey = ''; | |
for (let i in ns.notes) { | |
let intStart = parseInt(parseInt(ns.notes[i].startTime) / cutEverySecs); | |
let intEnd = parseInt(parseInt(ns.notes[i].endTime) / cutEverySecs); | |
if (intStart < notes.length && intEnd < notes.length) { | |
for (let j = intStart; j <= intEnd; j++) { | |
let pitch = ns.notes[i].pitch; | |
let duration = (ns.notes[i].endTime - (j * cutEverySecs)) - (ns.notes[i].startTime - (j * cutEverySecs)); | |
if (duration > cutEverySecs) | |
duration = cutEverySecs; | |
pitchKey = pitch + ''; | |
if (!notes[j].hasOwnProperty(pitchKey)) | |
notes[j][pitchKey] = 0; | |
notes[j][pitchKey] += duration; | |
} | |
} else { | |
break; | |
} | |
} | |
for (let i in notes) { | |
exitLocal.push(_.sortBy(_.toPairs(notes[i]), 1).reverse()); | |
for (let j in notes[i]) { | |
if (!notesGlobal.hasOwnProperty(j)) | |
notesGlobal[j] = 0; | |
notesGlobal[j] += notes[i][j]; | |
} | |
} | |
exitGlobal = _.sortBy(_.toPairs(notesGlobal), 1).reverse(); | |
return {local: exitLocal, global: exitGlobal}; | |
} | |
function getChordFromNotes(notes) { | |
let tonal = getNoteLetter(notes.global[0][0]); | |
let scale = getNoteScale(notes.global[0][0]); | |
let notesPerTime = [ | |
getNoteLetter(notes.local[0][0][0]), | |
getNoteLetter(notes.local[1][0][0]), | |
getNoteLetter(notes.local[2][0][0]), | |
getNoteLetter(notes.local[3][0][0]) | |
] | |
let probChrods = getProbableChordsFromNotes(tonal, notesPerTime); | |
let finalChord = probChrods[Math.floor(Math.random()*probChrods.length)]; | |
return [finalChord, tonal, scale]; | |
} | |
function setParamsToNotes(ns, params) { | |
let seq = mm.sequences.clone(ns); | |
for (var i in seq.notes) { | |
for (var j in params) { | |
seq.notes[i][j] = params[j]; | |
} | |
} | |
return seq; | |
} | |
function setInstrument(ns, instrument) { | |
for (var i in ns.notes) { | |
ns.notes[i].instrument = instrument; | |
ns.notes[i].isDrum = false; | |
ns.notes[i].program = instrument; | |
// ns.notes[i].velocity = 100; | |
} | |
return ns; | |
} | |
function expandSequence(ns, time, silence) { | |
let length = ns.totalTime + silence; | |
let diff = time - length; | |
let ratio = Math.abs(diff) / length; | |
let totalTime = time; | |
if (diff > 0) { | |
ratio += 1; | |
} else { | |
ratio = 1 - ratio; | |
} | |
for (var i in ns.notes) { | |
ns.notes[i].startTime *= ratio; | |
ns.notes[i].endTime *= ratio; | |
totalTime = ns.notes[i].endTime; | |
} | |
ns.totalTime = totalTime; | |
return ns; | |
} | |
function getAverageSilence(ns) { | |
let times = 0; | |
let total = 0; | |
if (ns.notes.length > 1) { | |
let prev = ns.notes[0]; | |
for (let i = 1; i < ns.notes.length; i++) { | |
let diff = ns.notes[i].startTime - prev.endTime; | |
if (diff > 0) { | |
total += diff; | |
times++; | |
} | |
prev = ns.notes[i]; | |
} | |
if (times > 0) | |
total /= times; | |
} | |
return total; | |
} | |
function duplicateFrom(ns, from) { | |
let totalTime = from; | |
for (let i in ns.notes) { | |
let note = cloneObject(ns.notes[i]); | |
note.startTime += from; | |
note.endTime += from; | |
totalTime = note.endTime; | |
ns.notes.push(note); | |
} | |
ns.totalTime = totalTime; | |
return ns; | |
} | |
function concatenateFrom(source, ns, from) { | |
let totalTime = from; | |
let cloned = mm.sequences.clone(ns); | |
for (let i in cloned.notes) { | |
let note = cloned.notes[i]; | |
note.startTime += from; | |
note.endTime += from; | |
totalTime = note.endTime; | |
source.notes.push(note); | |
} | |
source.totalTime = totalTime; | |
return source; | |
} | |
function cloneObject(obj) { | |
return JSON.parse(JSON.stringify(obj)); | |
} | |
function visualizeSequence(ns, n) { | |
let canvasObj = window['canvas' + n]; | |
visualizerArr[n] = new mm.Visualizer(ns, canvasObj, { | |
noteRGB: '255, 255, 255', | |
activeNoteRGB: '232, 69, 164', | |
pixelsPerTimeStep: window.innerWidth < 500 ? null: 80, | |
}); | |
} | |
function fixPitch(ns) { | |
let fns = mm.sequences.clone(ns); | |
let seq = fns.notes.map(a => a.pitch); | |
let minVal = 48; | |
let minSeq = Math.min(...seq); | |
if (minSeq < minVal) { | |
let diff = minVal - minSeq; | |
for (let i in fns.notes) { | |
fns.notes[i].pitch += diff; | |
} | |
} | |
return fns; | |
} | |
function fixMaxPitch(seq) { | |
let minVal = 48; | |
let minSeq = Math.min(...seq); | |
if (minSeq < minVal) { | |
let diff = minVal - minSeq; | |
for (let i in seq) { | |
seq[i] += diff; | |
} | |
} | |
return seq; | |
} | |
function createArp(seq) { | |
let notes = []; | |
let time = 0; | |
let duration = 0.125; | |
let step = duration * 2; | |
let noteSize = 8; | |
let rounds = parseInt(seq.length / noteSize); | |
if (rounds == 0 && seq.length >= 4) { | |
rounds = 1; | |
seq.length = 4; | |
seq = seq.concat(seq); | |
} | |
seq.length = rounds * noteSize; | |
seq = seq.concat(seq); | |
for (let i in seq) { | |
notes.push({pitch: seq[i], startTime: time, endTime: time + duration, velocity: 60}); | |
time += step; | |
} | |
let newSeq = new mm.NoteSequence({notes: notes, totalTime: time}); | |
return newSeq; | |
} | |
function combineSongs(song1, song2) { | |
let notes = []; | |
while (song1.notes.length != 0 || song2.notes.length !=0) { | |
if (song1.notes.length > 0 && song2.notes.length > 0) { | |
if (song1.notes[0].startTime < song2.notes[0].startTime) { | |
notes.push(song1.notes.shift()); | |
} else { | |
notes.push(song2.notes.shift()); | |
} | |
} else if (song1.notes.length > 0) { | |
notes.push(song1.notes.shift()); | |
} else if (song2.notes.length > 0) { | |
notes.push(song2.notes.shift()); | |
} | |
} | |
let totalTime = song1.totalTime>song2.totalTime?song1.totalTime:song2.totalTime; | |
let newSeq = new mm.NoteSequence({notes: notes, totalTime: totalTime}) | |
return newSeq; | |
} | |
function moveToTimeZero(ns) { | |
if (ns.notes.length > 0) { | |
let startTime = ns.notes.reduce((min, p) => p.y < min ? p.y : min, ns.notes[0].startTime); | |
let endTime = 0; | |
for (let i in ns.notes) { | |
ns.notes[i].startTime -= startTime; | |
if (ns.notes[i].startTime < 0) | |
ns.notes[i].startTime = 0 | |
ns.notes[i].endTime -= startTime; | |
if (ns.notes[i].endTime > endTime) | |
endTime = ns.notes[i].endTime; | |
} | |
ns.totalTime = endTime; | |
} | |
return ns; | |
} | |
function startPlayerNum(n) { | |
nowPlaying = n; | |
let containerObj = window['container_' + n]; | |
containerObj.scrollLeft = 0; | |
containerObj.classList.add('playing'); | |
playerMaster.start(visualizerArr[n].noteSequence, 0, {loop:true}); | |
} | |
function stopPlayerNum(n) { | |
let containerObj = window['container_' + n]; | |
playerMaster.stop(); | |
containerObj.classList.remove('playing'); | |
} | |
function updateWorkingState(actives, inactives) { | |
for (let active of actives) { | |
if (active) { | |
active.classList.add('working'); | |
} | |
} | |
for (let inactive of inactives) { | |
if (inactive) { | |
inactive.setAttribute('disabled', true); | |
} | |
} | |
} | |
function updateRecordBtn(defaultState) { | |
const el = btnRecord.firstElementChild; | |
el.textContent = defaultState ? 'Record audio' : 'Stop'; | |
const elSimple = btnRecordSimple.firstElementChild; | |
elSimple.textContent = defaultState ? 'Record audio' : 'Stop'; | |
} | |
function setActiveLevel(event, isAdvanced) { | |
document.querySelector('button.player.active').classList.remove('active'); | |
event.target.classList.add('active'); | |
if (isAdvanced) { | |
simple.style.display = 'none'; | |
btnSimpleMode.classList.remove('chosen'); | |
advance.style.display = 'block'; | |
btnAdvanceMode.classList.add('chosen'); | |
} else { | |
simple.style.display = 'block'; | |
btnSimpleMode.classList.add('chosen'); | |
advance.style.display = 'none'; | |
btnAdvanceMode.classList.remove('chosen'); | |
} | |
} | |
function hideOptions() { | |
songOptions.hidden = true; | |
songOptionsAdvanced.hidden = true; | |
} | |
function resetUIState() { | |
btnUpload.classList.remove('working'); | |
btnUpload.removeAttribute('disabled'); | |
if (supportsMidi) { | |
startStreamBtn.classList.remove('working'); | |
startStreamBtn.removeAttribute('disabled'); | |
} | |
btnRecord.classList.remove('working'); | |
btnRecordSimple.classList.remove('working'); | |
if (!recordingBroken) { | |
btnRecord.removeAttribute('disabled'); | |
btnRecordSimple.removeAttribute('disabled'); | |
} | |
} | |
function saveMidi(event) { | |
event.stopImmediatePropagation(); | |
saveAs(new File([mm.sequenceProtoToMidi(visualizer.noteSequence)], 'transcription.mid')); | |
} | |
function loadMultitrack() { | |
globalCompressor = new mm.Player.tone.MultibandCompressor(); | |
globalReverb = new mm.Player.tone.Freeverb(0.25); | |
globalLimiter = new mm.Player.tone.Limiter(); | |
globalCompressor.connect(globalReverb); | |
globalReverb.connect(globalLimiter); | |
globalLimiter.connect(mm.Player.tone.Master); | |
programMap = new Map(); | |
for (let i=0; i<128; i++) { | |
const programCompressor = new mm.Player.tone.Compressor(); | |
const pan = 2 * MAX_PAN * Math.random() - MAX_PAN; | |
const programPanner = new mm.Player.tone.Panner(pan); | |
programMap.set(i, programCompressor); | |
programCompressor.connect(programPanner); | |
programPanner.connect(globalCompressor); | |
} | |
drumMap = new Map(); | |
for (let i=MIN_DRUM; i<=MAX_DRUM; i++) { | |
const drumCompressor = new mm.Player.tone.Compressor(); | |
const pan = 2 * MAX_PAN * Math.random() - MAX_PAN; | |
const drumPanner = new mm.Player.tone.Panner(pan); | |
drumMap.set(i, drumCompressor); | |
drumCompressor.connect(drumPanner); | |
drumPanner.connect(globalCompressor); | |
} | |
} | |
function NSynthLoaded(urls) { | |
//console.log(urls); | |
} | |
function generateSample(doneCallback) { | |
const z = tf.randomNormal([1, Z_DIM]); | |
z.data().then(zArray => { | |
z.dispose(); | |
doneCallback(zArray); | |
}); | |
} | |
// Generate chord progression for each alpha. | |
function generateProgressions(doneCallback) { | |
let temp = []; | |
for (let i=0; i<numSteps; i++) { | |
temp.push([]); | |
} | |
generateInterpolations(0, temp, seqs => { | |
chordSeqs = seqs; | |
concatSeqs = chordSeqs.map(s => concatenateSequences(s)); | |
progSeqs = concatSeqs.map(seq => { | |
const mergedSeq = mm.sequences.mergeInstruments(seq); | |
const progSeq = mm.sequences.unquantizeSequence(mergedSeq); | |
progSeq.ticksPerQuarter = STEPS_PER_QUARTER; | |
return progSeq; | |
}); | |
const fullSeq = concatenateSequences(concatSeqs); | |
const mergedFullSeq = mm.sequences.mergeInstruments(fullSeq); | |
let notes = []; | |
for (let i = 0; i <= 128; i++) { | |
notes.push({ | |
pitch:60, | |
startTime:0, | |
endTime: 1, | |
instrument: i, | |
isDrum: false, | |
program: 0, | |
quantizedEndStep: 2, | |
quantizedStartStep: 1, | |
velocity: 113 | |
}); | |
} | |
let newSeq = new mm.NoteSequence({notes: notes, totalTime: 1}) | |
//playerMaster.loadSamples(mergedFullSeq) | |
playerMaster.loadSamples(mergedFullSeq) | |
.then(doneCallback); | |
}); | |
} | |
// Interpolate the two styles for a single chord. | |
function interpolateSamples(chord, doneCallback) { | |
const z1Tensor = tf.tensor2d(z1, [1, Z_DIM]); | |
const z2Tensor = tf.tensor2d(z2, [1, Z_DIM]); | |
const zInterp = slerp(z1Tensor, z2Tensor, numSteps); | |
multitrack_chords.decode(zInterp, undefined, [chord], STEPS_PER_QUARTER) | |
.then(sequences => doneCallback(sequences)); | |
} | |
// Construct spherical linear interpolation tensor. | |
function slerp(z1, z2, n) { | |
const norm1 = tf.norm(z1); | |
const norm2 = tf.norm(z2); | |
const omega = tf.acos(tf.matMul(tf.div(z1, norm1), | |
tf.div(z2, norm2), | |
false, true)); | |
const sinOmega = tf.sin(omega); | |
const t1 = tf.linspace(1, 0, n); | |
const t2 = tf.linspace(0, 1, n); | |
const alpha1 = tf.div(tf.sin(tf.mul(t1, omega)), sinOmega).as2D(n, 1); | |
const alpha2 = tf.div(tf.sin(tf.mul(t2, omega)), sinOmega).as2D(n, 1); | |
const z = tf.add(tf.mul(alpha1, z1), tf.mul(alpha2, z2)); | |
return z; | |
} | |
// Generate interpolations for all chords. | |
function generateInterpolations(chordIndex, result, doneCallback) { | |
if (chordIndex === numChords) { | |
doneCallback(result); | |
} else { | |
interpolateSamples(chords[chordIndex], seqs => { | |
for (let i=0; i<numSteps; i++) { | |
result[i].push(seqs[i]); | |
} | |
generateInterpolations(chordIndex + 1, result, doneCallback); | |
}) | |
} | |
} | |
// Concatenate multiple NoteSequence objects. | |
function concatenateSequences(seqs) { | |
const seq = mm.sequences.clone(seqs[0]); | |
let numSteps = seqs[0].totalQuantizedSteps; | |
for (let i=1; i<seqs.length; i++) { | |
const s = mm.sequences.clone(seqs[i]); | |
s.notes.forEach(note => { | |
note.quantizedStartStep += numSteps; | |
note.quantizedEndStep += numSteps; | |
seq.notes.push(note); | |
}); | |
numSteps += s.totalQuantizedSteps; | |
} | |
seq.totalQuantizedSteps = numSteps; | |
return seq; | |
} | |
// Randomly adjust note times. | |
function humanize(s) { | |
const seq = mm.sequences.clone(s); | |
seq.notes.forEach((note) => { | |
let offset = HUMANIZE_SECONDS * (Math.random() - 0.5); | |
if (seq.notes.startTime + offset < 0) { | |
offset = -seq.notes.startTime; | |
} | |
if (seq.notes.endTime > seq.totalTime) { | |
offset = seq.totalTime - seq.notes.endTime; | |
} | |
seq.notes.startTime += offset; | |
seq.notes.endTime += offset; | |
}); | |
return seq; | |
} | |
function createSong(callback, ns, idx, chordIdx, times, second) { | |
const unquantizedSeq = mm.sequences.unquantizeSequence(chordSeqs[idx][chordIdx]); | |
let humanized = humanize(unquantizedSeq); | |
ns.push(humanized); | |
second += 2; | |
if (chordIdx == (numChords - 1)) { | |
times = (times + 1) % numTimes; | |
if (times == 0) { | |
idx = (idx + 1) % numSteps; | |
} | |
} | |
chordIdx = (chordIdx + 1) % numChords; | |
if (idx == 0 && chordIdx == 0 && times == 0) { | |
let result = concatenateSequences(ns); | |
callback([result, ns]); | |
} else { | |
createSong(callback, ns, idx, chordIdx, times, second); | |
} | |
} | |
// Play the interpolated sequence for the current slider position. | |
function playProgression(idx, chordIdx, times) { | |
const unquantizedSeq = mm.sequences.unquantizeSequence(chordSeqs[idx][chordIdx]); | |
let humanized = humanize(unquantizedSeq); | |
playerMaster.start(humanized) | |
.then(() => { | |
if (chordIdx == (numChords - 1)) { | |
times = (times + 1) % numTimes; | |
if (times == 0) { | |
idx = (idx + 1) % numSteps; | |
} | |
} | |
chordIdx = (chordIdx + 1) % numChords; | |
playProgression(idx, chordIdx, times); | |
}); | |
} | |
// Save sequence as MIDI. | |
function saveSequence(ns) { | |
const midi = mm.sequenceProtoToMidi(ns); | |
const file = new Blob([midi], {type: 'audio/midi'}); | |
if (window.navigator.msSaveOrOpenBlob) { | |
window.navigator.msSaveOrOpenBlob(file, 'prog.mid'); | |
} else { // Others | |
const a = document.createElement('a'); | |
const url = URL.createObjectURL(file); | |
a.href = url; | |
a.download = 'prog.mid'; | |
document.body.appendChild(a); | |
a.click(); | |
setTimeout(() => { | |
document.body.removeChild(a); | |
window.URL.revokeObjectURL(url); | |
}, 0); | |
} | |
} | |
function loadSequence() { | |
exportSong(fullSong); | |
} | |
function sequenceToMidi(ns) { | |
if (mm.sequences.isQuantizedSequence(ns)) { | |
ns = mm.sequences.unquantizeSequence(ns); | |
} | |
if (!ns.tempos || ns.tempos.length === 0) { | |
ns.tempos = [{ time: 0, qpm: mm.constants.DEFAULT_QUARTERS_PER_MINUTE }]; | |
} | |
if (!ns.timeSignatures || ns.timeSignatures.length === 0) { | |
ns.timeSignatures = [{ time: 0, numerator: 4, denominator: 4 }]; | |
} | |
if (ns.tempos.length !== 1 || ns.tempos[0].time !== 0) { | |
throw new MidiConversionError('NoteSequence must have exactly 1 tempo at time 0'); | |
} | |
if (ns.timeSignatures.length !== 1 || ns.timeSignatures[0].time !== 0) { | |
throw new MidiConversionError('NoteSequence must have exactly 1 time signature at time 0'); | |
} | |
var json = { | |
header: { | |
bpm: ns.tempos[0].qpm, | |
PPQ: ns.ticksPerQuarter ? ns.ticksPerQuarter : | |
mm.constants.DEFAULT_TICKS_PER_QUARTER, | |
timeSignature: [ns.timeSignatures[0].numerator, ns.timeSignatures[0].denominator] | |
}, | |
tracks: [] | |
}; | |
var tracks = new Map(); | |
for (var _i = 0, _a = ns.notes; _i < _a.length; _i++) { | |
var note = _a[_i]; | |
var instrument = note.instrument ? note.instrument : 0; | |
if (!tracks.has(instrument)) { | |
tracks.set(instrument, []); | |
} | |
tracks.get(instrument).push(note); | |
} | |
var instruments = Array.from(tracks.keys()).sort(function (a, b) { return a - b; }); | |
for (var i = 0; i < instruments.length; i++) { | |
if (i !== instruments[i]) { | |
throw new MidiConversionError('Instrument list must be continuous and start at 0'); | |
} | |
var notes = tracks.get(i); | |
var track = { | |
id: i, | |
notes: [], | |
isPercussion: (notes[0].isDrum === undefined) ? false : notes[0].isDrum, | |
channelNumber: notes[0].isDrum ? mm.constants.DRUM_CHANNEL : | |
mm.constants.DEFAULT_CHANNEL, | |
instrumentNumber: (notes[0].program === undefined) ? | |
mm.constants.DEFAULT_PROGRAM : | |
notes[0].program | |
}; | |
track.notes = notes.map(function (note) { | |
var velocity = (note.velocity === undefined) ? | |
mm.constants.DEFAULT_VELOCITY : | |
note.velocity; | |
return { | |
midi: note.pitch, | |
time: note.startTime, | |
duration: note.endTime - note.startTime, | |
velocity: (velocity + 1) / mm.constants.MIDI_VELOCITIES | |
}; | |
}); | |
json['tracks'].push(track); | |
} | |
var converted = MidiConvert.fromJSON(json); | |
return converted; | |
} | |
function exportSong(ns) { | |
let exportFormat = songToExportFormat(ns); | |
let dataUrl = generateDataUrl(exportFormat); | |
let hash = '#cmp=' + dataUrl; | |
let url = '/daw/' + hash; | |
if (inIframe()) { | |
parent.DAW.addCompositionByURL( dataUrl ).catch( e => { | |
console.error( e ); | |
}).then( cmp => { | |
parent.DAW.openComposition( cmp.id ); | |
parent.gsuiPopup.style.visibility = 'hidden'; | |
} ); | |
//window.parent.location.assign(url); | |
//history.replaceState(undefined, undefined, hash); | |
//window.parent.location.reload(); | |
} else { | |
window.open(url); | |
} | |
} | |
function songToExportFormat(ns) { | |
let seq = sequenceToMidi(ns); | |
let newSong = cloneObject(emptySong); | |
newSong.id = guid(); | |
for (let i = 0; i < seq.tracks.length; i++) { | |
let instNumber = seq.tracks[i].instrumentNumber; | |
let track = seq.tracks[i]; | |
let keyName = 'k' + (i + 1); | |
let synthName = 's' + (i + 1); | |
let trackName = 't' + (i + 1); | |
newSong.synths[synthName].instrument.id = instNumber; | |
newSong.synths[synthName].instrument.isDrum = seq.tracks[i].isPercussion; | |
newSong.synths[synthName].oscillators = {}; | |
if (seq.tracks[i].isPercussion) { | |
newSong.synths[synthName].name = 'Drums'; | |
newSong.tracks[trackName].name = 'Drums'; | |
newSong.synths[synthName].oscillators['d' + instNumber] = { "order": 0, "type": "triangle", "detune": 0, "pan": -0.3, "gain": 0.46 }; | |
} else { | |
let instrumentName = instrumentList[instNumber+'']; | |
newSong.synths[synthName].name = instrumentName; | |
newSong.tracks[trackName].name = instrumentName; | |
newSong.synths[synthName].oscillators['o' + instNumber] = { "order": 0, "type": "triangle", "detune": 0, "pan": -0.3, "gain": 0.46 }; | |
} | |
for (let j = 0; j < track.notes.length; j++) { | |
let note = track.notes[j]; | |
newSong.keys[keyName][''+j] = { | |
"key": note.midi, "pan": 0, "gain": note.velocity, "duration": note.duration, "when": note.time | |
}; | |
} | |
} | |
return newSong; | |
} | |
function generateDataUrl(obj) { | |
let dataUrl = 'data:text/html;base64,' + btoa(JSON.stringify(obj)); | |
return dataUrl; | |
} | |
function guid() { | |
function s4() { | |
return Math.floor((1 + Math.random()) * 0x10000) | |
.toString(16) | |
.substring(1); | |
} | |
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); | |
} | |
function getProbableChordsFromNotes(scale, notes) { | |
let chordServe = new Chordserve(); | |
let results = chordServe.randomSelect(scale, notes); | |
return results; | |
} | |
function inIframe() { | |
try { | |
return window.self !== window.top; | |
} catch (e) { | |
return true; | |
} | |
} | |
function getMaxId(arr) { | |
let maxId = 0; | |
let maxVal = arr[0]; | |
for(let i = 1; i < arr.length; i++) { | |
if(arr[i] > maxVal) { | |
maxVal = arr[i]; | |
maxId = i; | |
} | |
} | |
return maxId; | |
} | |
function sequenceToVector(ns) { | |
const num_bars = 2; | |
const beat_resolution = 8; | |
const beats_per_bar = 4; | |
const min_pitch = 12; | |
const max_pitch = 96; | |
const beats = beats_per_bar * num_bars; | |
const step_size = 1 / beat_resolution; | |
const vector_size = beats_per_bar * beat_resolution * num_bars; | |
const sequence_steps = parseInt(ns.notes[ns.notes.length - 1].endTime / step_size); | |
let vector = new Array(vector_size).fill(0); | |
for (let note of ns.notes) { | |
let start_step = parseInt(note.startTime / step_size); | |
let duration = parseInt((note.endTime - note.startTime) / step_size); | |
if (duration <= 0) { | |
duration = 1; | |
} | |
let end_step = start_step + duration; | |
if (start_step > vector_size) { | |
break; | |
} | |
if (end_step > vector_size) { | |
end_step = vector_size; | |
} | |
for (let i = start_step; i < end_step; i++) { | |
if (note.pitch >= min_pitch && note.pitch <= max_pitch) { | |
vector[i] = note.pitch; | |
} | |
} | |
} | |
return vector; | |
} | |
function processSong() { | |
return new Promise((resolve, reject) => { | |
generateProgressions(() => { | |
createSong((ns) => { | |
resolve(ns); | |
}, [], 0, 0, 0, 0); | |
}); | |
}); | |
} | |
function encodeSong(ns) { | |
return new Promise((resolve, reject) => { | |
let qns = mm.sequences.quantizeNoteSequence(ns, STEPS_PER_QUARTER); | |
multitrack_chords.encode([qns], [chords[0]]).then((z) => { | |
z.data().then(zArray => { | |
z.dispose(); | |
resolve(zArray); | |
}); | |
}); | |
}); | |
} | |
async function predict_genre(vector) { | |
if(typeof vector == 'undefined') { | |
vector = [[64., 64., 64., 64., 64., 64., 64., 64., 54., 54., 54., 54., 54., 54., 54., 54., 59., 59., 59., 59., 59., 59., 59., 59., 64., 64., 64., 64., 64., 64., 64., 64., 59., 59., 59., 59., 59., 59., 59., 59., 54., 54., 54., 54., 54., 54., 54., 54., 64., 64., 64., 64., 64., 64., 64., 64., 57., 57., 57., 57., 57., 57., 57., 57.]]; | |
//vector0 = [[54., 54., 54., 0., 52., 52., 52., 0., 50., 50., 50., 0., 54., 54., 0., 0., 52., 52., 52., 52., 52., 52., 52., 52., 52., 52., 52., 52., 52., 52., 52., 52., 52., 52., 52., 52., 52., 52., 52., 0., 0., 0., 0., 0., 0., 0., 0., 0., 50., 0., 49., 0., 50., 0., 49., 0., 50., 50., 50., 0., 50., 0., 49., 0.]]; | |
//vector1 = [[31., 31., 31., 31., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 48., 48., 48., 48., 48., 48., 48., 48., 48., 48., 48., 48., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 48., 48., 48., 48., 48., 48., 48., 48., 0., 0., 0., 0.]]; | |
//vector2 = [[52., 52., 52., 52., 50., 50., 50., 50., 50., 50., 50., 50., 52., 52., 52., 52., 52., 52., 52., 52., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 47., 47., 47., 47., 47., 47., 47., 47., 45., 45., 45., 45., 45., 45., 45., 45., 0., 0., 0., 0.]]; | |
} | |
const genres = ['electronic', 'rock', 'jazz']; | |
const model = await tf.loadModel('assets/models/genre/model.json'); | |
const example = tf.tensor(vector); | |
const prediction = model.predict(example); | |
const readable_output = prediction.dataSync(); | |
const idx = getMaxId(readable_output); | |
const genre = genres[idx]; | |
return [genre, readable_output, genres]; | |
} | |
function forceDownloadFile(url) { | |
const a = document.createElement('a'); | |
a.href = url; | |
a.download = 'sound.wav'; | |
document.body.appendChild(a); | |
a.click(); | |
} | |
function downloadWav() { | |
const midi = mm.sequenceProtoToMidi(fullSong); | |
const file = new Blob([midi], {type: 'audio/midi'}); | |
var reader = new FileReader(); | |
// set callback for array buffer | |
reader.addEventListener('load', function load(event) { | |
// convert midi arraybuffer to wav blob | |
var wav = midiToWav(event.target.result, {verbose: true}).toBlob(); | |
// create a temporary URL to the wav file | |
var src = URL.createObjectURL(wav); | |
forceDownloadFile(src); | |
}); | |
// read the file as an array buffer | |
reader.readAsArrayBuffer(file); | |
} | |
function getDrum(genre, drumKick, drumHit) { | |
let drum = {notes: [], quantizationInfo: { stepsPerQuarter: 4 }, totalQuantizedSteps: 30 }; | |
if (genre == 'electronic') { | |
drum.notes = [ | |
{ pitch: drumKick, quantizedStartStep: 0, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 2, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 4, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 6, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 8, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 10, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 12, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 14, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 16, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 18, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 20, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 22, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 24, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 26, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 28, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 30, instrument: 2, isDrum: true } | |
]; | |
} else if (genre == 'rock') { | |
drum.notes = [ | |
{ pitch: drumKick, quantizedStartStep: 0, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 2, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 4, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 6, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 8, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 10, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 12, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 14, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 16, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 18, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 20, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 22, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 24, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 26, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 28, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 30, instrument: 2, isDrum: true } | |
]; | |
} else if (genre == 'jazz') { | |
drum.notes = [ | |
{ pitch: drumHit, quantizedStartStep: 0, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 0, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 4, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 7, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 8, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 8, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 12, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 15, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 16, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 16, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 20, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 23, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 24, instrument: 2, isDrum: true }, | |
{ pitch: drumKick, quantizedStartStep: 24, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 28, instrument: 2, isDrum: true }, | |
{ pitch: drumHit, quantizedStartStep: 31, instrument: 2, isDrum: true } | |
]; | |
} | |
return drum; | |
} | |
/* | |
* CLASSES | |
*/ | |
class Chordserve { | |
constructor() { | |
this.keys = { | |
ionian: { | |
rules: "wwhwwwh" | |
}, | |
lydian: { | |
rules: "wwwhwwh" | |
}, | |
mixolydian: { | |
rules: "wwhwwww" | |
}, | |
dorian: { | |
rules: "whwwwhw" | |
}, | |
aeolian: { | |
rules: "whwwhww" | |
}, | |
phrygian: { | |
rules: "hwwwwhw" | |
}, | |
locrian: { | |
rules: "hwwhxhw" | |
}, | |
harmonic_minor: { | |
rules: "whwwhxh" | |
}, | |
melodic_minor: { | |
rules: "whwwwwh" | |
}, | |
major_pentatonic: { | |
rules: "wwxwx" | |
}, | |
minor_pentatonic: { | |
rules: "xwwxw" | |
}, | |
minor_blues: { | |
rules: "xwhhxw" | |
} | |
}; | |
this.intervalArr = [ | |
"I", "II", "III", "IV", "V", "VI", "VII" | |
]; | |
this.progressions = [ | |
["I", "VImin", "IImin", "V"], | |
["IIImin", "VImin", "IImin", "V"], | |
["I", "IImin", "IIImin", "IV"], | |
["Imin", "bVII", "bVI", "bVII"], | |
["Imin", "bVII", "bVI", "V"], | |
["Imin", "IImin", "bIII", "IImin"] | |
]; | |
this.notes = [ | |
"Ab", "A", "Bb", "B", "C", "Db", "D", "Eb", "E", "F", "Gb", "G" | |
]; | |
this.key_notes = []; | |
} | |
createTriad(rootNote, addTriad) { | |
var triad = [] | |
var key_notes = this.generateKey(rootNote, "major"); | |
triad.push(rootNote); | |
triad.push(key_notes[3-1]) | |
triad.push(key_notes[5-1]) | |
var extraN = null; | |
var noteIndex = null; | |
if(addTriad === "min"){ | |
var n = triad[1] | |
noteIndex = this.notes.indexOf(n); | |
if (noteIndex > 0) { | |
noteIndex--; | |
} else { | |
noteIndex = this.notes.length - 1; | |
} | |
triad[1] = this.notes[noteIndex]; | |
} | |
if(addTriad === "7" && key_notes.length >= 7) { | |
extraN = key_notes[7-1]; | |
noteIndex = this.notes.indexOf(extraN); | |
if (noteIndex > 0) { | |
noteIndex--; | |
} else { | |
noteIndex = this.notes.length - 1; | |
} | |
triad.push(noteIndex); | |
} | |
if(addTriad === "M7" && key_notes.length >= 7) { | |
extraN = key_notes[7-1]; | |
noteIndex = this.notes.indexOf(extraN); | |
triad.push(noteIndex); | |
} | |
return triad; | |
} | |
convertIntervalToChord(interval, keyNotes) { | |
var accidentals = ["b", "#", "7", "M7", "min"]; | |
var usedAcc = []; | |
var parsedInterval = interval; | |
accidentals.map(i => { | |
if (parsedInterval.indexOf(i) > -1) | |
usedAcc.push(i) | |
parsedInterval = parsedInterval.replace(i, "") | |
return null; | |
}) | |
var intervalIndex = this.intervalArr.indexOf(parsedInterval); | |
// intervalIndex += 1 | |
if(usedAcc.indexOf("b") > 0) { | |
if (intervalIndex === 0) { | |
intervalIndex--; | |
} else { | |
intervalIndex = this.intervalArr.length - 1; | |
} | |
} | |
if(usedAcc.indexOf("#") >= 0) { | |
if (intervalIndex === (this.intervalArr.length - 1)) { | |
intervalIndex++; | |
} else { | |
intervalIndex = this.intervalArr.length - 1; | |
} | |
} | |
var addTriad = ""; | |
accidentals.slice(2).forEach((acc, idx, arr) => { | |
if(usedAcc.indexOf(acc) >= 0) { | |
addTriad = acc; | |
return | |
} | |
}) | |
var rootNote = keyNotes[intervalIndex]; | |
if (rootNote === undefined) { | |
return null; | |
} else { | |
var triad = this.createTriad(rootNote, addTriad) | |
return [interval, triad] | |
} | |
} | |
toTitleCase(str) { | |
return str.toLowerCase() | |
.split(' ') | |
.map(i => i[0].toUpperCase() + i.substring(1)) | |
.join(' ') | |
} | |
convertToKeyIndex(keyString) { | |
return keyString.toLowerCase().split(' ').join('_'); | |
} | |
generateKey(note, key) { | |
note = note.length === 1 ? note.toUpperCase() : this.toTitleCase(note); | |
var key_notes = [note]; | |
var root_index = this.notes.indexOf(note) | |
var index = root_index | |
let key_index = this.convertToKeyIndex(key); | |
if (key_index === 'minor') { | |
key_index = 'aeolian'; | |
} | |
if (key_index === 'major') { | |
key_index = 'ionian'; | |
} | |
var k = this.keys[key_index]; | |
// generate notes and chords | |
// first notes | |
var dist; | |
for(var step of Array.from(k.rules)) { | |
if (step === "w") { | |
dist = 2 | |
} else if (step === "h") { | |
dist = 1 | |
} else if (step === "x") { | |
dist = 3 | |
} | |
var new_index = index + dist | |
if (new_index >= this.notes.length) { | |
index = new_index - this.notes.length; | |
} else { | |
index = new_index | |
} | |
key_notes.push(this.notes[index]) | |
} | |
return key_notes; | |
} | |
randomSelect(note, extras) { | |
// on select key | |
var max = 0; | |
var exit = []; | |
var notesArr = this.notes; | |
//var note = notesArr[Math.floor(Math.random() * notesArr.length)]; | |
let keyList = Object.keys(this.keys); | |
for (let scale of keyList) { | |
//var scale = keyList[Math.floor(Math.random() * keyList.length)]; | |
let key_notes = this.generateKey(note, scale); | |
for (let randomProg of this.progressions) { | |
//var randomProg = this.progressions[Math.floor(Math.random() * this.progressions.length)]; | |
var progressionObj = []; | |
// go through intervals | |
for (let interval of randomProg) { | |
var results = this.convertIntervalToChord(interval, key_notes); | |
if (results != null) { | |
var triad = results[1]; | |
var chord = triad[0]; | |
if (interval.indexOf('min') != -1) { | |
chord += 'm'; | |
} | |
progressionObj.push([interval,triad,chord]); | |
} | |
} | |
if (progressionObj.length == 4) { | |
let cont = 0; | |
if (progressionObj[0][1].indexOf(extras[0]) != -1) | |
cont++; | |
if (progressionObj[1][1].indexOf(extras[1]) != -1) | |
cont++; | |
if (progressionObj[2][1].indexOf(extras[2]) != -1) | |
cont++; | |
if (progressionObj[3][1].indexOf(extras[3]) != -1) | |
cont++; | |
if (cont > max) { | |
exit = []; | |
max = cont; | |
} | |
if (cont == max) { | |
exit.push([ | |
progressionObj[0][2], | |
progressionObj[1][2], | |
progressionObj[2][2], | |
progressionObj[3][2] | |
]); | |
} | |
} | |
} | |
} | |
return exit; | |
} | |
} | |
/* | |
* CATALOGS | |
*/ | |
var emptySong = { | |
"id": "88fb1290-76e2-4aa1-be2b-0e1dd62226bb", | |
"bpm": 120, | |
"stepsPerBeat": 4, | |
"beatsPerMeasure": 4, | |
"name": "AI Demo", | |
"duration": 128, | |
"patterns": { | |
"p1": { "name": "", "type": "keys", "keys": "k1", "synth": "s1", "instrument": "i1", "duration": 128 }, | |
"p2": { "name": "", "type": "keys", "keys": "k2", "synth": "s2", "instrument": "i2", "duration": 128 }, | |
"p3": { "name": "", "type": "keys", "keys": "k3", "synth": "s3", "instrument": "i3", "duration": 128 }, | |
"p4": { "name": "", "type": "keys", "keys": "k4", "synth": "s4", "instrument": "i4", "duration": 128 }, | |
"p5": { "name": "", "type": "keys", "keys": "k5", "synth": "s5", "instrument": "i5", "duration": 128 }, | |
"p6": { "name": "", "type": "keys", "keys": "k6", "synth": "s6", "instrument": "i6", "duration": 128 }, | |
"p7": { "name": "", "type": "keys", "keys": "k7", "synth": "s7", "instrument": "i7", "duration": 128 }, | |
"p8": { "name": "", "type": "keys", "keys": "k8", "synth": "s8", "instrument": "i8", "duration": 128 } | |
}, | |
"synths": { | |
"s1": { | |
"name": "", | |
"instrument": { "id": 1, "name": "instrument1", "isDrum": false }, | |
"oscillators": { | |
"o1": { "order": 0, "type": "triangle", "detune": 0, "pan": -0.3, "gain": 0.46 } | |
} | |
}, | |
"s2": { | |
"name": "", | |
"instrument": { "id": 2, "name": "instrument2", "isDrum": false }, | |
"oscillators": { | |
"o2": { "order": 0, "type": "triangle", "detune": 0, "pan": -0.3, "gain": 0.46 } | |
} | |
}, | |
"s3": { | |
"name": "", | |
"instrument": { "id": 3, "name": "instrument3", "isDrum": false }, | |
"oscillators": { | |
"o3": { "order": 0, "type": "triangle", "detune": 0, "pan": -0.3, "gain": 0.46 } | |
} | |
}, | |
"s4": { | |
"name": "", | |
"instrument": { "id": 4, "name": "instrument4", "isDrum": false }, | |
"oscillators": { | |
"o4": { "order": 0, "type": "triangle", "detune": 0, "pan": -0.3, "gain": 0.46 } | |
} | |
}, | |
"s5": { | |
"name": "", | |
"instrument": { "id": 5, "name": "instrument5", "isDrum": false }, | |
"oscillators": { | |
"o5": { "order": 0, "type": "triangle", "detune": 0, "pan": -0.3, "gain": 0.46 } | |
} | |
}, | |
"s6": { | |
"name": "", | |
"instrument": { "id": 6, "name": "instrument6", "isDrum": false }, | |
"oscillators": { | |
"o6": { "order": 0, "type": "triangle", "detune": 0, "pan": -0.3, "gain": 0.46 } | |
} | |
}, | |
"s7": { | |
"name": "", | |
"instrument": { "id": 7, "name": "instrument7", "isDrum": false }, | |
"oscillators": { | |
"o7": { "order": 0, "type": "triangle", "detune": 0, "pan": -0.3, "gain": 0.46 } | |
} | |
}, | |
"s8": { | |
"name": "", | |
"instrument": { "id": 8, "name": "instrument8", "isDrum": false }, | |
"oscillators": { | |
"o8": { "order": 0, "type": "triangle", "detune": 0, "pan": -0.3, "gain": 0.46 } | |
} | |
} | |
}, | |
"tracks": { | |
"t1": { "order": 0, "name": "" }, | |
"t2": { "order": 1, "name": "" }, | |
"t3": { "order": 2, "name": "" }, | |
"t4": { "order": 3, "name": "" }, | |
"t5": { "order": 4, "name": "" }, | |
"t6": { "order": 5, "name": "" }, | |
"t7": { "order": 6, "name": "" }, | |
"t8": { "order": 7, "name": "" } | |
}, | |
"blocks": { | |
"0": { "pattern": "p1", "duration": 128, "when": 0, "track": "t1" }, | |
"1": { "pattern": "p2", "duration": 128, "when": 0, "track": "t2" }, | |
"2": { "pattern": "p3", "duration": 128, "when": 0, "track": "t3" }, | |
"3": { "pattern": "p4", "duration": 128, "when": 0, "track": "t4" }, | |
"4": { "pattern": "p5", "duration": 128, "when": 0, "track": "t5" }, | |
"5": { "pattern": "p6", "duration": 128, "when": 0, "track": "t6" }, | |
"6": { "pattern": "p7", "duration": 128, "when": 0, "track": "t7" }, | |
"7": { "pattern": "p8", "duration": 128, "when": 0, "track": "t8" } | |
}, | |
"keys": { | |
"k1": { | |
"0": { "key": 50, "pan": 0, "gain": 0.8, "duration": 1, "when": 0 } | |
}, | |
"k2": { | |
"0": { "key": 51, "pan": 0, "gain": 0.8, "duration": 1, "when": 0 } | |
}, | |
"k3": { | |
"0": { "key": 52, "pan": 0, "gain": 0.8, "duration": 1, "when": 0 } | |
}, | |
"k4": { | |
"0": { "key": 53, "pan": 0, "gain": 0.8, "duration": 1, "when": 0 } | |
}, | |
"k5": { | |
"0": { "key": 54, "pan": 0, "gain": 0.8, "duration": 1, "when": 0 } | |
}, | |
"k6": { | |
"0": { "key": 55, "pan": 0, "gain": 0.8, "duration": 1, "when": 0 } | |
}, | |
"k7": { | |
"0": { "key": 56, "pan": 0, "gain": 0.8, "duration": 1, "when": 0 } | |
}, | |
"k8": { | |
"0": { "key": 57, "pan": 0, "gain": 0.8, "duration": 1, "when": 0 } | |
} | |
}, | |
"synthOpened": "s1", | |
"savedAt": 1534026524, | |
"patternOpened": "p1" | |
}; | |
var allNoteLetters = { | |
'C': 36, 'C#': 37, 'D': 38, 'D#': 39, 'E': 40, 'F': 41, | |
'F#': 42, 'G': 43, 'G#': 44, 'A': 45, 'A#': 46, 'B': 47, | |
'Db': 37, 'Eb': 39, 'Gb': 42, 'Ab': 44, 'Bb': 46 | |
}; | |
var allNotes = Object.keys(allNoteLetters); | |
var chordsFromNote = { | |
'C' : ['C','Cm','F','Fm','G#', 'Am'], | |
'D' : ['D','Dm','G','Gm','A#', 'Bm'], | |
'E' : ['E','Em','A','Am','C' , 'C#m'], | |
'F' : ['F','Fm','B','Bm','C#', 'Dm'], | |
'G' : ['G','Gm','C','Cm','D#', 'Em'], | |
'A' : ['A','Am','D','Dm','F' , 'F#m'], | |
'B' : ['B','Bm','E','Em','G' , 'G#m'], | |
'C#': ['C#','C#m','F#','F#m','A','A#m'], | |
'D#': ['D#','D#m','B','Cm','G#','G#m'], | |
'F#': ['F#','F#m','B','Bm','D','D#m'], | |
'G#': ['G#','G#m','E','C#','C#m','Fm'], | |
'A#': ['A#','A#m','D#','D#m','F#','Gm'] | |
} | |
const instrumentList = { | |
"0": "acoustic_grand_piano", "1": "bright_acoustic_piano", | |
"2": "electric_grand_piano", "3": "honkytonk_piano", | |
"4": "electric_piano_1", "5": "electric_piano_2", | |
"6": "harpsichord", "7": "clavichord", "8": "celesta", | |
"9": "glockenspiel", "10": "music_box", "11": "vibraphone", | |
"12": "marimba", "13": "xylophone", "14": "tubular_bells", | |
"15": "dulcimer", "16": "drawbar_organ", "17": "percussive_organ", | |
"18": "rock_organ", "19": "church_organ", "20": "reed_organ", | |
"21": "accordion", "22": "harmonica", "23": "tango_accordion", | |
"24": "acoustic_guitar_nylon", "25": "acoustic_guitar_steel", | |
"26": "electric_guitar_jazz", "27": "electric_guitar_clean", | |
"28": "electric_guitar_muted", "29": "overdriven_guitar", | |
"30": "distortion_guitar", "31": "guitar_harmonics", | |
"32": "acoustic_bass", "33": "electric_bass_finger", | |
"34": "electric_bass_pick", "35": "fretless_bass", | |
"36": "slap_bass_1", "37": "slap_bass_2", "38": "synth_bass_1", | |
"39": "synth_bass_2", "40": "violin", "41": "viola", | |
"42": "cello", "43": "contrabass", "44": "tremolo_strings", | |
"45": "pizzicato_strings", "46": "orchestral_harp", "47": "timpani", | |
"48": "string_ensemble_1", "49": "string_ensemble_2", | |
"50": "synthstrings_1", "51": "synthstrings_2", "52": "choir_aahs", | |
"53": "voice_oohs", "54": "synth_voice", "55": "orchestra_hit", | |
"56": "trumpet", "57": "trombone", "58": "tuba", | |
"59": "muted_trumpet", "60": "french_horn", "61": "brass_section", | |
"62": "synthbrass_1", "63": "synthbrass_2", "64": "soprano_sax", | |
"65": "alto_sax", "66": "tenor_sax", "67": "baritone_sax", | |
"68": "oboe", "69": "english_horn", "70": "bassoon", "71": "clarinet", | |
"72": "piccolo", "73": "flute", "74": "recorder", "75": "pan_flute", | |
"76": "blown_bottle", "77": "shakuhachi", "78": "whistle", | |
"79": "ocarina", "80": "lead_1_square", "81": "lead_2_sawtooth", | |
"82": "lead_3_calliope", "83": "lead_4_chiff", "84": "lead_5_charang", | |
"85": "lead_6_voice", "86": "lead_7_fifths", "87": "lead_8_bass_lead", | |
"88": "pad_1_new_age", "89": "pad_2_warm", "90": "pad_3_polysynth", | |
"91": "pad_4_choir", "92": "pad_5_bowed", "93": "pad_6_metallic", | |
"94": "pad_7_halo", "95": "pad_8_sweep", "96": "fx_1_rain", | |
"97": "fx_2_soundtrack", "98": "fx_3_crystal", "99": "fx_4_atmosphere", | |
"100": "fx_5_brightness", "101": "fx_6_goblins", "102": "fx_7_echoes", | |
"103": "fx_8_scifi", "104": "sitar", "105": "banjo", "106": "shamisen", | |
"107": "koto", "108": "kalimba", "109": "bag_pipe", "110": "fiddle", | |
"111": "shanai", "112": "tinkle_bell", "113": "agogo", | |
"114": "steel_drums", "115": "woodblock", "116": "taiko_drum", | |
"117": "melodic_tom", "118": "synth_drum", "119": "reverse_cymbal", | |
"120": "guitar_fret_noise", "121": "breath_noise", "122": "seashore", | |
"123": "bird_tweet", "124": "telephone_ring", "125": "helicopter", | |
"126": "applause", "127": "gunshot", "drums": "percussion" | |
}; | |
/* | |
* RUN APP | |
*/ | |
init(); |