mmontag / chip-player-js

Web-based music player for a variety of video game and chiptune music formats.
https://chiptune.app
GNU General Public License v3.0
324 stars 17 forks source link

How to get tempo information from MIDIPlayer ? #82

Closed arcln closed 2 years ago

arcln commented 3 years ago

Hey, I am using the midi player as a library.

I want to seek the song to a specific bar. I need to calculate the position in ms, which is tempo dependent (I am assuming there is no tempo change during the song). Sequencer.tempo and MIDIFilePlayer.speed seem to be always 1. Despite, the song is playing at the correct tempo.

How can I get such info?

mmontag commented 3 years ago

Hey, good question.

The Sequencer has no concept of bars/beats/beats per minute (tempo). We have to consider these properties specific to MIDI files. The Sequencer.tempo is poorly named, it should be speed (as in, playback speed scale factor). But the MIDIFilePlayer could be extended to seek to bars.

MIDIFilePlayer uses the MIDIFile npm module. It parses MIDI files and returns a list of MIDI events. The existing method MIDIFilePlayer.setPosition(ms) uses event.playTime to seek, which has tempo baked in and is not suitable for seeking to a specific bar.

Every event also has a delta property (MIDI ticks since previous event). You could add a method MIDIFilePlayer.seekToBarBeatTick(bar, beat, tick) that uses this event.delta value, which would be independent of tempo.

Bars and beats are the tricky part. In order to count beats, we can use midiFile.header.getTicksPerBeat(), I think? In order to count bars, we have to keep track of the time signature as we go along, by looking at EVENT_META_TIME_SIGNATURE events.

mmontag commented 3 years ago

Hey @arcln, I created a branch here https://github.com/mmontag/chip-player-js/tree/midi-bars-beats with seek-to-bar functionality (90b720f).

It's still a bit messy, but it works. If you are building outside of chip-player-js, at least this should show you how it's possible. If you only care about seeking, you can just focus on that seekToBarBeatTick method.

arcln commented 2 years ago

Hey, sorry for the delay. Your method completely fits my needs! Thanks for your work.

mmontag commented 2 years ago

Awesome, thanks for the note.