【问题标题】:Reading MIDI Events, Messages from a chunk of a midi file using NAudio使用 NAudio 从一段 MIDI 文件中读取 MIDI 事件、消息
【发布时间】: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,我会发布一个答案。我想我今天或明天会这样做。感谢您分享您的案例!

标签: c# midi naudio


【解决方案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();
    }
}

我们不会实现CloneGetContentSizeWriteContent,因为您只对阅读感兴趣。 (如果您希望能够手动创建此类块并将其写入 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 进度……你是最棒的!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多