public void AddMetaData() { //create metadata track Track t = new Track(); byte[] tempo = new byte[3]; int ms_per_min = 60000000; int ms_per_quarternote = ms_per_min / FBPM; tempo[0] = (byte)((ms_per_quarternote >> 16) & byte.MaxValue); tempo[1] = (byte)((ms_per_quarternote >> 8) & byte.MaxValue); tempo[2] = (byte)(ms_per_quarternote & byte.MaxValue); TempoChangeBuilder tcBuilder = new TempoChangeBuilder(new MetaMessage(MetaType.Tempo, tempo)); tcBuilder.Build(); t.Insert(1, tcBuilder.Result); byte[] timesignature = new byte[4]; timesignature[0] = FTimeSignature.Numerator; timesignature[1] = (byte)Math.Log(FTimeSignature.Denominator, 2); timesignature[2] = FTimeSignature.MetronomePulse; timesignature[3] = FTimeSignature.NumberOf32nds; TimeSignatureBuilder tsBuilder = new TimeSignatureBuilder(new MetaMessage(MetaType.TimeSignature, timesignature)); tsBuilder.Build(); t.Insert(2, tsBuilder.Result); FSequence.Add(t); }
public void Process(MetaMessage message) { #region Require if (message == null) { throw new ArgumentNullException("message"); } #endregion #region Guard if (message.MetaType != MetaType.Tempo) { return; } #endregion TempoChangeBuilder builder = new TempoChangeBuilder(message); // Set the new tempo. Tempo = builder.Tempo; Tempo = Tempo; }
private void OnMetaMessagePlayed(object sender, MetaMessageEventArgs e) { if (e.Message.MetaType == MetaType.Tempo) { TempoChangeBuilder builder = new TempoChangeBuilder(e.Message); midiTempo = (60000000 / builder.Tempo); OnTempoChange?.Invoke(this, midiTempo); } if (e.Message.MetaType == MetaType.Lyric) { MetaTextBuilder builder = new MetaTextBuilder(e.Message); if (e.MidiTrack == LoadedTrack) { OnLyric?.Invoke(this, builder.Text); } } if (e.Message.MetaType == MetaType.TrackName) { MetaTextBuilder builder = new MetaTextBuilder(e.Message); ParseTrackName(e.MidiTrack, builder.Text); if (e.MidiTrack == LoadedTrack) { OnTrackNameChange?.Invoke(this, builder.Text); } } if (e.Message.MetaType == MetaType.InstrumentName) { MetaTextBuilder builder = new MetaTextBuilder(e.Message); OnTrackNameChange?.Invoke(this, builder.Text); Console.WriteLine("Instrument name: " + builder.Text); } }
public MetaMessage BuildMessage() { var b = new TempoChangeBuilder(); b.Tempo = (int)Tempo; b.Build(); return(b.Result); }
/// <summary> /// Adds a MIDI message which sets the tempo /// </summary> /// <param name="tempoBPM">Tempo in BPM</param> public void SetTempo(int tempoBPM) { TempoChangeBuilder builder = new TempoChangeBuilder { Tempo = 60000000 / tempoBPM // convert to PPQ }; builder.Build(); QueueMessage(builder.Result); }
private Sequence CreateSequence() { if (tunes == null || tunes.Count < 2) { return(null); } selectedTune = 1; SetDefaultValues(); nextNote = TimeSpan.Zero; SetHeaderValues(); SetHeaderValues(selectedTune, true); StartMeasure(); sequence = new Sequence(Ppqn); sequence.Format = 1; metaTrack = new Track(); mainTrack = new Track(); sequence.Add(metaTrack); TempoChangeBuilder tempoBuilder = new TempoChangeBuilder(); tempoBuilder.Tempo = Tempo; tempoBuilder.Build(); metaTrack.Insert(0, tempoBuilder.Result); TimeSignatureBuilder timeBuilder = new TimeSignatureBuilder(); timeBuilder.Numerator = timeSigNumerator; timeBuilder.Denominator = timeSigDenominator; timeBuilder.Build(); metaTrack.Insert(0, timeBuilder.Result); sequence.Add(mainTrack); MetaTextBuilder textBuilder = new MetaTextBuilder(); textBuilder.Type = MetaType.TrackName; textBuilder.Text = "Tempo Track"; textBuilder.Build(); metaTrack.Insert(0, textBuilder.Result); textBuilder = new MetaTextBuilder(); textBuilder.Type = MetaType.TrackName; textBuilder.Text = "Tune 1"; textBuilder.Build(); mainTrack.Insert(0, textBuilder.Result); while (tokenIndex < tokens.Count) { AddNextNote(); } return(sequence); }
public GuitarTempo(GuitarMessageList owner, MidiEvent ev) : base(owner, ev, null, GuitarMessageType.GuitarTempo) { if (ev == null) { this.Tempo = Utility.DummyTempo; } else { var cb = new TempoChangeBuilder((MetaMessage)ev.Clone()); this.Tempo = cb.Tempo; } }
public float GetTimeFromTick(int tick) { if (tick <= 0) { return(0f); } float ms = 0f; int mul = midiTempo; List <Track> trackEnumsToRemove = new List <Track>(); Dictionary <Track, IEnumerator <MidiEvent> > trackEnums = new Dictionary <Track, IEnumerator <MidiEvent> >(); foreach (Track t in Sequence) { var en = t.Iterator().GetEnumerator(); if (en.MoveNext()) { trackEnums.Add(t, en); } } for (int i = 0; i < tick; i++) { foreach (KeyValuePair <Track, IEnumerator <MidiEvent> > tem in trackEnums) { if (tem.Value == null) { continue; } Track tek = tem.Key; IEnumerator <MidiEvent> ten = tem.Value; MidiEvent ev = ten.Current; bool end = false; while (ten.Current.AbsoluteTicks < i) { if (!ten.MoveNext()) { end = true; break; } } if (ten.Current != ev) { // New event, apply if (ev.MidiMessage is MetaMessage) { MetaMessage msg = ev.MidiMessage as MetaMessage; if (msg.MetaType == MetaType.Tempo) { TempoChangeBuilder builder = new TempoChangeBuilder(msg); mul = (int)((60000000 / builder.Tempo) * Speed); } } } if (end) { trackEnumsToRemove.Add(tek); } } if (trackEnumsToRemove.Count > 0) { foreach (Track t in trackEnumsToRemove) { trackEnums.Remove(t); } trackEnumsToRemove.Clear(); } ms += 1 * (60000f / (float)(mul * Sequence.Division)); } return(ms); }
/// <summary> /// Creates a notechart from the specified midi path and the actual charttype /// (i.e. ExpertSingle from notes.mid). Due to the overhead necessary to /// parse a midi file. I am going to cram all midi->chart operations into /// one function call. /// This function uses the Sanford midi parser. While it is horribly slow /// on larger (e.g. RB) midis, it works without a hitch on every midi I've /// come across. /// </summary> /// <param name="chartSelection"> /// The information on which particular notechart to use. /// </param> /// <param name="chartInfo">The metadata on the chart.</param> /// <param name="BPMChanges">The list of BPM changes for this chart.</param> /// <returns> /// A filled out Notechart containing the needed information from the *.mid file. /// </returns> public static Notes ParseMidiInformationSanford(ChartSelection chartSelection, Info chartInfo, List <BPMChange> BPMChanges) { Notes notechartToReturn = new Notes(); notechartToReturn.instrument = chartSelection.instrument; notechartToReturn.difficulty = chartSelection.difficulty; // The following two switch's are used to get the proper midi terminology for // the selected track and difficulty. string instrumentPart = null; int greenKey = 0; int redKey = 0; int yellowKey = 0; int blueKey = 0; int orangeKey = 0; switch (chartSelection.instrument) { case "Single": instrumentPart = "PART GUITAR"; break; case "DoubleGuitar": instrumentPart = "PART GUITAR COOP"; break; case "DoubleBass": instrumentPart = "PART BASS"; break; case "Drums": instrumentPart = "PART DRUMS"; break; default: instrumentPart = "PART GUITAR"; break; } switch (chartSelection.difficulty) { case "Expert": greenKey = 96; redKey = 97; yellowKey = 98; blueKey = 99; orangeKey = 100; break; case "Hard": greenKey = 84; redKey = 85; yellowKey = 86; blueKey = 87; orangeKey = 88; break; case "Medium": greenKey = 72; redKey = 73; yellowKey = 74; blueKey = 75; orangeKey = 76; break; case "Easy": greenKey = 60; redKey = 61; yellowKey = 62; blueKey = 63; orangeKey = 64; break; default: greenKey = 96; redKey = 97; yellowKey = 98; blueKey = 99; orangeKey = 100; break; } Sequence mySequence = new Sequence(chartSelection.directory + "\\notes.mid"); Track trackToUse = new Track(); chartInfo.resolution = mySequence.Division; // Go through each event in the first track (which contains the BPM changes) // and parse the resulting string. Track sanTrack = mySequence[0]; foreach (Sanford.Multimedia.Midi.MidiEvent currEvent in sanTrack.Iterator()) { if (currEvent.MidiMessage.MessageType == MessageType.Meta) { MetaMessage currMessage = currEvent.MidiMessage as MetaMessage; //currTickValue += Convert.ToUInt32(splitEventString[1]); if (currMessage.MetaType == MetaType.Tempo) { TempoChangeBuilder tempoBuilder = new TempoChangeBuilder(currMessage); int midiBPMChange = tempoBuilder.Tempo; // In midi files, bpm chages are stored as "microseconds per quarter note" // and must be converted to BPM, and then into the non decimal format the game // uses. double currBPMDouble = 60000000 / (double)midiBPMChange; uint BPMToAdd = (uint)(currBPMDouble * 1000); BPMChanges.Add(new BPMChange((uint)currEvent.AbsoluteTicks, (uint)BPMToAdd)); } } } // Find the specified instrument's track for (int i = 1; i < mySequence.Count; i++) { sanTrack = mySequence[i]; Sanford.Multimedia.Midi.MidiEvent currEvent = sanTrack.GetMidiEvent(0); if (currEvent.MidiMessage.MessageType == MessageType.Meta) { MetaMessage currMessage = currEvent.MidiMessage as MetaMessage; if (currMessage.MetaType == MetaType.TrackName) { MetaTextBuilder trackName = new MetaTextBuilder(currMessage); // -If we come across a "T1 GEMS" track, we're in GH1 territory. // -GH2/FoF has both PART BASS and PART RHYTHM (one or the other depending // on the chart). if ((trackName.Text == instrumentPart) || (trackName.Text == "T1 GEMS") || ((trackName.Text == "PART RHYTHM") && (instrumentPart == "PART BASS"))) { trackToUse = sanTrack; } } } } Note currNote = new Note(); bool blankNote = true; // Scan through and record every note specific to the selected difficulty foreach (Sanford.Multimedia.Midi.MidiEvent currEvent in trackToUse.Iterator()) { // We need to specify wether a note is blank or not so we don't add // blank notes from other difficulties into the chart, but if we have // a filled out note, any nonzero tick value means we are moving to a // new note, so we must cut our ties and add this note to the chart. if ((currEvent.DeltaTicks != 0) && !blankNote) { notechartToReturn.notes.Add(currNote); currNote = new Note(); blankNote = true; } if (currEvent.MidiMessage.MessageType == MessageType.Channel) { ChannelMessage currMessage = currEvent.MidiMessage as ChannelMessage; if (currMessage.Command == ChannelCommand.NoteOn) { // Only consider notes within the octave our difficulty is in. if (((currMessage.Data1 == greenKey) || (currMessage.Data1 == redKey) || (currMessage.Data1 == yellowKey) || (currMessage.Data1 == blueKey) || (currMessage.Data1 == orangeKey)) && (currMessage.Data2 != 0)) { // If it's a new note, we need to setup the tick value of it. if (blankNote) { //currNote.TickValue = totalTickValue; currNote.tickValue = (uint)currEvent.AbsoluteTicks; blankNote = false; } if (currMessage.Data1 == greenKey) { currNote.addNote(0); } else if (currMessage.Data1 == redKey) { currNote.addNote(1); } else if (currMessage.Data1 == yellowKey) { currNote.addNote(2); } else if (currMessage.Data1 == blueKey) { currNote.addNote(3); } else if (currMessage.Data1 == orangeKey) { currNote.addNote(4); } } } } } return(notechartToReturn); }
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); }
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); }
private Track CreateTrack(R1Jaguar_MusicDescriptor jagFile) { Track t = new Track(); TempoChangeBuilder b = new TempoChangeBuilder(); b.Tempo = 22000; b.Build(); t.Insert(0, b.Result); ChannelMessageBuilder builder = new ChannelMessageBuilder(); Dictionary <int, int> curNoteOnChannel = new Dictionary <int, int>(); int timeScale = 1; for (int i = 0; i < jagFile.MusicData.Length; i++) { R1Jaguar_MusicData e = jagFile.MusicData[i]; if (e.Time != int.MaxValue) { int channelByte = BitHelpers.ExtractBits(e.Command, 8, 24); if (channelByte == 0x7F) // special point in the song { int idByte = BitHelpers.ExtractBits(e.Command, 8, 0); if (idByte == 0xFF && i >= jagFile.MusicData.Length - 2) // End point { t.EndOfTrackOffset = e.Time / timeScale; break; } else { // Loop point, or command to return to loop point } } else { int channel = BitHelpers.ExtractBits(e.Command, 4, 26); int command = BitHelpers.ExtractBits(e.Command, 1, 31); if (command == 1) { // Note off if (curNoteOnChannel.ContainsKey(channel)) { builder.Command = ChannelCommand.NoteOff; int note = curNoteOnChannel.TryGetValue(channel, out int val) ? val : 0; if (note >= 128) { builder.MidiChannel = 9; builder.Data1 = note - 128; } else { builder.MidiChannel = channel; builder.Data1 = note; } builder.Data2 = 127; builder.Build(); t.Insert(e.Time / timeScale, builder.Result); } // Program change int instrument = BitHelpers.ExtractBits(e.Command, 5, 21); if (PercussionInstruments[instrument] != Percussion.None) { builder.MidiChannel = 9; } else { builder.MidiChannel = channel; builder.Command = ChannelCommand.ProgramChange; builder.Data1 = GeneralMidiInstruments[instrument] == Instrument.None ? 0 : (int)GeneralMidiInstruments[instrument]; if (GeneralMidiInstruments[instrument] == Instrument.Sitar) { Controller.print("unknown @ " + jagFile.MusicDataPointer); } builder.Build(); t.Insert(e.Time / timeScale, builder.Result); } // Note on builder.Command = ChannelCommand.NoteOn; int freq = BitHelpers.ExtractBits(e.Command, 13, 8); int vel = BitHelpers.ExtractBits(e.Command, 7, 0); //bool hasVelocity = BitHelpers.ExtractBits(e.Command, 1, 7) == 1; builder.Data1 = GetMidiPitch(freq, 349f); //builder.Data2 = UnityEngine.Mathf.RoundToInt(127f * (vel / 7f)); float velf = ((vel + 1) / 128f); // hack int veli = Mathf.RoundToInt(velf * 127f); /*if (!hasVelocity) { * veli = 127; * }*/ if (PercussionInstruments[instrument] != Percussion.None) { builder.Data1 = (int)PercussionInstruments[instrument]; builder.Data2 = PercussionInstruments[instrument] == Percussion.None ? 0 : veli; curNoteOnChannel[channel] = builder.Data1 + 128; } else { builder.Data2 = GeneralMidiInstruments[instrument] == Instrument.None ? 0 : veli; curNoteOnChannel[channel] = builder.Data1; } builder.Build(); t.Insert(e.Time / timeScale, builder.Result); } else { builder.Command = ChannelCommand.NoteOff; int note = curNoteOnChannel.TryGetValue(channel, out int val) ? val : 0; if (note >= 128) { builder.MidiChannel = 9; builder.Data1 = note - 128; } else { builder.MidiChannel = channel; builder.Data1 = note; } builder.Data2 = 127; curNoteOnChannel.Remove(channel); builder.Build(); t.Insert(e.Time / timeScale, builder.Result); } } } } return(t); }
private static void CodeFromThread() { ChannelMessageBuilder channelBuilder = new ChannelMessageBuilder(); TempoChangeBuilder tempoBuilder = new TempoChangeBuilder(); Sequencer s = new Sequencer(); s.Sequence = new Sequence(); Track track0 = new Track(); Track track1 = new Track(); Track track2 = new Track(); s.Sequence.Add(track0); s.Sequence.Add(track1); s.Sequence.Add(track2); //tempoBuilder.Tempo = (int)(1 / 150.0 * 60000000); //tempoBuilder.Build(); //track0.Insert(0, tempoBuilder.Result); channelBuilder.MidiChannel = 1; channelBuilder.Command = ChannelCommand.ProgramChange; channelBuilder.Data1 = (int)GeneralMidiInstrument.AcousticGrandPiano; channelBuilder.Data2 = 0; channelBuilder.Build(); track1.Insert(0, channelBuilder.Result); channelBuilder.Command = ChannelCommand.NoteOn; channelBuilder.Data1 = 60; // note C channelBuilder.Data2 = 127; // velocity 127 channelBuilder.Build(); track1.Insert(0, channelBuilder.Result); channelBuilder.Command = ChannelCommand.NoteOff; channelBuilder.Data1 = 60; // note C channelBuilder.Data2 = 0; // note off, so velocity 0 channelBuilder.Build(); track1.Insert(479, channelBuilder.Result); channelBuilder.MidiChannel = 2; channelBuilder.Command = ChannelCommand.ProgramChange; channelBuilder.Data1 = (int)GeneralMidiInstrument.AcousticBass; channelBuilder.Data2 = 0; channelBuilder.Build(); track2.Insert(0, channelBuilder.Result); channelBuilder.Command = ChannelCommand.NoteOn; channelBuilder.Data1 = 67; // note G channelBuilder.Data2 = 60; // velocity 60 channelBuilder.Build(); track2.Insert(480, channelBuilder.Result); //channelBuilder.Command = ChannelCommand.NoteOff; //channelBuilder.Data1 = 67; // note G //channelBuilder.Data2 = 0; // note off, so velocity 0 //channelBuilder.Build(); //track2.Insert(480 + 760, channelBuilder.Result); s.Sequence.Save("test.mid"); //s.Start(); }
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); }
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); }
private Track CreateTrack(GAX2_Song song, int trackNum) { Track t = new Track(); TempoChangeBuilder b = new TempoChangeBuilder(); b.Tempo = 500000; b.Build(); t.Insert(0, b.Result); ChannelMessageBuilder builder = new ChannelMessageBuilder(); int?lastNoteOn = null; int currentTime = 0; int timeScale = 5; t.EndOfTrackOffset = (song.Patterns[trackNum].Length * song.NumRowsPerPattern) * timeScale; for (int trackPiece = 0; trackPiece < song.Patterns[trackNum].Length; trackPiece++) { GAX2_Pattern gaxTrack = song.Patterns[trackNum][trackPiece]; int baseTime = trackPiece * song.NumRowsPerPattern; currentTime = baseTime; for (int i = 0; i < gaxTrack.Rows.Length; i++) { GAX2_PatternRow cmd = gaxTrack.Rows[i]; switch (cmd.Command) { case GAX2_PatternRow.Cmd.Note: if (cmd.Instrument == 250) { continue; } if (exportSingleSoundfont) { if (song.InstrumentSet[cmd.Instrument]?.Value == null || song.InstrumentSet[cmd.Instrument].Value.Sample >= 128) { continue; } } else { if (song.InstrumentSet[cmd.Instrument]?.Value == null || Array.IndexOf(song.InstrumentIndices, cmd.Instrument) >= 128) { continue; } } // Note off if (lastNoteOn.HasValue) { builder.Command = ChannelCommand.NoteOff; builder.MidiChannel = 0; builder.Data1 = lastNoteOn.Value; builder.Data2 = 127; builder.Build(); t.Insert(currentTime * timeScale, builder.Result); lastNoteOn = null; } // Program change { int instrument = 0; if (exportSingleSoundfont) { instrument = song.InstrumentSet[cmd.Instrument].Value.Sample; } else { instrument = Array.IndexOf(song.InstrumentIndices, cmd.Instrument); } builder.MidiChannel = 0; builder.Command = ChannelCommand.ProgramChange; builder.Data1 = instrument; builder.Build(); t.Insert(currentTime * timeScale, builder.Result); } // Note on { builder.Command = ChannelCommand.NoteOn; int freq = cmd.Note; int vel = cmd.Velocity; builder.Data1 = freq; //GetMidiPitch(GetFrequency(freq)); float velf = (vel / 255f); // hack int veli = Mathf.RoundToInt(velf * 127f); builder.Data2 = veli; lastNoteOn = builder.Data1; builder.Build(); t.Insert(currentTime * timeScale, builder.Result); } break; } currentTime += cmd.Duration; } } if (lastNoteOn.HasValue) { builder.Command = ChannelCommand.NoteOff; builder.MidiChannel = 0; builder.Data1 = lastNoteOn.Value; builder.Data2 = 127; builder.Build(); t.Insert(currentTime * timeScale, builder.Result); lastNoteOn = null; } return(t); }