001package jmri.jmrit.audio.swing; 002 003import java.awt.FlowLayout; 004import java.awt.event.ActionEvent; 005import java.io.File; 006 007import javax.swing.BorderFactory; 008import javax.swing.BoxLayout; 009import javax.swing.JButton; 010import javax.swing.JCheckBox; 011import javax.swing.JFileChooser; 012import javax.swing.JLabel; 013import javax.swing.JPanel; 014import javax.swing.JSpinner; 015import javax.swing.JTextField; 016import javax.swing.SpinnerNumberModel; 017import javax.swing.filechooser.FileNameExtensionFilter; 018 019import jmri.Audio; 020import jmri.AudioException; 021import jmri.AudioManager; 022import jmri.InstanceManager; 023import jmri.jmrit.audio.AudioBuffer; 024import jmri.jmrit.beantable.AudioTableAction.AudioTableDataModel; 025import jmri.util.FileUtil; 026import jmri.util.swing.JmriJOptionPane; 027 028/** 029 * Defines a GUI to edit AudioBuffer objects. 030 * 031 * <hr> 032 * This file is part of JMRI. 033 * <p> 034 * JMRI is free software; you can redistribute it and/or modify it under the 035 * terms of version 2 of the GNU General Public License as published by the Free 036 * Software Foundation. See the "COPYING" file for a copy of this license. 037 * <p> 038 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 039 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 040 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 041 * 042 * @author Matthew Harris copyright (c) 2009 043 */ 044public class AudioBufferFrame extends AbstractAudioFrame { 045 046 private static int counter = 1; 047 048 private boolean newBuffer; 049 050 private final Object lock = new Object(); 051 052 // UI components for Add/Edit Buffer 053 private final JLabel urlLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("LabelURL"))); 054 private final JTextField url = new JTextField(40); 055 private final JButton buttonBrowse = new JButton("..."); 056 private final JCheckBox stream = new JCheckBox(Bundle.getMessage("LabelStream")); 057 // JLabel formatLabel = new JLabel(Bundle.getMessage("LabelFormat")); 058 // JTextField format = new JTextField(20); 059 private final JLabel loopStartLabel = new JLabel(Bundle.getMessage("LabelLoopStart")); 060 private final JSpinner loopStart = new JSpinner(); 061 private final JLabel loopEndLabel = new JLabel(Bundle.getMessage("LabelLoopEnd")); 062 private final JSpinner loopEnd = new JSpinner(); 063 private JFileChooser fileChooser; 064 // AudioWaveFormPanel waveForm = new AudioWaveFormPanel(); 065 066 private static final String PREFIX = "IAB"; 067 068// @SuppressWarnings("OverridableMethodCallInConstructor") 069 public AudioBufferFrame(String title, AudioTableDataModel model) { 070 super(title, model); 071 layoutFrame(); 072 073 // For now, disable editing of loop points 074 // TODO: enable editing of looping points 075 loopStart.setEnabled(false); 076 loopStartLabel.setEnabled(false); 077 loopEnd.setEnabled(false); 078 loopEndLabel.setEnabled(false); 079 } 080 081 @Override 082 public final void layoutFrame() { 083 super.layoutFrame(); 084 085 JPanel p = new JPanel(); 086 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 087 p.setBorder(BorderFactory.createCompoundBorder( 088 BorderFactory.createTitledBorder(Bundle.getMessage("LabelSample")), 089 BorderFactory.createEmptyBorder(5, 5, 5, 5))); 090 JPanel p2 = new JPanel(); 091 p2.setLayout(new FlowLayout()); 092 p2.add(urlLabel); 093 p2.add(url); 094 buttonBrowse.addActionListener( e -> browsePressed()); 095 buttonBrowse.setToolTipText(Bundle.getMessage("ToolTipButtonBrowse")); 096 p2.add(buttonBrowse); 097 p.add(p2); 098 p2 = new JPanel(); 099 p2.setLayout(new FlowLayout()); 100 p2.add(stream); 101 p.add(p2); 102// p2 = new JPanel(); p2.setLayout(new FlowLayout()); 103// p2.add(formatLabel); 104// p2.add(format); 105// p.add(p2); 106 main.add(p); 107 108// p = new JPanel(); 109// p.setBorder(BorderFactory.createCompoundBorder( 110// BorderFactory.createTitledBorder("Waveforms"), 111// BorderFactory.createEmptyBorder(5, 5, 5, 5))); 112// 113// JLabel label = new JLabel("Whole sample"); 114// label.setUI(new VerticalLabelUI()); 115// p.add(label); 116// 117// waveForm.setPreferredSize(new Dimension(400, 120)); 118// p.add(waveForm); 119// 120// label = new JLabel("Loop-point detail"); 121// label.setUI(new VerticalLabelUI()); 122// p.add(label); 123// 124// AudioWaveFormPanel waveFormLoop = new AudioWaveFormPanel(); 125// waveFormLoop.setPreferredSize(new Dimension(80, 120)); 126// p.add(waveFormLoop); 127// 128// main.add(p); 129// 130 p = new JPanel(); 131 p.setLayout(new FlowLayout()); 132 p.setBorder(BorderFactory.createCompoundBorder( 133 BorderFactory.createTitledBorder(Bundle.getMessage("LabelLoopPoints")), 134 BorderFactory.createEmptyBorder(5, 5, 5, 5))); 135 p.add(loopStartLabel); 136 loopStart.setPreferredSize(new JTextField(8).getPreferredSize()); 137 loopStart.setModel( 138 new SpinnerNumberModel(0L, 0L, Long.MAX_VALUE,1L)); 139 loopStart.addChangeListener( e -> 140 loopEnd.setValue( 141 ((Long) loopStart.getValue() 142 < (Long) loopEnd.getValue()) 143 ? loopEnd.getValue() 144 : loopStart.getValue())); 145 p.add(loopStart); 146 p.add(loopEndLabel); 147 loopEnd.setPreferredSize(new JTextField(8).getPreferredSize()); 148 loopEnd.setModel( 149 new SpinnerNumberModel(0L, 0L, Long.MAX_VALUE, 1L)); 150 loopEnd.addChangeListener( e -> 151 loopStart.setValue( 152 ((Long) loopEnd.getValue() 153 < (Long) loopStart.getValue()) 154 ? loopEnd.getValue() 155 : loopStart.getValue())); 156 p.add(loopEnd); 157 main.add(p); 158 159 p = new JPanel(); 160 JButton apply = new JButton(Bundle.getMessage("ButtonApply")); 161 p.add(apply); 162 apply.addActionListener( e -> applyPressed()); 163 JButton ok = new JButton(Bundle.getMessage("ButtonOK")); 164 p.add(ok); 165 ok.addActionListener((ActionEvent e) -> { 166 if (applyPressed()) { 167 frame.dispose(); 168 } 169 }); 170 JButton cancel = new JButton(Bundle.getMessage("ButtonCancel")); 171 p.add(cancel); 172 cancel.addActionListener( e -> frame.dispose()); 173 frame.getContentPane().add(p); 174 } 175 176 /** 177 * Populate the Edit Buffer frame with default values. 178 */ 179 @Override 180 public void resetFrame() { 181 synchronized (lock) { 182 sysName.setText(PREFIX + nextCounter()); // NOI18N 183 } 184 userName.setText(null); 185 url.setText(null); 186 // format.setText(null); 187 stream.setSelected(false); 188 stream.setEnabled(false); 189 loopStart.setValue(0L); 190 loopEnd.setValue(0L); 191 192 this.newBuffer = true; 193 } 194 195 /** 196 * Populate the Edit Buffer frame with current values. 197 */ 198 @Override 199 public void populateFrame(Audio a) { 200 if (!(a instanceof AudioBuffer)) { 201 throw new IllegalArgumentException(a + " is not an AudioBuffer object"); 202 } 203 super.populateFrame(a); 204 AudioBuffer b = (AudioBuffer) a; 205 url.setText(b.getURL()); 206 // format.setText(b.toString()); 207 stream.setSelected(b.isStreamed()); 208 stream.setEnabled(false); //(!b.isStreamedForced()); 209 loopStart.setValue(b.getStartLoopPoint()); 210 loopEnd.setValue(b.getEndLoopPoint()); 211 loopStart.setEnabled(true); 212 loopStartLabel.setEnabled(true); 213 loopEnd.setEnabled(true); 214 loopEndLabel.setEnabled(true); 215 216 this.newBuffer = false; 217 } 218 219 private void browsePressed() { 220 if (fileChooser == null) { 221 fileChooser = new jmri.util.swing.JmriJFileChooser("resources" + File.separator + "sounds" + File.separator); // NOI18N 222 fileChooser.setFileFilter(new FileNameExtensionFilter("Audio Files (*.wav)", "wav")); // NOI18N 223 } 224 225 // Show dialog 226 fileChooser.rescanCurrentDirectory(); 227 int retValue = fileChooser.showOpenDialog(this); 228 229 // Process selection 230 if (retValue == JFileChooser.APPROVE_OPTION) { 231 File file = fileChooser.getSelectedFile(); 232 String fileName = FileUtil.getPortableFilename(file); 233 if (!url.getText().equals(fileName)) { 234 url.setText(fileName); 235// try { 236// WaveFileReader wfr = new WaveFileReader(FileUtil.getExternalFilename(fileName)); 237// JmriJOptionPane.showMessageDialog(null, wfr.toString(), wfr.toString(), JmriJOptionPane.INFORMATION_MESSAGE); 238// } catch (AudioException ex) { 239// JmriJOptionPane.showMessageDialog(this, ex.getMessage(), rba.getString("TitleReadError"), JmriJOptionPane.ERROR_MESSAGE); 240// } 241 } 242 } 243 } 244 245 private boolean applyPressed() { 246 String sName = sysName.getText(); 247 if (entryError(sName, PREFIX, "" + counter)) { 248 return false; 249 } 250 String user = userName.getText(); 251 if (user.isEmpty()) { 252 user = null; 253 } 254 AudioBuffer b; 255 try { 256 AudioManager am = InstanceManager.getDefault(jmri.AudioManager.class); 257 if (newBuffer && am.getBySystemName(sName) != null) { 258 throw new AudioException(Bundle.getMessage("DuplicateSystemName")); 259 } 260 try { 261 b = (AudioBuffer) am.provideAudio(sName); 262 } catch (IllegalArgumentException ex) { 263 throw new AudioException(Bundle.getMessage("ProblemCreatingBuffer")); 264 } 265 if ((user != null) && newBuffer && am.getByUserName(user) != null) { 266 am.deregister(b); 267 synchronized (lock) { 268 prevCounter(); 269 } 270 throw new AudioException(Bundle.getMessage("DuplicateUserName")); 271 } 272 b.setUserName(user); 273 b.setStreamed(stream.isSelected()); 274 if (newBuffer || !b.getURL().equals(url.getText())) { 275 b.setURL(url.getText()); 276 log.debug("After load, end loop point = {}", b.getEndLoopPoint()); 277 //b.setStartLoopPoint((Long)loopStart.getValue()); 278 //b.setEndLoopPoint((Long)loopEnd.getValue()); 279 } else { 280 if (!b.getURL().equals(url.getText())) { 281 log.debug("Sound changed from: {}", b.getURL()); 282 b.setURL(url.getText()); 283 } 284 } 285 286 // Update streaming checkbox if necessary 287 stream.setSelected(b.isStreamed()); 288 stream.setEnabled(false); //(!b.isStreamedForced()); 289 290 // Notify changes 291 model.fireTableDataChanged(); 292 } catch (AudioException ex) { 293 JmriJOptionPane.showMessageDialog(this, ex.getMessage(), 294 Bundle.getMessage("AudioCreateErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 295 return false; 296 } 297 newBuffer = false; // If the user presses Apply, the dialog stays visible. 298 return true; 299 } 300 301 private static int nextCounter() { 302 counter++; 303 return counter-1; 304 } 305 306 private static void prevCounter() { 307 counter--; 308 } 309 310 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AudioBufferFrame.class); 311 312}