- •«Московский технический университет связи и информатики»
- •«Радио и телевидение»
- •«Телевидения и звукового вещания имени с.И. Катаева»
- •Содержание
- •Теоритическая часть исследования
- •Цель и задачи работы, объект исследования
- •Обзор современного состояния изучаемой области
- •Обзор публикаций по теме исследования
- •Сравнение методов в представленных публикациях
- •Результат сравнения методов
- •Практическая часть исследования
- •Представление модели в виде структурной схемы
- •Представление метода в виде функциональной схемы
- •Подбор входных и выходных данных для модели
- •Визуализация результатов работы модели
- •Выводы по результатам исследования
- •Список литературы
- •Приложение
- •Исходный код программы
- •Москва 2024
Приложение
Исходный код программы
from scipy.signal import firwin, lfilter, kaiserord #type: ignore
from soundfile import write as sf_write #type: ignore
from librosa import load as lbrs_load
from librosa.display import waveshow
from matplotlib import pyplot as plt #type: ignore
from time import perf_counter_ns
from os.path import isfile
import numpy.typing as npt
import typing as typ
import numpy as np
import curses #type: ignore
#Функция подготовки окна консоли к использованию
def init_cnvs(canvas):
win = curses.initscr()
curses.curs_set(0)
canvas.border()
canvas.refresh()
return win
#Отрисовка текста на экране в выбранной позиции
def draw_text(canvas,r: int,c: int,text: str) -> None:
canvas.addstr(r,c,text)
canvas.refresh()
curses.napms(500)
#Считывание строки, введённой пользователем
def get_text(canvas,r: int, c: int) -> str:
curses.curs_set(1)
canvas.refresh()
text: str = canvas.getstr(r,c)
curses.curs_set(0)
canvas.border()
canvas.refresh()
curses.flushinp()
return text
#Считывание символа, введённого пользователем
def get_char(canvas,r: int, c: int) -> str:
curses.curs_set(1)
canvas.refresh()
char: str = canvas.getkey(r,c)
curses.curs_set(0)
canvas.border()
canvas.refresh()
curses.flushinp()
return char
#Фильтрация
def filtration(samples: npt.NDArray,sr: float,cutoff_hz: typ.Tuple[int,int],bts: npt.DTypeLike) -> npt.NDArray:
nyq_rate: float = sr/2.0 #Частота Найквиста
trans_width: float = 5.0/nyq_rate #Ширина перехдной зоны
ripple_db: float = 15.0 #Величина затухание вне области пропускания
N, beta = kaiserord(ripple_db, trans_width) #Порядок и параметр Кайзера
filter_coeffs: npt.NDArray = firwin(N,cutoff_hz,window=('kaiser',beta),pass_zero='bandpass',fs=sr) #Получение коэффициентов фильтра
filtered_samples: npt.NDArray = lfilter(filter_coeffs,1.0,samples,axis=1) #Фильтрация
filtered_samples[0][abs(filtered_samples[0])<=1/((ripple_db/10)**10)] = 0 #Фильтрация по затуханию
filtered_samples[1][abs(filtered_samples[1])<=1/((ripple_db/10)**10)] = 0 #Фильтрация по затуханию
filtered_samples = filtered_samples.astype(bts)
return filtered_samples
#Сведение всех отфильтрованных сигналов в один
def signal_mixing(f_samples: typ.List[npt.NDArray]) -> typ.List[typ.List]:
counter: int = 0
mixed_signal: typ.List[typ.List] = [[],[]]
while counter != len(f_samples[0][0]):
if f_samples[0][0][counter] != 0:
#
mixed_signal[0].append(f_samples[0][0][counter])
mixed_signal[1].append(f_samples[0][1][counter])
#
elif f_samples[1][0][counter] != 0:
#
mixed_signal[0].append(f_samples[1][0][counter])
mixed_signal[1].append(f_samples[1][1][counter])
#
elif f_samples[2][0][counter] != 0:
#
mixed_signal[0].append(f_samples[2][0][counter])
mixed_signal[1].append(f_samples[2][1][counter])
#
else:
#
mixed_signal[0].append(f_samples[3][0][counter])
mixed_signal[1].append(f_samples[3][1][counter])
#
counter += 1
return mixed_signal
#Кодирование
def encoder(samples: typ.List[typ.List]) -> typ.List[typ.List]:
#Необходимые для кодирования величины
array_diff = [[0],[0]]
array_diff[0][0] = samples[0][0]
array_diff[1][0] = samples[1][0]
counter = 1
while counter != len(samples[0]):
array_diff[0].append(samples[0][counter]-samples[0][counter-1])
array_diff[1].append(samples[1][counter]-samples[1][counter-1])
counter += 1
return array_diff
#Декодирование
def decoder(samples: typ.List[typ.List]) -> typ.List[typ.List]:
#Необходимые для декодирования величины
array_rslt = [[0],[0]]
array_rslt[0][0] = samples[0][0]
array_rslt[1][0] = samples[1][0]
counter = 1
while counter != len(samples[0]):
array_rslt[0].append(samples[0][counter]+array_rslt[0][counter-1])
array_rslt[1].append(samples[1][counter]+array_rslt[1][counter-1])
counter += 1
return array_rslt
#Вывод волнового представления аудио
def show_waveform(array: npt.ArrayLike,number: int,sr: float,title: str='Waveform',label: str='Waveform') -> None:
plt.figure(number)
if type(array) == 'nd.array':
waveshow(array,sr=sr,label=label)
else:
waveshow(np.array(array),sr=sr,label=label)
if label != 'Waveform':
plt.legend()
plt.title(title)
#Инициализация окна при запуске программы
w = curses.wrapper(init_cnvs)
#Проверка размеров окна на подходящие для работы
if curses.COLS < 71 or curses.LINES < 21:
curses.endwin()
print(f'Размер окна консоли слишком мал и составляет {curses.COLS} на {curses.LINES} пикселей.\
\nУвеличьте размер окна консоли до 71 на 21 пикселей и запустите программу заново.')
exit()
else:
pass
#Считывание аудиофайла
draw_text(w,1,1,'Вас приветствует Кривая Имитация Реализации Кодека Aptx (К.И.Р.К.А.)!')
draw_text(w,2,1,'Для дальнейшей работы необходимо указать путь к .wav файлу.')
audio_file_input: str = get_text(w,3,1) #Исходный файл
if isfile(audio_file_input) == True: #Проверка существования файла
draw_text(w,4,1,'Файл успешно найден! Загружаю.')
samples_orig, sample_rate = lbrs_load(audio_file_input,sr=44100,mono=False,duration=None,dtype='float64')
draw_text(w,5,1,'Загрузка завершена.')
else:
draw_text(w,4,1,'Файл не найден, повторите попытку ещё раз.')
draw_text(w,5,1,'Программа завершится через две секунды.')
draw_text(w,6,1,'До свидания!')
curses.napms(1500)
exit()
#Фильтрация по диапазонам (Адаптивное кодирование)
friq_bands: typ.Tuple[typ.Tuple[int,int],typ.Tuple[int,int],typ.Tuple[int,int],typ.Tuple[int,int]]\
= ((1,5500),(5501,11000),(11001,16500),(16501,22000)) #Необходимые диапазоны частот
bytes_for_each_fb: typ.Tuple[npt.DTypeLike,npt.DTypeLike,npt.DTypeLike,npt.DTypeLike]\
= ('float64','float32','float16','float16') #Кол-во байт на значение отсчётов в каждом диапазоне
filtered_samples: typ.List[npt.NDArray] = [] #Список под разбитое по диапазонам аудио
counter: int = 0
perf: int = perf_counter_ns()
draw_text(w,7,1,'Произвожу фильтрацию по диапазонам частот.')
for i in friq_bands:
filtered_samples.append(filtration(samples_orig,sample_rate,i,bytes_for_each_fb[counter]))
counter += 1
draw_text(w,8,1,f'{counter} диапазон выделен!')
counter = 0
draw_text(w,8,1,f'Готово! Управилась за {(perf_counter_ns()-perf)/10**9:0.3f} с.')
#Структура filtered_samples[диапазон][канал][значение]
#Объединение в один «аудиофайл»
filtered_audio: typ.List[typ.List] = signal_mixing(filtered_samples)
del filtered_samples
#Кодирование
draw_text(w,9,1,'Произвожу кодирование.')
perf = perf_counter_ns()
encoded_audio: typ.List[typ.List] = encoder(filtered_audio)
draw_text(w,10,1,f'Готово! Управилась за {(perf_counter_ns()-perf)/10**9:0.3f} с.')
#Декодирование
draw_text(w,11,1,'Произвожу декодирование.')
perf = perf_counter_ns()
decoded_audio: typ.List[typ.List] = decoder(encoded_audio)
draw_text(w,12,1,f'Готово! Управилась за {(perf_counter_ns()-perf)/10**9:0.3f} с.')
del encoded_audio
#Вывод на экран волнового представления аудио
draw_text(w,13,1,'Формирую графики.')
perf = perf_counter_ns()
show_waveform(samples_orig,1,sample_rate,'Original audio')
show_waveform(filtered_audio,2,sample_rate,'Filtered audio')
show_waveform(decoded_audio,3,sample_rate,'Encoded-decoded audio')
show_waveform(samples_orig,4,sample_rate,label='Original')
show_waveform(filtered_audio,4,sample_rate,'Original and filtered audio','Filtered')
draw_text(w,14,1,f'Готово! Управилась за {(perf_counter_ns()-perf)/10**9:0.3f} с.')
del perf
del samples_orig
#Указание путей для вывода результата и запись в аудиофайл
draw_text(w,16,1,f'Я закончила работу. Желаете вывести результат? y|n')
flag: str = get_char(w,17,1)
if flag == 'y':
draw_text(w,17,1,f'ОК. Итоговый или только после фильтрации? r|f')
flag = get_char(w,18,1)
if flag == 'r':
draw_text(w,18,1,f'ОК. Укажите путь к файлу вывода (с .wav).')
audio_file_output_decoded: str = get_text(w,19,1) #Файл вывода для итогового аудио
sf_write(audio_file_output_decoded,np.array(decoded_audio,dtype='float64').T,sample_rate,subtype='PCM_16') #Запись результата в аудиофайл
draw_text(w,19,1,f'Я закончила! Не забудьте про графики!')
elif flag == 'f':
draw_text(w,18,1,f'ОК. Укажите путь к файлу вывода (с .wav).')
audio_file_output_filtered: str = get_text(w,19,1) #Файл вывода для фильтрованного аудио
sf_write(audio_file_output_filtered,np.array(filtered_audio,dtype='float64').T,sample_rate,subtype='PCM_16') #Запись результата в аудиофайл
draw_text(w,19,1,f'Я закончила, до свидания! Не забудьте про графики!')
else:
draw_text(w,18,1,f'Что ж. Без результатов, так без результатов.')
draw_text(w,19,1,f'Хорошо. Тогда я закончила! Не забудьте про графики!')
elif flag == 'n':
draw_text(w,17,1,f'Хорошо. Тогда я закончила! Не забудьте закрыть графики!')
plt.show()
curses.endwin()
