音乐信号处理基础

目录

  • 使用雅马哈 VST 3 SDK 自行调用音色库
  • 用C语言导入\生成WAV格式音频文件
    • 初始化portsf库
    • 写入新文件
    • 设置文件格式
    • 关闭声音文件(并通过peak chunk功能获取样本峰值)
  • 使用python实现音乐播放、可视化和下载
    • 播放
    • 可视化
      • 谱质心
      • 时域波形
      • 截取一部分并求过零点
      • 滚降(系数)
      • 梅尔倒频谱系数
    • 使用tkinter构建网易云下载小程序
  • 使用matlab分析参数
    • 过零点
    • 全(频)带低(频)带能量比
    • 归一化自协方差
    • 预相位能量比

使用雅马哈 VST 3 SDK 自行调用音色库

获取雅马哈旗下steinberg公司vst(2/3)插件开发工具包:(c++)
https://www.steinberg.net/en/company/developers.html
https://sdk.steinberg.net/index.php?sid=4e40f7ce3449dd801cbefa6dcb02a2e6
由于很少有人用Linux制作音乐,大部分音源插件不支持Linux,只能用Windows版本。SDK框架归纳为处理器和编辑器两部分,两者完全分离。这给编曲中自动化编写、远程控制提供方便。
类Steinberg::Vst::IComponent 和 Steinberg::Vst::IEditController 均从基类Steinberg::IPluginBase继承而来。…:IPluginBase传入参数Steinberg::Vst::IHostApplication。例如

tresult result = factory->createInstance(ID, Vst::IComponent::iid, (void**)&processorComponent);
if (processorComponent && (result == kResultOk))
{
	// 初始化
	res = (processorComponent->initialize(gStandardPluginContext) == kResultOk);
	// 若没有成功分离出编辑控制器
	if (processorComponent->queryInterface(Vst::IEditController::iid, (void**)&editController) != kResultTrue)
	{
		// 释放编辑器
		FUID controllerCID;
		// 寻找关联classID
		if (processorComponent->getControllerClassId(controllerCID) == kResultTrue && controllerCID.isValid())
		{
			// 创建编辑器部分
			result = factory->createInstance(controllerCID, Vst::IEditController::iid, (void**)&editController);
			if (editController && (result == kResultOk))
			{
				//  初始化
				res = (editController->initialize(gStandardPluginContext) == kResultOk);

				// 成功
			}
		}
	}
}

音乐信号处理基础_第1张图片
处理器构架如图所示:
音乐信号处理基础_第2张图片
使用VST插件协议调用juicy音源,如图所示:(代码过多,不贴)
音乐信号处理基础_第3张图片

用C语言导入\生成WAV格式音频文件

在python中十分简单直接导入pyaudio或pygame就完了,但在C中就不这么容易。

导入二进制音频文件,不仅需要提供采样率、采样类型、轨道数,还要提供样本自己的排列顺序(读取顺序)。WAV,AIFF,MP3等格式在文件头描述,程序以此获得文件读取方式。对没有任何标识的音频文件,设计程序来读取它是十分困难的。C语言程序库中已有向原始二进制音频文件写入读取信息的代码包,可直接使用。下面的程序写出简单的波形文件。
该外部文件暂时使用仅能读取WAV AIFF文件格式的文件。在GitHub中寻找ieee80.c,并根据提示更正几个指针错误直至VS不报错,如图所示。
音乐信号处理基础_第4张图片
音乐信号处理基础_第5张图片
在portsf.h文件中,使用enum定义音频声道的若干属性:

typedef enum { STDWAVE, MC_STD, MC_MONO, MC_STEREO, MC_QUAD,
	MC_LCRS, MC_BFMT, MC_DOLBY_5_1, MC_WAVE_EX }
psf_channelformat;

定义文件读取格式的若干方式和采样类型:

typedef enum {
	PSF_FMT_UNKNOWN = 0,
	PSF_STDWAVE,
	PSF_WAVE_EX,
	PSF_AIFF,
	PSF_AIFC
} psf_format;
typedef enum {
	PSF_SAMP_UNKNOWN = 0,
	PSF_SAMP_8, //还没支持
	PSF_SAMP_16,
	PSF_SAMP_24,
	PSF_SAMP_32,
	PSF_SAMP_IEEE_FLOAT
} psf_stype;

