В мире информатики азбука Морзе - одна из тех старых технологий, которые все еще остаются крутыми. Мы все смотрели фильмы о Второй мировой войне, где правительства отправляют телеграфы, используя азбуку Морзе, для передачи сообщений на огромные расстояния. Мы, наверное, также смотрели фильмы о флоте, где командиры кораблей посылают знаменитый сигнал «SOS», когда корабль попадает в беду. Эта технология была новаторской, когда была разработана, и актуальна до сих пор.

Поскольку меня это интересовало, я подумал, что было бы здорово создать код Морзе с помощью программы, если бы я мог. Я немного погуглил и обнаружил, что несколько человек использовали API веб-аудио для генерации кода Морзе с помощью Javascript. Затем я потратил последующее время на чтение (и кодирование) с таймерами Javascript и API веб-аудио, играя с различными методами отправки сообщений с помощью света (цвета) и звука.

И, в конце концов, поскольку многое из этого было довольно крутым, я решил написать об этом пост.

В оставшейся части этого поста мы расскажем о некоторых интересных способах создания кода Морзе с помощью Javascript. Я собираюсь рассмотреть реализации с использованием как API веб-аудио, так и функции setTimeout JavaScript.

Немного истории о азбуке Морзе

Код Морзе (произносится как Морс в Морсель, а не Моррис, как имя, как моя жена описывала мне) может быть определен как закодированный язык точек и тире, которые соответствуют заранее заданной таблице. персонажей. Символы включают алфавит (a-z), а также цифры (0–9) и некоторые знаки препинания. Точки и тире, составляющие азбуку Морзе, могут передаваться через все, что может генерировать различный сигнал. Это делает его действительно мощным, потому что пока и отправитель, и получатель понимают азбуку Морзе, сообщения можно отправлять на большие расстояния с помощью фонарика или радио. С изобретением телеграфа это стало еще более мощным, поскольку сообщения могли передаваться на очень большие расстояния еще в начале 1900-х годов.

Азбука Морзе также широко используется в вооруженных силах США (и в других странах) более 100 лет. Некоторые известные фильмы о войне, в которых они представлены, включают Охоту за красным октябрем и Игра в имитацию.

Я рекомендую заглянуть на Страницу азбуки Морзе в Википедии и Страницу телеграфной Википедии, чтобы узнать больше.

История веб-аудио

Теперь, когда у вас есть некоторый опыт работы с азбукой Морзе, давайте поговорим о том, как можно использовать Javascript для передачи его с помощью цветов и звуков.

По мере развития браузеров звук всегда был проблемой. Первоначальный веб-браузер имел минимальную поддержку звука с определенными элементами. Когда был разработан флэш-носитель, он представлял собой кросс-браузерное решение, но по-прежнему требовал от пользователей установки программного обеспечения. Плагины для Flash-носителей также были неоптимальными.

В HTML5 элемент ‹audio› исправил множество проблем, но имел огромные ограничения, если вы хотели создавать свои собственные звуки из веб-приложений. Элемент ‹audio› не давал вам точного контроля над звуками и манипуляциями, как это сделал бы ди-джей.

С появлением Web Audio API все предыдущие проблемы были решены, поскольку он предоставил основу для (почти) любого типа звуковой работы, о которой вы только можете подумать.

Знакомство с API веб-аудио

В следующем разделе я попытаюсь показать высокоуровневое представление API веб-аудио. Я рекомендую заглянуть на страницы здесь для получения дополнительной информации. Вы также можете проверить историю W3C API веб-аудио здесь.

Хорошо, давайте начнем с самого начала ...

API веб-аудио - это просто виртуальный способ создания звуковой системы. Это можно считать аналогичным тому, что ди-джей использует при создании «миксов».

API веб-аудио на самом деле представляет собой набор узлов, которые являются разными частями виртуальной сети. Вы создаете эту сеть для имитации звуков и подключения источника (записанного или сгенерированного звука) к месту назначения (динамикам). Вот отличная общая диаграмма, которую я скопировал со страниц MDN здесь:

Общая аудиосеть, созданная с помощью API веб-аудио, называется аудиографом. API веб-аудио называет этот звуковой график AudioContext, в котором звук может запускаться в текущем сеансе браузера. В каждом браузере есть AudioContext, и вам нужно знать, как ссылаться на него, чтобы использовать Web Audio API.

Помимо звука, вы также можете создавать визуализации звука с помощью Analyzer Node API веб-аудио.

Если все это кажется трудным для понимания, я рекомендую вам посмотреть следующее видео, в котором рассматриваются основы.

Звук Морзе

Теперь, когда вы познакомились с веб-аудио и кодом Морзе, приступим к кодированию! В процессе изучения азбуки Морзе я создал приложение, которое использует API веб-аудио для генерации звуков точка и тире, которые вы обычно слышите по телеграфу или радиопередаче. Чтобы создать приложение, я проконсультировался с сообщением в блоге здесь и связанным с ним Gist здесь, чтобы лучше понять, как все это связать. Приложение построено на Angular и размещено на Firebase.

