/* * Copyright (c) 2008, 2009, 2010, 2011 Denis Tulskiy * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>. */ package com.tulskiy.musique.gui.dialogs; import com.tulskiy.musique.audio.Converter; import com.tulskiy.musique.playlist.Track; import com.tulskiy.musique.system.Application; import com.tulskiy.musique.system.configuration.Configuration; import com.tulskiy.musique.util.Util; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.*; import java.util.List; /** * Author: Denis Tulskiy * Date: Jul 26, 2010 */ public class ConverterDialog extends JDialog { private Application app = Application.getInstance(); private Configuration config = app.getConfiguration(); private HashMap<String, JComponent> coders = new LinkedHashMap<String, JComponent>(); private HashMap<String, String> formatToCoder = new HashMap<String, String>() {{ put("WAV", "wav"); put("OGG Vorbis", "ogg"); put("WavPack", "wv"); put("Monkey's Audio", "ape"); put("FLAC", "flac"); }}; public ConverterDialog(final JComponent owner, final List<Track> tracks) { super(SwingUtilities.windowForComponent(owner), "Convert Files", ModalityType.MODELESS); setDefaultCloseOperation(DISPOSE_ON_CLOSE); coders.put("WAV", null); coders.put("OGG Vorbis", createVorbisSettings()); coders.put("WavPack", createWavpackSettings()); coders.put("Monkey's Audio", createAPESettings()); coders.put("FLAC", null); final JComboBox encoder = new JComboBox(coders.keySet().toArray()); final Box box = Box.createVerticalBox(); box.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); JPanel format = new JPanel(new BorderLayout()); format.add(encoder, BorderLayout.CENTER); format.setBorder(BorderFactory.createTitledBorder("Format")); final JButton coderSettings = new JButton("..."); final JDialog comp = this; coderSettings.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String key = (String) encoder.getSelectedItem(); showSettingsDialog(key, comp); } }); format.add(coderSettings, BorderLayout.EAST); encoder.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { String key = (String) e.getItem(); coderSettings.setEnabled(coders.get(key) != null); } }); encoder.setSelectedIndex(-1); String codec = config.getString("converter.encoder", "wav"); for (Map.Entry<String, String> entry : formatToCoder.entrySet()) { if (entry.getValue().equals(codec)) encoder.setSelectedItem(entry.getKey()); } JPanel folder = new JPanel(new GridLayout(2, 2)); folder.setBorder(BorderFactory.createTitledBorder("Path")); final JRadioButton pathSource = new JRadioButton("Source track folder"); folder.add(pathSource); boolean b = config.getBoolean("converter.saveToSourceFolder", true); pathSource.setSelected(b); JRadioButton pathSpecify = new JRadioButton("Specify folder"); pathSpecify.setSelected(!b); folder.add(pathSpecify); ButtonGroup g1 = new ButtonGroup(); g1.add(pathSource); g1.add(pathSpecify); folder.add(new JLabel()); final PathChooser path = new PathChooser(config.getString("converter.path", "")); folder.add(path); JPanel output = new JPanel(new GridLayout(3, 2)); output.setBorder(BorderFactory.createTitledBorder("Output files")); output.add(new JLabel("When file exists")); final JComboBox whenExists = new JComboBox(new String[]{"Ask", "Skip", "Overwrite"}); whenExists.setSelectedItem(config.getString("converter.actionWhenExists", "Ask")); output.add(whenExists); output.add(new JLabel("Filename format")); final JTextField fileNameFormat = new JTextField(); fileNameFormat.setText(config.getString("converter.fileNameFormat", "%fileName%")); output.add(fileNameFormat); final JCheckBox merge = new JCheckBox("Merge files into one image"); merge.setSelected(config.getBoolean("converter.merge", false)); output.add(merge); box.add(format); box.add(folder); box.add(output); box.add(Box.createVerticalGlue()); Box buttons = Box.createHorizontalBox(); buttons.add(Box.createHorizontalGlue()); JButton ok = new JButton(" OK "); buttons.add(ok); ok.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String codec = (String) encoder.getSelectedItem(); config.setString("converter.encoder", formatToCoder.get(codec)); config.setBoolean("converter.saveToSourceFolder", pathSource.isSelected()); config.setString("converter.path", path.getPath()); config.setString("converter.actionWhenExists", (String) whenExists.getSelectedItem()); config.setString("converter.fileNameFormat", fileNameFormat.getText()); config.setBoolean("converter.merge", merge.isSelected()); setVisible(false); dispose(); ProgressDialog progress = new ProgressDialog(owner, "Converting files"); progress.show(new ConvertTask(tracks)); } }); buttons.add(Box.createHorizontalStrut(3)); JButton cancel = new JButton("Cancel"); cancel.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { setVisible(false); dispose(); } }); buttons.add(cancel); buttons.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); setLayout(new BorderLayout()); add(box, BorderLayout.NORTH); add(buttons, BorderLayout.PAGE_END); setSize(500, 400); setLocationRelativeTo(SwingUtilities.windowForComponent(owner)); } private void showSettingsDialog(String key, JDialog owner) { final JDialog dialog = new JDialog(owner, true); dialog.setLayout(new BorderLayout()); final JComponent comp = coders.get(key); setTitle(comp.getName() + " Settings"); comp.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); dialog.add(comp, BorderLayout.CENTER); Box buttons = Box.createHorizontalBox(); buttons.add(Box.createHorizontalGlue()); JButton ok = new JButton(" OK "); ok.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { comp.firePropertyChange("accept", 1, 2); dialog.setVisible(false); dialog.dispose(); } }); buttons.add(ok); buttons.add(Box.createHorizontalStrut(3)); JButton cancel = new JButton("Cancel"); cancel.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { dialog.setVisible(false); dialog.dispose(); } }); buttons.add(cancel); buttons.add(Box.createHorizontalStrut(10)); dialog.add(buttons, BorderLayout.PAGE_END); dialog.setSize(500, 250); dialog.setLocationRelativeTo(owner); dialog.setVisible(true); } private JComponent createWavpackSettings() { JPanel panel = new JPanel(new BorderLayout(10, 10)); panel.setName("WavPack Encoder"); JPanel misc = new JPanel(new GridLayout(6, 2)); misc.add(new JLabel("Quality")); final JComboBox quality = new JComboBox(new String[]{ "fast", "normal", "high", "very high" }); quality.setSelectedItem(config.getString("encoder.wavpack.mode", "normal")); misc.add(quality); final JCheckBox hybrid = new JCheckBox("Enable hybrid mode"); misc.add(hybrid); hybrid.setSelected(config.getBoolean("encoder.wavpack.hybrid.enable", false)); final JCheckBox maxHybrid = new JCheckBox("Maximum hybrid compression"); misc.add(maxHybrid); maxHybrid.setSelected(config.getBoolean("encoder.wavpack.hybrid.wvc.optimize", false)); final JCheckBox wvc = new JCheckBox("Create correction file"); wvc.setSelected(config.getBoolean("encoder.wavpack.hybrid.wvc.enabled", false)); misc.add(wvc); misc.add(new JLabel()); misc.add(new JLabel("Bitrate, bits/sample")); final JSpinner bitrate = new JSpinner( new SpinnerNumberModel(4.0, 2.0, 16.0, 0.1)); bitrate.setValue((double) config.getFloat("encoder.wavpack.hybrid.bitrate", 4f)); misc.add(bitrate); misc.add(new JLabel("Noize shape override")); final JSpinner noise = new JSpinner(new SpinnerNumberModel(0, -1.0, 1.0, 0.1)); noise.setValue((double) config.getFloat("encoder.wavpack.hybrid.noiseShape", 0)); misc.add(noise); ItemListener hybridListener = new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { boolean b = hybrid.isSelected(); noise.setEnabled(b); wvc.setEnabled(b); maxHybrid.setEnabled(b); bitrate.setEnabled(b); } }; hybrid.addItemListener(hybridListener); hybridListener.itemStateChanged(null); panel.addPropertyChangeListener("accept", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { config.setString("encoder.wavpack.mode", (String) quality.getSelectedItem()); config.setBoolean("encoder.wavpack.hybrid.enable", hybrid.isSelected()); if (hybrid.isSelected()) { config.setFloat("encoder.wavpack.hybrid.bitrate", ((Number) bitrate.getValue()).floatValue()); config.setBoolean("encoder.wavpack.hybrid.wvc.enabled", wvc.isSelected()); config.setBoolean("encoder.wavpack.hybrid.wvc.optimize", maxHybrid.isSelected()); config.setFloat("encoder.wavpack.hybrid.noiseShape", ((Number) noise.getValue()).floatValue()); } } }); panel.add(misc, BorderLayout.PAGE_START); return panel; } private JComponent createVorbisSettings() { JPanel panel = new JPanel(new BorderLayout()); panel.setName("Ogg Vorbis Encoder"); panel.add(new JLabel("Quality"), BorderLayout.PAGE_START); final JSlider slider = new JSlider(-1, 10); slider.setMajorTickSpacing(1); slider.setPaintLabels(true); slider.setPaintTicks(true); slider.setSnapToTicks(true); slider.setValue((int) (config.getFloat("encoder.vorbis.quality", .3f) * 10)); Box box = Box.createVerticalBox(); box.add(slider); box.add(Box.createVerticalGlue()); panel.add(box, BorderLayout.CENTER); panel.addPropertyChangeListener("accept", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { config.setFloat("encoder.vorbis.quality", slider.getValue() / 10f); } }); return panel; } private JComponent createAPESettings() { JPanel panel = new JPanel(new BorderLayout()); panel.setName("Monkey's Audio Encoder"); panel.add(new JLabel("Compression Level"), BorderLayout.PAGE_START); final JSlider slider = new JSlider(1, 5); slider.setMajorTickSpacing(1); slider.setPaintLabels(true); slider.setPaintTicks(true); slider.setSnapToTicks(true); Hashtable<Integer, JLabel> labels = new Hashtable<Integer, JLabel>(); labels.put(1, new JLabel("Fast")); labels.put(2, new JLabel("Normal")); labels.put(3, new JLabel("High")); labels.put(4, new JLabel("Extra High")); labels.put(5, new JLabel("Insane")); slider.setLabelTable(labels); slider.setValue(config.getInt("encoder.ape.level", 2000) / 1000); Box box = Box.createVerticalBox(); box.add(slider); box.add(Box.createVerticalGlue()); panel.add(box, BorderLayout.CENTER); panel.addPropertyChangeListener("accept", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { config.setInt("encoder.ape.level", slider.getValue() * 1000); } }); return panel; } class ConvertTask extends Task { Converter converter = new Converter(); private List<Track> tracks; private Formatter formatter; private StringBuilder sb; ConvertTask(List<Track> tracks) { this.tracks = tracks; sb = new StringBuilder(); formatter = new Formatter(sb); } @Override public String getStatus() { try { Track track = converter.getTrack(); String status = "Input: "; if (track.getTrackData().isFile()) status += track.getTrackData().getFile().getAbsolutePath(); else status += track.getTrackData().getLocation().toString(); status += "\nOutput: "; status += converter.getOutput().getAbsolutePath(); return status; } catch (Exception ignored) { } return null; } @Override public void abort() { converter.stop(); } @Override public void start() { converter.convert(tracks); } @Override public boolean isIndeterminate() { return converter.getTotalSamples() < 0; } @Override public float getProgress() { return (float) converter.getCurrentSample() / converter.getTotalSamples(); } @Override public String getTitle() { String elapsed = Util.formatSeconds( converter.getElapsed() / 1000f, 0); String estimated = Util.samplesToTime( (long) converter.getEstimated(), converter.getTrack().getTrackData().getSampleRate(), 0); sb.setLength(0); formatter.format("Converting. Elapsed: %s Estimated: %s Speed: %.2fx", elapsed, estimated, converter.getSpeed()); return sb.toString(); } } }