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

    xiaoxiao2021-03-26  29

    修改前的代码: 《圈圈教你玩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键盘,还有很多工作要做。
    转载请注明原文地址: https://ju.6miu.com/read-500049.html

    最新回复(0)