所需前置知识
读音频 audioread
语法:
[y,Fs] = audioread(filename); 从名为 filename 的文件中读取数据,并返回样本数据 y 以及该数据的采样率 Fs。
[y,Fs] = audioread(filename,samples); 读取文件中所选范围的音频样本,其中 samples 是 [start,finish] 格式的向量。
[y,Fs] = audioread(___,dataType) ; 返回数据范围内与 dataType(‘native’ 或 ‘double’)对应的采样数据,可以包含先前语法中的任何输入参数。
写音频audiowrite
语法:
audiowrite(filename,y,Fs); 以采样率 Fs 将音频数据矩阵 y 写入名为 filename 的文件。filename 输入还指定了输出文件格式。输出数据类型取决于音频数据 y 的输出文件格式和数据类型。
audiowrite(filename,y,Fs,Name,Value) ; 使用一个或多个 Name,Value 对组参数指定的其他选项。
FFT 离散傅立叶变换的快速算法:此处 FFT 只用作绘制频谱图像进行分析,并不对音频格式转换有直接的影响:
N个采样点,经过FFT之后,就可以得到N个点的FFT结果。为了方便进行FFT运算,通常N取2的整数次方,但是这里直接取读取出来的样本数作为N。
FFT 计算公式如下:
Xk 长度与 N 相同。
根据奈科斯特定律,只有 f=fs/2 范围内的信号才是被采样到的有效信号,因此得到的频谱肯定是关于 N/2 对称的(就是只看前一半的波形就好)。
第k点的实际频率的计算为 f(k) = k * (fs / n) — — (横轴的频率范围为 :f = n * fs / N;)
X[0] 为直流分量 ,幅值 = 模值(X[0]) / N
X[k] 为个点的频率分量(除X[0]外),幅值 = 模值(X[k]) / (N / 2)
1 参数定义
能够修改的参数,放在这个部分来定义。
代码片段:
%% 参数定义 %%%%%%%%%%%
% 音频文件参数
% source_FS = 16000; %原采样率(该值弃用,从文件读取)
dest_FS = 8000; %目标采样率
bps = 16; %位深 (查看帮助文档可以获取支持的 bps)
Tfile = 0; %声音片段的总时长s (0为不修改)
% 音频通道
stereo = 'mono'; % 输出音频通道 'mono':单声道,'stereo':立体声
audio_ch = 'left'; % 立体声输入,单声道输出时,选用哪一通道? 'left','right','both'
Left_channel_ratio = 0.5; % 左声道占比
% 文件
inputfile = ('connected_CN.mp3'); % 输入文件名
outputfile = [inputfile,'.wav']; % 输出文件名
AudioInfo = audioinfo(inputfile) % 打印输入音频文件的信息(不加分号)
%播放设置
isplay = 1; % 0:播放原音频 1:播放生成音频
2 原始音频文件读取
读入原始音频,记得修改对应的文件名,带后缀。如果选择播放原音频,那么读取完源文件后,会从电脑喇叭播放声音。
代码片段:
%% 原始音频文件读取 %%%%%%%%%%%
[y_input,source_FS]=audioread(inputfile); % 将 WAV 文件转换成变量
fs_str = ['(Fs:',num2str(source_FS/1000),'KHz)']; % fs 的字符串,在图上显示文件 fs
whos y_input % 打印y的信息
if isplay == 0 % 如果选择播放原音频
if source_FS < 192000 % 电脑小于 192K 采样率才能播放
sound(y_input,source_FS) % 调用可以播放声音的函数 sound()
end
end
3 打印输入波形图像
这一步将 audioread 读取出来的原音频数据,通过图像展示出来,原音频文件解码后,会得到一个列表,根据源文件的通道数n来区分,常用的通道是双通道,即2声道立体声,还有有其他的如:2.1声道、4声道、5.1声道、7.1声道等,对应的n会不同,此处我只对2声道的立体声做处理,有需要其他声道的,可以在此基础上,增加不同 n 值处理的方法。
代码片段
%% 打印输入波形图像 %%%%%%%%%%%
figure('Name','输入文件波形','NumberTitle','off');
[m,n] = size(y_input); % 计算文件的长度与声道数量
if n==1 % 单声道
y1 = y_input(:,1); % 提取声道数据
subplot(2,1,1);
plot(y1); % 描绘左声道图像
title(['单声道原始波形 ',fs_str]);
legend(fs_str); % 显示图例
subplot(2,1,2);
Y_fft = fft(y1,4096); % 计算 FFT
plot(abs(Y_fft)); % 描绘 FFT 图像
title('单声道原始波形频谱')
else % 立体声
y1 = y_input(:,1); % 提取左声道数据
y2 = y_input(:,2); % 提取右声道数据
subplot(2,2,1);
plot(y1); % 描绘左声道图像
title(['左声道原始波形 ',fs_str]);
legend(fs_str); % 显示图例
subplot(2,2,2);
plot(y2); % 描绘右声道图像
title(['右声道原始波形 ',fs_str]);
legend(fs_str); % 显示图例
subplot(2,2,3);
Y_fft1 = fft(y1,4096); % 左声道 FFT 计算
plot(abs(Y_fft1)); % 描绘左声道 FFT 图像
title('左声道原始波形频谱')
subplot(2,2,4);
Y_fft2 = fft(y2,4096); % 右声道 FFT 计算
plot(abs(Y_fft2)); % 描绘右声道 FFT 图像
title('右声道原始波形频谱')
end
4 输出参数设置
这一个步骤是根据 2.1 设置的参数来去对读取出来的声道列表进行计算,可以根据需求来这里增加对音频序列的操作,但是本文的目标是转码,不做音频处理,因此,此处不对音频数据进行过多的处理。
代码片段:
%% 输出参数设置 %%%%%%%%%%%
if strcmp(stereo,'mono') % 输出选项为单声道
if n == 1 % 如果输入文件是单声道文件
y_ratio = y1; % 那么输出文件的数据与输入文件的数据一致
else % 如果输入是立体声文件
if strcmp(audio_ch,'left') % 输出通道选择 左声道
y_ratio = y1; % 将左声道的数据作为输出数据
elseif strcmp(audio_ch,'right') % 输出通道选择 右声道
y_ratio = y2; % 将右声道的数据作为输出数据
else % 输出通道选择其他选项(both)
y_ratio = y1*Left_channel_ratio + y2*(1-Left_channel_ratio); % 就将左声道数据与右声道数据,按照设定的比例来混合。
end
end
else % 如果输出选项为立体声
if n == 1 % 输入文件是单声道
y_ratio(:,1) = y1; % 那么左右声道都用输入的数据
y_ratio(:,2) = y1;
else % 输入文件是立体声
y_ratio = y_input; % 那么输出文件的数据与输入文件的数据一致
end
end
5 生成输出音频并转码
这一步是本文的关键步骤,转码的核心就是调用 audiowrite() ,也是需要去学习使用的函数。
代码片段:
%% 生成输出音频并转码 %%%%%%%%%%%
y_out = resample(y_ratio,dest_FS,source_FS); % 对即将输出的数据,进行重采样到设定的采样率
audiowrite(outputfile,y_out,dest_FS,'BitsPerSample',bps); % 将输出数据存储为音频文件。
6 输出音频文件读取
这一步将刚才生成的音频文件再重新读入进来,用于检查格式转换后的效果。如果选择播放生成音频,那么读取完源文件后,会从电脑喇叭播放声音。
代码片段:
%% 输出音频文件读取 %%%%%%%%%%%
figure('Name','输出文件波形','NumberTitle','off'); % 打开第2个figure
[y_output,dest_FS]=audioread(outputfile); % 将上一步生成的文件读取出来保存为变量
fs_str = ['(Fs:',num2str(dest_FS/1000),'KHz)']; % 采样率字符串
if isplay == 1 % 如果选择播放生成音频
if dest_FS < 192000 % 电脑小于 192K 采样率才能播放
sound(y_output,dest_FS) % 调用可以播放声音的函数 sound()
end
end
7 打印输出波形图像
这一步将 audioread 读取出来的转码后的音频数据,通过图像展示出来,操作方式与原文件的读取并打印波形类似。
代码片段:
%% 打印输出波形图像 (跟打印输入波形图像类似,由于后续复用性较低,就不单独编写函数了) %%%%%%%%%%%
[m,n] = size(y_output); % 计算文件的长度与声道数量
if n==1 % 单声道
y1 = y_output(:,1); % 提取声道数据
subplot(2,1,1);
plot(y1); % 描绘左声道图像
title(['单声道生成波形 ',fs_str]);
legend(fs_str); % 显示图例
subplot(2,1,2);
Y_fft = fft(y1,4096); % 计算 FFT
plot(abs(Y_fft)); % 描绘 FFT 图像
title('单声道生成波形频谱');
else %立体声
y1 = y_output(:,1); % 提取左声道数据
y2 = y_output(:,2); % 提取右声道数据
subplot(2,2,1);
plot(y1); % 描绘左声道图像
title(['左声道生成波形 ',fs_str]);
legend(fs_str); % 显示图例
subplot(2,2,2);
plot(y2); % 描绘右声道图像
title(['右声道生成波形 ',fs_str]);
legend(fs_str); % 显示图例
subplot(2,2,3);
Y_fft1 = fft(y1,4096); % 左声道 FFT 计算
plot(abs(Y_fft1)); % 描绘左声道 FFT 图像
title('左声道生成波形频谱')
subplot(2,2,4);
Y_fft2 = fft(y2,4096); % 右声道 FFT 计算
plot(abs(Y_fft2)); % 描绘右声道 FFT 图像
title('右声道生成波形频谱')
end
3、运行图片
先准备两首歌,用于测试。分别将 .m4a 转成 .ogg,将 .flac 转成 .wav。
3.1 例:.m4a 转 ogg
“萨克斯 - 莫斯科郊外的晚上 - 纯音乐.m4a” 转 .ogg,且将44.1KHz 采样率转为 48KHz采样率。
由于上述代码中,是按照 wav 的格式来输出,audiowrite 中的 name-value 键值对对 ogg 无效,因此修改一下 audiowrite 函数的 name-value 键值对,按照帮助文档提供的设置,可以选择以下选项:
因此修改一下 audiowrite 为:
audiowrite(outputfile,y_out,dest_FS,'Quality',100); % 将输出数据存储为音频文件。
同时修改 inputfile 与 outputfile。
运行程序后,生成下面文件
3.2 例:.flac转 wav
按照老方法,修改一下 audiowrite 函数的 name-value 键值对,选用bps参数。
运行代码,
同样也生成了“张国荣-沉默是金.flac.wav”文件,显示区间显示,有需要可以通过这个地方来设置在这里插入图片描述
完整代码
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 功能:音频格式转换(任意格式互转) %
% 作者:Mr-Ma Technology(马健维) %
% 时间:2020.04.09 %
% 转载请注明出处 %
% https://blog.csdn.net/qq_29225913/article/details/105445028
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 支持的转换格式 %%%%%%%%%%%
% |-----------------------------------------------------|
% | 格式 | 输入 | 输出 |
% |-----------------------------------------------------|
% |WAVE (.wav) | 支持 | 支持 |
% |OGG (.ogg) | 支持 | 支持 |
% |FLAC (.flac) | 支持 | 支持 |
% |AU (.au) | 支持 | 不支持 |
% |AIFF(.aiff/.aif) | 支持 | 不支持 |
% |AIFC (.aifc) | 支持 | 不支持 |
% |MP3 (.mp3) (注意电脑版本) | 支持 | 不支持 |
% |MPEG-4 AAC(.m4a/.mp4)(注意电脑版本) | 支持 | 支持 |
% |-----------------------------------------------------|
% 更多详细信息,请参考 Matlab 相关描述(通道数、比特率、位深)
% audioread :https://ww2.mathworks.cn/help/matlab/ref/audioread.html
% audiowrite :https://ww2.mathworks.cn/help/matlab/ref/audiowrite.html?ue&s_tid=gn_loc_dropp
%% 清空内存里的变量 %%%%%%%%%%%
clear;
close all;
clc;
%% 参数定义 %%%%%%%%%%%
% 音频文件参数
% source_FS = 16000; %原采样率(该值弃用,从文件读取)
dest_FS = 48000; % 目标采样率
bps = 16; % 位深 -audiowrite 参数(适用于输出 .wav/.flac 文件)
qlt = 80; % Quality -audiowrite 参数(适用于输出 .ogg 文件)
br = 0; % BitRate -audiowrite 参数(适用于输出 .m4a/.mp4 文件)
% Tfile = 0; % 声音片段的总时长s (0为不修改) 暂时没用这个参数
% 音频通道
stereo = 'stereo'; % 输出音频通道 'mono':单声道,'stereo':立体声
audio_ch = 'left'; % 立体声输入,单声道输出时,选用哪一通道? 'left','right','both'
Left_channel_ratio = 0.5; % 左声道占比
% 文件
inputfile = ('张国荣-沉默是金.flac'); % 输入文件名
outputfile = [inputfile,'.wav']; % 输出文件名
AudioInfo = audioinfo(inputfile) % 打印输入音频文件的信息(不加分号)
%播放设置
isplay = 1; % 0:播放原音频 1:播放生成音频
% 绘图
isdraw = 1; % 1:打开绘图, 0:关闭绘图, 计算 FFT 需要消耗时间
t_axis = [-1 1]; % 时域图显示区域(纵坐标),按实际来改
f_axis = [0 20000 0 0.002]; % 频域图显示区域,按实际来改
%% 原始音频文件读取 %%%%%%%%%%%
[y_input,source_FS]=audioread(inputfile); % 将 WAV 文件转换成变量
fs_str = ['(Fs:',num2str(source_FS/1000),'KHz)']; % fs 的字符串,在图上显示文件 fs
whos y_input % 打印y的信息
if isplay == 0 % 如果选择播放原音频
if source_FS < 192000 % 电脑小于 192K 采样率才能播放
sound(y_input,source_FS) % 调用可以播放声音的函数 sound()
end
end
%% 打印输入波形图像 %%%%%%%%%%%
if isdraw == 1
figure('Name','输入文件波形','NumberTitle','off');
[N,n] = size(y_input); % 计算文件的长度与声道数量
if n==1 % 单声道
y1 = y_input(:,1); % 提取声道数据
subplot(2,1,1);
plot(y1); % 描绘左声道图像
axis([0 N t_axis]); % 根据实际情况,限制可显示区间
xlabel('采样点');ylabel('幅度');
title(['单声道原始波形 ',fs_str]);
subplot(2,1,2);
NFFT = source_FS * floor(N / source_FS); % FFT 点数取 fs 的倍数
Y_fft = abs(fft(y1,NFFT)); % 左声道 FFT 计算
A_FFT1 = Y_fft * 2 / NFFT; % 转换为幅值
A_FFT1(1) = 0; % 忽略直流分量的影响
f = (0:(NFFT/2)) * source_FS / NFFT; % 频率范围,横坐标(半边谱)
plot(f ,A_FFT1(1:NFFT/2 +1)); % 描绘 FFT 图像
axis(f_axis); % 根据实际情况,限制可显示区间
xlabel('频率/Hz');ylabel('振幅');
title('单声道原始波形频谱')
else % 立体声
y1 = y_input(:,1); % 提取左声道数据
y2 = y_input(:,2); % 提取右声道数据
subplot(2,2,1);
plot(y1); % 描绘左声道图像
axis([0 N t_axis]); % 根据实际情况,限制可显示区间
xlabel('采样点');ylabel('幅度');
title(['左声道原始波形 ',fs_str]);
subplot(2,2,2);
plot(y2); % 描绘右声道图像
axis([0 N t_axis]); % 根据实际情况,限制可显示区间
xlabel('采样点');ylabel('幅度');
title(['右声道原始波形 ',fs_str]);
subplot(2,2,3);
NFFT = source_FS * floor(N / source_FS); % FFT 点数取 fs 的倍数
Y_fft1 = abs(fft(y1,NFFT)); % 左声道 FFT 计算
A_FFT1 = Y_fft1 * 2 / NFFT; % 转换为幅值
A_FFT1(1) = 0; % 忽略直流分量的影响
f = (0:(NFFT/2)) * source_FS / NFFT; % 频率范围,横坐标(半边谱)
plot(f ,A_FFT1(1:NFFT/2 +1)); % 描绘 FFT 图像
axis(f_axis); % 根据实际情况,限制可显示区间
xlabel('频率/Hz');ylabel('振幅');
title('左声道原始波形频谱')
subplot(2,2,4);
NFFT = source_FS * floor(N / source_FS); % FFT 点数取 fs 的倍数
Y_fft2 = abs(fft(y2,NFFT)); % 左声道 FFT 计算
A_FFT2 = Y_fft2 * 2 / NFFT; % 转换为幅值
A_FFT2(1) = 0; % 忽略直流分量的影响
f = (0:(NFFT/2)) * source_FS / NFFT; % 频率范围,横坐标(半边谱)
plot(f ,A_FFT2(1:NFFT/2 +1)); % 描绘 FFT 图像
axis(f_axis); % 根据实际情况,限制可显示区间
xlabel('频率/Hz');ylabel('振幅');
title('右声道原始波形频谱')
end
end
%% 输出参数设置 %%%%%%%%%%%
if strcmp(stereo,'mono') % 输出选项为单声道
if n == 1 % 如果输入文件是单声道文件
y_ratio = y1; % 那么输出文件的数据与输入文件的数据一致
else % 如果输入是立体声文件
if strcmp(audio_ch,'left') % 输出通道选择 左声道
y_ratio = y1; % 将左声道的数据作为输出数据
elseif strcmp(audio_ch,'right') % 输出通道选择 右声道
y_ratio = y2; % 将右声道的数据作为输出数据
else % 输出通道选择其他选项(both)
y_ratio = y1*Left_channel_ratio + y2*(1-Left_channel_ratio); % 就将左声道数据与右声道数据,按照设定的比例来混合。
end
end
else % 如果输出选项为立体声
if n == 1 % 输入文件是单声道
y_ratio(:,1) = y1; % 那么左右声道都用输入的数据
y_ratio(:,2) = y1;
else % 输入文件是立体声
y_ratio = y_input; % 那么输出文件的数据与输入文件的数据一致
end
end
%% 生成输出音频并转码 %%%%%%%%%%%
y_out = resample(y_ratio,dest_FS,source_FS); % 对即将输出的数据,进行重采样到设定的采样率
audiowrite(outputfile,y_out,dest_FS,'BitsPerSample',bps); % 将输出数据存储为音频文件。
%audiowrite(outputfile,y_out,dest_FS,'Quality',qlt); % 将输出数据存储为音频文件。
%% 输出音频文件读取 %%%%%%%%%%%
[y_output,dest_FS]=audioread(outputfile); % 将上一步生成的文件读取出来保存为变量
fs_str = ['(Fs:',num2str(dest_FS/1000),'KHz)']; % 采样率字符串
if isplay == 1 % 如果选择播放生成音频
if dest_FS < 192000 % 电脑小于 192K 采样率才能播放
sound(y_output,dest_FS) % 调用可以播放声音的函数 sound()
end
end
%% 打印输出波形图像 (跟打印输入波形图像类似,由于后续复用性较低,就不单独编写函数了) %%%%%%%%%%%
if isdraw == 1
figure('Name','输出文件波形','NumberTitle','off'); % 打开第2个figure
[N,n] = size(y_output); % 计算文件的长度与声道数量
if n==1 % 单声道
y1 = y_output(:,1); % 提取声道数据
subplot(2,1,1);
plot(y1); % 描绘左声道图像
axis([0 N t_axis]); % 根据实际情况,限制可显示区间
xlabel('采样点');ylabel('幅度');
title(['单声道生成波形 ',fs_str]);
subplot(2,1,2);
NFFT = dest_FS * floor(N / dest_FS); % FFT 点数取 fs 的倍数
Y_fft = abs(fft(y1,NFFT)); % 左声道 FFT 计算
A_FFT1 = Y_fft * 2 / NFFT; % 转换为幅值
A_FFT1(1) = 0; % 忽略直流分量的影响
f = (0:(NFFT/2)) * dest_FS / NFFT; % 频率范围,横坐标(半边谱)
plot(f ,A_FFT1(1:NFFT/2 +1)); % 描绘 FFT 图像
axis(f_axis); % 根据实际情况,限制可显示区间
xlabel('频率/Hz');ylabel('振幅');
title('单声道生成波形频谱');
else % 立体声
y1 = y_output(:,1); % 提取左声道数据
y2 = y_output(:,2); % 提取右声道数据
subplot(2,2,1);
plot(y1); % 描绘左声道图像
axis([0 N t_axis]); % 根据实际情况,限制可显示区间
xlabel('采样点');ylabel('幅度');
title(['左声道生成波形 ',fs_str]);
subplot(2,2,2);
plot(y2); % 描绘右声道图像
axis([0 N t_axis]); % 根据实际情况,限制可显示区间
xlabel('采样点');ylabel('幅度');
title(['右声道生成波形 ',fs_str]);
subplot(2,2,3);
NFFT = dest_FS * floor(N / dest_FS); % FFT 点数取 fs 的倍数
Y_fft1 = abs(fft(y1,NFFT)); % 左声道 FFT 计算
A_FFT1 = Y_fft1 * 2 / NFFT; % 转换为幅值
A_FFT1(1) = 0; % 忽略直流分量的影响
f = (0:(NFFT/2)) * dest_FS / NFFT; % 频率范围,横坐标(半边谱)
plot(f ,A_FFT1(1:NFFT/2 +1)); % 描绘 FFT 图像
axis(f_axis); % 根据实际情况,限制可显示区间
xlabel('频率/Hz');ylabel('振幅');
title('左声道生成波形频谱')
subplot(2,2,4);
NFFT = dest_FS * floor(N / dest_FS); % FFT 点数取 fs 的倍数
Y_fft2 = abs(fft(y2,NFFT)); % 左声道 FFT 计算
A_FFT2 = Y_fft2 * 2 / NFFT; % 转换为幅值
A_FFT2(1) = 0; % 忽略直流分量的影响
f = (0:(NFFT/2)) * dest_FS / NFFT; % 频率范围,横坐标(半边谱)
plot(f ,A_FFT2(1:NFFT/2 +1)); % 描绘 FFT 图像
axis(f_axis); % 根据实际情况,限制可显示区间
xlabel('频率/Hz');ylabel('振幅');
title('右声道生成波形频谱')
end
end
————————————————
版权声明:本文为CSDN博主「Mr-Ma Technology」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_29225913/article/details/105445028