Когда-то давным-давно, когда деревья были большие, был такой шутер Half-Life, продолжение которого ждут до сих пор — это уже притча во языцех.

Были там такие противники как Combines (Combine Soldiers) — измененные захватчиками люди.
Во время игры можно было слышать их переговоры по радио — и я просто мечтал о такой радиостанции, которая бы сделала голос похожим на них и имела такой-же звук окончания радиопередачи.

Спустя много времени я таки решился осуществить свою мечту.

Переговоры были типа вот такого:


В то время я реально мечтал сделать, чтобы у моей радиостанции был хотя-бы такой же roger beep как в этих переговорах. Кто не знает что такое roger beep — это сигнал окончания передачи, тот, что звучит в конце каждого сообщения.

В игре тон его меняется в зависимости от солдата, вот что-то среднее:


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

Сердцем данной схемы стал процессор от фирмы ATMEL — ATTINY85.

И да — real time audio processing на крохотной ATTINY85 — это вполне возможно :)

Результат работы на примере голоса Геральта из Ривии


Оригинальный звук


Модифицированный звук


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

Можно сказать, что это все «just for fun», однако если убрать трансформацию голоса, то схема позволяет добавить roger beep любой радиостанции, если у нее есть разъем для аксессуаров типа «kenwood» (тот самый двойной разъем).

Я тестировал это на Baofeng-888s, и как раз у нее roger beep-а в принципе нет — так что возможность сделать это, или, например, скрэмблинг — вполне себе не только забава.

Как работает прошивка?


На самом деле ничего сложного там нет.

Используется низко-скоростной режим работы с периферией (через PLLCSR) — в этом случае питать ATTINY можно от 2.7 вольта и это дает частоту дискретизации около 9kHz.

Можно было использовать и высокоскоростной режим, что дало бы частоту около 18kHz, но тогда пришлось бы использовать напряжение питания от 4.5 вольт, а с этим были проблемы.

При нажатии на кнопку передачи на тангенте, генерируется прерывание и ATTINY выходит из сна, включает режим передачи на радиостанции и использует ADC на частоте примерно 8.9kHz чтобы оцифровывать голос с микрофона в циклический буфер:



При занесении очередного значения в буфер, оно миксуется с предыдущим — находится среднее, т.е. формула такая: $(староеЗначение + новоеЗначение) / 2$.

Это дает нам возможность переживать случай, когда старое значение еще не отдалось, а на его место уже пришло новое. А это рано или поздно случится, т.к. скорости чтения и записи — разные.
Указатели на буфер увеличиваются после каждой такой операции, когда они доходят до максимума, то сбрасываются в ноль — т.е. в начало буфера — поэтому он и зовется кольцевым.

Памяти у ATTINY не много — всего 500 байт, в данном случае под кольцевой буфер будет использоваться 450 байт, т.к. память нужна еще для переменных и стэка.

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

При чтении данные отдаются через PWM и после сглаживающего фильтра они превращаются в звук, который уходит в микрофонный тракт радиостанции.

Кстати, через PWM получается очень неплохое качество и это можно использовать для любого места, где надо проиграть какой либо звук (музыкальные шкатулки, подарки и т.д.), причем выводов у ATTINY хватает, чтобы подключить даже SD — и тогда вообще можно хоть целые композиции играть.

Но вернемся к нашей схеме: при отпускании кнопки, ATTINY все еще удерживает режим передачи, прекращает оцифровку, и отдает через PWM оцифрованный звук roger beep-а, затем выключает режим передачи и уходит в сон, чтобы снизить потребление электроэнергии.

Звук, т.к. занимает достаточно много места — около 5 килобайт — занимает часть памяти под программу — т.к. данной памяти вполне достаточно для кода — это решает проблему с нехваткой памяти.

Что касается степени замедления или убыстрения голоса, то нужный коэффициент должен быть записан в 0 адрес EEPROM ATTINY, и, соответственно, его можно менять в пределах от 0 до 255.

