声音辨识系统专题报告(转载)

来源:互联网 发布:mac口红whirl玫瑰棕 编辑:程序博客网 时间:2024/06/11 16:17
一, 指导教授与作者群简介
指导教授:游国忠老师
游国忠老师目前担任真理大学专任老师,手边正
进行国科会计划.从中央大学博士班毕业後,就被前
资科系主任张志勇老师聘请来真理教书.老师曾说
过:「该学的专业知识是基本,那是该会的东西,大家
学的都一样,但是外文真的要非常好,这很重要,对
现在的台湾来说,外文能力要很棒才能赢过其他人」.
游老师所学的领域是影像处理,图形辨识,无线网路
方面.
作者群:资科系四A 彭文玉—我本身是一个转学生,在进入大学前是一个
五专工科的学生,读的是电子,学习的方向比较偏向硬体及实务操作方面.进入
大学後,因学习的内容和电子相差不远,只是比较偏重於理论和软体应用上的学
习,所以学习起来比较没有压力.
资科系四B 陈韵曲—二年级时从工管系转来资科系
资科系四B 陈慧琦—大学联考选填志愿进入资科系
二, 作者序:
一开始我们研究Midi中加入浮水印的可行性,发现Midi标头档可藏浮水印
的空间太小,因此之後便转往语音(wav)辨识方面的研究.
在人类声音辨识方面,由於人的声音变化多,有时候即使是同一个人的声音
耳朵也不一定分辨出来,人自己虽然可以分辨不同人的声音,但电脑是透过计算
分析出特徵值,之後才能分辨声音,所以在辨识能力上自然不如人的耳朵来得细
腻.
输入一个声音时,我们可以利用一些数学运算将这个声音处理,然後与资
料库中已建档的基本声音数值做比对,对照此声音是否比其他人的声音较接近基
声音,然後辨识出是否是同一人的声音.
声音辨识方面并不容易,因为音调有大小快慢,而频率不变,能量不变;
由此发现辨识声音的可能性.最後发现声音特色较为特别的人,其声音辨识度较
高.未来学弟妹们可以用不同麦克风测试声音的差异性,或寻求更为精准的辨识
方法,做出更完善的声音辨识系统.
三, 心路历程
彭文玉:为期三学期的专题终於要结束收尾了,在这三学期里发生了很多出
乎我意料之外的事,期间经历更换专题老师,更换组,更换题目的大小事件.
一开始本来是跟著陈育威老师和几位同班的同学一起做专题,在第一学期刚
开始时并没有太大的问题,我把我的心思都放在学习资料库SQL和新的程式语言
Java上,为了解了资料库SQL,参考了一些相关的书籍,一直到了学期末要报告
时,才发现和同学们在专题的理念上和方向并不是太一致,所以我选择了换组.
刚开始并没有想到要不要更换专题老师的问题,而是想到接下来我一个人能
独自完成什麼样的题目?这似乎很难.而後和几位较谈的来的同学交换了彼此的
心得後,我决定加入他们和他们同一组,换新的组别,新的题目,新的老师,新
的出发.
新学期在新的领域里,有许多新的知识是以前我从未接触过的,如所谓的浮
水印,声音档等,这些正等著我去学习.我们将专题题目专注在声音的分析上,
所以学习声音的格式及资料型态是我的第一个功课,第二个功课就是使用程式去
读取声音档,在学习声音档的过程当中,并不是那麼的顺利,在意见交换上我们
慢慢的出现了沟通不良的问题,这问题也造成我的学习一度中断,还好在互相摩
擦中,还是过完了一学期.
到了第三个学期,我们的组员有了一些更动,但专题依然延续上学期的题目
没有更动.学期刚开始时,对於这个专题题目真得有些茫然,不知该如何继续做
下去,後来,和游国忠老师谈了我们的想法也定出未来这一学期的工作内容.
整体专题的进度并没有如当初预期的快,所以在内容上和其他组比起来感觉
少了些,但我们还是很努力的在做专题,希望这学期能顺利结束.
陈韵曲:参与做专题前,心里是真的有点恐惧.在第一个学期的专题,是做
浮水印的研究,在研读浮水印PAPER的过程中,对我本身而言,就好像回到当
初高职准备考大学的时期,很多英文单字都要查字典,最後再整理翻译,但唯一
不同处是,有许多的电脑专有名词,使得了解上比较困难,这些是第一学期做的
事.到了第二个学期,我们换题目了,题目变成了"声音办识系统",和慧琦一
起读PAPER,一起学MATLAB,并用GOLDWAVE这套软体来录声音,在学习
MATLAB上,除了应用老师课堂上教的,还有一些功能及转换则要不停的去翻
书测试;最好玩的就是录音了,很多同学被我们拉到慧琦家录音,录完後,还可
以听听自己录的音,及看录完的波型,最後将这些声音档利用我们之前学的
MATLAB做正规化(以所有人里面,声音阵列长度最长的,取整数,来做补0
的动作),接著做傅立叶转换,写出自动化程式,秀出波型.第三学期,延续第
二学期的内容,主要是使用JAVA这套系统,除了利用上学期的MATLAB技术
外(在傅立叶转换後,将结果存成TXT档),并写了2,3个JAVA小程式,将
TXT档的资料做运算,首先将每个人的两两声音做加总,存成一个值,接著将
这些值做距离间的运算,结果发现自己和自己的数值,相减後距离会比较接近,
这样就达到我们要的结果了,虽然做起来的内容比起其他组感觉少了些,但是总
觉得自己有一次比一次进步,这是我觉得很高兴的地方;不过做专题,真的是很
辛苦,也是一件很有压力的事,希望这学期就能顺利结束.
陈慧琦:老实说一开始对声音的题目感到很茫然,毕竟不像影像,你看得到
也摸得到,声音过了就没了,到底要怎麼研究真是一大问号.然後三个学期下来,
经过一些风波折腾,也走到这样的地步,还真是满感动的.大家都很认真在做这
个专题,也算是因为专题我们的感情也培养得很不错.最後能够有些成果出来真
的很高兴,毕竟要辨识是谁的声音并不容易,但很庆幸老师的相助,从茫然中找
出些头绪.另外我们在程式撰写方面也出现不少困难,毕竟程式能力不强在专题
时很吃力,不过幸好还是撑过来了.SQL资料库在资料建构上可更完整,提供
使用者更人性化的功能,且JAVA和SQL的功能非常强大,可谓未来趋势.接
下来,就要靠学弟妹们将此系统的辨识能力提高了.
四, 背景动机
探讨加入数位浮水印—因为目前盗版猖狂,所以研究如何利用浮水印,来达
到保护智慧财产权的功效.这对台湾乐坛,大补帖氾滥的情况,应有阻赫作用;
简单的复制是以bit-to-bit来复制原始档案,简单且成本低廉;透过压缩帮助的无
线网路,样品和资讯的成本低廉的分发会产生新问题.数位浮水印已应用於音乐
CD上,令使用者无法转录到电脑上,虽是控制了某些音乐的版权,但消费者非
常不满为何花了钱买了CD却不能轻易听歌,Audio data应该让档案使用者可依
自己习惯去使用.
因此,如何加入,加入何种数位浮水印便是此时不容置缓的技术.档案从安
全环境中离开就会失去控制,而有一些IPR(Intellectual Property Rights智慧财
产权)保护的点子,像熟知的浮水印技术,可将IPR的相关资讯放入档案中.探
声音辨识系统—目前台湾社会动汤不安,打开电视都有好几条社会新闻,闹得
人心惶惶,每个人都担心住家是否安全,家是不是一个安全的地方?因此若有一
套靠特定声音才能进入屋内的语音辨识系统,想必对民众的生命财产安全保护上
有很大助益,警察先生也不用疲於奔命追拿犯人.研究语音辨识的门禁系统,防
止窃盗等犯罪现象的加温,才能确保每个人的生命财产安全.
另外,目前市面上有许多家电产品,也试图利用语音控制,来增加使用上的
便利性.
由此可知,语音辨识势必是未来趋势.
五, 系统功能简介
用GoldWave软体和麦克风录下声音.
一方面透过MATLAB声音(wav)做正规化,傅立叶转换的测试,把数
值存在资料库中.
另一方面用JAVA把声音读进来,做傅立叶转换,然後把这个声音和资料库
中的档案比对,分辨出是否是同一人的声音.
※ 数值转换:将存成字串格式的傅立叶资料档案转成数字格式之资料档案.
※ 能量转换分析:将傅立叶转换後之资料做等份之运算求取得特徵量值,以推
算出声音之特徵点.
※ 特徵选取:
声音特性:一连串随时间而变动的动态性讯号.※ 傅立叶『Fast Fourier
Transform(FFT)』转换:将时间函数转换成频率函数.
MATLAB可以看出将声音档案(wav档)执行傅立叶转换後,其波形能
以肉眼分辨,故只需研究出方法将取特徵值之演算法推出,即可用程式来取出每
个人声音之特徵,然後和资料库中现存的声音数值做比对,最後才能辨识出是同
一个人的声音?还是另一个人的声音?
六, 系统环境
Matlab的介面:
资料库的画面:
资料库内资料结构:
七, 硬体架构
『一台可以正常使用的电脑+麦克风』
八, 软体架构
(1)MATLAB 6.0 (MATrix LABoratory):具有用法简易,可灵活运用,程式结
构强,又兼具延展性.
Matlab的特色:1. 功能强的数值运算
2. 先进的资料视觉化功能
3. 高阶但简单的程式环境
4. 开放及可延伸的架构
(2)Visual C++
(3)GoldWave Version 4.25MSSQL 7.0TextPad
(7)Fast Fourier Transfer FunctionMicrosoft Access 2000(9)Microsoft Windows
2000 professional
九, 系统架构
(1)录音部份:首先利用录音系统(麦克风)将不同10个人的"开门"声音
录起来,每个人说20次"开门",共200次,每说一次就录一次,之後将这
200次的"开门",截取一样长度的声音档(正规化—空白部分补0增加为长
度80000),建立成资料库,接著再将这些声音转换,用PR变成相同模式.
(2)程式部份:从JMF去著手,声音读进来的coding部份用J AVA写程式.
1.完成资料库建立
2.用傅立叶转成频率
3.比较不同声音频率的特徵
◎用Matlab将wav档读入,傅立叶转换之後再经过运算,将结果数值存成文字
档,比较运算前後声音的差异,并将分析结果存在资料库中.
◎作资料库的目的,是为了作DATA搜寻用,让声音比对的效率更佳.
资料库部分:
JDBC-ODBC Bridge:
十, 功能方块图
十一,安装及执行
MATLAB的实作:
※其馀程式部分依J AVA的方式执行即可.
十二,人力分配分析
【三上】
陈韵曲(浮水印技术研究):寻求加入浮水印更为完善的方法.
陈慧琦(浮水印技术测试):计算公式,利用Matlab测试其可行性.
蔡政道(浮水印技术实做):加入浮水印,取出浮水印动作试验.
【三下】
陈韵曲:用Matlab将wav档读入,测试声音,秀出声纹,将声音档转成数值以
利分析,用傅立叶转换找特徵,将分析结果存成文字档,比较傅立叶转
换前後波型的差异,并设计自动化程式读取wav档.
陈慧琦:自动化读入声音,撷取基本声音录制成资料库,连接资料库抓取资料.
彭文玉:以JAVA读入声音(wav),判断读入的档案是否为wav档,显示wave档
的格式,然後作傅立叶转换.
蔡政道:和资料作比对,看频率作运算.
【四上】(JAVA实作)
陈韵曲:将转成数值的声音档进行运算分析彭文玉:读取声音档,将声音档转成
数值以利分析陈慧琦:连接资料库,建置资料库系统
十三,系统发展图
十四,成果画面展示
※ 利用MATLAB声音的频率波形变化
原始波形(Mix)
原始波形前10笔(Mix)
原始档案+0.7 (Mix)
原始档案-0.7 (Mix)
原始档案*2 (Mix)原始档案/2 (Mix)原始波形(一维)原始波形(二维):读取标头档
之程式码
读取档案资料程式码建立新档案之程式码
合并音效档之程式码测试数值变化程式码植入浮水印之程式码
数值转换1:
import java.util.StringTokenizer ;
傅立叶
import java.io.* ;
public class Trans2
{
public static void main ( String[] args ) throws Exception
{
int size ;
File fileName = new File ( "02.txt" ) ;
FileInputStream fileIn = new FileInputStream ( fileName ) ;
DataOutputStream fileOut = new DataOutputStream ( new
FileOutputStream ( "Fourier2.FFT" ) ) ;
size = fileIn.available () ;
byte[] data = new byte[size] ;
System.out.println ( "总位元数:" + ( size = fileIn.available
( ) ) ) ;
fileIn.read ( data ) ;
数值转换2:
String temp = new String ( data ) ;
StringTokenizer st = new StringTokenizer ( temp , "/n" ) ;
//String[] datas = new String[st.countTokens () ] ;
//float[] Fourier = new float[st.countTokens ()] ;
double[] Fourier = new double[st.countTokens ()] ;
for ( int i = 0 ; i < st.countTokens () + 1 ; i ++ )
{
//Fourier[i] = Float.parseFloat ( st.nextToken () ) ;
Fourier[i] = Double.parseDouble ( st.nextToken () ) ;
Fourier[i] = Float.parseFloat ( Fourier[i] ) ;
System.out.println ( "第" + ( i + 1 ) + "组:" + Fourier[i] ) ;
fileOut.writeDouble ( Fourier[i] ) ;
}
fileIn.close () ;
fileOut.close () ;
}
}
能量转换分析1:
import java.io.* ;
public class count
{
public static void main ( String[] args ) throws IOException
{
java.io.BufferedReader keyin ;
keyin = new java.io.BufferedReader ( new
java.io.InputStreamReader ( System.in ) ) ;
System.out.print ( "输入档名:" ) ;
String fileName = keyin.readLine () ;
DataInputStream fileIn = new DataInputStream ( new
FileInputStream ( fileName ) ) ;
float[] count = new float[80] ;
for ( int i = 0 ; i < 80000 ; i ++ )
能量转换分析2:
{
int j = Math.round ( i / 1000 ) ;
float k = fileIn.readFloat () ;
count[j] = count[j] + ( ( k * k ) / 1000 ) ;
if ( ( i % 1000 ) == 999 )
{
System.out.print ( "count[" + j + "]= " +
count[j] ) ;
if ( ( j + 1 ) % 5 == 0 )
System.out.println ( "" ) ;
else
System.out.print ( " " ) ;
}
}
fileIn.close () ;
}
}
取样数必须为2的次方.
程式(1):将读入声音档的程式和傅立叶转换程式合并.将傅立叶转换後的资料
以固定区间切割做加总.将刚计算好声音的加总阵列,和另一个声音的加总阵列
做比对运算.将数值以固定区间做加总,并将每一区间(1~10000)的加总存进
阵列中.
比对分析:将两阵列里的数值相减,平方,累加,并做开根号的动作;将新加入
声音做运算,并与资料库内的数值做搜寻比对.
声音辨识系统最後结果:
解释:以本组3位组员(mioko+annn+melody)的声音做比较.当新进一个
mioko的声音与已有的声音资料作比较,发现mioko和mioko的声音误差最小,
即可判断—此为mioko的声音.
十五,程式处理流程图
连结资料库流程:
1. 载入JDBC驱动程式
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
2. 连接到资料库
String url="jdbc:odbc:voice";
Connection con=DriverManager.getConnection(url,"帐号","密码");
3. 将SQL statement送到资料库Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT name, row01 FROM voice, tbid
WHERE id = name_id AND name LIKE 'mioko%'");4. 读入资料库传回的结果
while(rs.next()) { }
十六,说明书及操作手册
声音(wav档)在进行傅立叶转换时,资料取样数越多,转换後频带分的越
细,越能表现出原始的声音波形.
本语音辨识系统在资料取得间需要人力输入,此项有待改进.
SQL资料库建构资料可更完整,提供使用者更人性化的功能.JAVA和SQL
的功能强大,可谓未来趋势.
若数值运算上误差稍大,便无法很确定是同一人的声音,但只要声音特色较
明显的人,其声音辨识度会较高.
※『浮水印的研究』:
浮水印的种类:
1. 隐性浮水印
2. 显性浮水印
浮水印的特性:
1. 反抗性
2. 防御性
3. 公开性数位浮水印的应用:
1. 智慧财产权保护:IPR(Intellectual Property Rights) 2. 资料
篡改之侦测
3. 秘密通讯
◎ 浮水印不易被更改,可辨识;且浮水印具有Transparency(通透性),Robustness
(强健性),Security(安全性),Capacity(容量)四大特色.
(2)WAVE音乐格式:
※ 常使用到的MATLAB指令:
ans % 预设的计算结果的变数名
dir or ls % 显示所有档案
plot % 秀出档案的波形
figure % 开出另一个新的图形视窗
clear % 清除变数
clc % 清除萤幕
exit % 离开视窗
※ 标准乐器数位界面文件版式 Dustin Caldwell(节录)
1. 概述
MIDI的全名为Muscial Instrument Digital Interface,是乐器数位世界里的世界
语言.
MIDI(乐器数位界面)档之中基本上有2种东西,标头区块(Header Chunks)
和磁轨区块(Track Chunks).本文的第2大段解释标头区块,而第3大段解释磁
轨区块部分.MIDI档里"仅有"一个标头区块用来描述这个档案(MIDI档)的格
式等资料,并且纪录了所有磁轨区块的数目.我们可以把一个磁轨区块想成是一
个有许多磁轨的卡带.我们可以把一个磁轨当成是一个声音,个发音器,任何一
种设备或者任何想像得到的乐器.
2.标头区块
标头区块顾名思义就是在档案的开头,它分为三个部份来标示出MIDI档案
的内容.标头档的格式内容固定是:
4D 54 68 64 00 00 00 06 ff ff nn nn dd dd
在它前面的四个位元组(byte)的ASCII码固定是MThd(也就是4D 54 68
64).而接在MThd之後的四个位元组则标示著标头档的大小. 它固定是00 00
00 06,因为标头档的实际长度就只会有6个位元组.
ff ff则是档案的格式.总共有3种格式:
0- 单轨
1- 多轨,同步拨放
2- 多轨,异步拨放
单轨顾名思义就是只有一个磁轨.而同步多轨就是指所有磁轨将同步,或者
换句话说,这些磁轨同时开始,所以能够表现出一首歌曲之中的不同部份(因效
部份).至於异步多轨则不一定同时开始,甚至可能完全不同.
nn nn指出了MIDI档中总共有多少磁轨数.
dd dd则是乐谱中每一小节(quarter note)的拍子数(delta-time).(这将在以後
说明)
3.磁轨区块
档案中在标头区块後面剩下的部份就是磁轨区块.在每一个磁轨上都有一个
磁轨标头并且能包含许多MIDI指令(MIDI command).每个磁轨标头都和档
案标头很相似:
4D 54 72 6B XX XX XX XX
它的前四位元也有和档案标头前四位元组类似的ASCII码.不过这是
MTrk.在MTrk之後的4个位元标示出了这个磁轨的长度(不包含这个磁轨标
头的长度).
存在磁轨标头之後纪录的是MIDI的事件(MIDI event).这些事件动作都
是由MIDI埠从混音器传送和接收而的的资料(These events are identicial to the
actual data send and received by MIDI ports on a synth with one addition.).事件动
= 128).
这些命令将写在表格一中.每一个命令都有不同参数和长度,但是跟随命令的资
料都有0(小於128)的msb .例外的情况是一个变换事件(meta-event), 它
可能有1的 msb.然而变换事件需要能够缓和混乱的长度参数.
一个能够造成混乱的细微差别执行模式(One subtlety which can cause
confusion is running mode). 这是MIDI命令所遗漏的地方,而最後所发布的
MIDI命令(被)采取.这代表了MIDI事件将由节拍和某些有参数的命令所组成.
Midi event :
十六进位:8x
二进位:1000xxxx
资料:nn vv
叙述:
1. Note off (key is released)
2. nn=note number
3. vv=velocity
4 .结论
如果这个解释会更加混淆MIDI的发布,那麼在附表中有能够帮助澄清发发
布的例子.另外,还有2个实用和一个图片档案包含在这个文件里:
DEC.EXE 它能方便的将二进制档案(像.mid)转换为等值的十进制文字档.
REC.EXE 与DEC.EXE正好相反,是将十进制文字档转换成为二进制文件- .
MIDINOTE .PS 标示了钢琴键盘的标准数字记录的附加表格(※附录一).
Midi无法加入浮水印.
※附录一
MIDI事件命令
每一个命令位元组分为两个部份.左nybble(4位元)存放实际命令,而右
nybble存放将被执行的命令的MIDI频道编号(MIDI channel number).共有16
种MIDI频道,和8种MIDI命令(命令nybble必须有1的msb).
在以下的表格中,X代表MIDI频道编号.注意所有资料位元组都将小於128
(msb设为0).
在以下表格中列出了没有MIDI频道的变换事件.它们的格式是:FF xx nn dd
所有的变换事件都是由FF开始储存,然後是指令(XX),资料的长度大小(nn),
还有实际资料(dd).
十六进位 二进位 资料 叙述
8x 1000xxxx nn vv Note off (key is released)
nn=note number
vv=velocity
9x 1001xxxx nn vv Note on (key is pressed)
nn=note number
vv=velocity
Ax 1010xxxx nn vv Key after-touch
nn=note number
vv=velocity
Bx 1011xxxx cc vv Control Change
cc=controller number
vv=new value
Cx 1100xxxx pp Program (patch) change
pp=new program number
Dx 1101xxxx cc Channel after-touch
cc=channel number
Ex 1110xxxx bb tt Pitch wheel change(2000H is normal or no change)
bb=bottom (least sig) 7 bits of value
tt=top (most sig) 7 bits of value
十六进位 二进位 资料 叙述
00 00000000 nn ssss Sets the track's sequence number.
nn=02 (length of 2-byte sequence number)
ssss=sequence number
01 00000001 nn tt .. Text event- any text you want.
nn=length in bytes of text
tt=text characters
02 00000010 nn tt .. Same as text event, but used for
copyright info.
nn tt=same as text event
03 00000011 nn tt .. Sequence or Track name
nn tt=same as text event
04 00000100 nn tt .. Track instrument name
nn tt=same as text event
05 00000101 nn tt .. Lyric
nn tt=same as text event
06 00000110 nn tt .. Marker
nn tt=same as text event
07 00000111 nn tt .. Cue point
nn tt=same as text event
2F 00101111 00 This event must come at the end of each
track
51 01010001 03 tttttt Set tempo
tttttt=microseconds/quarter note
58 01011000 04 nn dd
ccbb
Time Signature
nn=numerator of time sig.
dd=denominator of time sig.
2=quarter
3=eighth, etc.
cc=number of ticks in metronome click
bb=number of 32nd notes to the quarter note
59 01011001 02 sf mi Key signature
sf=sharps/flats (-7=7 flats, 0=key of C,7=7
sharps)
mi=major/minor (0=major, 1=minor)
7F 01111111 xx dd .. Sequencer specific information
xx=number of bytes to be sent
dd=data
下面的表格列出了控制系统的系统讯息,这些也都没有频道编号.(通常它们只
会用来控制MIDI keyboard之类的乐器.)
十六进位 二进位 资料 叙述
F8 11111000 Timing clock used when synchronization is required.
FA 11111010 Start current sequence
FB 11111011 Continue a stopped sequence where left off
FC 11111100 Stop a sequence
以下表格列出的数字相当於用来记录指令.
Octave Note Numbers
C C# D D# E F F# G G# A A# B
0 0 1 2 3 4 5 6 7 8 9 10 11
1 12 13 14 15 16 17 18 19 20 21 22 23
2 24 25 26 27 28 29 30 31 32 33 34 35
3 36 37 38 39 40 41 42 43 44 45 46 47
4 48 49 50 51 52 53 54 55 56 57 58 59
5 60 61 62 63 64 65 66 67 68 69 70 71
6 72 73 74 75 76 77 78 79 80 81 82 83
7 84 85 86 87 88 89 90 91 92 93 94 95
8 96 97 98 99 100 101 102 103 104 105 106 107
9 108 109 110 111 112 113 114 115 116 117 118 119
10 120 121 122 123 124 125 126 127
MIDI规格制定的沿革与简史
1978年,在日本举办了一个乐器展览会,当时日本一家电子乐器制造厂商
Roland 公司,有感於电子合成乐器势必是未来的风潮,若不事先规划好一致的
共通规格,那麼日後将会带给乐器制造商及消费者使用上极大的不便.因此,
Roland 主动邀请如 YAMAHA,KORG 及欧美几个主要制造厂商,共同研讨电
子乐器的演奏资料传送方式,与会厂商希望能藉著这次的研讨会,制定出一种统
一的资料传递型态,而使得各厂商制造出来的合成乐器能彼此沟通连接使用.
西元 1981 年,Sequential Circuits公司的Dave Smith,Roland公司的
I.Kakehashi,与Oberheim Electronics公司的 Tom Oberheim,在NAMM(National
Association of Music Merchants)的June show上商谈,想定立一个synthesizer
control的标准,使得不同公司所生产的synthesizer可以互相沟通.之後,Dave
Smith拟出了一个相关提案,称之为 USI (Universal Synthesizer Interface),并於
1981年11月公开於Audio Engineers Society会议上.
西元1982年1月,USI在NAMM会场上引起厂商们广泛的兴趣,Syquential
Circuits,Roland,Oberheim,Yamaha,Korg和Kawai这些大厂派代表开会讨论
相关事谊,因为他们大部份在自己的产品上拥有独自的沟通标准.这些公司使得
NAMM更积极的去改善原有的USI规格使它更为完美...
原有的USI规格目标在於允许不同厂牌的合成器之间可以彼此控制,能够
有共同的语言标准,使彼此的资料能被不同厂牌的合成器正确的解读与接收.日
本 synthesizer工程师与Sequential Circuits 的工程师则希望USI未来能够加入其
它如 pitch wheels,foot pedal,modulation,wheels...等等的controller控制标准.
当这些厂商在1982年6月的NAMM show会议上再度碰面时,他们努力研
讨的结果已经出炉,并成为现今MID的骨架.同时 USI这个名称也被取消(因
为大家对"universal" 这个字不是很满意),新的名称将更能表现出这个标准的精
义与目标 : Musical Instrument Digital Interface (MIDI).
随著这个标准的提出,厂商们纷纷在他们的新synthesizer中加入必要的装置
与微处理器,使自己的产品能与其它的MIDI 装置沟通.至此1982年年底,MIDI
规格总算大功告成,而合乎 MIDI规格的乐器,真正被推出实践则是1983年的
事了....
MIDI规格的实现
1983年有几家电子乐器制造商几乎是同时推出符合MIDI规格的乐器,例
如:YAMAHA DX-7/Roland JX-3P/SEQUENTIAL Prophet-600...等等.1983年1月
在NAMM show上,Sequential Circuits Prophet 600成功的Roland JP-6连接沟通,
与会的代表们欢呼庆祝,MIDI规格至此终於成功的正式上路了.
然而当初制定出来的MIDI规格并不是完美无缺的,它遗漏了部份的定义,
也没有公开的说明文件,这使得这些MIDI synthesizer并不是所有它们能做的事
都能互相沟通,造成业界一片混乱.为了解决这个问题,改进後的MIDI Format
1.0在1983年8月再次的被公开提出来...
同年1983年6月29日在日本,synthesizer公司们成立了他们自己的集团,
称为JMSC(Japanese MIDI Standard Committee)日本 MIDI规格协议会,继日本之
後,美国也设立了一个协议 MIDI 规格的机构名为IMUG(International MIDI
Users' Group),去帮助厂商与相关的音乐工作者,很快的,这个组织就蓬勃发展
起来其後并更名为国际 MIDI协会,简称IMA (International MIDIAssociation).
MIDI规格的後续发展
当然MIDI规格并不能让每个人都很满意,这些不满意的人们企图加入IMA
去改变MIDI规格,而一些厂商也不愿完全依照MIDI规格的内容去生产他们的
产品,使得MIDI变得没什麼效用.於是在1984年秋天,Yamaha公司的Jim
Smerdel与其它厂商代表一起成立了MMA(MIDI Manufacturers Association)去维
护MIDI规格.他们印出来完整详细的MIDI规格书,消除大家心中对於MIDI
疑虑.现在,MMA与JMSC一起维护著MIDI规格,他们之中的任何一位成员
都愿意与MIDI规格妥协,因为他们发现如果 synthesizer要能互相沟通,则MIDI
就非存在不可.自1984 年起,MIDI就稳定的被使用在音乐工业上.从此MIDI
我们可以这样解释它...:
1) MIDI 是电脑音乐的重要功能之一.
2) MIDI 是一种统一的工业标准.
3) MIDI 是一种介面装置.
4) MIDI 是一种新进但不同於传统乐器的乐器.
5) MIDI 是一种数位语言.
Yamaha DX7惊人的销售量更使得原本没有生产MIDI synthesizer的厂商们
纷纷倒向MIDI阵营,人们渐渐感受到 MIDI的便利,也开始要求其它相关设施
须配有MIDI.像是 amplifier,或是signal processor.电脑使用者很快的察觉到,
MIDI的数位讯息能毫无错误的被电脑所接收,因此软体厂商们开发了许多应用
软体去控制synthesizer,使得使用者的双手可以做更多想像不到的事,而 MIDI
似乎也成为未来电脑发展必须容入的对象.在往後的将来可以肯定的是,会有更
多更多的MIDI周边产品诞生.
什麼是MIDI?
MIDI的全名为Muscial Instrument Digital Interface,我们可以将它翻译做『乐
器的数位界面』.看到『界面(Interface)』这个字,我们便可以知道,MIDI 不是
一个可以看得到,可以触摸得到的物质,也不是电脑上可执行的任何软体,MIDI
是一个标准,让synthesizer制造厂商可以依照这个标准来制造他们的乐器,使这
些不同厂牌的synthesizer可以藉著MIDI这个标准规格毫无困难的互相沟通.
举个例子来说吧!假设今天我在路上遇到了外星人,我说中文,他说外星
语,完全无法沟通,如果荷兰人懂英文,我也懂英文,那我们就可以用英文来沟
通,此时,英文便是我与外星人沟通的『界面』.
再来看看这些电子乐器吧!在没有MIDI这个共通的语言之前,甲牌乐器说
甲牌乐器才懂的语言,乙牌乐器说乙牌乐器才懂的语言,这样甲与乙两乐器根本
无法沟通,就像我遇到荷兰人一样,但如果甲具MIDI,乙也含MIDI,那甲与乙
牌的乐器就可以藉由MIDI来沟通了.
所以,我们可以这麼说:MIDI是乐器数位世界里的世界语言.MIDI Device
间流通的讯息我们称为MIDI Message.传送这些MIDI Message的Cable我们称
为MIDI Cable.而在MIDI Device上连接MIDI Cable的Port我们称为MIDI Port.
什麼可以成为MIDI device?
只要有内含微处理器,可以收送MIDI message,处理反应MIDI message,
并符合MIDI规格的设备,我们就可以称它为MIDI device.由此可知,MIDI device
不一定是指键盘状的东西,或是黑黑一个box状的音源机器,而是只要在里面装
上适当的装置,能将声音处理转换成MIDI message的机制,我们都可以将之视
为MIDI device;在吉它上放上一个可以将弦的振动转换为MIDI message的
pickup,这把吉它便可成为MIDI device.
除了乐器外,在你的电脑中插上一张MIDI介面卡,使它能够处理MIDI讯
息,此时你的电脑便是一台MIDI device.再比如说具MIDI port的照明器材也可
以回应收到的MIDI message,去改变灯光的颜色或明暗;含微处理器的 MIDI
混音器(Mixer)能够反应MIDI message去控制音量与产生特殊的效果音;这些东
西不一定是乐器,但它们都可以是 MIDI device.
MIDI Channel
MIDI Channel很像电视节目的Channel,MIDI规格中定义16个MIDI
Channel,分别编号由1 - 16.在没有MIDI Channel时,MIDI可以让所有MIDI
system中的device同时作用,却没办法请它们分别做不同的事,所有system中
的device只能做同一件事.有了MIDI Channel後,我们可以指定某项讯息仅供
n号Channel使用,其它非n号Channel 的device则不理会这个讯息,因此MIDI
中的16个Channel 最多可以指定MIDI device同时做16种不同的事.
MIDI channel接收模式有四种:
Mode1:Omni On/Poly
Mode2:Omni On/Mono
Mode3:Omni Off/Poly
Mode4:Omni Off/Mono
Omni On表示MIDI device会追踪所有channel并回应所有的channel
message,Omni Off则只接收指定channel的channel message,其它的它一概不
管.
Poly与 Mono则是指当MIDI device接到这个讯息时,要用什麼方法去play
它,Poly就像多发声数的synthesizer一般,它会同时play多个note,除非接到
这个note的结束讯息或是超出synthesizer发声数时才会停止play这个note.Mono
刚好相反,它一次只play一个note,只要你play 一个新的note,前一个Play的
note就会被中断.
MIDI Message
MIDI message在MIDI中算是相当重要的一部份,它们决定什麼样的音乐事件可
以在MIDI device之间流通,如果想了解MIDI,MIDI message的用法一定要知
道.
MIDI device 如何收送讯息?
MIDI是数位界面,因此它所收送的资料是由0与1所构成的,它藉著MIDI
cable在MIDI device间做收送,这些资料以1byte(8bits)为一组,2进位的8位数
字可表达10进位0-255之间的数字,所以它可以传送256种不同的讯息.
MIDI是以Serial Data Transmission的方式在传送资料,也就是将资料1bit,
1bit的丢出去,与Parallel Data Transmission的传送方式相较起来虽然慢了许多,
但却能够减少资料在传送过程中受到干扰而发生错误的机会,在正常的状态下,
MIDI每秒钟约可传送31250 bits的资料.(ps:虽然我们说一组讯息是8 bits,但
MIDI会在讯息的头跟尾各加上1 bit来判断资料的类型,因此正确说来,一组资
料应该是10 bits.)
MIDI message的组成
MIDI device通常使用2 - 3个byte来表示一个MIDI message,任何一个MIDI
message的第一个byte称为status byte,之後的称为data byte.status byte是用来
说明这个MIDI message的种类或作用,它可能表示这个message是个Note On
message或是Pitch Bend Change message...等.data byte则是给定status的值.举
个例子,你可以在音源使用手册後面看到这样的说明:
Note ON:
Status Second Third
----- ------ -----
9nH kkH vvH
n= MIDI channel number: 0H-FH (0-15)
kk= Note number : 00H-7FH (0-127)
vv= Velocity : 01H-7FH (1-127)
status byte告诉MIDI device这是一个Note On message,因为Mote ON message
是一个MIDI Channel Message,所以我们要指定channel给它,n值就是指定要
传送的channel编号.
後面跟著second和third就是data byte,用来定意status byte的值,kk表示这
个note的编号,编号范围由0-127;vv代表这个note的力度,范围由1-127(0代
表没有声音,属Note Off).
为了区别status byte与data byte,MIDI使用0-127代表data byte, 128-255代表
status byte.
MIDI message 的种类
MIDI message可分为System message和Channel message两种,Channel
message中的status byte包含channel编号,指定讯息由某个channel接收;System
message则没有,所以每一个channel都可以接收.System message与Channel
message的内容分别如下:
.System Message
a. Comman message
1.Song Position Pointer
2.Song Select
3.Tune Request
4.EOX (End of Exclusive)
b. Real-time Message
1.Timing Clock
2.Start
3.Stop
4.Continue
5.Active Sensing
6.System Reset
c. System exclusive message
. Channel Message
a. Mode message
1.Local Control
2.All Notes Off
3.Omni Mode Off
4.Omni Mode On
5.Mono Mode On
6.Poly Mode On
b. Voice message
1.Note On
2.Note Off
3.Polyphonic Key Pressure
4.Channel Pressure
5.Program Change
6.Control Change
7.Pitch Bend Change
其中Channel message中的Control Change message列表如下:
---------------------------------------------------
0 Undefined
1 Modulation wheel or lever
2 Breath controller
3 Undefined
4 Foot controller
5 Poramento time
6 Data entry MSB
7 Main volume
8 Balance
9 Undefined
10 Pan
11 Expression controller
12-15 Undefined
16-19 General-purpose controllers(number 1 to 4)
20-31 Undefined
32-63 LSB for values 0 to 31
64 Damper pedal (sustain)
65 Portamento
66 Sostenuto
67 Soft pedal
68 Undefined
69 Hold 2
70-79 Undefined
80-83 General-purpose controllers (numbered 5 to 8)
84-91 Undefined
92 Tremolo depth
93 Chorus depth
94 Celeste (detune) depth
95 Phaser depth
96 Data increment
97 Data decrement
98 Non-registered parameter number LSB
99 Non-registered parameter number MSB
100 Registered parameter number LSB
101 Registered parameter number MSB
102-121 Undefind
122 Reserved for channel mode message.
十七,参考资料
网址:
http://spinserve.com/stairway/midim/midim.htm
http://cslin.auto.fcu.edu.tw/scteach/scteach88/Tidsp/index.html
http://cslin.auto.fcu.edu.tw/scteach/scteach88/pack/index4.htm
书籍:
◎JAVA程式建构的思维—从入门到进阶
作者:周政宏
出版社:文魁资讯股份有限公司
2002年3月初版2刷
=10)
{
switch(x%10)
=10)
{
switch(x%10)
{
case 0:System.out.print("A");break;
case 1:System.out.print("B");break;
case 2:System.out.print("C");break;
case 3:System.out.print("D");break;
case 4:System.out.print("E");break;
case 5:System.out.print("F");break;
}
=10)
{
switch(y%10)
{
case 0:System.out.print("A");break;
case 1:System.out.print("B");break;
case 2:System.out.print("C");break;
case 3:System.out.print("D");break;
case 4:System.out.print("E");break;
case 5:System.out.print("F");break;
}
}
else
{
if(y!=0){System.out.print(y);}
else{if(x!=0)System.out.print(y);}
}
}
//-----------------------------------------------------------------------------------
public static void main ( String[] args ) throws IOException
{
FileInputStream fileRead = new FileInputStream ( "mioko2.wav" ) ;
int c,num=1;
int n[]=new int[4],j=0; int v[]=new int[2];
//----------------------------------------------------------------------------------
System.out.println("Header部份(0x0-0xB)") ;
System.out.print("(0x0-0x3)4个Bytes固定为 ") ;
while ( (c=fileRead.read ( )) != -1 && num <= 4)
{
n[j]=c; j++; num++;
if(num==5)
{
System.out.print("/" ");
for(int a=0;a<=3;a++)
{
int x=n[a]/16; int y=n[a]%16;
decide1(x,y);
System.out.print(" ");
}
System.out.print("/""+" "+"(");
for(int a=0;a<=3;a++)
System.out.print((char)n[a]);
System.out.print(")");
}
}
System.out.print("/n(0x4-0x7)4个Bytes为Wave Chunk大小 0x");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && (num =0;a--)
{
int x=n[a]/16; int y=n[a]%16;
decide2(x,y);
}
System.out.print(" Bytes" );
}
}
System.out.print("/n(0x8-0x11)4个Bytes固定为RIFF Type ");
j=0; n[j]=c; j++; num++;
while ( ((c=fileRead.read ( )) != -1) && (num <=12))
{
n[j]=c; j++; num++;
if(num==13)
{
System.out.print("/" ");
for(int a=0;a<=3;a++)
{
int x=n[a]/16; int y=n[a]%16;
decide1(x,y);
System.out.print(" ");
}
System.out.print("/""+" "+"(");
for(int i=0;i<=3;i++)
System.out.print( (char)n[i] );
System.out.print(")");
}
}
System.out.println("/n/nFormat部份(0xC-0x23)");
System.out.print("(0xC-0xF)4个Bytes固定为 ");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && num <= 16)
{
n[j]=c; j++; num++;
if(num==17)
{
System.out.print("/" ");
for(int a=0;a<=3;a++)
{
int x=n[a]/16; int y=n[a]%16;
decide1(x,y);
System.out.print(" ");
}
System.out.print("/""+" "+"(");
for(int i=0;i<=3;i++)
System.out.print((char)n[i]);
System.out.print(")");
}
}
System.out.print("/n(0x10-0x13)4个Bytes为Format Chunk大小 0x");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && (num <= 20))
=0;a--)
{
int x=n[a]/16; int y=n[a]%16;
decide2(x,y);
}
System.out.print(" Bytes" );
}
}
System.out.print("/n(0x14-0x15)2个Bytes为Format Type ");
j=0; v[j]=c; j++; num++;
while ( ((c=fileRead.read ( )) != -1) && (num Stereo");}
}
}
System.out.print("/n(0x16-0x17)2个Bytes定义通道数 ");
j=0; v[j]=c; j++; num++;
while ( ((c=fileRead.read ( )) != -1) && (num "+v[1]+v[0]+"个Channel");
}
}
System.out.print("/n(0x18-0x1B)4个Bytes定义取样率:此wave档的取样频率
0x");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && (num =0;a--)
{
int x=n[a]/16; int y=n[a]%16;
decide2(x,y);
}
}
}
System.out.print("/n(0x1C-0x1F)4个Bytes定义每秒Bytes数量 0x");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && (num =0;a--)
{
int x=n[a]/16; int y=n[a]%16;
decide2(x,y);
}
}
}
System.out.print("/n(0x20-0x21)2个Bytes定义每个取样的Bytes数量 ");
j=0; v[j]=c; j++; num++;
while ( ((c=fileRead.read ( )) != -1) && (num <=34))
{
v[j]=c; j++; num++;
=0;a--)
System.out.print (v[a]+" ") ;
}
}
System.out.print("/n(0x22-0x23)2个Bytes定义每个取样的Bits数量 ");
j=0; v[j]=c; j++; num++;
while ( ((c=fileRead.read ( )) != -1) && (num =0;a--)
System.out.print (v[a]+" ") ;
}
}
System.out.println("/n/nData部份(0x24~)") ;
System.out.print("(0x24-0x27)4个Bytes固定为 ") ;
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && num <= 40)
{
n[j]=c; j++; num++;
if(num==41)
{
System.out.print("/" ");
for(int a=0;a<=3;a++)
{
int x=n[a]/16; int y=n[a]%16;
decide1(x,y);
System.out.print(" ");
}
System.out.print("/""+" "+"(");
for(int a=0;a<=3;a++)
System.out.print((char)n[a]);
System.out.print(")");
}
}
System.out.print("/n(0x28-0x2B)4个Bytes为Data Chunk长度 0x");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && (num =10)
{
switch(x%10)
{
case 0:System.out.print("A");break;
case 1:System.out.print("B");break;
case 2:System.out.print("C");break;
case 3:System.out.print("D");break;
case 4:System.out.print("E");break;
case 5:System.out.print("F");break;
=10)
{
switch(y%10)
{
case 0:System.out.print("A");break;
case 1:System.out.print("B");break;
case 2:System.out.print("C");break;
case 3:System.out.print("D");break;
case 4:System.out.print("E");break;
case 5:System.out.print("F");break;
}
}
else
{
if(y!=0){System.out.print(y);}
else{if(x!=0)System.out.print(y);}
}
}
//--------------傅立叶转换-------------------------
static double[] SWAP(double data[],int a,int b) {
double tempr=data[a];
data[a]=data[b];
data[b]=tempr;
return data;
}
static double[] four1(double data[],int nn,int isign)
{
int n,mmax,m,j,istep,i;
double wtemp,wr,wpr,wpi,wi,theta;
double tempr,tempi;
n=nn << 1;
j=1;
for(i=1;i i) {
data=SWAP(data,j,i);
data=SWAP(data,j+1,i+1);
}
mmax) {
istep=mmax << 1;
theta=isign*(6.28318530717959/mmax);
wtemp=Math.sin(0.5*theta);
wpr=-2.0*wtemp*wtemp;
wpi=Math.sin(theta);
wr=1.0;
wi=0.0;
for(m=1;m for(i=m;i<=n;i+=istep) {
j=i+mmax;
tempr=wr*data[j]-wi*data[j+1];
tempi=wr*data[j+1]+wi*data[j];
data[j]=data[i]-tempr;
data[j+1]=data[i+1]-tempi;
data[i]+=tempr;
data[i+1]+=tempi;
}
wr=(wtemp=wr)*wpr-wi*wpi+wr;
wi=wi*wpr+wtemp*wpi+wi;
}
mmax=istep;
}
if (isign==-1)
for(m=1;m<=n;m++)
{
data[m] /= nn;
}
return data;
}
//-------------------------------------------------------------------------------------------
public static void main ( String[] args ) throws IOException
{
FileInputStream fileRead = new FileInputStream ( "08.wav" ) ;
int c,num=1;
int n[]=new int[4],j=0; int v[]=new int[2]; double data[]=new double[9];
//---------------------------------------Header部份-------------------------------------
System.out.println("Header部份(0x0-0xB)") ;
System.out.print("(0x0-0x3)4个Bytes固定为 ") ;
while ( (c=fileRead.read ( )) != -1 && num <= 4)
{
n[j]=c; j++; num++;
if(num==5)
{
System.out.print("/" ");
for(int a=0;a<=3;a++)
{
int x=n[a]/16; int y=n[a]%16;
decide1(x,y);
System.out.print(" ");
}
System.out.print("/""+" "+"(");
for(int a=0;a<=3;a++)
System.out.print((char)n[a]);
System.out.print(")");
}
}
System.out.print("/n(0x4-0x7)4个Bytes为Wave Chunk大小 0x");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && (num =0;a--)
{
int x=n[a]/16; int y=n[a]%16;
decide2(x,y);
}
System.out.print(" Bytes" );
}
}
System.out.print("/n(0x8-0x11)4个Bytes固定为RIFF Type ");
j=0; n[j]=c; j++; num++;
while ( ((c=fileRead.read ( )) != -1) && (num <=12))
{
n[j]=c; j++; num++;
if(num==13)
{
System.out.print("/" ");
for(int a=0;a<=3;a++)
{
int x=n[a]/16; int y=n[a]%16;
decide1(x,y);
System.out.print(" ");
}
System.out.print("/""+" "+"(");
for(int i=0;i<=3;i++)
System.out.print( (char)n[i] );
System.out.print(")");
}
}
//-------------------------------Format部份-----------------------------------------------
System.out.println("/n/nFormat部份(0xC-0x23)");
System.out.print("(0xC-0xF)4个Bytes固定为 ");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && num <= 16)
{
n[j]=c; j++; num++;
if(num==17)
{
System.out.print("/" ");
for(int a=0;a<=3;a++)
{
int x=n[a]/16; int y=n[a]%16;
decide1(x,y);
System.out.print(" ");
}
System.out.print("/""+" "+"(");
for(int i=0;i<=3;i++)
System.out.print((char)n[i]);
System.out.print(")");
}
}
System.out.print("/n(0x10-0x13)4个Bytes为Format Chunk大小 0x");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && (num =0;a--)
{
int x=n[a]/16; int y=n[a]%16;
decide2(x,y);
}
System.out.print(" Bytes" );
}
}
System.out.print("/n(0x14-0x15)2个Bytes为Format Type ");
j=0; v[j]=c; j++; num++;
while ( ((c=fileRead.read ( )) != -1) && (num Mono");}
Stereo");}
}
}
System.out.print("/n(0x16-0x17)2个Bytes定义通道数 ");
j=0; v[j]=c; j++; num++;
while ( ((c=fileRead.read ( )) != -1) && (num "+v[1]+v[0]+"个Channel");
}
}
System.out.print("/n(0x18-0x1B)4个Bytes定义取样率:此wave档的取样频率
0x");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && (num =0;a--)
{
int x=n[a]/16; int y=n[a]%16;
decide2(x,y);
}
}
}
System.out.print("/n(0x1C-0x1F)4个Bytes定义每秒Bytes数量 0x");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && (num <= 32))
{
n[j]=c; j++; num++;
=0;a--)
{
int x=n[a]/16; int y=n[a]%16;
decide2(x,y);
}
}
}
System.out.print("/n(0x20-0x21)2个Bytes定义每个取样的Bytes数量 ");
j=0; v[j]=c; j++; num++;
while ( ((c=fileRead.read ( )) != -1) && (num =0;a--)
System.out.print (v[a]+" ") ;
}
}
System.out.print("/n(0x22-0x23)2个Bytes定义每个取样的Bits数量 ");
j=0; v[j]=c; j++; num++;
while ( ((c=fileRead.read ( )) != -1) && (num =0;a--)
System.out.print (v[a]+" ") ;
}
}
//-------------------------------Data部份------------------------------------------
System.out.println("/n/nData部份(0x24~)") ;
System.out.print("(0x24-0x27)4个Bytes固定为 ") ;
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && num <= 40)
{
n[j]=c; j++; num++;
if(num==41)
{
System.out.print("/" ");
for(int a=0;a<=3;a++)
{
int x=n[a]/16; int y=n[a]%16;
decide1(x,y);
System.out.print(" ");
}
System.out.print("/""+" "+"(");
for(int a=0;a<=3;a++)
System.out.print((char)n[a]);
System.out.print(")");
}
}
System.out.print("/n(0x28-0x2B)4个Bytes为Data Chunk长度 0x");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && (num =0;a--)
{
int x=n[a]/16; int y=n[a]%16;
decide2(x,y);
}
System.out.print(" Bytes" );
}
}
System.out.print("/n"); int e=0;
int q=1;
while ( (c=fileRead.read ( )) != -1 && (num <= 52+e))
{
data[0]=0.0; data[q]=c;
q++;
num++;
}
System.out.print("/n转换前");
for(int u=1;u <=8;u++)
System.out.print(" "+data[u]+"/t");
four1(data,4,1);
System.out.println("/n转换後");
for(int p=1;p <=8;p++)
System.out.println(" "+data[p]);
four1(data,4,-1);
System.out.println("/n转换回原先资料");
for(int p=1;p <=8;p++)
System.out.println(" "+data[p]);
fileRead.close () ;
}
}
--------------------------加总------------------------
import java.io.*;
public class ReadLine
{
public static void main(String args[])throws Exception
{
double b[] = new double[16];
int i=0;
double f,a;
FileReader fr=new FileReader("02.txt");
BufferedReader br=new BufferedReader(fr);
String Line=br.readLine();
for (int j=0; j <16;j++)
{
while(Line!=null && i<=9999)
{
System.out.println("record"+i+",/t"+"value:"+Line);
a=Double.parseDouble(Line);
b[j]+=a;
System.out.println("record"+i+",/t"+"value:"+a);
Line=br.readLine();
i++;
}
i=0;
}
for (int j=0;j<16; j++)
System.out.println("sum[" + j + "]="+b[j]);
br.close();
fr.close();
}
}
---------------------------比对计算---------------------------------
import java.io.*;
public class cal
{
public static void main(String args[])throws Exception
{
double
a[]={-12.944152881688048,4.082702602541991,0.0,0.0,0.0,0.0,0.0,0.0,384.3178300
517009,70.82904190851768,58.939048922740255,66.91218671296072,67.72228973
296078,59.252603742740504,70.9853091385179,392.9166897817};
double
b[]={-18.054321451327972,2.6070862511619985,0.0,0.0,0.0,0.0,0.0,0.0,-106.547688
73349997,-32.56270462765004,-26.531717235440034,-14.147701522292923,-14.26
2758538992896,-28.07732822873998,-27.02223252765601,-95.0871843334993};
int i=0; double c,d=0.0,e=0.0;
for(int j=0;j<16;j++)
{
c=a[j]-b[j];
c*=c;
d+=c;
i++;
}
e=Math.sqrt(d);
System.out.println("计算结果:"+e);
}
}
---------------------------比对分析-------------------------
import java.io.*;
public class cala
{
public static void main(String args[])throws Exception
{
double f,g,h=0.0,e=992.4565135112156,r=542.9029162631446;
//double sum[]=new double[64]
// int t;
//for (int t=0;t<64;t++);
//{
h=e*0.8;
f=e-h;
g=e+h;
//}
if (r=f)
{System.out.println("资料符合!");
// System.out.println("h="+h+" f="+f+" g="+g);
}
else
{System.out.println("资料不符!");
// System.out.println("h="+h+" f="+f+" g="+g);
}
}
}
----------------------JDBC连接+搜寻资料--------------------------
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.table.TableModel;
import java.net.*;
import java.sql.*;
import java.lang.*;
public class jdbc7 extends JFrame
{
private JTable table;
private String[][] data;
public static void main(String[] args)
{
jdbc7 frame = new jdbc7();
frame.setSize(300, 300);
frame.pack();
frame.setVisible(true);
}
public jdbc7()
{
String[] columnNames = {"Name", "row1", "row2", "row3",
"row4", "row5", "row6", "row7",
"row8", "row9", "row10", "row11",
"row12", "row13", "row14", "row15",
"row16"
};
Connection connection;
Statement statement;
ResultSet resultset;
int iRecordNum = 0;
0)
{
data = new String[iRecordNum][]; //data取资料库内的二维资

resultset.beforeFirst(); //回到第一列
System.out.println("iRecordNum = " + iRecordNum);
for (int i = 0; i < iRecordNum; i++) //列
{
resultset.next();
data[i] = new String[17]; //某列的第几格
for (int j = 0; j < 17; j++) //行
{
data[i][j] = new String(resultset.getString(j + 1));
System.out.println("data["+ i + "][" + j + "] = " +
data[i][j]);
}
0)
{
// create table
table = new JTable(data, columnNames); //将资料和行数放入
JTable
table.setPreferredScrollableViewportSize(new Dimension(300, 100));
//文字表格大小
JScrollPane tablePane = new JScrollPane(table); //卷轴
setTitle("Demo of Table"); //Label
Container content = getContentPane();
content.add(tablePane, BorderLayout.CENTER);
//close
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
// single selection only
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
}
}
----------------------------------声音辨识系统完成-------------------------------------
import java.io.* ;
public class test13
{
static void decide1(int x,int y)
=10)
{
switch(x%10)
{
case 0:System.out.print("A");break;
case 1:System.out.print("B");break;
case 2:System.out.print("C");break;
=10)
{
switch(y%10)
{
case 0:System.out.print("A");break;
case 1:System.out.print("B");break;
case 2:System.out.print("C");break;
case 3:System.out.print("D");break;
case 4:System.out.print("E");break;
case 5:System.out.print("F");break;
}
}
else
{
if(y!=0){System.out.print(y);}
else{if(x!=0)System.out.print(y);}
}
}
//--------------------------------------傅立叶转换--------------------------------------------
static double[] SWAP(double data[],int a,int b) {
double tempr=data[a];
data[a]=data[b];
data[b]=tempr;
return data;
}
static double[] four1(double data[],int nn,int isign)
{
int n,mmax,m,j,istep,i;
double wtemp,wr,wpr,wpi,wi,theta;
double tempr,tempi;
n=nn << 1;
j=1;
for(i=1;i mmax) {
istep=mmax << 1;
theta=isign*(6.28318530717959/mmax);
wtemp=Math.sin(0.5*theta);
wpr=-2.0*wtemp*wtemp;
wpi=Math.sin(theta);
wr=1.0;
wi=0.0;
for(m=1;m for(i=m;i<=n;i+=istep) {
j=i+mmax;
tempr=wr*data[j]-wi*data[j+1];
tempi=wr*data[j+1]+wi*data[j];
data[j]=data[i]-tempr;
data[j+1]=data[i+1]-tempi;
data[i]+=tempr;
data[i+1]+=tempi;
}
wr=(wtemp=wr)*wpr-wi*wpi+wr;
wi=wi*wpr+wtemp*wpi+wi;
}
mmax=istep;
}
if (isign==-1)
for(m=1;m<=n;m++)
{
data[m] /= nn;
}
return data;
}
//-------------------------------------------比对-----------------------------------------------
static double SUM(double sum[],double as[])
{
double c,d=0.0,e=0.0;
int i=0,k=0;
for(int j=0;j<64;j++)
{
c=sum[j]-as[j];
c*=c;
d+=c;
}
e=Math.sqrt(d);
return e;
}
//----------------------------------------------------------------------------------------------------
public static void main ( String[] args ) throws IOException
{
FileInputStream fileRead = new FileInputStream ( "mioko1.wav" ) ; //
新的声音
int c,num=1,j=0,f=0,datasize=1024,n[]=new int[4],v[]=new int[2];
double data[],sum[]=new double[64],as[]=new double[64],r,bs[]=new
double[64],cs[]=new double[64];
data = new double[datasize + 1];
FileReader fr=new FileReader("mioko02.txt"); //读入要做比对的声音1
BufferedReader br=new BufferedReader(fr);
String Line=br.readLine();
while(Line!=null && f<64)
{
r=Double.parseDouble(Line);
as[f]=r;
Line=br.readLine();
f++;
}
f=0;
FileReader fr1=new FileReader("anny01.txt"); //读入要做比对的声音2
BufferedReader br1=new BufferedReader(fr1);
String Line1=br1.readLine();
while(Line1!=null && f<64)
{
r=Double.parseDouble(Line1);
bs[f]=r;
Line1=br1.readLine();
f++;
}
f=0;
FileReader fr2=new FileReader("melody02.txt"); //读入要做比对的声
音3
BufferedReader br2=new BufferedReader(fr2);
String Line2=br2.readLine();
while(Line2!=null && f<64)
{
r=Double.parseDouble(Line2);
cs[f]=r;
Line2=br2.readLine();
f++;
}
//--------------------------Header部份------------------------------------------------------
System.out.println("Header部份(0x0-0xB)") ;
System.out.print("(0x0-0x3)4个Bytes固定为 ") ;
while ( (c=fileRead.read ( )) != -1 && num <= 4)
{
n[j]=c; j++; num++;
if(num==5)
{
System.out.print("/" ");
for(int a=0;a<=3;a++)
{
int x=n[a]/16; int y=n[a]%16;
decide1(x,y);
System.out.print(" ");
}
System.out.print("/""+" "+"(");
for(int a=0;a<=3;a++)
System.out.print((char)n[a]);
System.out.print(")");
}
}
System.out.print("/n(0x4-0x7)4个Bytes为Wave Chunk大小 0x");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && (num =0;a--)
{
int x=n[a]/16; int y=n[a]%16;
decide2(x,y);
}
System.out.print(" Bytes" );
}
}
System.out.print("/n(0x8-0x11)4个Bytes固定为RIFF Type ");
j=0; n[j]=c; j++; num++;
while ( ((c=fileRead.read ( )) != -1) && (num <=12))
{
n[j]=c; j++; num++;
if(num==13)
{
System.out.print("/" ");
for(int a=0;a<=3;a++)
{
int x=n[a]/16; int y=n[a]%16;
decide1(x,y);
System.out.print(" ");
}
System.out.print("/""+" "+"(");
for(int i=0;i<=3;i++)
System.out.print( (char)n[i] );
System.out.print(")");
}
}
//-----------------------------------Format部份-----------------------------------------------
System.out.println("/n/nFormat部份(0xC-0x23)");
System.out.print("(0xC-0xF)4个Bytes固定为 ");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && num <= 16)
{
n[j]=c; j++; num++;
if(num==17)
{
System.out.print("/" ");
for(int a=0;a<=3;a++)
{
int x=n[a]/16; int y=n[a]%16;
decide1(x,y);
System.out.print(" ");
}
System.out.print("/""+" "+"(");
for(int i=0;i<=3;i++)
System.out.print((char)n[i]);
System.out.print(")");
}
}
System.out.print("/n(0x10-0x13)4个Bytes为Format Chunk大小 0x");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && (num =0;a--)
{
int x=n[a]/16; int y=n[a]%16;
decide2(x,y);
}
System.out.print(" Bytes" );
}
}
System.out.print("/n(0x14-0x15)2个Bytes为Format Type ");
j=0; v[j]=c; j++; num++;
while ( ((c=fileRead.read ( )) != -1) && (num Stereo");}
}
}
System.out.print("/n(0x16-0x17)2个Bytes定义通道数 ");
j=0; v[j]=c; j++; num++;
while ( ((c=fileRead.read ( )) != -1) && (num <=24))
{
v[j]=c; j++; num++;
if(num==25)
{
"+v[1]+v[0]+"个Channel");
}
}
System.out.print("/n(0x18-0x1B)4个Bytes定义取样率:此wave档的取样频率
0x");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && (num =0;a--)
{
int x=n[a]/16; int y=n[a]%16;
decide2(x,y);
}
}
}
System.out.print("/n(0x1C-0x1F)4个Bytes定义每秒Bytes数量 0x");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && (num =0;a--)
{
int x=n[a]/16; int y=n[a]%16;
decide2(x,y);
}
}
}
System.out.print("/n(0x20-0x21)2个Bytes定义每个取样的Bytes数量 ");
j=0; v[j]=c; j++; num++;
while ( ((c=fileRead.read ( )) != -1) && (num =0;a--)
System.out.print (v[a]+" ") ;
}
}
System.out.print("/n(0x22-0x23)2个Bytes定义每个取样的Bits数量 ");
j=0; v[j]=c; j++; num++;
while ( ((c=fileRead.read ( )) != -1) && (num =0;a--)
System.out.print (v[a]+" ") ;
}
}
//-------------------------------------Data部份------------------------------------------------
System.out.println("/n/nData部份(0x24~)") ;
System.out.print("(0x24-0x27)4个Bytes固定为 ") ;
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && num <= 40)
{
n[j]=c; j++; num++;
if(num==41)
{
System.out.print("/" ");
for(int a=0;a<=3;a++)
{
int x=n[a]/16; int y=n[a]%16;
decide1(x,y);
System.out.print(" ");
}
System.out.print("/""+" "+"(");
for(int a=0;a<=3;a++)
System.out.print((char)n[a]);
System.out.print(")");
}
}
System.out.print("/n(0x28-0x2B)4个Bytes为Data Chunk长度 0x");
j=0; n[j]=c; j++; num++;
while ( (c=fileRead.read ( )) != -1 && (num =0;a--)
{
int x=n[a]/16; int y=n[a]%16;
decide2(x,y);
}
System.out.print(" Bytes" );
}
}
System.out.print("/n"); int e=0,d=0,g=0; double cc=0.0;
do
//do...while回圈将Data每1024比做一次加总,存入sum阵列中
{
data[0]=0.0;
int q=1; cc=(double)c/256; data[q]=cc; q++; num++;
g++;
while ( (c=fileRead.read ( )) != -1 && (num <= datasize-1+ 45+e))
{
cc=(double)c/256;
data[q]=cc;
q++;
num++; g++;
}
e=e+1023;
four1(data,datasize/2,1); //-----------------呼叫傅立叶转换
-----------------------------------
for(int p=1;p <=datasize;p++)
sum[d]+=data[p];
d++;
}while( c != -1);
double h=SUM(sum,as); //------------------呼叫比对运算---和声音1做运算
--------------------
double h1=SUM(sum,bs); //------------------呼叫比对运算---和声音2做运算
--------------------
double h2=SUM(sum,cs); //------------------呼叫比对运算---和声音3做运算
--------------------
System.out.println("/n与mioko计算结果差:"+h);
System.out.println("/n与anny计算结果差:"+h1);
System.out.println("/n与melody计算结果差:"+h2);
//----------------------------------------------------------------------------------------------------
fileRead.close () ;
}
}
原创粉丝点击