《圈圈教你玩USB》 第七章 USB MIDI键盘 看书笔记

修改前的代码: 《圈圈教你玩USB》第四章 USB键盘 UsbKeyboard

修改后的代码: 《圈圈教你玩USB》第七章 USBMIDI键盘 UsbMidiKeyboard

7.1 MIDI简介

7.2 MIDI的工作原理

1.MIDI控制命令之一:让指定通道发声——Note On Message
格式(16进制):9n, kk, vv。
                           9:表示该消息的ID;
                           n:表示发送到的通道,可以选择为0~15,共16个通道;
                            kk:表示音符的音高,60(十进制)为中央C音;
                            vv:表示演奏的力度,0为关闭声音,127为最大音量。

7.3 USB MIDI设备的数据流模型

   一个USB MIDI设备可以有很多模块,例如MIDI合成器、MIDI转换器(必须的)、插孔等。本文设计无合成器等模块,只有MIDI插孔和转换器。
模块作用:
   转换器:将各个插孔和模块连接起来;
   插孔    : 是一个抽象概念,供MIDI信息输入输出使用。
传递MIDI消息的方法:批量端点,一个批量端点可以传输多路MIDI消息。
    此处传输的不是原始的MIDI消息,而是由打包为32位的USB-MIDI事件包发送。
    eg:Note On消息,打包方法:
        在前面加上0xP9,其中P为某一路MIDI消息的编号,本实例只有一路,设置未0即可,故为0x09
 

7.4 设备描述符

    将设备描述符中的PID改为0x0008.

7.5 配置描述符集合

删除无用描述符:
   配置描述符集合中的HID描述符;
   报告描述符、获取报告描述符的代码;
添加新的描述符:
    MIDI流接口描述符;
    类特殊接口描述符;
    类特殊端点描述符;
详细见7.5.1~7.5.7和USB MIDI设备类的协议文档。


7.5.1 配置描述符

该USBMIDI键盘的配置需要两个接口。
修改内容:
   修改配置描述符中的bNumInterfaces为0x02。

7.5.2 音频控制类接口描述符

      每个音频设备都有一个音频控制接口AC(Audio Control),可以有多个音频流接口或者MIDI流接口。
    作用:这里,音频控制接口没有实际的用途,仅是协议强制要求而已。
   所以使用的端点数目为0,接口类为音频类(0x01),子类为音频控制子类(0x01),没有使用协议,为0值。
重点添加内容:
    bNumEndpoints为0;
    bInterfaceClass为0x01;
    bInterfaceSubClass为0x01;
    bInterfaceProtocol为0x00;

7.5.3 类特殊音频控制接口描述符

    作用:本描述符用来描述该音频控制接口的属性,这里仅有一个头描述符,用来说明从属于该音频控制接口的其他接口有哪些。
重点添加内容:
   这里只有一个流接口,并且它的编号为1。
    bInCollection 为0x01        //流接口的数目
    baInterfaceNr(1)为0x01   //哪个MIDI流接口属于该音频控制接口(这里为接口1,即MIDI流接口)

7.5.4 MIDI流接口描述符

    作用:使用两个批量端点分别输入和输出MIDI流数据。
   本描述符使用标准的接口描述符类型,将前面的音频控制接口描述符复制一份修改为MIDI流接口描述符。
重点修改部分:
    bInterfaceNumber改为0x01   //这里属于第二个接口
    bNumEndpoints改为0x02      //端点数目
    bInterfaceSubClass改为0x03//子类代码,0x03表示MIDI流接口子类

7.5.5 类特殊MIDI流接口描述符

    作用:描述USB MIDI设备内部各模块(包括元件与插孔)间的连接关系。
    插孔分类:内嵌输入插孔、内嵌输出插孔、外部输入插孔、外部输出插孔(方向是对设备来说的,与前面的端点方向刚好相反)。
    插孔(USB MIDI设备中)简介:

