Support for MIDI pitchbend

,

It seems that when loading a MIDI file via playdate.sound.sequence.new([path.mid]) in Lua pitchbend information is not retained, and playing the resulting sequence only plays back affected notes at their starting pitch. This will give an unexpected result with, for example, guitar tracks that incorporate string bends, vibrato, etc. Similarly, APIs that deal with MIDI notes such as playdate.sound.instrument:playMIDINote and, by extension, playdate.sound.track:getNotes don't offer bend functionality or information.

It would be useful to have support for pitchbend messages in playdate.sound.sequence as well as other APIs like playdate.sound.instrument:playMIDINote, which could perhaps automatically vary the note pitch over the lifetime of the note played. The game I'm working on in particular would benefit from this feature.

I can't be sure about what the difficulties could be in implementing this, but I thought that pitchbend messages could be managed similarly to the way this guide explains. I'm happy to hear other thoughts on the matter as well.

5 Likes

Thanks for that link! That'll be handy when I get around to adding pitch bend. I skipped it when I added .mid file parsing because I couldn't think of a good way to wire it to the synth implementation. Looking at it again I think I'll just add a separate pitch bend input.

I've filed that, hope to get to it soon!

4 Likes

Thanks Dave, that'd be great!

Hey @dave! Just curious if this feature was scheduled for any specific upcoming release, as it would help with planning the remaining work on my game and have a better idea of what I'll be able to use. Any good news would be appreciated!

1 Like

if pitch bends are added, the only other features id love to see added which will make the chiptune functions of this device absolutely phenomenal would be individual note velocity (which I think is absolutely critical for more dynamic synth music) and to a lesser extent note panning.

1 Like

Hey Dave, just touching base a year on to see if there was any progress on this one?

Sounds like a good project for the plane ride back home tomorrow. I'll see if I can get it done!

2 Likes

So I think I have this working correctly, but I've only tested it with a .mid file I cobbled together by hand. Does anyone have a better test file I can try out?

Also, one related change I made while in the code was I switched the default instrument bend range from a full octave to two semitones, as that seems like the more common default. Do y'all think that's appropriate? I can also add a check for the midi sysex command for setting the bend range but that info will be in the sequence and it's not immediately obvious how to get that over to the instrument.

The quickest example I can offer is a very short and simple test track I have available here, with bends of 0.5, 2 and 3 semitones, if it can help in a pinch. Despite originally requesting this feature I'm not an expert on the technical aspects behind a typical implementation, but my understanding is the range would have to be at least +/- 3 semitones for this test file to work as expected, is that correct? If so, what is the downside of leaving the default range at one octave, especially if it may be complicated to set a custom value?

1 Like

Midi controllers often allow 1 octave up and down

1 Like

With 2.6.0 and the test track I provided (or others I planned on using in Agents of Groove) I can hear the pitch bend being used when the MIDI file is played as a sequence, but the bends sound like they're going higher than they should and thus get out of tune.

Since I have no visibility into what's going on I tried to look for the pitch bend data as it was read in, but retrieving the note properties for a track with track:getNotes() doesn't seem to return anything new in the note table array. Is it somewhere else and am I missing it? I appreciate that we can pass in a pitch bend value to an instrument via instrument:setPitchBend (as opposed to playMIDINote() which I was originally hoping for), but where can we get the value then?

I took another look and figured out where the pitch bend range is stored in the mid file: it's done through a sequence of control change messages, first two are to controllers 100 and 101 to set the MSB and LSB of the "Registered Parameter" you want to change (in this case both 0, because pitch bend range is RP #0), then set controller #6 to the value you want. In your file this is 6, while Playdate's default is 12--meaning the pitch bends go twice as far as they should.

You can fix this by manually setting the bend range with instrument:setPitchBendRange(6). I'll file an issue to have it set the range from the file, if the commands are there. ..I was going to say I don't know when I'll get around to it, but it'll be quick, so I'll go ahead and do it now while I still understand how it works instead of coming back to it later and having to figure it all out again. :smiley:

1 Like

Ok thanks, so sounds like this will be handled internally, but for us to find the range we'd have to parse the MIDI files ourselves, in lieu of using the Sequence API, is that correct?

Hopefully whatever you used to create the mid file can tell you what pitch bend range it's using. 6 is an unusual choice--I'd be surprised if that's the default.

Hopefully whatever you used to create the mid file can tell you what pitch bend range it's using. 6 is an unusual choice--I'd be surprised if that's the default.

I put together that test track quickly in Guitar Pro 8, and, as far as I'm able to tell, that program does in fact default to a range of 6 semitones. I don't see any way to change it either. In fact, calling instrument:setPitchBendRange(6) fixes playback for not only this but also other tracks I created similarly, thanks for pointing that out.

It's not a big deal to add that call if we know what range to use, but it may be less obvious if we work with MIDI files we didn't create in the first place, hence my question. At the moment that's not my specific case, but in that scenario your suggestion to auto-set the range, or additional query functions in the API, would likely help.

1 Like

@dave not seeing/hearing any problems with 2.6.1, having removed the explicit calls to setPitchBendRange. Much obliged :man_dancing: :mirror_ball:

1 Like