-
-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rework Song::processNextBuffer #5723
Conversation
🤖 Hey, I'm @LmmsBot from github.com/lmms/bot and I made downloads for this pull request, click me to make them magically appear! 🎩
Linux
Windows
macOS🤖{"platform_name_to_artifacts": {"Linux": [{"artifact": {"title": {"title": "(AppImage)", "platform_name": "Linux"}, "link": {"link": "https://10559-15778896-gh.circle-artifacts.com/0/lmms-1.3.0-alpha.1.15%2Bg92425b3-linux-x86_64.AppImage"}}, "build_link": "https://circleci.com/gh/LMMS/lmms/10559?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link"}], "Windows": [{"artifact": {"title": {"title": "32-bit", "platform_name": "Windows"}, "link": {"link": "https://10555-15778896-gh.circle-artifacts.com/0/lmms-1.3.0-alpha.1.15%2Bg92425b309-mingw-win32.exe"}}, "build_link": "https://circleci.com/gh/LMMS/lmms/10555?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link"}, {"artifact": {"title": {"title": "64-bit", "platform_name": "Windows"}, "link": {"link": "https://10556-15778896-gh.circle-artifacts.com/0/lmms-1.3.0-alpha.1.15%2Bg92425b309-mingw-win64.exe"}}, "build_link": "https://circleci.com/gh/LMMS/lmms/10556?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link"}, {"artifact": {"title": {"title": "32-bit", "platform_name": "Windows"}, "link": {"link": "https://ci.appveyor.com/api/buildjobs/2fdfchvd2r28xxbq/artifacts/build/lmms-1.3.0-alpha-msvc2017-win32.exe"}}, "build_link": "https://ci.appveyor.com/project/Lukas-W/lmms/builds/36389263"}, {"artifact": {"title": {"title": "64-bit", "platform_name": "Windows"}, "link": {"link": "https://ci.appveyor.com/api/buildjobs/tu1jd6i67y4j3790/artifacts/build/lmms-1.3.0-alpha-msvc2017-win64.exe"}}, "build_link": "https://ci.appveyor.com/project/Lukas-W/lmms/builds/36389263"}], "macOS": [{"artifact": {"title": {"title": "", "platform_name": "macOS"}, "link": {"link": "https://10557-15778896-gh.circle-artifacts.com/0/lmms-1.3.0-alpha.1.15%2Bg92425b309-mac10.13.dmg"}}, "build_link": "https://circleci.com/gh/LMMS/lmms/10557?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link"}]}, "commit_sha": "e0f50e80d3c429cc1175ff22d22d1432fd41b7b1"} |
Original reporter of the issue here. Can confirm this pull request fixes the issue completely. No random note stops during the last bar in the piano roll! |
Tested on 1.2.2 and found the misbehaviour (though only happend with this VST, I tested with other VST and they worked well ¿?). Reproduced with this PR and it works fine. |
Tested with the organ plugin on 1.2.2, 32bit Windows (so no usage of a bit bridge.) Was only able to reproduce the last bar not playing within the piano roll. Was not able to reproduce >bar 8 not playing in the song editor or piano roll. Tested again using the 32bit MSVC build from this PR. The fix seems to have worked. Last bar in piano roll plays successfully with the organ plugin. Other plugins tested perform as expected on both 1.2.2 and this build. |
I'm hesitant to leave an approving review since I haven't thoroughly checked out the old code to see that everything except the fix is (functionally) identical, but I don't see any obviously suspicious changes either. If you want, I can probably give it a more thorough look some time. Quality wise the new code looks good, and much better than the old. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code LGTM!
I had to learn the logic of the method as I reviewed, and I've small doubts about some details (would you mind me asking about them in Discord for the sake of learning?), but as far as the behavior of the method goes, the only difference I noticed was the way the loop points are enforced and how the time elapsed on the processed buffer is counted (aka the bugfix itself).
Great job on making the method more organized and readable!
I like the variables renaming proposed by PhysSong: I don't think their meaning is too obscure right now, but his suggestion could improve their understanding even more! There's also that small change on the lambda function we talked about earlier, but since you already said you'll look into it I'm not leaving it as a requested change (it's also not critical, just something to keep in mind in terms of code maintenance if we at some point reach C++20).
Merge? |
Tested and works fine. Merge. On a related note, please see #3028. Lines 258 to 259 in e0f50e8
This doesn't work if the loop is activated when the song is playing and outside of the loop. The song will skip to the loop but miss the first events. Off by one thing... |
I think it's because the frame count isn't reset when looping. The loop code only sets the ticks, so if we're not in the first frame of a tick when we jump to the beginning of the loop, the first tick's events will be skipped. The other loop checking code only ever runs at the first frame of a tick, so isn't affected. |
const f_cnt_t framesUntilNextPeriod = framesPerPeriod - frameOffsetInPeriod; | ||
const f_cnt_t framesUntilNextTick = static_cast<f_cnt_t>(std::ceil(framesPerTick - frameOffsetInTick)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
frameOffsetInTick
might be invalid at this point if enforceLoop()
changed the play position.
Edit: It's not a regression can even be wrong, so you can ignore it at this moment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A situation where it matters is:
- Add a note pattern starting at bar 3
- Add a note starting at the beginning of that pattern
- Locate the loop begin point at bar 3, but don't enable it yet
- Start playing song at bar 1
- Now enable looping
- The play head jumps to bar 3, but the note is not played
As I mentioned above, it's not a regression, but an old bug. You can ignore this.
If it's considered as a bug, I can open a new bug report.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
frameOffsetInTick
is correct, in that it still corresponds to the ticks of the current PlayPos
. The ticks of the PlayPos
should be reset to zero/fmod'd with one though (can't decide which is correct). The issue is #3028. (Zonkmachine mentioned this here: #5723 (comment).)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see. Thanks for mentioning the relevant issue.
} | ||
|
||
if( ( f_cnt_t ) currentFrame == 0 ) | ||
if (static_cast<f_cnt_t>(frameOffsetInTick) == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the same reason, this check might go wrong. This can result in skipping some TCOs.
Edit: It's not a regression and can even be wrong, so you can ignore it at this moment.
|
||
if (checkLoop || m_loopRenderRemaining > 1) | ||
// Handle loop points, and inform VST plugins of the loop status | ||
if (loopEnabled || m_loopRenderRemaining > 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (loopEnabled || m_loopRenderRemaining > 1) | |
if (loopEnabled || (m_loopRenderRemaining > 1 && getPlayPos() >= timeline->loopBegin())) |
This will be explained in the comment below.
// beginning of the range | ||
if( m_playPos[m_playMode] >= tl->loopEnd() ) | ||
// Loop if necessary, and decrement the remaining loops if we did | ||
if (enforceLoop(timeline->loopBegin(), timeline->loopEnd()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This adds m_playPos[m_playMode] < timeline->loopBegin()
condition for jumping back which is undesired in case m_loopRenderRemaining > 1
, and make the area before the loop point be skipped or play wrongly. A relevant bug was reported on Discord.
Also, the startCycle()
call should be checked by m_playPos[m_playMode] >= timeline->loopBegin()
, though it was already wrong before this PR.
The proposed fix is in the comment above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix looks good to me and seems to work properly; PR open at #5814.
This was originally an attempt to fix a VST bug reported on Discord. The issue turned out to be in
Song::processNextBuffer
, which was fairly scary and convoluted, so I tried to clean it up a bit. Testing and review would be particularly appreciated, as this bit of code is rather integral to playback, and is rather complex.Original report, from
@Ambi#0808
on Discord:It turns out that the
kVstTransportChanged
was incorrectly set for the whole last bar in the song editor, due to faulty loop handling inSong::processNextBuffer
.The first error is here:
lmms/src/core/Song.cpp
Lines 288 to 292 in 0d89d64
MidiTime::getTicks()
returns the total number of ticks, not the number of ticks within the current bar, so this condition is true all the time after the first bar, not just at the beginning of a new bar.This leads to problems here:
lmms/src/core/Song.cpp
Lines 316 to 318 in 0d89d64
This is intended to run when
m_playPos[m_playMode]
is at the end of the previous bar, butticks
is at the beginning of the next, hence the+ 1
correction. However, due to the previous issue, the condition is checked every tick after the first bar, and is true throughout the last bar. This leads to erroneous calls tom_vstSyncController.setPlaybackJumped(true)
, resulting in the observed issue.