001package jmri.jmrit.audio.swing; 002 003import java.awt.FlowLayout; 004import java.awt.event.ActionEvent; 005 006import javax.swing.BorderFactory; 007import javax.swing.BoxLayout; 008import javax.swing.JButton; 009import javax.swing.JCheckBox; 010import javax.swing.JComboBox; 011import javax.swing.JLabel; 012import javax.swing.JPanel; 013import javax.swing.JSpinner; 014import javax.swing.JTextField; 015import javax.swing.SpinnerNumberModel; 016import javax.swing.event.ChangeEvent; 017import javax.vecmath.Vector3f; 018 019import jmri.Audio; 020import jmri.AudioException; 021import jmri.AudioManager; 022import jmri.InstanceManager; 023import jmri.jmrit.audio.AudioSource; 024import jmri.jmrit.beantable.AudioTableAction.AudioTableDataModel; 025import jmri.util.swing.JmriJOptionPane; 026 027/** 028 * Defines a GUI for editing AudioSource objects. 029 * 030 * <hr> 031 * This file is part of JMRI. 032 * <p> 033 * JMRI is free software; you can redistribute it and/or modify it under the 034 * terms of version 2 of the GNU General Public License as published by the Free 035 * Software Foundation. See the "COPYING" file for a copy of this license. 036 * <p> 037 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 038 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 039 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 040 * 041 * @author Matthew Harris copyright (c) 2009 042 */ 043public class AudioSourceFrame extends AbstractAudioFrame { 044 045 private static int counter = 1; 046 047 private boolean newSource; 048 049 private final Object lock = new Object(); 050 051 // UI components for Add/Edit Source 052 JLabel assignedBufferLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("LabelAssignedBuffer"))); 053 JComboBox<String> assignedBuffer = new JComboBox<>(); 054 JLabel loopMinLabel = new JLabel(Bundle.getMessage("LabelLoopMin")); 055 JSpinner loopMin = new JSpinner(); 056 JLabel loopMaxLabel = new JLabel(Bundle.getMessage("LabelLoopMax")); 057 JSpinner loopMax = new JSpinner(); 058 // JLabel loopMinDelayLabel = new JLabel(Bundle.getMessage("LabelLoopMin")); 059 // JSpinner loopMinDelay = new JSpinner(); 060 // JLabel loopMaxDelayLabel = new JLabel(Bundle.getMessage("LabelLoopMax")); 061 // JSpinner loopMaxDelay = new JSpinner(); 062 // JLabel loopDelayUnitsLabel = new JLabel(Bundle.getMessage("UnitMS")); 063 JCheckBox loopInfinite = new JCheckBox(Bundle.getMessage("LabelLoopInfinite")); 064 JPanelVector3f position = new JPanelVector3f("", 065 Bundle.getMessage("UnitUnits")); 066 JCheckBox positionRelative = new JCheckBox(Bundle.getMessage("LabelPositionRelative")); 067 JPanelVector3f velocity = new JPanelVector3f(Bundle.getMessage("LabelVelocity"), 068 Bundle.getMessage("UnitU/S")); 069 JPanelSliderf gain = new JPanelSliderf(Bundle.getMessage("LabelGain"), 0.0f, 1.0f, 5, 4); 070 JPanelSliderf pitch = new JPanelSliderf(Bundle.getMessage("LabelPitch"), 0.5f, 2.0f, 6, 5); 071 JLabel refDistanceLabel = new JLabel(Bundle.getMessage("LabelReferenceDistance")); 072 JSpinner refDistance = new JSpinner(); 073 JLabel maxDistanceLabel = new JLabel(Bundle.getMessage("LabelMaximumDistance")); 074 JSpinner maxDistance = new JSpinner(); 075 JLabel distancesLabel = new JLabel(Bundle.getMessage("UnitUnits")); 076 JLabel rollOffFactorLabel = new JLabel(Bundle.getMessage("LabelRollOffFactor")); 077 JSpinner rollOffFactor = new JSpinner(); 078 JLabel fadeInTimeLabel = new JLabel(Bundle.getMessage("LabelFadeIn")); 079 JSpinner fadeInTime = new JSpinner(); 080 JLabel fadeOutTimeLabel = new JLabel(Bundle.getMessage("LabelFadeOut")); 081 JSpinner fadeOutTime = new JSpinner(); 082 JLabel fadeTimeUnitsLabel = new JLabel(Bundle.getMessage("UnitMS")); 083 084 private final static String PREFIX = "IAS"; 085 086// @SuppressWarnings("OverridableMethodCallInConstructor") 087 public AudioSourceFrame(String title, AudioTableDataModel model) { 088 super(title, model); 089 layoutFrame(); 090 } 091 092 @Override 093 public void layoutFrame() { 094 super.layoutFrame(); 095 JPanel p; 096 097 p = new JPanel(); 098 p.setLayout(new FlowLayout()); 099 p.add(assignedBufferLabel); 100 p.add(assignedBuffer); 101 main.add(p); 102 103 p = new JPanel(); 104 p.setLayout(new FlowLayout()); 105 p.setBorder(BorderFactory.createCompoundBorder( 106 BorderFactory.createTitledBorder(Bundle.getMessage("LabelLoop")), 107 BorderFactory.createEmptyBorder(5, 5, 5, 5))); 108 p.add(loopMinLabel); 109 loopMin.setPreferredSize(new JTextField(8).getPreferredSize()); 110 loopMin.setModel(new SpinnerNumberModel(0, 0, Integer.MAX_VALUE, 1)); 111 loopMin.addChangeListener((ChangeEvent e) -> { 112 loopMax.setValue( 113 ((Integer) loopMin.getValue() 114 < (Integer) loopMax.getValue()) 115 ? loopMax.getValue() 116 : loopMin.getValue()); 117 }); 118 p.add(loopMin); 119 p.add(loopMaxLabel); 120 loopMax.setPreferredSize(new JTextField(8).getPreferredSize()); 121 loopMax.setModel(new SpinnerNumberModel(0, 0, Integer.MAX_VALUE, 1)); 122 loopMax.addChangeListener((ChangeEvent e) -> { 123 loopMin.setValue( 124 ((Integer) loopMax.getValue() 125 < (Integer) loopMin.getValue()) 126 ? loopMax.getValue() 127 : loopMin.getValue()); 128 }); 129 p.add(loopMax); 130 loopInfinite.addChangeListener((ChangeEvent e) -> { 131 loopMin.setEnabled(!loopInfinite.isSelected()); 132 loopMax.setEnabled(!loopInfinite.isSelected()); 133 }); 134 p.add(loopInfinite); 135 main.add(p); 136 137// p = new JPanel(); p.setLayout(new FlowLayout()); 138// p.setBorder(BorderFactory.createCompoundBorder( 139// BorderFactory.createTitledBorder(Bundle.getMessage("LabelLoopDelay")), 140// BorderFactory.createEmptyBorder(5, 5, 5, 5))); 141// p.add(loopMinDelayLabel); 142// loopMinDelay.setPreferredSize(new JTextField(8).getPreferredSize()); 143// loopMinDelay.setModel(new SpinnerNumberModel(0,0,Integer.MAX_VALUE,1)); 144// loopMinDelay.addChangeListener(new ChangeListener() { 145// public void stateChanged(ChangeEvent e) { 146// loopMaxDelay.setValue( 147// ((Integer)loopMinDelay.getValue() 148// <(Integer)loopMaxDelay.getValue()) 149// ?loopMaxDelay.getValue() 150// :loopMinDelay.getValue()); 151// } 152// }); 153// p.add(loopMinDelay); 154// p.add(loopMaxDelayLabel); 155// loopMaxDelay.setPreferredSize(new JTextField(8).getPreferredSize()); 156// loopMaxDelay.setModel(new SpinnerNumberModel(0,0,Integer.MAX_VALUE,1)); 157// loopMaxDelay.addChangeListener(new ChangeListener() { 158// public void stateChanged(ChangeEvent e) { 159// loopMinDelay.setValue( 160// ((Integer)loopMaxDelay.getValue() 161// <(Integer)loopMinDelay.getValue()) 162// ?loopMaxDelay.getValue() 163// :loopMinDelay.getValue()); 164// } 165// }); 166// p.add(loopMaxDelay); 167// p.add(loopDelayUnitsLabel); 168// main.add(p); 169// 170 p = new JPanel(); 171 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 172 p.setBorder(BorderFactory.createCompoundBorder( 173 BorderFactory.createTitledBorder(Bundle.getMessage("LabelPosition")), 174 BorderFactory.createEmptyBorder(5, 5, 5, 5))); 175 p.add(position); 176 p.add(positionRelative); 177 main.add(p); 178 179 main.add(velocity); 180 main.add(gain); 181 main.add(pitch); 182 183 p = new JPanel(); 184 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 185 p.setBorder(BorderFactory.createCompoundBorder( 186 BorderFactory.createTitledBorder(Bundle.getMessage("LabelDistances")), 187 BorderFactory.createEmptyBorder(5, 5, 5, 5))); 188 189 JPanel p2; 190 p2 = new JPanel(); 191 p2.setLayout(new FlowLayout()); 192 p2.add(refDistanceLabel); 193 refDistance.setPreferredSize(new JTextField(8).getPreferredSize()); 194 refDistance.setModel( 195 new SpinnerNumberModel(Float.valueOf(0f), Float.valueOf(0f), Float.valueOf(Audio.MAX_DISTANCE), Float.valueOf(FLT_PRECISION))); 196 refDistance.setEditor(new JSpinner.NumberEditor(refDistance, "0.00")); 197 refDistance.addChangeListener((ChangeEvent e) -> { 198 maxDistance.setValue( 199 ((Float) refDistance.getValue() 200 < (Float) maxDistance.getValue()) 201 ? maxDistance.getValue() 202 : refDistance.getValue()); 203 }); 204 p2.add(refDistance); 205 206 p2.add(maxDistanceLabel); 207 maxDistance.setPreferredSize(new JTextField(8).getPreferredSize()); 208 maxDistance.setModel( 209 new SpinnerNumberModel(Float.valueOf(0f), Float.valueOf(0f), Float.valueOf(Audio.MAX_DISTANCE), Float.valueOf(FLT_PRECISION))); 210 maxDistance.setEditor(new JSpinner.NumberEditor(maxDistance, "0.00")); 211 maxDistance.addChangeListener((ChangeEvent e) -> { 212 refDistance.setValue( 213 ((Float) maxDistance.getValue() 214 < (Float) refDistance.getValue()) 215 ? maxDistance.getValue() 216 : refDistance.getValue()); 217 }); 218 p2.add(maxDistance); 219 p2.add(distancesLabel); 220 p.add(p2); 221 222 p2 = new JPanel(); 223 p2.setLayout(new FlowLayout()); 224 p2.add(rollOffFactorLabel); 225 rollOffFactor.setPreferredSize(new JTextField(8).getPreferredSize()); 226 rollOffFactor.setModel( 227 new SpinnerNumberModel(Float.valueOf(0f), Float.valueOf(0f), Float.valueOf(Audio.MAX_DISTANCE), Float.valueOf(FLT_PRECISION))); 228 rollOffFactor.setEditor(new JSpinner.NumberEditor(rollOffFactor, "0.00")); 229 p2.add(rollOffFactor); 230 p.add(p2); 231 main.add(p); 232 233 p = new JPanel(); 234 p.setLayout(new FlowLayout()); 235 p.setBorder(BorderFactory.createCompoundBorder( 236 BorderFactory.createTitledBorder(Bundle.getMessage("LabelFadeTimes")), 237 BorderFactory.createEmptyBorder(5, 5, 5, 5))); 238 239 p.add(fadeInTimeLabel); 240 fadeInTime.setPreferredSize(new JTextField(8).getPreferredSize()); 241 fadeInTime.setModel(new SpinnerNumberModel(0, 0, Integer.MAX_VALUE, 1)); 242 p.add(fadeInTime); 243 244 p.add(fadeOutTimeLabel); 245 fadeOutTime.setPreferredSize(new JTextField(8).getPreferredSize()); 246 fadeOutTime.setModel(new SpinnerNumberModel(0, 0, Integer.MAX_VALUE, 1)); 247 p.add(fadeOutTime); 248 249 p.add(fadeTimeUnitsLabel); 250 main.add(p); 251 252 p = new JPanel(); 253 JButton apply; 254 p.add(apply = new JButton(Bundle.getMessage("ButtonApply"))); 255 apply.addActionListener((ActionEvent e) -> { 256 applyPressed(e); 257 }); 258 JButton ok; 259 p.add(ok = new JButton(Bundle.getMessage("ButtonOK"))); 260 ok.addActionListener((ActionEvent e) -> { 261 if (applyPressed(e)) { 262 frame.dispose(); 263 } 264 }); 265 JButton cancel; 266 p.add(cancel = new JButton(Bundle.getMessage("ButtonCancel"))); 267 cancel.addActionListener((ActionEvent e) -> { 268 frame.dispose(); 269 }); 270 frame.getContentPane().add(p); 271 } 272 273 /** 274 * Populate the Edit Source frame with default values. 275 */ 276 @Override 277 public void resetFrame() { 278 synchronized (lock) { 279 sysName.setText(PREFIX + nextCounter()); 280 } 281 userName.setText(null); 282 assignedBuffer.setSelectedIndex(0); 283 loopInfinite.setSelected(false); 284 loopMin.setValue(AudioSource.LOOP_NONE); 285 loopMax.setValue(AudioSource.LOOP_NONE); 286// loopMinDelay.setValue(0); 287// loopMaxDelay.setValue(0); 288 position.setValue(new Vector3f(0, 0, 0)); 289 positionRelative.setSelected(false); 290 velocity.setValue(new Vector3f(0, 0, 0)); 291 gain.setValue(1.0f); 292 pitch.setValue(1.0f); 293 refDistance.setValue(1.0f); 294 maxDistance.setValue(Audio.MAX_DISTANCE); 295 rollOffFactor.setValue(1.0f); 296 fadeInTime.setValue(1000); 297 fadeOutTime.setValue(1000); 298 299 this.newSource = true; 300 } 301 302 /** 303 * Populate the Edit Source frame with current values. 304 */ 305 @Override 306 public void populateFrame(Audio a) { 307 if (!(a instanceof AudioSource)) { 308 throw new IllegalArgumentException(a.getSystemName() + " is not an AudioSource object"); 309 } 310 super.populateFrame(a); 311 AudioSource s = (AudioSource) a; 312 AudioManager am = InstanceManager.getDefault(jmri.AudioManager.class); 313 String ab = s.getAssignedBufferName(); 314 Audio b = am.getAudio(ab); 315 if (b != null) { 316 assignedBuffer.setSelectedItem(b.getUserName() == null ? ab : b.getUserName()); 317 } 318 loopInfinite.setSelected((s.getMinLoops() == AudioSource.LOOP_CONTINUOUS)); 319 loopMin.setValue(loopInfinite.isSelected() ? 0 : s.getMinLoops()); 320 loopMax.setValue(loopInfinite.isSelected() ? 0 : s.getMaxLoops()); 321 // loopMinDelay.setValue(s.getMinLoopDelay()); 322 // loopMaxDelay.setValue(s.getMaxLoopDelay()); 323 position.setValue(s.getPosition()); 324 positionRelative.setSelected(s.isPositionRelative()); 325 velocity.setValue(s.getVelocity()); 326 gain.setValue(s.getGain()); 327 pitch.setValue(s.getPitch()); 328 refDistance.setValue(s.getReferenceDistance()); 329 maxDistance.setValue(s.getMaximumDistance()); 330 rollOffFactor.setValue(s.getRollOffFactor()); 331 fadeInTime.setValue(s.getFadeIn()); 332 fadeOutTime.setValue(s.getFadeOut()); 333 334 this.newSource = false; 335 } 336 337 public void updateBufferList() { 338 AudioManager am = InstanceManager.getDefault(jmri.AudioManager.class); 339 assignedBuffer.removeAllItems(); 340 assignedBuffer.addItem(Bundle.getMessage("SelectBufferFromList")); 341 am.getNamedBeanSet(Audio.BUFFER).stream().forEach((s) -> { 342 Audio a = am.getAudio(s.getSystemName()); 343 if (a != null) { 344 String u = a.getUserName(); 345 if (u != null) { 346 assignedBuffer.addItem(u); 347 } else { 348 assignedBuffer.addItem(s.getSystemName()); 349 } 350 } else { 351 assignedBuffer.addItem(s.getSystemName()); 352 } 353 }); 354 } 355 356 private boolean applyPressed(ActionEvent e) { 357 String sName = sysName.getText(); 358 if (entryError(sName, PREFIX, "" + counter)) { 359 return false; 360 } 361 String user = userName.getText(); 362 if (user.equals("")) { 363 user = null; 364 } 365 AudioSource s; 366 try { 367 AudioManager am = InstanceManager.getDefault(jmri.AudioManager.class); 368 if (newSource && am.getBySystemName(sName) != null) { 369 throw new AudioException(Bundle.getMessage("DuplicateSystemName")); 370 } 371 try { 372 s = (AudioSource) am.provideAudio(sName); 373 } catch (IllegalArgumentException ex) { 374 throw new AudioException(Bundle.getMessage("ProblemCreatingSource")); 375 } 376 if ((user != null) && (newSource) && (am.getByUserName(user) != null)) { 377 am.deregister(s); 378 synchronized (lock) { 379 prevCounter(); 380 } 381 throw new AudioException(Bundle.getMessage("DuplicateUserName")); 382 } 383 s.setUserName(user); 384 if (assignedBuffer.getSelectedIndex() > 0) { 385 String sel = (String) assignedBuffer.getSelectedItem(); 386 if (sel != null) { 387 Audio a = am.getAudio(sel); 388 if (a != null) { 389 s.setAssignedBuffer(a.getSystemName()); 390 } 391 } 392 } 393 s.setMinLoops(loopInfinite.isSelected() ? AudioSource.LOOP_CONTINUOUS : (Integer) loopMin.getValue()); 394 s.setMaxLoops(loopInfinite.isSelected() ? AudioSource.LOOP_CONTINUOUS : (Integer) loopMax.getValue()); 395 // s.setMinLoopDelay((Integer) loopMinDelay.getValue()); 396 // s.setMaxLoopDelay((Integer) loopMaxDelay.getValue()); 397 s.setPosition(position.getValue()); 398 s.setPositionRelative(positionRelative.isSelected()); 399 s.setVelocity(velocity.getValue()); 400 s.setGain(gain.getValue()); 401 s.setPitch(pitch.getValue()); 402 s.setReferenceDistance((Float) refDistance.getValue()); 403 s.setMaximumDistance((Float) maxDistance.getValue()); 404 s.setRollOffFactor((Float) rollOffFactor.getValue()); 405 s.setFadeIn((Integer) fadeInTime.getValue()); 406 s.setFadeOut((Integer) fadeOutTime.getValue()); 407 408 // Notify changes 409 model.fireTableDataChanged(); 410 } catch (AudioException ex) { 411 JmriJOptionPane.showMessageDialog(this, ex.getMessage(), 412 Bundle.getMessage("AudioCreateErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 413 return false; 414 } 415 newSource = false; // If the user presses Apply, the dialog stays visible. 416 return true; 417 } 418 419 private static int nextCounter() { 420 return counter++; 421 } 422 423 private static void prevCounter() { 424 counter--; 425 } 426 427 //private static final Logger log = LoggerFactory.getLogger(AudioSourceFrame.class); 428 429}