wav音频文件的提取和分析

wav音频文件的提取和分析,第1张

WAV文件格式的介绍:WAV文件是计算机中最常见的存放声音的格式,其扩展名为.wav。WAV文件以RIFF(Resource interchange file format)档案的格式存储,含有不定长度的文件头(header)和数据(data),组成不定长度的区块(chunk)和子区块(sub-chunks)。
WAV文件可以分为三个子区块(chunk):

分区名称文件长度
RIFF chunk12 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.播放选项部分可以跟随动态显示图像。


已上传到我的资源。
有不足之处欢迎指出。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/925527.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-16
下一篇 2022-05-16

发表评论

登录后才能评论

评论列表(0条)

保存