/// <summary> /// Exports the current <see cref="Pattern"/> to track chunk. /// </summary> /// <param name="tempoMap">Tempo map to process pattern data according with.</param> /// <param name="channel">Channel of notes that will be generated by pattern.</param> /// <returns>The <see cref="TrackChunk"/> containing notes events generated by the current <see cref="Pattern"/>.</returns> /// <exception cref="ArgumentNullException"><paramref name="tempoMap"/> is null.</exception> public TrackChunk ToTrackChunk(TempoMap tempoMap, FourBitNumber channel) { ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); var context = new PatternContext(tempoMap, channel); var result = InvokeActions(0, context); // var trackChunk = new TrackChunk(); using (var notesManager = trackChunk.ManageNotes()) { notesManager.Notes.Add(result.Notes ?? Enumerable.Empty <Note>()); } using (var eventsManager = trackChunk.ManageTimedEvents()) { eventsManager.Events.Add(result.Events ?? Enumerable.Empty <TimedEvent>()); } // return(trackChunk); }
private static void SingleTrack(string filename) { // We open the file... var f = MidiFile.Read(filename); // Then we convert all the events to absolute time and merge them together var merged = new TrackChunk(); using (var mergedTimedEvents = merged.ManageTimedEvents()) { foreach (var chunk in f.GetTrackChunks()) { using var m = chunk.ManageTimedEvents(); mergedTimedEvents.Events.Add(m.Events); } mergedTimedEvents.SaveChanges(); } // And save var outFile = new MidiFile { TimeDivision = f.TimeDivision, Chunks = { merged } }; outFile.Write(Path.Join( Path.GetDirectoryName(filename), $"{Path.GetFileNameWithoutExtension(filename)}.singletrack.mid"), overwriteFile: true, format: MidiFileFormat.SingleTrack); }
/// <summary> /// Randomizes timed events contained in the specified <see cref="TrackChunk"/>. /// </summary> /// <param name="trackChunk"><see cref="TrackChunk"/> to randomize timed events in.</param> /// <param name="bounds">Bounds to randomize time within.</param> /// <param name="tempoMap">Tempo map used to calculate time bounds to randomize within.</param> /// <param name="settings">Settings according to which timed events should be randomized.</param> /// <exception cref="ArgumentNullException"><paramref name="trackChunk"/> is null. -or- /// <paramref name="bounds"/> is null. -or- <paramref name="tempoMap"/> is null.</exception> public static void RandomizeTimedEvents(this TrackChunk trackChunk, IBounds bounds, TempoMap tempoMap, TimedEventsRandomizingSettings settings = null) { ThrowIfArgument.IsNull(nameof(trackChunk), trackChunk); ThrowIfArgument.IsNull(nameof(bounds), bounds); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); using (var timedEventsManager = trackChunk.ManageTimedEvents()) { new TimedEventsRandomizer().Randomize(timedEventsManager.Events, bounds, tempoMap, settings); } }
/// <summary> /// Quantizes timed events contained in the specified <see cref="TrackChunk"/>. /// </summary> /// <param name="trackChunk"><see cref="TrackChunk"/> to quantize timed events in.</param> /// <param name="grid">Grid to quantize objects by.</param> /// <param name="tempoMap">Tempo map used to calculate times to quantize by.</param> /// <param name="settings">Settings according to which timed events should be quantized.</param> /// <exception cref="ArgumentNullException"> /// <para>One of the following errors occured:</para> /// <list type="bullet"> /// <item> /// <description><paramref name="trackChunk"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="grid"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="tempoMap"/> is <c>null</c>.</description> /// </item> /// </list> /// </exception> public static void QuantizeTimedEvents(this TrackChunk trackChunk, IGrid grid, TempoMap tempoMap, TimedEventsQuantizingSettings settings = null) { ThrowIfArgument.IsNull(nameof(trackChunk), trackChunk); ThrowIfArgument.IsNull(nameof(grid), grid); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); using (var timedEventsManager = trackChunk.ManageTimedEvents()) { new TimedEventsQuantizer().Quantize(timedEventsManager.Events, grid, tempoMap, settings); } }
static void Split(string filename) { // We open the file... var f = MidiFile.Read(filename); // We create channel handlers for each channel in the file... var channels = f.GetChannels().ToDictionary( x => x, x => new ChannelHandler(x)); // Then we parse the file and filter into these handlers var merged = new TrackChunk(); using (var mergedTimedEvents = merged.ManageTimedEvents()) { foreach (var chunk in f.GetTrackChunks()) { Console.WriteLine($"Adding {chunk.Events.Count} events from chunk..."); using var m = chunk.ManageTimedEvents(); mergedTimedEvents.Events.Add(m.Events); } mergedTimedEvents.SaveChanges(); Console.WriteLine($"Total events is now {mergedTimedEvents.Events.Count()}, length = {(TimeSpan)mergedTimedEvents.Events.Last().TimeAs<MetricTimeSpan>(f.GetTempoMap())}"); } var maxTime = merged.Events.Sum(x => x.DeltaTime); Console.WriteLine($"Total {merged.Events.Count} events, time {maxTime} = {(TimeSpan)TimeConverter.ConvertTo<MetricTimeSpan>(maxTime, f.GetTempoMap())}"); foreach (var e in merged.Events) { // Pass everything else through to all channels handlers foreach (var channelHandler in channels.Values) { channelHandler.Write(e); } } // Then pad them all to the same length var maxLength = channels.Values.Max(x => x.GetMaxLength()); foreach (var writer in channels.Values) { writer.WriteToDisk( maxLength, Path.Combine( Path.GetDirectoryName(filename) ?? "", Path.GetFileNameWithoutExtension(filename)), f.TimeDivision); } }
private static MidiFile CreateTestFile() { const int trackChunksNumber = 10; const int eventsPerTrackChunk = 10000; var midiFile = new MidiFile(); for (int i = 0; i < trackChunksNumber; i++) { var trackChunk = new TrackChunk(); using (var timedEventsManager = trackChunk.ManageTimedEvents()) { for (int j = 0; j < eventsPerTrackChunk; j++) { timedEventsManager.Events.Add(new TimedEvent(new SetTempoEvent(j + 1), j)); } } midiFile.Chunks.Add(trackChunk); } return(midiFile); }
private void btnSave_Click(object sender, EventArgs e) { if (Path.GetExtension(filePath) == ".mid") { var chunks = song.GetTrackChunks().ToArray(); var tempoMap = song.GetTempoMap(); var fixedMidi = new MidiFile(); var headerChunk = new TrackChunk(); int i = 0; var t0Notes = chunks[0].GetNotes().ToArray(); if (t0Notes.Length == 0) // check if there are notes in Track0 { i = 1; var timedEvents = chunks[0].GetTimedEvents().ToArray(); var title = timedEvents.Where(t => t.Event.EventType == MidiEventType.SequenceTrackName); if (title.Count() == 1) { headerChunk.AddTimedEvents(title); } } fixedMidi.Chunks.Add(headerChunk); fixedMidi.ReplaceTempoMap(tempoMap); for (int ch = 0; i < chunks.Count(); i++, ch++) { var newChunk = new TrackChunk(); var timedEvents = chunks[i].GetTimedEvents(); using (var timedEventsManager = newChunk.ManageTimedEvents()) { foreach (var timedEvent in timedEvents) { var eventType = timedEvent.Event.EventType; if (eventType == MidiEventType.SequenceTrackName) { timedEventsManager.Events.Add(timedEvent); } else if (eventType == MidiEventType.ProgramChange) { var pcEvent = timedEvent.Event as ProgramChangeEvent; pcEvent.Channel = (FourBitNumber)ch; timedEventsManager.Events.AddEvent(pcEvent, timedEvent.Time); } } } using (var notesManager = newChunk.ManageNotes()) { var notes = chunks[i].GetNotes().ToArray(); foreach (var n in notes) { var newNote = n; newNote.Channel = (FourBitNumber)ch; // change channel of the notes in track notesManager.Notes.Add(newNote); } } fixedMidi.Chunks.Add(newChunk); } string outputFilename = Path.GetFileNameWithoutExtension(filePath) + "_fix.mid"; WriteMidiToFile(fixedMidi, outputFilename); textBox1.AppendText("Wrote to file: " + outputFilename + Environment.NewLine); } else if (Path.GetExtension(filePath) == ".mml") { string outputFilename = Path.GetFileNameWithoutExtension(filePath) + "_fix.mml"; File.WriteAllText(outputFilename, sb.ToString()); textBox1.AppendText("Wrote to file: " + outputFilename + Environment.NewLine); } }
void _ParseAuthoring() { _authoring.Clear(); if (!_HasMidiFile()) { return; } TrackChunk authTrack = _FindAuthoringTracks(); if (authTrack == null) { return; } using (TimedEventsManager timedEventsManager = authTrack.ManageTimedEvents()) { // Get timed events ordered by time TimedEventsCollection events = timedEventsManager.Events; AuthoringEvent newEvent = null; int curNote = -1; foreach (var midiEvent in events) { if (!(midiEvent.Event is SequenceTrackNameEvent) && !(midiEvent.Event is BaseTextEvent) && (midiEvent.Event is NoteOnEvent)) //ignore text events! { newEvent = new AuthoringEvent(); curNote = (midiEvent.Event as NoteOnEvent).NoteNumber; newEvent.NoteIdx = curNote - BaseInstrumentNote; if (newEvent.NoteIdx < 0) //note was less than our BaseInstrumentNote, so ignore! { continue; } float startSecs = midiEvent.TimeAs <MetricTimeSpan>(_tempoMap).TotalMicroseconds / 1000000.0f; newEvent.NoteOnBeat = SecsToBeats(startSecs); _authoring.Add(newEvent); //now find a nearby note-off pair /*foreach (var pairedEvent in events) * { * if (_IsNearEvent(pairedEvent, midiEvent) && (pairedEvent.Event is NoteOffEvent) && (curNote >= 0) && (curNote == (pairedEvent.Event as NoteOffEvent).NoteNumber)) //found paired note off * { * float endSecs = pairedEvent.TimeAs<MetricTimeSpan>(_tempoMap).TotalMicroseconds / 1000000.0f; * newEvent.NoteOffBeat = SecsToBeats(endSecs); * * if (newEvent.IsValid()) //done? * { * _authoring.Add(newEvent); * * //Debug.Log("added event " + newEvent.NoteOnBeat + " -> " + newEvent.NoteOffBeat); * } * * break; * } * }*/ } } } Debug.Log("Found " + _authoring.Count + " authoring events!"); }