audio/zaudio.jl
2025-12-06 02:22:03 +04:00

220 lines
7.3 KiB
Julia
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

include("./zaf.jl")
using .zaf
using WAV
using Statistics
using Plots
using Tables
using CSV
using DSP
using SignalAnalysis
using DataFrames
# Папка сохранения полученных датасетов
data_dir = "datasets/"
# Папка исходных аудио-файлов
files_dir = "files/"
# Настройки CQT преобразования
octave_resolution = 24
minimum_frequency = 55
maximum_frequency = 9520
time_resolution = 25
# Размеры кадра в сек
window_duration = 0.04;
# Уровень энергии для порогового фильтра
energy_level = 50
# Количество кадров для сглаживания скользящей средней слепка аудио-данных
m_offset = 20
# Чтение WAV-файла
function read()
if !isempty(ARGS)
try
return wavread(files_dir*ARGS[1])
catch e
println("Ошибка чтения wav-файла")
end
else
println("Не указан входной файл")
exit()
end
end
# Загрузка сохраненного файла данных очищенных окон с преобразованием в среднеквадратичное отклонение по частотным полосам
function load(filename)::Vector{Any}
if !isempty(filename)
try
data = CSV.read(filename, DataFrame, skipto=2)
return data[!, :Column1]
catch e
println("Ошибка чтения csv-файла")
end
else
println("Не указан входной файл")
exit()
end
end
# Скользящее среднее
function m_average(arr::Vector{Float64}, n::Number)::Vector{Any}
so_far = sum(arr[1:n])
out = zero(arr[n:end])
out[1] = so_far
for (i, (start, stop)) in enumerate(zip(arr, arr[n+1:end]))
so_far += stop - start
out[i+1] = so_far
end
return out
end
# Нормализация уровня
function normalize(audio_input::Matrix{Float64},freq::Number)
return mean(audio_input, dims=2), freq
end
# Разбитие на окна
function windows(freq::Number)
local window_length = nextpow(2, ceil(Int, window_duration*freq))
local window_function = zaf.hamming(window_length, "periodic") # оконная аналитическая функция
return window_length, convert(Int, window_length/2), window_function
end
# Фильтрация окон по уровню минимальной энергии звука в каждом окне
function filter(audio_input::Matrix{Float64},window_length::Number)::Matrix{Float64}
output_signal = zeros(1,1)
number_samples = length(audio_input)
padding_length = floor(Int, window_length / 2)
number_times =
ceil(
Int,
((number_samples + 2 * padding_length) - window_length) /
step_length,
) + 1
local i = 0
for j = 1:(number_times-10) # 10 последних кадров отбарсываем
frame = audio_input[i+1:i+window_length]
if energy(frame) > energy_level
output_signal = [output_signal;frame]
end
i = i + step_length
end
return output_signal
end
# Преобразование Фурье
function stft(audio::Matrix{Float64},freq::Number)
local window_length,step_length,window_function = windows(freq)
return zaf.stft(audio, window_function, step_length), window_length, step_length, window_function
end
# Спектограмма разложения ряда Фурье
function spectrogram(audio::Matrix{Float64},freq::Number)::Matrix{Float64}
local audio_stft, window_length = stft(audio,freq)
local audio_spectrogram = abs.(audio_stft[2:convert(Int, window_length/2)+1, :]) # Отбрасываем постоянную составляющую
local number_samples = length(audio_input)
local xtick_step = 1
local ytick_step = 1000
local plot_object = zaf.specshow(audio_spectrogram, number_samples, freq, xtick_step, ytick_step)
heatmap!(title = "Spectrogram (dB)", size = (990, 600))
savefig(plot_object, data_dir*ARGS[1]*"-"*"spectr.png")
#CSV.write(data_dir*ARGS[1]*"-"*"spectr.csv", Tables.table(audio_spectrogram))
return audio_spectrogram
end
# CQT-октавное вевлет-преобразование
function cqt(audio::Matrix{Float64},freq::Number)::Matrix{Float64}
local cqt_kernel = zaf.cqtkernel(freq, octave_resolution, minimum_frequency, maximum_frequency)
cqt_spectrogram = zaf.cqtspectrogram(audio, freq, time_resolution, cqt_kernel)
CSV.write(data_dir*ARGS[1]*"-"*"cqt.csv", Tables.table(cqt_spectrogram))
local xtick_step = 1
local plot_object = zaf.cqtspecshow(cqt_spectrogram, time_resolution, octave_resolution, minimum_frequency, xtick_step)
heatmap!(title = "CQT spectrogram (dB)", size = (990, 600))
savefig(plot_object, data_dir*ARGS[1]*"-"*"cqt.png")
return cqt_spectrogram
end
# Преобразование вевлет-спектрограммы в спектр среднего значения по октавам со сглаживанием
function raft(spectr::Matrix{Float64})::Vector{Any}
freqs, frames = size(spectr)
raft_data = zeros(freqs)
for i in 1:freqs
raft_data[i] = mean(spectr[i, :])
end
return m_average(raft_data,m_offset)
end
# Cохранение образа записи
function save(audio_data::Vector{Any},freq::Number)::Vector{Any}
CSV.write(data_dir*ARGS[1]*"-"*"analise.csv", Tables.table(audio_data))
numbers = length(audio_data)
xtick_locations = [0:octave_resolution:numbers;]
xtick_labels = convert(
Array{Int},
minimum_frequency * 2 .^ (xtick_locations / octave_resolution),
)
plot_object = plot(audio_data, xticks = (xtick_locations, xtick_labels))
plot!(title = "Sound form", size = (990, 600))
savefig(plot_object, data_dir*ARGS[1]*"-"*"analise.png")
return audio_data
end
# Считываем данные из WAV файла и нормализуем их, частота дискретизации получается из заголовка WAV-файла
audio_input, freq = read()
audio_input, freq = normalize(audio_input, freq)
# Исходя из частоты дискретизации получаем длину кадров и шаг между ними
window_length,step_length,window_function = windows(freq)
# Сохраняем спектограмму исходного сигнала
spectrogram(audio_input,freq)
# Фильтруем кадры, отбрасывая кадры с низкой энергией
audio = filter(audio_input,window_length)
frames, freqs = size(audio)
if frames<5
println("Файл не прошел фильтр суммарной энергии сигнала")
exit()
end
# Осущетсвляем CQT-преобразование и сохранение спектограммы и массива данных
cqt_spectrogram = cqt(audio,freq)
# Преобразуем спектограмму в с спектр среднеквадратичных отклонений
raft_data = raft(cqt_spectrogram)
# Cохраняем результат
save(raft_data,freq)