Здесь можно поиграть с запущенной версией приложения. Вы также можете просмотреть исходный код приложения на GitHub здесь.

Поскольку приложение следует базовой структуре Angular, я не собираюсь выполнять стандартные операции «ng new» и настройку приложения. Я просто расскажу, как главный компонент работает с веб-API. Ознакомьтесь с исходным кодом, чтобы узнать, как было создано приложение, по ссылке GitHub, которую я предоставил выше.

Само приложение просто принимает текст, воспроизводит текст как код Морзе, а также выводит переведенный результат, как показано на следующем снимке экрана.

Итак, давайте рассмотрим настройку…

Прежде всего, я должен отметить, что API веб-аудио сегодня поддерживается большинством современных браузеров, но могут быть небольшие различия для конкретной реализации браузера. Код, который я здесь описываю, создан для Google Chrome.

В качестве меры безопасности Chrome не воспроизводит звук, если предварительно не сработает жест. Это имеет смысл, поскольку вы не хотите, чтобы приложения могли подключаться к аудио на ваших компьютерах без вашего согласия и т. Д. Поэтому для этого основная веб-форма моего приложения создает Аудиоконтекст с onSubmit, который привязан к основной форме, принимающей ввод. Вот код:

Здесь метод createContext создает аудиограф, о котором мы говорили. Сначала мы создаем экземпляр AudioContext, осциллятора и узла усиления со следующим:

this.audioContext = new AudioContext();
this.oscillator = this.audioContext.createOscillator();
this.gain = this.audioContext.createGain();

Затем мы устанавливаем значение для узла усиления равным «0», чтобы имитировать его как выключенное (чтобы он не воспроизводил звук). Затем мы устанавливаем частоту (высоту тона) осциллятора, подключаем осциллятор к узлу усиления и подключаем узел усиления на динамики (назначение звука) со следующим:

this.gain.gain.value = 0;
this.oscillator.frequency.value = 750;
this.oscillator.connect(this.gain);
this.gain.connect(this.audioContext.destination);

Наконец, мы устанавливаем скорость, с которой будут воспроизводиться звуки, и запускаем узел осциллятора. Это важно, потому что оно становится значением, которое мы устанавливаем для «планирования» звуков в сети.

this.dot = 1.2 / this.rate;
this.oscillator.start(0);

Результирующий звуковой график должен выглядеть примерно так, как на следующей диаграмме:

На приведенном здесь графике все узлы существуют в аудиоконтексте. Три узла, перечисленные здесь, являются лишь частью аудиограммы. Осциллятор подключается к усилению, который затем создает выходной сигнал, который отправляется на динамики (аудио назначения).

После настройки Audio Graph нам нужно заняться созданием звуков. Здесь мы собираемся «запланировать» звуки, имитируя включение и выключение узла усиления. Мы делаем это, буквально устанавливая значение усиления на «0» или «1» через определенные промежутки времени. Думайте об этом как о буквально включении и выключении громкости динамика. Мы делаем это следующим образом:

generateMorse(time: any, phrase: string) {
    phrase = phrase.toUpperCase();
    this.morseDisplay = [];
    for (const p of phrase) {
      if (p === ' ') {
        time += 3 * this.dot;
      } else if (this.MORSE[p] !== undefined) {
        time = this.createSound(time, this.MORSE[p]);
        time += 2 * this.dot;
      }

      const morseOuput = new MorseOutput();
      morseOuput.morseText = p;
      morseOuput.morseValue = this.MORSE[p];
      this.morseDisplay.push(morseOuput);
    }

    return time;
  }

Метод generateMorse использует время AudioContext со свойством audioContext.currentTime. AudioContext имеет внутренние часы, которые постоянно считают после создания. Мы собираемся использовать метод audioContext.setValueAtTime для «планирования» звуков звука с использованием этих часов. Вот почему время передается в качестве параметра методу generateMorse.

Массив MORSE - это просто набор пар ключ-значение, которые соотносят введенный символ со значением кода Морзе.

Также обратите внимание на использование значения точки для планирования времени. Это определяет частоту (темп), с которой программа будет воспроизводить «точки» и «тире».

С помощью метода generateMorse цикл for просматривает переданную ему фразу, а затем вызывает метод createSound. В этом методе происходит все волшебство.

createSound(time: any, char: string) {
    for (const c of char) {
      switch (c) {
      case '.':
        this.gain.gain.setValueAtTime(1.0, time);
        time += this.dot;
        this.gain.gain.setValueAtTime(0.0, time);
        break;
      case '-':
        this.gain.gain.setValueAtTime(1.0, time);
        time += 3 * this.dot;
        this.gain.gain.setValueAtTime(0.0, time);
        break;
      }
      time += this.dot;
    }

    return time;
  }

