public override void SaveAsMIDI(string fileName) { if (NumTracks == 0) { throw new InvalidDataException("This song has no tracks."); } CalculateTicks(); var midi = new Sequence(24) { Format = 1 }; var metaTrack = new Sanford.Multimedia.Midi.Track(); midi.Add(metaTrack); for (int i = 0; i < NumTracks; i++) { var track = new Sanford.Multimedia.Midi.Track(); midi.Add(track); int endOfPattern = 0, startOfPatternTicks = 0, endOfPatternTicks = 0, shift = 0; var playing = new List <M4ANoteCommand>(); for (int j = 0; j < Commands[i].Count; j++) { var e = Commands[i][j]; int ticks = e.AbsoluteTicks + (endOfPatternTicks - startOfPatternTicks); switch (e.Command) { case KeyShiftCommand keysh: shift = keysh.Shift; break; case M4ANoteCommand note: int n = (note.Note + shift).Clamp(0, 0x7F); track.Insert(ticks, new ChannelMessage(ChannelCommand.NoteOn, i, n, note.Velocity)); if (note.Duration != -1) { track.Insert(ticks + note.Duration, new ChannelMessage(ChannelCommand.NoteOff, i, n)); } else { playing.Add(note); } break; case EndOfTieCommand eot: M4ANoteCommand nc = null; if (eot.Note == -1) { nc = playing.LastOrDefault(); } else { nc = playing.LastOrDefault(no => no.Note == eot.Note); } if (nc != null) { n = (nc.Note + shift).Clamp(0, 0x7F); track.Insert(ticks, new ChannelMessage(ChannelCommand.NoteOff, i, n)); playing.Remove(nc); } break; case PriorityCommand prio: track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, (int)ControllerType.VolumeFine, prio.Priority)); break; case VoiceCommand voice: track.Insert(ticks, new ChannelMessage(ChannelCommand.ProgramChange, i, voice.Voice)); break; case VolumeCommand vol: track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, (int)ControllerType.Volume, vol.Volume)); break; case PanpotCommand pan: track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, (int)ControllerType.Pan, pan.Panpot + 0x40)); break; case BendCommand bend: track.Insert(ticks, new ChannelMessage(ChannelCommand.PitchWheel, i, 0, bend.Bend + 0x40)); break; case BendRangeCommand bendr: track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, 20, bendr.Range)); break; case LFOSpeedCommand lfos: track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, 21, lfos.Speed)); break; case LFODelayCommand lfodl: track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, 26, lfodl.Delay)); break; case ModDepthCommand mod: track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, (int)ControllerType.ModulationWheel, mod.Depth)); break; case ModTypeCommand modt: track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, 22, modt.Type)); break; case TuneCommand tune: track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, 24, tune.Tune)); break; case LibraryCommand xcmd: track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, 30, xcmd.Command)); track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, 29, xcmd.Argument)); break; case TempoCommand tempo: var change = new TempoChangeBuilder { Tempo = (60000000 / tempo.Tempo) }; change.Build(); metaTrack.Insert(ticks, change.Result); break; case CallCommand patt: int callCmd = Commands[i].FindIndex(c => c.GetOffset() == patt.Offset); endOfPattern = j; endOfPatternTicks = e.AbsoluteTicks; j = callCmd - 1; // -1 for incoming ++ startOfPatternTicks = Commands[i][j + 1].AbsoluteTicks; break; case ReturnCommand _: if (endOfPattern != 0) { j = endOfPattern; endOfPattern = startOfPatternTicks = endOfPatternTicks = 0; } break; case GoToCommand goTo: if (i == 0) { int jumpCmd = Commands[i].FindIndex(c => c.GetOffset() == goTo.Offset); metaTrack.Insert(Commands[i][jumpCmd].AbsoluteTicks, new MetaMessage(MetaType.Marker, new byte[] { (byte)'[' })); metaTrack.Insert(ticks, new MetaMessage(MetaType.Marker, new byte[] { (byte)']' })); } break; case FinishCommand _: goto endOfTrack; } } endOfTrack :; } midi.Save(fileName); }
public override void SaveAsMIDI(string fileName) { if (NumTracks == 0) { throw new InvalidDataException("Questa canzone non ha tracce."); } CalculateTicks(); var midi = new Sequence(24) { Format = 1 }; var metaTrack = new Sanford.Multimedia.Midi.Track(); midi.Add(metaTrack); for (int i = 0; i < NumTracks; i++) { var track = new Sanford.Multimedia.Midi.Track(); midi.Add(track); int endOfPattern = 0, startOfPatternTicks = 0, endOfPatternTicks = 0, shift = 0; var playing = new List <M4ANoteCommand>(); for (int j = 0; j < Commands[i].Count; j++) { var e = Commands[i][j]; int ticks = e.AbsoluteTicks + (endOfPatternTicks - startOfPatternTicks); if (e.Command is KeyShiftCommand keysh) { shift = keysh.Shift; } else if (e.Command is M4ANoteCommand note) { int n = (note.Note + shift).Clamp(0, 0x7F); track.Insert(ticks, new ChannelMessage(ChannelCommand.NoteOn, i, n, note.Velocity)); if (note.Duration != -1) { track.Insert(ticks + note.Duration, new ChannelMessage(ChannelCommand.NoteOff, i, n)); } else { playing.Add(note); } } else if (e.Command is EndOfTieCommand eot) { M4ANoteCommand nc = null; if (eot.Note == -1) { nc = playing.LastOrDefault(); } else { nc = playing.LastOrDefault(n => n.Note == eot.Note); } if (nc != null) { int no = (nc.Note + shift).Clamp(0, 0x7F); track.Insert(ticks, new ChannelMessage(ChannelCommand.NoteOff, i, no)); playing.Remove(nc); } } else if (e.Command is VoiceCommand voice) { // TODO: Fork and remove restriction track.Insert(ticks, new ChannelMessage(ChannelCommand.ProgramChange, i, voice.Voice & 0x7F)); } else if (e.Command is VolumeCommand vol) { track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, (int)ControllerType.Volume, vol.Volume)); } else if (e.Command is PanpotCommand pan) { track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, (int)ControllerType.Pan, pan.Panpot + 0x40)); } else if (e.Command is BendCommand bend) { track.Insert(ticks, new ChannelMessage(ChannelCommand.PitchWheel, i, 0, bend.Bend + 0x40)); } else if (e.Command is BendRangeCommand bendr) { track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, 20, bendr.Range)); } else if (e.Command is LFOSpeedCommand lfos) { track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, 21, lfos.Speed)); } else if (e.Command is LFODelayCommand lfodl) { track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, 26, lfodl.Delay)); } else if (e.Command is ModDepthCommand mod) { track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, (int)ControllerType.ModulationWheel, mod.Depth)); } else if (e.Command is ModTypeCommand modt) { track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, 22, modt.Type)); } else if (e.Command is TuneCommand tune) { track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, 24, tune.Tune)); } else if (e.Command is TempoCommand tempo) { var change = new TempoChangeBuilder { Tempo = (60000000 / tempo.Tempo) }; change.Build(); metaTrack.Insert(ticks, change.Result); } else if (e.Command is CallCommand patt) { int callCmd = Commands[i].FindIndex(c => c.GetOffset() == patt.Offset); endOfPattern = j; endOfPatternTicks = e.AbsoluteTicks; j = callCmd - 1; // -1 for incoming ++ startOfPatternTicks = Commands[i][j + 1].AbsoluteTicks; } else if (e.Command is ReturnCommand) { if (endOfPattern != 0) { j = endOfPattern; endOfPattern = startOfPatternTicks = endOfPatternTicks = 0; } } else if (i == 0 && e.Command is GoToCommand goTo) { int jumpCmd = Commands[i].FindIndex(c => c.GetOffset() == goTo.Offset); metaTrack.Insert(Commands[i][jumpCmd].AbsoluteTicks, new MetaMessage(MetaType.Marker, new byte[] { (byte)'[' })); metaTrack.Insert(ticks, new MetaMessage(MetaType.Marker, new byte[] { (byte)']' })); } else if (e.Command is FinishCommand fine) { // TODO: Fix ticks before end of track event // Library automatically is updating track.EndOfTrackOffset for us break; } } } midi.Save(fileName); }
internal void SaveAsMIDI(string fileName) { if (NumTracks == 0) { throw new InvalidDataException("This song has no tracks."); } if (ROM.Instance.Game.Engine.Type != EngineType.M4A) { throw new PlatformNotSupportedException("Exporting to MIDI from this game engine is not supported at this time."); } CalculateTicks(); var midi = new Sequence(Engine.GetTicksPerBar() / 4) { Format = 2 }; for (int i = 0; i < NumTracks; i++) { var track = new Sanford.Multimedia.Midi.Track(); midi.Add(track); int endOfPattern = 0, startOfPatternTicks = 0, endOfPatternTicks = 0, shift = 0; var playing = new List <M4ANoteCommand>(); for (int j = 0; j < Commands[i].Count; j++) { var e = Commands[i][j]; int ticks = (int)(e.AbsoluteTicks + (endOfPatternTicks - startOfPatternTicks)); if (e.Command is KeyShiftCommand keysh) { shift = keysh.Shift; } else if (e.Command is M4ANoteCommand note) { int n = (note.Note + shift).Clamp(0, 127); track.Insert(ticks, new ChannelMessage(ChannelCommand.NoteOn, i, n, note.Velocity)); if (note.Duration != -1) { track.Insert(ticks + note.Duration, new ChannelMessage(ChannelCommand.NoteOff, i, n)); } else { playing.Add(note); } } else if (e.Command is EndOfTieCommand eot) { M4ANoteCommand nc = null; if (eot.Note == -1) { nc = playing.LastOrDefault(); } else { nc = playing.LastOrDefault(n => n.Note == eot.Note); } if (nc != null) { int no = (nc.Note + shift).Clamp(0, 127); track.Insert(ticks, new ChannelMessage(ChannelCommand.NoteOff, i, no)); playing.Remove(nc); } } else if (e.Command is VoiceCommand voice) { track.Insert(ticks, new ChannelMessage(ChannelCommand.ProgramChange, i, voice.Voice)); } else if (e.Command is VolumeCommand vol) { track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, (int)ControllerType.Volume, vol.Volume)); } else if (e.Command is PanpotCommand pan) { track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, (int)ControllerType.Pan, pan.Panpot + 0x40)); } else if (e.Command is BendCommand bend) { track.Insert(ticks, new ChannelMessage(ChannelCommand.PitchWheel, i, 0, bend.Bend + 0x40)); } else if (e.Command is BendRangeCommand bendr) { track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, 20, bendr.Range)); } else if (e.Command is LFOSpeedCommand lfos) { track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, 21, lfos.Speed)); } else if (e.Command is LFODelayCommand lfodl) { track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, 26, lfodl.Delay)); } else if (e.Command is ModDepthCommand mod) { track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, (int)ControllerType.ModulationWheel, mod.Depth)); } else if (e.Command is ModTypeCommand modt) { track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, 22, modt.Type)); } else if (e.Command is TuneCommand tune) { track.Insert(ticks, new ChannelMessage(ChannelCommand.Controller, i, 24, tune.Tune)); } else if (e.Command is TempoCommand tempo) { var change = new TempoChangeBuilder { Tempo = (60000000 / tempo.Tempo) }; change.Build(); track.Insert(ticks, change.Result); } else if (e.Command is CallCommand patt) { int callCmd = Commands[i].FindIndex(c => c.Offset == patt.Offset); endOfPattern = j; endOfPatternTicks = (int)e.AbsoluteTicks; j = callCmd - 1; // -1 for incoming ++ startOfPatternTicks = (int)Commands[i][j + 1].AbsoluteTicks; } else if (e.Command is ReturnCommand) { if (endOfPattern != 0) { j = endOfPattern; endOfPattern = startOfPatternTicks = endOfPatternTicks = 0; } } else if (i == 0 && e.Command is GoToCommand goTo) { track.Insert(ticks, new MetaMessage(MetaType.Marker, new byte[] { (byte)']' })); int jumpCmd = Commands[i].FindIndex(c => c.Offset == goTo.Offset); track.Insert((int)Commands[i][jumpCmd].AbsoluteTicks, new MetaMessage(MetaType.Marker, new byte[] { (byte)'[' })); } else if (e.Command is FinishCommand fine) { track.Insert(ticks, new MetaMessage(MetaType.EndOfTrack, new byte[0])); break; } } } midi.Save(fileName); }