marytts.tools.redstart.AdminWindow.java Source code

Java tutorial

Introduction

Here is the source code for marytts.tools.redstart.AdminWindow.java

Source

/**
 * Copyright 2007 DFKI GmbH.
 * All Rights Reserved.  Use is subject to license terms.
 *
 * This file is part of MARY TTS.
 *
 * MARY TTS 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, version 3 of the License.
 *
 * 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
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package marytts.tools.redstart;

import org.apache.commons.io.FilenameUtils;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.util.Properties;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.SourceDataLine;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;

import marytts.signalproc.display.MultiDisplay;
import marytts.util.data.audio.MonoAudioInputStream;
import marytts.util.string.StringUtils;

/**
 *
 * @author  Mat Wilson <mwilson@dfki.de>
 */
public class AdminWindow extends javax.swing.JFrame {

    // ______________________________________________________________________
    // Instance fields

    // ______________________________________________________________________
    // Class fields

    // Column indices for prompt set table
    protected static final int REC_STATUS_COLUMN = 0; // First column is recording status 
    protected static final int BASENAME_COLUMN = 1; // Second column is basename
    protected static final int PROMPT_TEXT_COLUMN = 2; // Third collumn is prompt text       
    private JTable jTable_PromptSet; // Create our own table programmatically

    // Path for folder containing the voice - use AdminWindow.getVoiceFolderPath().getPath() to access externally
    private String voiceFolderPathString; // e.g., "/project/mary/mat/voices/bundesliga"

    // Folder names (below the level of the voice folder)
    private static final String PROMPT_FOLDER_NAME = "/text/";
    private static final String REC_FOLDER_NAME = "/wav/";
    private static final String SYNTH_FOLDER_NAME = "/prompt_wav/";
    private static final String TRANSCRIPTION_FOLDER_NAME = "/transcription/";

    private static boolean beepDemoOn = true; // Flag for toggling beep demo (clicking on status icon)

    // Forms and related fields
    private SpeakerWindow speakerWin = new SpeakerWindow(); // Speaker window instance
    private Options optionsDialog;

    // Boolean flags used for determining when buttons should be enabled and disabled
    private boolean playingStatus = false; // Boolean flag to indicate if playback is underway
    private boolean continuousMode = false; // Boolean flag to indicate if playback is underway
    private boolean recordingBlock = false; // Boolean flag to indicate if playback is part of a recording block

    // More objects    
    protected Prompt[] promptArray; // For storing the loaded prompt set
    private RecSession currentSession; // Recording session object
    private boolean stopPressed; // Internal setting, set to true by stop button

    private Font defaultPromptFont;

    private static boolean showTranscription = false; // if true show the transcription if they are available
    // showTranscription: if the input file text contains also the transcription, there is the possibility of see them  
    private static boolean redAlertMode = false; // if true show red alert portion of text
    // redAlert mode: if the input file text refers to the redalert mode (sensitive text portion surrounded by underscore char) there is the possibility of visualize them with a red color   

    // ______________________________________________________________________
    // Constructors

    /**
     * Creates new form AdminWindow and starts the recording session
     */
    public AdminWindow(String voiceFolderPathString) {
        this.voiceFolderPathString = voiceFolderPathString;

        optionsDialog = new Options(this);

        setSystemLookAndFeel();

        System.out.println("Initializing components...");

        // GUI form setup
        initComponents(); // Initialize GUI components (auto-generated code)
        initMoreComponents(); // Initialize some components further (editable)

        setupVoice();

        jTable_PromptSet.requestFocusInWindow();

        Test.output("|AdminWindow| Ready for testing."); // TESTCODE
    }

    private void setupVoice() {
        this.currentSession = createRecSession(); // Create new recording session
        System.out.println("Components initialized.");
        buildPromptTable(); // Set up and fill the prompt table

        // PRI4 - Encapsulate this better; poor design but functional
        this.speakerWin.setupProgressBar(this.promptArray.length);
        this.speakerWin.updateProgressBar(1); // Because we start at first prompt
        this.speakerWin.updatePromptCount(1);

        System.out.println("System is ready.");
    }

    public void setShowTranscription(boolean selected) {
        // TODO Auto-generated method stub
        showTranscription = selected;
    }

    public boolean getShowTranscription() {
        // TODO Auto-generated method stub
        return showTranscription;
    }

    public void setRedAlertMode(boolean selected) {
        // TODO Auto-generated method stub
        redAlertMode = selected;
    }

    public boolean getRedAlertMode() {
        return redAlertMode;
    }

    /** Updates the prompt table with prompt data
     *  @param newSession The current recording session object
     */
    private RecSession createRecSession() {

        RecSession newSession = null;
        try {
            newSession = new RecSession(this);
        } catch (FileNotFoundException ex) {

            String message = "File not found.";
            showMessage(message, true); // true = warning

            if (Test.isDebug) {
                ex.printStackTrace();
            }

        } catch (IOException ex) {

            String message = "File read error.";
            showMessage(message, true); // true = warning

            if (Test.isDebug) {
                ex.printStackTrace();
            }

        }
        Test.output("|AdminWindow| Recording session created."); // TESTCODE

        return newSession;
    }

    /** Programmatically set the column widths of the prompt set table */
    private void setColumnWidths() {
        // only try to do this when we are visible
        if (!this.isVisible())
            return;

        // Get columns from column model
        TableColumn recStatusColumn = jTable_PromptSet.getColumnModel().getColumn(0);
        TableColumn baseNameColumn = jTable_PromptSet.getColumnModel().getColumn(1);
        TableColumn promptColumn = jTable_PromptSet.getColumnModel().getColumn(2);

        // Get length of longest string in the column
        int widestBasename = LookAndFeel.getMaxColumnWidth(this.jTable_PromptSet, this.BASENAME_COLUMN);

        // These widths seem to be appropriate for the firs two columns - set them
        int recStatusColumnWidth = 50; // Leave hardcoded since only three possible values
        int baseNameColumnWidth = widestBasename + 10; // was 110 when hardcorded
        Test.output("Basename column width: " + widestBasename);
        recStatusColumn.setPreferredWidth(recStatusColumnWidth);
        baseNameColumn.setPreferredWidth(baseNameColumnWidth);

        // Now set prompt column to fill remaining width of the table
        int scrollbarBuffer = 3; // Buffer to avoid displaying a horizontal scrollbar
        int tableWidth = jScrollPane_PromptSet.getWidth();
        promptColumn.setPreferredWidth(tableWidth - (recStatusColumnWidth + baseNameColumnWidth + scrollbarBuffer));

    }

    /** Determines recording status given how many recordings a prompt has, as well as clipping status
     *  @param fileCount The number of files a prompt has
     *  @return The appropriate string, indicating the prompt's recording status
     */
    protected static String determineStatus(Recording rec) {
        String recStatus;
        String clipStatus;
        String combinedStatus;

        int fileCount = rec.getFileCount();

        // First determine recording status (number of recordings)
        if (fileCount == 0) {
            // Recording status is ""
            recStatus = "";
        } else if (fileCount == 1) {
            // Recording status is "R"
            recStatus = "R"; // "R" indicates 1 recording exists
        } else {
            // Recording status is "R+"
            recStatus = "R+"; // "R+" indicates >1 recording exists            
        }

        if (fileCount > 0) {
            // Now determine clipping status (audio or temporal clipping)
            rec.checkForAmpClipping();
            rec.checkForTempClipping();
        }

        // Determine message content related to clipping status
        if ((rec.isAmpClipped) || (rec.isTempClipped)) {
            clipStatus = "!"; // Show "!" if either amplitude or temporal clipping
        } else if (rec.isAmpWarning) {
            clipStatus = "?"; // Show "?" if amplitude clipping almost occurred (near threshold)
        } else {
            clipStatus = ""; // Otherwise no clipping of any sort
        }

        // Create the combined status
        combinedStatus = recStatus + clipStatus;

        Test.output("|AdminWindow.determineStatus| Combined status: " + combinedStatus);

        return combinedStatus;
    }

