beepody
Version:
Beep melody utilities.
317 lines (308 loc) • 8.1 kB
JavaScript
/*!
* beepody v0.3.33333333
* (c) Dylan Ferris
* Released under the GPL-3.0 License.
*/
;
Object.defineProperty(exports, '__esModule', { value: true });
var version = '0.3.33333333';
/**
* Default beep frequency in hertz.
*/
const DEFAULT_FREQUENCY = 440;
/**
* Default beep length in milliseconds.
*/
const DEFAULT_LENGTH = 200;
/**
* Character to separate notes in beep sequence URL hash.
*/
const NOTE_DELIMITER = '|';
/**
* Character to separate parameters in notes in beep sequence URL hash.
*/
const PARAMETER_DELIMITER = '^';
/**
* @class Tone player
* @name Tone
*/
class Tone {
ctx;
gainNode;
outNode;
/**
* Initialize a tone beeper.
*/
/* istanbul ignore next */
constructor() {
if (typeof window !== 'undefined' &&
typeof window.AudioContext !== 'undefined') {
this.ctx = new window.AudioContext();
this.gainNode = this.ctx.createGain();
this.gainNode.gain.value = 1 / 8;
this.outNode = this.ctx.destination;
this.gainNode.connect(this.outNode);
}
else {
throw 'AudioContext is required';
}
}
/**
* Play a beep after wait seconds, stopping after length seconds.
*/
/* istanbul ignore next */
beepOnBeepOff(frequency, length, wait) {
const osc = this.ctx.createOscillator();
osc.type = 'square';
osc.frequency.value = frequency;
osc.connect(this.gainNode);
osc.start(wait);
osc.stop(wait + length);
}
}
/**
* @class Beep utility
* @name Beep
*/
class Beep {
frequency;
length;
repeats;
/**
* Initialize a beep.
*/
constructor(frequency = DEFAULT_FREQUENCY, length = DEFAULT_LENGTH, repeats = 1) {
this.frequency = frequency;
this.length = length;
this.repeats = repeats;
}
/**
* The text representation.
*/
toString() {
return `Beep(${this.frequency} ${this.length} ${this.repeats})`;
}
}
/**
* @class Beep sequence
* @name BeepSequence
*/
class BeepSequence {
beeps;
tempo;
/**
* Initialize a beep sequence.
*/
constructor(beeps) {
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() {
const notes = [];
for (const beep of this.beeps) {
notes.push(`${beep.frequency}${PARAMETER_DELIMITER}${beep.length}`);
}
return notes.join(NOTE_DELIMITER);
}
/**
* Return the `beep` command.
*/
toBeepCommand() {
const notes = [];
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() {
const notes = [];
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() {
return `${this.constructor.name}(${this.toHash()})`;
}
/**
* The length of the playtime in seconds.
*/
lengthInSeconds() {
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 */
const playBeepSequence = (bs) => {
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.
*/
const playDefaultBeep = () => {
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.
*/
const parseBeepCommand = (s) => {
const sequence = new BeepSequence([]);
const args = s.split(/\s+/);
console.assert(args.shift() === 'beep');
let beep = new Beep();
const processOption = (name, value) => {
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) => {
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.
*/
const parseGRUBInitTune = (s) => {
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.
*/
const parseBeepHash = (s) => {
const sequence = new BeepSequence([]);
for (const note of s.split(NOTE_DELIMITER)) {
const params = note
.split(PARAMETER_DELIMITER)
.map((s) => parseFloat(s));
const beep = new Beep(...params);
sequence.beeps.push(beep);
}
return sequence;
};
class beepody {
static get version() {
return version;
}
}
exports.Beep = Beep;
exports.BeepSequence = BeepSequence;
exports["default"] = beepody;
exports.parseBeepCommand = parseBeepCommand;
exports.parseBeepHash = parseBeepHash;
exports.parseGRUBInitTune = parseGRUBInitTune;
exports.playBeepSequence = playBeepSequence;
exports.playDefaultBeep = playDefaultBeep;
exports.version = version;
//# sourceMappingURL=index.cjs.map