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