c# - 使用 NAudio 从一段 MIDI 文件中读取 MIDI 事件、消息
问题描述
我是 MIDI 方面的新手,所以请不要对我残忍 :) 我有一个 Yamaha midi 文件,其中包含一些部分,如 Midi Header 部分、CASM 部分、OTS 部分、MDB 部分和 MH 部分。我想关注一下 OTS 部分。OTS 部分包含 ID = 4 字节、数据长度 = 4 字节和数据。数据是一个 MIDI 文件结构块,但它不包含音符,只有使用的通道等设置,以及每个通道的设置,如使用语音的 MSB-LSB-PC、音量、和声等。问题是如何检索使用的通道,如何检索使用的 MSB-LSB-PC 对语音/鼓?NAudio 可以这样做还是我必须使用另一个 MIDI 包工具?
编辑:
OTS Data 将包含至少一个 OTS Track。每个 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 Data 将至少包含一次这种结构。我将能够从每个 OTS Track 中读取数据,但我不知道从那些 OTS Track Data SMF 数据中获取那些 MSB-LSB-PC 信息的 C# 指令是什么......
解决方案
我可以建议你使用我的DryWetMIDI库。库文档中有一篇文章描述了如何定义自定义块类并读取其数据:自定义块。
至于 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
. 例如,
var firstTrackChunkSysExEvents = otsChunk.TrackChunks.First().Events.OfType<NormalSysExEvent>();
var firstSysExEvent = firstTrackChunkSysExEvents.First();
var firstData = firstSysExEvent.Data;
firstData
将在 OTS 块的第一个轨道块中包含第一个 sys ex 事件的字节。请注意,数据不包含第一个F0
字节。
推荐阅读
- bash - 通过 DevOps SSH 任务访问管道变量
- python - 如何使用前向填充python重新采样
- r - 何在 R 中的条形顶部添加在 geom_histogram 中计算的计数值
- excel - Excel 列包含分号分隔的多个名称,需要过滤具有多个值的行,省略单个值
- php - Laravel:从下拉选择中将选定的值传递给路由功能
- python-3.x - 从数据集列中的某些日期在 pandas 数据集中插入值
- c++ - 为什么在这段代码中,当“swap”函数写在 int main() 之后而不是在它之前时,会发生交换?
- ios - Swift - 解决方法/替代 M3u8 播放 mp4 片段或将片段合并到 mp4
- sql - 如何在 BigQuery 中进行分区外连接
- docker - 使用 CircleCI 的预建镜像与拉取私有镜像之间的权衡是什么?