/* * * Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * 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 * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ package javax.microedition.media; import java.io.IOException; import java.io.InputStream; import java.util.Vector; import java.util.Enumeration; import java.util.Hashtable; import com.sun.j2me.log.Logging; import com.sun.j2me.log.LogChannels; import com.sun.mmedia.BasicPlayer; import com.sun.mmedia.PlayerImpl; import com.sun.mmedia.TonePlayer; import com.sun.mmedia.Configuration; import com.sun.mmedia.protocol.*; import com.sun.mmedia.DefaultConfiguration; import com.sun.mmedia.DirectPlayer; import javax.microedition.media.protocol.*; /** * <code>Manager</code> is the access point for obtaining * system dependent resources such as <code>Players</code> * for multimedia processing. * <p> * * A <a href="Player.html"<code>Player</code></a> * is an object used to * control and render media that * is specific to the * <a href="#content-type">content type</a> * of the data. * <p> * <code>Manager</code> provides access to an implementation specific * mechanism for constructing <code>Players</code>. * <p> * For convenience, <code>Manager</code> also provides a simplified * method to generate simple tones. * * <h2>Simple Tone Generation</h2> * * <blockquote> * The * <a href="#playTone(int, int, int)"> * <code>playTone</code></a> * function is defined to generate * tones. Given the note and duration, the function will * produce the specified tone. * </blockquote> * * <h2>Creating Players</h2> * <blockquote> * * <code>Manager</code> provides three methods to create a * <code>Player</code> for playing back media: * <ul> * <li> Create from a media locator. * <li> Create from a <code>DataSource</code>. * <li> Create from an <code>InputStream</code>. * </ul> * The <code>Player</code> returned can be used to control the * presentation of the media. * <p> * * The simplest way to create a <code>Player</code> is from a * <a href="#media-locator">locator in the URI syntax</a>. * Given a locator, * <a href="#createPlayer(java.lang.String)"> * <code>createPlayer</code></a> * will create a <code>Player</code> suitable to handle the media * identified by the locator. * <p> * Users can also implement a custom <code>DataSource</code> to * handle an application-defined protocol. * The custom <code>DataSource</code> can * be used to create a <code>Player</code> by using the * <a href="#createPlayer(javax.microedition.media.protocol.DataSource)"> * <code>createPlayer</code></a> method. * <p> * A third version of * <a href="#createPlayer(java.io.InputStream, java.lang.String)"> * <code>createPlayer</code></a> * creates a <code>Player</code> from an * <code>InputStream</code>. This can be used to interface with * other Java API's which use <code>InputStreams</code> such as * the java.io package. It should be noted that <code>InputStream</code> * does not provide the necessary random seeking functionality. So * a <code>Player</code> created from an <code>InputStream</code> may * not support random seeking (ala <code>Player.setMediaTime</code>). * </blockquote> * * <h2>System Time Base</h2> * * <blockquote> * All <code>Players</code> need a <code>TimeBase</code>. Many * use a system-wide <code>TimeBase</code>, often based on * a time-of-day clock. * <code>Manager</code> provides access to the system <code>TimeBase</code> * through * <a href="#getSystemTimeBase()"> * <code>getSystemTimeBase</code></a>. * </blockquote> * * <a name="content-type"></a> * <h2>Content Types</h2> * <blockquote> * Content types identify the type of media data. They are * defined to be the registered MIME types * (<a href= * "http://www.iana.org/assignments/media-types/"> * http://www.iana.org/assignments/media-types/</a>); * plus * some user-defined types that generally follow the MIME syntax * (<a href="ftp://ftp.isi.edu/in-notes/rfc2045.txt">RFC 2045</a>, * <a href="ftp://ftp.isi.edu/in-notes/rfc2046.txt">RFC 2046</a>). * <p> * For example, here are a few common content types: * <ol> * <li>Wave audio files: <code>audio/x-wav</code> * <li>AU audio files: <code>audio/basic</code> * <li>MP3 audio files: <code>audio/mpeg</code> * <li>MIDI files: <code>audio/midi</code> * <li>Tone sequences: <code>audio/x-tone-seq</code> * <li>MPEG video files: <code>video/mpeg</code> * </ol> * </blockquote> * * <a name="delivery-protocol"></a> * <h2>Data Delivery Protocol</h2> * <blockquote> * A data delivery protocol specifies how media data is * delivered to the media processing systems. Some common * protocols are: local file, disk I/O, HTTP, RTP streaming, * live media capture etc. * <p> * <a href="#media-locator">Media locators</a> are used to * identify the delivery protocol * (as well as the identifier/name of the media). * </blockquote> * * <a name="media-locator"></a> * <h2>Media Locator</h2> * <blockquote> * <a name="media-protocol"></a> * Media locators are specified in * <a href="http://www.ietf.org/rfc/rfc2396.txt">URI syntax</a> * which is defined in the form: * <p> *     <scheme>:<scheme-specific-part> * <p> * The "scheme" part of the locator string identifies the name * of the protocol being used to deliver the data. * <p> * Some media specific locator syntax are defined below: * * <a name="live-capture"></a> * <h3>1. Locators for Live-media Capture</h3> * The locators for capturing live media are defined * by the following syntax in * <a href="http://www.ietf.org/rfc/rfc2234">Augmented BNF</a> notations: * <p> * <pre> * "capture://" device [ "?" media_encodings ] * </pre> *   To identify the type or the specific * name of the device:<p> * <pre> * device = "audio" / "video" / "audio_video" / dev_name * dev_name = alphanumeric * alphanumeric = 1*( ALPHA / DIGIT ) * </pre> * The syntax for specifying the media encodings are defined in * <a href="Manager.html#media_encodings">Media Encoding Strings</a>.<br> * <br> *     Examples: * <br> * <pre> * capture://audio (default audio) * capture://audio?encoding=pcm (default audio in PCM format) * capture://devmic0?encoding=pcm&rate=11025&bits=16&channels=1 * (audio from a specific device--devmic0) * <br> * capture://video (default video) * capture://video?encoding=gray8&width=160&height=120 * capture://devcam0?encoding=rgb888&width=160&height=120&fps=7 * <br> * capture://mydev?myattr=123 (custom device with custom param) * </pre> * * <h3>2. Locators for RTP streaming</h3> * <a href="http://www.ietf.org/rfc/rfc1889.txt">RTP</a> * is a public standard for streaming media. The locator * syntax for specifying RTP sessions is: * <pre> * "rtp://" address [ ":" port ] [ "/" type ] * </pre> *     where: * <pre> * address and port defines the RTP session. The * address and port usage is similar to the host and port * usage as defined in the <a href="http://www.ietf.org/rfc/rfc2396.txt">URI syntax</a>. * <br> * type = "audio" / "video" / "text" * </pre> * <br> *     Example: * <br> * <pre> * rtp://224.1.2.3:12344/audio * </pre> * * <h3>3. Locators for Radio Tuner</h3> * To create a <code>Player</code> to tune into a radio program, the * following locator syntax is used: * <p> * <pre> * "capture://radio" [ "?" tuner_params ] * </pre> *     where: * <pre> * tuner_params = tuner_param *( "&" tuner_param ) * tuner_param = "f=" freq / * "mod=" modulation / * "st=" stereo_mode / * "id=" program_id / * "preset=" preset * freq = megahertz / * kilohertz / * hertz * megahertz = pos_integer "M" / * pos_integer "." pos_integer "M" * kilohertz = pos_integer "k" / * pos_integer "." pos_integer "k" * hertz = pos_integer * modulation = "fm" / "am" * stereo_mode = "mono" / "stereo" / "auto" * program_id = alpanumeric ; identifies an FM channel by its * program service name (PS) delivered * via Radio Data System (RDS)**. * preset = pos_integer ; predefined tuning number * </pre> * ** The RDS specification is available from * <a href="http://bsonline.techindex.co.uk">http://bsonline.techindex.co.uk</a>, id BSEN 50067:1998.<br> * <br> *     Examples: * <br> * <pre> * capture://radio?f=91.9M&st=auto * (91.9 MHz with automatic stereo setting) * capture://radio?f=558k&mod=am * (558 kHz with amplitude modulation) * capture://radio?id=yleq * (FM channel that has "yleq" as its program service name * delivered via Radia Data System) * </pre> * </blockquote> * <p> * * <a name="media_encodings"></a> * <h2>Media Encoding Strings</h2> * <blockquote> * There are a few places where media encodings are specified * as strings, e.g. in the capture media locator. Sections A to E * define the encoding syntax. * <a href="#encodings_rules">Section F</a> * defines the rules for how they should be handled. * <p> * *   A. Describing media encodings:<p> * <pre> * media_encodings = audio_encodings / * video_encodings / * mixed_encodings / * custom_encodings * </pre> * * <a name="audio_encodings"></a> *   B. Describing the audio encodings:<p> * <pre> * audio_encodings = audio_enc_param *( "&" audio_param ) * audio_enc_param = "encoding=" audio_enc * audio_enc = "pcm" / "ulaw" / "gsm" / content_type * audio_param = "rate=" rate / * "bits=" bits / * "channels=" channels / * "endian=" endian / * "signed=" signed / * "type=" audio_type * rate = "96000" / "48000" / "44100" / * "22050" / "16000" / "11025" / * "8000" / other_rate * other_rate = pos_integer * bits = "8" / "16" / "24" / other_bits * other_bits = pos_integer * channels = pos_integer * endian = "little" / "big" * signed = "signed" / "unsigned" * audio_type = bitrate_variable / other_type * other_type = alphanumeric * pos_integer = 1*DIGIT * * and * <a href="#content-type">content type</a> is given in the MIME syntax. * </pre> * <br> *     Example: * <br> * <pre> * encoding=pcm&rate=11025&bits=16&channels=1 * </pre> * * <a name="video_encodings"></a> *   C. Describing the video or image encodings:<p> * <pre> * video_encodings = video_enc_param *( "&" video_param ) * video_enc_param = "encoding=" video_enc * video_enc = "gray8" / "rgb888" / "bgr888" / * "rgb565" / "rgb555" / "yuv444" / * "yuv422" / "yuv420" / "jpeg" / "png" / * content_type * video_param = "width=" width / * "height=" height / * "fps=" fps / * "colors=" colors / * "progressive=" progressive / * "interlaced=" interlaced / * "type=" video_type * width = pos_integer * height = pos_integer * fps = pos_number * quality = pos_integer * colors = "p" colors_in_palette / * = "rgb" r_bits g_bits b_bits / * = "gray" gray_bits * colors_in_palette = pos_integer * r_bits = pos_integer * g_bits = pos_integer * b_bits = pos_integer * gray_bits = pos_integer * progressive = boolean * video_type = jfif / exif / other_type * other_type = alphanumeric * interlaced = boolean * pos_number = 1*DIGIT [ "." 1*DIGIT ] * boolean = "true" / "false" * * and * <a href="#content-type">content type</a> is given in the MIME syntax. * </pre> * <br> *     Examples: * <br> * <pre> * encoding=gray8&width=160&height=120 * encoding=jpeg&quality=80&progressive=true&type=jfif * (progressive JPEG with quality 80 in jfif format) * encoding=jpeg&type=exif * (JPEG in exif format) * encoding=png&colors=rgb888 * (24 bits/pixel PNG) * encoding=rgb888 * (raw 24-bit rgb image) * encoding=rgb&colors=rgb888 * (raw 24-bit rgb image) * </pre> * * <a name="mixed_params"></a> *   D. Describing mixed audio and video encodings:<p> * <pre> * mixed_encodings = audio_encodings "&" video_encodings * </pre> * <br> *     Example: * <br> * <pre> * encoding=pcm&encoding=gray8&width=160&height=160 * </pre> * *   E. Describing custom media encodings:<p> * <pre> * custom_encodings = custom_enc_param *( "&" param ) * custom_enc_param = "encoding=" value * param = key "=" value * key = alphanumeric * value = alphanumeric * </pre> * * <a name="encodings_rules"></a> *   F. Rules for handling the encodings strings:<p> * <ul> * <li> If a given parameter is a custom parameter and is not recognizable * by the implementation, the parameter is treated as an illegal * parameter and the method must throw an appropriate Exception * to denote that. * <li> If the value for a given parameter is incorrect because it is * syntactically wrong or illegal (e.g. out of range), * the method must throw an appropriate Exception to denote that. * </ul> * * @created January 13, 2005 * @see javax.microedition.media.protocol.DataSource * @see Player * @see TimeBase */ public final class Manager { private static Configuration config = Configuration.getConfiguration(); private static TonePlayer tonePlayer; /** * The locator to create a tone <code>Player</code> * to play back tone sequences. For example, * <pre> * try { * Player p = Manager.createPlayer(Manager.TONE_DEVICE_LOCATOR); * p.realize(); * ToneControl tc = (ToneControl)p.getControl("ToneControl"); * tc.setSequence(mySequence); * p.start(); * } catch (IOException ioe) { * } catch (MediaException me) {} * </pre> * * If a tone sequence is not set on the tone * <code>Player</code> via its <code>ToneControl</code>, * the <code>Player</code> does not carry any * sequence. <code>getDuration</code> returns 0 for this * <code>Player</code>. * <p> * The content type of the <code>Player</code> created from this * locator is <code>audio/x-tone-seq</code>. * <p> * A <code>Player</code> for this locator may not be supported * for all implementations. * <p> * Value "device://tone" is assigned to <code>TONE_DEVICE_LOCATOR</code>. */ public final static String TONE_DEVICE_LOCATOR = "device://tone"; /** * The locator to create a MIDI <code>Player</code> * which gives access to the MIDI device by making * {@link javax.microedition.media.control.MIDIControl MIDIControl} * available. For example, * <pre> * try { * Player p = Manager.createPlayer(Manager.MIDI_DEVICE_LOCATOR); * p.prefetch(); // opens the MIDI device * MIDIControl m = (MIDIControl)p.getControl("MIDIControl"); * } catch (IOException ioe) { * } catch (MediaException me) {} * </pre> * * The MIDI <code>Player</code> returned does not carry any * media data. <code>getDuration</code> returns 0 for this * <code>Player</code>. * <p> * The content type of the <code>Player</code> created from this * locator is <code>audio/midi</code>. * <p> * A <code>Player</code> for this locator may not be supported * for all implementations. * <p> * Value "device://midi" is assigned to <code>MIDI_DEVICE_LOCATOR</code>. */ public final static String MIDI_DEVICE_LOCATOR = "device://midi"; private static String DS_ERR = "Cannot create a DataSource for: "; private static String PL_ERR = "Cannot create a Player for: "; /** * This private constructor keeps anyone from actually * getting a <CODE>Manager</CODE>. */ private Manager() { } /** * Return the list of supported content types for the given protocol. * <p> * See <a href="#content-type">content types</a> for the syntax * of the content types returned. * See <a href="#media-protocol">protocol name</a> for the syntax * of the protocol used. * <p> * For example, if the given <code>protocol</code> * is <code>"http"</code>, * then the supported content types that can be played back * with the <code>http</code> protocol will be returned. * <p> * If <code>null</code> is passed in as the <code>protocol</code>, * all the supported content types for this implementation * will be returned. The returned array must be non-empty. * <p> * If the given <code>protocol</code> is an invalid or * unsupported protocol, then an empty array will be returned. * * @param protocol The input protocol for the supported content types. * @return The list of supported content types for the given protocol. */ public static String[] getSupportedContentTypes(String protocol) { return config.getSupportedContentTypes(protocol); } /** * Return the list of supported protocols given the content * type. The protocols are returned * as strings which identify what locators can be used for creating * <code>Player</code>'s. * <p> * See <a href="#media-protocol">protocol name</a> for the syntax * of the protocols returned. * See <a href="#content-type">content types</a> for the syntax * of the content type used. * <p> * For example, if the given <code>content_type</code> * is <code>"audio/x-wav"</code>, then the supported protocols * that can be used to play back <code>audio/x-wav</code> * will be returned. * <p> * If <code>null</code> is passed in as the * <code>content_type</code>, * all the supported protocols for this implementation * will be returned. The returned array must be non-empty. * <p> * If the given <code>content_type</code> is an invalid or * unsupported content type, then an empty array will be returned. * * @param content_type The content type for the supported protocols. * @return The list of supported protocols for the given content type. */ public static String[] getSupportedProtocols(String content_type) { return config.getSupportedProtocols(content_type); } /** * Create a <code>Player</code> from an input locator. * * @param locator A locator string in URI syntax that describes * the media content. * @return A new <code>Player</code>. * @exception MediaException Thrown if a <code>Player</code> cannot * be created for the given locator. * @exception IOException Thrown if there was a problem connecting * with the source pointed to by the <code>locator</code>. */ public static Player createPlayer(String locator) throws IOException, MediaException { if (locator == null) { throw new IllegalArgumentException(); } String locStr = locator.toLowerCase(); // System.out.println("[mmapi] createPlayer with " + locator); /* Verify if Protocol is supported */ String theProtocol = null; boolean found = false; int idx = locStr.indexOf(':'); if (idx != -1) { theProtocol = locStr.substring(0, idx); } else { throw new MediaException("Malformed locator"); } if (locStr.startsWith(DefaultConfiguration.RADIO_CAPTURE_LOCATOR)) { if (!config.isRadioSupported()) { throw new MediaException( "Radio Capture is not supported" ); } parseRadioLocatorStr(locator); } else if (locStr.startsWith(DefaultConfiguration.AUDIO_CAPTURE_LOCATOR) || locStr.startsWith(DefaultConfiguration.VIDEO_CAPTURE_LOCATOR)) { // separate device & encodings int encInd = locator.indexOf('?'); String encStr = null; if (encInd > 0) { locStr = locStr.substring(0, encInd); idx = locator.indexOf("encoding="); if (idx != -1) { encStr = locator.substring(idx+9); if (encStr != null) { idx = encStr.indexOf('&'); if (idx > 0) { encStr = encStr.substring(0, idx); } encStr = encStr.toLowerCase(); } } } found = true; String encodings = null; if (locStr.equals(DefaultConfiguration.AUDIO_CAPTURE_LOCATOR)) { String supported = System.getProperty("supports.audio.capture"); encodings = System.getProperty("audio.encodings"); if (supported == null || supported.equals("false") || encodings == null) { found = false; } } else if (locStr.equals(DefaultConfiguration.VIDEO_CAPTURE_LOCATOR)) { String supported = System.getProperty("supports.video.capture"); encodings = System.getProperty("video.encodings"); if (supported == null || supported.equals("false") || encodings == null) { found = false; } } if (encStr != null && encodings != null && encodings.indexOf(encStr) == -1) { found = false; } if (!found) { throw new MediaException("Player cannot be created for " + locator); } } else { String supportedProtocols[] = getSupportedProtocols(null); for (int i = 0; i < supportedProtocols.length && !found; i++) { if (theProtocol.equals(supportedProtocols[i])) { found = true; } } if (!found) { throw new MediaException("Player cannot be created for " + locator + " Unsupported protocol " + theProtocol); } } DataSource ds = createDataSource(locator); Player pp = null; try { pp = createPlayer(ds); } catch (MediaException ex) { ds.disconnect(); throw ex; } catch (IOException ex) { ds.disconnect(); throw ex; } return pp; } private static void parseRadioLocatorStr( String locator ) throws MediaException { final int prefixLen = DefaultConfiguration.RADIO_CAPTURE_LOCATOR.length(); if( null == locator ) { throw new MediaException( "radio locator is null" ); } if( !locator.startsWith( DefaultConfiguration.RADIO_CAPTURE_LOCATOR ) ) { throw new MediaException( "bad radio locator" ); } if ( locator.length() == prefixLen ) { return; } if( locator.charAt( prefixLen ) != '?' ) { throw new MediaException( "bad radio locator" ); } String params = locator.substring( prefixLen + 1 ); parseRadioParamsString( params ); } private static void parseRadioParamsString( String params ) throws MediaException { boolean foundFreq = false; boolean foundMod = false; boolean foundStereoMode = false; boolean foundId = false; boolean foundPreset = false; int i = 0, j = -1; do { j = params.indexOf( '&', i ); String param = j < 0 ? params.substring( i ) : params.substring( i, j ); if( param.startsWith( "f=" ) ) { if( foundFreq ) { throw new MediaException( "frequency is set more than" + " once in the radio locator string" ); } parseRadioFreqParam( param ); foundFreq = true; } else if ( param.startsWith( "mod=" ) ) { if( foundMod ) { throw new MediaException( "modulation is set more than" + " once in the radio locator string" ); } parseRadioModParam( param ); foundMod = true; } else if ( param.startsWith( "st=" ) ) { if( foundStereoMode ) { throw new MediaException( "stereo mode is set more than" + " once in the radio locator string" ); } parseRadioStereoParam( param ); foundStereoMode = true; } else if ( param.startsWith( "id=" ) ) { if( foundId ) { throw new MediaException( "Program ID is set more than" + " once in the radio locator string" ); } parseRadioIdParam( param ); foundId = true; } else if ( param.startsWith( "preset=" ) ) { if( foundPreset ) { throw new MediaException( "Preset is set more than" + " once in the radio locator string" ); } parseRadioPresetParam( param ); foundPreset = true; } else { throw new MediaException( "Unknown parameter in the" + " radio locator string" ); } i = j + 1; } while( j >= 0 ); } private static void parseRadioFreqParam( String s ) throws MediaException { String param = s; if( 'M' == param.charAt( param.length() - 1 ) || 'k' == param.charAt( param.length() - 1 ) ) { param = param.substring( 0, param.length() - 1 ); } try { if( 0 >= Float.parseFloat( param.substring( 2 ) ) ) { throw new MediaException( "Frequency is not positive" + " in the radio locator string" ); } } catch (NumberFormatException e) { throw new MediaException( "Frequency is not numeric or too big" + " in the radio locator string" ); } } private static void parseRadioModParam( String param ) throws MediaException { String mod = param.substring( "mod=".length() ); if( !mod.equals( "am" ) && !mod.equals( "fm" ) ) { throw new MediaException( "Unknown modulation in the" + " radio locator string parameters" ); } } private static void parseRadioStereoParam( String param ) throws MediaException { String mode = param.substring( "st=".length() ); if( !mode.equals( "mono" ) && !mode.equals( "stereo" ) && !mode.equals( "auto" )) { throw new MediaException( "Unknown stereo mode in the" + " radio locator string parameters" ); } } private static void parseRadioIdParam( String param ) throws MediaException { String id = param.toLowerCase().substring( "id=".length() ); if( 0 == id.length() ) { throw new MediaException( "Empty Program ID name in" + " the radio locator string parameters" ); } for( int i = 0; i < id.length(); i++ ) { char ch = id.charAt( i ); if( !Character.isLowerCase( ch ) && !Character.isDigit( ch ) ) { throw new MediaException( "Not an alphanumeric Program" + " ID in the radio locator string parameters" ); } } } private static void parseRadioPresetParam( String param ) throws MediaException { String preset = param.substring( "preset=".length() ); try { if( 0 >= Byte.parseByte( preset ) ) { throw new MediaException( "Preset number is not positive" + " in the radio locator string" ); } } catch (NumberFormatException e) { throw new MediaException( "Preset number is not numeric" + " or too big in the radio locator string" ); } } /** * Create a <code>Player</code> to play back media from an * <code>InputStream</code>. * <p> * The <code>type</code> argument * specifies the content-type of the input media. If * <code>null</code> is given, <code>Manager</code> will * attempt to determine the type. However, since determining * the media type is non-trivial for some media types, it * may not be feasible in some cases. The * <code>Manager</code> may throw a <code>MediaException</code> * to indicate that. * * @param stream The <code>InputStream</code> that delivers the * input media. * @param type The <code>ContentType</code> of the media. * @return A new <code>Player</code>. * @exception MediaException Thrown if a <code>Player</code> cannot * be created for the given stream and type. * @exception IOException Thrown if there was a problem reading data * from the <code>InputStream</code>. */ public static Player createPlayer(InputStream stream, String type) throws IOException, MediaException { if (stream == null) { throw new IllegalArgumentException(); } if (type == null) { throw new MediaException(PL_ERR + "cannot determine the media type"); } type = type.toLowerCase(); // Wrap the input stream with a CommonDS where the input // can be handled in a generic way. CommonDS ds = new CommonDS(); ds.setInputStream(stream); ds.setContentType(type); try { return createPlayer(ds); } catch (IOException ex) { throw new MediaException(PL_ERR + ex.getMessage()); } } /** * Play back a tone as specified by a note and its duration. * A note is given in the range of 0 to 127 inclusive. The frequency * of the note can be calculated from the following formula: * <pre> * SEMITONE_CONST = 17.31234049066755 = 1/(ln(2^(1/12))) * note = ln(freq/8.176)*SEMITONE_CONST * The musical note A = MIDI note 69 (0x45) = 440 Hz. * </pre> * This call is a non-blocking call. Notice that this method may * utilize CPU resources significantly on devices that don't * have hardware support for tone generation. * * @param note Defines the tone of the note as specified by the * above formula. * @param duration The duration of the tone in milli-seconds. * Duration must be positive. * @param volume Audio volume range from 0 to 100. 100 represents * the maximum * volume at the current hardware level. Setting the volume to a * value less * than 0 will set the volume to 0. Setting the volume to greater than * 100 will set the volume to 100. * @exception MediaException Thrown if the tone cannot be played * due to a device-related problem. */ public static void playTone(int note, int duration, int volume) throws MediaException { if (note < 0 || note > 127 || duration <= 0) { throw new IllegalArgumentException( "Invalid note(" + note + ") or duration (" + duration + ")" ); } if (volume < 0) { volume = 0; } else if (volume > 100) { volume = 100; } if (duration == 0 || volume == 0) { return; } if (tonePlayer == null) { tonePlayer = config.getTonePlayer(); } if (tonePlayer != null) { tonePlayer.playTone(note, duration, volume); } else { throw new MediaException("no tone player"); } } /** * MMAPI full specific methods. * * @param source Description of the Parameter * @return Description of the Return Value * @exception IOException Description of the Exception * @exception MediaException Description of the Exception */ /** * Create a <code>Player</code> for a <code>DataSource</code>. * * @param source The <CODE>DataSource</CODE> that provides * the media content. * @return A new <code>Player</code>. * @exception MediaException Thrown if a <code>Player</code> cannot * be created for the given <code>DataSource</code>. * @exception IOException Thrown if there was a problem connecting * with the source. */ public static Player createPlayer(DataSource source) throws IOException, MediaException { if (source == null) { throw new IllegalArgumentException(); } String type; try { type = source.getContentType(); } catch( IllegalStateException e ) { type = null; } if (type != null) { String theProtocol = null; String locator = source.getLocator(); if (locator != null) { int idx = locator.indexOf(':'); if (idx != -1) { theProtocol = locator.substring(0, idx); } } String supportedContentTypes[] = getSupportedContentTypes(theProtocol); boolean found = false; for(int i=0; i<supportedContentTypes.length && !found; i++) { if (type.equals(supportedContentTypes[i])) { found = true; } } if (!found) { throw new MediaException("Player cannot be created for " + type); } } PlayerImpl p = new PlayerImpl(source); return p; } /** * Create a <code>DataSource</code> for the specified media * identified by a locator. The <code>DataSource</code> * returned can be used to read the media data from the input * source. * <p> * The returned data source is <i>connected</i>; * <code>DataSource.connect</code> has been invoked. * <p> * If no suitable <code>DataSource</code> can be found to * handle the input, a <CODE>MediaException</CODE> * is thrown. * * @param locator The source protocol for the media data. * @return A connected <CODE>DataSource</CODE>. * @exception MediaException Thrown if no <CODE>DataSource</CODE> * can be found that supports the given protocol as specified by * the locator. * @exception IOException Thrown if there was a problem connecting * with the source (e.g. the source media does not exist). */ private static DataSource createDataSource(String locator) throws IOException, MediaException { String className = config.getProtocolHandler(BasicDS.getProtocol(locator)); if (className == null) { throw new MediaException(DS_ERR + locator); } try { // ... Try to create a DataSource instance ... Class protoClass = Class.forName(className); DataSource source = (DataSource) protoClass.newInstance(); // ... and get it connected ... ((BasicDS) source).setLocator(locator); if (locator.equals(TONE_DEVICE_LOCATOR)) { ((BasicDS) source).setContentType("audio/x-tone-seq"); } else if (locator.equals(MIDI_DEVICE_LOCATOR)) { ((BasicDS) source).setContentType("audio/midi"); } return source; } catch (MediaException e) { throw e; } catch (Exception e) { throw new MediaException(DS_ERR + e.getMessage()); } } private static TimeBase sysTimeBase = null; /** * Get the time-base object for the system. * * @return The system time base. */ public static TimeBase getSystemTimeBase() { if (sysTimeBase == null) { sysTimeBase = new SystemTimeBase(); } return sysTimeBase; } } /** * SystemTimeBase is the implementation of the default <CODE>TimeBase</CODE> * based on the system clock. * * @see TimeBase */ class SystemTimeBase implements TimeBase { /* * Pick some offset (start-up time) so the system time won't be * so huge. The huge numbers overflow floating point operations * in some cases. */ private static long offset = System.currentTimeMillis() * 1000L; /** * This is a straight-forward implementation of a * system time base using the system clock. * * @return The time value */ public long getTime() { return (System.currentTimeMillis() * 1000L) - offset; } }