WAV文件格式的介绍:WAV文件是计算机中最常见的存放声音的格式,其扩展名为.wav。WAV文件以RIFF(Resource interchange file format)档案的格式存储,含有不定长度的文件头(header)和数据(data),组成不定长度的区块(chunk)和子区块(sub-chunks)。
WAV文件可以分为三个子区块(chunk):
分区名称 | 文件长度 |
---|---|
RIFF chunk | 12 bytes |
”fmt” sub-chunk | 可变长度, 16 bytes + extra |
”data” sub-chunk | 可变长度, size of sample data |
辅助函数:将wav文件中读取的16进制转为10进制。
% A表示wav文件数据
% start表示起始位
% total表示总位数
% return 得到的十进制数
function tmp = add_helper(A,start,total)
tmp = A(start+total-1);
for i=total-2:-1:0
tmp = tmp*16*16+A(start+i);
end
end
(1)采样频率:也称采样速度或采样率。定义了声音信号在“模拟-数字”转化过程中,单位时间内从连续信号中提取并组成离散信号的采样个数。它用赫兹(Hz)表示。
%法一:matlab的audioread函数返回的第二个参数即为采样频率
[~,fs] = audioread([path,file]);
%法二:直接读取wav文件的28位到25位四位数据即为采样频率
fs = add_helper(A,25,4);
(2)声道数:
%法一:matlab的audioread函数会将声道数转换为列数
channels_num = size(x,2);
%法二:直接读取wav文件的24位到23位即为声道数
channels_num = add_helper(A,23,2);%在第24和23位,24位为高位
(3)样本位数:也称采样位数,即对声音的辨析度。8位或16位,8位采样将取样信号分为256份;16位采样将取样信号分为65536份。
%法一:通过matlab的库函数audioinfo读取
info = audioinfo([path,file]);
bits_per_sample =info.BitsPerSample;
disp(['采样位数为',int2str(bits_per_sample)]);
%法二:直接读取wav文件的36位到35位两位即为样本位数
bits_per_sample = add_helper(A,35,2);
(4)求文件长:即音频文件所占用的内存空间,这里以KB为单位。
%法一:通过matlab的dir函数读取
D = dir([path,file]);
[file_length,file_length_type] = calculate_file_len(D.bytes);
fprintf('文件长为%.2f %s\n',file_length,file_length_type);
%法二:直接计算得到文件长度,简单进行单位换算
[file_length,file_length_type] = calculate_file_len(length(A));
fprintf('文件长为%.2f %s\n',file_length,file_length_type);
(5)求数据长:即WAV文件中,data子区块所包含的数据的长度,也以KB为单位。
%法一:乘以2的原因是在调用audioread函数时matlab自动将2Byte数据转化为了一个数据,实际数据长度是x长度的两倍
fprintf('数据长为%.2fKB\n',2*length(x)/1024);
%法二:直接读取WAV文件的78位到75位的四位数据即为数据部分的长度
data_length = add_helper(A,75,4);
[file_length,file_length_type] = calculate_file_len(data_length);
fprintf('数据长为%.2f %s\n',file_length,file_length_type);
(6)求数码率:表示单位时间(1秒)内传送的比特数bps(bit per second,位/秒),比特率越高质量越好,声音越饱满。码率=采样率×每次采样得到的样本位数×声道数。如果单位一般是k,码率(kbps)=码率(bps)/1000。
%波形数据传输速率(每秒平均字节数) = 采样频率 × 音频通道数 × 每次采样得到的样本位数 / 8
%比特率(kbs) = 波形数据传输速率 × 8 / 1000,除以1000是因为单位是k
kbps = fs * max_track_number * info.BitsPerSample / 8 * 8 / 1000;
fprintf('数码率为%.2f\n',kbps);
(7)求音频文件长度:音频持续的时间,可以通过数据长/采样率计算得到。
%法一:直接调用Matlab的Duration函数
fprintf('音频文件的长度为%.2f\n',info.Duration);
%直接计算,单声道数据长度除以采样率即为音频文件长度
info.music_length = roundn(info.real_length/info.fs,-2);
(8)显示李萨如图形:(8)将两个声道的数据分别加至示波器的Y轴输入端和x轴输入端,将出现一个合成图形,这个图形就是李沙育图形。
李沙育图形随两个输入信号的频率、相位、幅度不同,所呈现的波形也不同。当两个信号相位差为90°时,合成图形为正椭圆,此时若两个信号的振幅相同的话,合成图形为圆;当两个信号相位差为0°时,合成图形为直线,此时若两个信号振幅相同则为与x轴成45°的直线 。
%因为李萨如图形是将信号以左声道作为x轴数据,以右声道数据作为y轴数据,因此至少要双声道
if channels_num >= 2
figure('Name','李萨如图形','NumberTitle','off');
plot(x(:,1),x(:,2));
title('李萨图图形','FontSize',20);
end
(9)实现音频播放功能:
choice = input('请选择使用左声道1播放还是右声道2:');
%法一:通过audioplayer函数实现播放
player = audioplayer(x(:,choice),fs);
play(player)%该函数既可以实现播放
pause(player)%该函数既可以实现暂停播放
resume(player)%该函数可以实现暂停后恢复播放
stop(player)%该函数可以实现停止播放
%法二:通过sound函数实现播放
sound(x(choice),fs);
(10)绘制波形图:
%法一:matlab的audioread函数会将声道数转换为列数
channels_num = size(x,2);
figure('Name','波形图形','NumberTitle','off');
for i = 1:track_number
subplot(track_number,1,i);
%横坐标表示序号,纵坐标表示归一化的频率值
plot(x(:,i));
title(['\fontsize{16}第',int2str(i),'声道']);
end
具体代码:
代码一:这个函数基本是通过调用matlab库函数实现的。
可以实现播放.wav文件;
可以选择文件;
显示波形图(单声道,双声道);
在波形图上显示声道数、采样频率、样本位数、文件长、数据长、数码率及音频文件长度等参数;
显示李沙育波形图;
可以选择声道播放。
clc;
clear;
close all;
%这里请自己设置路径,这里是以打开文件夹自己选择文件的方式
[file,path] = uigetfile('语音素材/wav_file.wav','*.wav');
if isequal(file,0)
disp('没有选择文件')
else
disp(['选择了文件',file])
end
%fs为采样率
info = audioinfo([path,file]);
[x,fs] = audioread([path,file]);
%最大声道数
channels_num = size(x,2);
choice = input('请选择使用左声道1播放还是右声道2还是双声道3:');
if choice == 1
sound(x(:,1),fs);
elseif choice == 2
sound(x(:,2),fs);
else
sound(x(:,1:2),fs);
end
%选择声道数
track_number = input(['总声道数为',int2str(channels_num),'请输入需要的声道数:']);
while track_number>channels_num
disp(['超出范围',int2str(channels_num),'请重新输入']);
track_number = input('请输入需要的声道数:');
end
figure('Name','波形图形','NumberTitle','off');
for i = 1:track_number
subplot(track_number,1,i);
%横坐标表示序号,纵坐标表示归一化的频率值
plot(x(:,i));
title(['\fontsize{16}第',int2str(i),'声道']);
end
if channels_num >= 2
figure('Name','李萨如图形','NumberTitle','off');
plot(x(:,1),x(:,2));
title('李萨图图形','FontSize',20);
end
%%
%获取相关信息
disp(['采样率为',int2str(fs)]);
disp(['声道数为',int2str(channels_num)]);
%需要获取文件本身的内容
%typec = class(x);
%fprintf(['采样位数为',typec(1,4:end),'\n']);
disp(['采样位数为',int2str(info.BitsPerSample)]);
%fprintf('音频文件的长度为%.2f\n',size(x,1)/fs);
fprintf('音频文件的长度为%.2f\n',info.Duration);
D = dir([path,file]);
fprintf('文件长为%dKB\n',round(D.bytes/1024));
%乘以2的原因是length(x)并不是真正数据的长度因为一个采样是需要16位,占两个byte
%而wav文件中给出的也是占用的byte数,而非通过audioread函数将16位数据自动换算为float
fprintf('数据长为%.2fKB\n',4*length(x)/1024);
%波形数据传输速率(每秒平均字节数) = 采样频率 × 音频通道数 × 每次采样得到的样本位数 / 8
%比特率(kbs) = 波形数据传输速率 × 8 / 1000,除以1000是因为单位是k
kbps = fs * channels_num * info.BitsPerSample / 8 * 8 / 1000;
fprintf('数码率为%.2f\n',kbps);
代码二:代码二的bug是数据长度这里,因为不同的文件数据长度在wav文件中不一定是从75开始的4位数据。只是对当前文件适用,暂时没找到确定数据长度位置的方法。
clc;
clear;
close all;
%获得数据长度
filepath = 'speaking.wav';%这里请自己设置路径
fileID = fopen(filepath);
A = fread(fileID);
%声道数
info.channels_num = add_helper(A,23,2);%在第24和23位,24位为高位
fprintf('声道数为%d位\n',info.channels_num);
%采样频率
info.fs = add_helper(A,25,4);%从25位开始,持续4位
fprintf('采样频率为%d位\n',info.fs);
%取样位数
info.bits_per_sample = add_helper(A,35,2);
fprintf('取样位数为%d位\n',info.bits_per_sample);
%数据长度,因为这里有一个数据类型转换两个byte才被视为一个数据值
data_length = add_helper(A,75,4);
[info.data_length,info.data_length_type] = calculate_file_len(data_length);
fprintf('数据长为%.2f %s\n',info.data_length,info.data_length_type);
%文件长
[info.file_length,info.file_length_type] = calculate_file_len(length(A));
fprintf('文件长为%.2f %s\n',info.file_length,info.file_length_type);
%因为是将两字节数据视为1个数据,所以要除以2,因为只取一个声道故需要处理声道数
info.real_length = data_length/info.channels_num/2;
%音频长度
info.music_length = roundn(info.real_length/info.fs,-2);
fprintf('音频文件长度为%.2f s\n',info.music_length);
%与采样位数有关,多少Byte合成一位
val = info.bits_per_sample/8;
%左声道数据
left_wav = zeros(info.real_length/2/val,1);
%右声道数据
right_wav = zeros(info.real_length/2/val,1);
for i = 0:info.real_length-1
left_wav(i+1) = add_helper(A,79+i*4,2);
right_wav(i+1) = add_helper(A,79+i*4+val,2);
end
[x,fs] = audioread(filepath,'double');
% figure;
% subplot(2,1,1)
% plot(left_wav);
% subplot(2,1,2)
% plot(right_wav);
Hex_A = dec2hex(A);
% A表示wav文件数据
% start表示起始位
% total表示总位数
% return 得到的十进制数据
function tmp = add_helper(A,start,total)
tmp = A(start+total-1);
for i=total-2:-1:0
tmp = tmp*16*16+A(start+i);
end
end
% A表示wav文件数据
% return file_length表示文件长度
% return len_type表示长度对应类型,有B,KB,MB,GB
function [len,len_type]=calculate_file_len(native_A)
len = native_A;
len_index = 1;
len_list = {'B','KB','MB','GB'};
while len >= 1024
len = len/1024;
len_index = len_index+1;
end
%保留两位小数
len = roundn(len,-2);
len_type = len_list{len_index};
end
代码三:
gui格式较为完整。
可以实现的功能如下:
1.数据部分显示音频文件的各项数据。
2.其他选项部分可以选择声道显示波形图异或是李萨如图形。
3.播放选项部分可以跟随动态显示图像。
已上传到我的资源。
有不足之处欢迎指出。