diff --git a/BardMusicPlayer.Transmogrify/Song/BmpSong.cs b/BardMusicPlayer.Transmogrify/Song/BmpSong.cs index 96a36f2e..eea422eb 100644 --- a/BardMusicPlayer.Transmogrify/Song/BmpSong.cs +++ b/BardMusicPlayer.Transmogrify/Song/BmpSong.cs @@ -745,298 +745,5 @@ public MemoryStream GetExportMidi() return stream; } - - /// - /// Exports the song to a midi file - /// - /// - public MemoryStream GetExportMidiB() - { - try - { - List c = new List(); - foreach (var tc in TrackContainers.Values) - c.Add(tc.SourceTrackChunk); - - var midiFile = new MidiFile(c); - midiFile.ReplaceTempoMap(SourceTempoMap); - - - Console.WriteLine("Exporting... "); - var loaderWatch = Stopwatch.StartNew(); - var newTrackChunks = new ConcurrentDictionary(); - var tempoMap = midiFile.GetTempoMap().Clone(); - long firstNote = midiFile.GetTrackChunks().GetNotes().First().GetTimedNoteOnEvent().TimeAs(tempoMap).TotalMicroseconds / 1000; - - var originalTrackChunks = new List(); - - foreach (var trackChunk in midiFile.GetTrackChunks()) - { - var thisTrack = new TrackChunk(new SequenceTrackNameEvent(trackChunk.Events.OfType().FirstOrDefault()?.Text)); - thisTrack.AddObjects(trackChunk.GetNotes()); - thisTrack.AddObjects(trackChunk.GetTimedEvents()); - originalTrackChunks.Add(thisTrack); - } - - Parallel.ForEach(originalTrackChunks.Where(x => x.GetNotes().Any()), (originalChunk, loopState, index) => - { - var watch = Stopwatch.StartNew(); - var tempoMap = midiFile.GetTempoMap().Clone(); - int noteVelocity = int.Parse(index.ToString()) + 1; - - Dictionary> allNoteEvents = new Dictionary>(); - for (int i = 0; i < 128; i++) allNoteEvents.Add(i, new Dictionary()); - - foreach (Note note in originalChunk.GetNotes()) - { - long noteOnMS; - long noteOffMS; - - try - { - noteOnMS = 5000 + (note.GetTimedNoteOnEvent().TimeAs(tempoMap).TotalMicroseconds / 1000) - firstNote; - noteOffMS = 5000 + (note.GetTimedNoteOffEvent().TimeAs(tempoMap).TotalMicroseconds / 1000) - firstNote; - } - catch (Exception) { continue; } - int noteNumber = note.NoteNumber; - - Note newNote = new Note((SevenBitNumber)noteNumber, - time: noteOnMS, - length: noteOffMS - noteOnMS - ) - { - Channel = (FourBitNumber)index, - Velocity = (SevenBitNumber)126, - OffVelocity = (SevenBitNumber)126 - }; - - if (allNoteEvents[noteNumber].ContainsKey(noteOnMS)) - { - Note previousNote = allNoteEvents[noteNumber][noteOnMS]; - if (previousNote.Length < note.Length) allNoteEvents[noteNumber][noteOnMS] = newNote; - } - else allNoteEvents[noteNumber].Add(noteOnMS, newNote); - } - watch.Stop(); - Debug.WriteLine("step 1: " + noteVelocity + ": " + watch.ElapsedMilliseconds); - watch = Stopwatch.StartNew(); - - TrackChunk newChunk = new TrackChunk(); - for (int i = 0; i < 128; i++) - { - long lastNoteTimeStamp = -1; - foreach (var noteEvent in allNoteEvents[i]) - { - if (lastNoteTimeStamp >= 0 && allNoteEvents[i][lastNoteTimeStamp].Length + lastNoteTimeStamp >= noteEvent.Key) - allNoteEvents[i][lastNoteTimeStamp].Length = allNoteEvents[i][lastNoteTimeStamp].Length - (allNoteEvents[i][lastNoteTimeStamp].Length + lastNoteTimeStamp + 1 - noteEvent.Key); - - lastNoteTimeStamp = noteEvent.Key; - } - } - newChunk.AddObjects(allNoteEvents.SelectMany(s => s.Value).Select(s => s.Value).ToArray()); - allNoteEvents = null; - watch.Stop(); - Debug.WriteLine("step 2: " + noteVelocity + ": " + watch.ElapsedMilliseconds); - watch = Stopwatch.StartNew(); - - Note[] notesToFix = newChunk.GetNotes().Reverse().ToArray(); - for (int i = 1; i < notesToFix.Count(); i++) - { - int noteNum = notesToFix[i].NoteNumber; - long time = (notesToFix[i].GetTimedNoteOnEvent().Time); - long dur = notesToFix[i].Length; - int velocity = notesToFix[i].Velocity; - - long lowestParent = notesToFix[0].GetTimedNoteOnEvent().Time; - for (int k = i - 1; k >= 0; k--) - { - long lastOn = notesToFix[k].GetTimedNoteOnEvent().Time; - if (lastOn < lowestParent) lowestParent = lastOn; - } - if (lowestParent <= time + 50) - { - time = lowestParent - 50; - if (time < 0) continue; - notesToFix[i].Time = time; - dur = 25; - notesToFix[i].Length = dur; - } - } - - watch.Stop(); - Debug.WriteLine("step 3: " + noteVelocity + ": " + watch.ElapsedMilliseconds); - watch = Stopwatch.StartNew(); - - notesToFix = notesToFix.Reverse().ToArray(); - List fixedNotes = new List(); - for (int j = 0; j < notesToFix.Count(); j++) - { - var noteNum = notesToFix[j].NoteNumber; - var time = notesToFix[j].Time; - var dur = notesToFix[j].Length; - var channel = notesToFix[j].Channel; - var velocity = notesToFix[j].Velocity; - - if (j + 1 < notesToFix.Count()) - { - switch (notesToFix[j].Length) - { - // BACON MEOWCHESTRA - // Bandaid fix: If sustained note is 100ms or greater, ensure 60ms between the end of that note and the beginning of the next note. - // Otherwise, leave the behavior as it was before. - case >= 100 when notesToFix[j + 1].Time <= notesToFix[j].Time + notesToFix[j].Length + 60: - dur = notesToFix[j + 1].Time - notesToFix[j].Time - 60; - dur = dur < 60 ? 60 : dur; - break; - case < 100 when notesToFix[j + 1].Time <= notesToFix[j].Time + notesToFix[j].Length + 25: - dur = notesToFix[j + 1].Time - notesToFix[j].Time - 25; - dur = dur < 25 ? 25 : dur; - break; - } - } - fixedNotes.Add(new Note(noteNum, dur, time) - { - Channel = channel, - Velocity = velocity, - OffVelocity = velocity - }); - } - notesToFix = null; - - watch.Stop(); - Debug.WriteLine("step 4: " + noteVelocity + ": " + watch.ElapsedMilliseconds); - watch = Stopwatch.StartNew(); - - #region Tracknaming and octave shifting - int octaveShift = 0; - string trackName = originalChunk.Events.OfType().FirstOrDefault()?.Text; - - if (trackName == null) trackName = ""; - trackName = trackName.ToLower().Trim().Replace(" ", String.Empty); - string o_trackName = trackName; - Regex rex = new Regex(@"^([A-Za-z]+)([-+]\d)?"); - if (rex.Match(trackName) is Match match) - { - if (!string.IsNullOrEmpty(match.Groups[1].Value)) - { - trackName = Instrument.Parse(match.Groups[1].Value).Name; - if (!string.IsNullOrEmpty(match.Groups[2].Value)) - if (int.TryParse(match.Groups[2].Value, out int os)) - octaveShift = os; - - if (octaveShift > 0) - trackName = trackName + "+" + octaveShift; - else if (octaveShift < 0) - trackName = trackName + octaveShift; - } - - //last try with the program number - if ((string.IsNullOrEmpty(match.Groups[1].Value)) || trackName.Equals("Unknown") || trackName.Equals("None")) - { - ProgramChangeEvent prog = originalChunk.Events.OfType().FirstOrDefault(); - if (prog != null) - trackName = Instrument.ParseByProgramChange(prog.ProgramNumber).Name; - } - - } - newChunk = new TrackChunk(new SequenceTrackNameEvent(trackName)); - #endregion Tracknaming and octave shifting - - //Create Progchange Event - foreach (var timedEvent in originalChunk.GetTimedEvents()) - { - var programChangeEvent = timedEvent.Event as ProgramChangeEvent; - if (programChangeEvent == null) - continue; - //Skip all except guitar | implement if we need this again - if ((programChangeEvent.ProgramNumber < 27) || (programChangeEvent.ProgramNumber > 31)) - continue; - - var channel = programChangeEvent.Channel; - using (var manager = new TimedObjectsManager(newChunk.Events)) - { - TimedObjectsCollection timedEvents = manager.Objects; - timedEvents.Add(new TimedEvent(new ProgramChangeEvent(programChangeEvent.ProgramNumber), 5000 + (timedEvent.TimeAs(tempoMap).TotalMicroseconds / 1000) - firstNote/* Absolute time too */)); - } - } - newChunk.AddObjects(fixedNotes); - - watch.Stop(); - Debug.WriteLine("step 5: " + noteVelocity + ": " + watch.ElapsedMilliseconds); - watch = Stopwatch.StartNew(); - - newTrackChunks.TryAdd(noteVelocity, newChunk); - - watch.Stop(); - Debug.WriteLine("step 6: " + noteVelocity + ": " + watch.ElapsedMilliseconds); - - }); - - var newMidiFile = new MidiFile(); - newTrackChunks.TryRemove(newTrackChunks.Count, out TrackChunk trackZero); - newMidiFile.Chunks.Add(trackZero); - newMidiFile.TimeDivision = new TicksPerQuarterNoteTimeDivision(600); - using (TempoMapManager tempoManager = newMidiFile.ManageTempoMap()) tempoManager.SetTempo(0, Tempo.FromBeatsPerMinute(100)); - newMidiFile.Chunks.AddRange(newTrackChunks.Values); - - tempoMap = newMidiFile.GetTempoMap(); - long delta = newMidiFile.GetTrackChunks().GetNotes().First().GetTimedNoteOnEvent().TimeAs(tempoMap).TotalMicroseconds / 1000; - - Parallel.ForEach(newMidiFile.GetTrackChunks(), chunk => - { - var te = chunk.Events.OfType().FirstOrDefault()?.Text; - var channel = chunk.Events.OfType().FirstOrDefault()?.Channel; - using (var manager = chunk.ManageTimedEvents()) - { - var prog = new ProgramChangeEvent((SevenBitNumber)Instrument.Parse(te).MidiProgramChangeCode); - prog.Channel = (FourBitNumber)channel; - manager.Objects.Add(new TimedEvent(prog, 5000)); - } - - using (var notesManager = chunk.ManageNotes()) - { - foreach (Note note in notesManager.Objects) - { - long newStart = note.Time - delta; - note.Time = newStart; - } - } - using (var manager = chunk.ManageTimedEvents()) - { - foreach (TimedEvent _event in manager.Objects) - { - var programChangeEvent = _event.Event as ProgramChangeEvent; - if (programChangeEvent == null) - continue; - - long newStart = _event.Time - delta; - if (newStart <= -1) - manager.Objects.Remove(_event); - else - _event.Time = newStart; - } - } - }); - - var stream = new MemoryStream(); - - using (var manager = new TimedObjectsManager(newMidiFile.GetTrackChunks().First().Events)) - manager.Objects.Add(new TimedEvent(new MarkerEvent(), (newMidiFile.GetDuration().TotalMicroseconds / 1000))); - - newMidiFile.Write(stream, MidiFileFormat.MultiTrack, new WritingSettings { }); - stream.Flush(); - stream.Position = 0; - - loaderWatch.Stop(); - Console.WriteLine("Export finished MS: " + loaderWatch.ElapsedMilliseconds); - - return stream; - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - throw ex; - } - } } }