diff --git a/README.md b/README.md index 5501289..c983ddf 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![npm-version]][npm] [![npm-downloads]][npm] [![travis-ci]][travis] Morse code encoder and decoder with no dependencies supports Latin, Cyrillic, Greek, Hebrew, -Arabic, Persian, Japanese, and Korean characters with audio generation functionality. +Arabic, Persian, Japanese, and Korean characters with audio generation functionality using the [Web Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API). ## Installation @@ -24,9 +24,11 @@ $ yarn add morsify ```js var morsify = require('morsify'); -morsify.encode('SOS'); // .../---/... -morsify.decode('.../---/...'); // S O S -morsify.audio('SOS'); // will return base64 encoded audio/wav data +var encoded = morsify.encode('SOS'); // .../---/... +var decoded = morsify.decode('.../---/...'); // S O S +var audio = morsify.audio('SOS'), oscillator = audio.oscillator; // OscillatorNode +audio.play(); // play audio +audio.stop(); // stop audio ``` Or alternatively, you can also use the library directly with including the source file. @@ -34,9 +36,11 @@ Or alternatively, you can also use the library directly with including the sourc ```html ``` @@ -52,7 +56,7 @@ Set the priority option according to the list below. - 1 => ASCII (Default) - 2 => Numbers - 3 => Punctuation -- 4 => Latin Extended (Turkish, Polski etc.) +- 4 => Latin Extended (Turkish, Polish etc.) - 5 => Cyrillic - 6 => Greek - 7 => Hebrew @@ -62,18 +66,21 @@ Set the priority option according to the list below. - 11 => Korean ```js -morsify.encode('Ленинград', { priority: 5 }) // .-.././-./../-./--./.-./.-/-.. -morsify.decode('.../.-/--./.-/.--./.--', { priority: 6 }) // Σ Α Γ Α Π Ω -morsify.decode('––– –... ––– –. ––. .. .–.. –––', { dash: '–', dot: '.', space: ' ', priority: 7 }) // ה ב ה נ ג י ל ה -morsify.audio('البُراق‎‎', { // generates the morse .-/.-../-.../.-./.-/--.- then generates the audio from it - channels: 1, - sampleRate: 1012, - bitDepth: 16, - unit: 0.1, - frequency: 440.0, - volume: 32767, - priority: 8 -}) +var cyrillic = morsify.encode('Ленинград', { priority: 5 }) // .-.././-./../-./--./.-./.-/-.. +var greek = morsify.decode('.../.-/--./.-/.--./.--', { priority: 6 }) // Σ Α Γ Α Π Ω +var hebrew = morsify.decode('––– –... ––– –. ––. .. .–.. –––', { dash: '–', dot: '.', space: ' ', priority: 7 }) // ה ב ה נ ג י ל ה +var arabicAudio = morsify.audio('البُراق‎‎', { // generates the morse .-/.-../-.../.-./.-/--.- then generates the audio from it + unit: 0.1, // period of one unit, in seconds, 1.2 / c where c is speed of transmission, in words per minute + oscillator: { + type: 'sine', // sine, square, sawtooth, triangle + frequency: 500, // value in hertz + onended: function () { // event that fires when the tone has stopped playing + console.log('ended'); + }, + } +}), oscillator = arabicAudio.oscillator; // OscillatorNode +arabicAudio.play(); // will start playing morse audio +arabicAudio.stop(); // will stop playing morse audio ``` ## Contributing and Known Issues @@ -84,8 +91,6 @@ Currently, as a major drawback, Chinese characters are missing. Someone with the [Chinese telegraph code](https://en.wikipedia.org/wiki/Chinese_telegraph_code) can help to implement it. Also someone who is proficient in Thai can help to include [Thai alphabet](https://th.wikipedia.org/wiki/รหัสมอร์ส). -On the other hand, audio generated by the script is playable in Firefox, however, Chrome support is currently missing. - ## License The MIT License (MIT). Please see [License File](LICENSE) for more information. diff --git a/index.js b/index.js index f31ceae..2c41292 100644 --- a/index.js +++ b/index.js @@ -36,7 +36,7 @@ 'Ș': '1111', 'Š': '1111', 'Ŝ': '00010', 'ß': '000000', 'Þ': '01100', 'Ü': '0011', 'Ù': '0011', 'Ŭ': '0011', 'Ž': '11001', 'Ź': '110010', 'Ż': '11001' }, - '5': { // Cyrilic Alphabet => https://en.wikipedia.org/wiki/Russian_Morse_code + '5': { // Cyrillic Alphabet => https://en.wikipedia.org/wiki/Russian_Morse_code 'А': '01', 'Б': '1000', 'В': '011', 'Г': '110', 'Д': '100', 'Е': '0', 'Ж': '0001', 'З': '1100', 'И': '00', 'Й': '0111', 'К': '101','Л': '0100', 'М': '11', 'Н': '10', 'О': '111', 'П': '0110', 'Р': '010', 'С': '000', @@ -105,18 +105,19 @@ var getOptions = function (options) { options = options || {}; + options.oscillator = options.oscillator || {}; options = { dash: options.dash || '-', dot: options.dot || '.', space: options.space || '/', invalid: options.invalid || '#', priority: options.priority || 1, - channels: options.channels || 1, - sampleRate: options.sampleRate || 1012, - bitDepth: options.bitDepth || 16, - unit: options.unit || 0.1, - frequency: options.frequency || 440.0, - volume: options.volume || 32767 + unit: options.unit || 0.08, // period of one unit, in seconds, 1.2 / c where c is speed of transmission, in words per minute + oscillator: { + type: options.oscillator.type || 'sine', // sine, square, sawtooth, triangle + frequency: options.oscillator.frequency || 500, // value in hertz + onended: options.oscillator.onended || null, // event that fires when the tone has stopped playing + } }; characters[0] = characters[options.priority]; return options; @@ -141,29 +142,24 @@ }).join(' ').replace(/\s+/g, ' '); }; - // Source: https://github.com/mattt/Morse.js var audio = function (text, opts) { - var options = getOptions(opts), morse = encode(text, opts), data = [], samples = 0, - pack = function (e) { - for (var b = '', c = 1, d = 0; d < e.length; d++) { - var f = e.charAt(d), a = arguments[c++]; - b += f === 'v' ? String.fromCharCode(a & 255, a >> 8 & 255) : String.fromCharCode(a & 255, a >> 8 & 255, a >> 16 & 255, a >> 24 & 255); - } - return b; - }, tone = function (length) { - for (var i = 0; i < options.sampleRate * options.unit * length; i++) { - for (var c = 0; c < options.channels; c++) { - var v = options.volume * Math.sin((2 * Math.PI) * (i / options.sampleRate) * options.frequency); - data.push(pack('v', v)); samples++; - } - } - }, silence = function (length) { - for (var i = 0; i < options.sampleRate * options.unit * length; i++) { - for (var c = 0; c < options.channels; c++) { - data.push(pack('v', 0)); samples++; - } - } - }; + var options = getOptions(opts), morse = encode(text, opts), + AudioContext = window.AudioContext || window.webkitAudioContext, ctx = new AudioContext(), + t = ctx.currentTime, oscillator = ctx.createOscillator(), gainNode = ctx.createGain(); + + oscillator.type = options.oscillator.type; + oscillator.frequency.value = options.oscillator.frequency; + oscillator.onended = options.oscillator.onended; + + gainNode.gain.setValueAtTime(0, t); + + var tone = function (i) { + gainNode.gain.setValueAtTime(1, t); + t += i * options.unit; + }, silence = function (i) { + gainNode.gain.setValueAtTime(0, t); + t += i * options.unit; + }; for (var i = 0; i <= morse.length; i++) { if (morse[i] === options.space) { @@ -179,34 +175,19 @@ } } - var chunk1 = [ - 'fmt ', - pack('V', 16), - pack('v', 1), - pack('v', options.channels), - pack('V', options.sampleRate), - pack('V', options.sampleRate * options.channels * options.bitDepth / 8), - pack('v', options.channels * options.bitDepth / 8), - pack('v', options.bitDepth) - ].join(''), - chunk2 = [ - 'data', - pack('V', samples * options.channels * options.bitDepth / 8), - data.join('') - ].join(''), - header = [ - 'RIFF', - pack('V', 4 + (8 + chunk1.length) + (8 + chunk2.length)), - 'WAVE' - ].join(''); + oscillator.connect(gainNode); + gainNode.connect(ctx.destination); - if (typeof btoa === 'undefined') { - global.btoa = function (str) { - return new Buffer(str).toString('base64'); - }; - } - - return 'data:audio/wav;base64,' + encodeURI(btoa([header, chunk1, chunk2].join(''))); + return { + play: function () { + oscillator.start(); + oscillator.stop(t); + }, + stop: function () { + oscillator.stop(); + }, + oscillator: oscillator + }; }; return { diff --git a/package.json b/package.json index 270702e..846a283 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "morsify", - "version": "0.1.0", + "version": "0.2.0", "description": "Encodes and decodes morse code, creates morse code audio from text.", "keywords": [ "morse", diff --git a/test/index.js b/test/index.js index 92be527..dd747be 100644 --- a/test/index.js +++ b/test/index.js @@ -158,8 +158,4 @@ describe('morsify', function () { t.equal(morsify.decode('---/.---/./../-/...', options), 'ㅍ ㅎ ㅏ ㅑ ㅓ ㅕ'); t.equal(morsify.decode('.-/-./..../.-./-../..-', options), 'ㅗ ㅛ ㅜ ㅠ ㅡ ㅣ'); }); - it('creates audio', function () { - t.equal('data:audio/wav;base64', morsify.audio('SOS').substr(0, 21)); - t.equal('UklGRsK8IAAAV0FWRWZtdCAQAAAAAQABAMO0AwAAw6gHAAACABAAZGF0YcKIIAAAAADDvjJ1wqLCm3hOwoDCpXE6wq/CiCJtEX/CvcKRaMKvwoJRfW/Cl8KBQsKTw654w53DhlBbwo7Csn9lwofCi10Cw40AAMO+MnXCosKbeE7CgMKlcTrCr8KIIm0Rf8K9wpFowq/CglF9b8KXwoFCwpPDrnjDncOGUFvCjsKyf2XCh8KLXQLDjQAAw74ydcKiwpt4TsKAwqVxOsKvwogibRF/wr3CkWjCr8KCUX1vwpfCgULCk8OueMOdw4ZQW8KOwrJ/ZcKHwotdAsONAADDvjJ1wqLCm3hOwoDCpXE6wq/CiCJtEX/CvcKRaMKvwoJRfW/Cl8KBQsKTw654w53DhlBbwo7Csn9lwofCi10Cw40AAMO+MnXCosKbeE7CgMKlcTrCr8KIIm0Rf8K', morsify.audio('SOS').substr(22, 463)); - }); });