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}