插孔数据流向:


 键盘的数据通过内嵌输出插孔输出给PC,但键盘的数据不能直接发送到内嵌输出插孔,而是假设有一个外部输入插孔(虚拟的),将该输入插孔连接到内嵌输出插孔的“输入引脚”上。

        对于PC端的数据也类似,数据将到达内嵌输入插孔,将内嵌输入插孔连接到外部输出插孔的输入引脚上,数据就可以发送出去了。

        各个插孔都有ID号,可以通过在各自插孔的描述符中指定某个插孔的ID号,把输入和输出插孔连接起来。

描述符简介:

        ① 类特殊MIDI流接口头描述符(class-specific MS interface header descriptor):头描述符,引导下面三种类特殊MIDI接口描述符。

        ② MIDI输入插孔描述符(MIDI IN jack descriptor)    :描述输入插孔;

        ③ MIDI输出插孔描述符(MIDI OUT jack descriptor):描述输出插孔;

        ④ 元件描述符(element descriptor)                             :描述USB MIDI设备的元件,例如MIDI合成器等(本例未用到)

描述符的结构和具体实现:

        ① 类特殊MIDI流接口头描述符:

           

        ② MIDI输入插孔描述符:
                bJackType为插孔的类型,可以选择内嵌(0x01)或者外部(0x02);
                bJackID    为插孔的唯一ID,可以用在输出插孔的输入源选择上。
                
         ③ MIDI输出插孔描述符
            前5个字节同输入插孔意义相同。
            

            中间的省略号部分表示可以有多组baSourceID和BaSourcePin;

            如“插孔数据流向”部分左图所示,这里指定内嵌输入插孔的ID为1;外部输入插孔的ID为2;内嵌输出(到PC)插孔的ID为3;外部输出(从PC)插孔的ID为4,则:

            在内嵌输出插孔的baSourceID中指定ID为2,即外部输入插孔;

            在外部输出插孔的baSourceID中指定ID为1,即内嵌输入插孔。

         ④ 元件描述符(Element Descriptor)在本实例中未用到,这里不详述了。可以在USB MIDI设备协议中查到。
最终设置好的类特殊MIDI流接口描述符包含下列部分:
        /************* 内嵌输入插孔描述符***********/

        /************* 外部输入插孔描述符***********/

        /************* 内嵌输出插孔描述符***********/

        /************* 外部输出插孔描述符***********/

 7.5.6 端点描述符和类特殊端点描述符

作用:
   标准批量数据端点描述符:
   类特殊MIDI流批量数据端点描述符:描述内嵌插孔是如何在端点上组织的,跟在每个标准的批量数据端点描述符后面。
描述符的结构:
    ① 类特殊MIDI流批量数据端点描述符
        
        bNumEmbMIDIJack:本例仅有一个内嵌输入插孔或内嵌输出插孔,所以为1;
        baAssocJackID       :可以有多个,视该端点内嵌插孔的数量而定。对于输入端点,指定为内嵌输出插孔的ID,对于输出
                                             端点,指定为内嵌输入插孔的ID。
最终设置好的代码如下:
        /***************标准批量数据输入端点描述符*********/
        /*************** 类特殊MIDI流批量数据端点描述符***/
        /***************标准批量数据输出端点描述符*********/
        /*************** 类特殊MIDI流批量数据端点描述符***/

7.5.7 字符串描述符

修不修改无所谓

7.6 修改好描述符后的测试

 

7.7 USB MIDI键盘的数据返回

本MIDI键盘的功能:产生一条Note On(音符开)消息。
32位MIDI事件包格式(16进制)为:P9,9n,kk,vv,其中
        第一个字节P9:为USB MIDI协议中增加的包头,P为某一路MIDI消息的编号(这里仅有一个内嵌输出插孔,为0);9为该包的ID标识。
        后面三个字节为实际的MIDI消息,9n表示在通道n上发送Note On消息;kk表示音符的音高;vv表示音符的力度(响度,127为最大声)。
