Shepard Piano βΎοΈπΉ (original) (raw)
``
window.CP = window.CP || {
shouldStopExecution:a=>0, exitedLoop:()=>0, }
const svgNS = "http://www.w3.org/2000/svg" const svg = document.createElementNS(svgNS, "svg") const group = document.createElementNS(svgNS, "g") const input = {} const value = {} let pointerdown = false Array.from(document.querySelectorAll(".ctrl input")).forEach(inp => { let val const name = inp.name input[name] = inp inp.addEventListener('input', change) inp.addEventListener('change', () => inp.value = val) change()
function change() { val = Math.min(+inp.max, Math.max(+inp.value, +inp.min)) const step = +inp.step || 1 val = Math.round(val / step) * step value[name] = val } }) const diam1 = 150 const diam2 = 215 const diam3 = 300 const whites = [0, null, 1, null, 2, 3, null, 4, null, 5, null, 6] const keyNumber = new WeakMap() let octaveCount = 3 let octaveLoop = 3
input.loop.addEventListener('input', function() { value.repeat = input.repeat.value = 1 }) group.classList.add("keyboard")
function angle(turn) { const rad = turn * Math.PI * 2 const cos = Math.cos(rad) const sin = Math.sin(rad) return { rad, cos, sin } }
function sharpPos(note) { if (note <= 5) return note * 3 / 7 / 5 else return 3 / 7 + (note - 5) * 4 / 7 / 7 }
function draw() {
octaveLoop = value.loop
octaveCount = octaveLoop * input.repeat.value
group.replaceChildren()
for (let octave = 0; octave < octaveCount; octave++) {
for (let i = 0; i < 12; i++) {
const white = whites[i]
const a1 = angle((octave + sharpPos(i)) / octaveCount)
const a2 = angle((octave + sharpPos(i + 1)) / octaveCount)
const elm = document.createElementNS(svgNS, "path")
if (i === 0) {
elm.classList.add('ut')
if (octave % octaveLoop === 0) {
elm.classList.add('first-ut')
}
}
let path = \ M <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mn>1.</mn><mi>c</mi><mi>o</mi><mi>s</mi><mo>β</mo><mi>d</mi><mi>i</mi><mi>a</mi><mi>m</mi><mn>2</mn></mrow><annotation encoding="application/x-tex">{a1.cos * diam2} </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="mord">1.</span><span class="mord mathnormal">cos</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">β</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">iam</span><span class="mord">2</span></span></span></span></span>{a1.sin * diam2}\ L <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mn>1.</mn><mi>c</mi><mi>o</mi><mi>s</mi><mo>β</mo><mi>d</mi><mi>i</mi><mi>a</mi><mi>m</mi><mn>3</mn></mrow><annotation encoding="application/x-tex">{a1.cos * diam3} </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="mord">1.</span><span class="mord mathnormal">cos</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">β</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">iam</span><span class="mord">3</span></span></span></span></span>{a1.sin * diam3}\ A <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><mi>i</mi><mi>a</mi><mi>m</mi><mn>3</mn></mrow><annotation encoding="application/x-tex">{diam3} </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="mord mathnormal">iam</span><span class="mord">3</span></span></span></span></span>{diam3} 0 0 1 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi>a</mi><mn>2.</mn><mi>c</mi><mi>o</mi><mi>s</mi><mo>β</mo><mi>d</mi><mi>i</mi><mi>a</mi><mi>m</mi><mn>3</mn></mrow><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">{a2.cos * diam3},</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="mord">2.</span><span class="mord mathnormal">cos</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">β</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">iam</span><span class="mord">3</span></span><span class="mpunct">,</span></span></span></span>{a2.sin * diam3}\ L <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi>a</mi><mn>2.</mn><mi>c</mi><mi>o</mi><mi>s</mi><mo>β</mo><mi>d</mi><mi>i</mi><mi>a</mi><mi>m</mi><mn>2</mn></mrow><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">{a2.cos * diam2},</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="mord">2.</span><span class="mord mathnormal">cos</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">β</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">iam</span><span class="mord">2</span></span><span class="mpunct">,</span></span></span></span>{a2.sin * diam2}
if (white == null) {
path += A <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><mi>i</mi><mi>a</mi><mi>m</mi><mn>2</mn></mrow><annotation encoding="application/x-tex">{diam2} </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="mord mathnormal">iam</span><span class="mord">2</span></span></span></span></span>{diam2} 0 0 0 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mn>1.</mn><mi>c</mi><mi>o</mi><mi>s</mi><mo>β</mo><mi>d</mi><mi>i</mi><mi>a</mi><mi>m</mi><mn>2</mn></mrow><annotation encoding="application/x-tex">{a1.cos * diam2} </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="mord">1.</span><span class="mord mathnormal">cos</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">β</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">iam</span><span class="mord">2</span></span></span></span></span>{a1.sin * diam2}
elm.classList.add("sharp")
} else {
const a0 = angle((octave + (white / 7)) / octaveCount)
const a3 = angle((octave + ((white + 1) / 7)) / octaveCount)
path += \ A <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><mi>i</mi><mi>a</mi><mi>m</mi><mn>2</mn></mrow><annotation encoding="application/x-tex">{diam2} </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="mord mathnormal">iam</span><span class="mord">2</span></span></span></span></span>{diam2} 0 0 1 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi>a</mi><mn>3.</mn><mi>c</mi><mi>o</mi><mi>s</mi><mo>β</mo><mi>d</mi><mi>i</mi><mi>a</mi><mi>m</mi><mn>2</mn></mrow><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">{a3.cos * diam2},</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="mord">3.</span><span class="mord mathnormal">cos</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">β</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">iam</span><span class="mord">2</span></span><span class="mpunct">,</span></span></span></span>{a3.sin * diam2}\ L <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi>a</mi><mn>3.</mn><mi>c</mi><mi>o</mi><mi>s</mi><mo>β</mo><mi>d</mi><mi>i</mi><mi>a</mi><mi>m</mi><mn>1</mn></mrow><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">{a3.cos * diam1},</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="mord">3.</span><span class="mord mathnormal">cos</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">β</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">iam</span><span class="mord">1</span></span><span class="mpunct">,</span></span></span></span>{a3.sin * diam1}\ A <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><mi>i</mi><mi>a</mi><mi>m</mi><mn>1</mn></mrow><annotation encoding="application/x-tex">{diam1} </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="mord mathnormal">iam</span><span class="mord">1</span></span></span></span></span>{diam1} 0 0 0 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi>a</mi><mn>0.</mn><mi>c</mi><mi>o</mi><mi>s</mi><mo>β</mo><mi>d</mi><mi>i</mi><mi>a</mi><mi>m</mi><mn>1</mn></mrow><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">{a0.cos * diam1},</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="mord">0.</span><span class="mord mathnormal">cos</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">β</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">iam</span><span class="mord">1</span></span><span class="mpunct">,</span></span></span></span>{a0.sin * diam1}\ L <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi>a</mi><mn>0.</mn><mi>c</mi><mi>o</mi><mi>s</mi><mo>β</mo><mi>d</mi><mi>i</mi><mi>a</mi><mi>m</mi><mn>2</mn></mrow><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">{a0.cos * diam2},</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="mord">0.</span><span class="mord mathnormal">cos</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">β</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">iam</span><span class="mord">2</span></span><span class="mpunct">,</span></span></span></span>{a0.sin * diam2}\ A <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><mi>i</mi><mi>a</mi><mi>m</mi><mn>2</mn></mrow><annotation encoding="application/x-tex">{diam2} </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="mord mathnormal">iam</span><span class="mord">2</span></span></span></span></span>{diam2} 0 0 1 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi>a</mi><mn>1.</mn><mi>c</mi><mi>o</mi><mi>s</mi><mo>β</mo><mi>d</mi><mi>i</mi><mi>a</mi><mi>m</mi><mn>2</mn></mrow><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">{a1.cos * diam2},</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="mord">1.</span><span class="mord mathnormal">cos</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">β</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">iam</span><span class="mord">2</span></span><span class="mpunct">,</span></span></span></span>{a1.sin * diam2}
}
elm.setAttribute("d", path)
group.appendChild(elm)
keyNumber.set(elm, (i + octave * 12) % (12 * octaveLoop))
}
}
svg.appendChild(group)
document.body.appendChild(svg)
}
draw()
input.loop.addEventListener("input", draw)
input.repeat.addEventListener("input", draw)
function resize() {
const vw = window.innerWidth
const vh = window.innerHeight
const min = Math.min(vw, vh)
const scale = min / diam3 / 2
const transform = translate(${vw / 2}, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi>v</mi><mi>h</mi><mi mathvariant="normal">/</mi><mn>2</mn></mrow><mo stretchy="false">)</mo><mi>s</mi><mi>c</mi><mi>a</mi><mi>l</mi><mi>e</mi><mo stretchy="false">(</mo></mrow><annotation encoding="application/x-tex">{vh / 2}) scale(</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="mord mathnormal">h</span><span class="mord">/2</span></span><span class="mclose">)</span><span class="mord mathnormal">sc</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">e</span><span class="mopen">(</span></span></span></span>{scale})
group.setAttribute('transform', transform)
}
resize()
window.addEventListener("resize", resize)
group.addEventListener('pointerdown', e => {
ac.resume()
pointerdown = true
play(keyNumber.get(e.target))
})
window.addEventListener('pointerup', () => {
pointerdown = false
stop()
})
group.addEventListener('pointerover', e => pointerdown && play(keyNumber.get(e.target)))
group.addEventListener('pointerleave', stop)
const ac = new AudioContext() const volume = ac.createGain() let wave let oldNote = null volume.gain.value = 0 volume.connect(ac.destination) const osc = ac.createOscillator()
function createWave() { const max = 13 const real = new Float32Array(Math.pow(2, max)) for (let i = 0; i < max; i += octaveLoop) { real[Math.pow(2, i)] = 1 real[Math.pow(2, i) * 3] = value.fifth real[Math.pow(2, i) * 5] = value.third real[Math.pow(2, i) * 7] = value.seventh } wave = ac.createPeriodicWave(real, real) osc.setPeriodicWave(wave) } input.loop.addEventListener("input", createWave) input.fifth.addEventListener("input", createWave) input.third.addEventListener("input", createWave) input.seventh.addEventListener("input", createWave) createWave() osc.connect(volume) osc.start()
function play(note) { volume.gain.value = .3 if (oldNote === null || value.slide === 0) { const freq = 440 * (Math.pow(2, (note - 9) / 12)) / 32 osc.frequency.setValueAtTime(freq, ac.currentTime) oldNote = note return } const note2 = note if (oldNote !== null) { while (note - oldNote > 6) note -= 12 while (note - oldNote < -6) note += 12 } const oldFreq = 440 * (Math.pow(2, (oldNote - 9) / 12)) / 32 const freq = 440 * (Math.pow(2, (note - 9) / 12)) / 32 osc.frequency.cancelScheduledValues(ac.currentTime) osc.frequency.setValueAtTime(oldFreq, ac.currentTime) osc.frequency.linearRampToValueAtTime(freq, ac.currentTime + value.slide) oldNote = note2 }
function stop() { volume.gain.value = 0 oldNote = null } ``
!