Последнее время я занимаюсь изучением азбуки Морзе с помощью данной программы. Но она рассчитана на изучение кодов кириллических букв, что является неактуальным в современной радиосвязи (все используют латинский алфавит, кроме нашей доблестной армии).
Такая ситуация меня не устроила, и было принято решение написать программу для генерации звукового кода Морзе из некоторого текста с настройкой скорости и возможностью добавления кодов динамически. Решение получилось достаточно оригинальным и гибким (ИМХО, конечно же). И я решил поделиться программой с общественностью: возможно, она будет кому-то полезна или покажется интересной.
В качестве инструмента реализации идеи был выбран С++ в связке с Qt.
Основная идея программы
Атомом (единицей времени) кода Морзе является точка, относительно нее формируется длительность всех остальных элементов:
- Тире равняется трем звучащим точкам;
- Пауза между элементами одного символа (знака) — одна незвучащая точка;
- Между знаками — три точки;
- Между словами — семь точек.
Как видите, любой код, основанный на азбуке морзе, можно представить в виде набора звучащих и незвучащих точек: от этой идеи я и отталкивался, причем такое решение мне показалось достаточно оригинальным.
Первоначальный вариант реализации
В первой версии программы комбинация звучащих и незвучащих точек хранилась в виде вектора с булевыми элементами, где true соответствовал включению звука, а false — выключению.
Как вы уже поняли, для получения конечного сигнала я всего лишь «дергал» звук с некоторой задержкой (с помощью таймера, равного длительности точки в миллисекундах) при бесконечно воспроизводящемся .wav файле с записью синуса. Но данный подход имел значительный минус и заключался он в том, что приходилось каждую точку загружать отдельно с помощью перегруженного оператора или специального метода. Из-за такого подхода пришлось писать отдельный макрос для каждой буквы (вроде такого — #define I DOT << false << DOT) и создать огромный жуткий switch для воспроизведения переданной строки. Это было ужасно, но если вам любопытно, то вы можете ознакомиться
с первой версией программы тут (у меня не получилось полностью загрузить на GitHub локальный репозиторий — только последнюю версию).
bool Morse::StringToMorse (QString &line) { line += '\0'; for (int i = 0; i < line.size () - 1; ++i) { switch (line.at(i).unicode ()) { case 'A': *this << A; if (line.at (i + 1) == ' ') continue; else *this << MINI_SPACE; break; case 'B': *this << B; if (line.at (i + 1) == ' ') continue; else *this << MINI_SPACE; break; // И так далее
void Morse::PlayLinePoints () { QTimer::singleShot (duration_point_, this, SLOT ( Mute () )); sound_.play (); } void Morse::Mute () { if (line_points_.empty ()) { //Останавливаем воспроизведение sound_.stop (); return; } if (line_points_.at (0)) { //Включаем звук sound_.setMuted (false); line_points_.remove (0); QTimer::singleShot (duration_point_, this, SLOT ( Mute () )); return; } else { sound_.setMuted (true); //Выключаем звук line_points_.remove (0); QTimer::singleShot (duration_point_, this, SLOT ( Mute () )); return; } }
Окончательная версия
Очень уж оказались эти макросы громоздки, и мой перфекционизм не смог больше смотреть на эти монструозные конструкции. Поразмыслив немного, пришел к мысли, что идея у меня хорошая, но хранение кодов в виде макросов очень неудобно и, если решить эту проблему, то все будет хорошо. В итоге для хранения кодов стал использоваться QMap:
//Хранит соответствующие комбинации точек и тире символов QMap<QChar, QBitArray> codes_;
Такой подход оказался очень удобным. Теперь я всего лишь использовал текущий воспроизводимый символ в качестве ключа и получал готовый
для воспроизведения код (набор булевых значений), правда, алгоритм воспроизведения немного усложнился: понадобилось ввести счетчик текущего элемента символа и счетчик символов в строке:
void Morse::MiniSpace () { if (stop_) { this->Stop (); return; } sound_.setMuted (true); ++id_element_; //Преходим на другой элемент кода if ( id_element_ == codes_.value ( string_to_play_.at (id_char_) ).size () ) { ++id_char_; id_element_ = 0; QTimer::singleShot (duration_dot_ * 3, this, SLOT ( Mute() )); //Пауза между символами return; } QTimer::singleShot (duration_dot_, this, SLOT ( Mute() )); //Пауза между элементами символа } void Morse::Space () { if (stop_) { this->Stop (); return; } sound_.setMuted (true); //Пауза длится 7 точек //Но так как после символа идет пауза в три точки, то доп паузу нужно выставить длиной в 4 точки QTimer::singleShot (duration_dot_ * 4, this, SLOT ( Mute() )); } void Morse::Mute () { if (stop_) { this->Stop (); return; } if (id_char_ == string_to_play_.size ()) { // Строка закончилась this->Stop (); return; } if (string_to_play_.at (id_char_) == ' ') { Space(); ++id_char_; //Преходим на другой элемент кода return; } if (codes_.find ( string_to_play_.at (id_char_) ) == codes_.end ()) { qDebug() << string_to_play_.at (id_char_) << ": No code!"; sound_.stop (); return; } sound_.setMuted (false); //Включаем звук if ( codes_.value ( string_to_play_.at (id_char_) ).at (id_element_)) { QTimer::singleShot (duration_dot_, this, SLOT ( MiniSpace() )); //Воспроизводим точку } else { QTimer::singleShot (duration_dot_ * 3, this, SLOT ( MiniSpace() )); //Воспроизводим тире } } bool Morse::Play () { if (!stop_) return false; if (string_to_play_ == "") return false; stop_ = false; id_char_ = 0; id_element_ = 0; sound_.setMuted (true); //Выключаем звук sound_.play (); Mute (); } void Morse::Stop () { if (stop_) return; sound_.stop (); id_char_ = 0; id_element_ = 0; stop_ = true; }
Флаг stop_ был введен для предотвращения некорректной работы программы (два вызова подряд Play() и прочих нехороших вещей).
Остальную часть исходных кодов и заголовочные файлы не вижу смысла приводить в теле статьи, так как там все достаточно очевидно и прозрачно.
Полный набор исходников последней версии вы можете скачать на гитхабе. Написание графического интерфейса является тривиальной задачей, но все же, если будет создан GUI, то ссылку я добавлю. Если есть какие-то вопросы или замечания, пишите в комментариях — обязательно отвечу.
ссылка на оригинал статьи http://habrahabr.ru/post/267605/
Добавить комментарий