按键对应音高:KEY1~KEY8对应简谱的5、6、1、2、3、5、6、1,第一个1为中央C,按照书中的介绍,可以计算出它们在MIDI消
                          息中的音高值分别为55、57、60、62、64、67、69、72。
代码修改:将原来返回报告的函数SendReport改为SendNoteOnMsg,在该函数中根据不同按键的按下和抬起来发送Note On消息。
                   当某个按键按下时,就发送力度值为最大的Note On消息,让某个音符发声;当某个按键弹起时,就发送力度为0的消
                   息,这将停止该音符的发声。
数据输出:因为实验板无法设置为31.25kb/s波特率,所以对于端点2输出的数据直接丢弃。仅在端点2输出中断处理中清除中断标志
                   和清空缓冲区。
                   需要注意的是,输出数据的处理速度一定要够,否则可能导致应用软件停止响应甚至整个操作系统崩溃。如果设备用不
                   到MIDI输出,则干脆在外部输出插孔描述符中修改baSourceID为0x02,选择输入源为外部输入插孔。这样内嵌输入插孔
                   就没有被使用,从而Windows就不会增加MIDI输出设备。正常使用时,将config.h中定义的调试宏删除,避免多余的消耗。

7.8 USB MIDI键盘的使用 

使用HappyEO电子琴软件
                  
       该MIDI键盘连接上PC后,会产生一个MIDI输出设备,Windows操作系统有时会自动将它选择为“MIDI音乐播放”的默认设备。如果此时播放一个MIDI文件,数据将发送到USB MIDI设备上去,从而PC声卡无声音输出。可以进入控制面板的“ 声音和音频设备 ”中选择需要的MIDI设备。如下图所示,一般声卡使用“ Microsoft GS波表软件合成器”。如果不想改这个MIDI音乐播放器的默认设置,参考7.7中最后一段。
                  

7.9 单片机自动弹奏的实现

找到曲子后,把每个音符翻译成MIDI消息,然后按照谱子中给定的时间间隔发送就行了。
减少数据存储量的方法:
        定义一种结构,曲子以行的格式保存,每行在同一个时刻(实际上是比较短的时间内分多个包发送的)声音。每行第一个字节为该行需要发声的个数;接下来的两个字节反别是音符的音高和力度,一行有多个音节时,音高和力度如此重复下去;最后两个字节为该行音符发送后停留的时间。
    整首曲子保存在一个数组中,数组的前两个字节为整个曲子的行数,接下来就是一行行的MIDI消息数据。具体数据格式参考源代码中的song.c文件,里面包含了曲子的数据和播放曲子的函数。
怎么区分通道:
    旋律音和打击乐是在不同通道上的,代码中利用0xff(数据中未用到0xff)作为通道切换指示。当遇到0xff时,就切换到另一个通道。
    代码中同时按下KEY1和KEY8后,开始自动播放。

7.10 本章小结

本章简单介绍了USB MIDI键盘的实现,MIDI以及音乐方面的知识;
               实现了USB MIDI设备的基本功能。