Примеры значений:
30 убыстрение голоса
55 без изменений
75 замедление голоса

Схема


Само устройство будет представлять собой тангенту (или правильней — манипулятор) к радиостанции и будет с ней работать через стандартный разъем для аксессуаров «Kenwood».

Схема очень и очень простая, легко собирается «на коленке».

Модуль микрофонного усилителя заказал на Aliexpress, причем рекомендую именно такой тип модуля, который здесь на фото. Питается от 3-5в, стоимость около 2$.

Динамик требуется около 8 ом, 0.5-1 ватт.

Кнопка — любая, работающая на замыкание. Светодиод любой с возможностью работы от 3 вольт, ну, или с соответствующим резистором.



Есть одна особенность, которая не попала в эту схему — в разъеме для аксессуаров предусмотрено 5-вольтовое питание аксессуаров, но вот конкретно в Baofeng-888s что-то у китайцев сделано не так. Мало того, что там 3 вольта, так еще и при нагрузке оно падает до 0.7 вольта и, естественно, схема не работает.

Для того, чтобы это обойти, был добавлен крохотный DC-DC преобразователь с 1.2 до 3.3 вольта с Aliexpress и внешний разъем для подключения любой AA-батарейки.

Причем по-умолчанию, когда в разъем ничего не вставлено, схема будет пытаться питаться от радиостанции.

Как выглядит схема в сборе:



Виновник торжества:



Как сделать двойной штекер (KENWOOD-разъем) для радиостанции:



Два штекера 3.5 и 2.5, смотанных вместе изолентой — куда уж без нее.

Корпус


Тут особо ничего сложного нет, единственное, с чем пришлось повозиться — это с окошком в виде лямбды. Получилось достаточно криво, но чуть-чуть похоже :)

Делал дремелем, затем с лицевой стороны заклеил скотчем, а с противоположной залил клей из клеевого пистолета, в него же утопил светодиод, который загорается, когда идет передача.

Здесь еще нет разъема под внешнее питание.

Снаружи:



Внутри:



Все вместе:



Резюме


Вобщем, детскую мечту я свою исполнил и хоть она и была иррациональна — ее исполнение греет мою взрослую душу. Надеюсь кто-то испытает схожие чувства. :)

Прошивка


FUSE-биты для ATTINY85 (8Mhz, питание >= 2.7в):

0xE2 LOW
0xDD HIGH
0xFF EXTENDED

Скачать файлы прошивки
В ближайшее время постараюсь причесать и выложить исходники всего этого.



Примечание


Поскольку у моего брата не сложилось с НЛО, то я решил опубликовать на Хабре эту статью от своего имени. Когда я ее собрал — знаете — был счастлив как ребенок. И подумал что она достойна внимания хабрасообщества.

У меня она живет совместно с радиостанцией YAESU, и отлично работает, питаясь от самой станции.

Новые версии прошивки и файлы с ней связанные можно будет найти в блоге моего брата protocoder.ru.

Ну а так как я принимал непосредственное участие в разработке и сам собрал такую-же штуку — то попытаюсь ответить на любые вопросы по ней.

