public override void SaveAsMIDI(string fileName) { if (NumTracks == 0) { throw new InvalidDataException("Questa canzone non ha tracce."); } CalculateTicks(); var midi = new Sequence(96) { 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); FreeNoteCommand freeNote = null; MidiEvent freeNoteOff = null; for (int j = 0; j < Commands[i].Count; j++) { var e = Commands[i][j]; // Extended note ended ended and wasn't renewed if (freeNoteOff != null && freeNoteOff.AbsoluteTicks < e.AbsoluteTicks * 2) { freeNote = null; freeNoteOff = null; } if (e.Command is VolumeCommand vol) { track.Insert(e.AbsoluteTicks * 2, new ChannelMessage(ChannelCommand.Controller, i, (int)ControllerType.Volume, vol.Volume / 2)); } else if (e.Command is VoiceCommand voice) { // TODO: Fork and remove restriction track.Insert(e.AbsoluteTicks * 2, new ChannelMessage(ChannelCommand.ProgramChange, i, voice.Voice & 0x7F)); } else if (e.Command is PanpotCommand pan) { track.Insert(e.AbsoluteTicks * 2, new ChannelMessage(ChannelCommand.Controller, i, (int)ControllerType.Pan, pan.Panpot / 2 + 0x40)); } else if (e.Command is BendCommand bend) { track.Insert(e.AbsoluteTicks * 2, new ChannelMessage(ChannelCommand.PitchWheel, i, 0, bend.Bend / 2 + 0x40)); } else if (e.Command is BendRangeCommand bendr) { track.Insert(e.AbsoluteTicks * 2, new ChannelMessage(ChannelCommand.Controller, i, 20, bendr.Range / 2)); } else if (e.Command is MLSSNoteCommand note) { // Extended note is playing and it should be extended by this note if (freeNote != null && freeNote.Note - 0x80 == note.Note) { // Move the note off command track.Move(freeNoteOff, freeNoteOff.AbsoluteTicks + note.Duration * 2); } // Extended note is playing but this note is different OR there is no extended note playing // Either way we play a new note and forget that one else { track.Insert(e.AbsoluteTicks * 2, new ChannelMessage(ChannelCommand.NoteOn, i, note.Note, 0x7F)); track.Insert(e.AbsoluteTicks * 2 + note.Duration * 2, new ChannelMessage(ChannelCommand.NoteOff, i, note.Note)); freeNote = null; freeNoteOff = null; } } else if (e.Command is FreeNoteCommand free) { // Extended note is playing and it should be extended if (freeNote != null && freeNote.Note == free.Note) { // Move the note off command track.Move(freeNoteOff, freeNoteOff.AbsoluteTicks + free.Duration * 2); } // Extended note is playing but this note is different OR there is no extended note playing // Either way we play a new note and forget that one else { track.Insert(e.AbsoluteTicks * 2, new ChannelMessage(ChannelCommand.NoteOn, i, free.Note - 0x80, 0x7F)); track.Insert(e.AbsoluteTicks * 2 + free.Duration * 2, new ChannelMessage(ChannelCommand.NoteOff, i, free.Note - 0x80)); freeNote = free; freeNoteOff = track.GetMidiEvent(track.Count - 2); // -1 would be the end of track event } } else if (i == 0 && e.Command is TempoCommand tempo) { var change = new TempoChangeBuilder { Tempo = (60000000 / tempo.Tempo) }; change.Build(); metaTrack.Insert(e.AbsoluteTicks * 2, change.Result); } 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 * 2, new MetaMessage(MetaType.Marker, new byte[] { (byte)'[' })); metaTrack.Insert(e.AbsoluteTicks * 2, 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); }