Search code examples
c#midinaudio

Reading MIDI Events, Messages from a chunk of a midi file using NAudio


I'm a novice in MIDI things, so please don't be cruel with me :) I have an Yamaha midi file which contains some Sections, like Midi Header Section, CASM Section, OTS Section, MDB Section and MH Section. I want to give some attention to OTS Section. OTS section contains ID = 4 bytes, Data Length = 4 bytes, and Data. Data is a midi file structure chunk but it not contains notes, only settings like channels used, and settings for each channel, like MSB-LSB-PC of used voice, Volumes, Harmony and so on. The question is how do I retrieve the channels used, how do I retrieve the MSB-LSB-PC pair of voice/drum used? Can NAudio do that or I must use another midi package tool?

Edit:

OTS Data will contains at least one OTS Track. Each OTS Track has the following structure:

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.

So, OTS Data will contain this structure at least one time. I will be able to read from each OTS Track, but I don't know which are the C# instructions to achieve those MSB-LSB-PC information from those OTS Track Data SMF data...


Solution

  • I can suggest you to use my DryWetMIDI library. There is the article on the library docs that desctibes how you can define custom chunk class and read its data: Custom chunks.

    As for OTS chunk, from links you've provided I see that OTS chunk's data is in fact MIDI file without header chunk. So we can read its content as MIDI file and get the file's track chunks.

    Let's define our chunk class:

    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();
        }
    }
    

    We won't implement Clone, GetContentSize and WriteContent since you're interested in reading only. (If you want to be able to create such chunks manually and write it to a MIDI file, you will need to implement last two methods too.)

    Now we can read Yamaha MIDI file and get OTS chunk:

    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();
    

    Then you can get regular MIDI events from each track chunk from otsChunk.TrackChunks. For example,

    var firstTrackChunkSysExEvents = otsChunk.TrackChunks.First().Events.OfType<NormalSysExEvent>();
    var firstSysExEvent = firstTrackChunkSysExEvents.First();
    var firstData = firstSysExEvent.Data;
    

    firstData will contain bytes of first sys ex event in the first track chunk of OTS chunk. Please note that data will not contain first F0 byte.