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