Exemplo n.º 1
0
        protected void Load(byte[] binary, EndianBinaryReader reader, int headerOffset)
        {
            _binary = binary;
            Header  = reader.ReadObject <M4ASongHeader>(headerOffset);

            VoiceTable = VoiceTable.LoadTable <M4AVoiceTable>(Header.VoiceTable - ROM.Pak);

            Commands = new List <SongEvent> [Header.NumTracks];
            for (int i = 0; i < Header.NumTracks; i++)
            {
                Commands[i] = new List <SongEvent>();
            }

            if (Header.NumTracks > ROM.Instance.Game.Engine.TrackLimit)
            {
                throw new InvalidDataException($"La canzone ha troppe tracce ({Header.NumTracks}).");
            }

            for (int i = 0; i < NumTracks; i++)
            {
                reader.BaseStream.Position = Header.Tracks[i] - ROM.Pak;

                byte cmd = 0, runCmd = 0, prevNote = 0, prevVelocity = 0x7F;

                while (cmd != 0xB1 && cmd != 0xB6)
                {
                    int      off     = (int)reader.BaseStream.Position;
                    ICommand command = null;

                    cmd = reader.ReadByte();
                    if (cmd >= 0xBD) // Commands that work within running status
                    {
                        runCmd = cmd;
                    }

                    #region TIE & Notes

                    if (runCmd >= 0xCF && cmd < 0x80) // Within running status
                    {
                        var peek = reader.PeekBytes(2);
                        if (peek[0] >= 0x80)
                        {
                            command = AddNoteEvent(cmd, prevVelocity, 0, runCmd, out prevNote, out prevVelocity);
                        }
                        else if (peek[1] > 3 || peek[1] < 1)
                        {
                            command = AddNoteEvent(cmd, reader.ReadByte(), 0, runCmd, out prevNote, out prevVelocity);
                        }
                        else
                        {
                            command = AddNoteEvent(cmd, reader.ReadByte(), reader.ReadByte(), runCmd, out prevNote, out prevVelocity);
                        }
                    }
                    else if (cmd >= 0xCF)
                    {
                        var peek = reader.PeekBytes(3);
                        if (peek[0] >= 0x80)
                        {
                            command = AddNoteEvent(prevNote, prevVelocity, 0, runCmd, out prevNote, out prevVelocity);
                        }
                        else if (peek[1] >= 0x80)
                        {
                            command = AddNoteEvent(reader.ReadByte(), prevVelocity, 0, runCmd, out prevNote, out prevVelocity);
                        }
                        // TIE cannot have an added duration so it needs to stop here
                        else if (cmd == 0xCF || peek[2] > 3 || peek[2] < 1)
                        {
                            command = AddNoteEvent(reader.ReadByte(), reader.ReadByte(), 0, runCmd, out prevNote, out prevVelocity);
                        }
                        else
                        {
                            command = AddNoteEvent(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), runCmd, out prevNote, out prevVelocity);
                        }
                    }

                    #endregion

                    #region Rests

                    else if (cmd >= 0x80 && cmd <= 0xB0)
                    {
                        command = new RestCommand {
                            Rest = SongEvent.RestFromCMD(0x80, cmd)
                        }
                    }
                    ;

                    #endregion

                    #region Commands

                    else if (runCmd < 0xCF && cmd < 0x80) // Commands within running status
                    {
                        switch (runCmd)
                        {
                        case 0xBD: command = new VoiceCommand {
                                Voice = cmd
                        }; break;

                        case 0xBE: command = new VolumeCommand {
                                Volume = cmd
                        }; break;

                        case 0xBF: command = new PanpotCommand {
                                Panpot = (sbyte)(cmd - 0x40)
                        }; break;

                        case 0xC0: command = new BendCommand {
                                Bend = (sbyte)(cmd - 0x40)
                        }; break;

                        case 0xC1: command = new BendRangeCommand {
                                Range = cmd
                        }; break;

                        case 0xC2: command = new LFOSpeedCommand {
                                Speed = cmd
                        }; break;

                        case 0xC3: command = new LFODelayCommand {
                                Delay = cmd
                        }; break;

                        case 0xC4: command = new ModDepthCommand {
                                Depth = cmd
                        }; break;

                        case 0xC5: command = new ModTypeCommand {
                                Type = cmd
                        }; break;

                        case 0xC8: command = new TuneCommand {
                                Tune = (sbyte)(cmd - 0x40)
                        }; break;

                        case 0xCD: command = new LibraryCommand {
                                Command = cmd, Argument = reader.ReadByte()
                        }; break;

                        case 0xCE: command = new EndOfTieCommand {
                                Note = (sbyte)cmd
                        }; prevNote = cmd; break;
                        }
                    }
                    else if (cmd > 0xB0 && cmd < 0xCF)
                    {
                        switch (cmd)
                        {
                        case 0xB1:     // FINE & PREV
                        case 0xB6: command = new M4AFinishCommand {
                                Type = cmd
                        }; break;

                        case 0xB2: command = new GoToCommand {
                                Offset = reader.ReadInt32() - ROM.Pak
                        }; break;

                        case 0xB3: command = new CallCommand {
                                Offset = reader.ReadInt32() - ROM.Pak
                        }; break;

                        case 0xB4: command = new ReturnCommand(); break;

                        case 0xB5: command = new RepeatCommand {
                                Times = reader.ReadByte(), Offset = reader.ReadInt32() - ROM.Pak
                        }; break;

                        case 0xB9: command = new MemoryAccessCommand {
                                Arg1 = reader.ReadByte(), Arg2 = reader.ReadByte(), Arg3 = reader.ReadByte()
                        }; break;

                        case 0xBA: command = new PriorityCommand {
                                Priority = reader.ReadByte()
                        }; break;

                        case 0xBB: command = new TempoCommand {
                                Tempo = (short)(reader.ReadByte() * 2)
                        }; break;

                        case 0xBC: command = new KeyShiftCommand {
                                Shift = reader.ReadSByte()
                        }; break;

                        // Commands that work within running status:
                        case 0xBD: command = new VoiceCommand {
                                Voice = reader.ReadByte()
                        }; break;

                        case 0xBE: command = new VolumeCommand {
                                Volume = reader.ReadByte()
                        }; break;

                        case 0xBF: command = new PanpotCommand {
                                Panpot = (sbyte)(reader.ReadByte() - 0x40)
                        }; break;

                        case 0xC0: command = new BendCommand {
                                Bend = (sbyte)(reader.ReadByte() - 0x40)
                        }; break;

                        case 0xC1: command = new BendRangeCommand {
                                Range = reader.ReadByte()
                        }; break;

                        case 0xC2: command = new LFOSpeedCommand {
                                Speed = reader.ReadByte()
                        }; break;

                        case 0xC3: command = new LFODelayCommand {
                                Delay = reader.ReadByte()
                        }; break;

                        case 0xC4: command = new ModDepthCommand {
                                Depth = reader.ReadByte()
                        }; break;

                        case 0xC5: command = new ModTypeCommand {
                                Type = reader.ReadByte()
                        }; break;

                        case 0xC8: command = new TuneCommand {
                                Tune = (sbyte)(reader.ReadByte() - 0x40)
                        }; break;

                        case 0xCD: command = new LibraryCommand {
                                Command = reader.ReadByte(), Argument = reader.ReadByte()
                        }; break;

                        case 0xCE:     // EOT
                            sbyte note;

                            if (reader.PeekByte() < 0x80)
                            {
                                note     = reader.ReadSByte();
                                prevNote = (byte)note;
                            }
                            else
                            {
                                note = -1;
                            }

                            command = new EndOfTieCommand {
                                Note = note
                            };
                            break;

                        default: Console.WriteLine("Comando invalido: 0x{0:X} = {1}", off, cmd); break;
                        }
                    }

                    #endregion

                    Commands[i].Add(new SongEvent(off, command));
                }
            }

            ICommand AddNoteEvent(byte note, byte velocity, byte addedDuration, byte runCmd, out byte prevNote, out byte prevVelocity)
            {
                return(new M4ANoteCommand
                {
                    Note = (sbyte)(prevNote = note),
                    Velocity = prevVelocity = velocity,
                    Duration = (short)(runCmd == 0xCF ? -1 : (SongEvent.RestFromCMD(0xCF, runCmd) + addedDuration))
                });
            }
        }
