import { Note, toCents } from './scale.mjs'; import GuitarFuzz from './vend/guitar-fuzz.mjs'; import Trombone from './vend/trombone.mjs'; const audioCtx = new AudioContext(); const fuzzWave = new PeriodicWave(audioCtx, { real: GuitarFuzz.real, imag: GuitarFuzz.imag }); const tromboneWave = new PeriodicWave(audioCtx, { real: Trombone.real, imag: Trombone.imag }); export default class extends HTMLElement { static name = 'x-player' static register() { console.debug('Registering Element', this.name, this); customElements.define(this.name, this); } // A4 aFreq = 440; // oscillatornodes notes = []; getOffsets = () => []; constructor() { super(); console.debug('Player#constructor', this); } connectedCallback() { this.globalGain = audioCtx.createGain(); this.globalGain.connect(audioCtx.destination); this.globalGain.gain.setValueAtTime(this.volume, audioCtx.currentTime); this.querySelector('.volume').onchange = e => { console.debug('vol changed', e, this.volume); this.globalGain.gain.setValueAtTime(this.volume, audioCtx.currentTime); } } get volume() { console.debug('Player#volume', this.querySelector('.volume').value) return Number(this.querySelector('.volume').value); } set volume(volume) { console.debug('Player#volume()', this, volume); this.querySelector('.volume').value = volume; } get isPlaying() { return this.classList.contains('playing'); } // offsets is an array of offsets in cents from A4. get offsets() { console.debug('offsets getoffsets', this.getOffsets); return this.getOffsets(); } set isPlaying(isPlaying) { console.debug('Player#isPlaying', this, isPlaying); if (isPlaying) { this.classList.add('playing'); this.start(); } else { this.classList.remove('playing'); this.stop(); } } start() { console.debug('Player#start', this); for (let i = 0; i < this.offsets.length; i++) { if (!this.notes[i]) { const note = new OscillatorNode(audioCtx, { frequency: this.aFreq, type: 'custom', periodicWave: tromboneWave, }); note.connect(this.globalGain); note.start(); this.notes[i] = note; } const note = this.notes[i]; note.detune.setValueAtTime(this.offsets[i], audioCtx.currentTime); } for (let j = this.notes.length-1; j >= this.offsets.length; j--) { this.notes[j].stop(); delete this.notes[j]; } } stop() { console.debug('Player#stop', this); this.notes.forEach(n => n.stop()); this.notes = []; } }