首页 > 编程知识 正文

圈圈教你玩USB 第七章 USB MIDI键盘 看书笔记,圈圈教你玩usb

时间:2023-05-05 15:41:25 阅读:285265 作者:1703

修改前的代码: 《圈圈教你玩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键盘,还有很多工作要做。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。