Exemplo n.º 2
0
 public void InsertEvent(SongEvent e, int trackIndex, int insertIndex)
 {
     Commands[trackIndex].Insert(insertIndex, e);
     CalculateTicks(trackIndex);
 }
Exemplo n.º 3
0
        public void SaveAsASM(string fileName)
        {
            if (NumTracks == 0)
            {
                throw new InvalidDataException("Questa canzone non ha tracce.");
            }
            if (ROM.Instance.Game.Engine.Type != EngineType.M4A)
            {
                throw new PlatformNotSupportedException("L'esportazione in ASM da questo motore di gioco non è supportata in questo momento.");
            }

            using (var file = new StreamWriter(fileName))
            {
                string label = Assembler.FixLabel(Path.GetFileNameWithoutExtension(fileName));
                file.WriteLine("\t.include \"MPlayDef.s\"");
                file.WriteLine();
                file.WriteLine($"\t.equ\t{label}_grp, voicegroup000");
                file.WriteLine($"\t.equ\t{label}_pri, 0");
                file.WriteLine($"\t.equ\t{label}_rev, 0");
                file.WriteLine($"\t.equ\t{label}_mvl, 127");
                file.WriteLine($"\t.equ\t{label}_key, 0");
                file.WriteLine($"\t.equ\t{label}_tbs, 1");
                file.WriteLine($"\t.equ\t{label}_exg, 1");
                file.WriteLine($"\t.equ\t{label}_cmp, 1");
                file.WriteLine();
                file.WriteLine("\t.section .rodata");
                file.WriteLine($"\t.global\t{label}");
                file.WriteLine("\t.align\t2");

                for (int i = 0; i < Commands.Length; i++)
                {
                    int num = i + 1;
                    file.WriteLine();
                    file.WriteLine($"@**************** Track {num} ****************@");
                    file.WriteLine();
                    file.WriteLine($"{label}_{num}:");

                    var offsets = Commands[i].Where(e => e.Command is CallCommand || e.Command is GoToCommand || e.Command is RepeatCommand)
                                  .Select(e => (int)(((dynamic)e.Command).Offset)).Distinct(); // Get all offsets we need labels for
                    int jumps  = 0;
                    var labels = new Dictionary <int, string>();
                    foreach (int o in offsets)
                    {
                        labels.Add(o, $"{label}_{num}_{jumps++:D3}");
                    }
                    int  ticks     = 0;
                    bool displayed = false;
                    foreach (var e in Commands[i])
                    {
                        void DisplayRest(int rest)
                        {
                            byte amt = SongEvent.RestToCMD[rest];

                            file.WriteLine($"\t.byte\tW{amt:D2}");
                            int rem = rest - amt;

                            if (rem != 0)
                            {
                                file.WriteLine($"\t.byte\tW{rem:D2}");
                            }
                            ticks    += rest; // TODO: Separate by 96 ticks
                            displayed = false;
                        }

                        var c = e.Command;

                        if (!displayed && ticks % 96 == 0)
                        {
                            file.WriteLine($"@ {ticks / 96:D3}\t----------------------------------------");
                            displayed = true;
                        }
                        int eOffset = e.GetOffset();
                        if (offsets.Contains(eOffset))
                        {
                            file.WriteLine($"{labels[eOffset]}:");
                        }

                        if (c == null)
                        {
                            continue;
                        }

                        if (c is TempoCommand tempo)
                        {
                            file.WriteLine($"\t.byte\tTEMPO , {tempo.Tempo}*{label}_tbs/2");
                        }
                        else if (c is RestCommand rest)
                        {
                            DisplayRest(rest.Rest);
                        }
                        else if (c is NoteCommand note)
                        {
                            // Hide base note, velocity and duration
                            dynamic dynote  = note;
                            byte    baseDur = dynote.Duration == -1 ? (byte)0 : SongEvent.RestToCMD[dynote.Duration];
                            int     rem     = dynote.Duration - baseDur;
                            string  name    = dynote.Duration == -1 ? "TIE" : $"N{baseDur:D2}";
                            string  not     = SongEvent.NoteName(dynote.Note, true);
                            string  vel     = $"v{dynote.Velocity:D3}";

                            if (dynote.Duration != -1 && rem != 0)
                            {
                                file.WriteLine($"\t.byte\t\t{name}   , {not} , {vel}, gtp{rem}");
                            }
                            else
                            {
                                file.WriteLine($"\t.byte\t\t{name}   , {not} , {vel}");
                            }
                        }
                        else if (c is EndOfTieCommand eot)
                        {
                            if (eot.Note != -1)
                            {
                                file.WriteLine("\t.byte\t\tEOT");
                            }
                            else
                            {
                                file.WriteLine($"\t.byte\t\tEOT   , {SongEvent.NoteName(eot.Note, true)}");
                            }
                        }
                        else if (c is VoiceCommand voice)
                        {
                            file.WriteLine($"\t.byte\t\tVOICE , {voice.Voice}");
                        }
                        else if (c is VolumeCommand volume)
                        {
                            file.WriteLine($"\t.byte\t\tVOL   , {volume.Volume}*{label}_mvl/mxv");
                        }
                        else if (c is PanpotCommand pan)
                        {
                            file.WriteLine($"\t.byte\t\tPAN   , {SongEvent.CenterValueString(pan.Panpot)}");
                        }
                        else if (c is BendCommand bend)
                        {
                            file.WriteLine($"\t.byte\t\tBEND  , {SongEvent.CenterValueString(bend.Bend)}");
                        }
                        else if (c is TuneCommand tune)
                        {
                            file.WriteLine($"\t.byte\t\tTUNE  , {SongEvent.CenterValueString(tune.Tune)}");
                        }
                        else if (c is BendRangeCommand bendr)
                        {
                            file.WriteLine($"\t.byte\t\tBENDR , {bendr.Range}");
                        }
                        else if (c is LFOSpeedCommand lfos)
                        {
                            file.WriteLine($"\t.byte\t\tLFOS  , {lfos.Speed}");
                        }
                        else if (c is LFODelayCommand lfodl)
                        {
                            file.WriteLine($"\t.byte\t\tLFODL , {lfodl.Delay}");
                        }
                        else if (c is ModDepthCommand mod)
                        {
                            file.WriteLine($"\t.byte\t\tMOD   , {mod.Depth}");
                        }
                        else if (c is ModTypeCommand modt)
                        {
                            file.WriteLine($"\t.byte\t\tMODT  , {modt.Type}");
                        }
                        else if (c is PriorityCommand prio)
                        {
                            file.WriteLine($"\t.byte\tPRIO , {prio.Priority}");
                        }
                        else if (c is KeyShiftCommand keysh)
                        {
                            file.WriteLine($"\t.byte\tKEYSH , {label}_key+{keysh.Shift}");
                        }
                        else if (c is GoToCommand goTo)
                        {
                            file.WriteLine("\t.byte\tGOTO");
                            file.WriteLine($"\t .word\t{labels[goTo.Offset]}");
                        }
                        else if (c is RepeatCommand rept)
                        {
                            file.WriteLine($"\t.byte\t\tREPT  , {rept.Times}");
                            file.WriteLine($"\t .word\t{labels[rept.Offset]}");
                        }
                        else if (c is M4AFinishCommand fine)
                        {
                            if (fine.Type == 0xB1)
                            {
                                file.WriteLine("\t.byte\tFINE");
                            }
                            else
                            {
                                file.WriteLine("\t.byte\t0xB6\t@PREV");
                            }
                        }
                        else if (c is CallCommand patt)
                        {
                            file.WriteLine("\t.byte\tPATT");
                            file.WriteLine($"\t .word\t{labels[patt.Offset]}");
                        }
                        else if (c is ReturnCommand pend)
                        {
                            file.WriteLine("\t.byte\tPEND");
                        }
                        else if (c is MemoryAccessCommand memacc)
                        {
                            file.WriteLine($"\t.byte\t\tMEMACC, {memacc.Arg1,4}, {memacc.Arg2,4}, {memacc.Arg3}");
                        }
                        else if (c is LibraryCommand xcmd)
                        {
                            file.WriteLine($"\t.byte\t\tXCMD  , {xcmd.Command,4}, {xcmd.Argument}");
                        }
                    }
                }

                file.WriteLine();
                file.WriteLine("@******************************************@");
                file.WriteLine("\t.align\t2");
                file.WriteLine();
                file.WriteLine($"{label}:");
                file.WriteLine($"\t.byte\t{NumTracks}\t@ NumTrks");
                file.WriteLine($"\t.byte\t{(this is M4ASong m4asong ? m4asong.Header.NumBlocks : 0)}\t@ NumBlks");
                file.WriteLine($"\t.byte\t{label}_pri\t@ Priority");
                file.WriteLine($"\t.byte\t{label}_rev\t@ Reverb.");
                file.WriteLine();
                file.WriteLine($"\t.word\t{label}_grp");
                file.WriteLine();
                for (int i = 0; i < NumTracks; i++)
                {
                    file.WriteLine($"\t.word\t{label}_{i + 1}");
                }
                file.WriteLine();
                file.WriteLine("\t.end");
            }
        }
