Browse Source

playback: fix notes ending before cursor position played on starting playback, add rules.PlayAlreadyStartedNotesFromCursorPosition

by setting osmd.EngravingRules.PlayAlreadyStartedNotesOnSettingPosition = false,
you can make it so that on starting playback from a cursor position,
only the notes starting from the cursor timestamp will be played.
If you leave it as true, the notes that started before the cursor position but haven't ended yet (e.g. half note)
will also be played.
false is now the default value.
sschmidTU 3 years ago
parent
commit
a744479a20

+ 3 - 0
src/MusicalScore/Graphical/EngravingRules.ts

@@ -326,6 +326,9 @@ export class EngravingRules {
 
     public static FixStafflineBoundingBox: boolean; // TODO temporary workaround
 
+    // Playback
+    public PlayAlreadyStartedNotesFromCursorPosition: boolean = false;
+
     constructor() {
         this.loadDefaultValues();
     }

+ 1 - 1
src/MusicalScore/Playback/PlaybackIterator.ts

@@ -14,7 +14,7 @@ export class PlaybackIterator {
     constructor(musicSheet: MusicSheet) {
         this.musicSheet = musicSheet;
         for (const v of musicSheet.PlaybackDataDict.keys()) {
-            const data: VoicePlaybackData = new VoicePlaybackData(v, musicSheet.PlaybackDataDict.getValue(v));
+            const data: VoicePlaybackData = new VoicePlaybackData(v, musicSheet.PlaybackDataDict.getValue(v), musicSheet.Rules);
             this.voiceCursors.setValue(v, data);
         }
         this.Reset();

+ 24 - 1
src/MusicalScore/Playback/VoicePlaybackData.ts

@@ -1,20 +1,24 @@
 import { Voice } from "../VoiceData";
 import { Fraction } from "../../Common/DataObjects";
 import { PlaybackEntry } from "./PlaybackEntry";
+import { EngravingRules } from "../Graphical/EngravingRules";
 
 export class VoicePlaybackData {
     private parentVoice: Voice;
     private readonly playbackEntries: { enrolledTimestamp: Fraction, playbackEntry: PlaybackEntry }[] = [];
     private readonly dueEntries: { enrolledTimestamp: Fraction, playbackEntry: PlaybackEntry }[] = [];
     private nextEntryTimestamp: Fraction;
+    private rules: EngravingRules;
 
     /**
      * Holds the list of all (enrolled) playback entries for a certain voice within a part in the musicsheet.
      * The list is sorted by the enrolled timestamp and a playback entry can be in the list more than once (due to repetitions)
      */
-    constructor (voice: Voice, playbackEntries: { enrolledTimestamp: Fraction, playbackEntry: PlaybackEntry}[]) {
+    constructor (voice: Voice, playbackEntries: { enrolledTimestamp: Fraction, playbackEntry: PlaybackEntry}[],
+        rules: EngravingRules) {
         this.parentVoice = voice;
         this.playbackEntries = playbackEntries;
+        this.rules = rules;
     }
 
     public get ParentVoice(): Voice {
@@ -88,6 +92,25 @@ export class VoicePlaybackData {
                 this.playbackIndex = i;
                 break;
             }
+            const maxEntryTimestamp: Fraction = entry.enrolledTimestamp.clone();
+            maxEntryTimestamp.Add(entry.playbackEntry.Notes[0].Length);
+            if (entry.enrolledTimestamp.RealValue < enrolledTimestamp.RealValue) {
+                console.log("entry: " + entry.enrolledTimestamp.RealValue);
+                console.log("enrolled: " + enrolledTimestamp.RealValue);
+                if (!this.rules.PlayAlreadyStartedNotesFromCursorPosition) {
+                    console.log("ratio: " + entry.enrolledTimestamp.RealValue / enrolledTimestamp.RealValue);
+                    if (entry.enrolledTimestamp.RealValue / enrolledTimestamp.RealValue < 0.99) {
+                        continue; // don't play notes that started before current cursor position
+                        // note that we'd ideally check entry < enrolled, but enrolled is imprecise,
+                        //   so e.g. if you start at a timestamp 5.5, enrolled will be ~5.5019
+                        //   so we have to add this tolerance interval for allowing a max timestamp,
+                        //   otherwise the current notes under the cursor wouldn't even be played.
+                    }
+                }
+                if (maxEntryTimestamp.lt(enrolledTimestamp)) {
+                    continue; // don't play notes that have already ended
+                }
+            }
             this.dueEntries.push(entry);
         }