若要制作一个完整、符合规范的USB MIDI键盘,还有很多工作要做。


  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/* 也是从其他地方搞过来的,花了不少积分,我就1分奉献了吧,绝对是好书~ */ 第1章USB概述及协议基础 1.1USB是什么 1.2USB的特点 1.3USB的拓扑结构 1.4USB的电气特性 1.5USB的线缆、插头及插座 1.6USB的插入检测机制 1.7USB的描述符及其之间的关系 1.8USB设备的枚举过程 1.9USB的包结构及传输过程 1.9.1USB的包结构及包的分类 1.9.2令牌包 1.9.3数据包 1.9.4握手包 1.9.5特殊包 1.9.6如何处理数据包 1.10USB的四种传输类型 1.10.1USB事务 1.10.2批量传输 1.10.3中断传输 1.10.4等时传输 1.10.5控制传输 1.10.6端点类型与传输类型的关系 1.10.7传输类型与端点支持的最大包长 1.11本章小结 第2章硬件系统设计 2.1方案及芯片的选定 2.2D12引脚功能说明 2.3D12与AT89S52的连接 2.4串口部分电路 2.5按键部分 2.6指示灯部分 2.7IDE接口部分 2.8单片机部分 2.9元件安装 2.10电路调试 2.11测试程序的编写和调试 2.11.1建立一个工程 2.11.2为工程添加源文件 2.11.3KEIL工具栏及仿真介绍 2.11.4按键驱动的编写 2.11.5串口驱动的编写 2.11.6PDIUSBDl2读写函数及读ID的实现 2.12本章小结 第3章USB鼠标的实现 3.1USB鼠标工程的建立 3.2USB的断开与连接 3.3USB中断的处理 3.4读取从主机发送到端点O的数据 3.5USB标准请求 3.5.1USB标准设备请求的结构 3.5.2GET_DESCRIPTOR请求 3.5.3SET_ADDRESS请求 3.5.4SETCONFIGURATION请求 3.6设备描述符的实现 3.7设备描述符的返回 3.8设置地址请求的处理 3.9配置描述符集合的结构 3.9.1配置描述符的结构 3.9.2接口描述符的结构 3.9.3端点描述符的结构 3.9.4HID描述符的结构 3.10配置描述符集合的实现以及返回 3.11字符串及语言ID请求的实现 3.12设置配置请求的实现 3.13报告描述符的结构及实现 3.14报告的返回 3.15BusHound工具的简介 3.16本章小结 第4章USB键盘的实现 4.1USB键盘工程的建立 4.2设备描述符的实现 4.3配置描述符集合的实现 4.3.1配置描述符 4.3.2接口描述符 4.3.3HID描述符_ 4.3.4端点描述符 4.4字符串描述符 4.5报告描述符 4.6输入和输出报告的实现 4.7 USB键盘实例的测试 4.8再谈USBHID的报告描述符 4.9带鼠标功能的USB键盘(方法一) 4.10带鼠标功能的USB键盘(方法二) 4.11多媒体USB键盘 4.12本章小结 第5章用户自定义的USBHID备 5.1MyUsbHid工程的建立 5.2描述符的修改 5.3报告的实现 5.4对用户自定义的USBHID设备的访问 5.5访问HID设备时所用到的相关函数 5.5.1 获取HID设备的接口类GUID的函数 5.5.2获取指定类的所有设备信息集合的函数 5.5.3从设备信息集合中获取一个设备接口信息的函数 5.5.4获取指定设备接口详细信息的函数 5.5.5打开设备的函数 5.5.6获取HID设备属性的函数 5.5.7从设备读取数据的函数 5.5.8往设备写数据的函数 5.5.9通过控制端点O读取报告的函数 5.5.10通过控制端点O发送报告的函数 5.5.11关闭句柄的函数 5.5.12需要包含的库文件 5.6访问USBHID设备的上位机软件的实现 5.6.1上位机程序编写的思路 5.6.2查找及打开HID设备的代码 5.6.3读输入报告线程的代码 5.6.4写输出报告的代码(发送LED的状态) 5.6.5写输出报告线程的代码 5.6.6线程的创建以及设备插拔事件的注册 5.6.7对设备状态改变事件的处理 5.7软件界面以及使用方法 5.8本章小结 第6章USB转串口 6.1串口家族历史 6.2串口接头的引脚分布及功能 6.3USB转串口的实现方法 6.4设备描述符 6.5字符串描述符 …… 第7章USBMIDI键盘 第8章U盘 第9章自定义USB设备及驱动开发 第10章USB过滤驱动开发 附录第3章实例的完整调试信息 参考文献 后记 ……

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值