UNPKG

cw

Version:

A comprehensive Morse Code (CW) library

166 lines (160 loc) 4.7 kB
'use strict'; /** * Morse Code Timing Utilities * * This module provides functions for calculating Morse code timing parameters. * * Farnsworth Timing: * Farnsworth timing is a method of sending Morse code where the individual characters * are sent at a higher speed (wpm), but the spacing between characters and words is * increased to achieve a lower effective speed (fwpm). This technique was developed by * Russ Farnsworth (W6TTB) to help students learn Morse code more effectively. * * The idea behind Farnsworth timing is that learners can become accustomed to hearing * and recognizing characters at higher speeds, while the extended spacing gives them * more time to process each character before the next one arrives. As proficiency * increases, the spacing can be gradually reduced until standard timing is achieved. * * In standard Morse timing, characters and spaces are proportional to the dit length. * In Farnsworth timing, the characters maintain the same internal timing as they would * at the higher speed, but the spaces between characters and words are extended. */ /** * Calculate the dit length in seconds based on words per minute * @param wpm Words per minute * @returns Dit length in seconds */ function tdit(wpm) { return 60 / (50 * wpm); } /** * Calculate the Farnsworth dit length in seconds * @param wpm Words per minute * @param fwpm Farnsworth words per minute (defaults to wpm if not provided) * @returns Farnsworth dit length in seconds */ function tfdit(wpm, fwpm) { // Ensure fwpm defaults to wpm when undefined or null const effectiveFwpm = fwpm === undefined ? wpm : fwpm; return (300 * wpm - 186 * effectiveFwpm) / (95 * wpm * effectiveFwpm); } const codes = { A: ".-", B: "-...", C: "-.-.", D: "-..", E: ".", F: "..-.", G: "--.", H: "....", I: "..", J: ".---", K: "-.-", L: ".-..", M: "--", N: "-.", O: "---", P: ".--.", Q: "--.-", R: ".-.", S: "...", T: "-", U: "..-", V: "...-", W: ".--", X: "-..-", Y: "-.--", Z: "--..", "1": ".----", "2": "..---", "3": "...--", "4": "....-", "5": ".....", "6": "-....", "7": "--...", "8": "---..", "9": "----.", "0": "-----", "=": "-...-", ".": ".-.-.-", ",": "--..--", "/": "-..-.", "?": "..--..", }; const DEFAULT_TONE_FREQUENCY = 700; const DEFAULT_WPM = 20; const DEFAULT_FWPM = 10; const START_DELAY = 0.5; const TRANSITION_PERIOD = 0.002; const DAH_LENGTH = 3; const INTER_WORD_SPACING = 3; const INTRA_WORD_SPACING = 4; // Actually 7, but we always add the inter-word spacing (3) so 7-3=4 /** * Initialize the Web Audio API context * @param opts Options for initializing the audio context * @returns The initialized audio context or undefined if window is not available */ function initAudioContext(opts = {}) { if (typeof window === "undefined") return; const tone = opts.tone || DEFAULT_TONE_FREQUENCY; const actx = new (window.AudioContext || window.webkitAudioContext)(); const osc = actx.createOscillator(); const gain = actx.createGain(); gain.connect(actx.destination); gain.gain.value = 0; osc.frequency.value = tone; osc.connect(gain); osc.start(); return { actx, osc, gain, }; } /** * Play a word or phrase in Morse code * @param word The word or phrase to play * @param opts Options for playback * @returns The total duration of playback in milliseconds */ function play(word, opts = {}) { const ctx = opts.actx || initAudioContext(opts); if (!ctx) return 0; const { actx, gain } = ctx; const ct = actx.currentTime; const wpm = opts.wpm || DEFAULT_WPM; const fwpm = opts.fwpm || DEFAULT_FWPM; const td = tdit(wpm); const tfd = tfdit(wpm, fwpm); let t = ct + START_DELAY; for (let i = 0; i < word.length; i++) { const w = word[i]; if (w === " ") { t += tfd * INTRA_WORD_SPACING; continue; } const c = codes[w.toUpperCase()]; if (!c) continue; // Skip characters that don't have a code for (let j = 0; j < c.length; j++) { const l = c[j] === "-" ? td * DAH_LENGTH : td; gain.gain.setTargetAtTime(1, t, TRANSITION_PERIOD); gain.gain.setTargetAtTime(0, t + l, TRANSITION_PERIOD); t += l + td; } t -= td; t += tfd * INTER_WORD_SPACING; } return t * 1000; } const cw = { tdit, tfdit, codes, initAudioContext, play }; module.exports = cw; //# sourceMappingURL=cw.cjs.js.map