grimmdude / MidiWriterJS

♬ A JavaScript library which provides an API for programmatically generating and creating expressive multi-track MIDI files and JSON.
MIT License
547 stars 58 forks source link

Place EndTrackEvent at a particular tick #84

Closed chr15m closed 1 year ago

chr15m commented 2 years ago

Thank you for MidiWriterJS, it's fantastic! I'm creating short MIDI loops with it. At the moment if my last note is somewhere in the middle of a loop, the midi track is shorter than it should be. Here's an example of a loop that falls short of the end of the bar:

screenshot

To mitigate this I am placing a note with {duration: "T1", pitch: 0, velocity 0, startTick: (looplength - 1)} which seems to work:

screenshot

It feels like a bit of a hack. I wonder if there should be a way to tell MidiWriterJS that the track should end at a particular tick?

Thanks so much for this library.

lplume commented 2 years ago

i was thinking a little bit about this, wouldn't fit better in MidiWriterJS a way to autofill emtpy measures? what do you think about this? At the moment I really do not have ideas on how the api could look like, but seems a good idea to me.

It could be the default behaviour of NoteEvent rather than an optional parameter.

thanks

grimmdude commented 2 years ago

Hey @chr15m thanks for your message! I can see what you mean.

@lplume, yea auto-padding a track to the end of the measure would probably be a good default behavior. I wonder if it would also be useful to have aTrack.length property which could override it. Could potentially provide more granular adjustments down to the tick.

I'll take a look shortly and see what we can do.

lplume commented 2 years ago

feel free to ping me again about this or other things, i'd like to code again on midiwriterjs!

grimmdude commented 2 years ago

@lplume awesome, feel free to tackle this one if you like!

davay42 commented 2 years ago

@grimmdude @lplume Yeah, this feature is very needed! I'm using MidiWriter to create small midi loops and the same issue arises. Adding a silent note helps, but it's somewhat dirty, especially when imported into the DAW.

lplume commented 2 years ago

a little busy lately, still thinking about this and could be tricky to "automagically" detect when to pad the measure length if needed. Keeping in mind the time signature and how the note-event works. @davay42 since you notice this as well, how do you think midiwriter should behave? Could you give a few examples based on your use cases in the form of "I have to do this [code example]" and how do you think it should be [code example]? That would be very useful. Also @chr15m at the moment do you experience to "hacky" the filling of the track with a 0-pitch note only at the end of it? And of course @grimmdude any thoughts about this? I'm not sure if the feature request it's about filling the last measure only or something more

[edit] im just wondering how the "autofill" should work: should it consider to fill to track lenght only? should it fill every measures if needed? only the last one? Also, yes, the issue clearly mention end track event, am i just wondering a little bit too much ^^'? [/edit]

thanks

chr15m commented 2 years ago

@lplume yes padding with an empty note feels hacky. Ideally there would be a way to situate the end-track even directly at the time where the user wants it but I don't know if the MIDI format supports this. I note it is possible elsewhere in the API to situate events at specific time points though so it seems like it should be possible.

davay42 commented 2 years ago

@lplume I think just the ability to set the end-of-track event manually at a certain time would be enough for the purpose.

davay42 commented 2 years ago

@lplume Interestingly Garage band recognizes the loop length correctly with midiTrack.setTempo(tempo.bpm); midiTrack.setTimeSignature(4, 4) in place. Works even for empty loops. The calculations shouldn't be too fancy here.

The issue arises when I try to import a midi file to my web editor back. So we need to do the calculations ourselves.

grimmdude commented 1 year ago

I've made some changes in #107 to allow EndTrackEvent to be manually added with a specific delta value. That is, the number of ticks from the previous event.

track.addEvent(new MidiWriter.EndTrackEvent({delta: 128}));

Might still be easier/more readable to pad a track with a silent note @chr15m, but at least this is now an option.

chr15m commented 1 year ago

Awesome, thank you, will check it out.

davay42 commented 1 year ago

Awesome, thanks!