import Tone from 'tone' import StartAudioContext from './startAudioContext' export const isIphone = (navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)) export const isIpad = (navigator.userAgent.match(/iPad/i)) export const isAndroid = (navigator.userAgent.match(/Android/i)) export const isMobile = isIphone || isIpad || isAndroid export const isDesktop = ! isMobile document.body.classList.add(isMobile ? 'mobile' : 'desktop') export const browser = { isIphone, isIpad, isMobile, isDesktop } export function choice (a){ return a[ Math.floor(Math.random() * a.length) ] } export function mod(n,m){ return n-(m * Math.floor(n/m)) } export function norm(n, min, max){ return (n - min) / (max - min) } export 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() } } export 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; } export function ftom(f) { // return (Math.log(f) - Math.log(261.626)) / Math.log(2) + 4.0 return 69 + 12 * Math.log2(f / 440) } export function mtof(m) { return 440 * Math.pow(2, (m - 69) / 12) } export function tap (fn) { return (e) => { if (browser.isMobile) fn() else if (e.press) fn() } } /* get minimum and maximum variance from row-to-row */ export function get_diff_bounds(rows){ const diffs = rows.map(row => { const row_min = Math.min.apply(Math, row) const row_max = Math.max.apply(Math, row) return row_max - row_min }) const min = Math.min.apply(Math, diffs) const max = Math.max.apply(Math, diffs) return { min, max } } /* get minimum and maximum values from a dataset */ export function get_bounds(dataset){ let rows = dataset.lines // rows.forEach(row => row.shift()) rows = rows.map(a => a.map(n => parseFloat(n))) const max = rows.reduce((a,b) => { return b.reduce((z,bb) => { return Math.max(z, bb) }, a) }, -Infinity) const min = rows.reduce((a,b) => { return b.reduce((z,bb) => { return Math.min(z, bb) }, a) }, Infinity) return { rows, max, min } } /* transpose a 2D array */ export function transpose(a) { let i_len = a.length, j_len = a[0].length let T = new Array(i_len) for (let i = 0; i < i_len; i++) { T[i] = new Array(j_len) for (var j = 0; j < j_len; j++) { T[i][j] = a[j][i] } } return T }