/********************************************************************************* * TotalCross Software Development Kit * * Copyright (C) 1998, 1999 Wabasoft <www.wabasoft.com> * * Copyright (C) 2000-2012 SuperWaba Ltda. * * All Rights Reserved * * * * This library and virtual machine 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. * * * * This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 * * A copy of this license is located in file license.txt at the root of this * * SDK or can be downloaded here: * * http://www.gnu.org/licenses/lgpl-3.0.txt * * * *********************************************************************************/ package totalcross.ui.media; import totalcross.Launcher; import totalcross.io.IOException; import totalcross.io.RandomAccessStream; import totalcross.ui.MainWindow; /** * MediaClip is a sound clip. It will be updated in the future to support movie clips. * <p> * Support for sound clips varies between platforms. Some Java virtual machines support .wav and .au sound files and * some versions don't seem to support either format. * <p> * Using a TotalCross Virtual Machine, .wav format sound clips are supported under Win32, WinCE and Palm OS. Under Win32 * and WinCE, the .wav files for sound clips may exist in a file outside of the program's tcz file; the wav file can be * stored inside a pdb/tcz file (and it has precedence over the one located in the file system). * <p> * In Palm OS, the wav must be added to the pdb (The Deployer does this automagically. Just reference a .wav and it will * be added). * <p> * If you're playing a sound clip under a Windows CE device and you don't hear anything, make sure that the device is * set to allow programs to play sounds. To check the setting, look at: * <p> * Start->Settings->Volume & Sounds * <p> * for the check box: * <p> * Enable sounds for: Programs * <p> * If it is not checked on, sound clips won't play. * <p> * Here is an example that plays a sound: * * <pre> * File soundFile = new File("sound.wav", File.READ_WRITE); * MediaClip s = new MediaClip(soundFile); * s.start(); * </pre> * * Under Palm OS, the currently supported formats are: uncompressed (PCM) or IMA 4-bit adaptive differential (IMA * ADPCM). The ADPCM type is also known as DVI ADPCM; in a WAVE file, it's known as format 0x11. One or two-channels; * All normal sampling rates (8k, 11k, 22.05k, 44.1k, 48, 96k). * <p> * Note that some Palm OS devices does not support 16bit waves, so better store them in 8bits. * <p> * MediaClip also support sound recording. When recording sound, you can only call the record and stop methods. Sound * recording does not work under JavaSE. * <p> * The MediaClip events are broadcasted to the MainWindow controls. */ public class MediaClip { Object mediaClipRef; Launcher.S2IS mediaStream; int currentState; static MainWindow mainWindow = MainWindow.getMainWindow(); /** The state of the MediaClip indicating that it has not acquired the required information and resources to function. */ public final static int UNREALIZED = 0; /** The state of the MediaClip indicating that it has acquired the required information but not the resources to function. */ public final static int REALIZED = 1; /** The state of the MediaClip indicating that it has acquired all the resources to begin playing. */ public final static int PREFETCHED = 2; /** The state of the MediaClip indicating that the MediaClip has already started. */ public final static int STARTED = 3; /** The state of the MediaClip indicating that the MediaClip is closed. */ public final static int CLOSED = 4; /** Used as a samplesPerSecond parameter in record method. */ public static final int VOICE = 8000; /** Used as a samplesPerSecond parameter in record method. */ public static final int LOW = 11025; /** Used as a samplesPerSecond parameter in record method. */ public static final int MEDIUM = 22050; /** Used as a samplesPerSecond parameter in record method. */ public static final int HIGH = 44100; /** * Create a MediaClip to play back or record a media from/to a RandomAccessStream. Currently the only media type * supported is audio wave.<br> * It's important to notice that you may not use the same object for play back and record.<br> * * @param stream * The random access stream used to read (when playing) or writting (when recording) the data. * @throws IOException * @see totalcross.io.RandomAccessStream */ public MediaClip(RandomAccessStream stream) throws IOException { try { mediaStream = new Launcher.S2IS(stream); mediaClipRef = new sun.audio.AudioStream(mediaStream); } catch (java.io.IOException e) { throw new totalcross.io.IOException(e.getMessage()); } if (mediaClipRef == null) throw new totalcross.io.IOException("Could not load the given file."); currentState = PREFETCHED; } /** * Starts the MediaClip as soon as possible. If the MediaClip was previously stopped by calling stop, it will resume * playback from where it was previously stopped. If the MediaClip has reached the end of media, calling start will * automatically start the playback from the start of the media.<br> * <br> * When start returns successfully, the MediaClip must have been started and a STARTED event will be delivered. * However, the MediaClip is not guaranteed to be in the STARTED state. The MediaClip may have already stopped (in * the PREFETCHED state) because the media has 0 or a very short duration.<br> * <br> * If start is called when the MediaClip is in the STARTED state, the request will be ignored. * * @throws IOException */ final public void start() throws IOException { if (currentState != PREFETCHED) return; if (!Launcher.isApplication) ((java.applet.AudioClip) mediaClipRef).play(); else sun.audio.AudioPlayer.player.start((sun.audio.AudioStream) mediaClipRef); currentState = STARTED; mainWindow.broadcastEvent(new MediaClipEvent(MediaClipEvent.STARTED, mainWindow)); } /** * Stops the MediaClip. It will pause the playback/record at the current media time.<br> * <br> * When stop returns, the MediaClip is in the PREFETCHED state. A STOPPED event will be delivered.<br> * <br> * If stop is called on a stopped MediaClip, the request is ignored. * * @throws IOException */ final public void stop() throws IOException { if (currentState != STARTED) return; if (!Launcher.isApplication) ((java.applet.AudioClip) mediaClipRef).stop(); else sun.audio.AudioPlayer.player.stop((sun.audio.AudioStream) mediaClipRef); currentState = PREFETCHED; mainWindow.broadcastEvent(new MediaClipEvent(MediaClipEvent.STOPPED, mainWindow)); } /** * Stops the playback and reset the media time to 0.<br> * * @throws IOException */ final public void reset() throws IOException { try { this.stop(); } catch (IOException e) { } } /** * Gets the current state of this MediaClip. The possible states are: UNREALIZED, REALIZED, PREFETCHED, STARTED, * CLOSED. * * @see #UNREALIZED * @see #REALIZED * @see #PREFETCHED * @see #STARTED * @see #CLOSED */ public int getCurrentState() { return currentState; } /** * Close the MediaClip and release its resources, WITHOUT closing the underlying stream.<br> * <br> * When the method returns, the MediaClip is in the CLOSED state and can no longer be used. A CLOSED event will be * delivered. <br> * <br> * If close is called on a closed MediaClip the request is ignored. * * @throws IOException */ final public void close() throws IOException { if (currentState == CLOSED) return; if (!Launcher.isApplication) ((java.applet.AudioClip) mediaClipRef).stop(); else sun.audio.AudioPlayer.player.stop((sun.audio.AudioStream) mediaClipRef); // guich@582_8 mediaClipRef = null; currentState = CLOSED; mainWindow.broadcastEvent(new MediaClipEvent(MediaClipEvent.CLOSED, mainWindow)); } /** * Starts recording the media until the <code>stop</code> method is called.<br> * Please notice you must explicitly close the MediaClip and close the underlying stream to make sure the data was * fully written to the underlying stream.<br> * <br> * * <pre> * Platform specific limitations: * Palm OS * you may only record if the given stream is an instance of totalcross.io.File. * the byte rate of the audio recorded must be under 60kbps. The byte rate is calculated as following: * byte rate = samples per second * bits per sample * (stereo ? 2 : 1) / 8<br> * If the byte rate exceeds this value, the record method will record in mono instead of stereo, and also * reduce also bits per sample to 8 if necessary.<br> * BlackBerry * the parameters received by record are not supported by BlackBerry, and therefore ignored. * "The BlackBerry smartphone supports audio recording using two different formats, Adaptive Multi-Rate (AMR) * and 8kHz mono-16-bit Pulse Code Modulation (PCM). The default encoding used by the BlackBerry smartphone is AMR." * </pre> * * @param samplesPerSecond * may be VOICE (8000), LOW (11025), MEDIUM (22050) or HIGH (44100), otherwise an IllegalArgumentException * is thrown. * @param bitsPerSample * must be 8 or 16, otherwise an IllegalArgumentException is thrown. * @param stereo * true for stereo recording or false for mono recording. * @throws IOException * @see #VOICE * @see #LOW * @see #MEDIUM * @see #HIGH */ final public void record(int samplesPerSecond, int bitsPerSample, boolean stereo) throws IOException { if (samplesPerSecond != VOICE && samplesPerSecond != LOW && samplesPerSecond != MEDIUM && samplesPerSecond != HIGH) throw new IllegalArgumentException("Invalid value for samplesPerSecond: " + samplesPerSecond); if (bitsPerSample != 8 && bitsPerSample != 16) throw new IllegalArgumentException("Invalid value for bitsPerSample: " + bitsPerSample); } }