VB.NET编写MIDI文件解析器,多轨读取触发EOF异常求助
多轨MIDI解析错误的问题排查与修复
你的代码存在几个核心问题,导致多轨文件解析时提前触发EndOfStream异常,下面逐个说明并给出修复方案:
1. 错误的"MTrk"轨道标识读取逻辑
你用字符串拼接循环查找"MTrk"的方式完全不符合MIDI文件规范,会直接破坏字节读取位置:
- 标准MIDI文件中,每个轨道块的开头是连续4字节的"MTrk"标识,不需要循环查找
- 原代码会将上一轨道残留的字节、甚至当前轨道的部分数据拼入
dataString,直到凑出结尾为"MTrk"的字符串,导致后续轨道长度和数据的读取完全偏移
替换这段错误代码:
dataString = "" While Not (dataString.EndsWith("MTrk")) dataString = dataString & Chr(reader.ReadByte) End While
为:
' 直接读取4字节轨道标识 Dim trackIdBytes(3) As Byte reader.Read(trackIdBytes, 0, 4) Dim trackId As String = System.Text.ASCIIEncoding.ASCII.GetString(trackIdBytes) If trackId <> "MTrk" Then Throw New InvalidDataException("非法轨道头:预期 'MTrk' 标识") End If
2. 未严格隔离轨道数据读取范围
你直接使用全局BinaryReader读取轨道事件,一旦getNextEvent出现字节读取错误(比如可变长度量解析失误),会直接影响下一个轨道的读取。正确的做法是按轨道长度读取完整数据到独立缓冲区,再单独解析:
修改轨道数据读取逻辑:
Dim trk As New Track Dim numberBytes As Integer = 0 ' 读取4字节轨道数据长度(大端序逻辑正确,保留) For z As Integer = 0 To 3 numberBytes = (256 * numberBytes) + reader.ReadByte Next ' 将当前轨道数据读取到独立缓冲区,避免干扰全局流位置 Dim trackData(numberBytes - 1) As Byte reader.Read(trackData, 0, numberBytes) Using trackReader As New BinaryReader(New MemoryStream(trackData)) Dim runningStatusType As Byte Dim runningStatusChannel As Byte Do Dim evt = getNextEvent(trackReader, runningStatusType, runningStatusChannel) trk.addEvent(evt) If TypeOf evt Is EndOfTrack Then Exit Do End If Loop End Using
3. 检查getNextEvent的核心实现
这是最容易隐藏问题的环节,必须确保:
- 可变长度量解析正确:MIDI事件的时间戳是可变长度量,需要循环读取字节直到最高位为0,再组合成正确数值,不能只读取单个字节
- Running Status重置规则正确:只有通道声音事件会延续Running Status,遇到元事件、系统实时事件时必须重置Running Status
- 事件数据读取完整:比如元事件需要先读取参数长度,再读取对应长度的参数;通道事件的参数长度要和事件类型匹配(如Note On/Off是2个参数字节)
4. 验证文件头轨道数的解析
确认metadata.NumberTracks的读取逻辑正确:MIDI头块的第3-4字节是轨道数(大端序),如果误将大端序按小端序解析,会导致循环次数错误(比如实际2轨却解析成65535轨),自然会提前读到文件末尾。
内容的提问来源于stack exchange,提问作者Roy H
相关产品推荐
相关产品推荐

