Conversation
|
@rvilarl, I finished making the test suite. Would you take a look at suggest anything I'm missing please? Note I decided against using parameterized |
|
I’m finally going to have some time this weekend to surge on this. Just gathering some thoughts. I think playback.Sequence’s complexity is a sign that it’s doing too much. It assumes its consumers (i.e. playback.Cursor for now) will interpolate values forcing it to figure out that information up front. Instead, I want to generate a simpler data structure up front: playback.Timeline. It will contain a playback.TimelineEvent[] and have methods that make it easy to query by index or timestamp. Some types that come to mind: type ElementTransition = {
type: 'start' | 'stop';
element: PlaybackElement;
}
type TimelineEvent = {
type: 'element-transition';
time: Duration;
transitions: ElementTransition[];
} | {
// For repeats, codas, etc. This is used to inform interpolation to the measure end.
type: 'jump';
originVoiceEntryKey: VoiceEntryKey;
destinationVoiceEnteyKey: VoiceEntryKey;
} | {
// Also used to inform measure-end interpolation
type: 'system-end';
systemKey: SystemKey;
};Then, it’s the cursor’s responsibility to create a list of interpolation instructions from the timeline. The cursor will expose its own types in its eventing. I think the timeline should remain private to vexml and users should rely on playback.Cursor for timeline queries. Ultimately, I think this approach will make playback.Timeline and playback.Cursor much easier to develop and test because I think it’s scoped better. One of the risks is that I might be underestimating how difficult it would be for playback.Cursor to create interpolation instructions from a playback.Timeline — this is where the current complexity exists in 0.1.4. I’m also not fully settled how I want to communicate special transitions (such as retriggering repeated notes vs. tied notes). |
|
I finished the I ended up stringifying the events for assertions so it's easier for a human to discern if the event sequence is correct or not. It does add complexity, especially if the The remaining work is to create separate cursor types and logic to take a |
|
I'm currently experimenting with the following idea of interface CursorFrame {
readonly tRange: DurationRange;
readonly xRange: util.NumberRange;
readonly yRange: util.NumberRange;
readonly activeElements: PlaybackElement[];
getHints(previousFrame: CursorFrame): CursorFrameHint[];
};
export type CursorFrameHint = RetriggerHint | SustainHint;
export type RetriggerHint = {
type: 'retrigger';
untriggerElement: PlaybackElement;
retriggerElement: PlaybackElement;
};
export type SustainHint = {
type: 'sustain';
previousElement: PlaybackElement;
currentElement: PlaybackElement;
};@rvilarl, for your use case you'll need to check the |
|
I pretty much finished the new implementation and it's now backed by tests. When manually testing in the dev server, I notice that some documents with repeat endings have "dead zones". I suspect that there is some discontinuity in the timeline. Even though I do have coverage for repeats and repeats with endings, I'll add more test cases around this. |
|
A shortlist of deadzone documents:
|

This PR fixes #276. It will be included in ^0.1.5
Demos:
Screen.Recording.2025-04-05.at.6.37.30.PM.mov
Screen.Recording.2025-04-05.at.6.36.29.PM.mov
demo.mov
I split
Sequenceinto two independently tested classes calledTimelineandCursorFrame.Timelinelists moments of interest whileCursorFrameare specific interpolation instructions for aCursor. I also fixed a bug whereMeasureSequenceIteratorwould not skip endings correctly when there were more than 2.