以上若干属性被添加到portsf.h头文件中,定义为PSF属性(把上面的合起来)。

typedef struct psf_props
{
	long 		srate;
	long 		chans;
	psf_stype 	samptype;
	psf_format 	format;
	psf_channelformat chformat;
} PSF_PROPS;

虽然面向过程,但也可认为这是封装了一个API。在PSF_PROPS中只有5个元素。不需要知道音频文件的很多信息,就能编辑它。
上述枚举类信息归纳如下:

对于PSF_PROPS中部分元素的解释:
srate(Sample Rate),采样率:1秒中的样本点数。大多数音频系统使用22050(广播),44100(日常大多数),48000,96000,192000(专业)Hz的采样率。

chans(channels),声道数:1叫单声道(听力收音机),2个叫立体声(绝大多数),6个叫杜比(Dolby)5.1(部分电影院)。在多声道系统中,定义FrameRate,比如上图就是2。

samptype:(sample type),采样类型:理解成每个样本点所占的字节空间。8 bits现在使用yue;16 bits对应C中的short类型;32 bits float对应float类型,32-bit integer对应C中长整型。此外还有64 bits,64 bits float等,在此不支持,所以枚举里没有写。而WAVEFORMATENSIBLE用来指示每个存储单位是否有空余空间,例如24 bits是在4个字节里存储3个字节的数据,另一个空着。这样的现在几乎没有了。第一个变量PSF_SAMP_UNKNOWN指示音频文件的采样类型是否已知。

chformat:(channel format,声道格式)上面代码中给出了几个常用的声道格式。仅当psf_format是PSF_WAVE_EX(WAVEFORMATEXTENSIBLE)(波形可扩展)时,chformat才有选择;否则,chformat将采用STDWAVE(standard wave)格式。在chformat中,MC_BFMT是新近流行的高级编码格式,用于虚拟现实设备的声音定位仿真, 采用Ambisonic的B格式信号,不采用一个喇叭给一组样本,而是给每个声音成分一个三维坐标(使用特殊的麦克风收音获得),创建球状声场,从柱坐标看包括水平定位、距离和高度信息。一个喇叭一组样本可以直接传送给麦克风,而这种需要解码再传。至少8个喇叭才能回放此编码声音定位的全部信息。

初始化portsf库

这个库有一个初始化函数。portsf对读入的声音文件是按块读的,它内部维持着一个音频块,块的末尾规定了一个固定的读取长度,默认是64.psf_init保证这个块在初始化时是空的。类似于C++的构造和析构,在程序的最后应该调用一个psf_finish函数。psf_finish关闭portsf打开的所有声音文件,返回初始时的空状态。

#include "ieee80.h"
#include "portsf.h"
int psf_init(void);
int psf_finish(void);
#include "ieee80.h"
#include "portsf.h"
#include
#include

#define _CRT_SECURE_NO_WARNINGS
int main(void)
{
	PSF_PROPS props;
	int sf;
	sf = psf_sndOpen("original.wav", &props, 0);
	if (sf < 0) {
		printf("错误:不能打开文件\n");
		system("pause");
		return 1;
	}
	printf("采样率:%d\n", props.srate);
	printf("通道数:%d\n", props.chans);
	system("pause");
	return 0;
}

其中PSF是 Portable Sound Format,可移植声音格式。http://www.vgmpf.com/Wiki/index.php?title=PSF

如图,先后出现许多错误。一个个纠正:

音乐信号处理基础_第6张图片
音乐信号处理基础_第7张图片
音乐信号处理基础_第8张图片
修正到可以运行时,发现显示
音乐信号处理基础_第9张图片
摘取psf_sndOpen上半段:

int psf_sndOpen(const char *path, PSF_PROPS *props, int rescale)
{
	int i, rc = 0;
	PSFFILE *sfdat;
	psf_format fmt;
	char *fname = NULL;

	/* RWD interesting syntax issue: I need the curlies, or break doesn't work properly */
	for (i = 0; i < psf_maxfiles; i++) {
		if (psf_files[i] == NULL)
			break;
	}
	if (i == psf_maxfiles) {
		return PSF_E_TOOMANYFILES;
	}

	sfdat = psf_newFile(NULL);
	if (sfdat == NULL) {
		return PSF_E_NOMEM;
	}
	sfdat->rescale = rescale;
	sfdat->is_little_endian = byte_order();
	fmt = psf_getFormatExt(path);
	if (!(fmt == PSF_STDWAVE || fmt == PSF_WAVE_EX || fmt == PSF_AIFF || fmt == PSF_AIFC))
		return PSF_E_BADARG;

	if ((sfdat->file = fopen(path, "rb")) == NULL) {
		DBGFPRINTF((stderr, "psf_sndOpen: cannot open '%s'\n", path));
		return PSF_E_CANT_OPEN;
	}
	sfdat->filename = (char *)malloc(strlen(path) + 1);
	if (sfdat->filename == NULL) {
		return PSF_E_NOMEM;
	}
	strcpy(sfdat->filename, path);
	sfdat->isRead = 1;
	sfdat->nFrames = 0;
	/* no need to calc header sizes */
	switch (fmt) {
	case(PSF_STDWAVE):
	case(PSF_WAVE_EX):
		rc = wavReadHeader(sfdat);
		break;
	case(PSF_AIFF):
		/* some .aiff files may actually be aifc - esp if floats! */
	case(PSF_AIFC):
		rc = aiffReadHeader(sfdat);
		/* try AIFC if AIFF fails */
		if (rc < PSF_E_NOERROR) {
			rewind(sfdat->file);
			rc = aifcReadHeader(sfdat);
		}
		break;
	default:
		DBGFPRINTF((stderr, "psf_sndOpen: unsupported file format\n"));
		rc = PSF_E_UNSUPPORTED;
	}

可以看出,返回的错误标识符是PSF_E_CANT_OPEN,((sfdat->file = fopen(path, “rb”)) == NULL),是路径找不出来,fopen不允许用外部指针链接。添加音频路径就解决了问题。
音乐信号处理基础_第10张图片
同理地,可以获取采样方式(samptype)等。props就是指properties。

	if (props.samptype == PSF_SAMP_8)
		printf("sample type: 8bit\n");
	else if (props.samptype == PSF_SAMP_16)
		printf("sample type: 16bit\n");

音乐信号处理基础_第11张图片

写入新文件

函数原型是int psf_sndCreate(const char * path, const PSF_PROPS * props,int clip_floats, int minheader, int mode
const char *path, const PSF_PROPS props与sndOpen中的用法相同,只不过这里sndOpen加了一个const声明,表示音频总体属性写入文件中不可改变。
clip_floats用于确定浮点数按照何种方式写入文件,是不是要同时放大/缩小一倍数再写,与CSS中clip属性类似。有些样本点可能超出读取范围,这就要归一化。音频头文件中要有峰值(peak chunk)信息,但很多应用中的音频模块很可能没有这方面信息(读取头文件的时候就掠过去了),把clip_floats设成1可能比较常见,当然对于峰值小的文件也可以设高一点。
minheader:通常设为0,对源自UNIX系统的一些无法读取概要信息的文件,把值设为1,就只生成按要求格式的数据块。一般的应用设成0.
mode:提供一些读写控制,在portsf.h的psf_create_mode中定义成这样:

typedef enum {PSF_CREATE_RDWR,PSF_CREATE_TEMPORARY,PSF_CREATE_WRONLY}psf_create_mode

目前这个portsf库mode只支持PSF_CREATE_RDWR。其他modes以备特殊情况扩展postsf的功能用。

#include "ieee80.h"
#include "portsf.h"
#include
#include

#define _CRT_SECURE_NO_WARNINGS
int readfile(void)
{
	PSF_PROPS props;
	int sf;
	sf = psf_sndOpen("example.wav", &props, 0);
	if (sf < 0) {
		printf("错误:不能打开文件\n");
		system("pause");
		return 1;
	}
	printf("采样率:%d\n", props.srate);
	printf("通道数:%d\n", props.chans);
	if (props.samptype == PSF_SAMP_8)
		printf("sample type: 8bit\n");
	else if (props.samptype == PSF_SAMP_16)
		printf("sample type: 16bit\n");
	system("pause");
	return 0;
}

这就成功创建了一文件。
音乐信号处理基础_第12张图片

设置文件格式

例如,将Windows或安卓系统音频文件转移到Mac上,需要把格式从wav转化成aiff。通过命令

psf_format format;
format=psf_getFormatExt("soundtrack.wav")

getFormatExt就是提取字符串并分类的程序。
音乐信号处理基础_第13张图片

执行这个函数就可以了。它返回psf_format类型文件,可以直接将原始二进制音频文件分配到合适的PSF_PROPS结构。

props.format=format;

如过.wav头信息无法辨认,则返回PSF_PMT_UNKNOWN类型。

关闭声音文件(并通过peak chunk功能获取样本峰值)

int psf_sndClose(int afd),返回值是读取失败对应的错误类型。

int psf_sndClose(int sfd);

typedef struct psf_chpeak {
float val;
unsigned long pos;
} PSF_CHPEAK;//portable sound format channel peak
long psf_sndReadPeaks(int sfd, PSF_CHPEAK peakdata[], long *peaktime);

使用python实现音乐播放、可视化和下载

播放

python用于音乐处理的librosa库已经给好了大多数函数。

import librosa
import sklearn
audio_path = 'D:\大二下\jupyter\WANNABE_ITZY.wav'

x,sr = librosa.load(audio_path, sr=44100)

import IPython.display as ipd
ipd.Audio(audio_path)

音乐信号处理基础_第14张图片
用上述图标可直接播放。

可视化

谱质心

谱质心是基于能量分布的频率一阶矩,理解成某时刻频率重心。
S C = ∑ n = 1 N f ( n ) ⋅ E ( n ) ∑ 1 N E ( n ) = ∑ n = 1 N f ( n ) ⋅ P ( E ( n ) ) \mathrm{SC}=\frac{\sum_{n=1}^{N} f(n) \cdot E(n)}{\sum_{1}^{N} E(n)}=\sum_{n=1}^{N} f(n) \cdot P(E(n)) SC=1NE(n)n=1Nf(n)E(n)=n=1Nf(n)P(E(n))f(n)是短时傅里叶变换所得频率,P(E(n))是所得频率的倒数。

# 寻找谱质心
spectral_centroids = librosa.feature.spectral_centroid(x, sr=sr)[0]
spectral_centroids.shape
(775,)
# 时间变量
frames = range(len(spectral_centroids))
t = librosa.frames_to_time(frames)
# 归一化谱质心
def normalize(x, axis=0):
    return sklearn.preprocessing.minmax_scale(x, axis=axis)
# 绘制图像
librosa.display.waveplot(x, sr=sr, alpha=0.4)
plt.plot(t, normalize(spectral_centroids), color='r')

音乐信号处理基础_第15张图片

时域波形

import librosa.display
import matplotlib.pyplot as plt
x,sr = librosa.load('D:\大二下学习文件\jupyter_deepLearning\example.wav')
#plot the signal
plt.figure(figsize = (14,5))
librosa.display.waveplot(x,sr = sr)

音乐信号处理基础_第16张图片

截取一部分并求过零点

n0= 57000
n1 = 57100
plt.figure(figsize = (14,5))
plt.plot(x[n0:n1])
plt.grid()

zero_crossings = librosa.zero_crossings(x[n0:n1],pad = False)
print(sum(zero_crossings))

音乐信号处理基础_第17张图片
音乐信号处理基础_第18张图片

滚降(系数)

python已有封装。
α = f Δ f N \alpha=\frac{f_\Delta}{f_N} α=fNfΔ分子是超出奈奎斯特采样频率的带宽扩展量,分母是奈奎斯特带宽(从下图可见,鼓点处、清音处的滚降大,动态平稳、频率均衡处小)

spectral_rolloff = librosa.feature.spectral_rolloff(x+0.01,sr = sr)[0]
librosa.display.waveplot(x,sr = sr, alpha = 0.4)
plt.plot(t,normalize(spectral_rolloff),color =  'r')

音乐信号处理基础_第19张图片

梅尔倒频谱系数

之前已有讲述。 c x [ n ] = 1 M ∑ m = 1 M Y [ m ] cos ⁡ ( π n ( m − 1 / 2 ) M ) c_{x}[n]=\frac{1}{M} \sum_{m=1}^{M} Y[m] \cos \left(\frac{\pi n(m-1 / 2)}{M}\right) cx[n]=M1m=1MY[m]cos(Mπn(m1/2))

x,fs = librosa.load('D:\大二下学习文件\jupyter_deepLearning\example.wav')
librosa.display.waveplot(x,sr = sr)

mfccs = librosa.feature.mfcc(x,sr = fs)
print (mfccs.shape)
librosa.display.specshow(mfccs,sr = sr, x_axis = 'time')

音乐信号处理基础_第20张图片

使用tkinter构建网易云下载小程序

网易云非付费内容爬取器:驱动Edge浏览器(自己写驱动会更高端)进入网易云搜索相应界面,爬取列表中第一个音频地址、载入音频并存入相应文件夹中。这里给出一个最简单的爬虫程序和一个简单的tkinter GUI编程。
注意,要先在网易云音乐网页中将第一个对应音频链接的位置定位:
音乐信号处理基础_第21张图片
以上定位可通过如下方式获得(定位器):

    req = driver.find_element_by_id('m-search')
    a_id = req.find_element_by_xpath('.//div[@class = "item f-cb h-flag  "]/div[2]//a').get_attribute("href")

在XML语言中寻找链接路径的方法可参见find_element_by_xpath
创建目录参见makedirs

这里的GUI需要tkinter添加文本。用text控件insert(插入文本)、see(滚动)、update(更新)等方法显示正在下载和已下载图样;在get_music_name函数中,首先从输入窗口获取名称,然后调用Edge驱动访问网易云音乐主页,通过'http://music.163.com/song/media/outer/url?id={}.mp3'.format(song_id)搜到歌曲,通过上述定位器找到歌曲地址和歌名。注意到第一个函数传入的应该是字典类型(有了这种语句:song_id = item['song_id']),那就创建一个字典后在函数体内调用song_load实现下载。在这之前,驱动就完成了任务,所以可以关闭驱动。
至于Tkinter的控件内容,应该根据实际情况试错和设计,界面编程相对还是比较简单的。(分别创建标签控件、输入框、列表框、按钮,并依次确定它们在主界面中的位置)

from tkinter import *
from selenium import webdriver
global entry
import os
from urllib.request import urlretrieve
#2.下载歌曲
def song_load(item):


    song_id = item['song_id']
    song_name = item['song_name']

    song_url = 'http://music.163.com/song/media/outer/url?id={}.mp3'.format(song_id)

    #创建文件夹
    os.makedirs('music_netease',exist_ok=True)
    path = 'music_netease\{}.mp3'.format(song_name)

    #显示数据到文本框
    text.insert(END,'歌曲:{},正在下载...'.format(song_name))
    #文本框滚动
    text.see(END)
    #更新
    text.update()
    #下载
    urlretrieve(song_url,path)
    #显示数据到文本框
    text.insert(END,'歌曲:{},下载完毕'.format(song_name))
    #文本框滚动
    text.see(END)
    #更新
    text.update()
#搜索
def get_music_name():
    #获取输入的内容
    name = entry.get()

    url = 'https://music.163.com/#/search/m/?s={}&type=1'.format(name)
    #搜索页面

    option = webdriver.EdgeOptions()
    option.add_argument('--headless')
    #driver = webdriver.Edge(edge_options=option)

    driver = webdriver.Edge('D:\\python\\msedgedriver')

    driver.get(url)
    driver.switch_to.frame('g_iframe')
    #获取歌曲的id
    req = driver.find_element_by_id('m-search')
    a_id = req.find_element_by_xpath('.//div[@class = "item f-cb h-flag  "]/div[2]//a').get_attribute("href")

    song_id = a_id.split('=')[-1]
    print(song_id)

    song_name = req.find_element_by_xpath('.//div[@class="item f-cb h-flag  "]/div[2]//b').get_attribute("title")
    print(song_name)
    #构造字典
    item = {}
    item['song_id'] = song_id
    item['song_name'] = song_name
    driver.quit()
    #下载歌曲
    song_load(item)

#get_music_name()


#形象工程
# 搭建界面
#创建画板
root = Tk()

#标题
root.title('网易云下载器')
#设置窗口大小
root.geometry('560x450+400+200')
#标签控件
label = Label(root,text = '输入要下载的歌曲:',font = ('华文行楷',20))
#标签定位
label.grid()
#输入框
entry = Entry(root,font = ('楷书',20))
#定位
entry.grid(row = 0,column = 1)
#列表框
text = Listbox(root,font = ('隶书',16),width = 50, heigh = 15)
text.grid(row = 1,columnspan = 2)
#点击按钮
button = Button(root,text = '开始下载',font = ('楷书',15),command=get_music_name)
button.grid(row=2, column=0,sticky=W)

button1 = Button(root,text = '退出程序',font = ('楷书',15),command=root.quit)
button1.grid(row=2, column=1,sticky=E)
#显示当前的界面内容
root.mainloop()

运行效果
音乐信号处理基础_第22张图片

进入music_netease文件夹,说明下载成功。
音乐信号处理基础_第23张图片

使用matlab分析参数

过零点

首先读入文件并画图。读取前8000个样本点。

>> a=fread(fp,8000);
>> plot(a)
>> fp=fopen('春天的脚步近了.m4a');
fseek(fp,60244,-1);
>> a=fread(fp,8000);
>> plot(a)

音乐信号处理基础_第24张图片
像刚才在python中做的一样,计算零交叉点的数目。8000稍多,这里不妨取第5000到第7000的样本点处理。(已事先在音频软件中观测到5000到7000的样本是在清音阶段)

figure;
for i=5000:5199
    if (a(i)>128) && (a(i+1)<128)
         x=x+1;
    else x=x;
    end
end
disp('number of zero crossing='); disp(x);

在这里插入图片描述

再在音频软件中定位到春天an处,(浊音)作出这附近的曲线:

fseek(fp,65042,-1);
b=fread(fp,2000);
plot(b);

音乐信号处理基础_第25张图片

用相同的方法,得到零交叉点数目,得到只有上段的一半。
在这里插入图片描述

再在音频软件中定位到脚步的j处(清音),作出这附近的曲线:

fseek(fp,6724,-1);
c=fread(fp,200);
plot(c);

音乐信号处理基础_第26张图片
可以看出,它的振幅变化幅度较浊音更大。

全(频)带低(频)带能量比

这个不用解释也比较清楚。显然浊音部分这个比大点,而清音部分这个比小点。计算低频带的界限是1000Hz。下面的程序按照惯常做法,取帧,(100个样本点一帧),共800个,

clear all;
fp=fopen('狗血.wav');
fseek(fp,44,-1);
a=fread(fp);
a=a-128;

for j =1:800
    fseek(fp,44+100*j,-1);
    a=fread(fp,100);
    a=a-128;
    for i =1:50
        b=abs(fft(a));
    end
    sum(j)=0;
    for i = 1:12
        sum(j)=sum(j)+b(i);
    end
    sum1(j)=0;
    for i =1:50
        if sum1(j)==0
            sum1(j)=1;
        end
    end
    s(j)=sum(j)/sum1(j);
end
plot(s);
title('低频和全频带的比');
xlabel('片段序号');
ylabel('比值');

这几个字是“别堆砌怀念让剧情变得狗血”。这几个字发音较典型,有“情”“砌””血’“别”“变”等典型字,往后这几个字可以用来验证更多的分析,这里用它分析清音和浊音的分辨也比较好的选择。片段8000左右的高峰是情字,片段12000左右的高峰是血字,200,1500一带堆着“别堆砌”三个字。
音乐信号处理基础_第27张图片

归一化自协方差

C 1 = ∑ n − 1 N S ( n ) S ( n − 1 ) ∑ n − 1 N S ( n ) ∑ n − 0 N − 1 S 2 ( n ) C_1=\frac{\sum_{n-1}^{N}S(n)S(n-1)}{\sqrt{\sum_{n-1}^N S^(n)\sum _{n-0}^{N-1}S^2(n)}} C1=n1NS(n)n0N1S2(n) n1NS(n)S(n1)
这个量规定了相邻音乐/语音片段的相关性。讲人话,C1指这时求的相邻单位时间延迟的自协方差。对于浊音,有大量低频信号能量,在浊音附近是的信号高度自相关的,这个值很大(讲人话,低频信号说明信号的乱起八糟的高频谐波占比比较少,信号比较“单纯”,相互的依赖性比较强);而在清音区域自协方差就很小,在寂静区域会更小(讲人话,就是噪声基本上无法预测、是独立产生的)。与之类似,在语音信号中通常再引入一个叫谱倾斜的东西,定义成
∑ i = 1 N s ( i ) s ( i − 1 ) ∑ i = 1 s 2 ( i ) \frac{\sum_{i=1}^{N}s(i)s(i-1)}{\sum_{i=1}s^2(i)} i=1s2(i)i=1Ns(i)s(i1)
可以用matlab对“春天来了”做差分,求出它的谱倾斜度(spectrum tilt)(当然,需要事先录制"春天来了"4字)

>>  fp=fopen('录音 (5).m4a');
%fseek(fp,800,-1);
a=fread(fp,400,'short');
a=a-128;
subplot(2,1,1);plot(a);
xlabel('sample no.');ylabel('amplitude');
for j=1:200,
	 fseek(fp,4*j,-1);
	 a=fread(fp,100);
	 a=a-128;
	 sum(j)=0;
	 for i=2:100,
	 	sum(j)=sum(j)+(a(i)*a(i-1));
	 end
	sum1(j)=0;
	for i=2:100,
		 if a(i)==0,
		 	a(i)=0.1;
		 end
	 sum1(j)=sum1(j)+a(i)*a(i);

	end
	s(j)=sum(j)/sum1(j);
end
subplot(2,1,2);plot(s);

得到的是下图。
音乐信号处理基础_第28张图片

可以看出,由于我说的比较波澜不惊,波形很规整的了。音频信号的自相关系数(谱倾斜度和自相关函数实际上差不多是同一种量度)在浊音处是十分高的;由于春和天是连在一起的,所以得到的相关性都比较大(接近1);而后来说来字的时候,音调和共振峰都发生了比较强烈的改变,所以它的相关性急剧下降。

预相位能量比

可以求归一化的预相位能量比区分浊音和清音片段。对于浊音,相邻样本之间的变化值很小,而对于清音部分这个分子很大。 P r = ∑ i = 1 N ∣ s ( i ) − s ( i − 1 ) ∣ ∑ i = 1 s 2 ( i ) Pr=\frac{\sum_{i=1}^{N}|s(i)-s(i-1)|}{\sum_{i=1}s^2(i)} Pr=i=1s2(i)i=1Ns(i)s(i1)

求所谓预相位能量的程序如下:
音乐信号处理基础_第29张图片

clear all;
fp = fopen('狗血.wav');
[p,fs]=audioread('狗血.wav');
fseek(fp,44,-1);
a=fread(fp);
a=a-128;
subplot(2,1,1);
plot(p);
title('plot of 狗血');
xlabel('sample no.');
ylabel('amplitude');
for j =1:12250
    fseek(fp,44+100*j,-1);
    a=fread(fp,100);
    a=a-128;
    sum(j)=0;
    for i=2:100
        sum(j)=sum(j)+abs(a(i)-a(i-1));
    end
    sum1(j)=0;
    for i =2:100
        if a(i)==0
            a(i)=0.1;
        end
        sum1(j)=sum1(j)+a(i)*a(i);
    end
    s(j)=sum(j)/sum1(j);
end
subplot(2,1,2);
plot(s);
title('预相位能量比');
xlabel('segment number');
ylabel('预相位能量比');

音乐信号处理基础_第30张图片
上面不怎么好看,下面直接把节奏整出来比对一下
音乐信号处理基础_第31张图片

样本点过多,这样对应着看验证得不是很成功,只在静默时这个比有明显下降,在有声阶段并不能很好表征清音和浊音的分化。

你可能感兴趣的:(笔记)