55package de .mossgrabers .bitwig .framework .daw ;
66
77import java .text .DecimalFormat ;
8+ import java .util .Map ;
9+ import java .util .TreeMap ;
810
11+ import com .bitwig .extension .controller .api .Arranger ;
912import com .bitwig .extension .controller .api .BeatTimeFormatter ;
1013import com .bitwig .extension .controller .api .ControllerHost ;
14+ import com .bitwig .extension .controller .api .SettableBeatTimeValue ;
1115import com .bitwig .extension .controller .api .SettableEnumValue ;
1216import com .bitwig .extension .controller .api .TimeSignatureValue ;
1317import com .bitwig .extension .controller .api .Transport ;
3539public class TransportImpl implements ITransport
3640{
3741 /** No pre-roll. */
38- private static final String PREROLL_NONE = "none" ;
42+ private static final String PREROLL_NONE = "none" ;
3943 /** 1 bar pre-roll. */
40- private static final String PREROLL_1_BAR = "one_bar" ;
44+ private static final String PREROLL_1_BAR = "one_bar" ;
4145 /** 2 bar pre-roll. */
42- private static final String PREROLL_2_BARS = "two_bars" ;
46+ private static final String PREROLL_2_BARS = "two_bars" ;
4347 /** 4 bar pre-Wroll. */
44- private static final String PREROLL_4_BARS = "four_bars" ;
48+ private static final String PREROLL_4_BARS = "four_bars" ;
4549
46- private static final String ACTION_JUMP_TO_END = "jump_to_end_of_arrangement" ;
50+ private static final String ACTION_JUMP_TO_END = "jump_to_end_of_arrangement" ;
4751
48- private static final AutomationMode [] AUTOMATION_MODES = new AutomationMode []
52+ private static final AutomationMode [] AUTOMATION_MODES = new AutomationMode []
4953 {
5054 AutomationMode .READ ,
5155 AutomationMode .LATCH ,
5256 AutomationMode .TOUCH ,
5357 AutomationMode .WRITE
5458 };
5559
56- private static final BeatTimeFormatter BEAT_POSITION_FORMATTER = formatAsBeats (1 );
57- private static final BeatTimeFormatter BEAT_LENGTH_FORMATTER = formatAsBeats (0 );
60+ private static final BeatTimeFormatter BEAT_POSITION_FORMATTER = formatAsBeats (1 );
61+ private static final BeatTimeFormatter BEAT_LENGTH_FORMATTER = formatAsBeats (0 );
5862
59- private final ControllerHost host ;
60- private final IApplication application ;
61- private final IValueChanger valueChanger ;
62- private final Transport transport ;
63+ private static final Map <Double , Double > ZOOM_RESOLUTIONS = new TreeMap <> ();
64+ static
65+ {
66+ ZOOM_RESOLUTIONS .put (Double .valueOf (8.8 ), Double .valueOf (1.0 / 1 ));
67+ ZOOM_RESOLUTIONS .put (Double .valueOf (27.94 ), Double .valueOf (1.0 / 4 ));
68+ ZOOM_RESOLUTIONS .put (Double .valueOf (279.11 ), Double .valueOf (1.0 / 16 ));
69+ ZOOM_RESOLUTIONS .put (Double .valueOf (661.61 ), Double .valueOf (1.0 / 32 ));
70+ ZOOM_RESOLUTIONS .put (Double .valueOf (1176.20 ), Double .valueOf (1.0 / 64 ));
71+ ZOOM_RESOLUTIONS .put (Double .valueOf (2091.03 ), Double .valueOf (1.0 / 128 ));
72+ ZOOM_RESOLUTIONS .put (Double .valueOf (4956.52 ), Double .valueOf (1.0 / 256 ));
73+ ZOOM_RESOLUTIONS .put (Double .valueOf (8811.59 ), Double .valueOf (1.0 / 512 ));
74+ ZOOM_RESOLUTIONS .put (Double .valueOf (20886.75 ), Double .valueOf (1.0 / 1024 ));
75+ ZOOM_RESOLUTIONS .put (Double .valueOf (37132.00 ), Double .valueOf (1.0 / 2048 ));
76+ ZOOM_RESOLUTIONS .put (Double .valueOf (66012.45 ), Double .valueOf (1.0 / 4096 ));
77+ ZOOM_RESOLUTIONS .put (Double .valueOf (156473.96 ), Double .valueOf (1.0 / 8192 ));
78+ ZOOM_RESOLUTIONS .put (Double .valueOf (278175.93 ), Double .valueOf (1.0 / 16384 ));
79+ ZOOM_RESOLUTIONS .put (Double .valueOf (600000 ), Double .valueOf (1.0 / 32768 ));
80+ ZOOM_RESOLUTIONS .put (Double .valueOf (800000 ), Double .valueOf (1.0 / 65536 ));
81+ }
6382
64- private final IParameter crossfadeParameter ;
65- private final IParameter metronomeVolumeParameter ;
66- private final IParameter automationModeParameter ;
83+ private final ControllerHost host ;
84+ private final IApplication application ;
85+ private final IValueChanger valueChanger ;
86+ private final Transport transport ;
87+
88+ private final IParameter crossfadeParameter ;
89+ private final IParameter metronomeVolumeParameter ;
90+ private final IParameter automationModeParameter ;
91+ private final Arranger bwArranger ;
6792
6893
6994 /**
7095 * Constructor
7196 *
7297 * @param host The host
7398 * @param application The application
99+ * @param bwArranger The Bitwig arranger
74100 * @param valueChanger The value changer
75101 */
76- public TransportImpl (final ControllerHost host , final IApplication application , final IValueChanger valueChanger )
102+ public TransportImpl (final ControllerHost host , final IApplication application , final Arranger bwArranger , final IValueChanger valueChanger )
77103 {
78104 this .host = host ;
79105 this .application = application ;
80106 this .valueChanger = valueChanger ;
81107 this .transport = host .createTransport ();
108+ this .bwArranger = bwArranger ;
82109
83110 this .transport .isPlaying ().markInterested ();
84111 this .transport .isArrangerRecordEnabled ().markInterested ();
@@ -103,6 +130,8 @@ public TransportImpl (final ControllerHost host, final IApplication application,
103130 this .transport .defaultLaunchQuantization ().markInterested ();
104131 this .transport .isFillModeActive ().markInterested ();
105132
133+ this .bwArranger .getHorizontalScrollbarModel ().getContentPerPixel ().markInterested ();
134+
106135 this .crossfadeParameter = new ParameterImpl (valueChanger , this .transport .crossfade ());
107136 this .metronomeVolumeParameter = new MetronomeVolumeParameterImpl (valueChanger , this .transport .metronomeVolume ());
108137 this .automationModeParameter = new AutomationModeParameter (valueChanger , this );
@@ -140,6 +169,7 @@ public void enableObservers (final boolean enable)
140169 Util .setIsSubscribed (this .transport .getClipLauncherPostRecordingTimeOffset (), enable );
141170 Util .setIsSubscribed (this .transport .defaultLaunchQuantization (), enable );
142171 Util .setIsSubscribed (this .transport .isFillModeActive (), enable );
172+ Util .setIsSubscribed (this .bwArranger .getHorizontalScrollbarModel ().getContentPerPixel (), enable );
143173
144174 this .crossfadeParameter .enableObservers (enable );
145175 this .metronomeVolumeParameter .enableObservers (enable );
@@ -522,27 +552,16 @@ public double getPosition ()
522552 @ Override
523553 public void changePosition (final boolean increase , final boolean slow )
524554 {
525- final double frac = getTimeFraction (slow );
526- final double position = this .transport .playStartPosition ().get ();
527- double newPos = Math .max (0 , position + (increase ? frac : -frac ));
528-
529- // Adjust to resolution
530- final double intPosition = Math .floor (newPos / frac );
531- newPos = intPosition * frac ;
532-
533- this .setPosition (newPos );
555+ final double resolution = this .getZoomResolution ();
556+ final double fraction = this .calcScrollFraction (resolution , slow );
557+ final double position = Math .round (this .transport .playStartPosition ().get () / fraction ) * fraction ;
558+ this .setPosition (increase ? position + fraction : Math .max (position - fraction , 0.0 ));
534559 }
535560
536561
537- /**
538- * Get the fraction to use for time changes.
539- *
540- * @param slow Slow change if true otherwise fast
541- * @return The fraction to change
542- */
543- private static double getTimeFraction (final boolean slow )
562+ private double calcScrollFraction (final double resolution , final boolean slow )
544563 {
545- return slow ? TransportConstants . INC_FRACTION_TIME_SLOW : TransportConstants . INC_FRACTION_TIME ;
564+ return slow ? resolution : resolution * this . getQuartersPerMeasure () * 4 ;
546565 }
547566
548567
@@ -558,8 +577,11 @@ public String getLoopStartBeatText ()
558577 @ Override
559578 public void changeLoopStart (final boolean increase , final boolean slow )
560579 {
561- final double frac = getTimeFraction (slow );
562- this .transport .arrangerLoopStart ().inc (increase ? frac : -frac );
580+ final double resolution = this .getZoomResolution ();
581+ final double fraction = this .calcScrollFraction (resolution , slow );
582+ final SettableBeatTimeValue arrangerLoopStart = this .transport .arrangerLoopStart ();
583+ final double position = Math .round (arrangerLoopStart .get () / fraction ) * fraction ;
584+ arrangerLoopStart .set (increase ? position + fraction : Math .max (position - fraction , 0.0 ));
563585 }
564586
565587
@@ -603,8 +625,11 @@ public String getLoopLengthBeatText ()
603625 @ Override
604626 public void changeLoopLength (final boolean increase , final boolean slow )
605627 {
606- final double frac = getTimeFraction (slow );
607- this .transport .arrangerLoopDuration ().inc (increase ? frac : -frac );
628+ final double resolution = this .getZoomResolution ();
629+ final double fraction = this .calcScrollFraction (resolution , slow );
630+ final SettableBeatTimeValue arrangerLoopDuration = this .transport .arrangerLoopDuration ();
631+ final double position = Math .round (arrangerLoopDuration .get () / fraction ) * fraction ;
632+ arrangerLoopDuration .set (increase ? position + fraction : Math .max (position - fraction , 0.0 ));
608633 }
609634
610635
@@ -924,4 +949,18 @@ private static BeatTimeFormatter formatAsBeats (final int offset)
924949 return StringUtils .formatMeasuresLong (quartersPerMeasure , beatTime , offset , true );
925950 };
926951 }
952+
953+
954+ private double getZoomResolution ()
955+ {
956+ final double contentPerPixel = this .bwArranger .getHorizontalScrollbarModel ().getContentPerPixel ().get ();
957+ final double inverseContentPerPixel = 1 / contentPerPixel ;
958+
959+ for (final Map .Entry <Double , Double > entry : ZOOM_RESOLUTIONS .entrySet ())
960+ {
961+ if (inverseContentPerPixel < entry .getKey ().doubleValue ())
962+ return entry .getValue ().doubleValue ();
963+ }
964+ return 800000.0 ;
965+ }
927966}
0 commit comments