Чим менше бітрейт потоку даних, тим далі можна його передати. Якщо вийде стиснути голос до 1-2 кБ / с, можна буде спілкуватися на відстані до 25 км (з використанням трансивера CC1125). Давайте спробуємо розробити голосовий кодек самостійно, без використання готових рішень на кшталт codec2.
Як влаштований голос? У ньому можна виділити кілька елементів:
- голосні звуки
- приголосні - шумові - вибухові: п, к, т ...
- приголосні - шумові - фрікатівние / свистячі: с, ш, щ, ф, в ...
- приголосні - сонорні - тремтячі: р
- приголосні - сонорні - носові: м, н
Голосні звуки можна розглядати як музичні - це чистий тон + його обертони різної амплітуди. Приголосні звуки - це шумові звуки з різними тембром і сонорні (тон + шум).
Голосні і приголосні здорово розрізняються по своїй інформативності, порівняйте:
Іронія полягає в тому, що голосні звуки дуже легко описати невеликим набором параметрів. У них немає частот, некратних частоті основного тону, тому все опис зводиться до кількох значень: частота основного тону + амплітуди гармонік. Причому частота домінанти повинна бути виміряна точно (з точністю до 16 біт), а ось амплітуди гармонік терплять і дуже сильну децимації - тому кожну амплітуду можна стиснути до 1 байта або навіть полубайта.
Давайте почнемо з голосних, якщо вже їх так легко описати. Я розробляв алгоритм на Python, просто тому що в ньому це реально швидко і просто.
Читання звукового файлу
Я записав звук «а» у власному виконанні та у виконанні дівчини, і зберіг його в сирому вигляді з дікретізаціей 8кГц у вигляді 16-бітних знакових цілих за допомогою Audacity. Зробимо функцію для читання такого файлу:
Я зберігаю тільки 800 відліків, тобто 100мс звуку - на такому відрізку ще можна прийняти сигнал стаціонарним, незмінні. Наступні фрейми потім проаналізуємо черговими проходами алгоритму. Побудуємо осциллограмму отриманого звуку:
побудова спектра
Обчислимо перетворення Фур'є для отриманого звуку. Фур'є повертає комплексну амплітуду, з якою можна обчислити амплітуду і фазу. Вухо практично невідчутно до фази, тому ми можемо заощадити половину бітрейта, повністю усунувши її - залишимо тільки амплітуду, обчисливши її функцією abs.
Гарний спектр, правда? Збільшимо цікавить нас фрагмент:
Добре видно основний тон і його обертони, спектр має періодичну структуру.
Положення кожного піку визначається співвідношенням: freq = samples / period. Абсциса піку дорівнює відношенню довжини фрейму в семплах (в нашому випадку 800) до періоду хвилі.
Правда, є одна проблема: якщо взяти чистий синус з частотою, що не потрапляє в обрану частотну сітку, то після перетворення Фур'є він перетвориться не в дельта-імпульс (одиничний сплеск), а в якусь розмазала фігуру:
Щоб зменшити цей ефект, необхідно застосувати до сигналу так зване «вікно». Я помножу сигнал на вікно Хеннінг.
Червона лінія - спектр вихідного сигналу, зелена - спектр сигналу з вікном Хеннінг:
Стало набагато краще: піки виділені сильніше, їх полушіріна менше і в спектрі менше шуму.
побудова кепстра
Тут вже видно все, що нам потрібно, але як програмно знайти піки і їх амплітуди? Проходити звичайним пошуком локального максимуму - невдячна справа: піки можуть бути злегка зміщені, дубльовані, і так ми знайдемо тільки цілу частину періоду, а він може бути дробовим. Як же нам тоді визначити цей період? Тим же способом, як ми визначали періоди в початковому звуковому сигналі, перетворенням Фур'є. Тільки потрібно спочатку логаріфміровать наш спектр, щоб сильніше виділити піки.
І застосувати до цього перетворення Фур'є:
Те, що ми побудували, називається прикольним словом «кепстра» (Cepstrum) - з назви зрозуміло, що це є чимось на зразок звернення спектра.
Пошук фундаментальної частоти
У ньому нам потрібен тільки перший пік, чиє становище буде в точності так само періоду шуканого основного тону. Ми знайдемо цей пік звичайним пошуком глобального максимуму, обмеживши область пошуку частотами 80..270 Гц, тому що саме в цьому діапазоні лежать основні частоти голосів дорослих людей. У нашому випадку це діапазон періодів 30..100 (8000 / 270..8000 / 80).
У моєму випадку фундаментальна частота дорівнювала 113Гц, а у моєї дівчини - рівно 200 Гц, що добре співвідноситься з цитатою з Вікіпедії:
запис обертонів
Все, тепер можна повернутися до спектру, пройти по всіх гармоникам знайденої основної частоти і записати їх амплітуду. Я трохи розширюю діапазони частот обертонів, щоб знайти суму всього піку (адже піки розмазані через розбіжність сітки частот з основною частотою).
Навіть дев'ята гармоніка має амплітуду, порівнянну з амплітудою основної частоти, тому потрібно записати досить багато обертонів, мінімум 8-10. З іншого боку, нам достатньо обмежитися смугою в 4кГц, вищі гармоніки вже не так потрібні для передачі голосу.
упаковка параметрів
Для опису одного фрейму з голосом (10мс в нашому випадку) потрібно:
- фундаментальна частота - 2 байта
- N * амплітуда обертони - N / 2 байт
Зазвичай достатньо не більше 10 обертонів, тобто характеристика займе 2 + 5 = 7 байт. Вихідний фрейм важив 1600 байт, тобто ступінь стиснення складає
230 раз. Але потрібно розуміти, що так ми стиснули тільки голосні звуки, ще залишилися приголосні - які, як на зло, передають куди більше інформації.
Розпакування звуку і порівняння
Разжатие знову переключитись можна було б зробити через зворотне перетворення Фур'є, але зробимо ще простіше - сгенерируем синусоїди потрібних частот і амплітуд, і складемо їх все один з одним.
Відкриємо в Audacity вихідний файл і відновлений, і порівняємо осцилограми:
Досить схоже. На слух теж схоже, причому 10 гармонік звучать набагато краще, ніж 8.