    // Specifically for SpeakerWindow.java to determine if it should show prompt count
    public Options getOptions() {
        return this.optionsDialog;
    }

    public SpeakerWindow getSpeakerWindow() {
        return speakerWin;
    }

    /** Gets the currently selected row in the prompt set table */
    public int getCurrentRow() {
        return jTable_PromptSet.getSelectedRow();
    }

    /** Displays the prompt text in the prompt display pane */
    private void displayPromptText() {
        int currentRow = getCurrentRow();
        if (currentRow == -1)
            return;
        Prompt prompt = promptArray[currentRow]; // Current prompt
        String promptText = prompt.getPromptText();
        Recording rec = prompt.getRecording(); // Most recent recording for selected prompt
        String nextPromptText = "";
        if (currentRow + 1 < promptArray.length) {
            nextPromptText = promptArray[currentRow + 1].getPromptText();
        }

        //Transcription if flag set and if file present
        String promptTranscription = "\n<";
        String nextPromptTranscription = "\n<";
        if (showTranscription) {
            promptTranscription = promptTranscription + promptArray[currentRow].getPromptTranscriptionText();
            if (currentRow + 1 < promptArray.length)
                nextPromptTranscription = nextPromptTranscription
                        + promptArray[currentRow + 1].getPromptTranscriptionText();
        }
        promptTranscription = promptTranscription + ">";
        nextPromptTranscription = nextPromptTranscription + ">";

        jTextPane_PromptDisplay.setFont(defaultPromptFont);
        if (this.isVisible()) {
            if (!showTranscription) {
                LookAndFeel.centerPromptText(jTextPane_PromptDisplay, promptText, redAlertMode);
                LookAndFeel.centerPromptText(jTextPane_nextSentence, nextPromptText, redAlertMode);
            } else {
                LookAndFeel.centerPromptText(jTextPane_PromptDisplay, promptText + promptTranscription,
                        redAlertMode);
                LookAndFeel.centerPromptText(jTextPane_nextSentence, nextPromptText + nextPromptTranscription,
                        redAlertMode);
            }
        }

        // Also update in Speaker window
        if (!showTranscription)
            this.speakerWin.updatePromptDisplay(promptText, nextPromptText, redAlertMode);
        else
            this.speakerWin.updatePromptDisplay(promptText + promptTranscription,
                    nextPromptText + nextPromptTranscription, redAlertMode);

        int promptNumber = getCurrentRow() + 1;
        this.speakerWin.updateProgressBar(promptNumber);
        this.speakerWin.updatePromptCount(promptNumber);

        // Display number of recordings in the message bar
        int recCount = prompt.getRecCount();
        String inflectedRecordings = " recordings";
        if (recCount == 1) {
            inflectedRecordings = " recording";
        }

        String clippingMessage = ""; // No clipping messasge (changed below if clipping)
        boolean warning = false; // Warning flag to show correct icon (false = info; true = warning)

        // If a recording exists
        if (recCount > 0) {

            // Enable play button
            jButton_Play.setEnabled(true);
            jButton_Display.setEnabled(true);

            // Get clipping status of most recent recording
            clippingMessage = getClippingMessage(rec);
            warning = rec.isAmpClipped || rec.isTempClipped; // Set warning level accordingly      

        } else {
            jButton_Play.setEnabled(false); // Disable Play button if no recording to play
            jButton_Display.setEnabled(false);
        }

        String message = "Ready: " + prompt.getBasename() + " selected (" + recCount + inflectedRecordings + "). "
                + clippingMessage;
        showMessage(message, warning); // false = info, not a warning; true = warning (clipping)

    }