Комментарии (41)


  1. catsmile
    12.03.2019 00:39
    +1

    Давным-давно был шутер Half Life, в котором не было никаких комбайнов. Потом вышло долгожданное продолжение Half Life 2, с комбайнами и гравипушкой, на совершенно другом движке и, я бы сказал, в другом жанре. Выросло, блин, поколение. Ностальгия, понимаишь.


    Беги, сейчас набигут более сложные комментаторы.


    1. ionicman Автор
      12.03.2019 06:57
      +1

      А Half Life 2 это не Half Life? :)


      Я прошел все части и все фановские дополнения Half Life, можно, конечно, делить на версии, но для меня, например, это одна вселенная Half Life.


      Ну и статья ведь не об этом, правда?


      Ну и, справедливости ради, движок Source — это дальнейшее развитие GoldSrc, используемое в первой части.


      А жанры всех этих игр — шутеры.


      1. kikiwora
        12.03.2019 08:11

        В оригинальном Half-Life были похожие звуки у военных, но они не такие интересные.

        Теперь я знаю кто будет работать на комбайнов :))))

        А за статью плюсанул бы, жаль что не могу, люди хабра не любят правду.
        Спасибо!


        1. atomlib
          12.03.2019 12:59

          В оригинальном Half-Life были похожие звуки у военных, но они не такие интересные.
          Это самое возмутительное заявление, что я видел сегодня. Звуки у них крайне интересны.

          В оригинальной Half-Life радиопереговоры военных — это не просто записанные заранее предложения, которые наговорил актёр. В игру встроен синтезатор речи. В файлах есть записи только отдельных слов, каждое записано в виде обычной речи и крика. Из них игра собирает реплики. Движок может менять тональность каждого из отдельных слов или целой реплики. От другой игры 1998 года такого ожидать было бы трудно, но это ведь Half-Life, где даже у тараканов есть игровой ИИ.


      1. atomlib
        12.03.2019 12:51
        +1

        Фановские? Рискну предположить, что речь про остальные продукты — переиграть во все существующие пользовательские моды времени не хватит. То, что создали в Gearbox — это официальные продукты, созданные по лицензии. Вообще, в истории многих продуктов «Вейлва» участие принимали сторонние подрядчики. Сейчас некоторые ещё помнят, почему определённая локация de_train называются popdog (из-за граффити на первой версии карты), но никто не объяснит, что это граффити означало (это логотип Barking Dog Studios, у которой эту карту и заказали).


        1. ionicman Автор
          12.03.2019 13:03

          уточню:
          Во офф. дополнения типа blueshift, и фановские, доступные тогда, когда играл.
          Причём пошёл далеко не все — некоторые не понравились совсем. А из всех допов больше всего понравился именно blueshift, хотя это и офф.


        1. tyomitch
          12.03.2019 18:29

          Слово valve читается /v?lv/ — не «вейлв»


        1. x67
          12.03.2019 23:28

          Целая игра вальвов — дота 2 была всего лишь картой для варкрафт 3, созданной айсфрогом. Для меня до сих пор загадка, почему близзард даже не пытались ничего с этим сделать, емнип по лицензии имея права на эту карту, название, прототипы героев и тп.


          1. atomlib
            13.03.2019 02:59

            Про это есть пост на «Хабре»: habr.com/ru/company/lawboot/blog/295422


  1. Igor_O
    12.03.2019 01:33
    +1

    Беги, сейчас набигут более сложные комментаторы.

    («Беги, дядя Митя, беги...» (с) ) Не все так страшно.
    Другое дело, что просто люди не знают, что такое настоящий звук в настоящем боевом радиоканале при использовании ларингофонов. В игре, конечно, звук получен с помощью цифровой обработки, но он достаточно близок к тому, что происходит в эфире на самом деле, особенно если мы в диапазоне радиостанции начинаем сигнал шифровать. А тоновые сигналы — я их наслушался в начале 2000-х в такси, когда все ездили с радиостанциями и в начале передачи и в конце передачи радиостанция передавала тональный сигнал, который для диспетчера идентифицировал водителя.
    И у меня звук радиостанций в HL не вызвал какого-то особого восторга или удивления. В такси начала 2000-х, конечно, из радиостанции звук не такой рафинированный, и ларингофоны никто не использовал. И шифрования не было. Но достаточно близко и похоже по звучанию.

    PS: я тоже не знаю, как оно на самом деле звучит в боевых условиях, но когда где-нибудь в горах и на пределе, или реальные переговоры летчиков из 70-х во время воздушного боя, все это можно послушать в ютюбе. Хотя чисто со звуком при использовании ларингофонов — проблема. Как ни пытался, находятся исключительно длинющщие занудные обзоры про то, что «для надевания на шею нужно растянуть пружинную дугу, занести это за голову, надеть на шею» (как объяснение этого можно растянуть на 20 минут???)… И ни одного образца звука «на той стороне» при использовании такого микрофона.


    1. Gogino2005
      12.03.2019 06:35
      +1

      И ни одного образца звука «на той стороне» при использовании такого микрофона.

      Автор тоже не стал демонстрировать конечный результат.
      Геральда как то мало, можно же переговоры на видео заснять на минутку.


    1. Voila2000
      12.03.2019 15:23

      Хотя чисто со звуком при использовании ларингофонов — проблема

      Не очень понял о чем речь, но, на всякий случай, купить для экспериментов на какой нибудь околооружейной барахолке танковый шлемофон с ларингофонами и наушниками можно совсем недорого. Было бы желание. Там даже радиостанцию танковую можно прикупить :)


    1. tyomitch
      12.03.2019 18:44

      реальные переговоры летчиков из 70-х во время воздушного боя, все это можно послушать в ютюбе

      Не обязательно 70-х, можно и вполне современных, причём в изобилии.


    1. Xandrmoro
      12.03.2019 20:02

      Звук крайне паршивый, особенно когда и динамикам, и ларингофонам, и радиостанциям под полсотни лет. Слова нечёткие, голоса идентифицировать начинаешь в лучшем случае через несколько дней, и то не все, адовейшие помехи…
      В боевых действиях не участвовал, но на учениях наслушался.


  1. Nuwen
    12.03.2019 07:01
    +1

    Осталось ещё написать приложение на смартфон, которое голосом H.E.V. будет сообщать о подключении зарядного устройства, уровень заряда, сообщения и уведомления. И уже сбудется моя мечта. Правда, я её уже на Siemens c75 осуществлял.


    1. ionicman Автор
      12.03.2019 07:15
      +1

      Если речь про андроид, и тел рутованный, то для осуществления вашей мечты нужно всего-лишь закинуть файлы со звуком в формате ogg в папку /root/system/media/audio/ui и перегрузить телефон. Никакое приложение писать не нужно.


      1. ffs
        12.03.2019 11:44

        А для чего при этом рутованность?


        1. Cerberuser
          12.03.2019 11:55

          А это место разве доступно на запись без рута?6


          1. ffs
            12.03.2019 11:58

            Точно, был неправ. Спросонья из того адреса мозг вычленил только /media/audio


      1. Nuwen
        12.03.2019 12:42

        На андроиде есть события подключения/отключения и окончания зарядки. А больше всего казалось полезным, что телефон прямо во время зарядки сообщает, что «уровень энергии достиг 70%» — если куда-то торопишься, то незаменимо.


  1. Vitalley
    12.03.2019 11:03

    ФНЧ на входе не понадобился?


    1. ionicman Автор
      12.03.2019 11:13

      Вполне неплохо без него.


  1. Androniy
    12.03.2019 15:22

    Если скорость записи обгоняет скорость чтения, то куски голоса должны пропадать, что должно быть слышно при близких скоростях, когда частота биения маленькая. Щелчек вы задавили ФНЧ (усреднением соседних семплов). Может имело смысл именно фильтром получить требуемый эффект? Тогда часть звука не будет пропадать…


    1. ionicman Автор
      12.03.2019 16:29

      Почему пропадать — там смешиваются сэмплы и откуда должны взяться щелчки?


      1. Androniy
        12.03.2019 16:47

        А что происходит, когда указатель записи на полный круг начинает обгонять указатель чтения? Вы передавали в радиостанцию участок сигнала и тут вдруг раз и уже другие данные на следующем такте передаёте (совершенно другой уровень). Усреднение соседних семплов — это заметание проблемы под ковер, чтобы не сильно слышно было. Ну а что происходит с теми данными, которые мы не успели отправить в аналоговый тракт за время пока один указатель обгоняет другой? Они пропадают.


        1. ionicman Автор
          12.03.2019 16:51

          Они не пропадают, а смешиваются с предыдущими. Щелчки при старте/окончании оцифровки лечатся на ходу рампингом.


          1. Androniy
            12.03.2019 19:22

            Значит вы плохо объяснили ваш алгоритм в статье, и я не понял. Вы написали, что смешиваете только два соседних сэмпла (бегущее среднее?). А выдача данных на ЦАП (PWM) идет со скоростью меньше, чем идет запись с АЦП. Смотрите, если вы записываете с АЦП со скоростью 8.9kHz, а выводите на ЦАП со скоростью в два раза меньше, например, то ваш буфер 450 сэмплов «переполнится» через 450/8900*2 = 0,1 секунду. На данный момент времени на выход уйдет всего-лишь 450 сэмплов, а записано будет 900 сэмплов. После этого произойдет «обгон» указателя записи и воспроизводиться будет текущий сэмпл (усредненный с предыдущим, но это не важно). В результате каждые 100 миллисекунд будет происходить прыжок на 50 мс. Половина данных потеряна. Если у вас другой алгоритм, то я его не понимаю.


            1. ionicman Автор
              12.03.2019 20:07

              Попробую еще раз объяснить более подробно как идет алгоритм (два независимых цикла).

              Запись:

              1. получили байт с АЦП
              2. взяли байт по указателю записи, сложили его с полученным с АЦП байтом, разделили на 2, сделали &0xFF
              3. положили этот байт по указателю записи
              4. увеличили указатель записи
              5. если указатель вышел за размер буфера — обнулили его


              Чтение:
              1. взяли байт по указателю чтения
              2. отправили его в PWM
              3. увеличили указатель на чтение
              4. если указатель вышел за размер буфера — обнулили его


              Т.е. смешивание идет непрерывно. Грубо говоря, если длины буфера не хватает — полезный сигнал просто начинает затухать. Никаких щелчков и потерь кусков быть не должно. Ну или я что-то не понимаю.

              Чтобы не быть голословным — мы болтали вдвоем и на одной скорости и на убыстренной и на замедленной — никаких щелчков и вообще проблем (насколько это может давать радиостанция) не было слышно. Среднее время одного вещания — т.е. нажали PTT — говорим — отжали PTT было около 5 секунд.

              Рампинг (медленное возрастание/затухание до нужного уровня) используется только при начале передачи и ее окончании после Roger Beep.

              Как-то так.


              1. Androniy
                12.03.2019 22:47

                Ваше усреднение соседних сэмплов — это просто частный случай цифрового ФНЧ. Никакого смешивания и накопления при этом не происходит. Если на вход подать низкочастотную синусоиду, то данное усреднение вообще мало повлияет на сигнал. А если чтение и запись идут на разной скорости, то рано или поздно один буфер циклически догонит другой.Если отношение скоростей небольшое, то это может происходить достаточно редко. Просто представьте что будет если на входе синусоида ну, например, 50 Гц. Воспроизведение из буфера в два раза медленней, чем запись. После 900 отсчетов запись будет производиться в то место, из которого читаем. Все оставшиеся в буфере 450 семплов уже никогда не будут воспроизведены, т.к. их будут перезаписывать новые данные. Если фаза новых данных не совпадет со старыми, то вы просто усредните эту точку и ослабите щелчок в два раза. Учитывая малую частоту воспроизведения вы это и не слышите особо, наверное, но стык там есть точно.
                А вот при ускорении получается обратный эффект. Там вы будете повторять эти ваши записанные сэмплы по два раза. Просто при таком алгоритме не может быть иначе.
                Начало воспроизведения и окончание — это вы все правильно делаете, но это не проблема. Вы их не слышите скорее всего потому, что частота воспроизведения и так маленькая, плюс вы еще фильтром срезаете верхние частоты.


                1. ionicman Автор
                  12.03.2019 23:41

                  Все оставшиеся в буфере 450 семплов уже никогда не будут воспроизведены, т.к. их будут перезаписывать новые данные

                  Вот это не понял — там же не перезаписываются данные никогда, они всегда накладываются на то, что уже есть (т.е. миксуются со старыми данными) и в любом случае попадут на выход.

                  Ну т.е. скажем в буфере лежит 1, 2, 3, предположим они перезаписываются с самого начала данными 4, 5, 6 => тогда в буфере окажутся данные (1+4)/2, (2+5)/2, (3+6)/2, затем они воспроизведутся.

                  Т.е. с моей точки зрения единственное, чем многократное прохождение чревато — затуханием старого сигнала.

                  Поправьте, если не так. Если можно — с примером.


                  1. Androniy
                    13.03.2019 08:11

                    А, я понял наконец-то. Вы не с предыдущим сэмплом из АЦП усредняете, а с тем, который был круг назад. Значит вы теряете только часть информации при перезаписи. Да, это меняет дело. Правда что при этом происходит со спектром уже сложно сказать. Вы давите те частоты, которые не входят целыми в ваш кольцевой буфер. Перевели потери из временнОй области в частотную. Это решение, да.
                    Как пример потери информации: например наш полезный сигнал — синусоида, которая за время записи буфера проходит половину периода (на самом деле любое количество периодов с половинкой, главное — смена фазы на противоположную). Первый круг мы прошли и записали половину периода синусоиды, а при втором прохождении мы усредняем её с противофазной второй половинкой. Второе кольцо мы получим тишину.
                    Ощутимое затухание возникнет, если скорость записи более чем в два раза выше, чем скорость воспроизведения. Тогда от некоторых участков данных останется 1/4 амплитуды (или меньше).


                    1. h45242
                      13.03.2019 23:08

                      а с тем, который был круг назад

                      а так вообще будет просто эхо…


                      1. ionicman Автор
                        13.03.2019 23:09

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


                        1. h45242
                          14.03.2019 07:34

                          ну так задержка и есть на 450 отсчетов. а потом сложение сигнала.


  1. Androniy
    12.03.2019 19:27

    А еще у вас на входе с микрофона нет аппаратного ФНЧ. Вероятней всего, ваш микрофон и его усилитель могут пропускать полосу частот намного больше 8,9кГц. При оцифровке с частотой 8,9кГц без аппаратного фильтра вы получаете алиасинг, т.е. вы отзеркалили частоты выше частоты Найквиста-Котельникова в интересующую вас область, что является искажением оригинального сигнала. Так делать не стОит.


    1. ionicman Автор
      12.03.2019 20:15

      На плате использована MAX9814, емнип там есть ФНЧ.

      Но было бы интересно, чем это все чревато? Что за искажения га слух будет при оцифровке?

      Я как бы не всамделишный сварщик: ) Т.е. я больше программист, чем электронщик.


      1. Androniy
        12.03.2019 22:35

        ФНЧ даже если его там нет сам собой получается. Вопрос в том на какой частоте он задавит звук достаточно сильно. В английской вики есть пример звучания алиасинга на синусоиде. Все составляющие после частоты Найквиста (в вашем примере все, что больше 4,4кГц) копируется на основной частотный диапазон, в результате появляются неестественные звуки.


        1. ionicman Автор
          12.03.2019 23:43

          Понял, спасибо за ликбез.

          Ну здесь я не замечал такого, может быть потому, что радиостанция и так достаточно хорошо искажает :)


  1. vdobler
    12.03.2019 20:09

    Мы подобное в школе делали.


  1. Habivax
    12.03.2019 20:09

    Большое спасибо за статью! Правда радиостанции у меня нет, зато узнал много нового и интересного про свой любимый Half-Life. И не подозревал что в то время в игре была такая продвинутая работа со звуком.

    Пытался понять как работает кольцевой буфер, но не осилил. За секунду получаем 8900 байт (если АЦП восьмиразрядное), а буфер всего 450 байт. То есть буфер на 50.5 миллисекунд. Как же это всё работает?!


    1. ionicman Автор
      12.03.2019 20:10

      Тут попытался подробнее расписать.