public static void ExportMsb(Stream dataStream, string outPath) { var file = MsbFile.Read(dataStream); var midiFile = new MidiFile(); var tempoMap = TempoMap.Create(new TicksPerQuarterNoteTimeDivision(960), Tempo.FromBeatsPerMinute((int)file.BpmEntries[0].Bpm)); foreach (var score in file.ScoreEntries) { var track = new TrackChunk(); var scoreNotes = new List <Note>(); foreach (var bar in score.Bars) { scoreNotes.Add(new Note(new SevenBitNumber((byte)(bar.Note + 24)), bar.Length, bar.Offset)); } track.AddNotes(scoreNotes); midiFile.Chunks.Add(track); } midiFile.ReplaceTempoMap(tempoMap); midiFile.Write(outPath); }
/// <summary> /// Creates a track chunk with the specified notes. /// </summary> /// <param name="notes">Collection of notes to create a track chunk.</param> /// <returns><see cref="TrackChunk"/> containing the specified notes.</returns> /// <exception cref="ArgumentNullException"><paramref name="notes"/> is <c>null</c>.</exception> public static TrackChunk ToTrackChunk(this IEnumerable <Note> notes) { ThrowIfArgument.IsNull(nameof(notes), notes); var trackChunk = new TrackChunk(); trackChunk.AddNotes(notes); return(trackChunk); }
public static MemoryStream ScrubFile(string filePath) { MidiFile midiFile; IEnumerable <TrackChunk> originalTrackChunks; TempoMap tempoMap; MidiFile newMidiFile; ConcurrentDictionary <int, TrackChunk> newTrackChunks; try { string md5 = CalculateMD5(filePath); if (lastMD5.Equals(md5) && lastFile != null) { var oldfile = new MemoryStream(); lastFile.Write(oldfile, MidiFileFormat.MultiTrack, new WritingSettings { CompressionPolicy = CompressionPolicy.NoCompression }); oldfile.Flush(); oldfile.Position = 0; return(oldfile); } if (Path.GetExtension(filePath).ToLower().Equals(".mmsong")) { var mmSongStream = MMSong.Open(filePath).GetMidiFile(false, true); lastFile = MidiFile.Read(mmSongStream); lastMD5 = md5; mmSongStream.Position = 0; return(mmSongStream); } midiFile = MidiFile.Read(filePath, new ReadingSettings { ReaderSettings = new ReaderSettings { ReadFromMemory = true }, InvalidChunkSizePolicy = InvalidChunkSizePolicy.Ignore, InvalidMetaEventParameterValuePolicy = InvalidMetaEventParameterValuePolicy.SnapToLimits, InvalidChannelEventParameterValuePolicy = InvalidChannelEventParameterValuePolicy.SnapToLimits, InvalidSystemCommonEventParameterValuePolicy = InvalidSystemCommonEventParameterValuePolicy.SnapToLimits, MissedEndOfTrackPolicy = MissedEndOfTrackPolicy.Ignore, NotEnoughBytesPolicy = NotEnoughBytesPolicy.Ignore, UnexpectedTrackChunksCountPolicy = UnexpectedTrackChunksCountPolicy.Ignore, UnknownChannelEventPolicy = UnknownChannelEventPolicy.SkipStatusByteAndOneDataByte, UnknownChunkIdPolicy = UnknownChunkIdPolicy.ReadAsUnknownChunk }); #region Require if (midiFile == null) { throw new ArgumentNullException(); } else { try { if (midiFile.Chunks.Count < 1) { throw new NotSupportedException(); } MidiFileFormat fileFormat = midiFile.OriginalFormat; if (fileFormat == MidiFileFormat.MultiSequence) { throw new NotSupportedException(); } } catch (Exception exception) when(exception is UnknownFileFormatException || exception is InvalidOperationException) { throw exception; } } #endregion var trackZeroName = midiFile.GetTrackChunks().First().Events.OfType <SequenceTrackNameEvent>().FirstOrDefault()?.Text; if (!string.IsNullOrEmpty(trackZeroName) && (trackZeroName.ToLower().Contains("mogamp") || trackZeroName.ToLower().Contains("mognotate"))) { var notateConfig = NotateConfig.GenerateConfigFromMidiFile(filePath); var mmSongStream = notateConfig.Transmogrify().GetMidiFile(false, true); lastFile = MidiFile.Read(mmSongStream); lastMD5 = md5; mmSongStream.Position = 0; return(mmSongStream); } Console.WriteLine("Scrubbing " + filePath); var loaderWatch = Stopwatch.StartNew(); originalTrackChunks = midiFile.GetTrackChunks(); tempoMap = midiFile.GetTempoMap(); newTrackChunks = new ConcurrentDictionary <int, TrackChunk>(); long firstNote = originalTrackChunks.GetNotes().First().GetTimedNoteOnEvent().TimeAs <MetricTimeSpan>(tempoMap).TotalMicroseconds / 1000; TrackChunk allTracks = new TrackChunk(); allTracks.AddNotes(originalTrackChunks.GetNotes()); midiFile.Chunks.Add(allTracks); originalTrackChunks = midiFile.GetTrackChunks(); Parallel.ForEach(originalTrackChunks.Where(x => x.GetNotes().Count() > 0), (originalChunk, loopState, index) => { var watch = Stopwatch.StartNew(); int noteVelocity = int.Parse(index.ToString()) + 1; Dictionary <int, Dictionary <long, Note> > allNoteEvents = new Dictionary <int, Dictionary <long, Note> >(); for (int i = 0; i < 127; i++) { allNoteEvents.Add(i, new Dictionary <long, Note>()); } foreach (Note note in originalChunk.GetNotes()) { long noteOnMS = 0; long noteOffMS = 0; try { noteOnMS = 5000 + (note.GetTimedNoteOnEvent().TimeAs <MetricTimeSpan>(tempoMap).TotalMicroseconds / 1000) - firstNote; noteOffMS = 5000 + (note.GetTimedNoteOffEvent().TimeAs <MetricTimeSpan>(tempoMap).TotalMicroseconds / 1000) - firstNote; } catch (Exception) { continue; } int noteNumber = note.NoteNumber; Note newNote = new Note(noteNumber: (SevenBitNumber)noteNumber, time: noteOnMS, length: noteOffMS - noteOnMS ) { Channel = (FourBitNumber)0, Velocity = (SevenBitNumber)noteVelocity, OffVelocity = (SevenBitNumber)noteVelocity }; 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 < 127; 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.AddNotes(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 <Note> fixedNotes = new List <Note>(); 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()) { if (notesToFix[j + 1].Time <= notesToFix[j].Time + notesToFix[j].Length + 25) { dur = notesToFix[j + 1].Time - notesToFix[j].Time - 25; dur = dur < 25 ? 1 : dur; } } 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(); int octaveShift = 0; string trackName = originalChunk.Events.OfType <SequenceTrackNameEvent>().FirstOrDefault()?.Text; if (trackName == null) { trackName = ""; } trackName = trackName.ToLower().Trim().Replace(" ", String.Empty); Regex rex = new Regex(@"^([A-Za-z]+)([-+]\d)?"); if (rex.Match(trackName) is Match match) { if (!string.IsNullOrEmpty(match.Groups[1].Value)) { trackName = match.Groups[1].Value; if (!string.IsNullOrEmpty(match.Groups[2].Value)) { if (int.TryParse(match.Groups[2].Value, out int os)) { octaveShift = os; } } (bool success, string parsedTrackName) = TrackNameToEnumInstrumentName(trackName); if (success) { trackName = parsedTrackName; } else { (success, parsedTrackName) = TrackNameToStringInstrumentName(trackName); if (success) { trackName = parsedTrackName; } else { var originalInstrument = originalChunk.Events.OfType <ProgramChangeEvent>().FirstOrDefault()?.ProgramNumber; if (!(originalInstrument is null) && originalInstrument.Equals(typeof(SevenBitNumber))) { (success, parsedTrackName) = ProgramToStringInstrumentName((SevenBitNumber)originalInstrument); } if (success) { trackName = parsedTrackName; } } }
internal static MidiFile Load(string filePath) { try { MMSong mmSong = JsonExtensions.DeserializeFromFileCompressed <MMSong>(filePath); if (mmSong.schemaVersion < 1 && mmSong.schemaVersion > 2) { throw new FileFormatException("Error: This mmsong file format is not understood."); } // For now, just play the first available song. // if (mmSong.songs.Count != 1) throw new FileFormatException("Error: BMP currently only supports mmsong files with 1 song in them."); MMSong.Song song = mmSong.songs[0]; MidiFile sequence = new MidiFile(); sequence.Chunks.Add(new TrackChunk()); sequence.TimeDivision = new TicksPerQuarterNoteTimeDivision(600); using (TempoMapManager tempoMapManager = sequence.ManageTempoMap()) tempoMapManager.SetTempo(0, Tempo.FromBeatsPerMinute(100)); foreach (MMSong.Bard bard in song.bards) { List <Note> notes = new List <Note>(); bool failure = false; switch (bard.instrument) { case Instrument.Cymbal: case Instrument.Trumpet: case Instrument.Trombone: case Instrument.Horn: case Instrument.Tuba: case Instrument.Saxophone: case Instrument.Violin: case Instrument.Viola: case Instrument.Cello: case Instrument.DoubleBass: bard.sequence = bard.sequence.ToDictionary( x => x.Key + 2, x => x.Value); break; default: break; } if (bard.sequence.Count % 2 == 0) { long lastTime = 0; int lastNote = 254; foreach (KeyValuePair <long, int> sEvent in bard.sequence) { if (!failure) { if (lastNote == 254) { if (sEvent.Value <= 60 && sEvent.Value >= 24 && ((sEvent.Key * 25 % 100) == 50 || (sEvent.Key * 25) % 100 == 0)) { lastNote = sEvent.Value + 24; lastTime = sEvent.Key * 25; } else { failure = true; } } else { if (sEvent.Value == 254) { long dur = (sEvent.Key * 25) - lastTime; notes.Add(new Note((SevenBitNumber)lastNote, dur, lastTime) { Channel = (FourBitNumber)14, Velocity = (SevenBitNumber)(int)127, OffVelocity = (SevenBitNumber)(int)0 }); lastNote = 254; lastTime = sEvent.Key * 25; } else { failure = true; } } } } } else { failure = true; } if (failure) { throw new FileFormatException("Error: This mmsong file is corrupted"); } TrackChunk currentChunk = new TrackChunk(new SequenceTrackNameEvent(bard.instrument.ToString())); currentChunk.AddNotes(notes); notes = null; sequence.Chunks.Add(currentChunk); currentChunk = null; } using (var manager = new TimedEventsManager(sequence.GetTrackChunks().First().Events)) manager.Events.Add(new TimedEvent(new MarkerEvent(), (sequence.GetDuration <MetricTimeSpan>().TotalMicroseconds / 1000) + 100)); return(sequence); } catch (Exception ex) { throw ex; } }
public static byte[] CreateMsbFromMidi(string path) { var midiFile = MidiFile.Read(path); //Ensures every chunk in the midi has at least 1 note foreach (var chunk in midiFile.GetTrackChunks()) { if (chunk.GetNotes().Count() == 0) { List <Note> dummyTrack = new List <Note>(); dummyTrack.Add(new Note(new SevenBitNumber((byte)60), 1000, 1)); chunk.AddNotes(dummyTrack); } } //Ensures there are at least 7 chunks in the midi while (midiFile.Chunks.Count < 7) { TrackChunk chunk = new TrackChunk(); List <Note> dummyTrack = new List <Note>(); dummyTrack.Add(new Note(new SevenBitNumber((byte)60), 1000, 1)); chunk.AddNotes(dummyTrack); midiFile.Chunks.Add(chunk); } //Ensures there are no more than 7 chunks in the midi while (midiFile.GetTrackChunks().Count() > 7) { midiFile.Chunks.RemoveAt(7); } //Reads BPM from midi if possible; if no BPM is declared, defaults to 100 ValueChange <Tempo> tempo = midiFile.GetTempoMap().Tempo.FirstOrDefault <ValueChange <Tempo> >(); float bpm = 100; if (tempo != null) { bpm = (float)tempo.Value.BeatsPerMinute; } //Assigns time signature based on first midi time signature event, or defaults to 4/4 ValueChange <TimeSignature> ts = midiFile.GetTempoMap().TimeSignature.FirstOrDefault <ValueChange <TimeSignature> >(); int numerator = 4; int denominator = 4; if (ts != null) { numerator = ts.Value.Numerator; denominator = ts.Value.Denominator; } var file = new MsbFile { BpmEntries = new List <MsbFile.MsbBpm> { new MsbFile.MsbBpm { Bpm = bpm, Measure1 = (byte)numerator, Measure2 = (byte)denominator } }, ScoreEntries = new List <MsbFile.MsbScore>() }; var trackChunks = midiFile.GetTrackChunks(); //If the first chunk contains only one note (dummy chunk or empty chunk) //Instead, re-order list to sort the list with the most notes into the first slot. if (trackChunks.ElementAt(0).GetNotes().Count() == 1) { trackChunks = trackChunks.OrderByDescending(c => c.GetNotes().Count()); } for (var trackIndex = 0; trackIndex < trackChunks.Count(); trackIndex++) { var notes = trackChunks.ElementAt(trackIndex).GetNotes(); notes = notes.OrderBy(c => c.Time); var msbScore = new MsbFile.MsbScore { Bars = new List <MsbFile.MsbBar>() }; for (var i = 0; i < notes.Count(); i++) { var note = notes.ElementAt(i); if ((int)note.NoteNumber < 48 || (int)note.NoteNumber > 84) { throw new Exception("Your Midi contains notes outside the range of FFXIV's playable range. " + $"Please enter only notes between C3 and C6 (inclusive). Detected incorrect range at Note#{i + 1} in Track#{trackIndex + 1}"); } //Detects note overlaps and chops the length to be strictly < the next note's time. var lengthChopOffset = 0L; if (i != notes.Count() - 1) { var nextNote = notes.ElementAt(i + 1); if (note.Time + note.Length > nextNote.Time) { lengthChopOffset = (note.Time + note.Length) - nextNote.Time; } } var msbBar = new MsbFile.MsbBar(); msbBar.Note = (byte)(note.NoteNumber - 24); msbBar.Length = (uint)(note.Length - lengthChopOffset); msbBar.Offset = (uint)note.Time; msbScore.Bars.Add(msbBar); } file.ScoreEntries.Add(msbScore); } return(file.GetBytes()); }
public static Sequence ScrubFile(string filePath) { MidiFile midiFile; IEnumerable <TrackChunk> originalTrackChunks; TempoMap tempoMap; MidiFile newMidiFile; ConcurrentDictionary <int, TrackChunk> newTrackChunks; Sequence sequence = null; try { midiFile = MidiFile.Read(filePath, new ReadingSettings { ReaderSettings = new ReaderSettings { ReadFromMemory = true } }); bool explode = false; #region Require if (midiFile == null) { throw new ArgumentNullException(); } else { try { if (midiFile.Chunks.Count < 1) { throw new NotSupportedException(); } MidiFileFormat fileFormat = midiFile.OriginalFormat; if (fileFormat == MidiFileFormat.MultiSequence) { throw new NotSupportedException(); } else if (fileFormat == MidiFileFormat.SingleTrack) { explode = true; } } catch (Exception exception) when(exception is UnknownFileFormatException || exception is InvalidOperationException) { throw exception; } } #endregion Console.WriteLine("Scrubbing " + filePath); var loaderWatch = Stopwatch.StartNew(); if (explode || midiFile.Chunks.Count == 1) { originalTrackChunks = midiFile.GetTrackChunks().First().Explode(); } else { originalTrackChunks = midiFile.GetTrackChunks(); } tempoMap = midiFile.GetTempoMap(); newTrackChunks = new ConcurrentDictionary <int, TrackChunk>(); long firstNote = originalTrackChunks.GetNotes().First().GetTimedNoteOnEvent().TimeAs <MetricTimeSpan>(tempoMap).TotalMicroseconds / 1000; Parallel.ForEach(originalTrackChunks.Where(x => x.GetNotes().Count() > 0), (originalChunk, loopState, index) => { var watch = Stopwatch.StartNew(); int noteVelocity = int.Parse(index.ToString()) + 1; Dictionary <int, Dictionary <long, Note> > allNoteEvents = new Dictionary <int, Dictionary <long, Note> >(); for (int i = 0; i < 127; i++) { allNoteEvents.Add(i, new Dictionary <long, Note>()); } // Fill the track dictionary and remove duplicate notes foreach (Note note in originalChunk.GetNotes()) { long noteOnMS = 0; long noteOffMS = 0; try { noteOnMS = note.GetTimedNoteOnEvent().TimeAs <MetricTimeSpan>(tempoMap).TotalMicroseconds / 1000 - firstNote; noteOffMS = note.GetTimedNoteOffEvent().TimeAs <MetricTimeSpan>(tempoMap).TotalMicroseconds / 1000 - firstNote; } catch (Exception) { continue; } // malformed note, most common is a note on missing a note off. int noteNumber = note.NoteNumber; Note newNote = new Note(noteNumber: (SevenBitNumber)noteNumber, time: noteOnMS, length: noteOffMS - noteOnMS ) { Channel = (FourBitNumber)0, Velocity = (SevenBitNumber)noteVelocity, OffVelocity = (SevenBitNumber)noteVelocity }; if (allNoteEvents[noteNumber].ContainsKey(noteOnMS)) { Note previousNote = allNoteEvents[noteNumber][noteOnMS]; if (previousNote.Length < note.Length) { allNoteEvents[noteNumber][noteOnMS] = newNote; // keep the longest of all duplicates } } else { allNoteEvents[noteNumber].Add(noteOnMS, newNote); } } watch.Stop(); Debug.WriteLine("step 1: " + noteVelocity + ": " + watch.ElapsedMilliseconds); watch = Stopwatch.StartNew(); // Merge all the dictionaries into one collection TrackChunk newChunk = new TrackChunk(); for (int i = 0; i < 127; 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.AddNotes(allNoteEvents.SelectMany(s => s.Value).Select(s => s.Value).ToArray()); allNoteEvents = null; watch.Stop(); Debug.WriteLine("step 2: " + noteVelocity + ": " + watch.ElapsedMilliseconds); watch = Stopwatch.StartNew(); // auto arpeggiate 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 <Note> fixedNotes = new List <Note>(); 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()) { if (notesToFix[j + 1].Time <= notesToFix[j].Time + notesToFix[j].Length + 25) { dur = notesToFix[j + 1].Time - notesToFix[j].Time - 25; dur = dur < 25 ? 1 : dur; } } 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(); // Discover the instrument name from the track title, and from program changes if that fails int octaveShift = 0; string trackName = originalChunk.Events.OfType <SequenceTrackNameEvent>().FirstOrDefault()?.Text; if (trackName == null) { trackName = ""; } trackName = trackName.ToLower().Trim().Replace(" ", String.Empty); Regex rex = new Regex(@"^([A-Za-z]+)([-+]\d)?"); if (rex.Match(trackName) is Match match) { if (!string.IsNullOrEmpty(match.Groups[1].Value)) { trackName = match.Groups[1].Value; if (!string.IsNullOrEmpty(match.Groups[2].Value)) { if (int.TryParse(match.Groups[2].Value, out int os)) { octaveShift = os; } } (bool success, string parsedTrackName) = TrackNameToEnumInstrumentName(trackName); if (success) { trackName = parsedTrackName; } else { (success, parsedTrackName) = TrackNameToStringInstrumentName(trackName); if (success) { trackName = parsedTrackName; } else { var originalInstrument = originalChunk.Events.OfType <ProgramChangeEvent>().FirstOrDefault()?.ProgramNumber; if (!(originalInstrument is null) && originalInstrument.Equals(typeof(SevenBitNumber))) { (success, parsedTrackName) = ProgramToStringInstrumentName((SevenBitNumber)originalInstrument); } if (success) { trackName = parsedTrackName; } } }