Music: fix last measure issues#65703
Merged
Merged
Conversation
breville
approved these changes
May 7, 2025
breville
left a comment
Member
There was a problem hiding this comment.
Thanks for the detailed explanation. Seems like a good overall improvement!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Warning!!
The AP CSP Create Performance Task is in progress. The most critical dates are from April 3 - April 30, 2025. Please consider any risk introduced by this PR that could affect our students taking AP CSP. Code.org students taking AP CSP primarily use App Lab for their Create Task, however a small percent use Game Lab. Carefully consider whether your change has any risk of alterering, changing, or breaking anything in these two labs. Even small changes, such as a different button color, are considered significant during this time period. Reach out to the Student Learning team or Curriculum team for more details.
Description
This addresses a class of bugs related computing the "last measure" of a student's song, which is used to determine several things such as when to stop, how long to render the timeline, and when to show validation. There have been a few bugs with this in the past, most recently reported by @dancodedotorg (Slack thread) where an incorrect calculation of the last measure would cause playback to stop unexpected early. This ended up being a bigger refactor that aims to simplify the contract between the view (MusicView) and the code compilation/execution unit (MusicBlocklyWorkspace).
Current State
Currently, MusicView owns a
Sequencercomponent that it passes toMusicBlocklyWorkspaceto provide as global scope for compiled Blockly JS code. The blockly code calls this sequencer which assembles a list of playback events, from which is derived the last measure.MusicViewreads data off of the sequencer after callingMusicBlocklyWorkspaceto compile and execute code.The issue is that because MusicView manages the sequencer, it needs to have internal knowledge of how exactly the sequencer gets used by the underlying Blockly execution code; i.e. when it's reset, when it will have the latest events, etc.
For these specific bugs, the issue was that in
MusicBlocklyWorkspace'sexecuteCompiledCodefunction, we would execute each code hook one after another - first the "when run" code, and then any active triggers. This is specifically to address the case where a trigger is currently playing but the code inside of it has changed. Because we executed these all one after another, when MusicView finally read the last measure from the sequencer, it would return the last measure of the last executed code hook specifically, and not the last measure overall. So if the full song had events going for 30 measure, but the last trigger executed only ran for 1 measure starting at measure 9, we would mistakenly compute the last measure as measure 10, and end early. This is true even if the when_run code was shorter than the triggers; for example, if two triggers had been executed but one ran longer than the other, if the shorter one executed second, we would treat the last measure of the shorter trigger as the last measure of the song and end early.Changes
Rather than trying to fix each bug one by one, the main refactor here is to simplify the interface between MusicView and MusicBlocklyWorkspace into something like: "given these blocks/code, give me the computed events and last measure". In order to do this, we move the Sequencer out of MusicView, and into the MusicBlocklyWorkspace as an internal component. MusicBlocklyWorkspace manages resetting the sequencer, merging events, and computing the last measure if there are multiple back to back executions, and simply returns the computed playback events, ordered functions, and last measure whenever it executes code. MusicView doesn't need to manage executing code and trying to manage the sequencer/knowing when it should read data from it. As a result, the last measure we get back should always be the true last measure (because MusicBlocklyWorkspace handles returning the max value after multiple executions), and we get a simpler interface between these two core components overall.
Screen Recordings
BEFORE
MusicLastMeasureBug.mov
AFTER
MusicLabLastMeasureFix.mp4
Links
Testing story
Tested locally (see screen recordings)