import Tone from 'tone' import 'nexusui' import keys from './lib/keys' import color from './lib/color' import kalimba from './lib/kalimba' import scales from './lib/scales' const nx = window.nx const noteCount = 24 const stepCount = 16 let grid var loop = new Tone.Sequence(function(time, col){ var column = grid.matrix[col] grid.jumpToCol(col) for (var i = 0; i < noteCount; i++){ if (column[i] === 1){ const freq = scales.current().index(noteCount - i - 12) kalimba.play(freq) } } }, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], "16n") nx.colorize('#f4d142') nx.onload = () => { grid = nx.widgets.grid grid.sequenceMode = true grid.bpm = 1 grid.col = stepCount grid.row = noteCount grid.init() grid.resize(480, 640) grid.draw() nx.widgets.shiftUp.on('*', e => e.press && shiftUp()) nx.widgets.shiftDown.on('*', e => e.press && shiftDown()) nx.widgets.slideUp.on('*', e => e.press && slideUp()) nx.widgets.slideDown.on('*', e => e.press && slideDown()) nx.widgets.rotateUp.on('*', e => e.press && rotateVertical(-1)) nx.widgets.rotateDown.on('*', e => e.press && rotateVertical(1)) nx.widgets.rotateLeft.on('*', e => e.press && rotateHorizontal(-1)) nx.widgets.rotateRight.on('*', e => e.press && rotateHorizontal(1)) nx.widgets.flip.on('*', e => e.press && flip()) nx.widgets.flop.on('*', e => e.press && flop()) loop.start() Tone.Transport.bpm.value = 108 Tone.Transport.start() } function shiftUp () { } function shiftDown () { } function slideUp () { assignNotes( mapFunction(findNotes(), (n) => { return (n - 1 + noteCount) % noteCount })) } function slideDown () { assignNotes( mapFunction(findNotes(), (n) => { return (n + 1 + noteCount) % noteCount })) } function flip () { assignNotes( mapReverse(findNotes()) ) } function flop () { assignPositions( mapReverse(findPositions()) ) } function rotateHorizontal (n) { assignPositions( remapArray(findPositions(), n) ) } function rotateVertical (n) { assignNotes( remapArray(findNotes(), n) ) } function assignPositions (notes) { if (! notes) return const a = grid.matrix const b = iota() stride(a, (i,j,v) => { if (i in notes) { b[notes[i]][j] = v } }) assign(grid.matrix, b) grid.draw() } function assignNotes (positions) { if (! positions) return const a = grid.matrix const b = iota() stride(a, (i,j,v) => { if (j in positions) { b[i][positions[j]] = v } }) assign(grid.matrix, b) grid.draw() } function stride (a, f) { const w = a.length, h = a[0].length for (let i = 0; i < w; i++) { for (let j = 0; j < h; j++) { f(i, j, a[i][j]) } } return a } // assign b -> a function assign (a, b) { return stride(b, (i,j,v) => a[i][j] = b[i][j]) } function clone (a) { const b = iota(a.length, a[0].length) return assign(b, a) } function iota () { const w = grid.matrix.length, h = grid.matrix[0].length const a = new Array(w) for (let i = 0; i < w; i++) { a[i] = new Array(h) for (let j = 0; j < h; j++) { a[i][j] = 0 } } return a } function findNotes () { const a = new Array(grid.matrix[0].length) grid.matrix.forEach((col, i) => { col.forEach((v, j) => { if (v) a[j] = 1 }) }) return a.reduce((acc, v, i) => { if (v === 1) acc.push(i) return acc }, []) } function findPositions () { return grid.matrix.reduce((acc, row, i) => { if (row.some((x) => x === 1)) acc.push(i) return acc }, []) } function remapArray (a, n) { if (! a.length) return const h = {} const b = rotate(a, n) for (let i = 0; i < b.length; i++) { h[b[i]] = a[i] } return h } function rotate(a, n) { const b = a.slice(0) b.unshift.apply( b, b.splice( -n, b.length ) ) console.log(b) return b } function mapFunction (a, f) { if (! a.length) return const h = {} for (let i = 0; i < a.length; i++) { h[a[i]] = f(a[i]) } console.log(h) return h } function mapReverse (a) { if (! a.length) return const h = {} const b = a.slice(0).reverse() for (let i = 0; i < b.length; i++) { h[b[i]] = a[i] } return h } keys.listen(function(index){ const freq = scales.current().index(index) kalimba.play(freq) })