1919
2020package heronarts .lx .snapshot ;
2121
22+ import java .util .ArrayDeque ;
2223import java .util .ArrayList ;
2324import java .util .Collections ;
2425import java .util .List ;
2526import java .util .Objects ;
27+ import java .util .Queue ;
2628
2729import com .google .gson .JsonArray ;
2830import com .google .gson .JsonElement ;
@@ -419,7 +421,7 @@ public LXGlobalSnapshot getCursorSnapshot() {
419421 return null ;
420422 }
421423
422- private List <LXSnapshot .View > recallViews = new ArrayList <>();
424+ private final List <LXSnapshot .View > recallViews = new ArrayList <>();
423425
424426 /**
425427 * Recall this snapshot, apply all of its values
@@ -431,20 +433,44 @@ public boolean recall(LXGlobalSnapshot snapshot) {
431433 return recall (snapshot , null );
432434 }
433435
436+ private boolean inRecall = false ;
437+
438+ private final Queue <LXGlobalSnapshot > snapshotQueue = new ArrayDeque <>();
439+
434440 /**
435441 * Recall this snapshot, and populate an array of commands which
436442 * would need to be undone by this operation.
437443 *
438444 * @param snapshot Snapshot to recall
439445 * @param commands Array to populate with all the commands processed
440- * @return True the snapshot was recalled, false if it was already mid-transition
446+ * @return True the snapshot was recalled, false if it was already mid-transition or another snapshot is mid-recall
441447 */
442448 public boolean recall (LXGlobalSnapshot snapshot , List <LXCommand > commands ) {
443449 if (this .inTransition == snapshot ) {
444450 finishTransition ();
445451 return false ;
446452 }
447453
454+ // Snapshot recall may end up triggering the recall of *another* snapshot due
455+ // to modulation mappings based upon snapshot parameters values. If this occurs,
456+ // queue up the re-entrant snapshot recalls such that are all processed in the
457+ // order in which they were triggered.
458+ if (this .inRecall ) {
459+ this .snapshotQueue .add (snapshot );
460+ return false ;
461+ }
462+
463+ this .inRecall = true ;
464+ _recall (snapshot , commands );
465+ while (!this .snapshotQueue .isEmpty ()) {
466+ _recall (this .snapshotQueue .remove (), null );
467+ }
468+ this .inRecall = false ;
469+ return true ;
470+ }
471+
472+ private void _recall (LXGlobalSnapshot snapshot , List <LXCommand > commands ) {
473+
448474 final boolean mixer = this .recallMixer .isOn ();
449475 final boolean pattern = this .recallPattern .isOn ();
450476 final boolean effect = this .recallEffect .isOn ();
@@ -458,7 +484,8 @@ public boolean recall(LXGlobalSnapshot snapshot, List<LXCommand> commands) {
458484 commands .add (new LXCommand .Parameter .SetValue (this .autoCycleCursor , this .autoCycleCursor .getValuei ()));
459485 }
460486 this .autoCycleCursor .setValue (snapshot .getIndex ());
461- this .recallViews = new ArrayList <>(snapshot .views );
487+ this .recallViews .clear ();
488+ this .recallViews .addAll (snapshot .views );
462489 if (this .transitionEnabled .isOn ()) {
463490 transition = true ;
464491 this .inTransition = snapshot ;
@@ -488,8 +515,6 @@ public boolean recall(LXGlobalSnapshot snapshot, List<LXCommand> commands) {
488515 if (transition ) {
489516 this .transition .trigger ();
490517 }
491-
492- return true ;
493518 }
494519
495520 private boolean isValidView (View view , boolean mixer , boolean pattern , boolean effect , boolean modulation , boolean output , boolean master ) {
0 commit comments