@@ -91,7 +91,7 @@ private SongPlayerFrame(final Song song) {
9191
9292 this .setTitle ("NoteBlockTool Song Player - " + song .getTitleOrFileNameOr ("No Title" ));
9393 this .setIconImage (new ImageIcon (this .getClass ().getResource ("/icon.png" )).getImage ());
94- this .setSize (500 , 400 );
94+ this .setSize (500 , 450 );
9595 this .setDefaultCloseOperation (JFrame .DO_NOTHING_ON_CLOSE );
9696 this .setLocationRelativeTo (null );
9797
@@ -106,45 +106,6 @@ private void initComponents() {
106106 root .setLayout (new BorderLayout ());
107107 this .setContentPane (root );
108108
109- { //North Panel
110- final JPanel northPanel = new JPanel ();
111- northPanel .setLayout (new GridBagLayout ());
112- root .add (northPanel , BorderLayout .NORTH );
113-
114- GBC .create (northPanel ).nextRow ().insets (5 , 5 , 5 , 5 ).anchor (GBC .LINE_START ).add (new JLabel ("Volume:" ));
115- GBC .create (northPanel ).nextColumn ().insets (5 , 0 , 5 , 5 ).weightx (1 ).width (2 ).fill (GBC .HORIZONTAL ).add (this .volumeSlider , () -> {
116- this .volumeSlider .setPaintLabels (true );
117- this .volumeSlider .setPaintTicks (true );
118- this .volumeSlider .setMajorTickSpacing (10 );
119- this .volumeSlider .setMinorTickSpacing (5 );
120- this .volumeSlider .addChangeListener (e -> {
121- if (this .songRenderer != null ) {
122- this .songRenderer .setMasterVolume (this .volumeSlider .getValue () / 100F );
123- }
124- lastVolume = this .volumeSlider .getValue ();
125- });
126- });
127-
128- GBC .create (northPanel ).nextRow ().skipColumn ().insets (5 , 0 , 0 , 5 ).anchor (GBC .LINE_START ).add (this .timingJitter , () -> {
129- this .timingJitter .setToolTipText ("Adds slight timing jitter (±1ms) to make the song sound more natural and less artificial.\n This emulates the behaviour of playing the song in Note Block Studio." );
130- this .timingJitter .addChangeListener (e -> {
131- if (this .songRenderer != null ) {
132- this .songRenderer .setTimingJitter (this .timingJitter .isSelected ());
133- }
134- lastTimingJitter = this .timingJitter .isSelected ();
135- });
136- });
137-
138- GBC .create (northPanel ).nextRow ().insets (5 , 5 , 0 , 5 ).anchor (GBC .LINE_START ).add (new JLabel ("Max Sounds:" ));
139- GBC .create (northPanel ).nextColumn ().insets (5 , 0 , 0 , 5 ).weightx (1 ).fill (GBC .HORIZONTAL ).add (this .maxSoundsSpinner , () -> {
140- this .maxSoundsSpinner .addChangeListener (e -> lastMaxSounds = (int ) this .maxSoundsSpinner .getValue ());
141- });
142- GBC .create (northPanel ).nextColumn ().insets (5 , 0 , 0 , 5 ).anchor (GBC .LINE_END ).add (this .threaded , () -> {
143- this .threaded .addChangeListener (e -> lastThreaded = this .threaded .isSelected ());
144- });
145-
146- GBC .create (northPanel ).nextRow ().insets (5 , 5 , 0 , 5 ).weightx (1 ).width (3 ).fill (GBC .HORIZONTAL ).add (new JSeparator ());
147- }
148109 { //Center Panel
149110 final JScrollPane centerScrollPane = new FastScrollPane ();
150111 final JPanel centerPanel = new ScrollPaneSizedPanel (centerScrollPane );
@@ -177,7 +138,7 @@ private void initComponents() {
177138 GBC .create (centerPanel ).nextColumn ().insets (5 , 0 , 0 , 5 ).weightx (1 ).fill (GBC .HORIZONTAL ).add (new NewLineLabel (DECIMAL_FORMAT .format (this .song .getNotes ().getNoteCount ())));
178139
179140 GBC .create (centerPanel ).nextRow ().insets (5 , 5 , 0 , 5 ).anchor (GBC .NORTHWEST ).add (new JLabel ("Tempo:" ));
180- GBC .create (centerPanel ).nextColumn ().insets (5 , 0 , 0 , 5 ).weightx (1 ).fill (GBC .HORIZONTAL ).add (new NewLineLabel (this .song .getTempoEvents ().getHumanReadableTempoRange () + " TPS" ));
141+ GBC .create (centerPanel ).nextColumn ().insets (5 , 0 , 5 , 5 ).weightx (1 ).fill (GBC .HORIZONTAL ).add (new NewLineLabel (this .song .getTempoEvents ().getHumanReadableTempoRange () + " TPS" ));
181142
182143 GBC .fillVerticalSpace (centerPanel );
183144 }
@@ -186,9 +147,11 @@ private void initComponents() {
186147 southPanel .setLayout (new GridBagLayout ());
187148 root .add (southPanel , BorderLayout .SOUTH );
188149
189- GBC .create (southPanel ).nextRow ().anchor (GBC .CENTER ).add ( this . progressLabel );
150+ GBC .create (southPanel ).nextRow ().anchor (GBC .CENTER ).weightx ( 1 ). width ( 2 ). fill ( GBC . HORIZONTAL ). add ( new JSeparator () );
190151
191- GBC .create (southPanel ).nextRow ().insets (5 , 5 , 0 , 5 ).weightx (1 ).fill (GBC .HORIZONTAL ).add (this .progressSlider , () -> {
152+ GBC .create (southPanel ).nextRow ().anchor (GBC .CENTER ).width (2 ).add (this .progressLabel );
153+
154+ GBC .create (southPanel ).nextRow ().insets (0 , 5 , 0 , 5 ).weightx (1 ).width (2 ).fill (GBC .HORIZONTAL ).add (this .progressSlider , () -> {
192155 this .progressSlider .addChangeListener (e -> {
193156 if (!this .progressSlider .getValueIsAdjusting ()) { // Skip updates if the value is set directly
194157 return ;
@@ -203,7 +166,7 @@ private void initComponents() {
203166 });
204167 });
205168
206- GBC .create (southPanel ).nextRow ().insets (5 , 5 , 5 , 5 ).weightx (1 ).width (2 ).fill (GBC .HORIZONTAL ).add (new JPanel (), buttonPanel -> {
169+ GBC .create (southPanel ).nextRow ().insets (5 , 5 , 0 , 5 ).weightx (1 ).width (2 ).fill (GBC .HORIZONTAL ).add (new JPanel (), buttonPanel -> {
207170 buttonPanel .setLayout (new GridLayout (1 , 3 , 5 , 0 ));
208171 buttonPanel .add (this .playStopButton );
209172 this .playStopButton .addActionListener (e -> {
@@ -243,11 +206,62 @@ private void initComponents() {
243206 });
244207 });
245208
246- final JPanel statusBar = new JPanel ();
247- statusBar .setBorder (BorderFactory .createEtchedBorder ());
248- statusBar .setLayout (new GridLayout (1 , 1 ));
249- statusBar .add (this .statusLine );
250- GBC .create (southPanel ).nextRow ().weightx (1 ).fill (GBC .HORIZONTAL ).add (statusBar );
209+ GBC .create (southPanel ).nextRow ().insets (5 , 5 , 5 , 5 ).weightx (1 ).width (2 ).fill (GBC .HORIZONTAL ).add (new JPanel (), controls -> {
210+ controls .setLayout (new GridLayout (1 , 2 , 5 , 5 ));
211+ {
212+ JPanel playbackPanel = new JPanel (new GridBagLayout ());
213+ playbackPanel .setBorder (BorderFactory .createTitledBorder ("Playback" ));
214+ controls .add (playbackPanel );
215+
216+ GBC .create (playbackPanel ).nextRow ().insets (0 , 5 , 0 , 5 ).anchor (GBC .LINE_START ).add (new JLabel ("Volume:" ));
217+ GBC .create (playbackPanel ).nextRow ().insets (0 , 5 , 0 , 5 ).weightx (1 ).fill (GBC .HORIZONTAL ).add (this .volumeSlider , () -> {
218+ this .volumeSlider .setPaintLabels (true );
219+ this .volumeSlider .setPaintTicks (true );
220+ this .volumeSlider .setMajorTickSpacing (20 );
221+ this .volumeSlider .setMinorTickSpacing (5 );
222+ this .volumeSlider .addChangeListener (e -> {
223+ if (this .songRenderer != null ) {
224+ this .songRenderer .setMasterVolume (this .volumeSlider .getValue () / 100F );
225+ }
226+ lastVolume = this .volumeSlider .getValue ();
227+ });
228+ });
229+
230+ GBC .create (playbackPanel ).nextRow ().insets (5 , 5 , 5 , 5 ).anchor (GBC .LINE_START ).add (this .timingJitter , () -> {
231+ this .timingJitter .setToolTipText ("Adds slight timing jitter (±1ms) to make the song sound more natural and less artificial.\n This emulates the behaviour of playing the song in Note Block Studio." );
232+ this .timingJitter .addChangeListener (e -> {
233+ if (this .songRenderer != null ) {
234+ this .songRenderer .setTimingJitter (this .timingJitter .isSelected ());
235+ }
236+ lastTimingJitter = this .timingJitter .isSelected ();
237+ });
238+ });
239+
240+ GBC .fillVerticalSpace (playbackPanel );
241+ }
242+ {
243+ JPanel rendererPanel = new JPanel (new GridBagLayout ());
244+ rendererPanel .setBorder (BorderFactory .createTitledBorder ("Renderer" ));
245+ controls .add (rendererPanel );
246+
247+ GBC .create (rendererPanel ).nextRow ().insets (0 , 5 , 0 , 5 ).anchor (GBC .LINE_START ).add (new JLabel ("Max Sounds:" ));
248+ GBC .create (rendererPanel ).nextRow ().insets (0 , 5 , 0 , 5 ).weightx (1 ).fill (GBC .HORIZONTAL ).add (this .maxSoundsSpinner , () -> {
249+ this .maxSoundsSpinner .addChangeListener (e -> lastMaxSounds = (int ) this .maxSoundsSpinner .getValue ());
250+ });
251+
252+ GBC .create (rendererPanel ).nextRow ().insets (5 , 5 , 5 , 5 ).anchor (GBC .LINE_START ).add (this .threaded , () -> {
253+ this .threaded .addChangeListener (e -> lastThreaded = this .threaded .isSelected ());
254+ });
255+
256+ GBC .fillVerticalSpace (rendererPanel );
257+ }
258+ });
259+
260+ GBC .create (southPanel ).nextRow ().weightx (1 ).width (2 ).fill (GBC .HORIZONTAL ).add (new JPanel (), statusBar -> {
261+ statusBar .setBorder (BorderFactory .createEtchedBorder ());
262+ statusBar .setLayout (new GridLayout (1 , 1 ));
263+ statusBar .add (this .statusLine );
264+ });
251265 }
252266 }
253267
0 commit comments