/** * @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 { 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 { 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();