    /** Further initialization of components on startup
     *  (These can be edited in NetBeans, initComponents() cannot)
     */
    private void initMoreComponents() {

        // Center window in the user's screen
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        Dimension frameSize = getSize();
        setLocation(
                new Point((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2));

        // Set application icon in title bar left-hand corner
        this.setIconImage(Toolkit.getDefaultToolkit().getImage(IconSet.LOGO_16x16_URL));

        // Change status icon in Admin window
        jLabel_SessionStatus.setIcon(IconSet.STOP_48X48);

        defaultPromptFont = jTextPane_PromptDisplay.getFont();

        speakerWin.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent evt) {
                jCheckBoxMenuItem_SpeakerWindow.setSelected(false);
            }
        });

        optionsDialog.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent evt) {
                speakerWin.showOrHidePromptCount();
            }
        });
    }

    private void buildPromptTable() {

        this.promptArray = this.currentSession.getPromptArray();

        System.out.println("Loading prompts...");
        Test.output("Array contains " + promptArray.length + " prompts.");

        // Make column names array
        String[] columnNames = new String[3];
        columnNames[REC_STATUS_COLUMN] = "Status";
        columnNames[BASENAME_COLUMN] = "Basename";
        columnNames[PROMPT_TEXT_COLUMN] = "Prompt Preview";

        // Now create the table itself        
        JTable table = new JTable(new PromptTableModel(promptArray, columnNames, redAlertMode));
        table.setColumnSelectionAllowed(false);
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        // Set alignment for the status colum to centered
        DefaultTableCellRenderer renderer = new ClippingColorRenderer();
        renderer.setHorizontalAlignment(JTextField.CENTER);
        table.getColumnModel().getColumn(REC_STATUS_COLUMN).setCellRenderer(renderer);

        // Set selection highlight colour to light blue
        table.setSelectionBackground(new java.awt.Color(153, 204, 255));

        // Add listeners
        table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent evt) {
                displayPromptText();
            }
        });

        // Store the table in an instance field accessible to the entire class
        this.jTable_PromptSet = table;

        Thread recordingStatusInitialiser = new Thread() {
            public void run() {
                updateAllRecStatus();
            }
        };
        recordingStatusInitialiser.start();

        // Display table in the appropriate component pane
        jScrollPane_PromptSet.setViewportView(table);

        if (promptArray.length > 0) {
            table.setRowSelectionInterval(0, 0); // Show first row of prompt table as selected               
            displayPromptText(); // Display the prompt text for the first prompt in the prompt display pane 
        }
        setColumnWidths();

        System.out.println("Total " + table.getRowCount() + " prompts loaded.");

    }

    private void updateAllRecStatus() {
        for (int row = 0; row < promptArray.length; row++) {
            String recStatus = determineStatus(promptArray[row].getRecording());
            // PRI2 Assumes saturation status is false; needs to actually check
            jTable_PromptSet.setValueAt(recStatus, row, REC_STATUS_COLUMN);
        }
    }

    /** Updates session status icon and calls method to play a synthesized file */
    private void playSynthesis() {

        preparePlayback();

        Prompt selectedPrompt = promptArray[getCurrentRow()];
        String basename = selectedPrompt.getBasename();

        // Change status icon in Admin window
        jLabel_SessionStatus.setIcon(IconSet.PLAY_48X48);

        // Change status icon in Speaker window
        speakerWin.updateSessionStatus(IconSet.PLAY_64X64);

        // PRI2 Get path name with DatabaseLayout method instead?
        String synthFolderPathString = getSynthFolderPath().getPath();

        // Synthesis files are prepended with "p_" (e.g., p_spike0001.wav)
        String soundFilePathString = synthFolderPathString + File.separator + "p_" + basename + ".wav";

        SourceDataLine speakerOutput = optionsDialog.getSpeakerOutputLine();
        Speech.play(soundFilePathString, speakerOutput, optionsDialog.getSpeakerOutputMode());

        // TESTCODE
        Test.output("|AdminWindow.playSynthesis| [Synthesis]:" + selectedPrompt.getPromptText());

        // Change status icon in Admin window
        jLabel_SessionStatus.setIcon(IconSet.STOP_48X48);

        // Change status icon in Speaker window
        speakerWin.updateSessionStatus(IconSet.STOP_64X64);

        // Enforce pause after synthesis playback (as set by user in Options dialog)
        try {
            Thread.sleep(optionsDialog.getPauseAfterSynth());
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }

    }

    /** Stops playback
    Separate method needed to stop recording */
    private void stopPlayback() {
        Speech.stopPlaying(); // Immediately stop any playback
        stopPressed = true;
    }

    /** Calls methods for making a recording */
    private void makeRecording() {

        // Get currently selected prompt
        int promptIndex = getCurrentRow();
        Prompt currentPrompt = this.promptArray[promptIndex];
        String currentBasename = currentPrompt.getBasename();

        // Change status icon in Admin Window
        jLabel_SessionStatus.setIcon(IconSet.REC_48X48);

        // Change status icon in Speaker Window
        speakerWin.updateSessionStatus(IconSet.REC_64X64);

        int nChars = currentPrompt.getPromptText().length();
        // Calculate mic open time, using bufferToAdd from the user options
        int micOpenTime = nChars * optionsDialog.getTimePerChar() + optionsDialog.getBufferToAdd();

        Test.output(
                "|AdminWindow.makeRecording()| nChars: " + nChars + " a " + optionsDialog.getTimePerChar() + " ms");
        Test.output("|AdminWindow.makeRecording()| buffer: " + optionsDialog.getBufferToAdd() + " ms");
        Test.output("|AdminWindow.makeRecording()| micOpenTime: " + micOpenTime + " ms");

        // Create recording object and rename any latest recording
        Recording currentRecording = currentPrompt.getRecording();
        if (currentRecording.getFileCount() > 0)
            currentRecording.archiveLatestRecording();

        try {

            boolean warning = false;

            // Update button text (change from "Record" to "Stop")
            toggleRecordToStop();

            // Update message bar
            String openDuration = new DecimalFormat("#.###").format(micOpenTime * 0.001);
            String message = "Recording for " + currentBasename + ": Microphone open for " + openDuration + " s...";
            showMessage(message, warning); // warning = false; just info

            // TESTCODE
            Test.output("|AdminWindow.makeRecording| [Recording]:" + currentPrompt.getPromptText());

            // Open the microphone
            currentRecording.timedRecord(optionsDialog.getTargetDataLine(), optionsDialog.getInlineFilter(),
                    micOpenTime);

            // Update recording status for current prompt            
            currentRecording.updateFileCount();
            String recStatus = determineStatus(currentRecording);
            message = getClippingMessage(currentRecording);
            warning = currentRecording.isAmpClipped || currentRecording.isTempClipped;
            Test.output("|AdminWindow.makeRecording()| New status: " + recStatus);
            jTable_PromptSet.setValueAt(recStatus, promptIndex, REC_STATUS_COLUMN);

            // Enable Play button only if at least one recording exists for selected basename
            if ((currentRecording.getFileCount() > 0) && (!this.recordingBlock)) {
                jButton_Play.setEnabled(true);
                jButton_Display.setEnabled(true);
            }

            // TESTCODE 
            Test.output("|AdminWindow.makeRecording| " + currentBasename + " now has "
                    + currentRecording.getFileCount() + " file(s)");

            // Finished recording

            // Update message bar
            message = "Recording for " + currentBasename + ": Microphone is closed. " + message;
            showMessage(message, warning); // false = info, not a warning; true if clipping occurred

        } catch (Exception ex) {
            ex.printStackTrace();
            String message = "Recording error. Check your audio options and see the console for details.";
            showMessage(message, true); // true = warning, not info
        }

        // Change status icon in Admin window and Speaker Window
        jLabel_SessionStatus.setIcon(IconSet.STOP_48X48);
        speakerWin.updateSessionStatus(IconSet.STOP_64X64);

        // Only do below if option is not selected to continue with next prompt
        if (!this.jCheckBox_ContinueWithNext.isSelected()) {

            // Re-enable the Play/Stop button
            this.jButton_Play.setEnabled(true);
            jButton_Display.setEnabled(true);

            // Update button text (change from "Stop" to "Record")
            toggleStopToRecord();

        }

    }

    /** Plays latest recording for the currently selected promtp */
    private void playRecording() {

        preparePlayback();

        Prompt selectedPrompt = promptArray[getCurrentRow()];
        String basename = selectedPrompt.getBasename();
        int recCount = selectedPrompt.getRecCount();

        // TESTCODE
        Test.output("|AdminWindow.playRecording| Number of recordings for " + basename + " is: " + recCount);

        // If selected prompt has a recording
        if (recCount > 0) {

            // Change status icon in Admin Window
            jLabel_SessionStatus.setIcon(IconSet.PLAY_48X48);

            // Change status icon in Speaker Window
            speakerWin.updateSessionStatus(IconSet.PLAY_64X64);

            // Update message bar            
            String message = "Playing latest recording for " + basename + ".";
            showMessage(message, false); // false = info, not a warning

            // Determine complete path for the .wav file
            String recFolderPathString = getRecFolderPath().getPath();
            String soundFilePathString = recFolderPathString + File.separator + basename + ".wav";

            // TESTCODE
            Test.output("|AdminWindow.playRecording| Sound file path: " + soundFilePathString);

            try {

                if (!this.recordingBlock) {
                    // We're using the Play button as the Stop button
                    // Update button text (change from "Play" to "Stop")
                    togglePlayToStop();
                }

                SourceDataLine expertOutput = optionsDialog.getExpertOutputLine();
                Speech.play(soundFilePathString, expertOutput, optionsDialog.getExpertOutputMode());

                // Update message bar            
                message = "Ready.";
                showMessage(message, false); // false = info, not a warning
                // PRI3 Change this to show current selection (as before)

            } catch (Exception ex) {
                ex.printStackTrace();
                message = "Playback error. Check your audio options and see the console for details.";
                showMessage(message, true); // true = warning, not info
            }

            // Change status icon in Admin window
            jLabel_SessionStatus.setIcon(IconSet.STOP_48X48);

            // Change status icon in Speaker window
            speakerWin.updateSessionStatus(IconSet.STOP_64X64);

        } else {
            // Update message bar
            String message = "No recording for " + basename + ".";
            showMessage(message, false); // false = info, not a warning
        }

        endPlayback();
    }

    /** Establishes logic for running a recording (with optional playback, etc.) */
    private void manageRecording() {

        this.recordingBlock = true; // To help know which buttons to enable and disable
        this.stopPressed = false;
        setCheckBoxes(false);

        int startingRow = getCurrentRow();
        int endRow = startingRow + 1;

        // When user has selected "Continue with next prompt"
        if (jCheckBox_ContinueWithNext.isSelected()) {

            // Get number of rows so that we continue to the end of the prompt set
            endRow = this.promptArray.length;

        }

        for (int row = startingRow; !stopPressed && row < endRow; row++) {

            // TESTCODE
            Test.output("|AdminWindow.jButton_RecordActionPerformed| [Current row]:" + row);
            Test.output("|AdminWindow.jButton_RecordActionPerformed| [End row]:" + endRow);

            // Get currently selected prompt
            int promptIndex = getCurrentRow();
            Prompt currentPrompt = this.promptArray[promptIndex];

            // Update prompt display
            displayPromptText();

            // Play synthesized prompt if this option is enabled
            if (jCheckBox_PlaySynthesis.isSelected()) {
                playSynthesis();
            } else if (row > startingRow) {
                // Apply user-defined silence before recording, only for non-initial recording
                try {
                    // Indicate pause duration in the message bar
                    String silenceDuration = new DecimalFormat("#.###")
                            .format(optionsDialog.getSilenceDuration() * 0.001);
                    String message = currentPrompt.getBasename() + ": " + silenceDuration
                            + " s pause to allow reading of the prompt...";
                    showMessage(message, false); // warning = false; just info

                    // Enforce the pause duration between prompt recordings (as set by user in Options dialog)
                    Thread.sleep(optionsDialog.getSilenceDuration());

                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }

            }

            if (!stopPressed) {
                // Disable the Play/Stop button so that user can't accidentally begin playing the file we're overwriting
                this.jButton_Play.setEnabled(false);
                jButton_Display.setEnabled(false);

                // Play beep to indicate microphone is open
                optionsDialog.playOpenBeep();
            }

            if (!stopPressed) {
                // Call method that coordinates the actual recording
                makeRecording();

                // Play beep to indicate microphone is closed (if option selected in GUI
                if (jCheckBox_PlayClosingBeep.isSelected()) {
                    optionsDialog.playClosedBeep();
                    Test.output("Second beep enabled.");
                } else {
                    Test.output("Second beep disabled.");
                }

            }

            // Listen to recording if this option is selected
            if (!stopPressed && jCheckBox_PlayBackRec.isSelected()) {
                playRecording();
            }
            // TESTCODE
            else
                Test.output("|AdminWindow.jButton_RecordActionPerformed| [No playback of recording]");

            // Select next row
            if ((row < (endRow - 1)) && (!stopPressed)) {
                jTable_PromptSet.setRowSelectionInterval(row + 1, row + 1);

                // Advance vertical scrollbar
                try {
                    int column = 1; //jTable_PromptSet.getSelectedColumn();                
                    Rectangle cellRect = jTable_PromptSet.getCellRect(row + 4, column, false);
                    jTable_PromptSet.scrollRectToVisible(cellRect);
                    jTable_PromptSet.updateUI();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }

        } // row

        // Re-enable buttons in the event they were disabled due to continuous mode
        this.jButton_Record.setEnabled(true);
        this.jButton_Play.setEnabled(true);
        jButton_Display.setEnabled(true);

        // Update button text (change from "Stop" to "Record" or "Play")
        toggleStopToRecord();
        toggleStopToPlay();

        setCheckBoxes(true);
        this.recordingBlock = false;
        this.continuousMode = false;
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
    private void initComponents() {
        jPanel_SpeakerView = new javax.swing.JPanel();
        jTextPane_PromptDisplay = new javax.swing.JTextPane();
        jTextPane_nextSentence = new javax.swing.JTextPane();
        jLabel_SessionStatus = new javax.swing.JLabel();
        jPanel_AdminControls = new javax.swing.JPanel();
        jScrollPane_PromptSet = new javax.swing.JScrollPane();
        jCheckBox_PlaySynthesis = new javax.swing.JCheckBox();
        jCheckBox_PlayBackRec = new javax.swing.JCheckBox();
        jCheckBox_ContinueWithNext = new javax.swing.JCheckBox();
        jButton_Record = new javax.swing.JButton();
        jButton_Play = new javax.swing.JButton();
        jSeparator_MessageBar = new javax.swing.JSeparator();
        jLabel_MessageBar = new javax.swing.JLabel();
        jCheckBox_PlayClosingBeep = new javax.swing.JCheckBox();
        jLabel_MessageBarIcon = new javax.swing.JLabel();
        jButton_Display = new javax.swing.JButton();
        jMenuBar_AdminWindow = new javax.swing.JMenuBar();
        jMenu_File = new javax.swing.JMenu();
        jMenuItem_Open = new javax.swing.JMenuItem();
        jMenuItem_ImportText = new javax.swing.JMenuItem();
        jSeparator_File = new javax.swing.JSeparator();
        jMenuItem_Exit = new javax.swing.JMenuItem();
        jMenu_Tools = new javax.swing.JMenu();
        jMenuItem_Options = new javax.swing.JMenuItem();
        jMenu_Window = new javax.swing.JMenu();
        jCheckBoxMenuItem_AdminWindow = new javax.swing.JCheckBoxMenuItem();
        jCheckBoxMenuItem_SpeakerWindow = new javax.swing.JCheckBoxMenuItem();
        jMenu_Help = new javax.swing.JMenu();
        jMenu_About = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Redstart - Admin Window");
        addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentResized(java.awt.event.ComponentEvent evt) {
                formComponentResized(evt);
            }
        });
        addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                formFocusGained(evt);
            }
        });
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
        });

        jPanel_SpeakerView.setBorder(javax.swing.BorderFactory.createTitledBorder("Speaker View"));
        jTextPane_PromptDisplay.setBorder(javax.swing.BorderFactory.createEtchedBorder());
        jTextPane_PromptDisplay.setEditable(false);
        jTextPane_PromptDisplay.setFont(new java.awt.Font("Tahoma", 0, 30));
        jTextPane_PromptDisplay.setText(
                "This is a long and boring test sentence, the only purpose of which is to see how to break between lines without making any difference across the windows.");
        jTextPane_PromptDisplay.setAutoscrolls(false);
        jTextPane_PromptDisplay.addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentResized(java.awt.event.ComponentEvent evt) {
                jTextPane_PromptDisplayComponentResized(evt);
            }
        });

        jTextPane_nextSentence.setBackground(new java.awt.Color(245, 245, 245));
        jTextPane_nextSentence.setBorder(javax.swing.BorderFactory.createEtchedBorder());
        jTextPane_nextSentence.setEditable(false);
        jTextPane_nextSentence.setFont(new java.awt.Font("Tahoma", 0, 24));
        jTextPane_nextSentence.setForeground(new java.awt.Color(50, 50, 50));
        jTextPane_nextSentence.setText(
                "This is a long and boring test sentence, the only purpose of which is to see how to break between lines without making any difference across the windows.");

        jLabel_SessionStatus.setIcon(
                new javax.swing.ImageIcon(getClass().getResource("/marytts/tools/redstart/stopped_48x48.png")));
        jLabel_SessionStatus.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jLabel_SessionStatusMouseClicked(evt);
            }
        });

        org.jdesktop.layout.GroupLayout jPanel_SpeakerViewLayout = new org.jdesktop.layout.GroupLayout(
                jPanel_SpeakerView);
        jPanel_SpeakerView.setLayout(jPanel_SpeakerViewLayout);
        jPanel_SpeakerViewLayout.setHorizontalGroup(jPanel_SpeakerViewLayout
                .createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                .add(org.jdesktop.layout.GroupLayout.TRAILING, jPanel_SpeakerViewLayout.createSequentialGroup()
                        .addContainerGap()
                        .add(jPanel_SpeakerViewLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                                .add(org.jdesktop.layout.GroupLayout.TRAILING, jTextPane_PromptDisplay,
                                        org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 773, Short.MAX_VALUE)
                                .add(org.jdesktop.layout.GroupLayout.TRAILING,
                                        jPanel_SpeakerViewLayout.createSequentialGroup().add(jTextPane_nextSentence,
                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 624, Short.MAX_VALUE)
                                                .add(101, 101, 101).add(jLabel_SessionStatus)))
                        .addContainerGap()));
        jPanel_SpeakerViewLayout.setVerticalGroup(jPanel_SpeakerViewLayout
                .createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                .add(org.jdesktop.layout.GroupLayout.TRAILING, jPanel_SpeakerViewLayout.createSequentialGroup()
                        .add(jTextPane_PromptDisplay, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 131,
                                Short.MAX_VALUE)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(jPanel_SpeakerViewLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
                                .add(jLabel_SessionStatus).add(jTextPane_nextSentence,
                                        org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 124, Short.MAX_VALUE))
                        .addContainerGap()));

        jPanel_AdminControls.setBorder(javax.swing.BorderFactory.createTitledBorder("Admin Controls"));
        jScrollPane_PromptSet.setFocusCycleRoot(true);
        jScrollPane_PromptSet.setNextFocusableComponent(jButton_Record);

        jCheckBox_PlaySynthesis.setText("Play synthesized prompt");
        jCheckBox_PlaySynthesis.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
        jCheckBox_PlaySynthesis.setMargin(new java.awt.Insets(0, 0, 0, 0));

        jCheckBox_PlayBackRec.setText("Play back after recording");
        jCheckBox_PlayBackRec.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
        jCheckBox_PlayBackRec.setMargin(new java.awt.Insets(0, 0, 0, 0));

        jCheckBox_ContinueWithNext.setSelected(true);
        jCheckBox_ContinueWithNext.setText("Continue with next prompt");
        jCheckBox_ContinueWithNext.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
        jCheckBox_ContinueWithNext.setMargin(new java.awt.Insets(0, 0, 0, 0));
        jCheckBox_ContinueWithNext.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jCheckBox_ContinueWithNextActionPerformed(evt);
            }
        });

        jButton_Record.setIcon(
                new javax.swing.ImageIcon(getClass().getResource("/marytts/tools/redstart/recording_16x16.png")));
        jButton_Record.setText("Record");
        jButton_Record.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
        jButton_Record.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
        jButton_Record.setMaximumSize(new java.awt.Dimension(95, 25));
        jButton_Record.setMinimumSize(new java.awt.Dimension(95, 25));
        jButton_Record.setPreferredSize(new java.awt.Dimension(95, 25));
        jButton_Record.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton_RecordActionPerformed(evt);
            }
        });

        jButton_Play.setIcon(
                new javax.swing.ImageIcon(getClass().getResource("/marytts/tools/redstart/playing_16x16.png")));
        jButton_Play.setText("Play");
        jButton_Play.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
        jButton_Play.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
        jButton_Play.setMaximumSize(new java.awt.Dimension(95, 25));
        jButton_Play.setMinimumSize(new java.awt.Dimension(95, 25));
        jButton_Play.setPreferredSize(new java.awt.Dimension(95, 25));
        jButton_Play.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton_PlayActionPerformed(evt);
            }
        });

        jLabel_MessageBar.setText("Ready.");

        jCheckBox_PlayClosingBeep.setSelected(true);
        jCheckBox_PlayClosingBeep.setText("Play beep when microphone is closed");
        jCheckBox_PlayClosingBeep.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
        jCheckBox_PlayClosingBeep.setMargin(new java.awt.Insets(0, 0, 0, 0));

        jLabel_MessageBarIcon.setText("Message:");

        jButton_Display.setText("Display");
        jButton_Display.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton_DisplayActionPerformed(evt);
            }
        });

        org.jdesktop.layout.GroupLayout jPanel_AdminControlsLayout = new org.jdesktop.layout.GroupLayout(
                jPanel_AdminControls);
        jPanel_AdminControls.setLayout(jPanel_AdminControlsLayout);
        jPanel_AdminControlsLayout.setHorizontalGroup(
                jPanel_AdminControlsLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                        .add(org.jdesktop.layout.GroupLayout.TRAILING, jPanel_AdminControlsLayout
                                .createSequentialGroup().addContainerGap().add(jPanel_AdminControlsLayout
                                        .createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING).add(
                                                jScrollPane_PromptSet, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
                                                773, Short.MAX_VALUE)
                                        .add(org.jdesktop.layout.GroupLayout.LEADING, jSeparator_MessageBar,
                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 773, Short.MAX_VALUE)
                                        .add(org.jdesktop.layout.GroupLayout.LEADING,
                                                jPanel_AdminControlsLayout
                                                        .createSequentialGroup().add(jLabel_MessageBarIcon)
                                                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                                                        .add(jLabel_MessageBar))
                                        .add(jPanel_AdminControlsLayout.createSequentialGroup()
                                                .add(jPanel_AdminControlsLayout
                                                        .createParallelGroup(
                                                                org.jdesktop.layout.GroupLayout.LEADING)
                                                        .add(jCheckBox_PlaySynthesis).add(jCheckBox_PlayBackRec)
                                                        .add(jCheckBox_PlayClosingBeep)
                                                        .add(jCheckBox_ContinueWithNext))
                                                .addPreferredGap(
                                                        org.jdesktop.layout.LayoutStyle.RELATED, 410,
                                                        Short.MAX_VALUE)
                                                .add(jPanel_AdminControlsLayout
                                                        .createParallelGroup(
                                                                org.jdesktop.layout.GroupLayout.LEADING)
                                                        .add(jButton_Display,
                                                                org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 98,
                                                                org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                                                        .add(org.jdesktop.layout.GroupLayout.TRAILING,
                                                                jButton_Record,
                                                                org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
                                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
                                                                org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                                                        .add(org.jdesktop.layout.GroupLayout.TRAILING, jButton_Play,
                                                                org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
                                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
                                                                org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))))
                                .addContainerGap()));
        jPanel_AdminControlsLayout.setVerticalGroup(jPanel_AdminControlsLayout
                .createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                .add(org.jdesktop.layout.GroupLayout.TRAILING, jPanel_AdminControlsLayout.createSequentialGroup()
                        .add(jScrollPane_PromptSet, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 95,
                                Short.MAX_VALUE)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(jPanel_AdminControlsLayout
                                .createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING, false)
                                .add(jPanel_AdminControlsLayout.createSequentialGroup()
                                        .add(jButton_Record, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
                                                org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED).add(jButton_Play,
                                                org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
                                                org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                                .add(org.jdesktop.layout.GroupLayout.LEADING, jPanel_AdminControlsLayout
                                        .createSequentialGroup().add(jCheckBox_PlaySynthesis)
                                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                                        .add(jCheckBox_PlayClosingBeep)
                                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED,
                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                        .add(jCheckBox_PlayBackRec)))
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(jPanel_AdminControlsLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                                .add(jCheckBox_ContinueWithNext).add(jButton_Display))
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(jSeparator_MessageBar, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
                                org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(jPanel_AdminControlsLayout
                                .createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                                .add(jLabel_MessageBar).add(jLabel_MessageBarIcon))
                        .addContainerGap()));

        jPanel_AdminControlsLayout.linkSize(new java.awt.Component[] { jButton_Play, jButton_Record },
                org.jdesktop.layout.GroupLayout.VERTICAL);

        jMenu_File.setText("File");
        jMenuItem_Open.setIcon(
                new javax.swing.ImageIcon(getClass().getResource("/marytts/tools/redstart/open_16x16.png")));
        jMenuItem_Open.setText("Open Voice...");
        jMenuItem_Open.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jMenuItem_OpenActionPerformed(evt);
            }
        });

        jMenu_File.add(jMenuItem_Open);

        jMenuItem_ImportText.setText("Import text file...");
        jMenuItem_ImportText.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jMenuItem_ImportTextActionPerformed(evt);
            }
        });

        jMenu_File.add(jMenuItem_ImportText);

        jMenu_File.add(jSeparator_File);

        jMenuItem_Exit.setIcon(
                new javax.swing.ImageIcon(getClass().getResource("/marytts/tools/redstart/exit_16x16.png")));
        jMenuItem_Exit.setText("Exit");
        jMenuItem_Exit.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jMenuItem_ExitActionPerformed(evt);
            }
        });

        jMenu_File.add(jMenuItem_Exit);

        jMenuBar_AdminWindow.add(jMenu_File);

        jMenu_Tools.setText("Tools");
        jMenuItem_Options.setIcon(
                new javax.swing.ImageIcon(getClass().getResource("/marytts/tools/redstart/options_16x16.png")));
        jMenuItem_Options.setText("Options...");
        jMenuItem_Options.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jMenuItem_OptionsActionPerformed(evt);
            }
        });

        jMenu_Tools.add(jMenuItem_Options);

        jMenuBar_AdminWindow.add(jMenu_Tools);

        jMenu_Window.setText("Window");
        jCheckBoxMenuItem_AdminWindow.setSelected(true);
        jCheckBoxMenuItem_AdminWindow.setText("Admin Window");
        jCheckBoxMenuItem_AdminWindow.setEnabled(false);
        jMenu_Window.add(jCheckBoxMenuItem_AdminWindow);

        jCheckBoxMenuItem_SpeakerWindow.setText("Speaker Window");
        jCheckBoxMenuItem_SpeakerWindow.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jCheckBoxMenuItem_SpeakerWindowActionPerformed(evt);
            }
        });

        jMenu_Window.add(jCheckBoxMenuItem_SpeakerWindow);

        jMenuBar_AdminWindow.add(jMenu_Window);

        jMenu_Help.setText("Help");
        jMenu_About.setIcon(
                new javax.swing.ImageIcon(getClass().getResource("/marytts/tools/redstart/about_16x16.png")));
        jMenu_About.setText("About Redstart");
        jMenu_About.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jMenu_AboutActionPerformed(evt);
            }
        });

        jMenu_Help.add(jMenu_About);

        jMenuBar_AdminWindow.add(jMenu_Help);

        setJMenuBar(jMenuBar_AdminWindow);

        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                .add(layout.createSequentialGroup().addContainerGap()
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
                                .add(org.jdesktop.layout.GroupLayout.LEADING, jPanel_AdminControls, 0,
                                        org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .add(org.jdesktop.layout.GroupLayout.LEADING, jPanel_SpeakerView,
                                        org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
                                        org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                        .addContainerGap()));
        layout.setVerticalGroup(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING).add(
                org.jdesktop.layout.GroupLayout.TRAILING,
                layout.createSequentialGroup()
                        .add(jPanel_SpeakerView, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(jPanel_AdminControls, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
                                org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                        .addContainerGap()));
        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void formFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_formFocusGained
        jTable_PromptSet.requestFocusInWindow();
    }//GEN-LAST:event_formFocusGained

    private void jMenuItem_ImportTextActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem_ImportTextActionPerformed
        JFileChooser fc = new JFileChooser(new File(voiceFolderPathString));
        fc.setDialogTitle("Choose text file to import");
        //fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        int returnVal = fc.showOpenDialog(this);
        if (returnVal != JFileChooser.APPROVE_OPTION)
            return;
        File file = fc.getSelectedFile();
        if (file == null)
            return;
        String[] lines = null;
        try {
            lines = StringUtils.readTextFile(file.getAbsolutePath(), "UTF-8");
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        if (lines == null || lines.length == 0)
            return;
        Object[] options = new Object[] { "Keep first column", "Discard first column" };
        int answer = JOptionPane.showOptionDialog(this,
                "File contains " + lines.length + " sentences.\n" + "Sample line:\n" + lines[0] + "\n"
                        + "Keep or discard first column?",
                "Import details", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options,
                options[1]);
        boolean discardFirstColumn = (answer == JOptionPane.NO_OPTION);

        String prefix = (String) JOptionPane.showInputDialog(this,
                "Prefix to use for individual sentence filenames:", "Choose filename prefix",
                JOptionPane.PLAIN_MESSAGE, null, null, "s");
        int numDigits = (int) Math.log10(lines.length) + 1;
        String pattern = prefix + "%0" + numDigits + "d.txt";
        File scriptFile = new File(voiceFolderPathString + "/" + file.getName() + ".script.txt");
        PrintWriter scriptWriter = null;
        try {
            scriptWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(scriptFile), "UTF-8"));
        } catch (IOException e) {
            JOptionPane.showMessageDialog(this,
                    "Cannot write to script file " + scriptFile.getAbsolutePath() + ":\n" + e.getMessage());
            if (scriptWriter != null)
                scriptWriter.close();
            return;
        }
        File textFolder = getPromptFolderPath();

        // if filename ends with ".txt_tr" then it has also transcriptions in it
        String selectedFile_ext = FilenameUtils.getExtension(file.getName());
        Boolean inputHasAlsoTranscription = false;
        File transcriptionFolder = new File("");

        // transcription folder name, and makedir
        if (selectedFile_ext.equals("txt_tr")) {
            System.out.println("txt_tr");
            if (lines.length % 2 == 0) {
                // even
            } else {
                // odd
                System.err.println(".txt_tr file has an odd number of lines, so it's corrupted, exiting.");
                System.exit(0);
            }
            inputHasAlsoTranscription = true;
            String transcriptionFolderName = voiceFolderPathString + AdminWindow.TRANSCRIPTION_FOLDER_NAME;
            transcriptionFolder = new File(transcriptionFolderName);
            if (transcriptionFolder.exists()) {
                System.out.println("transcription folder already exists");
            } else {
                if (transcriptionFolder.mkdirs()) {
                    System.out.println("transcription folder created");
                } else {
                    System.err.println("Cannot create transcription folder -- exiting.");
                    System.exit(0);
                }
            }
        } else {
            System.out.println("input file extension is not txt_tr, but " + selectedFile_ext
                    + ", so it contains ortographic sentences without transcriptions.");
        }

        for (int i = 0; i < lines.length; i++) {
            String line = lines[i];
            if (discardFirstColumn)
                line = line.substring(line.indexOf(' ') + 1);
            int sent_index = i + 1;
            if (inputHasAlsoTranscription == true) {
                sent_index = i / 2 + 1;
            }

            String filename = String.format(pattern, sent_index);
            System.out.println(filename + " " + line);
            File textFile = new File(textFolder, filename);
            if (textFile.exists()) {
                JOptionPane.showMessageDialog(this, "Cannot writing file " + filename + ":\n" + "File exists!\n"
                        + "Aborting text file import.");
                return;
            }
            PrintWriter pw = null;
            try {
                pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(textFile), "UTF-8"));
                pw.println(line);
                scriptWriter.println(filename.substring(0, filename.lastIndexOf('.')) + " " + line);
            } catch (IOException ioe) {
                JOptionPane.showMessageDialog(this, "Error writing file " + filename + ":\n" + ioe.getMessage());
                ioe.printStackTrace();
                return;
            } finally {
                if (pw != null)
                    pw.close();
            }

            // transcription case:
            if (inputHasAlsoTranscription == true) {
                // modify pattern: best would be something like sed "s/.txt$/.tr$/"
                // easy but dirty:
                String transc_pattern = pattern.replace(".txt", ".tr");
                filename = String.format(transc_pattern, sent_index);
                i++;
                line = lines[i];
                if (discardFirstColumn)
                    line = line.substring(line.indexOf(' ') + 1);
                File transcriptionTextFile = new File(transcriptionFolder, filename);
                if (transcriptionTextFile.exists()) {
                    JOptionPane.showMessageDialog(this, "Cannot writing file " + transcriptionTextFile.getName()
                            + ":\n" + "File exists!\n" + "Aborting text file import.");
                    return;
                }
                pw = null;
                try {
                    pw = new PrintWriter(
                            new OutputStreamWriter(new FileOutputStream(transcriptionTextFile), "UTF-8"));
                    pw.println(line);
                    scriptWriter.println(filename.substring(0, filename.lastIndexOf('.')) + " " + line);
                } catch (IOException ioe) {
                    JOptionPane.showMessageDialog(this,
                            "Error writing file " + filename + ":\n" + ioe.getMessage());
                    ioe.printStackTrace();
                    return;
                } finally {
                    if (pw != null)
                        pw.close();
                }
            }

        }
        scriptWriter.close();
        setupVoice();
    }//GEN-LAST:event_jMenuItem_ImportTextActionPerformed

    private void jButton_DisplayActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton_DisplayActionPerformed
        Prompt selectedPrompt = promptArray[getCurrentRow()];
        try {
            File f = selectedPrompt.getRecording().getFile();
            AudioInputStream audio = AudioSystem.getAudioInputStream(f);
            if (audio.getFormat().getChannels() > 1) {
                audio = new MonoAudioInputStream(audio, optionsDialog.getInputMode());
            }
            MultiDisplay d = new MultiDisplay(audio, selectedPrompt.getBasename(), false);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }//GEN-LAST:event_jButton_DisplayActionPerformed

    private void jTextPane_PromptDisplayComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_jTextPane_PromptDisplayComponentResized
        //this.displayPromptText();
    }//GEN-LAST:event_jTextPane_PromptDisplayComponentResized

    private void jCheckBox_ContinueWithNextActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBox_ContinueWithNextActionPerformed
        if (this.jCheckBox_ContinueWithNext.isSelected()) {
            this.continuousMode = true;
        } else {
            this.continuousMode = false;
        }
    }//GEN-LAST:event_jCheckBox_ContinueWithNextActionPerformed

    private void jLabel_SessionStatusMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jLabel_SessionStatusMouseClicked
        // Play a beep (for demo purposes to the speaker; also useful for quickly testing audio setup)
        if (beepDemoOn) {
            optionsDialog.playOpenBeep();
            if (jCheckBox_PlayClosingBeep.isSelected()) {
                beepDemoOn = false;
            }
        } else {
            optionsDialog.playClosedBeep();
            beepDemoOn = true;
        }
    }//GEN-LAST:event_jLabel_SessionStatusMouseClicked

    private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
        System.out.println("Exiting Recording Session Manager... Done.");
    }//GEN-LAST:event_formWindowClosing

    private void jMenuItem_OpenActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem_OpenActionPerformed

        // Allow user to choose a different voice (prompt set) without exiting the tool

        // Create a file chooser
        final JFileChooser openDialog = new JFileChooser();

        // Set the current directory to the voice currently in use
        openDialog.setCurrentDirectory(getVoiceFolderPath());
        openDialog.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        int result = openDialog.showDialog(AdminWindow.this, "Open Voice");

        if (result == JFileChooser.APPROVE_OPTION) {
            File voice = openDialog.getSelectedFile();
            setVoiceFolderPath(voice); // Set to the selected the voice folder path
            Test.output("Open voice: " + voice);
            setupVoice();
        } else {
            Test.output("Open command cancelled.");
        }

    }//GEN-LAST:event_jMenuItem_OpenActionPerformed

    /** Exits the application
     *  @param evt An action event
     */
    private void jMenuItem_ExitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem_ExitActionPerformed
        System.out.println("Exiting Recording Session Manager... Done.");
        System.exit(0);
    }//GEN-LAST:event_jMenuItem_ExitActionPerformed

    /** Shows an options dialog
     *  @param evt An action event
     */
    private void jMenuItem_OptionsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem_OptionsActionPerformed
        optionsDialog.setVisible(true);
    }//GEN-LAST:event_jMenuItem_OptionsActionPerformed

    /** Shows a simple About dialog for the application
     *  @param evt An action event
     */
    private void jMenu_AboutActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenu_AboutActionPerformed
        // PRI4 Could show a more elaborate dialog
        // More info, DFKI logo, etc.
        //JOptionPane.showMessageDialog(this, "Recording Session Manager\n(c) 2007 DFKI GmbH");
        new About(this).setVisible(true);
    }//GEN-LAST:event_jMenu_AboutActionPerformed

    /* Activates or deactivates the speaker window, depending on current state
     * @param evt Action event
     */
    private void jCheckBoxMenuItem_SpeakerWindowActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBoxMenuItem_SpeakerWindowActionPerformed

        // Activate speaker window if checkbox menu item was not selected
        // Else close speaker window if checkbox menu item was already selected
        if (jCheckBoxMenuItem_SpeakerWindow.isSelected()) {
            speakerWin.updateSessionStatus(IconSet.STOP_64X64);
            speakerWin.setVisible(true);
        } else {
            speakerWin.setVisible(false);
        }

    }//GEN-LAST:event_jCheckBoxMenuItem_SpeakerWindowActionPerformed

    /** Resizes columns to fit expected data
     *  @param evt Component event
     */
    private void formComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentResized
        // Note: event also appears to include component initialization, not just resizing
        if (this.isVisible()) {
            setColumnWidths();
            displayPromptText(); // Re-center prompt text
        }
    }//GEN-LAST:event_formComponentResized

    public void setVisible(boolean visible) {
        super.setVisible(visible);
        if (visible) {
            setColumnWidths();
            displayPromptText();
        }
    }

    /* Interrupts recording or playback
     * @param evt Action event
     */
    /** Plays the latest recorded version of the currently selected prompt */
    private void jButton_PlayActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton_PlayActionPerformed
        if (this.playingStatus) {
            stopPlayback(); // Stop playback if underway
            this.stopPressed = true;
            toggleStopToRecord(); // Needed if Play/Stop is pressed during continuous record mode
            this.jButton_Record.setEnabled(true);
            toggleStopToPlay();
        }
        // Otherwise play the recording
        else {
            togglePlayToStop();
            new Thread() {
                public void run() {
                    playRecording();
                }
            }.start();
        }
    }//GEN-LAST:event_jButton_PlayActionPerformed

    /** Handles the recording logic, including potential playback and continuous mode (if selected)
     *  @param evt Action event
     */
    private void jButton_RecordActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton_RecordActionPerformed

        if (this.playingStatus) {
            stopRecord(); // Stop recording if underway
            this.stopPressed = true;
            this.playingStatus = false; // PRI3 Can we combine these two flags or is stopPressed needed for setupRecording()?
            toggleStopToRecord();
            toggleStopToPlay();

            // Disable Record button so that user can't press it while waiting to stop
            this.jButton_Record.setEnabled(false);
            // Re-enabled at end of manageRecording();

            // Temporary solution until method implemented to stop actual recording
            String message = "Stop button pressed.";
            showMessage(message, true); // true = warning, not info
        }
        // Otherwise make the recording
        else {

            // Don't toggle yet if playing synthesized prompt first
            if (!jCheckBox_ContinueWithNext.isSelected()) {
                toggleRecordToStop();
            }
            new Thread() {
                public void run() {
                    manageRecording();
                }
            }.start();
            Test.output("Dispatched recording thread");

        }

    }//GEN-LAST:event_jButton_RecordActionPerformed

    /** Sets the GUI look and feel to that of the system (e.g., Windows XP, Mac OS) */
    private void setSystemLookAndFeel() {

        // Get the properties from the options file
        Properties options = new Properties();
        try {
            // First, load defaults from resource in classpath:
            options.load(Redstart.class.getResourceAsStream("user.options"));

            // Then, overwrite from file if present:
            File fileHandle = new File(optionsDialog.getOptionsPathString());
            if (fileHandle.exists()) {
                FileInputStream optionsStream;
                try {
                    optionsStream = new FileInputStream(fileHandle);
                    options.load(optionsStream);

                    // Close the input stream
                    optionsStream.close();
                } catch (FileNotFoundException ex) {
                    ex.printStackTrace();
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        String systemLookAndFeelProp = options.getProperty("systemLookAndFeel");

        if (systemLookAndFeelProp.equals("true")) {

            // Use the look and feel of the system
            try {
                // Set to system look and feel
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (UnsupportedLookAndFeelException ex) {
                ex.printStackTrace();
            } catch (ClassNotFoundException ex) {
                ex.printStackTrace();
            } catch (InstantiationException ex) {
                ex.printStackTrace();
            } catch (IllegalAccessException ex) {
                ex.printStackTrace();
            }

        }
    }

    /** Returns path for folder containing basenames and prompts
     *  @return Folder path containing basenames and prompts (e.g., /project/mary/mat/voices/bundesliga/text)
     */
    public File getPromptFolderPath() {

        // Create the instructions folder path from the voice folder path
        String pathString = voiceFolderPathString + AdminWindow.PROMPT_FOLDER_NAME;
        File promptFolderPath = new File(pathString);

        return promptFolderPath;
    }

    /** Returns file path for folder containing the speaker recordings
     *  @return File path for folder containing the speaker recordings (e.g., /project/mary/mat/voices/bundesliga/wav)
     */
    public File getRecFolderPath() {

        // Create the recordings folder path from the voice folder path
        String pathString = voiceFolderPathString + AdminWindow.REC_FOLDER_NAME;
        File recFolderPath = new File(pathString);

        // TESTCODE
        Test.output("|AdminWindow.getRecFolderPath()| Recordings Path = " + recFolderPath.getPath());

        return recFolderPath;

    }

    /** Returns file path for folder containing the synthesized recordings
     *  @return File path for folder containing the synthesized recordings (e.g., /project/mary/mat/voices/bundesliga/prompt_wav)
     */
    public File getSynthFolderPath() {

        // Create the recordings folder path from the voice folder path
        String pathString = voiceFolderPathString + AdminWindow.SYNTH_FOLDER_NAME;
        File synthFolderPath = new File(pathString);

        // TESTCODE
        Test.output("|AdminWindow.getSynthFolderPath()| Synthesis Path = " + synthFolderPath.getPath());

        return synthFolderPath;

    }

    /** Returns file path for folder containing the synthesized recordings
     *  @return File path for folder containing the synthesized recordings (e.g., /project/mary/mat/voices/bundesliga/prompt_wav)
     */
    public File getTranscriptionFolderPath() {

        // Create the recordings folder path from the voice folder path
        String pathString = voiceFolderPathString + AdminWindow.TRANSCRIPTION_FOLDER_NAME;
        File transcriptionFolderPath = new File(pathString);

        // TESTCODE
        Test.output("|AdminWindow.getTranscriptionFolderPath()| Transcription Path = "
                + transcriptionFolderPath.getPath());

        return transcriptionFolderPath;
    }

    /** Returns file path for folder containing the voice
     *  @return File path for folder containing the voice (e.g., /project/mary/mat/voices/bundesliga)
     */
    public File getVoiceFolderPath() {
        return new File(voiceFolderPathString);
    }

    protected void setVoiceFolderPath(File newPath) {

        // PRI2 Check that the path is valid

        // Set the new path
        voiceFolderPathString = newPath.getPath();
        optionsDialog.saveVoicePath(newPath);

    }

    public void deselectSpeakerWindow() {
        // Deselect Speaker Window in Window menu now that window has been closed by the user
        this.jMenu_Window.setSelected(false);
    }

    private void stopRecord() {
        Recording currentRecording = promptArray[getCurrentRow()].getRecording();
        currentRecording.stopRecording();
    }

    private void togglePlayToStop() {
        jButton_Play.setText("Stop");
        jButton_Play.setIcon(IconSet.STOP_16X16);
        this.playingStatus = true;
    }

    private void toggleStopToPlay() {
        jButton_Play.setText("Play");
        jButton_Play.setIcon(IconSet.PLAY_16X16);
        this.playingStatus = false;
    }

    private void toggleRecordToStop() {
        jButton_Record.setText("Stop");
        jButton_Record.setIcon(IconSet.STOP_16X16);
        this.playingStatus = true;
    }

    private void toggleStopToRecord() {
        jButton_Record.setText("Record");
        jButton_Record.setIcon(IconSet.REC_16X16);
        this.playingStatus = false;
    }

    protected void showMessage(String message, boolean warning) {
        jLabel_MessageBarIcon.setText(""); // No text, just an icon
        if (warning) {
            jLabel_MessageBarIcon.setIcon(IconSet.WARNING_16X16);
            jLabel_MessageBar.setForeground(Color.RED); // PRI4 Make darker red (more readable than default red)
        } else {
            jLabel_MessageBarIcon.setIcon(IconSet.INFO_16X16);
            jLabel_MessageBar.setForeground(Color.BLACK);
        }
        jLabel_MessageBar.setText(message); // Display the actual message next to the icon
        jLabel_MessageBar.setForeground(Color.BLACK);
    }

    private void setCheckBoxes(boolean flag) {
        this.jCheckBox_ContinueWithNext.setEnabled(flag);
        this.jCheckBox_PlayBackRec.setEnabled(flag);
        this.jCheckBox_PlayClosingBeep.setEnabled(flag);
        this.jCheckBox_PlaySynthesis.setEnabled(flag);
    }

    private void preparePlayback() {
        this.setCheckBoxes(false);

        if (this.recordingBlock) {

            // We're in a recording block so we want to use the Record button as the Stop button
            this.toggleRecordToStop();

            // Disable the Play/Stop button so that user can't accidentally begin playback
            this.jButton_Play.setEnabled(false);
            jButton_Display.setEnabled(false);

        } else {

            // Disable the Record/Stop button so that user can't accidentally begin a recording
            this.jButton_Record.setEnabled(false);

        }
    }

    private void endPlayback() {

        if (!this.continuousMode) {

            // We're not in continuous mode, so return the Record button to normal

            if (this.recordingBlock) {

                // Record button was used as the Stop button

                toggleStopToRecord(); // Update button text (change from "Stop" to "Record")
                this.jButton_Play.setEnabled(true); // Re-enable the Play/Stop button
                jButton_Display.setEnabled(true);
            } else {

                // Play button was used as the Stop button

                toggleStopToPlay(); // Update button text (change from "Stop" to "Play")
                this.jButton_Record.setEnabled(true); // Re-enable the Record/Stop button

            }

        }
        // If we're in continuous mode then we want to leave these buttons as they are

        this.setCheckBoxes(true);

    }

    private String getClippingMessage(Recording rec) {

        String message;

        if ((rec.isAmpClipped) && (rec.isTempClipped)) {
            message = "Amplitude and temporal clipping detected.";
        } else if (rec.isAmpClipped) {
            message = "Amplitude clipping detected.";
        } else if (rec.isTempClipped) {
            message = "Temporal clipping detected. ";
        } else if (rec.isAmpWarning) {
            message = "Near amplitude clipping threshold";
        } else {
            message = "No clipping detected.";
        }

        return message;

    }

    public class ClippingColorRenderer extends DefaultTableCellRenderer {
        public ClippingColorRenderer() {
            super();
            setOpaque(true); //MUST do this for background to show up.
        }

        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                boolean hasFocus, int row, int column) {
            if (promptArray != null && promptArray.length >= row) {
                Prompt currentPrompt = promptArray[row];
                if (currentPrompt != null) {
                    Recording currentRecording = currentPrompt.getRecording();
                    if (currentRecording != null) {
                        // Determine cell colour
                        Color backgroundColor = Color.WHITE; // Reset first to white
                        // If amplitude clipping or temporal clipping occurred, then cell background is pink
                        // Note: Red is too harsh (too saturated)
                        if ((currentRecording.isAmpClipped) || (currentRecording.isTempClipped)) {
                            backgroundColor = Color.PINK;
                        }
                        // Otherwise if within the warning threshold, cell background is yellow
                        else if (currentRecording.isAmpWarning) {
                            backgroundColor = Color.YELLOW;
                        }

                        setBackground(backgroundColor);
                    }
                }
            }
            return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
        }
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton jButton_Display;
    private javax.swing.JButton jButton_Play;
    private javax.swing.JButton jButton_Record;
    private javax.swing.JCheckBoxMenuItem jCheckBoxMenuItem_AdminWindow;
    private javax.swing.JCheckBoxMenuItem jCheckBoxMenuItem_SpeakerWindow;
    private javax.swing.JCheckBox jCheckBox_ContinueWithNext;
    private javax.swing.JCheckBox jCheckBox_PlayBackRec;
    private javax.swing.JCheckBox jCheckBox_PlayClosingBeep;
    private javax.swing.JCheckBox jCheckBox_PlaySynthesis;
    private javax.swing.JLabel jLabel_MessageBar;
    private javax.swing.JLabel jLabel_MessageBarIcon;
    private javax.swing.JLabel jLabel_SessionStatus;
    private javax.swing.JMenuBar jMenuBar_AdminWindow;
    private javax.swing.JMenuItem jMenuItem_Exit;
    private javax.swing.JMenuItem jMenuItem_ImportText;
    private javax.swing.JMenuItem jMenuItem_Open;
    private javax.swing.JMenuItem jMenuItem_Options;
    private javax.swing.JMenuItem jMenu_About;
    private javax.swing.JMenu jMenu_File;
    private javax.swing.JMenu jMenu_Help;
    private javax.swing.JMenu jMenu_Tools;
    private javax.swing.JMenu jMenu_Window;
    private javax.swing.JPanel jPanel_AdminControls;
    private javax.swing.JPanel jPanel_SpeakerView;
    private javax.swing.JScrollPane jScrollPane_PromptSet;
    private javax.swing.JSeparator jSeparator_File;
    private javax.swing.JSeparator jSeparator_MessageBar;
    private javax.swing.JTextPane jTextPane_PromptDisplay;
    private javax.swing.JTextPane jTextPane_nextSentence;
    // End of variables declaration//GEN-END:variables
}