View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0001017 | OpenMPT | libopenmpt | public | 2017-08-26 21:27 | 2024-10-26 18:04 |
Reporter | Saga Musix | Assigned To | |||
Priority | normal | Severity | feature | Reproducibility | N/A |
Status | new | Resolution | open | ||
Target Version | OpenMPT 1.33 / libopenmpt 0.9 (goals) | ||||
Summary | 0001017: Tick boundary rendering (was: | ||||
Description | PoroCYon asked on IRC if it's possible to retrieve the next play position (row/pattern). We already have this information, but it is currently not exposed. | ||||
Additional Information | |||||
Tags | No tags attached. | ||||
Has the bug occurred in previous versions? | |||||
Tested code revision (in case you know it) | |||||
related to | 0001287 | acknowledged | manx | make libopenmpt samplerate non-variable |
related to | 0001766 | resolved | Saga Musix | Provide a way to retrieve numeric values of +++ and --- values via libopenmpt API |
related to | 0001791 | resolved | Saga Musix | Display timecode in milliseconds |
Uhm, what would be the use case here? I'd rather see this aspect to be factored into the already planned tick-boundary accurate playback. |
|
I can see use cases like OpenMPT's smooth pattern scrolling - depending on the next play position, you may have to scroll either up or down. |
|
Another use case is any general monitoring of pattern transitions. Let's say you want to do a transition to a different pattern once the current pattern has finished playing. Since the pattern may end on any row due to pattern break commands, it may not be known what is going to be the last row of the current pattern. If you want to solve this using tick-boundary rendering, you will have to throw away rendered audio for at least one tick, which I think would generally make control flow more complicated (and of course increase the required processing time). |
|
In that case, the required API would be more narrow than what is required to solve this issue. It would only be necessary to query the next play position after a tick has been rendered completely. And I would be very much OK with an API only implementing exactly that (once we have tick-boundary rendering). I am very hesitant to add an API like the one described in this issue as it (on the API level) requires predicting the future. After all, semantically, the next play position does not actually need to be known until after the current tick has been rendered completely. It may be possible to implement that unambiguously for the currently known and supported formats, but if some other format or tracker quirk shows up that makes the next play position depending on anything mixer-related (sample loops or whatever), this API would not be supportable without adding another buffering or prediction layer inside libopenmpt. I do not think it makes much sense to try to handle any of the use cases described here while not thinking about tick-boundary rendering at the same time. |
|
Related: It might make sense to provide two more functions,
|
|
To summarize the additional design suggestions so far:
|
|
We also should investigate whether splitting |
|
One challenge here is going to be that, from what I understand, information like |
|
This is precisely the reason why I think we need the explicit |
|
Note that the recently-added
|
|
Hi. I'm not very familiar with the standard way of doing these things, but might it make sense to have this setup? "stopReadAtOrderEnd" and "priorReadHitOrderEnd" flags If user sets the stopReadAtOrderEnd flag, each call to read() will fill the buffer as normal, unless it hits the end of an order, at which point it will fill the buffer only up to the end of that order. When this happens, read() will also set the "priorReadHitOrderEnd" flag (which is otherwise cleared). Subsequent calls to read() continue filling the buffer as normal until the next order-end event. To react to a change in order, the user could set the "stopReadAtOrderEnd" flag, then check the "priorReadHitOrderEnd" flag after each read(). When the "priorReadHitOrderEnd" flag becomes set, the user could react by running their order-change logic, then immediately call read() again to fill the rest of the buffer. The same could be done for rows with "stopReadAtRowEnd" and "priorReadHitRowEnd" or even ticks with "stopReadAtTickEnd" and "priorReadHitTickEnd". |
|
(Sorry for the mass revisions. I didn't realize they'd all show up in the history.) |
|
All of these are representable in a more general way by allowing to query the tick length and always reading the exact tick length amount. There are no additional flags or state required. |
|
I see. So can you get a tick length (in samples) when you starting playing the mod file, and then just keep using that same tick length throughout the play? |
|
No, you would query the tick length before/inbetween/after each tick. This is a detail that has yet to be determined. The tick length changes based on speed/tempo/shuffle. |
|
Ok. So, the user follows up each read with a query of how many ticks that read contained, keep a tally of the ticks read, use that to determine when they are approaching the point in the mod that they care about, then shift to reading tick-by-tick until they have hit their mark. Is that the idea? EDIT: Or, you say "This is a detail that has yet to be determined.", so is this one of a few possible ideas? |
|
Yes, that's the idea. Note that this is in no way different in principle from what you are suggesting, with the only difference that the position checks happen outside of libopenmpt instead of inside. This has the major advantage of covering any and all kinds of events any user might care about, as opposed to adding functions and state the libopenmpt for each and every one of them. |
|
The aspect that has yet to be determined, is how to handle tick transition. I suspect there may be legitimate reasons to inspect playback state right after a tick has been completely rendered, as well as right after a tick has been parsed and not yet rendered at all, or even in an in-between state (whatever that implies). examples:
|
|
I see one technical difference: To examine this performance penalty, I picked a simple song that has stable ticks, rows and orders and worked out the samples per tick:
I altered a test program from filling up the audio buffer in one mod::read() call, to filling it up in multiple mod::read() calls that were maxed at 918 samples per read. This represents the equivalent of reading tick-by-tick for this particular song. When I ran the song with the test program, it came out heavily distorted. Admittedly, the distortion went away after I bumped the max samples to (int)(918*1.2) = 1101 samples per read. However, my computer has a 4GHz quad-core processor, running at 17% cpu outside of the test program. The song has 9 channels and the test program is a console application dedicated exclusively to playing the mod (I removed the callback and all console output). I expect that this test is easier for libopenmpt than the average use case. I recognize that there may be better ways for a user to count ticks than what I am testing for. I'm also open to any critiques on this test. EDIT: I just tried the test again, with the same song, but with a single solo'd channel. I tried soloing each channel in turn. It didn't have an appreciable effect. |
|
And, yeah, I recognize that my test invalidates my earlier idea of flags... at least as far as ticks are concerned. It occurred to me that reading tick-by-tick might work better if it was only for a short while. I modified the program to fill the buffer normally unless I hit a particular key, then it would read tick-by-tick for 2 ticks before returning to filling the buffer again. I chose to use two ticks because I realized that my audio buffer was at 1024, which is less than 918*2. The result was more palatable. However, there was a quick "crunch" like a footstep in snow. Sometimes it was too quiet to hear, sometimes loud and distinct. |
|
Your measurements do not make much sense to me. libopenmpt already renders at max one single tick at a time. You cannot render any module any other way anyway. The only thing that would differ is returning to the caller instead of continuing inside libopenmpt. If the output was distorted for you, you probably did something wrong, or coupled the rendered amount to the sound output buffer length or to the screen update interval (which is a fair thing to do if you control the granularity, but makes no sense if the granularity is adopted to the module tick structure). |
|
It's possible. I was thinking that the read function call had some static time overhead that was being compounded with more frequent calls, though I haven't actually read the function to see if this is true. I'll double check my code. |
|
You were right. I had a bug while advancing the audio stream pointer as I was filling the stream piece by piece from libopenmpt. After I fixed that, things worked fine all the way down to adding 6 samples per read. It started glitching at 5 samples per read, but that is far lower than what is needed. Anyway, I am sorry for wasting your time with all that. It occurs to me that your design suggestion of using the "tick_auto_advance" flag actually seems somewhat similar to my earlier suggestion. The second flag in my suggestion wouldn't make sense for ticks anyway, since they are smaller than any reasonable read() and the second flag was to let you wait for the next read that contained the event. However, I'm confused about what you mean when you say that tick_auto_advance==false makes the system require "manual tock advancing". How does manual tock advancing occur? Does the read() only accept sample size of 1, or is the read disabled altogether and advance_tick() is the way advance? You describe your dilemma as "regardless of what the API looks like. It currently does too many things at once". It sounds like you're describing the solution as breaking up the steps of methods like read(). This ise a useful way to give more control to those that need it, but would it make sense to also leave the monolithic methods for those who don't need that granularity, or would be daunted by its complexity? The monolithic methods could just be a series of calls to other functions that the user would have access to directly if they wanted. In fact, the monolithic methods could provide a form of documentation for how to effectively manage advanced control. Also, it seems like "advance_tick()" should end at the earliest possible point of interest, then have new methods to advance to subsequent points of interest, like "render_tick()" and "parse_tick()". What should happen if "advance_tick()" is called after calls to subsequent stage methods like "render_tick()" have occurred? It could recognize which stage the process is in, complete the uncompleted stages, then advance to the end of the current tick (and, thus, the start of the next). Alternately, it could error out. But just having "advance_tick()" pause at the earliest possible point is a good start and provides the space to add more interaction points down the road. |
|
Date Modified | Username | Field | Change |
---|---|---|---|
2017-08-26 21:27 | Saga Musix | New Issue | |
2017-08-27 06:38 | manx | Note Added: 0003194 | |
2017-08-27 09:27 | manx | Target Version | => OpenMPT 1.29.01.00 / libopenmpt 0.5.0 (upgrade first) |
2017-08-27 14:34 | Saga Musix | Note Added: 0003195 | |
2017-08-27 18:07 | Saga Musix | Note Added: 0003196 | |
2017-08-27 18:27 | manx | Note Added: 0003197 | |
2018-02-13 15:39 | manx | Additional Information Updated | |
2018-02-14 19:48 | Saga Musix | Note Added: 0003421 | |
2018-02-14 20:04 | manx | Note Added: 0003422 | |
2018-02-14 20:08 | manx | Note Added: 0003423 | |
2018-02-14 20:08 | manx | Target Version | OpenMPT 1.29.01.00 / libopenmpt 0.5.0 (upgrade first) => libopenmpt 0.4 (goals) |
2018-02-14 20:13 | Saga Musix | Note Added: 0003424 | |
2018-02-14 20:23 | manx | Note Added: 0003425 | |
2018-02-14 20:25 | manx | Summary |
libopenmpt: Provide access to next play position => Tick boundary rendering (was: |
2018-08-18 12:49 | Saga Musix | Target Version | libopenmpt 0.4 (goals) => OpenMPT 1.29.01.00 / libopenmpt 0.5.0 (upgrade first) |
2020-01-05 10:44 | manx | Target Version | OpenMPT 1.29.01.00 / libopenmpt 0.5.0 (upgrade first) => OpenMPT 1.30.01.00 / libopenmpt 0.6.0 (upgrade first) |
2020-01-07 10:54 | manx | Relationship added | related to 0001287 |
2020-05-28 11:02 | manx | Note Added: 0004353 | |
2020-11-28 19:59 | manx | Target Version | OpenMPT 1.30.01.00 / libopenmpt 0.6.0 (upgrade first) => OpenMPT 1.31.01.00 / libopenmpt 0.7.0 (upgrade first) |
2021-08-09 18:48 | manx | Note Added: 0004836 | |
2021-12-04 22:50 | Saga Musix | Note Added: 0004939 | |
2023-01-10 09:55 | manx | Note Added: 0005459 | |
2023-01-10 19:50 | violgamba | Note Added: 0005460 | |
2023-01-10 19:54 | violgamba | Note Edited: 0005460 | |
2023-01-10 19:55 | violgamba | Note Edited: 0005460 | |
2023-01-10 19:55 | violgamba | Note Edited: 0005460 | |
2023-01-10 19:56 | violgamba | Note Edited: 0005460 | |
2023-01-10 19:58 | violgamba | Note Edited: 0005460 | |
2023-01-10 20:00 | violgamba | Note Edited: 0005460 | |
2023-01-10 20:01 | violgamba | Note Edited: 0005460 | |
2023-01-10 20:07 | violgamba | Note Edited: 0005460 | |
2023-01-10 20:08 | violgamba | Note Edited: 0005460 | |
2023-01-10 20:10 | violgamba | Note Edited: 0005460 | |
2023-01-10 20:10 | violgamba | Note Edited: 0005460 | |
2023-01-10 20:11 | violgamba | Note Edited: 0005460 | |
2023-01-10 20:16 | violgamba | Note Edited: 0005460 | |
2023-01-10 20:16 | violgamba | Note Edited: 0005460 | |
2023-01-10 20:25 | violgamba | Note Edited: 0005460 | |
2023-01-10 20:33 | violgamba | Note Edited: 0005460 | |
2023-01-10 20:46 | violgamba | Note Edited: 0005460 | |
2023-01-11 03:09 | violgamba | Note Edited: 0005460 | |
2023-01-11 03:10 | violgamba | Note Edited: 0005460 | |
2023-01-11 03:41 | violgamba | Note Edited: 0005460 | |
2023-01-12 22:43 | violgamba | Note Added: 0005472 | |
2023-01-13 08:27 | manx | Note Added: 0005473 | |
2023-01-13 09:24 | violgamba | Note Added: 0005474 | |
2023-01-13 09:30 | manx | Note Added: 0005475 | |
2023-01-13 09:52 | violgamba | Note Added: 0005476 | |
2023-01-13 09:55 | violgamba | Note Edited: 0005476 | |
2023-01-13 09:56 | manx | Note Added: 0005477 | |
2023-01-13 10:00 | violgamba | Note Edited: 0005476 | |
2023-01-13 10:30 | manx | Note Added: 0005478 | |
2023-01-13 11:51 | violgamba | Note Added: 0005479 | |
2023-01-13 11:54 | violgamba | Note Edited: 0005479 | |
2023-01-13 12:30 | violgamba | Note Edited: 0005479 | |
2023-01-13 12:50 | violgamba | Note Added: 0005480 | |
2023-01-13 12:54 | violgamba | Note Edited: 0005480 | |
2023-01-13 12:55 | violgamba | Note Edited: 0005480 | |
2023-01-13 13:58 | manx | Note Added: 0005481 | |
2023-01-13 14:09 | violgamba | Note Added: 0005482 | |
2023-01-13 17:41 | violgamba | Note Added: 0005483 | |
2023-01-13 17:44 | violgamba | Note Edited: 0005483 | |
2023-01-13 17:47 | violgamba | Note Edited: 0005483 | |
2023-04-10 08:25 | manx | Target Version | OpenMPT 1.31.01.00 / libopenmpt 0.7.0 (upgrade first) => OpenMPT 1.32 / libopenmpt 0.8 (goals) |
2024-03-30 13:16 | manx | Relationship added | related to 0001766 |
2024-06-15 13:50 | manx | Relationship added | related to 0001791 |
2024-10-26 18:04 | manx | Target Version | OpenMPT 1.32 / libopenmpt 0.8 (goals) => OpenMPT 1.33 / libopenmpt 0.9 (goals) |