-
-
Notifications
You must be signed in to change notification settings - Fork 74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Iterate through noteOn's and get their lengths #8
Comments
Hi @f3flight , Thanks for using the library! The library has clear conception of "levels of abstraction". Smf namespace contains raw MIDI structures as they are presented in a MIDI file, without any processing. Smf.Interaction provides high level objects which are useful for real tasks. So it is correct that there are only low level objects inside Note On event hasn't length as well as all other events. You need to get corresponding Note Off event and take difference between times of both events as length of the note constructed from those events. You can take a look at CreateNotes private method of the So for every Probably I'll add some extension method like |
@f3flight Did you solve your problem? |
Nope not yet, didn't touch. Well I have a code which does the trick when using MidiSharp lib, but wanted to switch to something esle bcz that guy didnt care about my pull request. You can check the only pull request in his github repo. Not the cleanest way to do things but worked ok. |
First of all, it is wrong to add length to event. While it can be useful for some tasks it is conceptually wrong. MIDI event is just a point in time, event is momentary thing and has no length. From my practice when you start changing code to satisfy particulat task ignoring conceptions of subject domain, you'll encounter problems sooner or later with maintaining and changing of the codebase. I can suggest you a solution that will keep things clear. First of all define some class that will attach length to public sealed class LengthedEvent
{
public LengthedEvent(TimedEvent timedEvent)
{
TimedEvent = timedEvent;
}
public TimedEvent TimedEvent { get; }
public long? Length { get; set; }
} Now you can process public static IEnumerable<LengthedEvent> ProcessTimedEvents(IEnumerable<TimedEvent> timedEvents)
{
var result = new List<LengthedEvent>();
var noteOnTimedEvents = new List<TimedEvent>();
foreach (var timedEvent in timedEvents)
{
var midiEvent = timedEvent.Event;
if (midiEvent is NoteOnEvent)
{
noteOnTimedEvents.Add(timedEvent);
result.Add(new LengthedEvent(timedEvent));
continue;
}
var noteOffEvent = midiEvent as NoteOffEvent;
if (noteOffEvent != null)
{
var channel = noteOffEvent.Channel;
var noteNumber = noteOffEvent.NoteNumber;
var noteOnTimedEvent = noteOnTimedEvents.FirstOrDefault(e => IsAppropriateNoteOnTimedEvent(e, channel, noteNumber));
if (noteOnTimedEvent == null)
continue;
noteOnTimedEvents.Remove(noteOnTimedEvent);
var lengthedEvent = result.FirstOrDefault(e => e.TimedEvent == noteOnTimedEvent);
lengthedEvent.Length = timedEvent.Time - noteOnTimedEvent.Time;
continue;
}
result.Add(new LengthedEvent(timedEvent));
}
return result;
}
private static bool IsAppropriateNoteOnTimedEvent(TimedEvent timedEvent, FourBitNumber channel, SevenBitNumber noteNumber)
{
var noteOnEvent = timedEvent.Event as NoteOnEvent;
return noteOnEvent != null &&
noteOnEvent.Channel == channel &&
noteOnEvent.NoteNumber == noteNumber;
}
var midiFile = MidiFile.Read("Some MIDI file.mid");
var timedEvents = midiFile.GetTimedEvents();
var lengtehedEvents = ProcessTimedEvents(timedEvents); Also note that DryWetMIDI provides a way to convert raw TempoMap tempoMap = midiFile.GetTempoMap();
MetricTimeSpan metricLength = LengthConverter.ConvertTo<MetricTimeSpan>(lengthEvent.Length, lengthedEvent.TimedEvent.Time, tempoMap);
Is seems interesting to add some extension method like |
OK, I've added Usage of the method: IEnumerable<ITimedObject> timedObjects = midiFile.GetTimedEvents().ExtractNotes(); At now the method exist only in develop branch. Nearest release will have it. @f3flight Any news on your task? |
@melanchall thas good news, thank you! Unfortunately the folks Im trying to do my project with are lacking in enthusiasm, and i always suck at doing stuff solely by myself. If youre interested in some C# + Unity + VR programming for fun and self improvement - pm me (fb, vk, telegram - f3flight), i really need someone who will want to work together with me on this. |
I've tested this, looks like there's a bug.
Another concern I have is that the absolute time in seconds is a bit off from what I got when working with this file via my own calculating functions... |
@melanchall I checked with my older code, I get 410.3503 seconds which mostly matches your 6:50:356, just 6ms diff, maybe because I'm using float in my code. So there's no issue with time calc. |
@melanchall I'm pretty sure the issue is due to lack of handling of some of these situations:
basically in this particular file (and probably some others in the wild), there's a mess with noteOns, so I guess that's what breaks it. Looks like 2 is covered as the file pretty much has no noteoff events, but maybe 1 is still an issue. I did a bit of debugging and found that noteEventDescriptors starts growing after some moment, and keeps accumulating notes, up to ~5000 at the point when TimedEvent (noteOnEvent) start being yielded instead of "Note" - which happens around "Event at 155040" |
Thanks for investigation! I'm a little busy so cannot handle the issue quickly :( As for special cases:
The bug is critical so I'll look into it as soon as possible. Thank you again. |
@f3flight I've found the bug and fixed it. Please take latest version and check |
@melanchall thanks I'll test! I recommend to put link to issue inside commit message, then it will be automatically shown in the thread. I'll put this for reference - c8af797 |
Looks like it's fixed and works well! I'll close this issue. |
Hi @melanchall ! Great lib, trying to use it in my project - I need a visual midi "player" and found that "getTimedEvents" works quite well for this purpose, however one piece of puzzle is missing - I need to know note length for every noteOn - this seems to only be available for generated objects from smf.Interaction, but not from loaded with midifile - is that correct? If so I think we should try to add this functionality. Otherwise please tell me how to do this, maybe it's very easy but I just didn't figure out.
The text was updated successfully, but these errors were encountered: