0001: /**********************************************************************************
0002: * $URL$
0003: * $Id$
0004: ***********************************************************************************
0005: *
0006: * Copyright (c) 2005, 2006 The Sakai Foundation.
0007: *
0008: * Licensed under the Educational Community License, Version 1.0 (the"License");
0009: * you may not use this file except in compliance with the License.
0010: * You may obtain a copy of the License at
0011: *
0012: * http://www.opensource.org/licenses/ecl1.php
0013: *
0014: * Unless required by applicable law or agreed to in writing, software
0015: * distributed under the License is distributed on an "AS IS" BASIS,
0016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: * See the License for the specific language governing permissions and
0018: * limitations under the License.
0019: *
0020: **********************************************************************************/package org.sakaiproject.tool.assessment.audio;
0021:
0022: import java.io.BufferedInputStream;
0023: import java.io.BufferedOutputStream;
0024: import java.io.ByteArrayInputStream;
0025: import java.io.ByteArrayOutputStream;
0026: import java.io.DataInputStream;
0027: import java.io.File;
0028: import java.io.FileInputStream;
0029: import java.io.IOException;
0030: import java.io.OutputStream;
0031: import java.net.URL;
0032: import java.net.URLConnection;
0033: import java.util.Locale;
0034: import java.util.ResourceBundle;
0035: import java.util.Vector;
0036: import javax.sound.sampled.AudioFileFormat;
0037: import javax.sound.sampled.AudioFormat;
0038: import javax.sound.sampled.AudioInputStream;
0039: import javax.sound.sampled.AudioSystem;
0040: import javax.sound.sampled.DataLine;
0041: import javax.sound.sampled.LineUnavailableException;
0042: import javax.sound.sampled.SourceDataLine;
0043: import javax.sound.sampled.TargetDataLine;
0044:
0045: import java.awt.BorderLayout;
0046: import java.awt.Color;
0047: import java.awt.Dimension;
0048: import java.awt.Font;
0049: import java.awt.Graphics;
0050: import java.awt.GridLayout;
0051: import java.awt.Toolkit;
0052: import java.awt.event.ActionEvent;
0053: import java.awt.event.ActionListener;
0054: import java.awt.event.WindowAdapter;
0055: import java.awt.event.WindowEvent;
0056: import javax.swing.Action;
0057: import javax.swing.AbstractAction;
0058: import javax.swing.BoxLayout;
0059: import javax.swing.ImageIcon;
0060: import javax.swing.JButton;
0061: import javax.swing.JFileChooser;
0062: import javax.swing.JFrame;
0063: import javax.swing.JLabel;
0064: import javax.swing.JPanel;
0065: import javax.swing.JTextField;
0066: import javax.swing.SwingConstants;
0067: import javax.swing.Timer;
0068: import javax.swing.border.Border;
0069: import javax.swing.border.CompoundBorder;
0070: import javax.swing.border.EmptyBorder;
0071: import javax.swing.border.SoftBevelBorder;
0072: import javax.swing.filechooser.FileFilter;
0073: import java.io.InputStream;
0074:
0075: /**
0076: * Record audio in different formats and then playback the recorded audio. The
0077: * captured audio can be saved either as a WAVE, AU or AIFF. Or load an audio
0078: * file for streaming playback.
0079: *
0080: * @version
0081: * @(#)CapturePlayback.java 1.11 99/12/03
0082: * @author Brian Lichtenwalter
0083: * @author Ed Smiley numerous modifications
0084: */
0085: public class AudioRecorder extends JPanel implements ActionListener,
0086: AudioControlContext {
0087:
0088: static ResourceBundle res;
0089:
0090: final int bufSize = 16384;
0091:
0092: FormatControls formatControls = null;
0093:
0094: Capture capture = new Capture();
0095:
0096: Playback playback = new Playback();
0097:
0098: AudioInputStream audioInputStream;
0099:
0100: SamplingGraph samplingGraph;
0101:
0102: Timer timer;
0103:
0104: JButton playB, captB;
0105:
0106: JTextField textField;
0107:
0108: JTextField rtextField;
0109:
0110: JLabel statusLabel = new JLabel("", SwingConstants.LEFT);
0111:
0112: String fileName;
0113:
0114: String agentId;
0115:
0116: String errStr;
0117:
0118: double duration, seconds;
0119:
0120: int attempts;
0121:
0122: File file;
0123:
0124: Vector lines = new Vector();
0125:
0126: AudioRecorderParams params;
0127:
0128: String imageUrl;
0129:
0130: ImageIcon recordIcon = null;
0131:
0132: ImageIcon playIcon = null;
0133:
0134: ImageIcon stopIcon = null;
0135:
0136: public AudioRecorder(AudioRecorderParams params) {
0137: res = AudioUtil.getInstance().getResourceBundle();
0138: fileName = res.getString("default_file_name");
0139:
0140: this .params = params;
0141: if (params.getAttemptsRemaining() == -1) {
0142: params.setAttemptsRemaining(params.getAttemptsAllowed());
0143: }
0144:
0145: // load images
0146: imageUrl = params.getImageUrl();
0147: // System.out.println("**** imageUrl="+imageUrl);
0148: try {
0149: recordIcon = new ImageIcon(new URL(imageUrl
0150: + "/audio_record.gif"));
0151: playIcon = new ImageIcon(new URL(imageUrl
0152: + "/audio_play.gif"));
0153: stopIcon = new ImageIcon(new URL(imageUrl
0154: + "/audio_stop.gif"));
0155: } catch (Exception ex) {
0156: reportStatus("**** cannot create image icons for applet:"
0157: + ex.toString());
0158: }
0159:
0160: EmptyBorder eb = new EmptyBorder(5, 5, 5, 5);
0161: SoftBevelBorder sbb = new SoftBevelBorder(
0162: SoftBevelBorder.LOWERED);
0163: Border b = new CompoundBorder(eb, sbb);
0164:
0165: formatControls = new FormatControls(params);
0166:
0167: /*
0168: * Samigo 2.2 does not required Advanced Settings, so comment it out
0169: * setLayout(new BorderLayout()); setBorder(new EmptyBorder(5, 5, 5,
0170: * 5)); JPanel p1 = new JPanel(); p1.setLayout(new BoxLayout(p1,
0171: * BoxLayout.X_AXIS)); p1.add(formatControls);
0172: */
0173:
0174: JPanel p2 = new JPanel();
0175: p2.setLayout(new BoxLayout(p2, BoxLayout.Y_AXIS));
0176: p2.add(BorderLayout.WEST, makeAttemptsAllowedLabel());
0177: p2.add(makeAudioButtonsPanel());
0178:
0179: JPanel samplingPanel = makeAudioSamplingPanel(b);
0180: p2.add(samplingPanel);
0181:
0182: JPanel savePanel = new JPanel();
0183: savePanel.setLayout(new BoxLayout(savePanel, BoxLayout.Y_AXIS));
0184: JPanel saveTFpanel = makeSaveTFPanel();
0185: savePanel.add(saveTFpanel);
0186: p2.add(savePanel);
0187: add(p2);
0188:
0189: // p1.add(p2);
0190: // add(p1);
0191: }
0192:
0193: private JPanel makeSaveTFPanel() {
0194: GridLayout grid = new GridLayout(2, 2);
0195: JPanel saveTFpanel = new JPanel(grid);
0196: JLabel flabel = new JLabel(res
0197: .getString("current_recordig_length"),
0198: SwingConstants.LEFT);
0199: JLabel rlabel = new JLabel(res.getString("attempts_remaining"),
0200: SwingConstants.LEFT);
0201: saveTFpanel.add(flabel);
0202: saveTFpanel.add(textField = new JTextField(""
0203: + params.getCurrentRecordingLength()));
0204: saveTFpanel.add(rlabel);
0205:
0206: if (params.getAttemptsRemaining() >= 9999) {
0207: saveTFpanel.add(rtextField = new JTextField("Unlimited"));
0208: } else if (params.getAttemptsRemaining() > 0
0209: && params.getAttemptsRemaining() < 9999)
0210: saveTFpanel.add(rtextField = new JTextField(""
0211: + params.getAttemptsRemaining()));
0212: else
0213: saveTFpanel.add(rtextField = new JTextField("0"));
0214:
0215: textField.setEditable(false);
0216: rtextField.setEditable(false);
0217: Font font = new Font("Ariel", Font.PLAIN, 11);
0218: flabel.setFont(font);
0219: rlabel.setFont(font);
0220: textField.setFont(font);
0221: rtextField.setFont(font);
0222: return saveTFpanel;
0223: }
0224:
0225: private JPanel makeAudioButtonsPanel() {
0226: JPanel buttonsPanel = new JPanel();
0227: if (params.getAttemptsRemaining() > 0
0228: || params.getAttemptsRemaining() == -1) {
0229: captB = addButton(res.getString("Record"), buttonsPanel,
0230: true, params.isEnableRecord());
0231: if (recordIcon != null)
0232: captB.setIcon(recordIcon);
0233: } else {
0234: captB = addButton(res.getString("Record"), buttonsPanel,
0235: false, params.isEnableRecord());
0236: if (recordIcon != null)
0237: captB.setIcon(recordIcon);
0238: }
0239: playB = addButton(res.getString("Play"), buttonsPanel, false,
0240: params.isEnablePlay());
0241: if (playIcon != null)
0242: playB.setIcon(playIcon);
0243: return buttonsPanel;
0244: }
0245:
0246: private JPanel makeAudioSamplingPanel(Border b) {
0247: JPanel samplingPanel = new JPanel(new BorderLayout());
0248: samplingPanel.setBorder(b);
0249: samplingPanel.setPreferredSize(new Dimension(450, 150));
0250: samplingPanel.add(samplingGraph = new SamplingGraph());
0251: return samplingPanel;
0252: }
0253:
0254: public void open() {
0255: /*
0256: * daisy test code - pls do not delete String mediaUrl =
0257: * "http://sakai-l.stanford.edu:8080/samigo/servlet/ShowMedia?mediaId=107";
0258: * //String mediaUrl =
0259: * "http://sakai-l.stanford.edu:8080/samigo/spacemusic.au";
0260: * System.out.println("*****open applet="+mediaUrl);
0261: * createAudioInputStream(mediaUrl, true);
0262: */
0263: }
0264:
0265: public void close() {
0266: if (playback.thread != null) {
0267: playB.doClick(0);
0268: }
0269: if (capture.thread != null) {
0270: captB.doClick(0);
0271: }
0272: }
0273:
0274: /**
0275: * Add a button to a panel, enabled or disabled, visible or invisible. We
0276: * create invisible buttons if their function is turned off in our
0277: * configuration, so that the UI logic always works the same way.
0278: *
0279: * @param name
0280: * @param p
0281: * the panel
0282: * @param state
0283: * enabled/disabled state
0284: * @param visible
0285: * visible/invisible state
0286: * @return
0287: */
0288: private JButton addButton(String name, JPanel p, boolean state,
0289: boolean visible) {
0290: JButton b = new JButton(name);
0291: b.addActionListener(this );
0292: b.setEnabled(state);
0293: b.setVisible(visible);
0294: p.add(b);
0295: return b;
0296: }
0297:
0298: public void actionPerformed(ActionEvent e) {
0299: Object obj = e.getSource();
0300:
0301: statusLabel.setVisible(false);
0302: statusLabel.setText("");
0303: if (obj.equals(playB)) {
0304: if (playB.getText().startsWith(res.getString("Play"))) {
0305: playback.start();
0306: samplingGraph.start();
0307: captB.setEnabled(false);
0308: playB.setText(" " + res.getString("playB_Text"));
0309: if (stopIcon != null)
0310: playB.setIcon(stopIcon);
0311: } else {
0312: playback.stop();
0313: samplingGraph.stop();
0314: if (params.getAttemptsAllowed() > 0) {
0315: if (attempts == 0)
0316: captB.setEnabled(false);
0317: else
0318: captB.setEnabled(true);
0319: if (recordIcon != null)
0320: captB.setIcon(recordIcon);
0321: }
0322: playB.setText(res.getString("Play"));
0323: if (playIcon != null)
0324: playB.setIcon(playIcon);
0325: }
0326: } else if (obj.equals(captB)) {
0327: if (captB.getText().startsWith(res.getString("Record"))) {
0328: file = null;
0329: capture.start();
0330: fileName = res.getString("default_file_name");
0331: samplingGraph.start();
0332: playB.setEnabled(false);
0333: if (playIcon != null)
0334: playB.setIcon(playIcon);
0335: captB.setText(" " + res.getString("playB_Text"));
0336: if (stopIcon != null)
0337: captB.setIcon(stopIcon);
0338: startTimer();
0339: } else {
0340: statusLabel.setText(res.getString("processing"));
0341: statusLabel.setVisible(true);
0342: playB.setEnabled(false);
0343: captB.setEnabled(false);
0344: captureAudio();
0345: if (recordIcon != null)
0346: captB.setIcon(recordIcon);
0347: }
0348: }
0349: }
0350:
0351: private void resetAttempts(int attempts0) {
0352: if (attempts0 > 0) {
0353: rtextField.setText("" + attempts0);
0354: }
0355: }
0356:
0357: public JPanel getFormatControlsPanel() {
0358: return formatControls;
0359: }
0360:
0361: public void createAudioInputStream(File file,
0362: boolean updateComponents) {
0363: if (file != null && file.isFile()) {
0364: try {
0365: this .file = file;
0366: errStr = null;
0367: audioInputStream = AudioSystem
0368: .getAudioInputStream(file);
0369: // playB.setEnabled(true);
0370: fileName = file.getName();
0371: long milliseconds = (long) ((audioInputStream
0372: .getFrameLength() * 1000) / audioInputStream
0373: .getFormat().getFrameRate());
0374: duration = milliseconds / 1000.0;
0375: if (updateComponents) {
0376: formatControls.setFormat(audioInputStream
0377: .getFormat());
0378: samplingGraph.createWaveForm(null, lines,
0379: audioInputStream);
0380: }
0381: } catch (Exception ex) {
0382: reportStatus(ex.toString());
0383: }
0384: } else {
0385: reportStatus(res.getString("Audio_file_required_"));
0386: }
0387: }
0388:
0389: /**
0390: * Post audio data directly.
0391: *
0392: * @param audioType
0393: * the audio type string
0394: * @param urlString
0395: * the url (in applets must use getCodeBase().toString() +
0396: * same-host relative url)
0397: * @param inputStream
0398: * the input stream
0399: * @param attemptsLeft
0400: * attempts left
0401: */
0402: public void saveAndPost(InputStream inputStream,
0403: final AudioFileFormat.Type audioType,
0404: final String urlString, int attemptsLeft, final boolean post) {
0405: Thread saveAndPostThread = new Thread() {
0406: public void run() {
0407: if (audioInputStream == null) {
0408: reportStatus(res.getString("No_loaded_audio_to"));
0409: return;
0410: }
0411: // reset to the beginnning of the captured data
0412: try {
0413: audioInputStream.reset();
0414: } catch (Exception ex) {
0415: reportStatus(res.getString("Unable_to_reset") + ex);
0416: return;
0417: }
0418:
0419: if (post)
0420: postAudio(audioType, urlString);
0421: // hide the statusLabel and enable buttons after we saved the
0422: // media
0423: statusLabel.setVisible(false);
0424: statusLabel.setText("");
0425: playB.setEnabled(true);
0426: if (attempts == 0)
0427: captB.setEnabled(false);
0428: else
0429: captB.setEnabled(true);
0430:
0431: samplingGraph.repaint();
0432: } // end of run
0433: }; // end of saveAndPostThread
0434: saveAndPostThread.start();
0435: }
0436:
0437: public void postAudio(AudioFileFormat.Type audioType,
0438: String urlString) {
0439: String suffix = getAudioSuffix();
0440: URL url;
0441: URLConnection urlConn;
0442: try {
0443: // URL of audio processing servlet
0444: // note for applet security, this must be on same host
0445: agentId = params.getAgentId();
0446: String queryString = "&agent=" + agentId + "&lastDuration="
0447: + duration + "&suffix=" + suffix;
0448: url = new URL(urlString + queryString);
0449: urlConn = url.openConnection();
0450: urlConn.setDoInput(true);
0451: urlConn.setDoOutput(true);
0452:
0453: // No caching, we want the real thing.
0454: urlConn.setUseCaches(false);
0455:
0456: urlConn.setRequestProperty("CONTENT-TYPE",
0457: getMimeType(audioType));
0458: // Send binary POST output.
0459: OutputStream outputStream = urlConn.getOutputStream();
0460:
0461: try {
0462: // System.out.println("**** no. of bytes
0463: // recorded="+audioInputStream.available());
0464: int c = AudioSystem.write(audioInputStream, audioType,
0465: outputStream);
0466: if (c <= 0) {
0467: throw new IOException(res
0468: .getString("Problems_writing_to"));
0469: }
0470: outputStream.flush();
0471: outputStream.close();
0472:
0473: // Get response data.
0474: String reportStr = res.getString("contentlenw") + ": "
0475: + c + " " + res.getString("bytes") + ".\n ";
0476: DataInputStream input = new DataInputStream(urlConn
0477: .getInputStream());
0478:
0479: // need to check that acknowlegement from server matches or
0480: // display
0481: String str;
0482: while (null != ((str = input.readLine()))) {
0483: reportStr += str;
0484: }
0485: input.close();
0486: // mock up doesn't require report, let's comment it out
0487: // reportStatus(reportStr + "\n");
0488: } catch (Exception ex) {
0489: reportStatus(ex.toString());
0490: }
0491: } catch (IOException ex) {
0492: reportStatus(ex.toString());
0493: }
0494: }
0495:
0496: private void reportStatus(String msg) {
0497: if ((errStr = msg) != null) {
0498: System.out.println(errStr);
0499: samplingGraph.repaint();
0500: }
0501: }
0502:
0503: /**
0504: * Write data to the OutputChannel.
0505: */
0506: public class Playback implements Runnable {
0507:
0508: SourceDataLine line;
0509:
0510: Thread thread;
0511:
0512: public void start() {
0513: errStr = null;
0514: thread = new Thread(this );
0515: thread.setName(res.getString("thread_Name"));
0516: thread.start();
0517: }
0518:
0519: public void stop() {
0520: thread = null;
0521: }
0522:
0523: private void shutDown(String message) {
0524: if ((errStr = message) != null) {
0525: System.err.println(errStr);
0526: samplingGraph.repaint();
0527: }
0528: if (thread != null) {
0529: thread = null;
0530: samplingGraph.stop();
0531: if (params.getAttemptsAllowed() > 0) {
0532: if (attempts == 0)
0533: captB.setEnabled(false);
0534: else
0535: captB.setEnabled(true);
0536:
0537: }
0538: playB.setText(res.getString("Play"));
0539: if (playIcon != null)
0540: playB.setIcon(playIcon);
0541: }
0542: }
0543:
0544: public void run() {
0545: // reload the file if loaded by file
0546: if (file != null) {
0547: createAudioInputStream(file, false);
0548: }
0549:
0550: // make sure we have something to play
0551: if (audioInputStream == null) {
0552: shutDown(res.getString("No_loaded_audio_to1"));
0553: return;
0554: }
0555: // reset to the beginnning of the stream
0556: try {
0557: audioInputStream.reset();
0558: } catch (Exception e) {
0559: shutDown(res.getString("Unable_to_reset_the") + e);
0560: return;
0561: }
0562:
0563: // get an AudioInputStream of the desired format for playback
0564: AudioFormat format = formatControls.getFormat();
0565: AudioInputStream playbackInputStream = AudioSystem
0566: .getAudioInputStream(format, audioInputStream);
0567:
0568: if (playbackInputStream == null) {
0569: shutDown(res.getString("Unable_to_convert")
0570: + audioInputStream + res.getString("to_format")
0571: + format);
0572: return;
0573: }
0574:
0575: // define the required attributes for our line,
0576: // and make sure a compatible line is supported.
0577:
0578: DataLine.Info info = new DataLine.Info(
0579: SourceDataLine.class, format);
0580: if (!AudioSystem.isLineSupported(info)) {
0581: shutDown(res.getString("Line_matching") + info
0582: + res.getString("not_supported_"));
0583: return;
0584: }
0585:
0586: // get and open the source data line for playback.
0587:
0588: try {
0589: line = (SourceDataLine) AudioSystem.getLine(info);
0590: line.open(format, bufSize);
0591: } catch (LineUnavailableException ex) {
0592: shutDown(res.getString("Unable_to_open_the") + ex);
0593: return;
0594: }
0595:
0596: // play back the captured audio data
0597:
0598: int frameSizeInBytes = format.getFrameSize();
0599: int bufferLengthInFrames = line.getBufferSize() / 8;
0600: int bufferLengthInBytes = bufferLengthInFrames
0601: * frameSizeInBytes;
0602: byte[] data = new byte[bufferLengthInBytes];
0603: int numBytesRead = 0;
0604:
0605: // start the source data line
0606: line.start();
0607:
0608: while (thread != null) {
0609: try {
0610: if ((numBytesRead = playbackInputStream.read(data)) == -1) {
0611: break;
0612: }
0613: int numBytesRemaining = numBytesRead;
0614: while (numBytesRemaining > 0) {
0615: numBytesRemaining -= line.write(data, 0,
0616: numBytesRemaining);
0617: }
0618: } catch (Exception e) {
0619: shutDown(res.getString("Error_during_playback") + e);
0620: break;
0621: }
0622: }
0623: // we reached the end of the stream. let the data play out, then
0624: // stop and close the line.
0625: if (thread != null) {
0626: line.drain();
0627: }
0628: line.stop();
0629: line.close();
0630: line = null;
0631: shutDown(null);
0632: }
0633: } // End class Playback
0634:
0635: /**
0636: * Reads data from the input channel and writes to the output stream
0637: */
0638: class Capture implements Runnable {
0639:
0640: TargetDataLine line;
0641:
0642: Thread thread;
0643:
0644: public void start() {
0645: errStr = null;
0646: thread = new Thread(this );
0647: thread.setName(res.getString("thread_Name1"));
0648: thread.start();
0649: }
0650:
0651: public void stop() {
0652: thread = null;
0653: }
0654:
0655: private void shutDown(String message) {
0656: if ((errStr = message) != null && thread != null) {
0657: thread = null;
0658: samplingGraph.stop();
0659: playB.setEnabled(true);
0660: captB.setText(res.getString("Record"));
0661: System.err.println(errStr);
0662: samplingGraph.repaint();
0663: }
0664: }
0665:
0666: public void run() {
0667:
0668: duration = 0;
0669: audioInputStream = null;
0670:
0671: // define the required attributes for our line,
0672: // and make sure a compatible line is supported.
0673:
0674: AudioFormat format = formatControls.getFormat();
0675: DataLine.Info info = new DataLine.Info(
0676: TargetDataLine.class, format);
0677:
0678: if (!AudioSystem.isLineSupported(info)) {
0679: shutDown(res.getString("Line_matching") + info
0680: + res.getString("not_supported_"));
0681: return;
0682: }
0683:
0684: // get and open the target data line for capture.
0685:
0686: try {
0687: line = (TargetDataLine) AudioSystem.getLine(info);
0688: line.open(format, line.getBufferSize());
0689: } catch (LineUnavailableException ex) {
0690: shutDown(res.getString("Unable_to_open_the") + ex);
0691: return;
0692: } catch (SecurityException ex) {
0693: shutDown(ex.toString());
0694: AudioConfigHelp help = new AudioConfigHelp(true, false,
0695: true, true);
0696: help.configHelp();
0697: return;
0698: } catch (Exception ex) {
0699: shutDown(ex.toString());
0700: return;
0701: }
0702:
0703: // play back the captured audio data
0704: ByteArrayOutputStream out = new ByteArrayOutputStream();
0705: int frameSizeInBytes = format.getFrameSize();
0706: int bufferLengthInFrames = line.getBufferSize() / 8;
0707: int bufferLengthInBytes = bufferLengthInFrames
0708: * frameSizeInBytes;
0709: byte[] data = new byte[bufferLengthInBytes];
0710: int numBytesRead;
0711:
0712: line.start();
0713:
0714: while (thread != null) {
0715: if ((numBytesRead = line.read(data, 0,
0716: bufferLengthInBytes)) == -1) {
0717: break;
0718: }
0719: out.write(data, 0, numBytesRead);
0720: }
0721:
0722: // we reached the end of the stream. stop and close the line.
0723: line.stop();
0724: line.close();
0725: line = null;
0726:
0727: // stop and close the output stream
0728: try {
0729: out.flush();
0730: out.close();
0731: } catch (IOException ex) {
0732: ex.printStackTrace();
0733: }
0734:
0735: // load bytes into the audio input stream for playback
0736:
0737: byte audioBytes[] = out.toByteArray();
0738: ByteArrayInputStream bais = new ByteArrayInputStream(
0739: audioBytes);
0740: audioInputStream = new AudioInputStream(bais, format,
0741: audioBytes.length / frameSizeInBytes);
0742:
0743: long milliseconds = (long) ((audioInputStream
0744: .getFrameLength() * 1000) / format.getFrameRate());
0745: duration = milliseconds / 1000.0;
0746:
0747: try {
0748: audioInputStream.reset();
0749: } catch (Exception ex) {
0750: ex.printStackTrace();
0751: return;
0752: }
0753:
0754: samplingGraph.createWaveForm(audioBytes, lines,
0755: audioInputStream);
0756: }
0757: } // End class Capture
0758:
0759: /**
0760: * Controls for the AudioFormat.
0761: */
0762: class FormatControls extends AudioFormatPanel implements
0763: AudioControlContext {
0764: FormatControls(AudioRecorderParams params) {
0765: super (params);
0766: }
0767:
0768: public void open() {
0769: }
0770:
0771: public void close() {
0772: if (playback.thread != null) {
0773: playB.doClick(0);
0774: }
0775: if (capture.thread != null) {
0776: captB.doClick(0);
0777: }
0778:
0779: }
0780: } // End class FormatControls
0781:
0782: /**
0783: * Render a WaveForm.
0784: */
0785: class SamplingGraph extends AudioSampleGraphPanel implements
0786: Runnable {
0787:
0788: private Thread thread;
0789:
0790: public void reportGraphStatus(String message) {
0791: reportStatus(message);
0792: }
0793:
0794: public void paint(Graphics g) {
0795: AudioSamplingData data = new AudioSamplingData();
0796: data.setAudioInputStream(audioInputStream);
0797: data.setCapture(capture);
0798: data.setCaptureThread(capture.thread);
0799: data.setDuration(duration);
0800: data.setErrStr(errStr);
0801: data.setFileName(fileName);
0802: data.setLine(lines);
0803: data.setSeconds(seconds);
0804: data.setMaxSeconds(params.getMaxSeconds());
0805:
0806: paintData(g, data);
0807:
0808: }
0809:
0810: public void start() {
0811: thread = new Thread(this );
0812: thread.setName(this .res.getString("thread_Name2"));
0813: thread.start();
0814: seconds = 0;
0815: }
0816:
0817: public void stop() {
0818: if (thread != null) {
0819: thread.interrupt();
0820: }
0821: thread = null;
0822: }
0823:
0824: public void run() {
0825: seconds = 0;
0826: while (thread != null) {
0827: if ((playback.line != null) && (playback.line.isOpen())) {
0828:
0829: long milliseconds = (long) (playback.line
0830: .getMicrosecondPosition() / 1000);
0831: seconds = milliseconds / 1000.0;
0832: } else if ((capture.line != null)
0833: && (capture.line.isActive())) {
0834:
0835: long milliseconds = (long) (capture.line
0836: .getMicrosecondPosition() / 1000);
0837: seconds = milliseconds / 1000.0;
0838: }
0839:
0840: try {
0841: thread.sleep(100);
0842: } catch (Exception e) {
0843: break;
0844: }
0845:
0846: repaint();
0847:
0848: while ((capture.line != null && !capture.line
0849: .isActive())
0850: || (playback.line != null && !playback.line
0851: .isOpen())) {
0852: try {
0853: thread.sleep(10);
0854: } catch (Exception e) {
0855: break;
0856: }
0857: }
0858: }
0859: seconds = 0;
0860: repaint();
0861: }
0862: } // End class SamplingGraph
0863:
0864: public static void main(String s[]) {
0865: AudioRecorderParams params = new AudioRecorderParams();
0866: AudioRecorder capturePlayback = new AudioRecorder(params);
0867: capturePlayback.open();
0868: JFrame f = new JFrame(res.getString("Capture_Playback"));
0869: f.addWindowListener(new WindowAdapter() {
0870: public void windowClosing(WindowEvent e) {
0871: System.exit(0);
0872: }
0873: });
0874: f.getContentPane().add("Center", capturePlayback);
0875: f.pack();
0876: Dimension screenSize = Toolkit.getDefaultToolkit()
0877: .getScreenSize();
0878: int w = 600;
0879: int h = 250;
0880: f.setLocation(screenSize.width / 2 - w / 2, screenSize.height
0881: / 2 - h / 2);
0882: f.setSize(w, h);
0883: f.show();
0884: }
0885:
0886: private JPanel makeAttemptsAllowedLabel() {
0887: GridLayout grid = new GridLayout(2, 3);
0888: JPanel panel = new JPanel(grid);
0889: JLabel label1 = new JLabel(res.getString("attempts_allowed")
0890: + " " + params.getAttemptsAllowed(),
0891: SwingConstants.LEFT);
0892: if (params.getAttemptsAllowed() >= 9999) {
0893: label1 = new JLabel(res.getString("attempts_allowed") + " "
0894: + res.getString("unlimited"), SwingConstants.LEFT);
0895: }
0896: JLabel label2 = new JLabel(res.getString("time_limit") + " "
0897: + params.getMaxSeconds() + " sec", SwingConstants.LEFT);
0898: Font font = new Font("Ariel", Font.PLAIN, 11);
0899: label1.setFont(font);
0900: label2.setFont(font);
0901:
0902: Font statusFont = new Font("Ariel", Font.BOLD, 12);
0903: statusLabel = new JLabel("", SwingConstants.CENTER);
0904: statusLabel.setFont(statusFont);
0905: statusLabel.setForeground(Color.red);
0906:
0907: panel.add(label2);
0908: panel.add(statusLabel);
0909: panel.add(label1);
0910: return panel;
0911: }
0912:
0913: private String getAudioSuffix() {
0914: String suffix = "au";
0915: if (params.isSaveWave())
0916: suffix = "wav";
0917: if (params.isSaveAiff())
0918: suffix = "aif";
0919: return suffix;
0920: }
0921:
0922: private AudioFileFormat.Type getAudioType() {
0923: AudioFileFormat.Type type = AudioFileFormat.Type.AU;
0924: if (params.isSaveWave())
0925: type = AudioFileFormat.Type.WAVE;
0926: if (params.isSaveAiff())
0927: type = AudioFileFormat.Type.AIFF;
0928: return type;
0929: }
0930:
0931: private void saveMedia() {
0932: AudioFileFormat.Type type = getAudioType();
0933:
0934: attempts = params.getAttemptsRemaining();
0935:
0936: if (attempts > 0) {
0937: if (attempts < 9999) {
0938: params.setAttemptsRemaining(--attempts);
0939: rtextField.setText("" + attempts);
0940: }
0941:
0942: if (params.isSaveToUrl()) {
0943: saveAndPost(audioInputStream, type, params.getUrl(),
0944: attempts, true);
0945: } else {
0946: // this is for preview when u just wnat to keep the recording at
0947: // client
0948: saveAndPost(audioInputStream, type, params.getUrl(),
0949: attempts, false);
0950: }
0951:
0952: // earlier we add 1sec leeway for the applet to load after the timer
0953: // start. to avoid
0954: // alarm user, the duration value shown wuld not be over max seconds
0955: // allowed.
0956: // However, for record keeping, the actual duration is saved.
0957: if (duration > params.getMaxSeconds())
0958: textField.setText("" + params.getMaxSeconds());
0959: else
0960: textField.setText("" + duration);
0961: if (attempts == 0)
0962: captB.setEnabled(false);
0963: ;
0964: }
0965: }
0966:
0967: private String getMimeType(AudioFileFormat.Type audioType) {
0968: String mimeType = "audio/basic";
0969:
0970: if (audioType.equals(AudioFileFormat.Type.AIFF)) {
0971: mimeType = "audio/x-aiff";
0972: } else if (audioType.equals(AudioFileFormat.Type.WAVE)) {
0973: mimeType = "audio/x-wav";
0974: }
0975: return mimeType;
0976: }
0977:
0978: private void captureAudio() {
0979: // timer was started by clicking Record to enforce time limit
0980: if (timer != null) {
0981: timer.stop();
0982: }
0983:
0984: lines.removeAllElements();
0985: capture.stop();
0986: samplingGraph.stop();
0987: // playB.setEnabled(true);
0988: captB.setText(res.getString("Record"));
0989: int retry = 1;
0990: while (audioInputStream == null && retry < 5) {
0991: retry++;
0992: try {
0993: Thread.sleep(1000);
0994: } catch (Exception ex) {
0995: System.out.println(ex.getMessage());
0996: }
0997: }
0998: saveMedia();
0999: }
1000:
1001: private void startTimer() {
1002: Action stopRecordingAction = new AbstractAction() {
1003: public void actionPerformed(ActionEvent e) {
1004: // reportStatus(res.getString("time_passed")+"\n" );
1005: statusLabel.setText(res.getString("time_expired"));
1006: statusLabel.setVisible(true);
1007: playB.setEnabled(false);
1008: captB.setEnabled(false);
1009: captureAudio();
1010: }
1011: };
1012: // allow 1sec for leeway in case the page loads slow
1013: timer = new Timer(params.getMaxSeconds() * 1000 + 1000,
1014: stopRecordingAction);
1015: timer.start();
1016: }
1017:
1018: /* Daisy test code for loading media back to applet - pls do not delete */
1019: public void createAudioInputStream(String mediaUrl,
1020: boolean updateComponents) {
1021: try {
1022: URL url = new URL(mediaUrl);
1023: audioInputStream = AudioSystem.getAudioInputStream(url);
1024: // playB.setEnabled(true);
1025: long milliseconds = (long) ((audioInputStream
1026: .getFrameLength() * 1000) / audioInputStream
1027: .getFormat().getFrameRate());
1028: duration = milliseconds / 1000.0;
1029: System.out.println("*****createAudioInpuStream= "
1030: + audioInputStream);
1031: System.out.println("*** duration= " + duration);
1032: if (updateComponents) {
1033: formatControls.setFormat(audioInputStream.getFormat());
1034: System.out
1035: .println("*** calling samplingGraph.createWaveForm");
1036: samplingGraph.createWaveForm(null, lines,
1037: audioInputStream);
1038: }
1039: } catch (Exception ex) {
1040: // doesn't matter; treat it as no input stream
1041: System.out.println(ex.getMessage());
1042: }
1043: }
1044:
1045: }
|