UNPKG

noteplayer.js

Version:

Play, tune, and transform notes and frequencies in a snap!

120 lines (119 loc) 3.31 kB
// src/index.ts var notePlayer = class { audioCtx; gainNode; oscillator; DEFAULT_FREQUENCY = 440; DEFAULT_OSCILLATOR_TYPE = "sine"; concert_pitch = 440; // based on A4 CONCERT_PITCH_OCTAVE = 4; // based on A4 temperament = 12; noteNames = [ "A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#" ]; // Based on Chromatic scale (12-TET) only, TODO: auto detect notes based on temperament noteNameRegex = /^(A|B|C|D|E|F|G)(#?)(\d)$/; constructor() { this.audioCtx = new AudioContext(); this.gainNode = this.audioCtx.createGain(); this.oscillator = this.audioCtx.createOscillator(); this.oscillator.connect(this.gainNode); this.setOscillatorDefaultSettings(); } init() { this.oscillator.start(); } setOscillatorDefaultSettings() { this.oscillator.frequency.setValueAtTime( this.DEFAULT_FREQUENCY, this.audioCtx.currentTime ); this.oscillator.type = this.DEFAULT_OSCILLATOR_TYPE; } setOscillatorType(type) { this.oscillator.type = type; } setFrequency(frequency) { this.oscillator.frequency.setValueAtTime( frequency, this.audioCtx.currentTime ); } setGain(gain) { this.gainNode.gain.value = gain; } play(frequency) { if (frequency) { this.setFrequency(frequency); } this.gainNode.connect(this.audioCtx.destination); } stop() { this.gainNode.disconnect(this.audioCtx.destination); } setTemperament(temperament) { this.temperament = temperament; } setConcertPitch(concert_pitch) { this.concert_pitch = concert_pitch; } getFrenquencyFromSteps(steps) { const frequency = 2 ** (steps / this.temperament) * this.concert_pitch; return frequency; } getStepsFromFrequency(frequency) { const steps = this.temperament * Math.log2(frequency / this.concert_pitch); return Math.round(steps); } getNoteNameFromSteps(steps) { const octave = Math.floor(steps / this.temperament) + this.CONCERT_PITCH_OCTAVE; let noteIndex = (steps >= 0 ? steps : Math.abs(this.noteNames.length + steps)) % this.temperament; return `${this.noteNames[noteIndex]}${octave}`; } getFrequencyFromNoteName(noteFullName) { const match = noteFullName.match(this.noteNameRegex); if (!match) { throw new Error("Invalid note format"); } const [, noteLetter, sharp, octaveStr] = match; const noteName = `${noteLetter}${sharp}`; const octave = Number(octaveStr); const noteIndex = this.noteNames.findIndex((note) => note === noteName); if (noteIndex === -1) { throw new Error("Invalid note"); } const stepsFromOctave = this.temperament * octave; const stepsBase = noteIndex; const steps = this.getLowestStep() + stepsFromOctave + stepsBase; const frequency = this.getFrenquencyFromSteps(steps); return frequency; } getLowestStep() { const step = -this.temperament * this.CONCERT_PITCH_OCTAVE; return step; } getLowestFrequency() { const step = this.getLowestStep(); const frequency = this.getFrenquencyFromSteps(step); return frequency; } getLowestMetrics() { return { step: this.getLowestStep(), frequency: this.getLowestFrequency() }; } }; export { notePlayer as default };