Added a few features to libopenmpt_ext.interactive. Next steps?

Started by violgamba, January 10, 2023, 03:06:07

Previous topic - Next topic

violgamba

Saga Musix was recently kind enough to point out some great libopenmpt features to use it in an interactive way.  Since then I've added a few more to my local libopenmpt build.  I'm thinking that they'd be useful to others, so I'm interested in discussing the possibility of integrating them into libopenmpt.

Here are the features:

1) The ability to add callbacks for the event of a pattern ending.  The callback must be in this form:

void onPatternEnded(int oldPatternId, int newPatternId)

EDIT: Technically, the form uses mpt::uint16, but you get the point.

This callback feature is used by the next feature...



2) An extension to the function "set_position_order_row(int order, int row)".  I wanted to overload the function, but C doesn't let you do that, so the new function is:

set_position_order_row_at_event(int order, int row, int when)

The final parameter "when" can be one of three values:

- SET_POSITION_IMMEDIATELY - The position is set immediately.  This is identical to the original "set_position_order_row" functionality.
- SET_POSITION_AFTER_CURRENT_PATTERN - The position isn't set until JUST as the current pattern ends.  This allows for a seamless transition between patterns.
- <id of a pattern> - The position isn't set until JUST as the signified pattern ends.  This allows waiting for a multi-pattern phrase to end before setting the new position.

manx

Thanks for your suggestion.

We absolutely know that libopenmpt is currently lacking in the area where you added additional functionality, however the API you are suggesting is not a good fit for libopenmpt, because any callbacks make it extremely difficult for language bindings to interact with libopenmpt. I am strongly opposed to adding any callback functionality.

I also do not like the ability to cue actions into libopenmpt. It opens many unanswered questions like "What happens when a seek happens before the queued event occurs?" "Can the user queue multiple events?" "What happens if an earlier event conflicts with a later event in some form?".

In my opinion, the core functionality that is lacking in libopenmpt right now is the ability to know when a tick starts rendering and when it ends rendering. The current read() calls just ignore that, and happily cross multiple ticks without the user getting to know about it. We have been discussing a possible future API in this area in the tick boundary rendering issue.

Once you can render precisely to the end of a tick, any libopenmpt user can render tick-wise and seek to any other position if desired precisely when a tick ends (including when the last tick of a pattern ends).

Do you think your usecase would be solvable by the feature as suggested in the issue? Or do you see missing functionality with that approach? Please feel free to contribute to the issue. The more use cases we consider, hopefully the better the resulting feature will be.

violgamba

I'm mostly interested in easily shifting the song at musically intelligible points.  My work just happened to require reacting to pattern change events, so the callback was an easy add.  Also, while it'd be nice to offer this functionality to others, I have what I need and I am grateful for the opportunity to build on the excellent work already done with OpenMPT.

I can't speak to the callback messing with bindings, though I AM binding libopenmpt to GDScript in Godot at the moment.  I understand your concern about the added complexity of queuing.  I think my method is pretty benign: there's a single variable: no multi-queuing.  This should be all I need to pivot the music during scene changes.  I'm not very familiar with the causes of seeks beyond setting position.  The technique I use seems to work well so far: I check if the pattern has changed between the start and end of the "ProcessRow" function.  Actually, "set_position_order_row()" occasionally causes silence until it's called again (I should probably report that soon) while the version of set_position that waits for the pattern change has been rock solid.

As for the ability to know when each tick starts and ends rendering.  I agree, it could allow you to poll for the end of the of the current pattern as you approach it.  This could get tricky if there are a lot of effects related to speed, position, etc.  Without using events or queuing, it also seems like you'd need to make many granular reads near the end of the pattern to avoid overshooting?  Not sure of the performance implications there.  Also, waiting for the end of a multi-pattern phrase requires longer polling that includes checking the current pattern.

I don't know, it seems doable under normal circumstances at least.  I guess from my perspective the engine already knows precisely when the event I care about occurs.  It seems less effort and more reliable to have it tell me, rather than try and get technical and find and monitor all the variables.  You undoubtedly have good reasons for avoiding doing things this way, but it works well for me.  Between this and the channel volume and mute options, I'm very happy with what I can do.

EDIT: I might also be biased due to comparing a working solution with a hypothetical, solution who's parameters are a bit hazy to me.  I'm probably not your best target audience.

violgamba

After further thought, I've decided it's worth it to weigh in on the "Tick boundary rendering" after all.  I've posted there.