Exemple #1
0
        private static void loadDirectly(string filePath, List <MidiTrack> midiTracks, ref ushort timeDivision)              // returns the MIDI loaded in the List of all individual tracks
        {
            // FileStreams seem to have their own buffering layer so there is no need for an additional Buffered Stream
            FileStream   midiFileStream   = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
            BinaryReader midiBinaryStream = new BinaryReader(midiFileStream);

            midiFileStream.Position = 0xA;      // seek to the amount of tracks in the MIDI file
            int numTracks = midiBinaryStream.ReadByte() << 8 | midiBinaryStream.ReadByte();

            timeDivision = (ushort)(midiBinaryStream.ReadByte() << 8 | midiBinaryStream.ReadByte());
            // finished reading the header data, now continue transscribing the tracks

            for (int currentTrack = 0; currentTrack < numTracks; currentTrack++)
            {
                MidiTrack cTrk = new MidiTrack();
                midiTracks.Add(cTrk);     // we have to create the object of the track first and we can add it later to out track list if the track was transscribed into it's objects
                long       currentTick     = 0;
                NormalType lastEventType   = NormalType.NoteOFF;
                byte       lastMidiChannel = 0;
                // check if the track doesn't begin like expected with an MTrk string
                byte[] textString = new byte[4];
                midiBinaryStream.Read(textString, 0, 4);
                if (Encoding.ASCII.GetString(textString, 0, 4) != "MTrk")
                {
                    throw new Exception("Track doesn't start with MTrk string!");
                }
                byte[] intArray = new byte[4];
                midiBinaryStream.Read(intArray, 0, 4);    // read the track length
                // this value isn't even needed, so we don't do further processing with it; I left it in the code for some usage in the future; no specific plan???

                // now do the event loop and load all the events
                #region EventLoop
                while (true)
                {
                    // first thing that is done is getting the next delta length value and add the value to the current position to calculate the absolute position of the event
                    currentTick += readVariableLengthValue(midiBinaryStream);

                    // now check what event type is used and disassemble it

                    byte eventTypeByte = midiBinaryStream.ReadByte();

                    // do a jumptable for each event type

                    if (eventTypeByte == 0xFF)      // if META Event
                    {
                        byte   metaType   = (byte)midiFileStream.ReadByte();
                        long   metaLength = readVariableLengthValue(midiBinaryStream);
                        byte[] metaData   = new byte[metaLength];
                        midiBinaryStream.Read(metaData, 0, (int)metaLength);

                        if (metaType == 0x2F)
                        {
                            break;        // if end of track is reached, break out of the loop, End of Track Events aren't written into the objects
                        }
                        cTrk.midiEvents.Add(new MetaMidiEvent(currentTick, metaType, metaData));
                    }
                    else if (eventTypeByte == 0xF0 || eventTypeByte == 0xF7)        // if SysEx Event
                    {
                        long   sysexLength = readVariableLengthValue(midiBinaryStream);
                        byte[] sysexData   = new byte[sysexLength];
                        midiBinaryStream.Read(sysexData, 0, (int)sysexLength);
                        cTrk.midiEvents.Add(new SysExMidiEvent(currentTick, eventTypeByte, sysexData));
                    }
                    else if (eventTypeByte >> 4 == 0x8)     // if Note OFF command
                    {
                        byte par1 = midiBinaryStream.ReadByte();
                        byte par2 = midiBinaryStream.ReadByte();
                        cTrk.midiEvents.Add(new MessageMidiEvent(currentTick, (byte)(eventTypeByte & 0xF), NormalType.NoteOFF, par1, par2));
                        // save the last event type and channel
                        lastEventType   = NormalType.NoteOFF;
                        lastMidiChannel = (byte)(eventTypeByte & 0xF);
                    }
                    else if (eventTypeByte >> 4 == 0x9)     // if Note ON command
                    {
                        byte par1 = midiBinaryStream.ReadByte();
                        byte par2 = midiBinaryStream.ReadByte();
                        cTrk.midiEvents.Add(new MessageMidiEvent(currentTick, (byte)(eventTypeByte & 0xF), NormalType.NoteON, par1, par2));
                        // save the last event type and channel
                        lastEventType   = NormalType.NoteON;
                        lastMidiChannel = (byte)(eventTypeByte & 0xF);
                    }
                    else if (eventTypeByte >> 4 == 0xA)     // if Aftertouch command
                    {
                        byte par1 = midiBinaryStream.ReadByte();
                        byte par2 = midiBinaryStream.ReadByte();
                        cTrk.midiEvents.Add(new MessageMidiEvent(currentTick, (byte)(eventTypeByte & 0xF), NormalType.NoteAftertouch, par1, par2));
                        // save the last event type and channel
                        lastEventType   = NormalType.NoteAftertouch;
                        lastMidiChannel = (byte)(eventTypeByte & 0xF);
                    }
                    else if (eventTypeByte >> 4 == 0xB)     // if MIDI controller command
                    {
                        byte par1 = midiBinaryStream.ReadByte();
                        byte par2 = midiBinaryStream.ReadByte();
                        cTrk.midiEvents.Add(new MessageMidiEvent(currentTick, (byte)(eventTypeByte & 0xF), NormalType.Controller, par1, par2));
                        // save the last event type and channel
                        lastEventType   = NormalType.Controller;
                        lastMidiChannel = (byte)(eventTypeByte & 0xF);
                    }
                    else if (eventTypeByte >> 4 == 0xC)     // if Preset command
                    {
                        byte par1 = midiBinaryStream.ReadByte();
                        byte par2 = 0x0;    // unused
                        cTrk.midiEvents.Add(new MessageMidiEvent(currentTick, (byte)(eventTypeByte & 0xF), NormalType.Program, par1, par2));
                        // save the last event type and channel
                        lastEventType   = NormalType.Program;
                        lastMidiChannel = (byte)(eventTypeByte & 0xF);
                    }
                    else if (eventTypeByte >> 4 == 0xD)     // if Channel Aftertouch command
                    {
                        byte par1 = midiBinaryStream.ReadByte();
                        byte par2 = 0x0;    // unused
                        cTrk.midiEvents.Add(new MessageMidiEvent(currentTick, (byte)(eventTypeByte & 0xF), NormalType.ChannelAftertouch, par1, par2));
                        // save the last event type and channel
                        lastEventType   = NormalType.ChannelAftertouch;
                        lastMidiChannel = (byte)(eventTypeByte & 0xF);
                    }
                    else if (eventTypeByte >> 4 == 0xE)     // if Pitch Bend command
                    {
                        byte par1 = midiBinaryStream.ReadByte();
                        byte par2 = midiBinaryStream.ReadByte();
                        cTrk.midiEvents.Add(new MessageMidiEvent(currentTick, (byte)(eventTypeByte & 0xF), NormalType.PitchBend, par1, par2));
                        // save the last event type and channel
                        lastEventType   = NormalType.PitchBend;
                        lastMidiChannel = (byte)(eventTypeByte & 0xF);
                    }
                    else if (eventTypeByte >> 4 < 0x8)
                    {
                        byte par1 = eventTypeByte;
                        byte par2;
                        switch (lastEventType)
                        {
                        case NormalType.NoteOFF:
                            par2 = midiBinaryStream.ReadByte();
                            cTrk.midiEvents.Add(new MessageMidiEvent(currentTick, lastMidiChannel, NormalType.NoteOFF, par1, par2));
                            break;

                        case NormalType.NoteON:
                            par2 = midiBinaryStream.ReadByte();
                            cTrk.midiEvents.Add(new MessageMidiEvent(currentTick, lastMidiChannel, NormalType.NoteON, par1, par2));
                            break;

                        case NormalType.NoteAftertouch:
                            par2 = midiBinaryStream.ReadByte();
                            cTrk.midiEvents.Add(new MessageMidiEvent(currentTick, lastMidiChannel, NormalType.NoteAftertouch, par1, par2));
                            break;

                        case NormalType.Controller:
                            par2 = midiBinaryStream.ReadByte();
                            cTrk.midiEvents.Add(new MessageMidiEvent(currentTick, lastMidiChannel, NormalType.Controller, par1, par2));
                            break;

                        case NormalType.Program:
                            cTrk.midiEvents.Add(new MessageMidiEvent(currentTick, lastMidiChannel, NormalType.Program, par1, 0x0));
                            break;

                        case NormalType.ChannelAftertouch:
                            cTrk.midiEvents.Add(new MessageMidiEvent(currentTick, lastMidiChannel, NormalType.ChannelAftertouch, par1, 0x0));
                            break;

                        case NormalType.PitchBend:
                            par2 = midiBinaryStream.ReadByte();
                            cTrk.midiEvents.Add(new MessageMidiEvent(currentTick, lastMidiChannel, NormalType.PitchBend, par1, par2));
                            break;
                        }
                    }
                    else
                    {
                        throw new Exception("Bad MIDI event at 0x" + midiBinaryStream.BaseStream.Position.ToString("X8") + ": 0x" + eventTypeByte.ToString("X2"));
                    }
                }                                                                                                         // end of the event transscribing loop
                #endregion
            }                                                                                                             // end of the track loop
            midiBinaryStream.BaseStream.Close();
        }                                                                                                                 // end of function loadDirectly
