UNPKG

beepody

Version:
257 lines (238 loc) 5.66 kB
import { DEFAULT_LENGTH, DEFAULT_FREQUENCY, NOTE_DELIMITER, PARAMETER_DELIMITER, } from './config' import Tone from './Tone' /** * @class Beep utility * @name Beep */ export class Beep { frequency: number length: number repeats: number /** * Initialize a beep. */ constructor( frequency: number = DEFAULT_FREQUENCY, length: number = DEFAULT_LENGTH, repeats = 1, ) { this.frequency = frequency this.length = length this.repeats = repeats } /** * The text representation. */ toString(): string { return `Beep(${this.frequency} ${this.length} ${this.repeats})` } } /** * @class Beep sequence * @name BeepSequence */ export class BeepSequence { beeps: Beep[] tempo: number /** * Initialize a beep sequence. */ constructor(beeps: Beep[]) { this.beeps = beeps this.tempo = 600 } /** * Return the URL hash for the sequence. * Each note is "frequency (Hz), length (ms), repeats" separated by "|", with defaults (440 200 1). * Notes are separated by ",". */ toHash(): string { const notes: string[] = [] for (const beep of this.beeps) { notes.push(`${beep.frequency}${PARAMETER_DELIMITER}${beep.length}`) } return notes.join(NOTE_DELIMITER) } /** * Return the `beep` command. */ toBeepCommand(): string { const notes: string[] = [] for (const beep of this.beeps) { let s = `-f ${beep.frequency} -l ${beep.length}` if (beep.repeats !== 1) { s += ` -r ${beep.repeats}` } notes.push(s) } return `beep ${notes.join(' -n ')}` } /** * Return the GRUB init tune. */ toGRUBInitTune(): string { const notes: string[] = [] let s = `play ${this.tempo}` for (const beep of this.beeps) { notes.push(`${beep.frequency} ${beep.length / 100}`) } if (notes.length) s += ` ${notes.join(' ')}` return s } /** * The text representation. */ toString(): string { return `${this.constructor.name}(${this.toHash()})` } /** * The length of the playtime in seconds. */ lengthInSeconds(): number { let s = 0 for (const beep of this.beeps) { for (let r = -1; r < beep.repeats; r++) { s += beep.length } } return s * 0.001 } } /** * Play a beep sequence to the browser audio. */ /* istanbul ignore next */ export const playBeepSequence = (bs: BeepSequence): void => { if (typeof window === 'undefined') return let wait = 0 const tone = new Tone() for (const beep of bs.beeps) { const seconds = beep.length * 0.001 tone.beepOnBeepOff(beep.frequency, seconds, wait) wait += seconds } } /** * Play the default beep. */ export const playDefaultBeep = (): void => { playBeepSequence(new BeepSequence([new Beep()])) } const BEEP_OPTIONS = [ ['f', 'frequency', 'FREQ'], ['l', 'length', 'LEN'], ['r', 'repeats', 'REPEATS'], ['d', 'delay', 'DELAY'], ] const BEEP_COMMANDS = [['n', 'new', 'NEW']] /** * Parse a Linux "beep" command. */ export const parseBeepCommand = (s: string): BeepSequence => { const sequence = new BeepSequence([]) const args = s.split(/\s+/) console.assert(args.shift() === 'beep') let beep = new Beep() const processOption = (name: string, value: string): void => { switch (name) { case 'frequency': beep.frequency = parseFloat(value) break case 'length': beep.length = parseFloat(value) break case 'repeats': beep.repeats = parseInt(value, 10) break } } const processCommand = (name: string): void => { switch (name) { case 'new': sequence.beeps.push(beep) beep = new Beep() break } } let option for (const arg of args) { if (arg[0] === '-') { if (arg[1] === '-') { // # eg: --frequency const name = arg.substring(2) for (const opt of BEEP_OPTIONS) { if (opt[1] === name) { option = opt[1] } } for (const opt of BEEP_COMMANDS) { if (opt[1] === name) { processCommand(opt[1]) } } } else { // # eg: -f const letter = arg[1] for (const opt of BEEP_OPTIONS) { if (opt[0] === letter) { option = opt[1] } } for (const opt of BEEP_COMMANDS) { if (opt[0] === letter && typeof opt[1] === 'string') { processCommand(opt[1]) } } } } else { if (option) { processOption(option, arg) } } } sequence.beeps.push(beep) return sequence } /** * Parse a Grub init tune "play" line. */ export const parseGRUBInitTune = (s: string): BeepSequence => { const sequence = new BeepSequence([]) const args = s.split(/\s+/) console.assert(args.shift() === 'play') sequence.tempo = parseFloat(args.shift() || '60') let pitch for (const arg of args) { if (pitch) { const duration = parseFloat(arg) * 100 const beep = new Beep() beep.frequency = pitch beep.length = duration sequence.beeps.push(beep) pitch = null } else { pitch = parseFloat(arg) } } return sequence } /** * Parse a beep sequence hash. */ export const parseBeepHash = (s: string): BeepSequence => { const sequence = new BeepSequence([]) for (const note of s.split(NOTE_DELIMITER)) { const params = note .split(PARAMETER_DELIMITER) .map((s: string) => parseFloat(s)) const beep = new Beep(...params) sequence.beeps.push(beep) } return sequence } export default Beep