-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscript.js
121 lines (106 loc) · 4.07 KB
/
script.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
const rhythmDisplay = document.querySelector('.rhythm-display')
const pulsesInput = document.getElementById('pulses')
const stepsInput = document.getElementById('steps')
const pixelsInput = document.getElementById('pixels')
const playButton = document.getElementById('playButton') // think globally, act locally
const stopButton = document.getElementById('stopButton') // think globally, act locally
const bpmSlider = document.querySelector('input[name="bpm"]')
const volumeSlider = document.querySelector('input[name="volume"]')
Tone.Master.volume.value = -10
let currentSequence
stepsInput.addEventListener('input', handleInput)
pulsesInput.addEventListener('input', handleInput)
pixelsInput.addEventListener('input', event => generateStyleSheet(event.target.value))
playButton.addEventListener('click', _ => playSequence())
stopButton.addEventListener('click', _ => stopSequence())
volumeSlider.addEventListener('input', event => {
const volumeEmojis = ['🔇', '🔈', '🔉', '🔊'].reverse()
const RANGE = event.target.max - event.target.min
const decibels = parseInt(event.target.value)
const indexesBetweenEmojis = RANGE / volumeEmojis.length
const emojiIndex = Math.floor((-decibels + 12) / indexesBetweenEmojis)
document.querySelector('label[for="volume"] > span').innerText = volumeEmojis[emojiIndex] || volumeEmojis[3]
Tone.Master.volume.value = decibels
})
bpmSlider.addEventListener('input', e => {
const displayedBpm = e.target.value.length === 3 ? e.target.value : ` ${e.target.value}`
document.querySelector('label[for="bpm"] > span').innerHTML = displayedBpm
Tone.Transport.bpm.value = e.target.value
})
generateStyleSheet()
drawDivs()
function generateStyleSheet (pixels) {
const styleTag = document.querySelector('style')
if (styleTag) document.head.removeChild(styleTag)
const sheet = document.createElement('style')
sheet.innerHTML = makeRule(pixels)
document.head.appendChild(sheet)
}
function makeRule (pixels = 48) {
return `
.beat {
min-width: ${pixels}px;
max-width: ${pixels}px;
min-height: ${pixels}px;
max-height: ${pixels}px;
box-sizing: border-box;
border: 1px solid rgb(174, 164, 109);
}
`
}
function handleInput (event) {
const MAX = 512
const MIN = 10
if (parseInt(pixelsInput.value) < MIN) pixelsInput.value = MIN
if (parseInt(stepsInput.value) > MAX) stepsInput.value = MAX
if (parseInt(pulsesInput.value) > MAX) pulsesInput.value = MAX
if (parseInt(stepsInput.value) < 1) stepsInput.value = 1
if (parseInt(pulsesInput.value) < 1) pulsesInput.value = 1
playSequence()
drawDivs()
}
function drawDivs () {
rhythmDisplay.innerHTML = ''
let pulses = pulsesInput.value
let steps = stepsInput.value
let pattern = generatePattern(pulses, steps)
pattern.forEach(beat => {
let div = document.createElement('div')
div.className = beat ? 'on beat' : 'off beat'
rhythmDisplay.appendChild(div)
})
}
function getSequenceFromDOM () {
const beats = document.getElementsByClassName('beat')
let sequence = [...beats].map(beat => beat.classList.contains('on') ? 1 : 0)
return sequence
}
function playSequence () {
if (currentSequence) {
currentSequence.stop()
currentSequence.dispose()
}
const [pulses, steps] = [pulsesInput.value, stepsInput.value]
generateToneSequence(generatePattern(pulses, steps))
currentSequence.start(0)
Tone.Transport.start()
}
function generateToneSequence (pattern) {
currentSequence = new Tone.Sequence(function (time, note) {
const index = parseInt(currentSequence.progress * currentSequence.length)
lightUp(index)
const kick = new Tone.MembraneSynth().toMaster()
if (note) kick.triggerAttackRelease('8n', time)
}, pattern, '8n')
}
function stopSequence () {
Tone.Transport.stop()
const beats = [...document.querySelectorAll('.beat')]
beats.forEach(beat => beat.classList.remove('active'))
}
function lightUp (index) {
const beats = document.querySelectorAll('.beat')
const rotatedLastIndex = index - 1 === -1 ? beats.length - 1 : index - 1
beats[index].classList.add('active')
beats[rotatedLastIndex].classList.remove('active')
}