import keys from './lib/keys'
import color from './lib/color'
import kalimba from './lib/kalimba'
import life from './lib/life'
import organ from './lib/organ'
import { browser, requestAudioContext, choice } from './lib/util'
let instrument = kalimba
const root = 440
const s = 50
const w = window.innerWidth
const h = window.innerHeight
const ws = Math.ceil(w/s), hs = Math.ceil(h/s)
const add_on = 0
const mul_on = 1.0
const add_off = 0.1
const mul_off = 0.9
let dragging = false
let erasing = false
let lastFreq = 0
let notes = []
requestAudioContext( () => {
for (var i = 0; i < ws; i++) {
notes[i] = []
for (var j = 0; j < hs; j++) {
notes[i][j] = add(i, j)
}
}
life.init(notes, assign)
})
function play(freq) {
freq.playing = true
instrument.play(freq.frequency)
freq.div.classList.add('playing')
life.assign_item(freq, true)
}
function pause(freq) {
freq.playing = false
instrument.pause(freq.frequency)
freq.div.classList.remove('playing')
life.assign_item(freq, false)
}
function assign(freq, state) {
if (state) {
play(freq)
} else {
pause(freq)
}
}
function toggle(freq) {
assign(freq, freq.playing = !freq.playing)
}
const gliderShape = [
[0,0,0,0,0],
[0,1,1,1,0],
[0,0,0,1,0],
[0,0,1,0,0],
[0,0,0,0,0],
]
const gliderShapeFlip = gliderShape.map(a => a.slice(0).reverse())
const gliderShapes = [
gliderShape,
gliderShapeFlip,
gliderShape.slice(0).reverse(),
gliderShapeFlip.slice(0).reverse(),
]
function glider() {
const x = Math.floor(Math.random() * ws)
const y = Math.floor(Math.random() * hs)
console.log("glider at", x, y)
const shape = choice(gliderShapes)
weave(x,y,shape)
}
function weave (x,y,shape) {
const xmag = shape.length
const ymag = shape[0].length
let i, j, px, py
for (i = 0; i < xmag; i++) {
for (j = 0; j < ymag; j++) {
px = (x+i) % ws
py = (y+j) % hs
assign(notes[px][py], shape[i][j])
}
}
}
function forEach(f){
let i, j, note, s;
for (i = 0; i < ws; i++) {
for (j = 0; j < hs; j++) {
note = notes[i][j]
s = f(i,j,note.playing)
assign(note, s)
}
}
}
function clear(){
forEach((x,y,state) => {
return false
})
}
function white(){
forEach((x,y,state) => {
return false
})
}
function stripex(odd){
odd = !! odd
forEach((x,y,state) => {
return x % 2 ? odd : !odd
})
}
function stripey(odd){
odd = !! odd
forEach((x,y,state) => {
return y % 2 ? odd : !odd
})
}
function noise(){
forEach((x,y,state) => {
return Math.random() < 0.5
})
}
function add (i, j) {
const a = i + 1
const b = j + 1
const div = document.createElement('div')
const frequency = root * a/b
let add = 0
let frac = Math.log2(a/b) % 1
div.style.left = (i * s) + 'px'
div.style.top = (j * s) + 'px'
div.innerHTML = `
${a}<\/div>
\/
${b}<\/div>`
const freq = { frequency, div, i, j, playing: false }
if (frac < 0) {
frac += 1
}
if (a < b) {
add = -Math.log(b/a) / 3.5
}
else {
add = Math.log(a/b) / 6
}
if ( frac === 0) {
div.style.fontWeight = '900'
div.style.left = (i * s) + 'px'
div.style.top = (j * s) + 'px'
}
div.style.backgroundColor = color(frac, add_off + add, mul_off)
if (browser.isDesktop) {
div.addEventListener('mousedown', function(){
div.style.backgroundColor = color(frac, add + add_on, mul_on)
toggle( freq )
erasing = !freq.playing
})
div.addEventListener('mouseenter', function(){
div.style.backgroundColor = color(frac, add + add_on, mul_on)
if (dragging) {
if (erasing) {
pause( freq )
} else {
toggle( freq )
}
}
})
div.addEventListener('mouseleave', function(){
div.style.backgroundColor = color(frac, add + add_off, mul_off)
})
}
else {
div.addEventListener('touchstart', function(e){
e.preventDefault()
toggle( freq )
erasing = !freq.playing
lastFreq = freq
})
}
document.body.appendChild(div)
return freq
}
if (browser.isDesktop) {
document.addEventListener('mousedown', () => { dragging = true })
document.addEventListener('mouseup', () => { dragging = false })
}
else {
document.addEventListener('touchstart', (e) => { e.preventDefault(); dragging = true })
document.addEventListener('touchmove', (e) => {
e.preventDefault()
const x = Math.floor( e.touches[0].pageX / s )
const y = Math.floor( e.touches[0].pageY / s )
if (! (x in notes) || ! (y in notes[x])) return
const freq = notes[x][y]
if (freq !== lastFreq) {
if (dragging) {
if (erasing) {
pause( freq )
} else {
toggle( freq )
}
}
lastFreq = freq
}
})
document.addEventListener('touchend', () => { dragging = false })
}
function swap_instrument(){
instrument = (instrument === kalimba) ? organ : kalimba
}
let life_bpm = 50
window.addEventListener("keydown", keydown, true)
function keydown(e){
// console.log(e.keyCode)
if (e.altKey || e.ctrlKey || e.metaKey) return
switch (e.keyCode){
case 32: // space
life.toggle()
break
case 38: // up
life_bpm += e.shiftKey ? 1 : 5
life.setTempo(life_bpm)
break
case 40: // down
life_bpm -= e.shiftKey ? 1 : 5
life_bpm = Math.max(1, life_bpm)
life.setTempo(life_bpm)
break
case 71: // g
glider()
break
case 83: // s
swap_instrument()
break
case 67: // c
clear()
break
case 87: // w
white()
break
case 78: // n
noise()
break
case 69: // e
stripex(Math.random() < 0.5)
break
case 82: // r
stripey(Math.random() < 0.5)
break
}
}
keys.listen(function(index){
// const freq = scales.current().index(index)
// document.body.style.backgroundColor = color( index / scales.current().scale.length )
// instrument.toggle(freq)
})
let hash = window.location.hash || window.location.search
if (hash.match('sin') || hash.match('organ')) {
instrument = organ
}
if (hash.match('glider')) {
instrument = organ
clear()
glider()
life.setTempo(life_bpm = 120 * 8)
life.toggle()
}