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