Накидал скрипт на python, реализующий получение ужатого спектра, содержащего только частоты ОТ.
Таким образом, получается в вектор размером 30-60 поместить спектр размером 256 с минимальными потерями. Причём только нижние гармоники важно передавать полностью, высшие(>10) уже можно склеивать вместе и усреднять.
Спойлер
Код: Выделить всё
#размер кадра data=512 семплов, частота дискретизации 16кГц, частота кадров 50Гц(шаг кадров 320семплов)
data*=maskH #накидываем оконную функцию
sp=np.fft.rfft(data)#вычисляем спектр от действительных данных
#получаем амплитудный спектр и немножко его нормируем(чтобы амплитуда компонент на превышала 1.0)
spA=np.abs(sp)
spA=spA/(len(spA)*0.5)#делитель подбирается в зависимости от реализации FFT и размерности входных данных
spA[:3]=0 #ФВЧ,отсекающий постоянку и частоты до 100гц
#коэффициент основного тона(ОТ) eсли 0.6..1.0, то в сигнале определённо есть основной тон, иначе считаем, что сигнал шумовой(без ОТ)
kF0=np.dot(data,np.roll(data,1))/(max(np.dot(data,data),0.00001))
spA32=np.zeros(32)#резервируем вектор для урезанного спектра
if kF0<0.6:#для шумового сигнала f0 фиксирована
f0=float(len(spA))/len(spA32)
else:
#получение частоты основного тона в масштабе спектра f0=2.0..12.0 всего 127 шагов(7бит)
f0=getF0(spA)
#заполняем урезанный спектр из основного с учётом частоты ОТ
i32=1
i=f0
while i<len(spA):
k1=i%1.0
k2=1.0-k1
spA32[i32]=k2*spA[int(i)]+k1*spA[int(i)+1]
i32+=1
i+=f0
if i32>=len(spA32) or i+2>len(spA):
break
#на синтезатор посылаем f0 и вектор spA32
#вектор spA32 представляет из себя амплитуды синусоид кратных f0
#например, 10 элемент spA32 это амплитуда синусоидального колебания частотой 10*f0
#f0 представляется 7 битами, можно уменьшить шаг до полутона и представить диапазон 5 битами(32 значения)
При таком подходе речь ещё остаётся весьма разборчивой. Хоть и звучит весьма "металично". Частично побороть этот эффект, я думаю, возможно в синтезаторе, слегка "распушив" высокие частоты. Но это не первостепенная задача, так как надо сначала поработать над сжатием. Синтезатор работает весьма примитивно, выдавая на каждом шаге частоты дискретизации(16кГц) сумму синусоид определённой амплитуды и частоты.
Основное сжатие будет происходить на векторе ужатого спектра.Амплитуды синусоид могут быть представтлены весьма грубо вплоть до точности +- в 2 раза. Из чего родилась идея логарифмировать их по основанию 2 и оставить только целую часть. Максимальное значение амплитуды 1 , минимальное 0.
Примем минимальное значение равным 2 в степени -15, тогда с учетом округлени до целого каждая компонента будет занимать 4 бита.
Получаем сжатый поток (7+31*4)*50=6550 бит/с.
Но такой большой динамический диапазон в 30000 раз слегка избыточен. Можно выделить характеристику, отвечающую за среднюю амплитиуду(энергию) спектра(4бита) и тогда можно представить компоненты с разрешением в 3 бита(динамический диапазон около 100 раз)
Поток почти не теряет в качестве, зато ужимается до (7+4+31*3)*50=5200 бит/с.
Дальнейшая идея сжатия основана на том, что соседние компоненты весьма коррелированы и можно объединить их в группу, представив средней амплитудой, уменьшив таким образом размерность вектора спектра. Практика показала, что младшие компоненты лучше не объединять, во всяком случае, до 5-го. Дальше уже можно по парам и далее, чем выше номер компонента, тем больше можно делать размер группы. Я перепробовал несколько формул объединения и приятнее всего мне показалась такая.[1,1,1,1,1,1,1,1,2,2,2,3,4,5,5] - цифрами обозначены размеры групп. Размер вектора спектра получается 15 и битрейт (7+4+15*3)*50=2800 бит/с. Звук, конечно, портится, но разборчивость всё ещё нормальная.
Ну и не мог я не попробовать ужаться совсем экстремально по формуле [1,1,1,1,1,3,3,10,10] до (7+4+9*3)*50=1900 бит/с. Качество, как по мне, неприемлемое, даже для рации. Но я сравнивая с готовыми кодеками менее 2кбит/с скажу, что они все звучат немного не нормально. К тому же я пытаюсь хоть как то представить высокие частоты (>4кГц), в то время как большинство не вылазят за полосу 4кГц, начиная с источника 8кГц частотой дискретизации. Может и я к этому приду, но пока я работаю с исходным сигналом в 16кГц и явно слышу разницу в высоких частотах. Пока так. Образцы звука в архиве. Подумаю пока как ещё можно сжать данные, сохранив качество получше. С эти можно ещё поработать.
Насчёт МК думаю взять китайский ESP32. Его быстродействия точно хватит. Там и FPU есть и 2 ядра. И цена неплохая. Пока разбираюсь с SDK.
примеры звука