Этот метод вызывает метод узла усиления setValueAtTime, который «планирует» воспроизведение звуков в созданном аудиографе. Значение return важно, потому что оно заставляет цикл событий Javascript использовать генерацию звука как операцию в стеке. Если вы не включите return, тогда область действия Javascript пропустит время, и вы не услышите ни звука.

Собирая все вместе, конечный продукт выглядит так:

Проверьте работающую версию приложения или выполните git clone и поиграйте с кодом самостоятельно.

Морзе Лайт

В дополнение к звуковому приложению, которое я создал выше, я также создал второе приложение Angular, которое генерирует азбуку Морзе с помощью света. Технически приложение просто использует элемент холста HTML и перерисовывает круг с желтым и белым, чтобы имитировать включение фонарика, включенного и выкл.. Кроме того, когда отображается индикатор кода Морзе, соответствующий перевод «буква в морзе» выводится во времени справа от нарисованного индикатора.

Рабочая версия приложения доступна здесь. Вы также можете ознакомиться с исходным кодом на GitHub здесь.

Вот скриншот приложения в действии:

Как и в случае со звуковым приложением, я не собираюсь вдаваться в основы создания начальной структуры с помощью Angular CLI. Я просто собираюсь сосредоточиться на основной логике приложения и рекомендую вам просмотреть проект в GitHub, чтобы узнать больше о том, как реальный проект структурирован и т. Д.

Для базовой презентации света я использую async / await и цикл обработки событий Javascript для имитации того, что свет включен и выключен. Приложение в основном принимает фразу в качестве входных данных, а затем выполняет синхронизированные вызовы метода, который возвращает обещание. Поскольку я использую async / await, основной цикл событий Javascript вынужден ждать, пока это обещание не будет выполнено с заданным временем. Если вы не знакомы с async / await, рекомендую посмотреть мой пост здесь.

Приложение выполняет большую часть работы с помощью метода передачи и метода фонарика.

Во-первых, метод передачи вызывается кнопкой «отправить» в основной форме ввода. В этом методе используются вызовы async / await, как я первоначально объяснил, для управления синхронизацией последовательностей включения и выключения света.

async transmit() {
      // time = 1200 / words per minute
      // 20 words per minute
      // follows a 3 to 1 ratio
      // 60 milliseconds for one dot
      // 180 milliseconds for a dash
      // multiplied by factor of 4 to slow it down here
      const dot = 60 * 4;
      const dash = 180 * 4;

      this.showMorse = '';
      const messageUpper = this.message.toUpperCase();
      for (const char of messageUpper) {
        this.showMorse = this.showMorse + '(' + char + ') ';
        const morseValue = this.morseTranslation[char];
        for (const morse of morseValue) {
          this.showMorse = this.showMorse + ' ' + morse;
          if (morse === '.') {
            // dot
            await this.flashlight('yellow', dot);
            // show white light to show when flash is finished
            await this.flashlight('white', 60);
          } else {
            // dash at 3 X 60 or 180
            await this.flashlight('yellow', dash);
            // show white light to show when flash is finished
            await this.flashlight('white', 60);
          }
        }
      }
  }

Как видно из этого кода, метод async вызывает flashlight. Вызов имеет ожидание, которое блокирует основной цикл событий, заставляя программу ждать, прежде чем изменить цвет света. В зависимости от переданного значения цвета вызов фонарика также управляет цветом. Таким образом, желтый имитирует включенный свет, а белый - выключенный. Если вы передадите это со строкой кода Морзе в форме точек = ”.” и тире = ”-“, то вы сможете пройти по этой строке и смоделировать соответствующие значения on и off.

flashlight(color: String, time: any): Promise<any> {
    return new Promise(resolve => {
      setTimeout(function() {
        // this.drawLight(color);
        const c: any = document.getElementById('flashlight');
        const ctx = c.getContext('2d');
        ctx.beginPath();
        ctx.arc(c.width / 2, c.height - 50, 50, 0, 2 * Math.PI);
        ctx.fillStyle = color;
        ctx.fill();
        ctx.stroke();
        resolve(true);
      }, time);
    });
  }

Метод фонарик просто окрашивает элемент холст в течение определенного периода времени. Настоящая магия заключается в том, что async / await блокирует основной поток, вызывая ожидание, прежде чем рисование / перерисовка холста изменит цвет индикаторов.

Собирая все вместе, компонент Morse Light выглядит следующим образом:

Заключение

Лично все это было интересным способом узнать азбуку Морзе. Я надеюсь, что созданные мной ссылки и приложения помогут вам понять некоторые основы веб-аудио, а также время событий в Javascript.

Первоначально опубликовано на https://rhythmandbinary.com 19 января 2019 г.