使用 NAudio 从一段 MIDI 文件中读取 MIDI 事件、消息
Posted
技术标签:
【中文标题】使用 NAudio 从一段 MIDI 文件中读取 MIDI 事件、消息【英文标题】:Reading MIDI Events, Messages from a chunk of a midi file using NAudio 【发布时间】:2020-09-06 09:52:08 【问题描述】:我是 MIDI 方面的新手,所以请不要对我残忍 :) 我有一个 Yamaha midi 文件,其中包含一些部分,例如 Midi Header 部分、CASM 部分、OTS 部分、MDB 部分和 MH 部分。 我想关注一下 OTS 部分。 OTS 部分包含 ID = 4 字节、数据长度 = 4 字节和数据。数据是一个 MIDI 文件结构块,但它不包含音符,只有使用的通道等设置,以及每个通道的设置,如使用语音的 MSB-LSB-PC、音量、和声等。 问题是如何检索使用的通道,如何检索使用的 MSB-LSB-PC 对语音/鼓? NAudio 可以这样做还是我必须使用其他 MIDI 包工具?
编辑:
OTS 数据将包含至少一个 OTS 轨道。每个 OTS Track 的结构如下:
byte[0]->byte[3] = 'MTrk' (midi track header of SMF)
byte[4]->byte[7] = 256*256*256*byte[4] + 256*256*byte[5] + 256*byte[6] + byte[7] -> Data length on OTS Track.
byte[8]->byte[n] = SMF data of OTS Track.
因此,OTS 数据将至少包含一次这种结构。我将能够读取每个 OTS Track,但我不知道哪些 C# 指令可以从那些 OTS Track Data SMF 数据中获取那些 MSB-LSB-PC 信息...
【问题讨论】:
我是 DryWetMIDI 库的作者,它允许您读取 MIDI 数据,包括自定义块(如 OTS)。但当然,您需要自己编写代码来读取您的自定义块。 Here 定义自定义块的示例,以便您可以阅读它。另外,您能否提供一些带有 OTS 块的示例文件? 嗨@Maxim。我已经编辑了我的问题。请告诉我如何使用您的库来读取 SMF 中频道的 MSB-LSB-PC? 为了帮助你,我需要知道 OTS 块的确切格式。因为它是一个 MIDI 块,所以我知道它的标题。但是 chink 内容是什么? “OTS Track的SMF数据”是什么?我也不知道什么是“MSB-LSB-PC”。除了格式信息,您可以提供带有 OTS 块的文件吗?你也可以create new issue on GitHub. wierzba.homepage.t-online.de/stylefiles.htm 这是一个文件,它将详细解释样式文件包含的内容。我需要知道(提取)来自 OTSc 的数据。 mediafire.com/file/d4ryg390q5qdkqu/… 这是一个包含 OTSc 块的 Yamaha Style 文件。我必须从那里提取所有 OTS 音轨,并为每个 OTS 音轨提取一些信息,例如程序更改(包括用于选择正确语音的 MSB 和 LSB)音量、和声和其他数据... 现在你应该自己做很多工作。 但是我做了快速测试,我会尝试在 DryWetMIDI 中提供一个 API,让您可以轻松读取 OTS 块。一旦我实现了那个 API,我会发布一个答案。我想我今天或明天会这样做。感谢您分享您的案例! 【参考方案1】:我可以建议您使用我的 DryWetMIDI 库。库文档中有一篇文章描述了如何定义自定义块类并读取其数据:Custom chunks。
至于 OTS 块,从您提供的链接中,我看到 OTS 块的数据实际上是没有标题块的 MIDI 文件。因此我们可以将其内容读取为 MIDI 文件并获取文件的音轨块。
让我们定义我们的块类:
public sealed class OtsChunk : MidiChunk
public const string Id = "OTSc";
public OtsChunk()
: base(Id)
public IEnumerable<TrackChunk> TrackChunks get; private set;
protected override void ReadContent(MidiReader reader, ReadingSettings settings, uint size)
var data = reader.ReadBytes((int)size);
using (var memoryStream = new MemoryStream(data))
var midiFile = MidiFile.Read(memoryStream, new ReadingSettings
NoHeaderChunkPolicy = NoHeaderChunkPolicy.Ignore
);
TrackChunks = midiFile.GetTrackChunks().ToArray();
public override MidiChunk Clone()
throw new NotImplementedException();
protected override uint GetContentSize(WritingSettings settings)
throw new NotImplementedException();
protected override void WriteContent(MidiWriter writer, WritingSettings settings)
throw new NotImplementedException();
我们不会实现Clone
、GetContentSize
和WriteContent
,因为您只对阅读感兴趣。 (如果您希望能够手动创建此类块并将其写入 MIDI 文件,则还需要实现最后两个方法。)
现在我们可以读取 Yamaha MIDI 文件并获取 OTS 块:
var midiFile = MidiFile.Read("LionelRichie Hello_Amkey_TY.sty", new ReadingSettings
CustomChunkTypes = new ChunkTypesCollection
typeof(OtsChunk), OtsChunk.Id
);
var otsChunk = midiFile.Chunks.OfType<OtsChunk>().FirstOrDefault();
然后您可以从otsChunk.TrackChunks
的每个轨道块中获取常规 MIDI 事件。例如,
var firstTrackChunkSysExEvents = otsChunk.TrackChunks.First().Events.OfType<NormalSysExEvent>();
var firstSysExEvent = firstTrackChunkSysExEvents.First();
var firstData = firstSysExEvent.Data;
firstData
将在 OTS 块的第一个轨道块中包含第一个 sys ex 事件的字节。请注意,数据不会包含第一个F0
字节。
【讨论】:
谢谢@Maxim。当然,我会试一试。 很抱歉在这里发帖,但我无权发布其他问题。如何在CustomChunk
中替换TrackChunk
?。所以,正如你所看到的,在那个 OTS 中,我有 4 首曲目,我想替换它们,每首曲目都有另一个 TrackChunk
,甚至是一个空的 TruckChunk
。谢谢。
你能实现类似ReplaceChunk(ChunkType oldChunk, ChunkType newChunk)
的方法吗?能够加载 Midi 文件并运行这个小命令。 MidiChunk 会根据新数据自动改变它的大小……你怎么看?此外,RemoveChunk()、EmptyChunk()、RemoveTrack()、EmptyTrack() 可能很有用!
已通过电子邮件回复。
谢谢马克西姆。 Github 进度……你是最棒的!以上是关于使用 NAudio 从一段 MIDI 文件中读取 MIDI 事件、消息的主要内容,如果未能解决你的问题,请参考以下文章
使用 NAudio 在 MIDI 文件中设置 MIDI 速度