import Nexus from 'nexusui' // import Tone from 'tone' // import StartAudioContext from './startAudioContext' const isIphone = (navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)) const isIpad = (navigator.userAgent.match(/iPad/i)) const isAndroid = (navigator.userAgent.match(/Android/i)) const isMobile = isIphone || isIpad || isAndroid const isDesktop = ! isMobile document.body.classList.add(isMobile ? 'mobile' : 'desktop') const browser = { isIphone, isIpad, isMobile, isDesktop } function choice (a){ return a[ Math.floor(Math.random() * a.length) ] } function randint (n) { return Math.floor(Math.random() * n) } function mod(n,m){ return n-(m * Math.floor(n/m)) } function norm(n, min, max){ return (n - min) / (max - min) } function shuffle(a){ a = a.slice(0) for (var i = a.length; i > 0; i--){ var r = randint(i) var swap = a[i-1] a[i-1] = a[r] a[r] = swap } return a } function shuffleInPlace(a){ for (var i = a.length; i > 0; i--){ var r = randint(i) var swap = a[i-1] a[i-1] = a[r] a[r] = swap } return a } function requestAudioContext (fn) { if (isMobile) { const container = document.createElement('div') const button = document.createElement('div') button.innerHTML = 'Tap to start - please unmute your phone' Object.assign(container.style, { position: 'absolute', width: '100%', height: '100%', zIndex: '10000', top: '0px', left: '0px', backgroundColor: 'rgba(0, 0, 0, 0.8)', }) Object.assign(button.style, { position: 'absolute', left: '50%', top: '50%', padding: '20px', backgroundColor: '#7F33ED', color: 'white', fontFamily: 'monospace', borderRadius: '3px', transform: 'translate3D(-50%,-50%,0)', textAlign: 'center', lineHeight: '1.5', }) container.appendChild(button) document.body.appendChild(container) StartAudioContext.setContext(Tone.context) StartAudioContext.on(button) StartAudioContext.onStarted(_ => { container.remove() fn() }) } else { fn() } } function dataURItoBlob(dataURI) { // convert base64 to raw binary data held in a string // doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this var byteString = atob(dataURI.split(',')[1]); // separate out the mime component var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0] // write the bytes of the string to an ArrayBuffer var ab = new ArrayBuffer(byteString.length); // create a view into the buffer var ia = new Uint8Array(ab); // set the bytes of the buffer to the correct values for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } // write the ArrayBuffer to a blob, and you're done var blob = new Blob([ab], {type: mimeString}); return blob; } function ftom(f) { // return (Math.log(f) - Math.log(261.626)) / Math.log(2) + 4.0 return 69 + 12 * Math.log2(f / 440) } function mtof(m) { return 440 * Math.pow(2, (m - 69) / 12) } function tap (fn) { return (e) => { if (browser.isMobile) fn() else if (e.press) fn() } } function update_value_on_change(el, id, is_int, fn) { const label = document.querySelector(id + ' + .val') const update = v => { label.innerHTML = is_int ? parseInt(v) : v.toFixed(2) fn && fn(v) } el.on('change', update) label.innerHTML = is_int ? parseInt(el.value) : el.value.toFixed(2) el.update = update } function update_radio_value_on_change(el, id, values, fn) { let old_v = el.active const label = document.querySelector(id + ' + .val') const update = v => { if (v === -1) { v = el.active = old_v } else { old_v = v } label.innerHTML = values[v][1] fn && fn(v) } el.on('change', update) update(el.active) el.update = update } function build_options(el, lists, fn) { Object.keys(lists).forEach(key => { const list = lists[key] const option = document.createElement('option') option.innerHTML = list.name option.value = key el.appendChild(option) }) el.addEventListener('input', function(e){ fn(e.target.value) }) } function Slider(parent, tag, title, options, is_int, fn){ const block = document.createElement('div') block.classList.add('block') const el = document.createElement('div') el.setAttribute('id', tag) const val = document.createElement('span') val.classList.add('val') const label = document.createElement('label') label.classList = tag label.innerHTML = title block.appendChild(label) block.appendChild(el) block.appendChild(val) parent.appendChild(block) options.size = [200, 24] const nx = new Nexus.Slider('#' + tag, options) update_value_on_change(nx, '#' + tag, is_int, fn) return nx } function Statistic(parent, label, fn){ const block = document.createElement('div') block.classList.add('stat') const key = document.createElement('div') key.classList.add('key') const val = document.createElement('div') val.classList.add('val') block.appendChild(key) block.appendChild(val) parent.appendChild(block) key.innerHTML = label val.innerHTML = fn() let old_v return () => { const v = fn() if (old_v === v) return val.innerText = old_v = v } } export { mod, norm, choice, shuffle, randint, browser, requestAudioContext, ftom, mtof, tap, dataURItoBlob, update_value_on_change, update_radio_value_on_change, build_options, Slider, Statistic, }