在MATLAB2014a中先对音乐信号的单声道进行归一化再通过恒q变换实现对一个12s的音乐信号的音阶提取,但不使用MATLAB自带的CQT函数,绘制出音阶图并给出详细代码及注释
时间: 2024-02-15 16:02:57 浏览: 17
好的,我会尽力回答您的问题。首先,需要明确一点,恒Q变换即为常数Q的离散傅里叶变换。在本问题中,需要将音频信号进行分帧处理,然后对每一帧进行恒Q变换,得到各个频率对应的能量值,再根据音乐理论将其转换为对应的音阶。
以下是详细的MATLAB代码及注释:
```matlab
% 读取音频文件并进行归一化
[x, fs] = audioread('music.wav');
x = x(:,1); % 取单声道
x = x / max(abs(x)); % 归一化
% 分帧处理
frame_length = round(0.03 * fs); % 帧长为30ms
frame_shift = round(0.01 * fs); % 帧移为10ms
frame_num = fix((length(x) - frame_length) / frame_shift) + 1; % 总帧数
frames = zeros(frame_length, frame_num); % 初始化帧矩阵
for i = 1:frame_num
start = (i - 1) * frame_shift + 1;
frames(:, i) = x(start:start + frame_length - 1);
end
% 设置恒Q变换参数
Q = 25; % Q值
K = 2^(1/24); % 半音频率比例
fmin = 130.81; % C4的频率
B = round(Q * log2(K) * fs / fmin); % 滤波器组数
f = fmin * 2.^(0:B/Q-1/Q); % 每个滤波器组的中心频率
fft_len = 2^nextpow2(frame_length + B); % FFT长度
filter_coef = zeros(B, fft_len); % 滤波器系数矩阵
for i = 1:B
freq = 0:fft_len-1;
freq = freq / fft_len * fs;
freq_res = freq(2) - freq(1);
filter_coef(i,:) = 2 * cos(2 * pi * freq / fs * f(i)) ...
.* hanning(fft_len)' / fft_len * sqrt(2 * freq_res / Q);
end
% 对每一帧进行恒Q变换
energy = zeros(B, frame_num); % 每帧每个滤波器组的能量值
for i = 1:frame_num
frame = frames(:, i);
frame_fft = fft(frame .* hanning(frame_length)', fft_len);
for j = 1:B
filter_fft = fft(filter_coef(j,:), fft_len);
energy(j,i) = abs(sum(frame_fft .* filter_fft))^2;
end
end
% 转换为音阶
note_names = {'C','C#','D','D#','E','F','F#','G','G#','A','A#','B'}; % 不同音符的名称
note_freqs = [130.81 138.59 146.83 155.56 164.81 174.61 185.00 196.00 207.65 220.00 233.08 246.94]; % 不同音符的频率
note_energy = zeros(length(note_freqs), frame_num); % 每帧每个音符的能量值
for i = 1:length(note_freqs)
for j = 1:B
freq_index = round(note_freqs(i) / (fs / fft_len));
note_energy(i,:) = note_energy(i,:) + energy(j,:) .* (abs(freq_index-j) <= Q/2);
end
end
note_energy = 20*log10(note_energy / max(max(note_energy))); % 转换为对数形式
note_energy(note_energy < -60) = -60; % 对数能量小于一定阈值的置为-60dB,避免图像中出现过多黑色
% 绘制音阶图
figure;
imagesc(note_energy);
colormap(gray);
axis xy;
xlabel('Time (s)');
ylabel('Note');
set(gca, 'YTick', 1:length(note_names));
set(gca, 'YTickLabel', note_names);
```
解释一下代码中的几个关键步骤:
1. 分帧处理:将整个音频信号分成若干个长度为30ms的帧,每相邻两帧之间相隔10ms。
2. 恒Q变换:对每一帧进行恒Q变换,得到各个频率对应的能量值。
3. 转换为音阶:根据不同音符的频率,将各个频率对应的能量值转换为对应的音符的能量值。
4. 绘制音阶图:将每个音符的能量值绘制成图像即可。
希望能够对您有所帮助。