Parse a midi file!
I've been having trouble with this for a while now... I can't find any answers that lead me to much.
Is it possible for me to parse a midi file to something like these items
class {
int InstrumentNumber;
float TimeCalled;
float Length;
int Pitch; //Any Format is Okay for Pitch
}
and have an array for them?
Answer by melanchall · Aug 04, 2017 at 04:27 AM
If you write in C# you can use DryWetMIDI. It's open source .NET library to work with MIDI files. With the library your task can be solved with this code:
public sealed class NoteInfo
{
public int? ProgramNumber { get; set; }
public long Time { get; set; }
public long Length { get; set; }
public int NoteNumber { get; set; }
}
private static IEnumerable<NoteInfo> GetNotesInfo(string filePath)
{
var midiFile = MidiFile.Read(filePath);
// build the program changes map
var programChanges = new Dictionary<FourBitNumber, Dictionary<long, SevenBitNumber>>();
foreach (var timedEvent in midiFile.GetTimedEvents())
{
var programChangeEvent = timedEvent.Event as ProgramChangeEvent;
if (programChangeEvent == null)
continue;
var channel = programChangeEvent.Channel;
Dictionary<long, SevenBitNumber> changes;
if (!programChanges.TryGetValue(channel, out changes))
programChanges.Add(channel, changes = new Dictionary<long, SevenBitNumber>());
changes[timedEvent.Time] = programChangeEvent.ProgramNumber;
}
// collect notes info
return midiFile.GetNotes()
.Select(n => new NoteInfo
{
ProgramNumber = GetProgramNumber(n.Channel, n.Time, programChanges),
Time = n.Time,
Length = n.Length,
NoteNumber = n.NoteNumber
});
}
private static int? GetProgramNumber(FourBitNumber channel, long time, Dictionary<FourBitNumber, Dictionary<long, SevenBitNumber>> programChanges)
{
Dictionary<long, SevenBitNumber> changes;
if (!programChanges.TryGetValue(channel, out changes))
return null;
var times = changes.Keys.Where(t => t <= time).ToArray();
return times.Any()
? (int?)changes[times.Max()]
: null;
}
ProgramNumber
is defined as int?
since Program Change event can be missed in a file so in this case you should decide by yourself which instrument to use.
Time
and Length
defined as long because all timing in a MIDI file presented as integer numbers meaning of which defined by the time division of a file. In 99.9% files times and lengths expressed in ticks. If you want to get time as hours, minutes, seconds or fraction of the whole note's length you can use TimeAs
method on Note
class:
TempoMap tempoMap = midiFile.GetTempoMap();
MetricTimeSpan metricTime = n.TimeAs<MetricTimeSpan>(tempoMap);
MusicalTimeSpan musicalTime = n.TimeAs<MusicalTimeSpan>(tempoMap);
For Length
there is the LengthAs
method:
MetricTimeSpan metricLength = n.LengthAs<MetricTimeSpan>(tempoMap);
MusicalTimeSpan musicalLength = n.LengthAs<MusicalTimeSpan>(tempoMap);
DryWet$$anonymous$$IDI looks to be a higher than .NET 3.5 library. How did you manage to use it in Unity?
Hi Giovanni, it seems that the new Unity 2017.1 update is supporting .NET 4.6, so since DryWet$$anonymous$$idi is built on .NET 4.6 you technically can use the library now. I haven't tried it yet because i'm not quite sure how to integrate the library with Unity, but i'll try and see how it goes. If you try to update Unity and integrate DryWet$$anonymous$$idi to your project i'd love to hear how has it gone. Greetings :)
Thanks much, good to know! I dodged the issue using a web service for parsing and Unity-$$anonymous$$idi (https://github.com/n-yoda/unity-midi) for playing. It should work also for parsing, but I have not tried yet. :-)
Hello, how can i add this lib on Unity? Sorry for the dumb question.
Hi,
I suppose you need to take the code of the library and place to your project's folder (for example, Assets/Scripts). So you will build the library along with entire project build. Note that you need only files placed in DryWet$$anonymous$$idi folder (other folders contain files for developer of the library, not for end user).
Please take the code from master branch which contains stable release: DryWet$$anonymous$$IDI master.
Your answer
Follow this Question
Related Questions
How to check if username or email has been taken already? [Using Parse] 0 Answers
Error: Input string was not in the correct format - HELP! 0 Answers
Change color of button and convert string into int 1 Answer
How to parse SAMI file in unity? 0 Answers
Help int.Parse "Input String was not in the correct format" PROBLEM 0 Answers