/* * Copyright 2004-2010 Information & Software Engineering Group (188/1) * Institute of Software Technology and Interactive Systems * Vienna University of Technology, Austria * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.ifs.tuwien.ac.at/dm/somtoolbox/license.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package at.tuwien.ifs.somtoolbox.apps.viewer.controls; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Properties; import java.util.StringTokenizer; import java.util.Vector; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.Mixer; import javax.sound.sampled.SourceDataLine; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.JTextPane; import javax.swing.JToggleButton; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableModel; import at.tuwien.ifs.somtoolbox.apps.viewer.CommonSOMViewerStateData; import at.tuwien.ifs.somtoolbox.apps.viewer.GeneralUnitPNode; import at.tuwien.ifs.somtoolbox.apps.viewer.MapPNode; import at.tuwien.ifs.somtoolbox.apps.viewer.SOMPane; import at.tuwien.ifs.somtoolbox.apps.viewer.controls.multichannelPlayback.ActivityGridModel; import at.tuwien.ifs.somtoolbox.apps.viewer.controls.multichannelPlayback.Commons; import at.tuwien.ifs.somtoolbox.apps.viewer.controls.multichannelPlayback.ControlFrame; import at.tuwien.ifs.somtoolbox.apps.viewer.controls.multichannelPlayback.FindMeLoopThread; import at.tuwien.ifs.somtoolbox.apps.viewer.controls.multichannelPlayback.LayoutTable; import at.tuwien.ifs.somtoolbox.apps.viewer.controls.multichannelPlayback.LineListModel; import at.tuwien.ifs.somtoolbox.apps.viewer.controls.multichannelPlayback.PlaybackThread; import at.tuwien.ifs.somtoolbox.apps.viewer.controls.multichannelPlayback.TPlaybackThreadDataRecord; import at.tuwien.ifs.somtoolbox.layers.Unit; import at.tuwien.ifs.somtoolbox.models.GrowingSOM; /** * @author Ewald Peiszer * @version $Id: MultichannelPlaybackPanel.java 3877 2010-11-02 15:43:17Z frank $ */ @SuppressWarnings({ "rawtypes", "unchecked" }) public class MultichannelPlaybackPanel extends AbstractSelectionPanel implements ActionListener, ListSelectionListener { private static final long serialVersionUID = 1L; // JB-Stuff BorderLayout borderLayout1 = new BorderLayout(); JTabbedPane jTabbedPane1 = new JTabbedPane(); JPanel jPanel1 = new JPanel(); // JPanel jPanel2 = new JPanel(); GridBagLayout gridBagLayout1 = new GridBagLayout(); GridBagLayout gridBagLayout2 = new GridBagLayout(); JList li_lines = new JList(); JScrollPane jScrollPane1 = new JScrollPane(); JToggleButton btn_findme_loop = new JToggleButton(); JButton btn_findme = new JButton(); JLabel jLabel3 = new JLabel(); JPanel pnl_layout_table_gen = new JPanel(); JTextField txt_table_x = new JTextField(); JLabel jLabel4 = new JLabel(); JTextField txt_table_y = new JTextField(); JLabel jLabel5 = new JLabel(); JButton btn_gen = new JButton(); JScrollPane jScrollPane2 = new JScrollPane(); JButton btn_autoassign = new JButton(); JButton btn_start = new JButton(); JLabel jLabel6 = new JLabel(); JScrollPane jScrollPane3 = new JScrollPane(); JTextPane tp_help = new JTextPane(); JButton btn_load = new JButton(); JButton btn_save = new JButton(); // dummyInit stuff (used if Java version insufficient JTextPane tpHint = new JTextPane(); // my stuff LayoutTable tb_layout = null; DefaultTableModel tbmod_layout = null; /** * Stores the assignments for each cell of the layout table. * <p> * The vector contains <code>GeneralUnitPNode</code>s */ Vector[][] aavGPNs = null; boolean bAssignmentChanged = false; protected SOMPane somPane; protected MapPNode map; protected GrowingSOM gsom; protected FindMeLoopThread fmlt; /** * If true, changes of the selection in the SOMPane will be ignored. * <p> * Used if selection is programmatically removed (because anotehr cell in layout table is being selected; then the * assigment should not be changed. */ private boolean bIgnoreNextSelectionChange = false; /** Is created and filled when parsing layout table */ protected TPlaybackThreadDataRecord[] aPtdata; protected LinkedHashMap<String, PlaybackThread> lhmThreads = new LinkedHashMap<String, PlaybackThread>(); protected ActivityGridModel actmod; // public MultichannelMp3Panel() { // // } public MultichannelPlaybackPanel(CommonSOMViewerStateData state, SOMPane somPane) { super(new GridBagLayout(), state, Commons.APP_NAME); this.somPane = somPane; this.map = somPane.getMap(); this.gsom = map.getGsom(); try { String vers = System.getProperty("java.version"); // Test, ob Java 1.5 oder 1.6 verwendet wird // (ich bin zu faul, um jetzt ein allgemiene Abfrage > 1.4 zu schreiben) if (!vers.startsWith("1.5") && !vers.startsWith("1.6")) { Commons.log.info("You are using Java version " + vers + "..."); Commons.log.warning("... not sufficient. You need at least 1.5 (5.0)"); JOptionPane.showMessageDialog(null, Commons.APP_NAME + " needs at least Java Version 1.5 (5.0)", "Wrong Java version", JOptionPane.ERROR_MESSAGE); dummyInit(); } else { jbInit(); myInit(); } setVisible(true); } catch (Exception e) { e.printStackTrace(); } } private void dummyInit() throws Exception { tpHint.setContentType("text/html"); tpHint.setText("<p style='color:#aa0000; font-family:Arial'><b>" + Commons.APP_NAME + "</b> needs at least Java Version 1.5 (5.0). " + "<br><br>If you would like to use this panel please start SOMViewer again using a more recent JVM.</p>"); tpHint.setEditable(false); this.getContentPane().setLayout(borderLayout1); this.getContentPane().add(tpHint, BorderLayout.CENTER); } private void jbInit() throws Exception { btn_start.setEnabled(false); btn_start.setMaximumSize(new Dimension(123, 27)); btn_start.setMinimumSize(new Dimension(123, 27)); btn_start.setPreferredSize(new Dimension(123, 60)); btn_start.setToolTipText("Open Control Dialog and start playback"); this.getContentPane().setLayout(borderLayout1); jPanel1.setLayout(gridBagLayout1); // jPanel2.setLayout(gridBagLayout2); btn_findme_loop.setMaximumSize(new Dimension(2147483647, 45)); btn_findme_loop.setMinimumSize(new Dimension(110, 27)); btn_findme_loop.setPreferredSize(new Dimension(85, 27)); btn_findme_loop.setToolTipText("Plays a short soundclip to each of your output lines; one after another, " + "until you press the button a second time."); btn_findme_loop.setActionCommand("findmeloop"); btn_findme_loop.setMargin(new Insets(2, 2, 2, 2)); btn_findme_loop.setMnemonic('L'); btn_findme_loop.setText("Find me (loop)"); btn_findme.setText("Find me"); btn_findme.setMaximumSize(new Dimension(2147483647, 45)); btn_findme.setMinimumSize(new Dimension(110, 27)); btn_findme.setPreferredSize(new Dimension(85, 27)); btn_findme.setToolTipText("Plays a short soundclip to the selected output line."); btn_findme.setActionCommand("findme"); btn_findme.setMargin(new Insets(2, 2, 2, 2)); btn_findme.setMnemonic('F'); jLabel3.setLabelFor(jScrollPane2); jLabel3.setText("Layout table:"); pnl_layout_table_gen.setMinimumSize(new Dimension(220, 30)); pnl_layout_table_gen.setPreferredSize(new Dimension(220, 30)); txt_table_x.setMinimumSize(new Dimension(22, 24)); txt_table_x.setPreferredSize(new Dimension(22, 24)); txt_table_x.setToolTipText("Number of columns for layout table"); txt_table_x.setText("55"); jLabel4.setLabelFor(txt_table_x); jLabel4.setText("columns, "); txt_table_y.setMinimumSize(new Dimension(22, 24)); txt_table_y.setPreferredSize(new Dimension(22, 24)); txt_table_y.setToolTipText("Number of rows for layout table"); txt_table_y.setText("5"); jLabel5.setLabelFor(txt_table_y); jLabel5.setText("rows"); btn_gen.setMaximumSize(new Dimension(50, 24)); btn_gen.setMinimumSize(new Dimension(50, 24)); btn_gen.setPreferredSize(new Dimension(50, 24)); btn_gen.setToolTipText("<html>Generate new layout table<br><b>Note: the current assignment " + "will be lost!</b></html>"); btn_gen.setMargin(new Insets(2, 2, 2, 2)); btn_gen.setMnemonic('G'); btn_gen.setText("Gen!"); jScrollPane1.setBorder(null); jScrollPane1.setDebugGraphicsOptions(0); jScrollPane1.setMinimumSize(new Dimension(200, 100)); jScrollPane1.setPreferredSize(new Dimension(220, 200)); li_lines.setToolTipText("These are the sound output lines that are currently available on " + "your system."); li_lines.setMinimumSize(new Dimension(0, 0)); jScrollPane2.setBorder(null); jScrollPane2.setMinimumSize(new Dimension(200, 100)); jScrollPane2.setToolTipText("<html>One ore more units of the SOM should be assigned to each cell " + "of this layout table<p>Use Ctrl-Left-mouse-button to deselect a selected " + "table cell.</p></html>"); btn_autoassign.setEnabled(false); btn_autoassign.setToolTipText("<html>Tries to find a optimal assignment regarding the current SOM.<p>\u000B" + "Note: the current assignment will be lost!</p></html>"); btn_autoassign.setActionCommand("autoassign"); btn_autoassign.setMnemonic('C'); btn_autoassign.setText("Automatic assignment"); btn_start.setActionCommand("start"); btn_start.setMnemonic('P'); btn_start.setText("Start Playback!"); jLabel6.setLabelFor(li_lines); jLabel6.setText("Available output lines:"); jLabel6.setDisplayedMnemonic('O'); jPanel1.setBorder(null); jPanel1.setMinimumSize(new Dimension(220, 398)); jPanel1.setToolTipText("Control layout of speakers and assignment to SOM units"); jTabbedPane1.setBorder(null); jTabbedPane1.setMinimumSize(new Dimension(220, 427)); tp_help.setEditable(false); tp_help.setText("<html><h1>todo</h1></html>"); tp_help.setContentType("text/html"); // jPanel2.setToolTipText("Set preferences"); jScrollPane3.setToolTipText("Instructions how to use this control panel / About"); btn_load.setMaximumSize(new Dimension(50, 24)); btn_load.setMinimumSize(new Dimension(50, 24)); btn_load.setPreferredSize(new Dimension(50, 24)); btn_load.setMargin(new Insets(2, 2, 2, 2)); btn_load.setText("Load"); btn_save.setMaximumSize(new Dimension(50, 24)); btn_save.setMinimumSize(new Dimension(50, 24)); btn_save.setPreferredSize(new Dimension(50, 24)); btn_save.setMargin(new Insets(2, 2, 2, 2)); btn_save.setText("Save"); /* * jPanel2.add(jTextField1, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0 ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, * 0, 0), 56, 0)); jPanel2.add(jLabel1, new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0 ,GridBagConstraints.SOUTHWEST, GridBagConstraints.NONE, * new Insets(0, 0, 0, 1), 0, 0)); jPanel2.add(jLabel2, new GridBagConstraints(0, 2, 2, 1, 0.0, 0.0 ,GridBagConstraints.WEST, * GridBagConstraints.NONE, new Insets(30, 0, 0, 0), 0, 0)); jPanel2.add(jButton1, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0 * ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), -27, 0)); jPanel2.add(jTextField2, new GridBagConstraints(0, * 3, 1, 1, 0.0, 0.0 ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, -2, 0, 2), 57, 0)); */ this.getContentPane().add(jTabbedPane1, BorderLayout.CENTER); jTabbedPane1.add(jPanel1, "Assignment"); jScrollPane1.getViewport().add(li_lines, null); pnl_layout_table_gen.add(txt_table_x, null); pnl_layout_table_gen.add(jLabel4, null); pnl_layout_table_gen.add(txt_table_y, null); pnl_layout_table_gen.add(jLabel5, null); pnl_layout_table_gen.add(btn_gen, null); jScrollPane3.getViewport().add(tp_help); // jTabbedPane1.add(jPanel2, "Prefs"); jTabbedPane1.add(jScrollPane3, "?"); jPanel1.add(jLabel3, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(25, 0, 0, 0), 0, 0)); jPanel1.add(jLabel6, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); jPanel1.add(jScrollPane2, new GridBagConstraints(0, 5, 3, 1, 0.0, 0.3, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); jPanel1.add(btn_findme_loop, new GridBagConstraints(0, 2, 1, 1, 0.5, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); jPanel1.add(btn_findme, new GridBagConstraints(1, 2, 2, 1, 0.5, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); jPanel1.add(pnl_layout_table_gen, new GridBagConstraints(0, 4, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); jPanel1.add(btn_autoassign, new GridBagConstraints(0, 6, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); jPanel1.add(btn_start, new GridBagConstraints(0, 7, 3, 4, 0.0, 0.2, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(25, 0, 0, 0), 0, 0)); jPanel1.add(jScrollPane1, new GridBagConstraints(0, 1, 3, 1, 1.0, 0.5, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); /* * jTabbedPane1.setMnemonicAt(0, 'a'); jTabbedPane1.setMnemonicAt(1, 'p'); jTabbedPane1.setMnemonicAt(2, '?'); * jTabbedPane1.setToolTipTextAt(0, jPanel1.getToolTipText()); jTabbedPane1.setToolTipTextAt(1, * jPanel2.getToolTipText()); jTabbedPane1.setToolTipTextAt(2, jScrollPane3.getToolTipText()); */ jPanel1.add(btn_load, new GridBagConstraints(1, 3, 1, 1, 0.5, 0.0, GridBagConstraints.SOUTHEAST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); jPanel1.add(btn_save, new GridBagConstraints(2, 3, 1, 1, 0.5, 0.0, GridBagConstraints.SOUTHEAST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); jTabbedPane1.setMnemonicAt(0, 'a'); jTabbedPane1.setMnemonicAt(1, '?'); jTabbedPane1.setToolTipTextAt(0, jPanel1.getToolTipText()); jTabbedPane1.setToolTipTextAt(1, jScrollPane3.getToolTipText()); } /** * "handwritten" Swing related init-stuff, in order not to interfere with JBuilder's automatically generated code */ private void myInit() { li_lines.setModel(new LineListModel()); btn_findme.addActionListener(this); btn_findme_loop.addActionListener(this); btn_gen.addActionListener(this); btn_autoassign.addActionListener(this); btn_start.addActionListener(this); btn_save.addActionListener(this); btn_load.addActionListener(this); // btn_save.setEnabled(false); btn_load.setToolTipText("Loads assignment from file. The dimensions of the current SOM and the SOM that was loaded when you saved the assignment must match."); btn_save.setToolTipText("Saves current assignment (SOM units -> layout table cell) as a file."); // pre fill in textfields for layout table int iGuess = (int) Math.ceil(Math.sqrt(li_lines.getModel().getSize() * 2)); txt_table_x.setText("" + iGuess); txt_table_y.setText("" + iGuess); try { tp_help.setPage(ClassLoader.getSystemResource(Commons.INSTRUCTIONS_HTMLFILENAME)); } catch (IOException ex) { Commons.log.warning("Cannot display helppage."); // ex.printStackTrace(); tp_help.setText("<html><h2>Warning:</h2><p>Cannot display helppage.</p></html>"); } } /** Let the user confirm that the current assignment will be lost if he/she continues */ boolean confirmNewAssignment() { return !bAssignmentChanged || JOptionPane.showConfirmDialog(this, "The current assignment will be lost.\n\nContinue?", Commons.APP_NAME, JOptionPane.OK_CANCEL_OPTION) == JOptionPane.YES_OPTION; } /** * Let the user confirm that the current assignment AND SPEAKER LAYOUT will be lost if he/she continues */ boolean confirmNewAssignmentAndSpeakerLayout() { return !bAssignmentChanged || JOptionPane.showConfirmDialog(this, "Both the current speaker layout and assignment will be lost.\n\nContinue?", Commons.APP_NAME, JOptionPane.OK_CANCEL_OPTION) == JOptionPane.YES_OPTION; } /** * Adds the data directory (or "fileprefix") to each music file in the given array. * <p> * Both <code>asInputs</code> and <code>sDatadir</code> may be null. * * @return array of string of the same size as the input array or a zero-sized array if <code>asInputs</code> is * null */ protected String[] createFileArray(String[] asInputs, String sDatadir) { if (sDatadir == null) { // no change return asInputs; } if (asInputs == null) { // return empty array return new String[0]; } String[] as = new String[asInputs.length]; for (int i = 0; i < asInputs.length; i++) { as[i] = Commons.makeSureThatLastCharIsACorrectFileSeparator(sDatadir) + asInputs[i]; } return as; } /** * <p> * Parses the data in layout table and constructs the array used to create the <code>PlaybackThread</code>s * <p> * Checks if the data is valid. * <p> * If not valid, asks the user for confirmation. * * @return true if data is valid OR user has confirmed in case of invalidity */ boolean isValidOrConfirmed() { // Create new, empty data record array aPtdata = new TPlaybackThreadDataRecord[LineListModel.getMixerCount()]; TableModel mod = tb_layout.getModel(); // New table model actmod = new ActivityGridModel(mod); String field; char ch; int iChannel; int iLine = -1; GeneralUnitPNode pnode; Unit u; String[] asChannel = { "Left", "Right" }; StringBuffer sbWarnings = new StringBuffer(); int iWarningsCount = 0; // Go through all cells and fill in data into array for (int c = 0; c < mod.getColumnCount(); c++) { for (int r = 0; r < mod.getRowCount(); r++) { field = (String) mod.getValueAt(r, c); if (field != null && !field.equals("")) { // We assume the contents is well-formed. // if not, an exception is thrown try { ch = field.substring(field.length() - 1).toUpperCase().charAt(0); // ch contains 'L' or 'R' if (ch == 'L') { iChannel = 0; } else { iChannel = 1; } iLine = Integer.parseInt(Commons.cutEndOfString(field, 1)); // Create object if null if (aPtdata[iLine] == null) { aPtdata[iLine] = new TPlaybackThreadDataRecord(); } // Enter position of this channel of this line if (!aPtdata[iLine].setPos(iChannel, c, r)) { // position has already be entered, multiple usage of the same speaker // iWarningsCount++; sbWarnings.append("(column " + c + " / row " + r + "): Multiple usage of speaker " + field + "\n"); } // Build table model actmod.setSpeakerAt(c, r, LineListModel.getOurMixerIdString(iLine) + ", " + asChannel[iChannel]); // Enter music file list if (aavGPNs[c][r] != null && aavGPNs[c][r].size() > 0) { Iterator iter = aavGPNs[c][r].iterator(); while (iter.hasNext()) { pnode = (GeneralUnitPNode) iter.next(); u = pnode.getUnit(); aPtdata[iLine].addSongs( iChannel, createFileArray(u.getMappedInputNames(), CommonSOMViewerStateData.fileNamePrefix)); } } else { // no unit assigned iWarningsCount++; sbWarnings.append("(column " + c + " / row " + r + "): No units assigned to speaker " + field + "\n"); } } catch (NumberFormatException nfex) { iWarningsCount++; sbWarnings.append("(column " + c + " / row " + r + "): " + field + " is not a well formed speaker identification string.\n\tPlease refer to the manual (third tab at this control panel).\n"); } catch (ArrayIndexOutOfBoundsException nfex) { iWarningsCount++; sbWarnings.append("(column " + c + " / row " + r + "): Output line " + iLine + " does not exist.\n\tRange of allowed indices: 0 to " + (LineListModel.getMixerCount() - 1) + "\n"); } } else { // cell empty iWarningsCount++; sbWarnings.append("(column " + c + " / row " + r + "): empty, no speaker\n"); } } } // loop over array to find null values for (int i = 0; i < aPtdata.length; i++) { if (aPtdata[i] == null) { // no speaker of this line used iWarningsCount++; sbWarnings.append("(" + LineListModel.getOurMixerIdString(i) + "): not used\n"); } else { if (aPtdata[i].avMusic[0] == null) { // left speaker of this line not used iWarningsCount++; sbWarnings.append("(" + LineListModel.getOurMixerIdString(i) + "): left channel not used\n"); } if (aPtdata[i].avMusic[1] == null) { // right speaker of this line not used iWarningsCount++; sbWarnings.append("(" + LineListModel.getOurMixerIdString(i) + "): right channel not used\n"); } } } final int GRENZE = 700; String sWarningsMax; if (sbWarnings.length() > GRENZE) { sWarningsMax = sbWarnings.substring(0, GRENZE) + "\n[...]\n"; } else { sWarningsMax = sbWarnings.toString(); } // returns true if there were no warnings or if the user confirms the warnings return iWarningsCount == 0 || JOptionPane.showConfirmDialog(this, "" + iWarningsCount + " warning(s) occured:\n\n" + sWarningsMax + "\nAre you sure you want to start playback anyway?\n\n", Commons.APP_NAME, JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION; } @Override public void actionPerformed(ActionEvent parm1) { if (parm1.getSource() == btn_findme) { // Find me (once) // Nur wenn etwas in der Liste markiert ist int[] aInt = li_lines.getSelectedIndices(); if (aInt.length > 0) { Commons.playSound("airhorn_l.mp3", "airhorn_r.mp3", aInt[0]); } } if (parm1.getSource() == btn_findme_loop) { if (btn_findme_loop.isSelected()) { int[] aInt = li_lines.getSelectedIndices(); if (aInt.length > 0) { fmlt = new FindMeLoopThread(aInt[0]); // Start at selected index } else { fmlt = new FindMeLoopThread(0); // start at 0 } // start thread fmlt.start(); } else { // stop thread fmlt.stopIt(); } } if (parm1.getSource() == btn_gen) { // Generate new layout table if (tb_layout == null || confirmNewAssignmentAndSpeakerLayout()) { try { int rows = Integer.parseInt(txt_table_y.getText()); int cols = Integer.parseInt(txt_table_x.getText()); aavGPNs = new Vector[cols][rows]; for (Vector[] element : aavGPNs) { for (int j = 0; j < element.length; j++) { element[j] = new Vector(); } } // table init tb_layout = new LayoutTable(this, aavGPNs); tbmod_layout = new DefaultTableModel(rows, cols); tb_layout.setModel(tbmod_layout); jScrollPane2.getViewport().add(tb_layout); btn_autoassign.setEnabled(true); bAssignmentChanged = false; // clear marking on mapPane unmarkUnits(); btn_start.setEnabled(false); // btn_save.setEnabled(false); } catch (NumberFormatException ex) { JOptionPane.showMessageDialog( this, "The inputs for the number of rows and columns resp are not valid.\n\nThe layout table will not be created.", Commons.APP_NAME, JOptionPane.ERROR_MESSAGE); } } } if (parm1.getSource() == btn_autoassign) { // Start autoassignment if (tb_layout != null && confirmNewAssignment()) { // Table exists and (has not been changed OR user confirms) int iSourceX = tb_layout.getColumnCount(); int iSourceY = tb_layout.getRowCount(); int iTargetX = gsom.getLayer().getXSize(); int iTargetY = gsom.getLayer().getYSize(); float fRatioX = (float) iTargetX / (float) iSourceX; float fRatioY = (float) iTargetY / (float) iSourceY; int iCellX = 0, iCellY = 0, iOldCellX = 0, iOldCellY = 0; float fValueX = 0, fValueY = 0; for (int x = 0; x < iSourceX; x++, fValueY = 0, iOldCellY = 0) { fValueX += fRatioX; iCellX = Math.round(fValueX); for (int y = 0; y < iSourceY; y++) { aavGPNs[x][y].clear(); fValueY += fRatioY; iCellY = Math.round(fValueY); for (int x1 = iOldCellX; x1 < iCellX; x1++) { for (int y1 = iOldCellY; y1 < iCellY; y1++) { aavGPNs[x][y].add(map.getUnit(x1, y1)); } } iOldCellY = iCellY; } iOldCellX = iCellX; } tb_layout.repaint(); bAssignmentChanged = true; tb_layout.changeSelection(0, 0, false, false); setToolTipsForLayoutTable(); btn_start.setEnabled(true); btn_save.setEnabled(true); } } if (parm1.getSource() == btn_start) { // Start playback // isValidOrConfirmed() does all the parsing if (tb_layout != null && isValidOrConfirmed()) { // create Threads // lhmThreads = new LinkedHashMap(); // HashMap is assumed to be empty here (we save time by not creating it // new every time PlaybackThread pt; for (int i = 0; i < aPtdata.length; i++) { if (aPtdata[i] != null) { try { Mixer.Info mi = LineListModel.getOurMixerInfoAt(i); SourceDataLine sdl = null; sdl = (SourceDataLine) AudioSystem.getMixer(mi).getLine(Commons.datalineformat_info); pt = new PlaybackThread(LineListModel.getOurMixerIdString(i), aPtdata[i], sdl); lhmThreads.put("" + i, pt); Commons.log.fine("Created PlaybackThread for line #" + i); } catch (LineUnavailableException ex1) { Commons.log.warning("Could not create PlaybackThread for line #" + i); ex1.printStackTrace(); } } else { // line not used/assigned/etc Commons.log.info("Did not create PlaybackThread for line #" + i); } } Commons.cf = new ControlFrame(lhmThreads, actmod, btn_start); // MultichannelMp3_Shared.cf.setModal(true); Commons.cf.setVisible(true); } } if (parm1.getSource() == btn_save) { // Save assignment if (tb_layout != null) { File f = null; try { String sKey; String sValue; Properties props = new Properties(); int rows, cols; Unit u; StringBuffer sb; rows = tb_layout.getModel().getRowCount(); cols = tb_layout.getModel().getColumnCount(); // Dimensions of layout table: x,y props.setProperty(Commons.KEY_DIM_LAYOUT_TABLE, "" + cols + Commons.SEP_IN_VALUE + rows); // Dimension of SOM: x,y props.setProperty(Commons.KEY_DIM_SOM, "" + gsom.getLayer().getXSize() + Commons.SEP_IN_VALUE + gsom.getLayer().getYSize()); // Filename of SOM: filename (just for info) props.setProperty( Commons.KEY_FILE_SOM, "(not implemented yet, is a nice-to-have, because then the user could get a hint for which SOM this assignment was intended.)"); // Assignments and speaker layout for (int c = 0; c < aavGPNs.length; c++) { for (int r = 0; r < aavGPNs[c].length; r++) { sb = new StringBuffer(); sKey = "" + Commons.SEP_IN_KEY + c + Commons.SEP_IN_KEY + r; if (aavGPNs[c][r] != null && aavGPNs[c][r].size() > 0) { Iterator iter = aavGPNs[c][r].iterator(); while (iter.hasNext()) { u = ((GeneralUnitPNode) iter.next()).getUnit(); sb.append(u.getXPos()); sb.append("/"); sb.append(u.getYPos()); sb.append(Commons.SEP_IN_VALUE); } } // todo: cut 1 char from end? - seems not necessary props.setProperty(Commons.KEY_ASSIGNMENT + sKey, sb.toString()); // speaker layout sValue = (String) tb_layout.getModel().getValueAt(r, c); if (sValue != null) { props.setProperty(Commons.KEY_SPEAKER + sKey, sValue); } } } f = Commons.getChosenFile(true, this, Commons.PROPFILE_SUFFIX, state.getFileChooser()); if (f != null) { FileOutputStream fos = new FileOutputStream(f); props.store(fos, Commons.PROPFILEHEADER); fos.close(); Commons.log.info("Assignment file " + f + " successfully saved."); } } catch (Exception ex2) { Commons.log.warning("Could not save assignment file."); ex2.printStackTrace(); JOptionPane.showMessageDialog(this, "Could not save assignment file:\n" + f.getAbsolutePath() + "\n(" + ex2.getMessage() + ")", "Error", JOptionPane.ERROR_MESSAGE); } } } if (parm1.getSource() == btn_load) { // Load assignment if (confirmNewAssignmentAndSpeakerLayout()) { File f = null; try { f = Commons.getChosenFile(false, this, Commons.PROPFILE_SUFFIX, state.getFileChooser()); if (f != null) { String sKey; String sValue; Properties props = new Properties(); int rows, cols, x, y; StringTokenizer st, st2; String sUnit_coo; FileInputStream fis = null; fis = new FileInputStream(f); props = new Properties(); props.load(fis); fis.close(); // Dimension of SOM: x,y sValue = props.getProperty(Commons.KEY_DIM_SOM); st = new StringTokenizer(sValue, "" + Commons.SEP_IN_VALUE); cols = Integer.parseInt(st.nextToken()); rows = Integer.parseInt(st.nextToken()); // Check if dimensions of current SOM are the same if (cols != gsom.getLayer().getXSize() || rows != gsom.getLayer().getYSize()) { JOptionPane.showMessageDialog(this, "Wrong SOM dimensions.\nExpected: " + cols + "/" + rows + "\nCurrent: " + gsom.getLayer().getXSize() + "/" + gsom.getLayer().getYSize(), "Error", JOptionPane.ERROR_MESSAGE); throw new Exception("The dimensions of the current SOM do not match!"); } // Dimensions of layout table: x,y sValue = props.getProperty(Commons.KEY_DIM_LAYOUT_TABLE); st = new StringTokenizer(sValue, "" + Commons.SEP_IN_VALUE); cols = Integer.parseInt(st.nextToken()); rows = Integer.parseInt(st.nextToken()); // Create new layout table aavGPNs = new Vector[cols][rows]; for (Vector[] element : aavGPNs) { for (int j = 0; j < element.length; j++) { element[j] = new Vector(); } } // table init tb_layout = new LayoutTable(this, aavGPNs); tbmod_layout = new DefaultTableModel(rows, cols); tb_layout.setModel(tbmod_layout); jScrollPane2.getViewport().add(tb_layout); // Assignments ans speaker layout for (int c = 0; c < cols; c++) { for (int r = 0; r < rows; r++) { sKey = Commons.KEY_ASSIGNMENT + Commons.SEP_IN_KEY + c + Commons.SEP_IN_KEY + r; sValue = props.getProperty(sKey); st = new StringTokenizer(sValue, "" + Commons.SEP_IN_VALUE); while (st.hasMoreTokens()) { sUnit_coo = st.nextToken(); // something like "12/1" st2 = new StringTokenizer(sUnit_coo, "/"); x = Integer.parseInt(st2.nextToken()); y = Integer.parseInt(st2.nextToken()); aavGPNs[c][r].add(somPane.getMap().getUnit(x, y)); } // speaker layout sKey = Commons.KEY_SPEAKER + Commons.SEP_IN_KEY + c + Commons.SEP_IN_KEY + r; sValue = props.getProperty(sKey); if (sValue != null) { tb_layout.getModel().setValueAt(sValue, r, c); } } } Commons.log.info("Assignment file " + f + " successfully loaded."); bAssignmentChanged = true; setToolTipsForLayoutTable(); // Buttons states btn_autoassign.setEnabled(true); btn_start.setEnabled(true); } } catch (Exception ex2) { Commons.log.warning("Could not load assignment file."); ex2.printStackTrace(); JOptionPane.showMessageDialog(this, "Could not load assignment file:\n" + f.getAbsolutePath() + "\n(" + ex2.getMessage() + ")", "Error", JOptionPane.ERROR_MESSAGE); } } } } /** called whenever the selection in the somPane has changed */ @Override public void unitSelectionChanged(Object[] selection, boolean newSelection) { if (bIgnoreNextSelectionChange) { // Ignore this change // System.out.println("bIgnoreNextSelectionChange was true"); return; } if (tb_layout != null && this.isVisible()) { bAssignmentChanged = true; btn_start.setEnabled(true); btn_save.setEnabled(true); unmarkUnits(); // for single selection mode // int rowIndex = tb_layout.getSelectedRow(); // int colIndex = tb_layout.getSelectedColumn(); // Get the min and max ranges of selected cells int rowIndexStart = tb_layout.getSelectedRow(); int rowIndexEnd = tb_layout.getSelectionModel().getMaxSelectionIndex(); int colIndexStart = tb_layout.getSelectedColumn(); int colIndexEnd = tb_layout.getColumnModel().getSelectionModel().getMaxSelectionIndex(); // Check each cell in the range for (int r = rowIndexStart; r <= rowIndexEnd; r++) { for (int c = colIndexStart; c <= colIndexEnd; c++) { if (tb_layout.isCellSelected(r, c)) { // cell is selected aavGPNs[c][r].clear(); aavGPNs[c][r].addAll(Arrays.asList(selection)); } } } } // actually it would be enough if this is called only if the mouse button is released // but how do I know that? // Anyway it seems that things still go fast enough setToolTipsForLayoutTable(); } /** * Iterates through cells of layout table and constructs an array with strings for those cells, that have been * assigned. * <p> * Unassigned cells' strings remain null * <p> * Finally the array is given to the layout table. */ public void setToolTipsForLayoutTable() { // only if layout table exists if (tb_layout != null) { Vector v; Iterator iter; StringBuffer sb = new StringBuffer(); String[][] aasToolTips = new String[aavGPNs.length][aavGPNs[0].length]; String[] names; GeneralUnitPNode pnode; Unit u; int iTTLines, iTTCols, iMore = 0; // if no nodes are assigned at a certain cell, the place in the array can remain null for (int i = 0; i < aavGPNs.length; i++) { for (int j = 0; j < aavGPNs[i].length; j++) { v = aavGPNs[i][j]; if (v != null && v.size() > 0) { sb.replace(0, sb.length(), "<html><p>"); iTTLines = 0; iTTCols = 0; iter = v.iterator(); while (iter.hasNext()) { pnode = (GeneralUnitPNode) iter.next(); u = pnode.getUnit(); names = u.getMappedInputNames(); if (names != null) { for (String element : names) { if (iTTLines < Commons.MAX_TOOLTIP_LINES) { sb.append(element); sb.append(", "); iTTCols += element.length() + 2; if (iTTCols > Commons.MAX_TOOLTIP_COLUMNS) { // new line iTTCols = 0; sb.append("<br>"); iTTLines++; } } else { iMore++; } } } } if (iTTLines >= Commons.MAX_TOOLTIP_LINES) { sb.append("<br>..." + iMore + " more"); } sb.append("</p></html>"); aasToolTips[i][j] = sb.toString(); } } } tb_layout.setToolTips(aasToolTips); } } // @Override // public Dimension getPreferredSize() { // return new Dimension(state.controlElementsWidth, 660); // } @Override public Dimension getMinimumSize() { return new Dimension(state.controlElementsWidth, 330); // debug epei_intern (war: 330) } /** * Is called if the selection of <code>tb_layout</code> changes. Since it is not possible to determine the table * from the event's source, <code>tb_layout</code> is hardcoded. * <p> * The method marks all units on the <code>mapPane</code> who are assigned to at least one of the selected cells of * <code>tb_layout</code> */ @Override public void valueChanged(ListSelectionEvent e) { JTable table = tb_layout; if (e.getValueIsAdjusting()) { // The mouse button has not yet been released } // For single selection mode // int rowIndex = tb_layout.getSelectedRow(); // int colIndex = tb_layout.getSelectedColumn(); // Get the min and max ranges of selected cells int rowIndexStart = table.getSelectedRow(); int rowIndexEnd = table.getSelectionModel().getMaxSelectionIndex(); int colIndexStart = table.getSelectedColumn(); int colIndexEnd = table.getColumnModel().getSelectionModel().getMaxSelectionIndex(); bIgnoreNextSelectionChange = true; // unitSelectionChanged will be called // somPane.getCanvas().removeSelection(); // but it will be ignored bIgnoreNextSelectionChange = false; unmarkUnits(); // Check each cell in the range for (int r = rowIndexStart; r <= rowIndexEnd; r++) { for (int c = colIndexStart; c <= colIndexEnd; c++) { if (table.isCellSelected(r, c)) { if (r != -1 && c != -1 && aavGPNs[c][r] != null) { Iterator iter = aavGPNs[c][r].iterator(); while (iter.hasNext()) { GeneralUnitPNode item = (GeneralUnitPNode) iter.next(); markUnit(item.getUnit().getXPos(), item.getUnit().getYPos()); } } } } } } /** We print relevant command line parameters if this panel is shown */ @Override public void setVisible(boolean visible) { super.setVisible(visible); if (visible) { // is to become visible Commons.log.fine("Using directory for decoded files: " + Commons.sDecodedOutputDir); Commons.log.fine("Decoding probability: " + Commons.p_decode); } } }