spotify / pedalboard

🎛 🔊 A Python library for audio.

Home Page:https://spotify.github.io/pedalboard

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Incorrect timestamps in midi events for external instrument plugins

DamRsn opened this issue · comments

Expected behaviour

Instrument plugins expect midi messages' timestamp to be relative to the start of the current processBlock buffer.

Actual behaviour

In external plugins (at least), the midi messages are timestamped relatively to the start of the full audio sequence. This results in note_on or note_off messages that are never taken into account by the plugin because they happen at indices way larger than the bufferSize.

With this code:

sample_rate = 44100
num_channels = 2

audio_out = my_plugin(
        [mido.Message("note_on", note=60, time=0.5)],
        sample_rate=sample_rate,
        duration=5,
        num_channels=num_channels
    )

the plugin processBlock will receive the note_on message at index 22050 (44100 * 0.5) and will thus never be played by the plugin (that has in my case a bufferSize of 2048).

Steps to reproduce the behaviour

Load an instrument plugin and send midi messages at time different than 0. You should observe that nothing happens because most of the time, the timestamp of the event sent to the plugin is larger than the bufferSize (notes not played (note_on) or not stopped (note_off)).

Fix

A simple fix I use is to replace

midiChunk.addEvents(midiInputBuffer, i, chunkSampleCount, 0);

by

midiChunk.addEvents(midiInputBuffer, i, chunkSampleCount, -i);

in ExternalPlugin::renderMIDIMessages(): the sampleDeltaToAdd is changed from 0 to -i so that each midi event is timestamped according to the start of the current buffer and not that of the whole sequence.

Hi @DamRsn!

Great find! I've added a quick test to confirm this issue, but unfortunately I can't replicate exactly what you're seeing. In my tests, notes are received by the plugins; they're just effectively quantized to the nearest bufferSize, which is still wrong and something that needs to be fixed. (I'm curious if there might be an implementation difference between the open-source plugin I'm using for test - specifically Magical8BitPlug2 - and the plugin you're testing with.)

Regardless, your fix looks good and I'll get that merged, add a test, and get a new deploy out. Thanks!

This has been fixed by #259 and released in v0.8.3, now live on PyPI.