/*
* Copyright (C) 2007, 2011 IsmAvatar <IsmAvatar@gmail.com>
* Copyright (C) 2007, 2008, 2009 Quadduc <quadduc@gmail.com>
* Copyright (C) 2014, Robert B. Colton
*
* This file is part of LateralGM.
* LateralGM is free software and comes with ABSOLUTELY NO WARRANTY.
* See LICENSE for details.
*/
package org.lateralgm.subframes;
import static javax.swing.GroupLayout.PREFERRED_SIZE;
import java.awt.BorderLayout;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.GroupLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JToolBar;
import javax.swing.SwingConstants;
import javax.swing.GroupLayout.Alignment;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.lateralgm.components.CustomFileChooser;
import org.lateralgm.components.impl.CustomFileFilter;
import org.lateralgm.components.impl.ResNode;
import org.lateralgm.file.FileChangeMonitor;
import org.lateralgm.file.ProjectFile;
import org.lateralgm.file.FileChangeMonitor.FileUpdateEvent;
import org.lateralgm.main.LGM;
import org.lateralgm.main.Prefs;
import org.lateralgm.main.UpdateSource.UpdateEvent;
import org.lateralgm.main.UpdateSource.UpdateListener;
import org.lateralgm.main.Util;
import org.lateralgm.messages.Messages;
import org.lateralgm.resources.Sound;
import org.lateralgm.resources.Sound.PSound;
import org.lateralgm.resources.Sound.SoundKind;
import org.lateralgm.resources.Sound.SoundType;
import org.lateralgm.ui.swing.propertylink.ComboBoxLink.DefaultComboBoxConversion;
import org.lateralgm.ui.swing.propertylink.ComboBoxLink.KeyComboBoxConversion;
import org.lateralgm.ui.swing.util.SwingExecutor;
public class SoundFrame extends InstantiableResourceFrame<Sound,PSound>
{
private static final long serialVersionUID = 1L;
private static final ImageIcon LOAD_ICON = LGM.getIconForKey("SoundFrame.LOAD"); //$NON-NLS-1$
private static final ImageIcon PLAY_ICON = LGM.getIconForKey("SoundFrame.PLAY"); //$NON-NLS-1$
private static final ImageIcon STOP_ICON = LGM.getIconForKey("SoundFrame.STOP"); //$NON-NLS-1$
private static final ImageIcon STORE_ICON = LGM.getIconForKey("SoundFrame.STORE"); //$NON-NLS-1$
private static final ImageIcon EDIT_ICON = LGM.getIconForKey("SoundFrame.EDIT"); //$NON-NLS-1$
public JButton load;
public JButton play;
public JButton stop;
public JButton store;
public JSlider volume;
public JSlider pan;
public JButton center;
public JCheckBox preload;
public JButton edit;
public byte[] data;
public boolean modified = false;
private CustomFileChooser fc = new CustomFileChooser("/org/lateralgm","LAST_SOUND_DIR");
private SoundEditor editor;
private Clip clip;
private JLabel statusLabel, lPosition;
private JPanel statusBar;
//private JSlider pitch;
private JSlider position;
public String formatTime(long duration)
{
String formated = String.format(
"%dm%ds",
TimeUnit.MICROSECONDS.toMinutes(duration),
TimeUnit.MICROSECONDS.toSeconds(duration)
- TimeUnit.MINUTES.toSeconds(TimeUnit.MICROSECONDS.toMinutes(duration)));
return formated;
}
public SoundFrame(Sound res, ResNode node)
{
super(res,node);
setLayout(new BorderLayout());
statusBar = makeStatusBar();
add(statusBar,BorderLayout.SOUTH);
add(makeToolBar(),BorderLayout.NORTH);
JPanel content = new JPanel();
add(content,BorderLayout.CENTER);
GroupLayout layout = new GroupLayout(content);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
content.setLayout(layout);
String s[] = { ".ogg",".wav",".mid",".mp3",".mod",".xm",".s3m",".it",".nfs",".gfs",".minigfs",
".flac" };
String[] d = { Messages.getString("SoundFrame.FORMAT_SOUND"), //$NON-NLS-1$
Messages.getString("SoundFrame.FORMAT_OGG"), //$NON-NLS-1$
Messages.getString("SoundFrame.FORMAT_WAV"), //$NON-NLS-1$
Messages.getString("SoundFrame.FORMAT_MID"), //$NON-NLS-1$
Messages.getString("SoundFrame.FORMAT_MP3") }; //$NON-NLS-1$
CustomFileFilter soundsFilter = new CustomFileFilter(d[0],s);
fc.addChoosableFileFilter(soundsFilter);
fc.addChoosableFileFilter(new CustomFileFilter(d[1],s[0]));
fc.addChoosableFileFilter(new CustomFileFilter(d[2],s[1]));
fc.addChoosableFileFilter(new CustomFileFilter(d[3],s[2]));
fc.addChoosableFileFilter(new CustomFileFilter(d[4],s[3]));
fc.setFileFilter(soundsFilter);
JPanel pKind = makeAttributesPane();
JPanel pEffects = makeEffectsPane();
JPanel pAttr = makeFormatPane();
final JLabel lVolume = new JLabel(Messages.getString("SoundFrame.VOLUME") + ": 100"); //$NON-NLS-1$
volume = new JSlider(0,100,100);
//volume.setPaintLabels(true);
volume.setMajorTickSpacing(10);
volume.setPaintTicks(true);
volume.setSize(new Dimension(50,50));
volume.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent ev)
{
lVolume.setText(Messages.getString("SoundFrame.VOLUME") + ": " + volume.getValue());
}
});
plf.make(volume.getModel(),PSound.VOLUME,100.0);
final JLabel lPan = new JLabel(Messages.getString("SoundFrame.PAN") + ": 0"); //$NON-NLS-1$
pan = new JSlider(-100,100,0);
//pan.setPaintLabels(true);
pan.setMajorTickSpacing(20);
pan.setPaintTicks(true);
pan.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent ev)
{
lPan.setText(Messages.getString("SoundFrame.PAN") + ": " + pan.getValue());
}
});
plf.make(pan.getModel(),PSound.PAN,100.0);
lPosition = new JLabel(Messages.getString("SoundFrame.DURATION") + ": 0m0s | "
+ Messages.getString("SoundFrame.POSITION") + ": 0m0s"); //$NON-NLS-1$
position = new JSlider(0,100,0);
//pan.setPaintLabels(true);
position.setMajorTickSpacing(10);
position.setMinorTickSpacing(2);
position.setPaintTicks(true);
position.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent ev)
{
if (clip != null && position.getValueIsAdjusting())
{
clip.setFramePosition(
(int) (((float) position.getValue() / 100) * clip.getFrameLength()));
}
updatePositionLabel();
}
});
/* TODO: Not sure if this button is needed since I added the label listener
* and you can tell when you set it to 0.
*/
/*
center = new JButton(Messages.getString("SoundFrame.PAN_CENTER")); //$NON-NLS-1$
center.addActionListener(this);
center.setPreferredSize(edit.getSize());
*/
data = res.data;
loadClip();
layout.setHorizontalGroup(layout.createParallelGroup()
/**/.addGroup(layout.createSequentialGroup()
/* */.addComponent(lPosition))
/* */.addComponent(position).addGroup(layout.createSequentialGroup()
/* */.addComponent(lVolume))
/* */.addComponent(volume).addGroup(layout.createSequentialGroup()
/* */.addComponent(lPan))
/* */.addComponent(pan).addGroup(layout.createSequentialGroup())
/**/.addGroup(layout.createSequentialGroup()
/* */.addComponent(pKind)
/* */.addComponent(pEffects)
/* */.addComponent(pAttr,PREFERRED_SIZE,PREFERRED_SIZE,PREFERRED_SIZE)));
layout.setVerticalGroup(layout.createSequentialGroup()
/**/.addGroup(layout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(lPosition).addGap(0))
/* */.addComponent(position).addGroup(layout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(lVolume).addGap(0))
/* */.addComponent(volume).addGroup(layout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(lPan).addGap(0))
/* */.addComponent(pan)
/**/.addGroup(layout.createParallelGroup(Alignment.CENTER)
/* */.addComponent(pKind)
/* */.addComponent(pEffects)
/* */.addComponent(pAttr)));
layout.linkSize(SwingConstants.VERTICAL,pEffects,pAttr);
pack();
}
private JToolBar makeToolBar()
{
JToolBar tool = new JToolBar();
tool.setFloatable(false);
tool.add(save);
tool.addSeparator();
load = new JButton(LOAD_ICON);
load.setToolTipText(Messages.getString("SoundFrame.LOAD")); //$NON-NLS-1$
load.addActionListener(this);
tool.add(load);
store = new JButton(STORE_ICON);
store.setToolTipText(Messages.getString("SoundFrame.STORE")); //$NON-NLS-1$
store.addActionListener(this);
tool.add(store);
tool.addSeparator();
edit = new JButton(EDIT_ICON); //$NON-NLS-1$
edit.setToolTipText(Messages.getString("SoundFrame.EDIT"));
edit.addActionListener(this);
tool.add(edit);
play = new JButton(PLAY_ICON);
play.setToolTipText(Messages.getString("SoundFrame.PLAY"));
play.addActionListener(this);
play.setEnabled(false);
tool.add(play);
stop = new JButton(STOP_ICON);
stop.setToolTipText(Messages.getString("SoundFrame.STOP"));
stop.addActionListener(this);
stop.setEnabled(false);
tool.add(stop);
tool.addSeparator();
preload = new JCheckBox(Messages.getString("SoundFrame.PRELOAD")); //$NON-NLS-1$
preload.setOpaque(false);
plf.make(preload,PSound.PRELOAD);
tool.add(preload);
tool.addSeparator();
name.setColumns(13);
name.setMaximumSize(name.getPreferredSize());
tool.add(new JLabel(Messages.getString("SoundFrame.NAME"))); //$NON-NLS-1$
tool.add(name);
return tool;
}
private JPanel makeAttributesPane()
{
JPanel pAttr = new JPanel();
// The options must be added in the order corresponding to Sound.SoundKind
final String kindOptions[] = { Messages.getString("SoundFrame.NORMAL"),
Messages.getString("SoundFrame.BACKGROUND"),Messages.getString("SoundFrame.THREE"),
Messages.getString("SoundFrame.MULT") };
JComboBox<String> kindCombo = new JComboBox<String>(kindOptions);
plf.make(kindCombo,PSound.KIND,new KeyComboBoxConversion<SoundKind>(ProjectFile.SOUND_KIND,
ProjectFile.SOUND_KIND_CODE));
JLabel kindLabel = new JLabel(Messages.getString("SoundFrame.KIND"));
JCheckBox compressedCB = new JCheckBox(Messages.getString("SoundFrame.COMPRESSED"));
plf.make(compressedCB,PSound.COMPRESSED);
JCheckBox streamedCB = new JCheckBox(Messages.getString("SoundFrame.STREAMED"));
plf.make(streamedCB,PSound.STREAMED);
JCheckBox decompressCB = new JCheckBox(Messages.getString("SoundFrame.DECOMPRESS"));
plf.make(decompressCB,PSound.DECOMPRESS_ON_LOAD);
GroupLayout aLayout = new GroupLayout(pAttr);
pAttr.setLayout(aLayout);
aLayout.setHorizontalGroup(aLayout.createParallelGroup()
/**/.addGroup(aLayout.createSequentialGroup()
/* */.addComponent(kindLabel)
/* */.addComponent(kindCombo,PREFERRED_SIZE,PREFERRED_SIZE,PREFERRED_SIZE))
/**/.addComponent(compressedCB)
/**/.addComponent(streamedCB)
/**/.addComponent(decompressCB));
aLayout.setVerticalGroup(aLayout.createSequentialGroup()
/**/.addGroup(aLayout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(kindLabel)
/* */.addComponent(kindCombo))
/**/.addComponent(compressedCB)
/**/.addComponent(streamedCB)
/**/.addComponent(decompressCB));
return pAttr;
}
private JPanel makeFormatPane()
{
JPanel pFormat = new JPanel();
final String typeOptions[] = { Messages.getString("SoundFrame.MONO"),
Messages.getString("SoundFrame.STEREO"),Messages.getString("SoundFrame.THREE") };
JComboBox<String> typeCombo = new JComboBox<String>(typeOptions);
plf.make(typeCombo,PSound.TYPE,new KeyComboBoxConversion<SoundType>(ProjectFile.SOUND_TYPE,
ProjectFile.SOUND_TYPE_CODE));
final Integer depthOptions[] = { 8, 16 };
JComboBox<Integer> depthCombo = new JComboBox<Integer>(depthOptions);
plf.make(depthCombo,PSound.BIT_DEPTH,new DefaultComboBoxConversion<Integer>());
final Integer sampleOptions[] = { 5512,11025,22050,32000,44100,48000 };
JComboBox<Integer> sampleCombo = new JComboBox<Integer>(sampleOptions);
plf.make(sampleCombo,PSound.SAMPLE_RATE,new DefaultComboBoxConversion<Integer>());
JLabel sampleLabel = new JLabel(Messages.getString("SoundFrame.SAMPLERATE"));
ArrayList<Integer> bitOptions = new ArrayList<Integer>();
for (int i = 8; i <= 512; i += 8 * Math.floor(Math.log(i) / Math.log(8)))
{
bitOptions.add(i);
}
JComboBox<Integer> bitCombo = new JComboBox<Integer>(
bitOptions.toArray(new Integer[bitOptions.size()]));
plf.make(bitCombo,PSound.BIT_RATE,new DefaultComboBoxConversion<Integer>());
JLabel bitLabel = new JLabel(Messages.getString("SoundFrame.BITRATE"));
GroupLayout aLayout = new GroupLayout(pFormat);
aLayout.setAutoCreateGaps(true);
pFormat.setLayout(aLayout);
pFormat.setBorder(BorderFactory.createTitledBorder(Messages.getString("SoundFrame.FORMAT")));
aLayout.setHorizontalGroup(aLayout.createSequentialGroup()
/**/.addGroup(aLayout.createParallelGroup()
/* */.addComponent(typeCombo)
/* */.addComponent(depthCombo))
/**/.addGroup(aLayout.createParallelGroup(Alignment.TRAILING)
/* */.addComponent(sampleLabel)
/* */.addComponent(bitLabel))
/**/.addGroup(aLayout.createParallelGroup()
/* */.addComponent(sampleCombo)
/* */.addComponent(bitCombo)));
aLayout.setVerticalGroup(aLayout.createSequentialGroup()
/**/.addGroup(aLayout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(typeCombo)
/* */.addComponent(sampleLabel)
/* */.addComponent(sampleCombo))
/**/.addGroup(aLayout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(depthCombo)
/* */.addComponent(bitLabel)
/* */.addComponent(bitCombo)));
return pFormat;
}
private JPanel makeEffectsPane()
{
// these are in bit order as appears in a GM6 file, not the same as GM shows them
//effects = new IndexButtonGroup(5,false);
AbstractButton eChorus = new JCheckBox(Messages.getString("SoundFrame.CHORUS")); //$NON-NLS-1$
plf.make(eChorus,PSound.CHORUS);
AbstractButton eEcho = new JCheckBox(Messages.getString("SoundFrame.ECHO")); //$NON-NLS-1$
plf.make(eEcho,PSound.ECHO);
AbstractButton eFlanger = new JCheckBox(Messages.getString("SoundFrame.FLANGER")); //$NON-NLS-1$
plf.make(eFlanger,PSound.FLANGER);
AbstractButton eGargle = new JCheckBox(Messages.getString("SoundFrame.GARGLE")); //$NON-NLS-1$
plf.make(eGargle,PSound.GARGLE);
AbstractButton eReverb = new JCheckBox(Messages.getString("SoundFrame.REVERB")); //$NON-NLS-1$
plf.make(eReverb,PSound.REVERB);
JPanel pEffects = new JPanel();
GroupLayout eLayout = new GroupLayout(pEffects);
pEffects.setLayout(eLayout);
pEffects.setBorder(BorderFactory.createTitledBorder(Messages.getString("SoundFrame.EFFECTS")));
eLayout.setHorizontalGroup(eLayout.createSequentialGroup()
/**/.addGroup(eLayout.createParallelGroup()
/* */.addComponent(eChorus)
/* */.addComponent(eFlanger)
/* */.addComponent(eReverb))
/**/.addGroup(eLayout.createParallelGroup()
/* */.addComponent(eEcho)
/* */.addComponent(eGargle)));
eLayout.setVerticalGroup(eLayout.createSequentialGroup()
/**/.addGroup(eLayout.createParallelGroup()
/* */.addComponent(eChorus)
/* */.addComponent(eEcho))
/**/.addGroup(eLayout.createParallelGroup()
/* */.addComponent(eFlanger)
/* */.addComponent(eGargle))
/**/.addComponent(eReverb));
return pEffects;
}
private void updatePositionLabel()
{
long length = 0, position = 0;
if (clip != null)
{
length = clip.getMicrosecondLength();
position = clip.getMicrosecondPosition();
}
lPosition.setText(Messages.getString("SoundFrame.DURATION") + ": " + formatTime(length)
+ " | " + Messages.getString("SoundFrame.POSITION") + ": " + formatTime(position));
}
protected boolean areResourceFieldsEqual()
{
return !modified;
}
public void commitChanges()
{
res.setName(name.getText());
res.data = data;
}
public void updateResource(boolean commit)
{
super.updateResource(commit);
modified = false;
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == load)
{
File f;
while (true)
{
if (fc.showOpenDialog(LGM.frame) != JFileChooser.APPROVE_OPTION) return;
f = fc.getSelectedFile();
if (f.exists()) break;
JOptionPane.showMessageDialog(fc,
f.getName() + Messages.getString("SoundFrame.FILE_MISSING"), //$NON-NLS-1$
Messages.getString("SoundFrame.FILE_OPEN"),JOptionPane.WARNING_MESSAGE); //$NON-NLS-1$
}
try
{
data = fileToBytes(f);
//loadClip();
String fn = f.getName();
String extension = "";
int i = fn.lastIndexOf('.');
if (i > 0)
{
extension = fn.substring(i + 1);
}
// Set multi-media player for mp3's like Game Maker 8.1 one did for DirectSound
// is ignored in OpenAL anyway so it don't matter.
if (extension.toLowerCase().equals("mp3"))
{
res.put(PSound.KIND,Sound.SoundKind.MULTIMEDIA);
}
res.put(PSound.FILE_NAME,fn);
String ft = CustomFileFilter.getExtension(fn);
if (ft == null) ft = "";
res.put(PSound.FILE_TYPE,ft);
updateStatusLabel();
if (clip != null && clip.isOpen())
{
clip.stop();
clip.close();
clip.flush();
}
clip = null;
loadClip();
}
catch (Exception ex)
{
ex.printStackTrace();
}
modified = true;
cleanup();
return;
}
if (e.getSource() == play)
{
if (data == null || data.length == 0) return;
if (clip == null)
{
loadClip();
}
play.setEnabled(false);
stop.setEnabled(true);
clip.setFramePosition((int) (((float) position.getValue() / 100) * clip.getFrameLength()));
clip.start();
new Thread()
{
public void run()
{
LGM.addDefaultExceptionHandler();
while (clip != null && clip.isActive())
{
float pos = (float) clip.getLongFramePosition() / (float) clip.getFrameLength();
position.setValue((int) (pos * position.getMaximum()));
try
{
Thread.sleep(50);
}
catch (InterruptedException e)
{
if (clip.isOpen()) clip.stop();
break;
}
}
if (clip != null) {
clip.setFramePosition(0);
play.setEnabled(true);
}
position.setValue(0);
stop.setEnabled(false);
}
}.start();
return;
}
if (e.getSource() == stop)
{
if (clip != null && clip.isOpen()) clip.stop();
return;
}
if (e.getSource() == store)
{
if (fc.showSaveDialog(LGM.frame) != JFileChooser.APPROVE_OPTION) return;
try
{
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(
fc.getSelectedFile()));
out.write(data);
out.close();
}
catch (IOException ex)
{
ex.printStackTrace();
}
return;
}
if (e.getSource() == edit)
{
try
{
if (editor == null)
new SoundEditor();
else
editor.start();
}
catch (IOException ex)
{
ex.printStackTrace();
}
return;
}
if (e.getSource() == center)
{
pan.setValue(0);
return;
}
super.actionPerformed(e);
}
public static byte[] fileToBytes(File f) throws IOException
{
InputStream in = null;
try
{
return Util.readFully(in = new FileInputStream(f)).toByteArray();
}
finally
{
if (in != null)
{
try
{
in.close();
}
catch (IOException ioe)
{
LGM.showDefaultExceptionHandler(ioe);
}
}
}
}
private JPanel makeStatusBar()
{
JPanel status = new JPanel(new FlowLayout());
BoxLayout layout = new BoxLayout(status,BoxLayout.X_AXIS);
status.setLayout(layout);
status.setMaximumSize(new Dimension(Integer.MAX_VALUE,11));
statusLabel = new JLabel();
status.add(statusLabel);
updateStatusLabel();
return status;
}
public void loadClip()
{
play.setEnabled(false);
if (data == null || data.length <= 0)
{
return;
}
try
{
InputStream source = new ByteArrayInputStream(data);
AudioInputStream ais = AudioSystem.getAudioInputStream(new BufferedInputStream(source));
AudioFormat fmt = ais.getFormat();
//Forcibly convert to PCM Signed because non-pulse can't play unsigned (Java bug)
if (fmt.getEncoding() != AudioFormat.Encoding.PCM_SIGNED)
{
fmt = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,fmt.getSampleRate(),
fmt.getSampleSizeInBits() * 2,fmt.getChannels(),fmt.getFrameSize() * 2,
fmt.getFrameRate(),true);
ais = AudioSystem.getAudioInputStream(fmt,ais);
}
//Clip c = AudioSystem.getClip() generates a bogus format instead of using ais.getFormat.
clip = (Clip) AudioSystem.getLine(new DataLine.Info(Clip.class,fmt));
clip.open(ais);
play.setEnabled(true);
}
catch (IOException e)
{
LGM.showDefaultExceptionHandler(e);
}
catch (LineUnavailableException e)
{
LGM.showDefaultExceptionHandler(e);
}
catch (UnsupportedAudioFileException e)
{
// do nothing, file was unsupported
}
updatePositionLabel();
}
private void updateStatusLabel()
{
String stat = " " + Messages.getString("SoundFrame.FILENAME") + ": "
+ res.get(PSound.FILE_NAME) + " | " + Messages.getString("SoundFrame.MEMORY") + ": ";
if (res.data != null && res.data.length != 0)
{
stat += Util.formatDataSize(res.data.length);
}
else if (data != null)
{
stat += Util.formatDataSize(data.length);
}
else
{
stat += Util.formatDataSize(0);
}
statusLabel.setText(stat);
}
private class SoundEditor implements UpdateListener
{
public final FileChangeMonitor monitor;
public SoundEditor() throws IOException,UnsupportedOperationException
{
File f = File.createTempFile(res.getName(),
new File((String) res.get(PSound.FILE_NAME)).getName(),LGM.tempDir);
f.deleteOnExit();
FileOutputStream out = null;
try
{
out = new FileOutputStream(f);
out.write(data);
}
finally
{
if (out != null)
{
out.close();
}
}
monitor = new FileChangeMonitor(f,SwingExecutor.INSTANCE);
monitor.updateSource.addListener(this);
editor = this;
start();
}
public void start() throws IOException
{
if (!Prefs.useExternalSoundEditor || Prefs.externalSoundEditorCommand == null)
try
{
Desktop.getDesktop().edit(monitor.file);
}
catch (UnsupportedOperationException e)
{
throw new UnsupportedOperationException("no internal or system sound editor",e);
}
catch (IOException e)
{
e.printStackTrace();
}
else
Runtime.getRuntime().exec(
String.format(Prefs.externalSoundEditorCommand,monitor.file.getAbsolutePath()));
}
public void stop()
{
monitor.stop();
monitor.file.delete();
editor = null;
}
public void updated(UpdateEvent e)
{
if (!(e instanceof FileUpdateEvent)) return;
switch (((FileUpdateEvent) e).flag)
{
case CHANGED:
try
{
data = fileToBytes(monitor.file);
updateStatusLabel();
}
catch (IOException ioe)
{
LGM.showDefaultExceptionHandler(ioe);
return;
}
modified = true;
break;
case DELETED:
editor = null;
}
}
}
public void dispose()
{
cleanup();
super.dispose();
}
protected void cleanup()
{
if (editor != null) editor.stop();
if (clip != null && clip.isOpen())
{
clip.stop();
clip.close();
}
clip = null;
}
}