- UID
- 1
- 精华
- 积分
- 76361
- 威望
- 点
- 宅币
- 个
- 贡献
- 次
- 宅之契约
- 份
- 最后登录
- 1970-1-1
- 在线时间
- 小时
|
来源:http://www.0xaa55.com/thread-1489-1-1.html
转载请保留出处。
因为读写文件、发送Midi指令的函数都需要用户来亲自完成(实现接口)因此,可以实现跨平台。
不过目前我只写了个Windows平台的播放实例。DOS的以后会写出能在DOSBox播放Midi音乐的。
另外我这个的播放效果比mciSendCommand(或mciSendString)播放Midi文件的效果好很多。mciSendXXXX这玩意儿播放Midi存在跳音符的问题(比如某些鼓点没打上、节奏出现问题等),而我的这个Midi解析库则解决了这个问题。
midifile.h:- //=============================================================================
- //作者:0xAA55
- //论坛:http://www.0xaa55.com/
- //版权所有(C) 2013-2015 技术宅的结界
- //请保留原作者信息,否则视为侵权。
- //-----------------------------------------------------------------------------
- #ifndef _MIDI_Parser_
- #define _MIDI_Parser_
- #include<stdint.h>
- //=============================================================================
- //宏,可以用于调整调用约定、符号导入导出规则等
- //-----------------------------------------------------------------------------
- //MIDIFILE的函数调用约定
- #ifndef Midi_c
- #define Midi_c _cdecl
- #endif // !Midi_c
- //MIDIFILE的回调函数调用约定
- #ifndef Midi_cb
- #define Midi_cb _cdecl
- #endif // !Midi_cb
- //MIDIFILE的符号导出规则
- #ifndef Midi_x
- #ifdef __cplusplus
- #define Midi_x extern"C"
- #else // !__cplusplus
- #define Midi_x extern
- #endif // !__cplusplus
- #endif // !Midi_x
- //MIDIFILE的导出函数
- #define Midi_fn(fn,rt) Midi_x rt Midi_c fn
- //用户自定义数据的格式
- #ifndef Midi_UserData_t
- #define Midi_UserData_t void
- #endif
- #ifndef Midi_UserData_p
- #define Midi_UserData_p Midi_UserData_t*
- #endif
- //为了防止出现fpos_t未定义的问题,这里定义一次。
- typedef __int64 fpos_t;
- //Midi时间刻度
- #ifndef MidiTime_t
- #define MidiTime_t double
- #endif
- #ifndef MidiTime_p
- #define MidiTime_p MidiTime_t*
- #endif
- //-----------------------------------------------------------------------------
- //=============================================================================
- //错误代码
- //-----------------------------------------------------------------------------
- typedef enum
- {
- ME_OK = 0, //无错误
- ME_OutOfMem, //内存不足
- ME_TellFail, //取得文件指针失败
- ME_SeekFail, //设置文件指针失败
- ME_ReadFail, //读取失败
- ME_BadFile //文件格式错误
- }MidiErrCode_t, * MidiErrCode_p;
- //=============================================================================
- //用户回调函数,用户必须提供实现的函数
- //-----------------------------------------------------------------------------
- //读取文件:要求成功返回非零,失败返回0
- typedef int(Midi_cb* ReadFile_f)
- (
- void* pBuf, //缓冲区
- size_t cb, //要读取的字节数
- Midi_UserData_p pUserData //用户数据
- );
- //-----------------------------------------------------------------------------
- //取得文件指针:要求成功返回非零,失败返回0
- typedef int(Midi_cb* GetFilePtr_f)
- (
- fpos_t* pPosition, //取得位置
- Midi_UserData_p pUserData //用户数据
- );
- //-----------------------------------------------------------------------------
- //设置文件指针:要求成功返回非零,失败返回0
- typedef int(Midi_cb* SetFilePtr_f)
- (
- fpos_t Position, //新的位置
- Midi_UserData_p pUserData //用户数据
- );
- //-----------------------------------------------------------------------------
- //发送MIDI命令
- typedef void(Midi_cb* SendMidiMsg_f)
- (
- uint32_t TickCount, //当前命令时钟
- uint16_t TrackNo, //当前音轨号
- uint8_t Command, //命令
- uint8_t Param1, //参数1
- uint8_t Param2, //参数2
- Midi_UserData_p pUserData //用户数据
- );
- //-----------------------------------------------------------------------------
- //错误处理
- typedef void(Midi_cb* Error_f)(MidiErrCode_t);
- //-----------------------------------------------------------------------------
- //=============================================================================
- //MIDI解析器的音轨数据
- //-----------------------------------------------------------------------------
- typedef struct
- {
- fpos_t StartPos; //音轨开始的文件偏移
- fpos_t TrackSize; //音轨大小
- fpos_t NextNotePos;//音轨下个音符的文件偏移
- uint32_t LastNoteTick;//上个音符的时钟
- uint32_t TotalTicks;//这个音轨的总时钟数
- uint8_t EndOfTrack; //是否到达了当前轨道的末尾
- uint8_t LastCommand;//上个命令
- uint8_t LastParam1; //上个命令的参数1
- uint8_t LastParam2; //上个命令的参数2
- }TrackData_t, * TrackData_p;
- //=============================================================================
- //MIDI解析器结构体
- //-----------------------------------------------------------------------------
- typedef struct
- {
- //=========================================================================
- //用户需要先填写如下的参数,才能使用MidiFile接口
- ReadFile_f pfnReadFile; //读取文件回调
- GetFilePtr_f pfnGetFilePtr; //设置文件指针回调
- SetFilePtr_f pfnSetFilePtr; //设置文件指针回调
- SendMidiMsg_f pfnSendMidiMsg; //发送Midi指令回调
- Error_f pfnOnError; //错误处理回调
- Midi_UserData_p pUserData; //用户自定义数据
- //-------------------------------------------------------------------------
- uint16_t wType; //Midi文件类型
- uint16_t wNbTracks; //音轨数
- uint16_t wTicksPerCrotchet; //每四分音符的Tick数
- TrackData_p pTrackData; //音轨数据
- uint32_t Velocity; //播放速度,一个四分音符的微秒数
- uint32_t TotalTicks; //整个Midi文件的时钟数(长度)
- uint8_t EndOfFile; //是否已经更新到了结尾
- MidiErrCode_t LastErr; //错误代码
- }MidiParser_t, * MidiParser_p;
- //MIDI文件的类型
- #define MIDIT_SingleTrack 0 /*单音轨*/
- #define MIDIT_MultiSync 1 /*同步多音轨*/
- #define MIDIT_MultiAsync 2 /*异步多音轨*/
- //=============================================================================
- //MidiParserStart:
- //分析MIDI文件,为播放做准备。失败返回零,成功返回非零
- //-----------------------------------------------------------------------------
- Midi_fn(MidiParserStart, int)
- (
- MidiParser_p pParser
- );
- //=============================================================================
- //MidiParserUpdate:
- //更新播放状态到当前时间
- //-----------------------------------------------------------------------------
- Midi_fn(MidiParserUpdate, int)
- (
- MidiParser_p pParser,
- MidiTime_t dbMilliSeconds,
- uint8_t uSilent
- );
- //=============================================================================
- //MidiParserReset:
- //重置Midi解析器状态
- //-----------------------------------------------------------------------------
- Midi_fn(MidiParserReset, void)
- (
- MidiParser_p pParser
- );
- //=============================================================================
- //MidiParserParseToEnd:
- //分析整个文件
- //-----------------------------------------------------------------------------
- Midi_fn(MidiParserParseToEnd, int)
- (
- MidiParser_p pParser
- );
- //=============================================================================
- //MidiParserShutdown:
- //关闭Midi解析器
- //-----------------------------------------------------------------------------
- Midi_fn(MidiParserShutdown, void)
- (
- MidiParser_p pParser
- );
- /******************************************************************************
- 注释
- 1、播放
- ·用户需要自己编写读取文件、定位文件指针的函数、发送Midi字节的函数(按照本头文
- 件所描述)的规格进行编写。然后按照说明填写MidiParser_t结构体的成员。
- ·填写完成后,调用MidiParserStart,初始化播放器。
- ·随后,不断调用MidiParserUpdate(并提供播放时间)进行Midi文件的播放。播放时间
- 的数值只能向后增长。
- ·播放完最后一个音符后,MidiParser_t的成员EndOfFile将会被置1。如需重新播放,请
- 调用MidiParserReset,然后重新计时并重复上一个步骤。
- ·不需要播放的时候,请调用MidiParserShutdown完成最后的处理。
- ·请注意,如果你在Windows使用mciSendCommand播放Midi音乐,你会遇到跳音符的问题。
- 而本库所使用的算法比Windows的mciSendCommand好,因此没有这个问题。请使用本库。
- 2、定位到指定播放位置(慎用:可能导致播放乐曲的效果偏离作曲者的设定)
- ·调用MidiParserReset重置播放状态。
- ·随后,调用MidiParserUpdate并提供你要定位的时间值(如果设置Silent参数的话,
- MidiParserUpdate将不会发送Midi字节)。
- 3、本程序也可用于Midi文件的解析器——获取每个Midi指令、时间、轨道等信息。
- 注:本程序对于格式有问题的Midi文件并不能做到很好的解析——要求:
- ·每个Midi音轨的最后一个音符必须是元数据——音轨结束标识
- ******************************************************************************/
- #endif // !_MIDI_Parser_
复制代码 midifile.c:- //=============================================================================
- //作者:0xAA55
- //论坛:http://www.0xaa55.com/
- //版权所有(C) 2013-2015 技术宅的结界
- //请保留原作者信息,否则视为侵权。
- //-----------------------------------------------------------------------------
- #include"midifile.h"
- #include<stdio.h>
- #include<malloc.h>
- #include<memory.h>
- #pragma pack(push,1)
- //MIDI文件头的结构体
- typedef struct
- {
- uint32_t dwFlag; //MThd标识
- uint32_t dwRestLen; //剩余部分长度
- uint16_t wType; //类型
- uint16_t wNbTracks; //音轨数
- uint16_t wTicksPerCrotchet; //每四分音符的Tick数
- }MIDIHeader, * MIDIHeaderP;
- #pragma pack(pop,1)
- //MIDI文件中出现过的标识
- #define MIDI_MThd 0x6468544D
- #define MIDI_MTrk 0x6B72544D
- //各种长度的Big-Endian到Little-Endian的转换
- #define BSwapW(x) ((((x) & 0xFF)<<8)|(((x) & 0xFF00)>>8))
- #define BSwap24(x) ((((x) & 0xFF)<<16)|((x) & 0xFF00)|(((x) & 0xFF0000)>>16))
- #define BSwapD(x) ((((x) & 0xFF)<<24)|(((x) & 0xFF00)<<8)|(((x) & 0xFF0000)>>8)|(((x) & 0xFF000000)>>24))
- //用于读取文件的宏。要使用它,函数必须要有MidiParser_p pParser参数
- #define ErrC(e) {pParser->LastErr=(e);pParser->pfnOnError((e));goto BadRet;}
- #define Tell(x) if(!pParser->pfnGetFilePtr((x),pParser->pUserData))\
- ErrC(ME_TellFail)
- #define Seek(x) if(!pParser->pfnSetFilePtr((x),pParser->pUserData))\
- ErrC(ME_SeekFail)
- #define Read(x) if(!pParser->pfnReadFile(&(x),sizeof((x)),pParser->pUserData))\
- ErrC(ME_ReadFail)
- #define Skip(x) {fpos_t __CurPos;Tell(&__CurPos);__CurPos+=(x);Seek(__CurPos);}
- //=============================================================================
- //ReadDynamicByte:
- //读取动态字节,最多读取4字节。返回读取的字节数
- //-----------------------------------------------------------------------------
- static size_t ReadDynamicByte(MidiParser_p pParser, uint32_t* pOut)
- {
- size_t BytesRead;//已读取的字节数
- uint32_t uVal = 0;
- for (BytesRead = 1; BytesRead <= 4; BytesRead++)//最多读取4字节
- {
- uint8_t Value;
- Read(Value);//读取一个字节
- uVal = (uVal << 7) | (Value & 0x7F);//新读入的是低位
- if (!(Value & 0x80))//如果没有后续字节
- break;//就停止读取。
- }
- *pOut = uVal;
- return BytesRead;//返回读取的字节数
- BadRet:
- *pOut = uVal;
- return 0;
- }
- //=============================================================================
- //GetCommandNbParams:
- //取得MIDI命令的参数个数
- //-----------------------------------------------------------------------------
- static uint8_t GetCommandNbParams(uint8_t bEvent)
- {
- if (bEvent <= 0x7F)return 0;//上个命令的参数
- else if (bEvent <= 0x8F)return 2;//两个参数
- else if (bEvent <= 0x9F)return 2;//两个参数
- else if (bEvent <= 0xAF)return 2;//两个参数
- else if (bEvent <= 0xBF)return 2;//两个参数
- else if (bEvent <= 0xCF)return 1;//一个参数
- else if (bEvent <= 0xDF)return 1;//一个参数
- else if (bEvent <= 0xEF)return 2;//两个参数
- else if (bEvent <= 0xFF)return 0;//机器码
- else return 2;//元数据
- }
- //=============================================================================
- //DefOnError:
- //默认的错误提示函数
- //-----------------------------------------------------------------------------
- static void Midi_cb DefOnError(MidiErrCode_t Err)
- {
- //什么也不做
- }
- //=============================================================================
- //MidiParserStart:
- //分析MIDI文件,为播放做准备。失败返回零,成功返回非零
- //-----------------------------------------------------------------------------
- Midi_fn(MidiParserStart, int)
- (
- MidiParser_p pParser
- )
- {
- MIDIHeader midiHeader;
- size_t i;
- pParser->wType = 0;
- pParser->wNbTracks = 0;
- pParser->wTicksPerCrotchet = 0;
- pParser->pTrackData = NULL;
- pParser->Velocity = 0;
- pParser->TotalTicks = 0;
- pParser->EndOfFile = 0;
- pParser->LastErr = ME_OK;
- if (!pParser->pfnOnError)
- pParser->pfnOnError = DefOnError;
- //MIDI文件头就是一个MIDIHeader结构体。
- //但是要注意其中的数值都是Big-Endian存储的
- //需要进行转换
- //读取MIDI文件头
- Read(midiHeader);
- //检查文件格式
- if (midiHeader.dwFlag != MIDI_MThd)//标识必须是"MThd"
- ErrC(ME_BadFile);
- //转换为Little-Endian
- midiHeader.dwRestLen = BSwapD(midiHeader.dwRestLen);
- midiHeader.wType = BSwapW(midiHeader.wType);
- midiHeader.wNbTracks = BSwapW(midiHeader.wNbTracks);
- midiHeader.wTicksPerCrotchet = BSwapW(midiHeader.wTicksPerCrotchet);
- //分析器数据
- pParser->wType = midiHeader.wType;
- pParser->wNbTracks = midiHeader.wNbTracks;
- pParser->wTicksPerCrotchet = midiHeader.wTicksPerCrotchet;
- //跳转到MIDI音轨的位置
- Seek(8 + midiHeader.dwRestLen);
- //轨道信息
- i = sizeof(TrackData_t) * pParser->wNbTracks;//轨道信息总大小
- pParser->pTrackData = (TrackData_p)malloc(i);//分配内存
- if (!pParser->pTrackData)
- ErrC(ME_OutOfMem);
- memset(pParser->pTrackData, 0, i);//清零
- //准备读取各个音轨
- for (i = 0; i < midiHeader.wNbTracks; i++)
- {
- uint32_t dwTrackFlag;
- uint32_t dwTrackLen;
- uint8_t LastEvent = 0;//上一个事件
- uint8_t EndOfTrack;//是否读取到了音轨的结束位置
- //每个音轨的开头都是一个dwTrackFlag和一个dwTrackLen
- //dwTrackFlag的值必须是MIDI_MTrk
- //dwTrackLen标记了下一个音轨的位置
- fpos_t TrackStartPos;
- //轨道标记
- Read(dwTrackFlag);
- if (dwTrackFlag != MIDI_MTrk)
- ErrC(ME_BadFile);
- //轨道长度
- Read(dwTrackLen);
- dwTrackLen = BSwapD(dwTrackLen);//转换Big-Endian
- Tell(&TrackStartPos);//记录当前位置
- pParser->pTrackData[i].StartPos = TrackStartPos;
- pParser->pTrackData[i].TrackSize = dwTrackLen;
- pParser->pTrackData[i].NextNotePos = TrackStartPos;
- pParser->pTrackData[i].LastNoteTick = 0;
- pParser->pTrackData[i].EndOfTrack = 0;
- //音轨的重要内容是事件数据
- for (EndOfTrack = 0; !EndOfTrack;)//循环读取事件
- {
- uint32_t dwDelay;
- uint8_t bEvent;
- //每个事件的构成都是:
- //延时,事件号,参数
- //其中的延时是动态字节,参数大小随事件号而定
- //读取延时
- if (!ReadDynamicByte(pParser, &dwDelay))
- ErrC(ME_BadFile);
- //统计当前音轨总时钟数
- pParser->pTrackData[i].TotalTicks += dwDelay;
- //读取事件号
- Read(bEvent);
- //分析事件
- if (bEvent <= 0x7F)
- {//0到0x7F:和上一个事件的事件号相同,而读取的这个字节就是上一个事件
- //号的参数的第一个字节
- Skip(GetCommandNbParams(LastEvent) - 1);
- bEvent = LastEvent;
- }
- else if (bEvent <= 0xEF)
- {//基本的MIDI命令
- Skip(GetCommandNbParams(bEvent));
- }
- else if (bEvent == 0xF0)
- {//0xF0:系统码
- uint8_t bSysCode = 0;
- do
- {
- Read(bSysCode);
- } while (bSysCode != 0xF7);
- }
- else if (bEvent == 0xFF)
- {//元事件
- uint8_t bType, Bytes;
- fpos_t CurrentPos;
- Read(bType);//元数据类型
- Read(Bytes);//元数据字节数
- Tell(&CurrentPos);//记录元数据读取的位置
- switch (bType)
- {
- case 0x2F://音轨结束标识
- EndOfTrack = 1;
- break;
- case 0x51://速度
- {
- uint8_t bVelocity1, bVelocity2, bVelocity3;
- //速度是24位整数,一个四分音符的微秒数。
- Read(bVelocity1);
- Read(bVelocity2);
- Read(bVelocity3);
- if (!pParser->Velocity)
- pParser->Velocity =
- ((uint32_t)bVelocity3 |
- ((uint32_t)bVelocity2 << 8) |
- ((uint32_t)bVelocity1 << 16));
- }
- break;
- default:
- break;
- }
- Seek(CurrentPos + Bytes);//读取完成后正确跳到下一个事件的位置。
- }
- else//其它事件,未知事件
- {
- ErrC(ME_BadFile);
- }
- LastEvent = bEvent;
- }//回去继续读取事件。
- //如果是异步多音轨的话,记录整个Midi文件的播放时长
- if (pParser->wType == MIDIT_MultiAsync)
- {
- //异步多音轨,同级总时长
- pParser->TotalTicks += pParser->pTrackData[i].TotalTicks;
- }
- else//否则(单音轨、同步多音轨)以时长最长的音轨为整个音乐的长度。
- {
- //同步多音轨或单音轨,找到时长最长的音轨。
- if (pParser->pTrackData[i].TotalTicks > pParser->TotalTicks)
- pParser->TotalTicks = pParser->pTrackData[i].TotalTicks;
- }
- Seek(TrackStartPos + dwTrackLen);//转到下一个音轨
- }
- return 1;
- BadRet:
- MidiParserShutdown(pParser);
- return 0;
- }
- //=============================================================================
- //MidiParserUpdate:
- //更新播放状态到当前时间
- //-----------------------------------------------------------------------------
- Midi_fn(MidiParserUpdate, int)
- (
- MidiParser_p pParser,
- MidiTime_t dbMilliSeconds,
- uint8_t uSilent
- )
- {
- uint16_t CurTrack;
- uint32_t CurTick;
- if (dbMilliSeconds < 0)
- dbMilliSeconds = 0;
- //当前时钟数
- CurTick = (uint32_t)(dbMilliSeconds * 1000.0 * pParser->wTicksPerCrotchet / pParser->Velocity);
- //检查是否播放到了结尾
- if (CurTick >= pParser->TotalTicks)
- {
- pParser->EndOfFile = 1;
- }
- for (CurTrack = 0; CurTrack < pParser->wNbTracks; CurTrack++)
- {
- TrackData_p pData = &(pParser->pTrackData[CurTrack]);
- //每个事件的构成都是:
- //延时,事件号,参数
- //其中的延时是动态字节,参数大小随事件号而定
- //异步多音轨单独处理
- if (pParser->wType == MIDIT_MultiAsync)
- {
- if (CurTick >= pData->TotalTicks)
- {
- CurTick -= pData->TotalTicks;
- continue;
- }
- }
- for (; !pData->EndOfTrack; )
- {
- uint32_t dwDelay;
- uint8_t bEvent;
- //定位到当前轨道的下一个音符的位置
- Seek(pData->NextNotePos);
- //读取延时
- if (!ReadDynamicByte(pParser, &dwDelay))
- ErrC(ME_BadFile);
- if (CurTick >= pData->LastNoteTick + dwDelay)
- {
- pData->LastNoteTick += dwDelay;
- //读取事件号
- Read(bEvent);
- //分析事件
- if (bEvent <= 0x7F)
- { //0到0x7F:和上一个事件的事件号相同,而读取的这个字节就是上一
- //个事件号的参数的第一个字节
- uint8_t NbParams = GetCommandNbParams(pData->LastCommand);
- if (NbParams == 1)
- {
- pData->LastParam1 = bEvent;
- if (!uSilent)
- {
- pParser->pfnSendMidiMsg(pData->LastNoteTick,
- CurTrack, pData->LastCommand, bEvent, 0,
- pParser->pUserData);
- }
- }
- else if (NbParams == 2)
- {
- uint8_t bParam2;
- Read(bParam2);
- pData->LastParam1 = bEvent;
- pData->LastParam2 = bParam2;
- if (!uSilent)
- {
- pParser->pfnSendMidiMsg(pData->LastNoteTick,
- CurTrack, pData->LastCommand, bEvent, bParam2,
- pParser->pUserData);
- }
- }
- }
- else if (bEvent <= 0xEF)
- {
- uint8_t NbParams = GetCommandNbParams(bEvent);
- pData->LastCommand = bEvent;
- if (NbParams == 1)
- {
- uint8_t bParam1;
- Read(bParam1);
- pData->LastParam1 = bParam1;
- if (!uSilent)
- {
- pParser->pfnSendMidiMsg(pData->LastNoteTick,
- CurTrack, bEvent, bParam1, 0, pParser->pUserData);
- }
- }
- else if (NbParams == 2)
- {
- uint8_t bParam1, bParam2;
- Read(bParam1);
- Read(bParam2);
- pData->LastParam1 = bParam1;
- pData->LastParam2 = bParam2;
- if (!uSilent)
- {
- pParser->pfnSendMidiMsg(pData->LastNoteTick,
- CurTrack, bEvent, bParam1, bParam2,
- pParser->pUserData);
- }
- }
- }
- else if (bEvent == 0xF0)
- {//0xF0:系统码
- uint8_t bSysCode = 0;
- do
- {
- Read(bSysCode);
- } while (bSysCode != 0xF7);
- }
- else if (bEvent == 0xFF)
- {//元事件
- uint8_t bType, Bytes;
- fpos_t CurrentPos;
- Read(bType);//元数据类型
- Read(Bytes);//元数据字节数
- Tell(&CurrentPos);//记录元数据读取的位置
- switch (bType)
- {
- case 0x2F://音轨结束标识
- pData->EndOfTrack = 1;
- break;
- }
- Seek(CurrentPos + Bytes);//读取完成后正确跳到下一个事件的位置
- }
- else//其它事件,未知事件
- {
- ErrC(ME_BadFile);
- }
- Tell(&(pData->NextNotePos));
- if (pData->NextNotePos >= pData->StartPos + pData->TrackSize)
- pData->EndOfTrack = 1;
- }
- else
- break;
- }
- //异步多音轨单独处理
- if (pParser->wType == MIDIT_MultiAsync)
- {
- //防止同步播放其它音轨
- if (CurTick < pData->TotalTicks)
- break;
- }
- }
- return 1;
- BadRet:
- MidiParserShutdown(pParser);
- return 0;
- }
- //=============================================================================
- //MidiParserParseToEnd:
- //分析整个文件
- //-----------------------------------------------------------------------------
- Midi_fn(MidiParserParseToEnd, int)
- (
- MidiParser_p pParser
- )
- {
- return MidiParserUpdate(pParser, (double)(pParser->TotalTicks) * pParser->Velocity / pParser->wTicksPerCrotchet * 0.001, 0);
- }
- //=============================================================================
- //MidiParserReset:
- //重置Midi解析器状态
- //-----------------------------------------------------------------------------
- Midi_fn(MidiParserReset, void)
- (
- MidiParser_p pParser
- )
- {
- uint16_t CurTrack;
- for (CurTrack = 0; CurTrack < pParser->wNbTracks; CurTrack++)
- {
- pParser->pTrackData[CurTrack].NextNotePos =
- pParser->pTrackData[CurTrack].StartPos;
- pParser->pTrackData[CurTrack].LastNoteTick = 0;
- pParser->pTrackData[CurTrack].EndOfTrack = 0;
- }
- pParser->EndOfFile = 0;
- }
- //=============================================================================
- //MidiParserShutdown:
- //关闭Midi解析器
- //-----------------------------------------------------------------------------
- Midi_fn(MidiParserShutdown, void)
- (
- MidiParser_p pParser
- )
- {
- if (pParser)
- {
- free(pParser->pTrackData);
- pParser->pTrackData = NULL;
- pParser->wType = 0;
- pParser->wNbTracks = 0;
- pParser->wTicksPerCrotchet = 0;
- pParser->Velocity = 0;
- pParser->TotalTicks = 0;
- pParser->EndOfFile = 0;
- }
- }
- #undef ErrC
- #undef Tell
- #undef Seek
- #undef Read
复制代码 entry.c:
有趣的是,这个CMD窗口显示的内容很有节奏感233(它会随着音乐跳动)
这东西除了可以做成Midi播放器以外,还可以做成解析器,用来分析一个Midi音乐(偷谱)
BIN:
MIDIPlayBin.7z
(46.88 KB, 下载次数: 56)
SRC:
BIN的用法:将一个midi文件拖到它的图标上,松开鼠标就开始播放了。
|
|