Exemplo n.º 4
0
        protected void Load(byte[] binary, M4ASongHeader head)
        {
            _binary = binary;
            Header  = head;
            Array.Resize(ref Header.Tracks, Header.NumTracks); // Not really necessary yet
            Commands = new List <SongEvent> [Header.NumTracks];
            for (int i = 0; i < Header.NumTracks; i++)
            {
                Commands[i] = new List <SongEvent>();
            }

            VoiceTable = VoiceTable.LoadTable <M4AVoiceTable>(Header.VoiceTable);

            if (NumTracks == 0 || NumTracks > 16)
            {
                return;
            }

            var reader = new ROMReader();

            reader.InitReader(_binary);

            for (int i = 0; i < NumTracks; i++)
            {
                reader.SetOffset(Header.Tracks[i]);

                byte cmd = 0, runCmd = 0, prevNote = 0, prevVelocity = 127;

                while (cmd != 0xB1 && cmd != 0xB6)
                {
                    uint     off     = reader.Position;
                    ICommand command = null;

                    cmd = reader.ReadByte();
                    if (cmd >= 0xBD) // Commands that work within running status
                    {
                        runCmd = cmd;
                    }

                    #region TIE & Notes

                    if (runCmd >= 0xCF && cmd < 0x80) // Within running status
                    {
                        var  o     = reader.Position;
                        byte peek1 = reader.ReadByte(),
                             peek2 = reader.ReadByte();
                        reader.SetOffset(o);
                        if (peek1 >= 128)
                        {
                            command = AddNoteEvent(cmd, prevVelocity, 0, runCmd, out prevNote, out prevVelocity);
                        }
                        else if (peek2 > 3 || peek2 < 1)
                        {
                            command = AddNoteEvent(cmd, reader.ReadByte(), 0, runCmd, out prevNote, out prevVelocity);
                        }
                        else
                        {
                            command = AddNoteEvent(cmd, reader.ReadByte(), reader.ReadByte(), runCmd, out prevNote, out prevVelocity);
                        }
                    }
                    else if (cmd >= 0xCF)
                    {
                        var  o     = reader.Position;
                        byte peek1 = reader.ReadByte(),
                             peek2 = reader.ReadByte(),
                             peek3 = reader.ReadByte();
                        reader.SetOffset(o);
                        if (peek1 >= 128)
                        {
                            command = AddNoteEvent(prevNote, prevVelocity, 0, runCmd, out prevNote, out prevVelocity);
                        }
                        else if (peek2 >= 128)
                        {
                            command = AddNoteEvent(reader.ReadByte(), prevVelocity, 0, runCmd, out prevNote, out prevVelocity);
                        }
                        // TIE cannot have an added duration so it needs to stop here
                        else if (cmd == 0xCF || peek3 > 3 || peek3 < 1)
                        {
                            command = AddNoteEvent(reader.ReadByte(), reader.ReadByte(), 0, runCmd, out prevNote, out prevVelocity);
                        }
                        else
                        {
                            command = AddNoteEvent(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), runCmd, out prevNote, out prevVelocity);
                        }
                    }

                    #endregion

                    #region Rests

                    else if (cmd >= 0x80 && cmd <= 0xB0)
                    {
                        command = new RestCommand {
                            Rest = SongEvent.RestFromCMD(0x80, cmd)
                        }
                    }
                    ;

                    #endregion

                    #region Commands

                    else if (runCmd < 0xCF && cmd < 0x80) // Commands within running status
                    {
                        switch (runCmd)
                        {
                        case 0xBD: command = new VoiceCommand {
                                Voice = cmd
                        }; break;

                        case 0xBE: command = new VolumeCommand {
                                Volume = cmd
                        }; break;

                        case 0xBF: command = new PanpotCommand {
                                Panpot = (sbyte)(cmd - 0x40)
                        }; break;

                        case 0xC0: command = new BendCommand {
                                Bend = (sbyte)(cmd - 0x40)
                        }; break;

                        case 0xC1: command = new BendRangeCommand {
                                Range = cmd
                        }; break;

                        case 0xC2: command = new LFOSpeedCommand {
                                Speed = cmd
                        }; break;

                        case 0xC3: command = new LFODelayCommand {
                                Delay = cmd
                        }; break;

                        case 0xC4: command = new ModDepthCommand {
                                Depth = cmd
                        }; break;

                        case 0xC5: command = new ModTypeCommand {
                                Type = cmd
                        }; break;

                        case 0xC8: command = new TuneCommand {
                                Tune = (sbyte)(cmd - 0x40)
                        }; break;

                        case 0xCD: command = new LibraryCommand {
                                Command = cmd, Argument = reader.ReadByte()
                        }; break;

                        case 0xCE: command = new EndOfTieCommand {
                                Note = (sbyte)cmd
                        }; prevNote = cmd; break;
                        }
                    }
                    else if (cmd > 0xB0 && cmd < 0xCF)
                    {
                        switch (cmd)
                        {
                        case 0xB1:     // FINE & PREV
                        case 0xB6: command = new M4AFinishCommand {
                                Type = cmd
                        }; break;

                        case 0xB2: command = new GoToCommand {
                                Offset = reader.ReadPointer()
                        }; break;

                        case 0xB3: command = new CallCommand {
                                Offset = reader.ReadPointer()
                        }; break;

                        case 0xB4: command = new ReturnCommand(); break;

                        case 0xB5: command = new RepeatCommand {
                                Times = reader.ReadByte(), Offset = reader.ReadPointer()
                        }; break;

                        case 0xB9: command = new MemoryAccessCommand {
                                Arg1 = reader.ReadByte(), Arg2 = reader.ReadByte(), Arg3 = reader.ReadByte()
                        }; break;

                        case 0xBA: command = new PriorityCommand {
                                Priority = reader.ReadByte()
                        }; break;

                        case 0xBB: command = new TempoCommand {
                                Tempo = (ushort)(reader.ReadByte() * 2)
                        }; break;

                        case 0xBC: command = new KeyShiftCommand {
                                Shift = reader.ReadSByte()
                        }; break;

                        // Commands that work within running status:
                        case 0xBD: command = new VoiceCommand {
                                Voice = reader.ReadByte()
                        }; break;

                        case 0xBE: command = new VolumeCommand {
                                Volume = reader.ReadByte()
                        }; break;

                        case 0xBF: command = new PanpotCommand {
                                Panpot = (sbyte)(reader.ReadByte() - 0x40)
                        }; break;

                        case 0xC0: command = new BendCommand {
                                Bend = (sbyte)(reader.ReadByte() - 0x40)
                        }; break;

                        case 0xC1: command = new BendRangeCommand {
                                Range = reader.ReadByte()
                        }; break;

                        case 0xC2: command = new LFOSpeedCommand {
                                Speed = reader.ReadByte()
                        }; break;

                        case 0xC3: command = new LFODelayCommand {
                                Delay = reader.ReadByte()
                        }; break;

                        case 0xC4: command = new ModDepthCommand {
                                Depth = reader.ReadByte()
                        }; break;

                        case 0xC5: command = new ModTypeCommand {
                                Type = reader.ReadByte()
                        }; break;

                        case 0xC8: command = new TuneCommand {
                                Tune = (sbyte)(reader.ReadByte() - 0x40)
                        }; break;

                        case 0xCD: command = new LibraryCommand {
                                Command = reader.ReadByte(), Argument = reader.ReadByte()
                        }; break;

                        case 0xCE:     // EOT
                            sbyte note;

                            if (reader.PeekByte() < 128)
                            {
                                note     = reader.ReadSByte();
                                prevNote = (byte)note;
                            }
                            else
                            {
                                note = -1;
                            }

                            command = new EndOfTieCommand {
                                Note = note
                            };
                            break;

                        default: Console.WriteLine("Invalid command: 0x{0:X} = {1}", reader.Position, cmd); break;
                        }
                    }

                    #endregion

                    Commands[i].Add(new SongEvent(off, command));
                }
            }

            ICommand AddNoteEvent(byte note, byte velocity, byte addedDuration, byte runCmd, out byte prevNote, out byte prevVelocity)
            {
                return(new M4ANoteCommand
                {
                    Note = (sbyte)(prevNote = note),
                    Velocity = prevVelocity = velocity,
                    Duration = (short)(runCmd == 0xCF ? -1 : (SongEvent.RestFromCMD(0xCF, runCmd) + addedDuration))
                });
            }
        }