├── .gitignore ├── .babelrc ├── watch.sh ├── audio ├── concert-crowd.mp3 └── concert-crowd.ogg ├── README.md ├── src ├── audio-distortion-node.js ├── get-set-controls.js ├── tracker-table.js ├── tracker-table-svg.js ├── simple-tracker.js ├── app.js └── default-track.js ├── package.json ├── main.css └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | nbproject 2 | dist 3 | node_modules 4 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "presets": ["es2015"] 4 | 5 | } 6 | -------------------------------------------------------------------------------- /watch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | watchify src/app.js -t --debug -o 'bundle.js' 3 | -------------------------------------------------------------------------------- /audio/concert-crowd.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diversen/drum-machine-javascript/HEAD/audio/concert-crowd.mp3 -------------------------------------------------------------------------------- /audio/concert-crowd.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diversen/drum-machine-javascript/HEAD/audio/concert-crowd.ogg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # drum-machine-javascript 2 | 3 | A simple drum-machine written in javascript 4 | 5 | It uses these great drum-samples: 6 | 7 | https://github.com/oramics/sampled/ 8 | 9 | You can try it out here: https://diversen.github.io/drum-machine-javascript/ 10 | 11 | ## Install 12 | 13 | git clone https://github.com/diversen/drum-machine-javascript 14 | 15 | Just clone this repo, and start a server inside it - og place it on a server. 16 | 17 | ## Dev install 18 | 19 | Install deps: 20 | 21 | npm install 22 | 23 | ## Watch and build: 24 | 25 | You will need watchify `sudo npm install -g watchify` 26 | 27 | watchify src/main.js -t --debug -o 'bundle.js' 28 | 29 | Or (short form of above) 30 | 31 | npm run watch 32 | 33 | ## Licence 34 | 35 | MIT © [Dennis Iversen](https://github.com/diversen) 36 | 37 | -------------------------------------------------------------------------------- /src/audio-distortion-node.js: -------------------------------------------------------------------------------- 1 | function audioDistortionNode(ctx) { 2 | 3 | this.ctx = ctx; 4 | this.distortion; 5 | this.amount = 400; 6 | 7 | this.getDistortionNode = function (amount) { 8 | 9 | if (amount) { 10 | this.amount = amount; 11 | } 12 | 13 | this.distortion = this.ctx.createWaveShaper(); 14 | this.distortion.oversample = '4x'; 15 | this.distortion.curve = this.makeDistortionCurve(this.amount); 16 | return this.distortion; 17 | } 18 | 19 | this.makeDistortionCurve = function (amount) { 20 | var k = typeof amount === 'number' ? amount : 50, 21 | n_samples = 44100, 22 | curve = new Float32Array(n_samples), 23 | deg = Math.PI / 180, 24 | i = 0, 25 | x; 26 | for (; i < n_samples; ++i) { 27 | x = i * 2 / n_samples - 1; 28 | curve[i] = (3 + k) * x * 20 * deg / (Math.PI + k * Math.abs(x)); 29 | } 30 | return curve; 31 | 32 | }; 33 | } 34 | 35 | module.exports = audioDistortionNode; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "drum-machine", 3 | "version": "3.0.2", 4 | "description": "A simple drum machine / sequencer written in javascript", 5 | "main": "src/main.js", 6 | "scripts": { 7 | "test": "test.js", 8 | "watch": "watchify src/main.js -t --debug -o 'bundle.js'" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/diversen/drum-machine-javascript.git" 13 | }, 14 | "keywords": [ 15 | "drum-machine", 16 | "sequencer" 17 | ], 18 | "author": "dennis iversen", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/diversen/drum-machine-javascript/issues" 22 | }, 23 | "homepage": "https://github.com/diversen/drum-machine-javascript#readme", 24 | "dependencies": { 25 | "adsr-gain-node": "^3.1.0", 26 | "file-saver": "^1.3.8", 27 | "get-html-rows-cells": "^2.0.0", 28 | "get-set-form-values": "^4.0.0", 29 | "has-class": "^1.0.6", 30 | "load-sample-set": "^4.0.2", 31 | "select-element": "^2.1.0", 32 | "set-value": "^2.0.1", 33 | "tiny-sample-loader": "^2.0.0", 34 | "toggle-class": "0.0.0", 35 | "waaclock": "^0.5.3" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/get-set-controls.js: -------------------------------------------------------------------------------- 1 | const getSetFormValues = require('get-set-form-values'); 2 | 3 | function getSetControls() { 4 | 5 | this.getTrackerControls = function() { 6 | 7 | let formValues = new getSetFormValues(); 8 | let form = document.getElementById("trackerControls"); 9 | let values = formValues.get(form); 10 | let ret = {}; 11 | for (let key in values) { 12 | 13 | if (key === 'delayEnabled') { 14 | ret[key] = 'delay'; 15 | continue; 16 | } 17 | if (key === 'gainEnabled') { 18 | ret[key] = 'gain'; 19 | continue; 20 | } 21 | 22 | if (key === 'sampleSet') { 23 | ret[key] = values[key]; 24 | continue; 25 | } 26 | ret[key] = parseFloat(values[key]); 27 | } 28 | return ret; 29 | } 30 | 31 | this.setTrackerControls = function (values) { 32 | if (!values) { 33 | values = this.getTrackerControls(); 34 | } 35 | this.options = values; 36 | }; 37 | 38 | } 39 | 40 | module.exports = getSetControls; 41 | -------------------------------------------------------------------------------- /main.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Merriweather:400,700); 2 | 3 | .merriweather { 4 | font-family: 'Merriweather'; 5 | } 6 | 7 | /* 8 | #controls { 9 | font-family: 'Merriweather'; 10 | font-size: 10px; 11 | }*/ 12 | a { 13 | color: hotpink; 14 | } 15 | 16 | #tracker-container { 17 | background-color: ghostwhite; 18 | width: 100%; 19 | padding: 20px; 20 | background-color: #F4A360; 21 | display: flex; 22 | flex-wrap:wrap; 23 | } 24 | 25 | #tracker-controls { 26 | margin-left: 20px; 27 | padding:20px; 28 | background-color: tomato; 29 | } 30 | 31 | 32 | #tracker-parent { 33 | padding:20px; 34 | flex-basis: auto; 35 | background-color: gainsboro; 36 | /*overflow-x: visible;*/ 37 | } 38 | 39 | 40 | .tracker-cell, .tracker-first-cell, .tracker-cell-header { 41 | height: 20px; 42 | min-width: 20px; 43 | border: black; 44 | border-color: black; 45 | background: #fff; 46 | border-style: solid; 47 | border-width: 1px; 48 | font-size: 75%; 49 | padding:2px; 50 | /*background-color: pink;*/ 51 | } 52 | 53 | .tracker-enabled { 54 | background: orange; 55 | } 56 | 57 | .tracker-current { 58 | background: blueviolet; 59 | } 60 | 61 | #app-message { 62 | position: fixed; 63 | top: 10px; 64 | left: 10px; 65 | background-color: peru; 66 | margin: 10px; 67 | padding: 10px; 68 | display: none; 69 | } 70 | -------------------------------------------------------------------------------- /src/tracker-table.js: -------------------------------------------------------------------------------- 1 | function trackerTable() { 2 | 3 | this.str = ''; 4 | this.getTable = function () { 5 | return '' + this.str + '
'; 6 | }; 7 | 8 | this.setHeader = function (numRows, data) { 9 | this.str += ``; 10 | this.str += this.getCells('header', numRows, { header: true }); 11 | this.str += ``; 12 | 13 | }; 14 | 15 | this.setRows = function (numRows, numCols, data) { 16 | 17 | this.setHeader(numCols, data); 18 | for (let rowID = 0; rowID < numRows; rowID++) { 19 | this.str += ``; 20 | this.str += this.getCells(rowID, numCols, data); 21 | this.str += ``; 22 | } 23 | }; 24 | 25 | this.getFirstCell = function (rowID, data) { 26 | var str = ''; 27 | 28 | str += ``; 29 | if (data.title) { 30 | str += data.title[rowID]; 31 | } 32 | 33 | str += ``; 34 | return str; 35 | }; 36 | 37 | this.getCells = function (rowID, numRows, data) { 38 | var str = ''; 39 | 40 | str += this.getFirstCell(rowID, data); 41 | 42 | let cssClass = 'tracker-cell' 43 | 44 | if (rowID == 'header') { 45 | cssClass = 'tracker-cell-header' 46 | } 47 | 48 | for (let c = 0; c < numRows; c++) { 49 | str += ``; 50 | if (data.header) { 51 | str += c + 1; 52 | } 53 | str += ``; 54 | } 55 | return str; 56 | }; 57 | } 58 | 59 | module.exports = trackerTable; 60 | -------------------------------------------------------------------------------- /src/tracker-table-svg.js: -------------------------------------------------------------------------------- 1 | function trackerTableSvg() { 2 | 3 | this.str = ''; 4 | this.options = { 5 | cellFirst: 120, 6 | cellWidth: 16, 7 | cellSpace: 4 8 | } 9 | 10 | this.options.cellWithSpace = this.options.cellSpace + this.options.cellWidth 11 | this.options.cellFirstWithSpace = this.options.cellFirst + this.options.cellSpace 12 | 13 | /** 14 | * @param int number of cells 15 | * @param int number of rows 16 | * @param object data 17 | */ 18 | this.setRows = function (numRows, numRows, data) { 19 | 20 | this.numRows = numRows; 21 | this.numRows = numRows; 22 | 23 | this.setHeader(numRows, data); 24 | for (let rowID = 0; rowID < numRows; rowID++) { 25 | this.str += this.getCells(rowID, numRows, data); 26 | } 27 | }; 28 | 29 | this.setHeader = function (numRows, data) { 30 | this.str += this.getCells('header', numRows, { header: true }); 31 | }; 32 | 33 | this.getTable = function () { 34 | 35 | 36 | let boxX = (this.options.cellFirst + this.options.cellSpace) + 37 | (this.numRows * this.options.cellWithSpace) 38 | let boxY = (this.numRows + 1) * (this.options.cellWithSpace) 39 | 40 | let html = ` 41 | 46 | `; 47 | html += this.str; 48 | html += ` 49 | 50 | ` 51 | return html 52 | }; 53 | 54 | this.getFirstCell = function (rowID, data) { 55 | 56 | let str = ''; 57 | str += ` 58 | `; 65 | 66 | if (data.title) { 67 | let text = data.title[rowID]; 68 | str += ` 69 | ${text} 73 | `; 74 | } 75 | 76 | this.currentX = this.options.cellFirstWithSpace 77 | return str; 78 | }; 79 | 80 | this.currentX = 0 81 | this.getCurrentX = function (x) { 82 | return x + this.currentX 83 | } 84 | 85 | this.y = 0; 86 | this.getCells = function (rowID, numRows, data) { 87 | var str = ''; 88 | var x = 0; 89 | 90 | // Get first cell. E.g. instrument 91 | str += this.getFirstCell(rowID, data); 92 | for (let c = 0; c < numRows; c++) { 93 | 94 | str += ` 95 | 103 | `; 104 | 105 | if (data.header) { 106 | // column header. A number 107 | let text = c + 1; 108 | str += ` 109 | ${text} 116 | `; 117 | } 118 | 119 | x += this.options.cellWithSpace 120 | } 121 | 122 | this.y += this.options.cellWithSpace 123 | return str; 124 | }; 125 | } 126 | 127 | module.exports = trackerTableSvg; 128 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | javascript drum machine 6 | 8 | 9 | 11 | 12 | 13 | 14 | 18 | 19 |
20 | 21 |
22 |
23 |
24 | 25 | 26 | 27 |
28 |
29 | 38 | 39 |
40 | 41 | Measure length
42 | BPM 43 |
44 | 45 |
46 | 47 | Detune / Tune
48 | 49 |
50 | 51 | Enabled gain
52 | 53 | AttackAmp
54 | SustainAmp
55 | DecayAmp
56 | ReleaseAmp
57 | Attacktime
58 | DecayTime
59 | Sustaintime
60 | Releasetime
61 | 62 | AdsrInterval
63 | 64 |
65 | 66 | Enabled delay 67 |
68 | Delay
69 | Filter
70 |
71 |
72 |
73 |
74 |
75 | 76 | 77 | 78 |
79 |
80 |
81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /src/simple-tracker.js: -------------------------------------------------------------------------------- 1 | const WAAClock = require('waaclock'); 2 | const trackerTable = require('./tracker-table'); 3 | const hasClass = require('has-class'); 4 | 5 | /** 6 | * Construct object 7 | * @param {audioContext} ctx 8 | * @param {function} scheduleAudioBeat funtion when an audio is played 9 | */ 10 | function tracker(ctx, scheduleAudioBeat) { 11 | 12 | this.measureLength = 16; 13 | this.scheduleAudioBeat = scheduleAudioBeat; 14 | this.scheduleForward = 0.1; 15 | this.current = 0; 16 | this.eventMap = {}; 17 | this.clock = new WAAClock(ctx); 18 | this.clock.start(); 19 | this.running = false; 20 | 21 | /** 22 | * Draw a tracker table by numRows and numCols 23 | */ 24 | this.drawTracker = function(numRows, numCols, data) { 25 | 26 | let htmlTable = new trackerTable(); 27 | 28 | htmlTable.setRows(numRows, numCols, data); 29 | let str = htmlTable.getTable(); 30 | 31 | let t = document.getElementById('tracker-parent'); 32 | t.innerHTML = ''; 33 | t.insertAdjacentHTML('afterbegin', str); 34 | } 35 | 36 | /** 37 | * Push current beat one forward 38 | */ 39 | this.next = function () { 40 | this.current++; 41 | if (this.current >= this.measureLength) { 42 | this.current = 0; 43 | } 44 | }; 45 | 46 | /** 47 | * Calculate milli seconds per beat 48 | */ 49 | this.milliPerBeat = function (beats) { 50 | if (!beats) { 51 | beats = 60; 52 | } 53 | return 1000 * 60 / beats; 54 | }; 55 | 56 | /** 57 | * Get a tracker row from a cell-id 58 | */ 59 | this.getTrackerRowValues = function (colId) { 60 | let values = []; 61 | let selector = `[data-col-id="${colId}"]`; 62 | 63 | let elems = document.querySelectorAll(selector); 64 | elems.forEach((el) => { 65 | let val = Object.assign({}, el.dataset); 66 | val.enabled = el.classList.contains('tracker-enabled'); 67 | values.push(val); 68 | }); 69 | return values; 70 | }; 71 | 72 | /** 73 | * Schedule a beat column 74 | */ 75 | this.schedule = function () { 76 | let beatColumn = this.getTrackerRowValues(this.current); 77 | let now = ctx.currentTime; 78 | 79 | let selector = `[data-col-id="${this.current}"]`; 80 | 81 | let event = this.clock.callbackAtTime(() => { 82 | let elems = document.querySelectorAll(selector); 83 | elems.forEach( (e) => { 84 | e.classList.add('tracker-current') 85 | }) 86 | }, now + this.scheduleForward); 87 | 88 | this.clock.callbackAtTime(() => { 89 | let elems = document.querySelectorAll(selector); 90 | elems.forEach( (e) => { 91 | e.classList.remove('tracker-current') 92 | }) 93 | }, now + this.scheduleForward + this.milliPerBeat(this.bpm) / 1000); 94 | 95 | beatColumn.forEach((beat) => { 96 | this.scheduleBeat(beat, now); 97 | }); 98 | }; 99 | 100 | this.scheduleBeat = function (beat, now) { 101 | 102 | let triggerTime = now + this.scheduleForward; 103 | this.scheduleMap[beat.colId] = triggerTime; 104 | if (beat.enabled) { 105 | this.eventMap[this.getEventKey(beat)] = this.clock.callbackAtTime(() => { 106 | this.scheduleAudioBeat(beat, triggerTime); 107 | }, now); 108 | } 109 | }; 110 | 111 | this.scheduleMap = {}; 112 | 113 | this.scheduleAudioBeatNow = function (beat) { 114 | 115 | if (beat.enabled) { 116 | let beatEvent = this.eventMap[this.getEventKey(beat)]; 117 | if (beatEvent) { 118 | beatEvent.clear(); 119 | delete this.eventMap[this.getEventKey(beat)]; 120 | } 121 | return; 122 | } 123 | 124 | let triggerTime = this.scheduleMap[0] + beat.colId * this.milliPerBeat(this.bpm) / 1000; 125 | let now = ctx.currentTime; 126 | this.eventMap[this.getEventKey(beat)] = this.clock.callbackAtTime(() => { 127 | this.scheduleAudioBeat(beat, triggerTime); 128 | }, now); 129 | }; 130 | 131 | this.interval; 132 | this.runSchedule = function (bpm) { 133 | this.running = true; 134 | this.bpm = bpm; 135 | let interval = this.milliPerBeat(bpm); 136 | 137 | setTimeout(() => { 138 | this.schedule(); 139 | this.next(); 140 | }, 0); 141 | 142 | this.interval = setInterval(() => { 143 | this.schedule(); 144 | this.next(); 145 | 146 | }, interval); 147 | }; 148 | 149 | this.stop = function () { 150 | this.running = false; 151 | clearInterval(this.interval); 152 | }; 153 | 154 | this.getEventKey = function getEventKey(beat) { 155 | return beat.rowId + beat.colId; 156 | }; 157 | 158 | /** 159 | * Get tracker values 160 | */ 161 | this.getTrackerValues = function () { 162 | let values = []; 163 | let elems = document.querySelectorAll('.tracker-cell'); 164 | elems.forEach(function (e) { 165 | let val = Object.assign({}, e.dataset); 166 | val.enabled = hasClass(e, "tracker-enabled"); 167 | values.push(val); 168 | }); 169 | return values; 170 | }; 171 | 172 | /** 173 | * Load tracker values in JSON format 174 | */ 175 | this.loadTrackerValues = function (json) { 176 | 177 | let elems = document.querySelectorAll('.tracker-enabled'); 178 | elems.forEach(function(e) { 179 | e.classList.remove('tracker-enabled'); 180 | }); 181 | 182 | json.forEach(function (data) { 183 | if (data.enabled === true) { 184 | let selector = `.tracker-cell[data-row-id="${data.rowId}"][data-col-id="${data.colId}"]`; 185 | let elem = document.querySelector(selector); 186 | if (elem) { 187 | elem.classList.add("tracker-enabled"); 188 | } 189 | } 190 | }); 191 | }; 192 | 193 | /** 194 | * Listen on tracker-cell 195 | * Schedule if cell is clicked and toggle css class 196 | */ 197 | this.setupEvents = function () { 198 | 199 | let elems = document.querySelectorAll('.tracker-cell'); 200 | 201 | elems.forEach(function (e) { 202 | e.addEventListener('click', function(e) { 203 | let val = Object.assign({}, e.target.dataset); 204 | val.enabled = hasClass(e.target, "tracker-enabled"); 205 | let currentBeat = e.target.dataset.colId; 206 | if (val.colId > currentBeat) { 207 | this.scheduleAudioBeatNow(val); 208 | } 209 | e.target.classList.toggle('tracker-enabled'); 210 | }) 211 | }) 212 | } 213 | } 214 | 215 | module.exports = tracker; -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | const loadSampleSet = require('load-sample-set'); 2 | const selectElement = require('select-element'); 3 | const getSetFormValues = require('get-set-form-values'); 4 | const adsrGainNode = require('adsr-gain-node'); 5 | const simpleTracker = require('./simple-tracker'); 6 | const FileSaver = require('file-saver'); 7 | 8 | const getSetControls = require('./get-set-controls'); 9 | const getSetAudioOptions = new getSetControls(); 10 | 11 | const ctx = new AudioContext(); 12 | const defaultTrack = require('./default-track'); 13 | 14 | var buffers; 15 | var currentSampleData; 16 | var storage; 17 | 18 | function initializeSampleSet(ctx, dataUrl, track) { 19 | 20 | var sampleSetPromise = loadSampleSet(ctx, dataUrl); 21 | sampleSetPromise.then(function (data) { 22 | 23 | buffers = data.buffers; 24 | sampleData = data.data; 25 | 26 | if (!track) { 27 | track = storage.getTrack(); 28 | } 29 | 30 | if (!track.settings.measureLength) { 31 | track.settings.measureLength = 16; 32 | } 33 | 34 | currentSampleData = sampleData; 35 | setupTrackerHtml(sampleData, track.settings.measureLength); 36 | schedule.loadTrackerValues(track.beat); 37 | schedule.setupEvents(); 38 | }); 39 | 40 | } 41 | 42 | window.onload = function () { 43 | 44 | let formValues = new getSetFormValues(); 45 | let form = document.getElementById("trackerControls"); 46 | 47 | formValues.set(form, defaultTrack.settings); 48 | getSetAudioOptions.setTrackerControls(defaultTrack.settings); 49 | 50 | initializeSampleSet(ctx, defaultTrack.settings.sampleSet, defaultTrack); 51 | setupBaseEvents(); 52 | 53 | storage = new tracksLocalStorage(); 54 | storage.setupStorage(); 55 | }; 56 | 57 | var instrumentData = {}; 58 | function setupTrackerHtml(data, measureLength) { 59 | instrumentData = data; 60 | instrumentData.title = instrumentData.filename; 61 | schedule.drawTracker(data.filename.length, measureLength, instrumentData); 62 | return; 63 | } 64 | 65 | function disconnectNode(node, options) { 66 | let totalLength = 67 | options.attackTime + options.sustainTime + options.releaseTime; 68 | setTimeout(() => { 69 | node.disconnect(); 70 | }, totalLength * 1000); 71 | } 72 | 73 | function scheduleAudioBeat(beat, triggerTime) { 74 | 75 | let instrumentName = instrumentData.filename[beat.rowId]; 76 | let instrument = buffers[instrumentName].get(); 77 | let options = getSetAudioOptions.getTrackerControls(); 78 | 79 | 80 | function play(source) { 81 | 82 | source.detune.value = options.detune; 83 | 84 | // Gain 85 | let node = routeGain(source) 86 | node = routeDelay(node); 87 | // node = routeCompressor(node); 88 | node.connect(ctx.destination); 89 | source.start(triggerTime); 90 | 91 | } 92 | 93 | 94 | function routeGain (source) { 95 | let gain = new adsrGainNode(ctx); 96 | gain.mode = 'linearRampToValueAtTime'; 97 | let options = getSetAudioOptions.getTrackerControls(); 98 | 99 | let gainNode; 100 | 101 | // Not enabled - default gain 102 | if (!options.gainEnabled) { 103 | gainNode = gain.getGainNode(triggerTime); 104 | source.connect(gainNode); 105 | return gainNode; 106 | } 107 | 108 | gain.setOptions(options); 109 | gainNode = gain.getGainNode(triggerTime); 110 | source.connect(gainNode); 111 | return gainNode; 112 | 113 | 114 | } 115 | 116 | // Note delay always uses above gain - even if not enabled 117 | function routeDelay(node) { 118 | if (!options.delayEnabled) { 119 | return node; 120 | } 121 | 122 | // create delay node 123 | let delay = ctx.createDelay(); 124 | delay.delayTime.value = options.delay; 125 | 126 | // create adsr gain node 127 | let gain = new adsrGainNode(ctx); 128 | gain.mode = 'linearRampToValueAtTime'; 129 | gain.setOptions(options); 130 | let feedbackGain = gain.getGainNode(triggerTime); 131 | 132 | // create filter 133 | let filter = ctx.createBiquadFilter(); 134 | filter.frequency.value = options.filter; 135 | 136 | // delay -> feedbackGain 137 | delay.connect(feedbackGain); 138 | disconnectNode(delay, options); 139 | 140 | // feedback -> filter 141 | feedbackGain.connect(filter); 142 | 143 | // filter ->delay 144 | filter.connect(delay); 145 | 146 | node.connect(delay); 147 | 148 | return delay; 149 | } 150 | play(instrument); 151 | } 152 | 153 | var schedule = new simpleTracker(ctx, scheduleAudioBeat); 154 | 155 | function setupBaseEvents() { 156 | 157 | // var initializedCtx; 158 | document.getElementById('play').addEventListener('click', function (e) { 159 | 160 | ctx.resume().then(() => { 161 | console.log('Playback resumed successfully'); 162 | }); 163 | 164 | let storage = new tracksLocalStorage(); 165 | let track = storage.getTrack(); 166 | 167 | schedule.measureLength = track.settings.measureLength; 168 | schedule.stop(); 169 | 170 | schedule.runSchedule(getSetAudioOptions.options.bpm); 171 | }); 172 | 173 | document.getElementById('pause').addEventListener('click', function (e) { 174 | schedule.stop(); 175 | }); 176 | 177 | document.getElementById('stop').addEventListener('click', function (e) { 178 | schedule.stop(); 179 | schedule = new simpleTracker(ctx, scheduleAudioBeat); 180 | }); 181 | 182 | document.getElementById('bpm').addEventListener('change', function (e) { 183 | getSetAudioOptions.setTrackerControls(); 184 | if (schedule.running) { 185 | schedule.stop(); 186 | schedule.runSchedule(getSetAudioOptions.options.bpm); 187 | } 188 | }); 189 | 190 | document.getElementById('measureLength').addEventListener('input', (e) => { 191 | 192 | $('#measureLength').bind('keypress keydown keyup', (e) => { 193 | if (e.keyCode == 13) { 194 | 195 | e.preventDefault(); 196 | 197 | let value = document.getElementById('measureLength').value; 198 | let length = parseInt(value); 199 | 200 | if (length < 1) return; 201 | schedule.measureLength = length; 202 | 203 | let track = schedule.getTrackerValues(); 204 | setupTrackerHtml(currentSampleData, length); 205 | schedule.measureLength = length; 206 | schedule.loadTrackerValues(track) 207 | schedule.setupEvents(); 208 | } 209 | }); 210 | }); 211 | 212 | $('.base').on('change', function () { 213 | getSetAudioOptions.setTrackerControls(); 214 | }); 215 | } 216 | 217 | $('#sampleSet').on('change', function () { 218 | initializeSampleSet(ctx, this.value); 219 | }); 220 | 221 | function tracksLocalStorage() { 222 | 223 | this.setLocalStorage = function (update) { 224 | var storage = {}; 225 | storage['Select'] = 'Select'; 226 | 227 | 228 | for (var i = 0, len = localStorage.length; i < len; ++i) { 229 | let item = localStorage.key(i); 230 | storage[item] = item; 231 | } 232 | 233 | // Create select element 234 | var s = new selectElement( 235 | 'load-storage', // id to append the select list to 236 | 'beat-list', // id of the select list 237 | storage // 238 | ); 239 | 240 | if (update) { 241 | s.update('beat-list', storage); 242 | } else { 243 | s.create(); 244 | } 245 | }; 246 | 247 | this.getFilename = function () { 248 | let filename = $('#filename').val(); 249 | if (!filename) { 250 | filename = 'untitled'; 251 | } 252 | return filename; 253 | } 254 | 255 | /** 256 | * Get complete song 257 | */ 258 | this.getTrack = function () { 259 | let formData = getSetAudioOptions.getTrackerControls(); 260 | 261 | let beat = schedule.getTrackerValues(); 262 | let song = { "beat": beat, "settings": formData }; 263 | 264 | return song; 265 | } 266 | 267 | this.alert = function (message) { 268 | let appMessage = document.getElementById('app-message'); 269 | 270 | appMessage.innerHTML = message 271 | appMessage.style.display = 'block' 272 | setTimeout(function () { 273 | appMessage.style.display = 'none' 274 | }, 2000) 275 | } 276 | 277 | this.setupStorage = function () { 278 | 279 | this.setLocalStorage(); 280 | document.getElementById('save').addEventListener('click', (e) => { 281 | e.preventDefault(); 282 | 283 | let song = this.getTrack(); 284 | let json = JSON.stringify(song); 285 | 286 | let filename = this.getFilename(); 287 | 288 | localStorage.setItem(filename, json); 289 | this.setLocalStorage('update'); 290 | 291 | $("#beat-list").val(filename); 292 | 293 | this.alert(`The track has been saved to local storage as ${filename}`) 294 | 295 | }); 296 | 297 | // saveAsJson 298 | document.getElementById('saveAsJson').addEventListener('click', (e) => { 299 | e.preventDefault(); 300 | 301 | let song = this.getTrack(); 302 | let json = JSON.stringify(song); 303 | 304 | let filename = this.getFilename(); 305 | 306 | var blob = new Blob([json], {type: "application/json"}); 307 | FileSaver.saveAs(blob, filename + ".json"); 308 | 309 | 310 | }); 311 | 312 | $('#filename').bind('keypress keydown keyup', (e) => { 313 | if (e.keyCode == 13) { 314 | e.preventDefault(); 315 | } 316 | }); 317 | 318 | document.getElementById('beat-list').addEventListener('change', (e) => { 319 | let item = $('#beat-list').val(); 320 | if (item === 'Select') { 321 | document.getElementById('filename').value = ''; 322 | return; 323 | } 324 | 325 | document.getElementById('filename').value = item; 326 | let track = JSON.parse(localStorage.getItem(item)); 327 | 328 | let formValues = new getSetFormValues(); 329 | let form = document.getElementById("trackerControls"); 330 | 331 | formValues.set(form, track.settings); 332 | getSetAudioOptions.setTrackerControls(track.settings); 333 | schedule.stop(); 334 | schedule.measureLength = track.settings.measureLength; 335 | 336 | initializeSampleSet(ctx, track.settings.sampleSet, track); 337 | 338 | }); 339 | 340 | document.getElementById('delete').addEventListener('click', (e) => { 341 | 342 | e.preventDefault(); 343 | 344 | let elem = document.getElementById('beat-list'); 345 | let toDelete = elem.options[elem.selectedIndex].text; 346 | 347 | localStorage.removeItem(toDelete); 348 | document.getElementById('filename').value = ''; 349 | this.setLocalStorage('update'); 350 | 351 | this.alert(`Track has been deleted`) 352 | 353 | }); 354 | }; 355 | } 356 | -------------------------------------------------------------------------------- /src/default-track.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | beat: [ 3 | { rowId: "0", colId: "0", enabled: false }, 4 | { rowId: "0", colId: "1", enabled: false }, 5 | { rowId: "0", colId: "2", enabled: false }, 6 | { rowId: "0", colId: "3", enabled: false }, 7 | { rowId: "0", colId: "4", enabled: false }, 8 | { rowId: "0", colId: "5", enabled: false }, 9 | { rowId: "0", colId: "6", enabled: false }, 10 | { rowId: "0", colId: "7", enabled: false }, 11 | { rowId: "0", colId: "8", enabled: false }, 12 | { rowId: "0", colId: "9", enabled: false }, 13 | { rowId: "0", colId: "10", enabled: false }, 14 | { rowId: "0", colId: "11", enabled: false }, 15 | { rowId: "0", colId: "12", enabled: false }, 16 | { rowId: "0", colId: "13", enabled: false }, 17 | { rowId: "0", colId: "14", enabled: false }, 18 | { rowId: "0", colId: "15", enabled: false }, 19 | { rowId: "0", colId: "16", enabled: false }, 20 | { rowId: "0", colId: "17", enabled: false }, 21 | { rowId: "0", colId: "18", enabled: false }, 22 | { rowId: "0", colId: "19", enabled: false }, 23 | { rowId: "0", colId: "20", enabled: false }, 24 | { rowId: "0", colId: "21", enabled: false }, 25 | { rowId: "0", colId: "22", enabled: false }, 26 | { rowId: "0", colId: "23", enabled: false }, 27 | { rowId: "0", colId: "24", enabled: false }, 28 | { rowId: "0", colId: "25", enabled: false }, 29 | { rowId: "0", colId: "26", enabled: false }, 30 | { rowId: "0", colId: "27", enabled: false }, 31 | { rowId: "0", colId: "28", enabled: false }, 32 | { rowId: "0", colId: "29", enabled: false }, 33 | { rowId: "0", colId: "30", enabled: false }, 34 | { rowId: "0", colId: "31", enabled: false }, 35 | { rowId: "1", colId: "0", enabled: false }, 36 | { rowId: "1", colId: "1", enabled: false }, 37 | { rowId: "1", colId: "2", enabled: false }, 38 | { rowId: "1", colId: "3", enabled: false }, 39 | { rowId: "1", colId: "4", enabled: false }, 40 | { rowId: "1", colId: "5", enabled: false }, 41 | { rowId: "1", colId: "6", enabled: false }, 42 | { rowId: "1", colId: "7", enabled: false }, 43 | { rowId: "1", colId: "8", enabled: false }, 44 | { rowId: "1", colId: "9", enabled: false }, 45 | { rowId: "1", colId: "10", enabled: false }, 46 | { rowId: "1", colId: "11", enabled: false }, 47 | { rowId: "1", colId: "12", enabled: false }, 48 | { rowId: "1", colId: "13", enabled: false }, 49 | { rowId: "1", colId: "14", enabled: false }, 50 | { rowId: "1", colId: "15", enabled: false }, 51 | { rowId: "1", colId: "16", enabled: false }, 52 | { rowId: "1", colId: "17", enabled: false }, 53 | { rowId: "1", colId: "18", enabled: false }, 54 | { rowId: "1", colId: "19", enabled: false }, 55 | { rowId: "1", colId: "20", enabled: false }, 56 | { rowId: "1", colId: "21", enabled: false }, 57 | { rowId: "1", colId: "22", enabled: false }, 58 | { rowId: "1", colId: "23", enabled: false }, 59 | { rowId: "1", colId: "24", enabled: false }, 60 | { rowId: "1", colId: "25", enabled: false }, 61 | { rowId: "1", colId: "26", enabled: false }, 62 | { rowId: "1", colId: "27", enabled: false }, 63 | { rowId: "1", colId: "28", enabled: false }, 64 | { rowId: "1", colId: "29", enabled: false }, 65 | { rowId: "1", colId: "30", enabled: false }, 66 | { rowId: "1", colId: "31", enabled: false }, 67 | { rowId: "2", colId: "0", enabled: false }, 68 | { rowId: "2", colId: "1", enabled: false }, 69 | { rowId: "2", colId: "2", enabled: false }, 70 | { rowId: "2", colId: "3", enabled: false }, 71 | { rowId: "2", colId: "4", enabled: true }, 72 | { rowId: "2", colId: "5", enabled: false }, 73 | { rowId: "2", colId: "6", enabled: false }, 74 | { rowId: "2", colId: "7", enabled: false }, 75 | { rowId: "2", colId: "8", enabled: false }, 76 | { rowId: "2", colId: "9", enabled: false }, 77 | { rowId: "2", colId: "10", enabled: false }, 78 | { rowId: "2", colId: "11", enabled: false }, 79 | { rowId: "2", colId: "12", enabled: false }, 80 | { rowId: "2", colId: "13", enabled: false }, 81 | { rowId: "2", colId: "14", enabled: false }, 82 | { rowId: "2", colId: "15", enabled: false }, 83 | { rowId: "2", colId: "16", enabled: false }, 84 | { rowId: "2", colId: "17", enabled: false }, 85 | { rowId: "2", colId: "18", enabled: false }, 86 | { rowId: "2", colId: "19", enabled: false }, 87 | { rowId: "2", colId: "20", enabled: false }, 88 | { rowId: "2", colId: "21", enabled: false }, 89 | { rowId: "2", colId: "22", enabled: false }, 90 | { rowId: "2", colId: "23", enabled: true }, 91 | { rowId: "2", colId: "24", enabled: false }, 92 | { rowId: "2", colId: "25", enabled: false }, 93 | { rowId: "2", colId: "26", enabled: false }, 94 | { rowId: "2", colId: "27", enabled: false }, 95 | { rowId: "2", colId: "28", enabled: false }, 96 | { rowId: "2", colId: "29", enabled: false }, 97 | { rowId: "2", colId: "30", enabled: false }, 98 | { rowId: "2", colId: "31", enabled: false }, 99 | { rowId: "3", colId: "0", enabled: false }, 100 | { rowId: "3", colId: "1", enabled: true }, 101 | { rowId: "3", colId: "2", enabled: false }, 102 | { rowId: "3", colId: "3", enabled: false }, 103 | { rowId: "3", colId: "4", enabled: false }, 104 | { rowId: "3", colId: "5", enabled: false }, 105 | { rowId: "3", colId: "6", enabled: true }, 106 | { rowId: "3", colId: "7", enabled: false }, 107 | { rowId: "3", colId: "8", enabled: false }, 108 | { rowId: "3", colId: "9", enabled: false }, 109 | { rowId: "3", colId: "10", enabled: false }, 110 | { rowId: "3", colId: "11", enabled: false }, 111 | { rowId: "3", colId: "12", enabled: false }, 112 | { rowId: "3", colId: "13", enabled: false }, 113 | { rowId: "3", colId: "14", enabled: false }, 114 | { rowId: "3", colId: "15", enabled: false }, 115 | { rowId: "3", colId: "16", enabled: false }, 116 | { rowId: "3", colId: "17", enabled: true }, 117 | { rowId: "3", colId: "18", enabled: false }, 118 | { rowId: "3", colId: "19", enabled: false }, 119 | { rowId: "3", colId: "20", enabled: false }, 120 | { rowId: "3", colId: "21", enabled: false }, 121 | { rowId: "3", colId: "22", enabled: false }, 122 | { rowId: "3", colId: "23", enabled: false }, 123 | { rowId: "3", colId: "24", enabled: false }, 124 | { rowId: "3", colId: "25", enabled: false }, 125 | { rowId: "3", colId: "26", enabled: false }, 126 | { rowId: "3", colId: "27", enabled: false }, 127 | { rowId: "3", colId: "28", enabled: false }, 128 | { rowId: "3", colId: "29", enabled: false }, 129 | { rowId: "3", colId: "30", enabled: false }, 130 | { rowId: "3", colId: "31", enabled: false }, 131 | { rowId: "4", colId: "0", enabled: true }, 132 | { rowId: "4", colId: "1", enabled: false }, 133 | { rowId: "4", colId: "2", enabled: true }, 134 | { rowId: "4", colId: "3", enabled: false }, 135 | { rowId: "4", colId: "4", enabled: false }, 136 | { rowId: "4", colId: "5", enabled: false }, 137 | { rowId: "4", colId: "6", enabled: false }, 138 | { rowId: "4", colId: "7", enabled: false }, 139 | { rowId: "4", colId: "8", enabled: false }, 140 | { rowId: "4", colId: "9", enabled: false }, 141 | { rowId: "4", colId: "10", enabled: false }, 142 | { rowId: "4", colId: "11", enabled: false }, 143 | { rowId: "4", colId: "12", enabled: true }, 144 | { rowId: "4", colId: "13", enabled: false }, 145 | { rowId: "4", colId: "14", enabled: true }, 146 | { rowId: "4", colId: "15", enabled: false }, 147 | { rowId: "4", colId: "16", enabled: false }, 148 | { rowId: "4", colId: "17", enabled: false }, 149 | { rowId: "4", colId: "18", enabled: false }, 150 | { rowId: "4", colId: "19", enabled: false }, 151 | { rowId: "4", colId: "20", enabled: false }, 152 | { rowId: "4", colId: "21", enabled: true }, 153 | { rowId: "4", colId: "22", enabled: false }, 154 | { rowId: "4", colId: "23", enabled: false }, 155 | { rowId: "4", colId: "24", enabled: false }, 156 | { rowId: "4", colId: "25", enabled: true }, 157 | { rowId: "4", colId: "26", enabled: false }, 158 | { rowId: "4", colId: "27", enabled: false }, 159 | { rowId: "4", colId: "28", enabled: false }, 160 | { rowId: "4", colId: "29", enabled: false }, 161 | { rowId: "4", colId: "30", enabled: false }, 162 | { rowId: "4", colId: "31", enabled: false }, 163 | { rowId: "5", colId: "0", enabled: false }, 164 | { rowId: "5", colId: "1", enabled: false }, 165 | { rowId: "5", colId: "2", enabled: false }, 166 | { rowId: "5", colId: "3", enabled: false }, 167 | { rowId: "5", colId: "4", enabled: true }, 168 | { rowId: "5", colId: "5", enabled: false }, 169 | { rowId: "5", colId: "6", enabled: false }, 170 | { rowId: "5", colId: "7", enabled: true }, 171 | { rowId: "5", colId: "8", enabled: false }, 172 | { rowId: "5", colId: "9", enabled: false }, 173 | { rowId: "5", colId: "10", enabled: false }, 174 | { rowId: "5", colId: "11", enabled: true }, 175 | { rowId: "5", colId: "12", enabled: false }, 176 | { rowId: "5", colId: "13", enabled: false }, 177 | { rowId: "5", colId: "14", enabled: false }, 178 | { rowId: "5", colId: "15", enabled: true }, 179 | { rowId: "5", colId: "16", enabled: false }, 180 | { rowId: "5", colId: "17", enabled: false }, 181 | { rowId: "5", colId: "18", enabled: false }, 182 | { rowId: "5", colId: "19", enabled: true }, 183 | { rowId: "5", colId: "20", enabled: false }, 184 | { rowId: "5", colId: "21", enabled: false }, 185 | { rowId: "5", colId: "22", enabled: false }, 186 | { rowId: "5", colId: "23", enabled: false }, 187 | { rowId: "5", colId: "24", enabled: false }, 188 | { rowId: "5", colId: "25", enabled: false }, 189 | { rowId: "5", colId: "26", enabled: false }, 190 | { rowId: "5", colId: "27", enabled: false }, 191 | { rowId: "5", colId: "28", enabled: false }, 192 | { rowId: "5", colId: "29", enabled: false }, 193 | { rowId: "5", colId: "30", enabled: false }, 194 | { rowId: "5", colId: "31", enabled: false }, 195 | { rowId: "6", colId: "0", enabled: false }, 196 | { rowId: "6", colId: "1", enabled: false }, 197 | { rowId: "6", colId: "2", enabled: false }, 198 | { rowId: "6", colId: "3", enabled: false }, 199 | { rowId: "6", colId: "4", enabled: false }, 200 | { rowId: "6", colId: "5", enabled: false }, 201 | { rowId: "6", colId: "6", enabled: false }, 202 | { rowId: "6", colId: "7", enabled: false }, 203 | { rowId: "6", colId: "8", enabled: false }, 204 | { rowId: "6", colId: "9", enabled: false }, 205 | { rowId: "6", colId: "10", enabled: false }, 206 | { rowId: "6", colId: "11", enabled: false }, 207 | { rowId: "6", colId: "12", enabled: false }, 208 | { rowId: "6", colId: "13", enabled: false }, 209 | { rowId: "6", colId: "14", enabled: false }, 210 | { rowId: "6", colId: "15", enabled: false }, 211 | { rowId: "6", colId: "16", enabled: false }, 212 | { rowId: "6", colId: "17", enabled: false }, 213 | { rowId: "6", colId: "18", enabled: false }, 214 | { rowId: "6", colId: "19", enabled: false }, 215 | { rowId: "6", colId: "20", enabled: false }, 216 | { rowId: "6", colId: "21", enabled: false }, 217 | { rowId: "6", colId: "22", enabled: false }, 218 | { rowId: "6", colId: "23", enabled: false }, 219 | { rowId: "6", colId: "24", enabled: false }, 220 | { rowId: "6", colId: "25", enabled: false }, 221 | { rowId: "6", colId: "26", enabled: false }, 222 | { rowId: "6", colId: "27", enabled: true }, 223 | { rowId: "6", colId: "28", enabled: false }, 224 | { rowId: "6", colId: "29", enabled: false }, 225 | { rowId: "6", colId: "30", enabled: false }, 226 | { rowId: "6", colId: "31", enabled: true }, 227 | { rowId: "7", colId: "0", enabled: false }, 228 | { rowId: "7", colId: "1", enabled: false }, 229 | { rowId: "7", colId: "2", enabled: false }, 230 | { rowId: "7", colId: "3", enabled: false }, 231 | { rowId: "7", colId: "4", enabled: false }, 232 | { rowId: "7", colId: "5", enabled: false }, 233 | { rowId: "7", colId: "6", enabled: false }, 234 | { rowId: "7", colId: "7", enabled: false }, 235 | { rowId: "7", colId: "8", enabled: false }, 236 | { rowId: "7", colId: "9", enabled: true }, 237 | { rowId: "7", colId: "10", enabled: false }, 238 | { rowId: "7", colId: "11", enabled: false }, 239 | { rowId: "7", colId: "12", enabled: false }, 240 | { rowId: "7", colId: "13", enabled: false }, 241 | { rowId: "7", colId: "14", enabled: false }, 242 | { rowId: "7", colId: "15", enabled: false }, 243 | { rowId: "7", colId: "16", enabled: false }, 244 | { rowId: "7", colId: "17", enabled: false }, 245 | { rowId: "7", colId: "18", enabled: false }, 246 | { rowId: "7", colId: "19", enabled: false }, 247 | { rowId: "7", colId: "20", enabled: false }, 248 | { rowId: "7", colId: "21", enabled: false }, 249 | { rowId: "7", colId: "22", enabled: false }, 250 | { rowId: "7", colId: "23", enabled: false }, 251 | { rowId: "7", colId: "24", enabled: false }, 252 | { rowId: "7", colId: "25", enabled: false }, 253 | { rowId: "7", colId: "26", enabled: false }, 254 | { rowId: "7", colId: "27", enabled: false }, 255 | { rowId: "7", colId: "28", enabled: false }, 256 | { rowId: "7", colId: "29", enabled: false }, 257 | { rowId: "7", colId: "30", enabled: false }, 258 | { rowId: "7", colId: "31", enabled: false }, 259 | { rowId: "8", colId: "0", enabled: false }, 260 | { rowId: "8", colId: "1", enabled: false }, 261 | { rowId: "8", colId: "2", enabled: false }, 262 | { rowId: "8", colId: "3", enabled: false }, 263 | { rowId: "8", colId: "4", enabled: false }, 264 | { rowId: "8", colId: "5", enabled: false }, 265 | { rowId: "8", colId: "6", enabled: false }, 266 | { rowId: "8", colId: "7", enabled: false }, 267 | { rowId: "8", colId: "8", enabled: false }, 268 | { rowId: "8", colId: "9", enabled: false }, 269 | { rowId: "8", colId: "10", enabled: false }, 270 | { rowId: "8", colId: "11", enabled: false }, 271 | { rowId: "8", colId: "12", enabled: false }, 272 | { rowId: "8", colId: "13", enabled: false }, 273 | { rowId: "8", colId: "14", enabled: false }, 274 | { rowId: "8", colId: "15", enabled: false }, 275 | { rowId: "8", colId: "16", enabled: false }, 276 | { rowId: "8", colId: "17", enabled: false }, 277 | { rowId: "8", colId: "18", enabled: false }, 278 | { rowId: "8", colId: "19", enabled: false }, 279 | { rowId: "8", colId: "20", enabled: false }, 280 | { rowId: "8", colId: "21", enabled: false }, 281 | { rowId: "8", colId: "22", enabled: false }, 282 | { rowId: "8", colId: "23", enabled: false }, 283 | { rowId: "8", colId: "24", enabled: false }, 284 | { rowId: "8", colId: "25", enabled: false }, 285 | { rowId: "8", colId: "26", enabled: false }, 286 | { rowId: "8", colId: "27", enabled: false }, 287 | { rowId: "8", colId: "28", enabled: false }, 288 | { rowId: "8", colId: "29", enabled: true }, 289 | { rowId: "8", colId: "30", enabled: false }, 290 | { rowId: "8", colId: "31", enabled: false }, 291 | { rowId: "9", colId: "0", enabled: false }, 292 | { rowId: "9", colId: "1", enabled: false }, 293 | { rowId: "9", colId: "2", enabled: false }, 294 | { rowId: "9", colId: "3", enabled: false }, 295 | { rowId: "9", colId: "4", enabled: false }, 296 | { rowId: "9", colId: "5", enabled: false }, 297 | { rowId: "9", colId: "6", enabled: false }, 298 | { rowId: "9", colId: "7", enabled: false }, 299 | { rowId: "9", colId: "8", enabled: false }, 300 | { rowId: "9", colId: "9", enabled: false }, 301 | { rowId: "9", colId: "10", enabled: false }, 302 | { rowId: "9", colId: "11", enabled: false }, 303 | { rowId: "9", colId: "12", enabled: false }, 304 | { rowId: "9", colId: "13", enabled: false }, 305 | { rowId: "9", colId: "14", enabled: false }, 306 | { rowId: "9", colId: "15", enabled: false }, 307 | { rowId: "9", colId: "16", enabled: false }, 308 | { rowId: "9", colId: "17", enabled: false }, 309 | { rowId: "9", colId: "18", enabled: false }, 310 | { rowId: "9", colId: "19", enabled: false }, 311 | { rowId: "9", colId: "20", enabled: false }, 312 | { rowId: "9", colId: "21", enabled: false }, 313 | { rowId: "9", colId: "22", enabled: false }, 314 | { rowId: "9", colId: "23", enabled: false }, 315 | { rowId: "9", colId: "24", enabled: false }, 316 | { rowId: "9", colId: "25", enabled: false }, 317 | { rowId: "9", colId: "26", enabled: false }, 318 | { rowId: "9", colId: "27", enabled: false }, 319 | { rowId: "9", colId: "28", enabled: false }, 320 | { rowId: "9", colId: "29", enabled: false }, 321 | { rowId: "9", colId: "30", enabled: false }, 322 | { rowId: "9", colId: "31", enabled: false }, 323 | { rowId: "10", colId: "0", enabled: false }, 324 | { rowId: "10", colId: "1", enabled: false }, 325 | { rowId: "10", colId: "2", enabled: false }, 326 | { rowId: "10", colId: "3", enabled: false }, 327 | { rowId: "10", colId: "4", enabled: false }, 328 | { rowId: "10", colId: "5", enabled: false }, 329 | { rowId: "10", colId: "6", enabled: false }, 330 | { rowId: "10", colId: "7", enabled: false }, 331 | { rowId: "10", colId: "8", enabled: false }, 332 | { rowId: "10", colId: "9", enabled: false }, 333 | { rowId: "10", colId: "10", enabled: false }, 334 | { rowId: "10", colId: "11", enabled: false }, 335 | { rowId: "10", colId: "12", enabled: false }, 336 | { rowId: "10", colId: "13", enabled: false }, 337 | { rowId: "10", colId: "14", enabled: false }, 338 | { rowId: "10", colId: "15", enabled: false }, 339 | { rowId: "10", colId: "16", enabled: false }, 340 | { rowId: "10", colId: "17", enabled: false }, 341 | { rowId: "10", colId: "18", enabled: false }, 342 | { rowId: "10", colId: "19", enabled: false }, 343 | { rowId: "10", colId: "20", enabled: false }, 344 | { rowId: "10", colId: "21", enabled: false }, 345 | { rowId: "10", colId: "22", enabled: false }, 346 | { rowId: "10", colId: "23", enabled: false }, 347 | { rowId: "10", colId: "24", enabled: false }, 348 | { rowId: "10", colId: "25", enabled: false }, 349 | { rowId: "10", colId: "26", enabled: false }, 350 | { rowId: "10", colId: "27", enabled: false }, 351 | { rowId: "10", colId: "28", enabled: false }, 352 | { rowId: "10", colId: "29", enabled: false }, 353 | { rowId: "10", colId: "30", enabled: false }, 354 | { rowId: "10", colId: "31", enabled: false }, 355 | { rowId: "11", colId: "0", enabled: false }, 356 | { rowId: "11", colId: "1", enabled: false }, 357 | { rowId: "11", colId: "2", enabled: false }, 358 | { rowId: "11", colId: "3", enabled: false }, 359 | { rowId: "11", colId: "4", enabled: false }, 360 | { rowId: "11", colId: "5", enabled: false }, 361 | { rowId: "11", colId: "6", enabled: false }, 362 | { rowId: "11", colId: "7", enabled: false }, 363 | { rowId: "11", colId: "8", enabled: false }, 364 | { rowId: "11", colId: "9", enabled: false }, 365 | { rowId: "11", colId: "10", enabled: false }, 366 | { rowId: "11", colId: "11", enabled: false }, 367 | { rowId: "11", colId: "12", enabled: false }, 368 | { rowId: "11", colId: "13", enabled: false }, 369 | { rowId: "11", colId: "14", enabled: false }, 370 | { rowId: "11", colId: "15", enabled: false }, 371 | { rowId: "11", colId: "16", enabled: false }, 372 | { rowId: "11", colId: "17", enabled: false }, 373 | { rowId: "11", colId: "18", enabled: false }, 374 | { rowId: "11", colId: "19", enabled: false }, 375 | { rowId: "11", colId: "20", enabled: false }, 376 | { rowId: "11", colId: "21", enabled: false }, 377 | { rowId: "11", colId: "22", enabled: false }, 378 | { rowId: "11", colId: "23", enabled: false }, 379 | { rowId: "11", colId: "24", enabled: false }, 380 | { rowId: "11", colId: "25", enabled: false }, 381 | { rowId: "11", colId: "26", enabled: false }, 382 | { rowId: "11", colId: "27", enabled: false }, 383 | { rowId: "11", colId: "28", enabled: false }, 384 | { rowId: "11", colId: "29", enabled: false }, 385 | { rowId: "11", colId: "30", enabled: false }, 386 | { rowId: "11", colId: "31", enabled: false }, 387 | { rowId: "12", colId: "0", enabled: false }, 388 | { rowId: "12", colId: "1", enabled: false }, 389 | { rowId: "12", colId: "2", enabled: false }, 390 | { rowId: "12", colId: "3", enabled: false }, 391 | { rowId: "12", colId: "4", enabled: false }, 392 | { rowId: "12", colId: "5", enabled: false }, 393 | { rowId: "12", colId: "6", enabled: false }, 394 | { rowId: "12", colId: "7", enabled: false }, 395 | { rowId: "12", colId: "8", enabled: false }, 396 | { rowId: "12", colId: "9", enabled: false }, 397 | { rowId: "12", colId: "10", enabled: false }, 398 | { rowId: "12", colId: "11", enabled: false }, 399 | { rowId: "12", colId: "12", enabled: false }, 400 | { rowId: "12", colId: "13", enabled: false }, 401 | { rowId: "12", colId: "14", enabled: false }, 402 | { rowId: "12", colId: "15", enabled: false }, 403 | { rowId: "12", colId: "16", enabled: false }, 404 | { rowId: "12", colId: "17", enabled: false }, 405 | { rowId: "12", colId: "18", enabled: false }, 406 | { rowId: "12", colId: "19", enabled: false }, 407 | { rowId: "12", colId: "20", enabled: false }, 408 | { rowId: "12", colId: "21", enabled: false }, 409 | { rowId: "12", colId: "22", enabled: false }, 410 | { rowId: "12", colId: "23", enabled: true }, 411 | { rowId: "12", colId: "24", enabled: false }, 412 | { rowId: "12", colId: "25", enabled: true }, 413 | { rowId: "12", colId: "26", enabled: false }, 414 | { rowId: "12", colId: "27", enabled: false }, 415 | { rowId: "12", colId: "28", enabled: false }, 416 | { rowId: "12", colId: "29", enabled: false }, 417 | { rowId: "12", colId: "30", enabled: false }, 418 | { rowId: "12", colId: "31", enabled: false }, 419 | { rowId: "13", colId: "0", enabled: false }, 420 | { rowId: "13", colId: "1", enabled: false }, 421 | { rowId: "13", colId: "2", enabled: false }, 422 | { rowId: "13", colId: "3", enabled: false }, 423 | { rowId: "13", colId: "4", enabled: false }, 424 | { rowId: "13", colId: "5", enabled: false }, 425 | { rowId: "13", colId: "6", enabled: false }, 426 | { rowId: "13", colId: "7", enabled: false }, 427 | { rowId: "13", colId: "8", enabled: false }, 428 | { rowId: "13", colId: "9", enabled: false }, 429 | { rowId: "13", colId: "10", enabled: false }, 430 | { rowId: "13", colId: "11", enabled: false }, 431 | { rowId: "13", colId: "12", enabled: false }, 432 | { rowId: "13", colId: "13", enabled: false }, 433 | { rowId: "13", colId: "14", enabled: false }, 434 | { rowId: "13", colId: "15", enabled: false }, 435 | { rowId: "13", colId: "16", enabled: false }, 436 | { rowId: "13", colId: "17", enabled: false }, 437 | { rowId: "13", colId: "18", enabled: false }, 438 | { rowId: "13", colId: "19", enabled: false }, 439 | { rowId: "13", colId: "20", enabled: false }, 440 | { rowId: "13", colId: "21", enabled: false }, 441 | { rowId: "13", colId: "22", enabled: false }, 442 | { rowId: "13", colId: "23", enabled: false }, 443 | { rowId: "13", colId: "24", enabled: true }, 444 | { rowId: "13", colId: "25", enabled: false }, 445 | { rowId: "13", colId: "26", enabled: false }, 446 | { rowId: "13", colId: "27", enabled: false }, 447 | { rowId: "13", colId: "28", enabled: true }, 448 | { rowId: "13", colId: "29", enabled: false }, 449 | { rowId: "13", colId: "30", enabled: false }, 450 | { rowId: "13", colId: "31", enabled: false }, 451 | { rowId: "14", colId: "0", enabled: false }, 452 | { rowId: "14", colId: "1", enabled: false }, 453 | { rowId: "14", colId: "2", enabled: false }, 454 | { rowId: "14", colId: "3", enabled: false }, 455 | { rowId: "14", colId: "4", enabled: false }, 456 | { rowId: "14", colId: "5", enabled: false }, 457 | { rowId: "14", colId: "6", enabled: false }, 458 | { rowId: "14", colId: "7", enabled: false }, 459 | { rowId: "14", colId: "8", enabled: false }, 460 | { rowId: "14", colId: "9", enabled: false }, 461 | { rowId: "14", colId: "10", enabled: false }, 462 | { rowId: "14", colId: "11", enabled: false }, 463 | { rowId: "14", colId: "12", enabled: false }, 464 | { rowId: "14", colId: "13", enabled: false }, 465 | { rowId: "14", colId: "14", enabled: false }, 466 | { rowId: "14", colId: "15", enabled: false }, 467 | { rowId: "14", colId: "16", enabled: false }, 468 | { rowId: "14", colId: "17", enabled: false }, 469 | { rowId: "14", colId: "18", enabled: false }, 470 | { rowId: "14", colId: "19", enabled: false }, 471 | { rowId: "14", colId: "20", enabled: false }, 472 | { rowId: "14", colId: "21", enabled: false }, 473 | { rowId: "14", colId: "22", enabled: false }, 474 | { rowId: "14", colId: "23", enabled: false }, 475 | { rowId: "14", colId: "24", enabled: false }, 476 | { rowId: "14", colId: "25", enabled: false }, 477 | { rowId: "14", colId: "26", enabled: true }, 478 | { rowId: "14", colId: "27", enabled: false }, 479 | { rowId: "14", colId: "28", enabled: false }, 480 | { rowId: "14", colId: "29", enabled: false }, 481 | { rowId: "14", colId: "30", enabled: false }, 482 | { rowId: "14", colId: "31", enabled: false } 483 | ], 484 | settings: { 485 | sampleSet: 486 | "https://raw.githubusercontent.com/oramics/sampled/master/DRUMS/pearl-master-studio/sampled.instrument.json", 487 | measureLength: 32, 488 | bpm: 460, 489 | detune: 0, 490 | gainEnabled: "gain", 491 | attackAmp: 0, 492 | sustainAmp: 0.4, 493 | decayAmp: 0.7, 494 | releaseAmp: 1, 495 | attackTime: 0, 496 | decayTime: 0, 497 | sustainTime: 2, 498 | releaseTime: 2, 499 | adsrInterval: 0.1, 500 | delay: 0.01, 501 | filter: 1000 502 | } 503 | }; 504 | --------------------------------------------------------------------------------