Skip to content

Commit 4d5befa

Browse files
committed
Restructured export window
1 parent 153423a commit 4d5befa

2 files changed

Lines changed: 169 additions & 168 deletions

File tree

src/main/java/net/raphimc/noteblocktool/frames/ExportFrame.java

Lines changed: 118 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import net.raphimc.noteblocktool.audio.renderer.SongRenderer;
3434
import net.raphimc.noteblocktool.audio.renderer.impl.ProgressSongRenderer;
3535
import net.raphimc.noteblocktool.audio.util.LameException;
36+
import net.raphimc.noteblocktool.elements.FastScrollPane;
3637
import net.raphimc.noteblocktool.elements.VerticalFileChooser;
3738
import net.raphimc.noteblocktool.util.filefilter.SingleFileFilter;
3839

@@ -58,27 +59,34 @@
5859

5960
public class ExportFrame extends JFrame {
6061

61-
private static final int MAX_SOUNDS = 16384;
62-
6362
private final ListFrame parent;
6463
private final List<ListFrame.LoadedSong> loadedSongs;
64+
private final JLabel formatLabel = new JLabel("Format:");
6565
private final JComboBox<OutputFormat> format = new JComboBox<>(OutputFormat.values());
66-
private final JCheckBox globalNormalization = new JCheckBox("Global Normalization");
67-
private final JCheckBox threaded = new JCheckBox("Multithreaded Rendering");
68-
private final JLabel sampleRateLabel = new JLabel("Sample Rate:");
66+
67+
// Audio File settings
68+
private final JPanel audioFilePanel = new JPanel(new GridBagLayout());
6969
private final JSpinner sampleRate = new JSpinner(new SpinnerNumberModel(48000, 8000, 192000, 8000));
70+
private final JComboBox<Channels> channels = new JComboBox<>(Channels.values());
7071
private final JLabel wavBitDepthLabel = new JLabel("WAV Bit Depth:");
7172
private final JComboBox<WavBitDepth> wavBitDepth = new JComboBox<>(WavBitDepth.values());
7273
private final JLabel mp3QualityLabel = new JLabel("MP3 Quality:");
7374
private final JSlider mp3Quality = new JSlider(0, 100, 60);
74-
private final JLabel channelsLabel = new JLabel("Channels:");
75-
private final JComboBox<Channels> channels = new JComboBox<>(Channels.values());
76-
private final JLabel volumeLabel = new JLabel("Volume:");
75+
76+
// Playback settings
77+
private final JPanel playbackPanel = new JPanel(new GridBagLayout());
7778
private final JSlider volume = new JSlider(0, 100, 50);
7879
private final JCheckBox timingJitter = new JCheckBox("Artificial Timing Jitter");
79-
private JPanel progressPanel;
80+
81+
// Renderer settings
82+
private final JPanel rendererPanel = new JPanel(new GridBagLayout());
83+
private final JSpinner maxSounds = new JSpinner(new SpinnerNumberModel(16384, 64, 131070, 64));
84+
private final JCheckBox globalNormalization = new JCheckBox("Global Normalization");
85+
private final JCheckBox threaded = new JCheckBox("Multithreaded Rendering");
86+
87+
private final JPanel progressPanel = new JPanel();
8088
private final JProgressBar progressBar = new JProgressBar();
81-
private final JButton exportButton = new JButton("Export");
89+
private final JButton export = new JButton("Export");
8290
private Thread exportThread;
8391

8492
public ExportFrame(final ListFrame parent, final List<ListFrame.LoadedSong> loadedSongs) {
@@ -92,7 +100,7 @@ public ExportFrame(final ListFrame parent, final List<ListFrame.LoadedSong> load
92100
this.setLocationRelativeTo(null);
93101

94102
this.initComponents();
95-
this.updateVisibility();
103+
this.updateVisibility(true);
96104
this.initFrameHandler();
97105

98106
this.setMinimumSize(this.getSize());
@@ -101,92 +109,109 @@ public ExportFrame(final ListFrame parent, final List<ListFrame.LoadedSong> load
101109

102110
private void initComponents() {
103111
JPanel root = new JPanel();
104-
root.setLayout(new GridBagLayout());
112+
root.setLayout(new BorderLayout());
105113
this.setContentPane(root);
106-
int gridy = 0;
107-
108-
GBC.create(root).grid(0, gridy).insets(5, 5, 0, 5).anchor(GBC.LINE_START).add(new JLabel("Format:"));
109-
GBC.create(root).grid(1, gridy++).insets(5, 0, 0, 5).weightx(1).fill(GBC.HORIZONTAL).add(this.format, () -> {
110-
this.format.addActionListener(e -> this.updateVisibility());
111-
});
112-
113-
GBC.create(root).grid(1, gridy++).insets(5, 0, 0, 5).anchor(GBC.LINE_START).add(this.globalNormalization);
114-
GBC.create(root).grid(1, gridy++).insets(5, 0, 0, 5).anchor(GBC.LINE_START).add(this.threaded);
115-
116-
GBC.create(root).grid(0, gridy).insets(5, 5, 0, 5).anchor(GBC.LINE_START).add(this.sampleRateLabel);
117-
GBC.create(root).grid(1, gridy++).insets(5, 0, 0, 5).weightx(1).fill(GBC.HORIZONTAL).add(this.sampleRate);
118114

119-
GBC.create(root).grid(0, gridy).insets(5, 5, 0, 5).anchor(GBC.LINE_START).add(this.wavBitDepthLabel);
120-
GBC.create(root).grid(1, gridy++).insets(5, 0, 0, 5).weightx(1).fill(GBC.HORIZONTAL).add(this.wavBitDepth, () -> {
121-
this.wavBitDepth.setSelectedItem(WavBitDepth.PCM16);
122-
});
115+
{ // North panel
116+
final JPanel northPanel = new JPanel(new GridBagLayout());
117+
root.add(northPanel, BorderLayout.NORTH);
118+
GBC.create(northPanel).nextRow().insets(5, 5, 5, 5).anchor(GBC.LINE_START).add(this.formatLabel);
119+
GBC.create(northPanel).nextColumn().insets(5, 5, 5, 5).weightx(1).fill(GBC.HORIZONTAL).add(this.format, format -> {
120+
format.addActionListener(e -> this.updateVisibility(true));
121+
});
122+
}
123123

124-
GBC.create(root).grid(0, gridy).insets(5, 5, 0, 5).anchor(GBC.LINE_START).add(this.mp3QualityLabel);
125-
GBC.create(root).grid(1, gridy++).insets(5, 0, 0, 5).weightx(1).fill(GBC.HORIZONTAL).add(this.mp3Quality, () -> {
126-
this.mp3Quality.setMajorTickSpacing(10);
127-
this.mp3Quality.setMinorTickSpacing(5);
128-
this.mp3Quality.setPaintTicks(true);
129-
this.mp3Quality.setPaintLabels(true);
130-
});
124+
{ // Center panel
125+
final JScrollPane centerScrollPane = new FastScrollPane();
126+
final JPanel centerPanel = new ScrollPaneSizedPanel(centerScrollPane);
127+
centerScrollPane.setViewportView(centerPanel);
128+
centerPanel.setLayout(new GridBagLayout());
129+
root.add(centerScrollPane, BorderLayout.CENTER);
130+
GBC.create(centerPanel).nextRow().insets(0, 5, 0, 5).width(2).weightx(1).fill(GBC.HORIZONTAL).add(this.audioFilePanel, audioFilePanel -> {
131+
audioFilePanel.setBorder(BorderFactory.createTitledBorder("Audio File"));
132+
GBC.create(audioFilePanel).nextRow().insets(0, 5, 0, 5).anchor(GBC.LINE_START).add(new JLabel("Sample Rate:"));
133+
GBC.create(audioFilePanel).nextColumn().insets(0, 0, 0, 5).weightx(1).fill(GBC.HORIZONTAL).add(this.sampleRate);
134+
GBC.create(audioFilePanel).nextRow().insets(5, 5, 0, 5).anchor(GBC.LINE_START).add(new JLabel("Channels:"));
135+
GBC.create(audioFilePanel).nextColumn().insets(5, 0, 0, 5).weightx(1).fill(GBC.HORIZONTAL).add(this.channels, channels -> {
136+
channels.setSelectedItem(Channels.STEREO);
137+
});
138+
GBC.create(audioFilePanel).nextRow().insets(5, 5, 5, 5).anchor(GBC.LINE_START).add(this.wavBitDepthLabel);
139+
GBC.create(audioFilePanel).nextColumn().insets(5, 0, 5, 5).weightx(1).fill(GBC.HORIZONTAL).add(this.wavBitDepth, wavBitDepth -> {
140+
wavBitDepth.setSelectedItem(WavBitDepth.PCM16);
141+
});
142+
GBC.create(audioFilePanel).nextRow().insets(5, 5, 5, 5).anchor(GBC.LINE_START).add(this.mp3QualityLabel);
143+
GBC.create(audioFilePanel).nextColumn().insets(5, 0, 5, 5).weightx(1).fill(GBC.HORIZONTAL).add(this.mp3Quality, mp3Quality -> {
144+
mp3Quality.setMajorTickSpacing(10);
145+
mp3Quality.setMinorTickSpacing(5);
146+
mp3Quality.setPaintTicks(true);
147+
mp3Quality.setPaintLabels(true);
148+
});
149+
});
131150

132-
GBC.create(root).grid(0, gridy).insets(5, 5, 0, 5).anchor(GBC.LINE_START).add(this.channelsLabel);
133-
GBC.create(root).grid(1, gridy++).insets(5, 0, 0, 5).weightx(1).fill(GBC.HORIZONTAL).add(this.channels, () -> {
134-
this.channels.setSelectedItem(Channels.STEREO);
135-
});
151+
GBC.create(centerPanel).nextRow().insets(5, 5, 0, 5).width(2).weightx(1).fill(GBC.HORIZONTAL).add(this.playbackPanel, playbackPanel -> {
152+
playbackPanel.setBorder(BorderFactory.createTitledBorder("Playback"));
153+
GBC.create(playbackPanel).nextRow().insets(0, 5, 0, 5).anchor(GBC.LINE_START).add(new JLabel("Volume:"));
154+
GBC.create(playbackPanel).nextColumn().insets(0, 0, 0, 5).weightx(1).fill(GBC.HORIZONTAL).add(this.volume, volume -> {
155+
volume.setMajorTickSpacing(10);
156+
volume.setMinorTickSpacing(5);
157+
volume.setPaintLabels(true);
158+
volume.setPaintTicks(true);
159+
});
160+
GBC.create(playbackPanel).nextRow().insets(5, 5, 5, 5).width(2).anchor(GBC.LINE_START).add(this.timingJitter, timingJitter -> {
161+
timingJitter.setToolTipText("Adds slight timing jitter (±1ms) to make the song sound more natural and less artificial.\nThis emulates the behaviour of playing the song in Note Block Studio.");
162+
});
163+
});
136164

137-
GBC.create(root).grid(0, gridy).insets(5, 5, 0, 5).anchor(GBC.LINE_START).add(this.volumeLabel);
138-
GBC.create(root).grid(1, gridy++).insets(5, 0, 0, 5).weightx(1).fill(GBC.HORIZONTAL).add(this.volume, () -> {
139-
this.volume.setMajorTickSpacing(10);
140-
this.volume.setMinorTickSpacing(5);
141-
this.volume.setPaintTicks(true);
142-
this.volume.setPaintLabels(true);
143-
});
144-
GBC.create(root).grid(1, gridy++).insets(5, 0, 0, 5).anchor(GBC.LINE_START).add(this.timingJitter, () -> {
145-
this.timingJitter.setToolTipText("Adds slight timing jitter (±1ms) to make the song sound more natural and less artificial.\nThis emulates the behaviour of playing the song in Note Block Studio.");
146-
});
165+
GBC.create(centerPanel).nextRow().insets(5, 5, 0, 5).width(2).weightx(1).fill(GBC.HORIZONTAL).add(this.rendererPanel, rendererPanel -> {
166+
rendererPanel.setBorder(BorderFactory.createTitledBorder("Renderer"));
167+
GBC.create(rendererPanel).nextRow().insets(0, 5, 0, 5).anchor(GBC.LINE_START).add(new JLabel("Max Sounds:"));
168+
GBC.create(rendererPanel).nextColumn().insets(0, 0, 0, 5).weightx(1).fill(GBC.HORIZONTAL).add(this.maxSounds);
169+
GBC.create(rendererPanel).nextRow().insets(5, 5, 0, 5).width(2).anchor(GBC.LINE_START).add(this.globalNormalization);
170+
GBC.create(rendererPanel).nextRow().insets(5, 5, 5, 5).width(2).anchor(GBC.LINE_START).add(this.threaded);
171+
});
147172

148-
GBC.create(root).grid(0, gridy++).insets(5, 5, 0, 5).width(1).width(2).weight(1, 1).fill(GBC.BOTH).add(() -> {
149-
JScrollPane scrollPane = new JScrollPane();
150-
this.progressPanel = new ScrollPaneSizedPanel(scrollPane);
151-
this.progressPanel.setLayout(new VerticalLayout(5, 5));
152-
scrollPane.setViewportView(this.progressPanel);
153-
scrollPane.setBorder(BorderFactory.createEmptyBorder());
154-
return scrollPane;
155-
});
173+
GBC.create(centerPanel).nextRow().insets(5, 5, 0, 5).width(1).width(2).weight(1, 1).fill(GBC.BOTH).add(this.progressPanel, progressPanel -> {
174+
progressPanel.setLayout(new VerticalLayout(5, 5));
175+
});
156176

157-
JPanel bottomPanel = new JPanel();
158-
bottomPanel.setLayout(new GridBagLayout());
159-
GBC.create(root).grid(0, gridy++).insets(0, 0, 0, 0).weightx(1).width(2).fill(GBC.HORIZONTAL).add(bottomPanel);
177+
GBC.fillVerticalSpace(centerPanel);
178+
}
160179

161-
GBC.create(bottomPanel).grid(0, 0).insets(5, 5, 5, 5).weightx(1).fill(GBC.HORIZONTAL).add(this.progressBar, () -> {
162-
this.progressBar.setStringPainted(true);
163-
});
164-
GBC.create(bottomPanel).grid(1, 0).insets(5, 0, 5, 5).anchor(GBC.LINE_END).add(this.exportButton, () -> {
165-
this.exportButton.addActionListener(e -> this.export());
166-
});
180+
{ // South panel
181+
final JPanel southPanel = new JPanel(new GridBagLayout());
182+
root.add(southPanel, BorderLayout.SOUTH);
183+
GBC.create(southPanel).nextRow().insets(5, 5, 5, 5).weightx(1).fill(GBC.HORIZONTAL).add(this.progressBar, progressBar -> {
184+
progressBar.setStringPainted(true);
185+
});
186+
GBC.create(southPanel).nextColumn().insets(5, 0, 5, 5).anchor(GBC.LINE_END).add(this.export, exportButton -> {
187+
exportButton.addActionListener(e -> this.export());
188+
});
189+
}
167190
}
168191

169-
private void updateVisibility() {
170-
final OutputFormat outputFormat = (OutputFormat) this.format.getSelectedItem();
171-
172-
this.globalNormalization.setVisible(outputFormat.isAudioFile());
173-
this.threaded.setVisible(outputFormat.isAudioFile());
174-
175-
this.sampleRateLabel.setVisible(outputFormat.isAudioFile());
176-
this.sampleRate.setVisible(outputFormat.isAudioFile());
192+
private void updateVisibility(final boolean showSettings) {
193+
if (showSettings) {
194+
final OutputFormat outputFormat = (OutputFormat) this.format.getSelectedItem();
195+
this.formatLabel.setVisible(true);
196+
this.format.setVisible(true);
197+
this.audioFilePanel.setVisible(outputFormat.isAudioFile());
198+
this.playbackPanel.setVisible(outputFormat.isAudioFile());
199+
this.rendererPanel.setVisible(outputFormat.isAudioFile());
200+
this.progressPanel.setVisible(false);
201+
202+
this.wavBitDepthLabel.setVisible(outputFormat.isAudioFile() && outputFormat.equals(OutputFormat.WAV));
203+
this.wavBitDepth.setVisible(outputFormat.isAudioFile() && outputFormat.equals(OutputFormat.WAV));
204+
this.mp3QualityLabel.setVisible(outputFormat.isAudioFile() && outputFormat.equals(OutputFormat.MP3));
205+
this.mp3Quality.setVisible(outputFormat.isAudioFile() && outputFormat.equals(OutputFormat.MP3));
177206

178-
this.wavBitDepthLabel.setVisible(outputFormat.isAudioFile() && outputFormat.equals(OutputFormat.WAV));
179-
this.wavBitDepth.setVisible(outputFormat.isAudioFile() && outputFormat.equals(OutputFormat.WAV));
180-
181-
this.mp3QualityLabel.setVisible(outputFormat.isAudioFile() && outputFormat.equals(OutputFormat.MP3));
182-
this.mp3Quality.setVisible(outputFormat.isAudioFile() && outputFormat.equals(OutputFormat.MP3));
183-
184-
this.channelsLabel.setVisible(outputFormat.isAudioFile());
185-
this.channels.setVisible(outputFormat.isAudioFile());
186-
187-
this.volumeLabel.setVisible(outputFormat.isAudioFile());
188-
this.volume.setVisible(outputFormat.isAudioFile());
189-
this.timingJitter.setVisible(outputFormat.isAudioFile());
207+
} else {
208+
this.formatLabel.setVisible(false);
209+
this.format.setVisible(false);
210+
this.audioFilePanel.setVisible(false);
211+
this.playbackPanel.setVisible(false);
212+
this.rendererPanel.setVisible(false);
213+
this.progressPanel.setVisible(true);
214+
}
190215
}
191216

192217
private void initFrameHandler() {
@@ -216,37 +241,21 @@ private void export() {
216241
} catch (InterruptedException ignored) {
217242
}
218243

219-
this.format.setEnabled(true);
220-
this.globalNormalization.setEnabled(true);
221-
this.threaded.setEnabled(true);
222-
this.sampleRate.setEnabled(true);
223-
this.wavBitDepth.setEnabled(true);
224-
this.mp3Quality.setEnabled(true);
225-
this.channels.setEnabled(true);
226-
this.volume.setEnabled(true);
227-
this.timingJitter.setEnabled(true);
228244
this.progressPanel.removeAll();
229-
this.exportButton.setText("Export");
245+
this.export.setText("Export");
230246
this.progressBar.setValue(0);
247+
this.updateVisibility(true);
231248
return;
232249
}
233250

234251
File out = this.openFileChooser();
235252
if (out == null) return;
236253

237-
this.format.setEnabled(false);
238-
this.globalNormalization.setEnabled(false);
239-
this.threaded.setEnabled(false);
240-
this.sampleRate.setEnabled(false);
241-
this.wavBitDepth.setEnabled(false);
242-
this.mp3Quality.setEnabled(false);
243-
this.channels.setEnabled(false);
244-
this.volume.setEnabled(false);
245-
this.timingJitter.setEnabled(false);
246254
this.progressPanel.removeAll();
247-
this.exportButton.setText("Cancel");
255+
this.export.setText("Cancel");
248256
this.progressBar.setValue(0);
249257
this.progressBar.setMaximum(this.loadedSongs.size());
258+
this.updateVisibility(false);
250259

251260
this.exportThread = new Thread(() -> this.doExport(out), "Song Export Thread");
252261
this.exportThread.setDaemon(true);
@@ -404,19 +413,11 @@ private void doExport(final File outFile) {
404413
JOptionPane.showMessageDialog(this, "Failed to export songs:\n" + t.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
405414
} finally {
406415
SwingUtilities.invokeLater(() -> {
407-
this.format.setEnabled(true);
408-
this.globalNormalization.setEnabled(true);
409-
this.threaded.setEnabled(true);
410-
this.sampleRate.setEnabled(true);
411-
this.wavBitDepth.setEnabled(true);
412-
this.mp3Quality.setEnabled(true);
413-
this.channels.setEnabled(true);
414-
this.volume.setEnabled(true);
415-
this.timingJitter.setEnabled(true);
416-
this.exportButton.setText("Export");
416+
this.export.setText("Export");
417417
this.progressBar.setValue(this.loadedSongs.size());
418418
this.progressBar.revalidate();
419419
this.progressBar.repaint();
420+
this.updateVisibility(true);
420421
});
421422
}
422423
}
@@ -427,7 +428,7 @@ private void exportSong(final ListFrame.LoadedSong song, final File file, final
427428
this.writeSong(song, file, outputFormat.getSongFormat());
428429
} else if (outputFormat.isAudioFile()) {
429430
final PcmFloatAudioFormat renderAudioFormat = new PcmFloatAudioFormat(((Number) this.sampleRate.getValue()).floatValue(), ((Channels) this.channels.getSelectedItem()).getChannels());
430-
final SongRenderer songRenderer = new ProgressSongRenderer(song.song(), MAX_SOUNDS, !this.globalNormalization.isSelected(), this.threaded.isSelected(), renderAudioFormat, progressConsumer);
431+
final SongRenderer songRenderer = new ProgressSongRenderer(song.song(), (int) this.maxSounds.getValue(), !this.globalNormalization.isSelected(), this.threaded.isSelected(), renderAudioFormat, progressConsumer);
431432
songRenderer.setMasterVolume(this.volume.getValue() / 100F);
432433
songRenderer.setTimingJitter(this.timingJitter.isSelected());
433434
final float[] samples;

0 commit comments

Comments
 (0)