Exemple #2
0
        public static void saveToFile(string filePath, List <MidiTrack> midiTracks, ushort timeDivision)
        {
            Console.WriteLine("Saving MIDI to type 1 file...");
            // first of all check if a file with the name already exists
            if (File.Exists(filePath))
            {
                File.Delete(filePath);
            }
            FileStream   midiFileStream = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.None);
            BinaryWriter midiWriter     = new BinaryWriter(midiFileStream);

            Console.WriteLine("The new MIDI file has {0} tracks!", midiTracks.Count);

            // first of all write MIDI header string
            midiWriter.Write(Encoding.ASCII.GetBytes("MThd"));
            // writer the header chunk length (=6)
            midiWriter.Write(intToBigEndian(6));
            // write the midi file type (=1)
            midiWriter.Write(ushortToBigEndian(1));
            // write the amount of tracks
            midiWriter.Write(ushortToBigEndian((ushort)midiTracks.Count));
            // write the time division
            midiWriter.Write(ushortToBigEndian(timeDivision));
            // finished writing the header, now do the tracks
            long[] trackHeaderOffset = new long[midiTracks.Count];
            long[] trackStartOffset  = new long[midiTracks.Count];
            long[] trackEndOffset    = new long[midiTracks.Count];

            for (int currentTrack = 0; currentTrack < midiTracks.Count; currentTrack++)
            {
                MidiTrack cTrk = midiTracks[currentTrack];
                trackHeaderOffset[currentTrack] = midiWriter.BaseStream.Position;    // save the offset to the track header
                // write the header info
                midiWriter.Write(Encoding.ASCII.GetBytes("MTrk"));
                midiWriter.Write((int)0);                                        // write 0 into the chunk length slot; it'll get filled later; 0 is the same in Little Endian as in Big, so we can use the normal int32 writing

                trackStartOffset[currentTrack] = midiWriter.BaseStream.Position; // save the track beginning (doesn't point to the header, it points to the data)
                long currentTick = 0;                                            // init the current tick to 0 to calculate Delta Time values

                for (int currentEvent = 0; currentEvent < cTrk.midiEvents.Count; currentEvent++)
                {
                    // write the Delta time to the stream
                    midiWriter.Write(VariableLength.ConvertToVariableLength(cTrk.midiEvents[currentEvent].absoluteTicks - currentTick));
                    // write the actual Event data
                    midiWriter.Write(cTrk.midiEvents[currentEvent].getEventData());

                    currentTick = cTrk.midiEvents[currentEvent].absoluteTicks;
                }

                midiWriter.Write((byte)0x0);                                   // write delta time for track end
                midiWriter.Write((byte)0xFF);                                  // write META event byte
                midiWriter.Write((byte)0x2F);                                  // write End of Track byte
                midiWriter.Write((byte)0x0);                                   // the length of this META event data is 0 (no data follows)

                trackEndOffset[currentTrack] = midiWriter.BaseStream.Position; // calc the length of the event data and backup the position
            }                                                                  // end of track loop
            midiWriter.BaseStream.Close();
            // close the filestreams and create a new one to edit the file
            midiFileStream = new FileStream(filePath, FileMode.Open, FileAccess.Write, FileShare.None);
            midiWriter     = new BinaryWriter(midiFileStream);

            for (int currentTrack = 0; currentTrack < midiTracks.Count; currentTrack++)
            {
                midiWriter.BaseStream.Position = trackHeaderOffset[currentTrack] + 4;
                midiWriter.Write(intToBigEndian((int)(trackEndOffset[currentTrack] - trackStartOffset[currentTrack])));
            }
            midiWriter.Close();
            // close filestream and finish
            Console.WriteLine("Successfully finished creating MIDI file!");
        }                                                     // end of function