示例#1
0
        public static void ExportMsb(Stream dataStream, string outPath)
        {
            var file = MsbFile.Read(dataStream);

            var midiFile = new MidiFile();

            var tempoMap = TempoMap.Create(new TicksPerQuarterNoteTimeDivision(960),
                                           Tempo.FromBeatsPerMinute((int)file.BpmEntries[0].Bpm));

            foreach (var score in file.ScoreEntries)
            {
                var track = new TrackChunk();

                var scoreNotes = new List <Note>();

                foreach (var bar in score.Bars)
                {
                    scoreNotes.Add(new Note(new SevenBitNumber((byte)(bar.Note + 24)), bar.Length, bar.Offset));
                }

                track.AddNotes(scoreNotes);

                midiFile.Chunks.Add(track);
            }

            midiFile.ReplaceTempoMap(tempoMap);

            midiFile.Write(outPath);
        }
示例#2
0
        public static byte[] CreateMsbFromMidi(string path)
        {
            var midiFile = MidiFile.Read(path);

            //Ensures every chunk in the midi has at least 1 note
            foreach (var chunk in midiFile.GetTrackChunks())
            {
                if (chunk.GetNotes().Count() == 0)
                {
                    List <Note> dummyTrack = new List <Note>();
                    dummyTrack.Add(new Note(new SevenBitNumber((byte)60), 1000, 1));
                    chunk.AddNotes(dummyTrack);
                }
            }

            //Ensures there are at least 7 chunks in the midi
            while (midiFile.Chunks.Count < 7)
            {
                TrackChunk  chunk      = new TrackChunk();
                List <Note> dummyTrack = new List <Note>();
                dummyTrack.Add(new Note(new SevenBitNumber((byte)60), 1000, 1));
                chunk.AddNotes(dummyTrack);
                midiFile.Chunks.Add(chunk);
            }

            //Ensures there are no more than 7 chunks in the midi
            while (midiFile.GetTrackChunks().Count() > 7)
            {
                midiFile.Chunks.RemoveAt(7);
            }

            //Reads BPM from midi if possible; if no BPM is declared, defaults to 100
            ValueChange <Tempo> tempo = midiFile.GetTempoMap().Tempo.FirstOrDefault <ValueChange <Tempo> >();
            float bpm = 100;

            if (tempo != null)
            {
                bpm = (float)tempo.Value.BeatsPerMinute;
            }

            //Assigns time signature based on first midi time signature event, or defaults to 4/4
            ValueChange <TimeSignature> ts = midiFile.GetTempoMap().TimeSignature.FirstOrDefault <ValueChange <TimeSignature> >();
            int numerator   = 4;
            int denominator = 4;

            if (ts != null)
            {
                numerator   = ts.Value.Numerator;
                denominator = ts.Value.Denominator;
            }

            var file = new MsbFile
            {
                BpmEntries = new List <MsbFile.MsbBpm> {
                    new MsbFile.MsbBpm {
                        Bpm = bpm, Measure1 = (byte)numerator, Measure2 = (byte)denominator
                    }
                },
                ScoreEntries = new List <MsbFile.MsbScore>()
            };

            var trackChunks = midiFile.GetTrackChunks();

            //If the first chunk contains only one note (dummy chunk or empty chunk)
            //Instead, re-order list to sort the list with the most notes into the first slot.
            if (trackChunks.ElementAt(0).GetNotes().Count() == 1)
            {
                trackChunks = trackChunks.OrderByDescending(c => c.GetNotes().Count());
            }

            for (var trackIndex = 0; trackIndex < trackChunks.Count(); trackIndex++)
            {
                var notes = trackChunks.ElementAt(trackIndex).GetNotes();
                notes = notes.OrderBy(c => c.Time);

                var msbScore = new MsbFile.MsbScore {
                    Bars = new List <MsbFile.MsbBar>()
                };

                for (var i = 0; i < notes.Count(); i++)
                {
                    var note = notes.ElementAt(i);

                    if ((int)note.NoteNumber < 48 || (int)note.NoteNumber > 84)
                    {
                        throw new Exception("Your Midi contains notes outside the range of FFXIV's playable range. " +
                                            $"Please enter only notes between C3 and C6 (inclusive). Detected incorrect range at Note#{i + 1} in Track#{trackIndex + 1}");
                    }

                    //Detects note overlaps and chops the length to be strictly < the next note's time.
                    var lengthChopOffset = 0L;
                    if (i != notes.Count() - 1)
                    {
                        var nextNote = notes.ElementAt(i + 1);
                        if (note.Time + note.Length > nextNote.Time)
                        {
                            lengthChopOffset = (note.Time + note.Length) - nextNote.Time;
                        }
                    }

                    var msbBar = new MsbFile.MsbBar();

                    msbBar.Note   = (byte)(note.NoteNumber - 24);
                    msbBar.Length = (uint)(note.Length - lengthChopOffset);
                    msbBar.Offset = (uint)note.Time;

                    msbScore.Bars.Add(msbBar);
                }

                file.ScoreEntries.Add(msbScore);
            }

            return(file.GetBytes());
        }
示例#3
0
        public static MsbFile Read(Stream stream)
        {
            var file = new MsbFile();

            file.ScoreEntries = new List <MsbScore>();

            using (var reader = new BinaryReader(stream))
            {
                if (reader.ReadInt32() != 0x4642534D) // MSBF
                {
                    throw new ArgumentException("Not a MSB file", nameof(stream));
                }

                stream.Position++; //?

                var scoreCount = reader.ReadByte();
                var headerSize = reader.ReadUInt16();

                var musicLength = reader.ReadUInt32();

                stream.Position += 4;

                var extendedHeaderSize = reader.ReadUInt32();
                stream.Position = extendedHeaderSize; // Extended header, maybe instruments for ensembles?



                // BPM HEADER
                stream.Position += 4;
                var bpmHeaderCount = reader.ReadUInt32();

                stream.Position += 8;

                file.BpmEntries = new List <MsbBpm>();
                for (var bpmIndex = 0; bpmIndex < bpmHeaderCount; bpmIndex++)
                {
                    var bpm = new MsbBpm();

                    stream.Position++; //?

                    bpm.Measure1 = reader.ReadByte();
                    bpm.Measure2 = reader.ReadByte();

                    stream.Position++;

                    bpm.Bpm = reader.ReadSingle();

                    reader.ReadUInt32(); // Unknown
                    reader.ReadUInt32();

                    file.BpmEntries.Add(bpm);
                }


                for (var i = 0; i < scoreCount; i++)
                {
                    var score = new MsbScore();
                    score.Bars = new List <MsbBar>();

                    stream.Position += 4;

                    var noteCount = reader.ReadUInt32();

                    stream.Position += 8;

                    for (var noteIndex = 0; noteIndex < noteCount; noteIndex++)
                    {
                        var bar = new MsbBar();

                        stream.Position++; //?

                        bar.Note = reader.ReadByte();

                        stream.Position += 2; //?

                        bar.Offset = reader.ReadUInt32();
                        bar.Length = reader.ReadUInt32();

                        reader.ReadUInt32(); // Unknown

                        score.Bars.Add(bar);
                    }

                    file.ScoreEntries.Add(score);
                }
            }

            return(file);
        }