Developers
Local Navigation
Richard Evers, Editor
Developing Media API applications is a fairly straightforward process providing that you remember that the MIDP 2.0 specification for the Media API does not support video handling or the capture of audio.
Topics within this article include:
- Player Creation
- Player Controls
- Player Events
- javax.microedition.media.Player
- An in-depth look at Player interface methods
- void realize()
- void prefetch()
- void start()
- void stop()
- void close()
- void deallocate()
- void addPlayerListener(PlayerListener playerListener)
- void removePlayerListener(PlayerListener playerListener)
- String getContentType()
- long getDuration()
- long getMediaTime()
- void setMediaTime(long now)
- int getState()
- void setLoopCount(int count)
- javax.microedition.media.Manager
- Supported Fields
- javax.microedition.media.Controllable
- javax.microedition.media.control
- MetaDataControl
- ToneControl
- Supported Fields
- VolumeControl
- In Summary
The Media API provides a factory class to instantiate media player objects from DataSource objects, URI locator strings or InputStream objects. Player classes are provided to support specific media formats, with Control interfaces provided so that application-specific Player interactions can occur. Listeners can be set up to capture and act upon Player events as they occur, and timing can be handling using the TimeBase class.
The following is a diagram that show how everything fits together.
Note: The unsupported functionality detailed in the previous diagram is part of the Mobile Media API. This functionality is not supported in the MIDP 2.0 Media API.
As shown, Manager is used to create a media Player with a source of type StreamSource.
After creation, the Player (which extends Controllable) is used to determine which control, or controls, can be used, then creates the Control object(s). A listener (PlayerListener) can be set up to catch Player events as they occur. Exceptions can be thrown by Manager, Player and the controls.
Player States
Player objects cycle through several states before they can be used. As the following diagram shows, these states are:
- Unrealized
The Player is constructed in the Unrealized state. At this point it is incapable of doing anything constructive for lack of information and resources.
- Realized
Moving the Player into a Realized state via realize() allows the Player to locate all of the information it needs to get the basic resources to play a media clip.
- Prefetched
Moving the Player into the Prefetched state via prefetch() allows the Player to have all the resources it needs. It also "fetches" enough of the media clip to start playing immediately.
- Started
Moving the Player into the Started state via start() starts immediate playback of media. Calling stop() from the Started state will move it back to a Prefetched state.
Calling start() at any state before Started causes all previous states to be realized through applicable calls. It's best to call realize() and then prefetch() before calling start() to provide control over when the media playback will start. The transitions from Unrealized to Realized, and from Realized to Prefetched are both potentially time consuming. If you call start() from an Unrealized state, you will not get immediate playback.
- Closed
Player Creation
Provides two ways to create a Player object.
- Player createPlayer(String locator)
- Player createPlayer(InputStream stream, String type)
The following is a quick look at each creation method.
createPlayer
(String locator)
Used to create a Player from a locator string in URI syntax that describes the media content.
In the BlackBerry® device implementation, support is provided for the following audio formats:
BlackBerry 8700 Series:
- audio/mpeg
- audio/midi
- audio/amr
- audio/x-wav
BlackBerry 7100 Series:
- audio/midi
- audio/x-wav
BlackBerry 7130e™ device:
- audio/mpeg
- audio/midi
Other BlackBerry devices support tones and partial MIDI:
Example:
- Player createPlayer("http://www.rim.com/rim.mp3");
- Player createPlayer("http://www.rim.com/rim.mid");
- Player createPlayer("http://www.rim.com/rim.wav");
Manager uses the passed String to connect to the location specified by the passed URL. After a connection has been established, Manager uses getContentType() to determine the MIME content type of the source data. This type is used to construct the class name for the Player that will be loaded.
The MIME type would be "audio/mpeg" for a passed URL such as http://www.rim.com/rim.mp3. Manager now loads and instantiates the class. Once instantiated, Manager calls Player.setSource() to associate the data source with the Player. Afterwards, the Player is returned to the calling application.
This method will throw:
- IllegalArgumentException
"locator" is null
- MediaException
A Player cannot be created because the passed "locator" is invalid
- IOException
Cannot connect to the source pointed to by the "locator"
- SecurityException
The caller lacks security permission to create the Player
createPlayer
(InputStream stream, String type)
Used to create a Player object from an InputStream. The "type" parameter is the input media content-type. If null is passed, Manager will try to determine the type.
This method will throw:
- IllegalArgumentException
Stream is null
- MediaException
A Player cannot be created or "type" parameter is null
- IOException
There was a problem reading data from the InputStream
- SecurityException
The caller lacks security permission to create the Player
Example:
RecordStore recSt;
int recId;
try {
InputStream inpStr = new
ByteArrayInputStream((store.getRecord(recId));
Player p = Manager.createPlayer
(inpStr, "audio/mpeg");
p.start();
} catch (IOException ioEx) {
} catch (MediaException meEx) {
Note: The code fragment above won't work unless you fill in the missing pieces such as store, RecId, etc.
Player Controls
Controls are used to provide additional functionality to a Player such as a VolumeControl to allow adjustment of the volume levels. Multiple Controls can also be implemented by the same object, for example, as a VolumeControl and a ToneControl to control volume and tone generation. In handling it in this manner, a single Control class can support several different Players while the Player only has to implement a small public API to provide enhanced functionality. Note that Controls do not affect the underlying state of a Player.
Use the Player.Controls() method to return an array containing all Controls that are available for the Player.
Control support has been provided for the following javax.microedition.media.control interfaces:
- MetaDataControl
Provides XML-like accessor methods to recognize, retrieve and store metadata within media streams. Predefined keys are provided for common metadata fields such as title, copyright, data, and author. MetaDataControl cannot be used with a MIDI Player.
- ToneControl
An interface used to enable playback of a user-defined sequence of tones in an unvarying pitch. It can only be used with Tone Players.
- VolumeControl
Controls the volume of a Player. VolumeControl cannot be used with Tone Players.
Player Events
Media API uses events to notify an application about changes in a Player's state. This is accomplished through the PlayerListener interface. This interface is used to receive asynchronous events generated by Players. You need to implement this interface and register implementations with the addPlayerListener() method in Player. Several standard Player events are defined in this interface.
void playerUpdate
(Player player, String event,
Object eventData)
This method is called to deliver an event to a registered listener when a Player event has been observed. The "player" parameter is the Player that generated the event. The "event" parameter is the event generated as defined by the enumerated types. The "eventData" parameter is the associated event data.
To follow is a simple implementation of a Player instance with a listener:
private void doPlay()
throws IOException, MediaException
{
Player p = Manager.createPlayer
("http://www.rim.com/rim.mp3");
p.addPlayerListener(this);
// best practice would be to call realize();
// then prefetch(); and then start();
p.start();
}
public void playerUpdate(Player player,
String event, Object eventData)
{
// release resources
player.close();
if ( event == PlayerListener.END_OF_MEDIA )
// do something if you want
}
The following event types are supported:
- CLOSED
Posted when a Player is closed. The "eventData" parameter is null when this event is received.
- DEVICE_AVAILABLE
Posted when the system or another higher priority application has released an exclusive device, which is now available to the Player. The Player is in the realized state when this event is received. The application may acquire the device with the prefetch or start method. A DEVICE_UNAVAILABLE event must precede this event. The "eventData" parameter is a String specifying the name of the device.
- DEVICE_UNAVAILABLE
Posted when the system or another higher priority application has temporarily taken control of an exclusive device which was previously available to the Player. The Player will be in the realized state when this event is received. This event must be followed by either a DEVICE_AVAILABLE event when the device becomes available again, or an ERROR event if the device becomes permanently unavailable. The "eventData" parameter is a String specifying the name of the device.
- DURATION_UPDATED
Posted when the duration of a Player is updated. This happens for some media types where the duration cannot be determined ahead of time. It can only be determined after the media is played for a period of time; for example, when it reaches a key frame with duration info, or when it reaches the end of media. When this event is received, the "eventData" parameter will be a Long object set to the duration of the media.
- END_OF_MEDIA
Posted when a Player has reached the end of the media. When this event is received, the "eventData" parameter is a Long object designating the media time when the Player reached end of media and stopped.
- ERROR
Posted when an error had occurred. When this event is received, the "eventData" parameter is a String object set to the error message.
- STARTED
Posted when a Player is started. When this event is received, the eventData parameter is a Long object set to the media time when the Player is started.
- STOPPED
Posted when a Player stops in response to the stop method call. When this event is received, the eventData parameter is a Long object set to the media time when the Player stopped.
javax.microedition.media.Player
Player provides the core Media API functionality. It controls rendering of time based media files, manages Player life cycles, controls the playback process and more. As shown earlier, a Player can be moved through five states:
- UNREALIZED
Point of construction
Do not call these methods while in the unrealized state:
- getContentType()
- setMediaTime()
- getControls()
- getControl()
- REALIZED
Has basic resources to play media
- PREFETCHED
Has all resources need to play media
- STARTED
Immediate start of media playback
Never call this method while in the started state:
- setLoopCount()
- CLOSED
An in-depth look at Player interface methods
void realize()
Creates a portion of the Player without acquiring scarce and exclusive resources. Transitioning into this state may be time consuming as it may have to examine media data before achieving a realized state.
void prefetch()
Transitions the Player from a realized to a prefetched state. Calling this method will acquire scarce and exclusive resources needed to play media, and will process as much data as necessary to reduce the start latency. If prefetch() is called when the Player is in an unrealized state then realize() will be called automatically.
This method wil throw an IllegalStateEzception if the Player is in a closed state, will throw MediaException if the Player cannot be prefetched, and will throw a SecurityException if the caller lacks security permission to prefetch a Player.
void start()
Transitions the Player from a prefetched to start state. Calling this method will start immediate playback of media if realize() and prefetch() were called before start(). If not then start() will call prefetch(0, which in turn will call realize(), which will cause delays in the start of playback. If a Player had been stopped by calling stop() then calling start() resumes playback from where the media had been stopped. If the Player stopped at the end of media, then playback resumes at the start of the media.
This method throws IllegalStateException if the Player is in a closed state, throws MediaException if the Player cannot be started, and throws SecurityException if the caller lacks security permission to prefetch a Player. A START event is delivered to the registered PlayerListeners before this method returns.
void stop()
Stops the Player by pausing playback at the current media time. This returns the Player to a prefetched state. This method throws IllegalStateException if the Player is in a closed state, and will throw MediaException if the Player cannot be stopped. A STOP event will be delivered to the registered PlayerListeners before this method returns.
void close()
Closes the Player and releases all related resources. The Player can no longer before used once it is in a closed state. A CLOSED event is delivered to the registered PlayerListeners before this method returns.
void deallocate()
Call this method to release scarce or exclusive resources such as the audio device acquired by the Player, and regress the event state to unrealized or realized.
Regression to an unrealized state occurs if the Player is blocked when calling realize() while realizing. In this situation calling deallocate() unblocks the realize() call and returns the Player to an unrealized state. Regression will occur to a realized state if there was no block to Player when calling realize(). If start() is called while a Player is in a deallocated state, then deallocate() will implicitly call stop() on the Player. This method throws IllegalStateException if the Player is in a closed state when deallocate() is called.
void addPlayerListener
(PlayerListener playerListener)
This method adds a Player listener for this Player and is ignored if null is passed as the listener. This method throws IllegalStateException if the Player is in a closed state when called.
void removePlayerListener
(PlayerListener playerListener)
This method removes an existing Player listener associated with this Player. It is ignored if null is passed as the listener or if the listener is not associated with the Player. This method throws IllegalStateException if the Player is in a closed state when called.
String getContentType()
Returns the content type of the media that is being played back by this Player. Currently supported contents types are as follows:
BlackBerry 8700 Series:
- audio/mpeg
- audio/midi
- audio/amr
- audio/x-wav
BlackBerry 7100 Series:
- audio/midi
- audio/x-wav
BlackBerry 7130e device:
- audio/mpeg
- audio/midi
This method throws IllegalStateException if the Player is in an unrealized or closed state when called.
long getDuration()
Returns the duration of the media in microseconds as if it had played at the default rate. Returns TIME_UNKNOWN if the duration cannot be determined, for example, because the Player is presenting live media. This method throws IllegalStateException if the Player is in a closed state when called.
long getMediaTime()
Returns the media's current media time in microseconds or TIME_UNKNOWN if the media time cannot be determined. This method throws IllegalStateException if the Player is in a closed state when called.
void setMediaTime
(long now)
Sets the Player's media time in microseconds and returns the actual media time set. Sets the media time to the start if zero or a negative value is passed, and sets the media time to end of media if a time that exceeds the media duration is passed. This method throws MediaException if the media type does not support setting the media time.
int getState()
Returns the current state of this Player as shown below:
- UNREALIZED
- REALIZED
- PREFETCHED
- STARTED
- CLOSED
void setLoopCount
(int count)
Sets the number of times that the Player will repeat content playback in a loop. The default setting is 1. Setting the loop count to -1 will cause the Player to loop indefinitely. If the Player is stopped within a loop, calling start() will resume where the Player left off. An END_OF_MEDIA event is posted to the associated listener each time the Player reaches the end of media. This method throws IllegalArgumentException if an invalid "count" value is passed such as zero, and throws IllegalStateException if the Player is in a started or closed state.
javax.microedition.media.Manager
Manager provides an access point to get resources such as Players for media processing and also provides a simplified way to generate simple tones. The following is an in-depth look at methods and fields within the Manager class.
String[] getSupportedContentTypes
(String protocol)
Returns a list of supported content types for the passed protocol in the URI form <scheme>:<scheme-specific-part> where the "scheme" part of the string identifies the name of the protocol being used to deliver the data.
Common content types include:
- Wave audio files: audio/x-wav
- MP3 audio files: audio/mpeg
- MIDI files: audio/midi
- Tone sequences: audio/x-tone-seq
As an example, if the passed protocol is "http", then the supported content types that can be played back with the http protocol will be returned. If null is passed, all supported content types will be returned. If the passed value is invalid or unsupported, then an empty array will be returned. The "protocol" parameter is the input protocol for the supported content types.
String[] getSupportedContentTypes
(String protocol)
Returns a list of supported protocols associated with the passed content type. The returned protocols identify what locators can be used to create Players. If null is passed, all supported content types will be returned. If the passed value is invalid or unsupported, then an empty array will be returned. The "content_type" parameter is the content type for the supported protocols.
Player createPlayer
(String locator)
Creates a Player from a "locator" String in URI syntax that describes the media content. The method returns a new Player.
This method throws:
- IllegalArgumentException
"locator" is null
- MediaException
A Player cannot be created for the passed "locator"
- IOException
There was a problem connecting with the source pointed to by the "locator"
- SecurityException
The caller lacks security permission to create the Player
Example:
Player p = Manager.createPlayer(Manager.TONE_DEVICE_LOCATOR); p.realize(); p.prefetch(); p.start();
Player createPlayer
(InputStream stream, String type)
Creates a Player to play back media from an InputStream. The "stream" parameter is the media InputStream and the "type" parameter is the input media ContentType. Manager will try to determine the type if null is passed. This method returns a new Player.
This method throws:
- IllegalArgumentException
Stream is null
- MediaException
A Player cannot be created
- IOException
There was a problem reading data from the InputStream
- SecurityException
The caller lacks security permission to create the Player
Example:
RecordStore recSt;
int recId;
try {
InputStream inpStr = new ByteArrayInputStream
((store.getRecord(recId));
Player p = Manager.createPlayer
(inpStr, "audio/mpeg");
p.realize();
p.prefetch();
p.start();
} catch (IOException ioEx) {
} catch (MediaException meEx) {
}
Player createPlayer
(InputStream stream, String type)
Used to play a tone by note, duration and volume level. The "note" parameter is the tone of a note in the range of 0 to 127 where the note frequency can be calculated using the following algorithm:
SEMITONE_CONST = 1/(ln(2^(1/12)))
This is the same as:
SEMITONE_CONST = 17.31234049066755
note = ln(freq/8.176)*SEMITONE_CONST
The "duration" parameter is the positive duration of the tone in milliseconds. The "volume" parameter is the audio volume within the range of 0 to 100 where 100 is the maximum volume.
This method throws:
- IllegalArgumentException
Parameter is out of range
- MediaException
Tone cannot be played due to a device-related problem
Supported Fields
TONE_DEVICE_LOCATOR
The locater used to create a Player of tone sequences.
For example:
try {
Player p = Manager.createPlayer(Manager.TONE_DEVICE_LOCATOR);
p.realize();
ToneControl tc = (ToneControl)p.getControl("ToneControl");
tc.setSequence(mySequence);
p.realize();
p.prefetch();
p.start();
} catch (IOException ioe) {
} catch (MediaException me) {}
MIDI_DEVICE_LOCATOR
The locater that is set to "device://midi" is used to create a MIDI Player. For example:
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) {}
javax.microedition.media.Controllable
Controllable obtains Controls from an object such as a Player and provides methods to query supported Controls and obtain a Control based on its class name. Note that Player extends Controllable.
Control[] getControls()
Returns a collection of Controls from the object that implements this interface. Your implementation should check each object against different Control types because a single object can implement multiple Control interfaces. The returned list of Control objects will not contain any duplicates and will not change over time. Note that a zero length array will be returned if no Control is supported.
This method throws:
IllegalStateException
getControls() has been called in an invalid state
Example:
Controllable controllable;
Control cs[];
cs = controllable.getControls();
for (int i = 0; i < cs.length; i++) {
if (cs[i] instanceof ControlTypeA)
doSomethingA();
if (cs[i] instanceof ControlTypeB)
doSomethingB();
// etc.
}
Control getControl
(String controlType)
Returns the object that implements the passed Control interface, or null if the Control interface is not supported. The "controlType" parameter is the class name of the Control provided as the fully-qualified name of the class. If the class package is not provided, the javax.microedition.media.control package is assumed. Note that only one object will be returned if the Controllable supports multiple objects that implement the same passed Control interface. Use getControls() to check the list for all Controls of the requested type.
This method throws:
- IllegalArgumentException
controlType is null
- IllegalStateException
getControl() is called in a wrong state
javax.microedition.media.control
It's relatively easy to create a Player from a known source, start the Player, and listen for events as they occur. The most difficult process is getting the audio characteristics right and providing a decent user experience through use of Controls. To follow is an in-depth look at supported and unsupported Player controls and associated fields.
MetaDataControl
Provides XML-like accessor methods to recognize, retrieve and store metadata within media streams. Predefined keys are provided for common metadata fields such as title, copyright, data, and author.
Note: This control does not work with MIDI Players.
String[] getKeys()
Returns a list of keys for the available metadata values. The returned array must contain at least one key.
String getKeyValue
(String key)
Retrieves the metadata value associated with the passed key. Only keys retrieved using getKeys() can be used to retrieve metadata values. Some keys are valid but the associated metadata may not be available before a certain portion of the media has been played. In this situation, calling getKeyValues() with those keys will return null before the data is available. The "key" parameter is a key such as title, copyright, data, author, etc. to retrieve the metadata value. This method returns the value of the key or null if the associated value is not yet available.
This method throws:
- IllegalArgumentException
The passed key is null or invalid
ToneControl
An interface that is used by Tone Players to enable playback of a user-defined sequence of tones in an unvarying pitch.
void setSequence
(byte[] sequence)
Sets the tone sequence.
This method throws:
- IllegalArgumentException
The sequence is null or invalid
- IllegalStateException
The associated Player is in the prefetched or started state
Supported Fields
AUTHOR_KEY
- Default key for author information.
- Set to "author".
COPYRIGHT_KEY
- Default key for copyright information.
- Set to "copyright".
DATE_KEY
- Default key for date information.
- Set to "date".
TITLE_KEY
- Default key for title information.
- Set to "title".
VERSION
- The VERSION attribute tag.
TEMPO
- The TEMPO event tag.
RESOLUTION
- The RESOLUTION event tag.
BLOCK_START
- Defines a starting point for a block.
BLOCK_END
- Defines an ending point for a block.
PLAY_BLOCK
- Play a defined block.
SET_VOLUME
- The SET_VOLUME event tag.
REPEAT
- The REPEAT event tag.
C4
- Middle C.
- Set to 60.
SILENCE
- Silence.
Example:
// ------------------------
// Tone Sequence definition
// ------------------------
//
// NOTE that BYTE is a single signed character
// in the range of -128 to +127
//
// ------------------------
// FIELD version:
// -- one occurrence
// ------------------------
VERSION BYTE; // Predefined constant
version_number 1; // Version # 1
//
// ------------------------
// FIELD tempo_definition
// -- at most one occurrence
// ------------------------
TEMPO BYTE; // Predefined constant
tempo_modifier BYTE; // BYTE range is 5 to 127
// Multiply by 4 to get the tempo (in bpm) used in the sequence
// Effective range is 20 bpm to 508 bpm
// Default is 120 bpm
//
// ------------------------
// FIELD resolution_definition
// -- at most one occurrence
// ------------------------
RESOLUTION BYTE; // Predefined constant
resolution_unit BYTE; // BYTE range is 1 to 127
// Effective range is 1/1 note
// to 1/127 note
// Default is 1/64 note
//
// ------------------------
// FIELD block_definition
// -- 0 to 127 occurrences
// ------------------------
BLOCK_START BYTE; // Predefined constant
block_number BYTE; // block_number specified in
// BLOCK_END has to be the same
// as the one in BLOCK_START
// BYTE range is 0 to 127
(
// ------------------------
// EITHER tone_event
// ------------------------
note BYTE; // Note to be played
// Range is 0 to 127
// or SILENCE (-1)
// Effective range is C1 to G9
// or rest
duration BYTE; // Duration of the note
// Range is 1 to 127
) || (
// ------------------------
// OR block_event
// ------------------------
PLAY_BLOCK BYTE; // Predefined constant
block_number BYTE; // block_number must be previously
// defined by a full
// block_definition
) || (
// ------------------------
// OR volume_event
// ------------------------
SET_VOLUME BYTE; // Predefined constant
volume BYTE; // New volume
// Range is 0 to 100
// Effective range is 0% to 100%
// Default is 100%
) || (
// ------------------------
// OR repeat_event
// ------------------------
REPEAT BYTE; // Predefined constant
multiplier BYTE; // Number of times to
// repeat a tone
// Range is 2 to 127
note BYTE; // Note to be played
// Range is 0 to 127
// or SILENCE (-1)
// Effective range is C1 to G9
// or rest
duration BYTE; // Duration of the note
// Range is 1 to 127
)
BLOCK_END BYTE; // Predefined constant
block_number BYTE; // block_number specified in
// BLOCK_END has to be the same
// as the one in BLOCK_START
// ------------------------
// End of definition
// ------------------------
Note values can be calculated using the following formula:
// SEMITONE_CONST = 1/(ln(2^(1/12))) SEMITONE_CONST = 17.31234049066755 note = ln(note_frequency/8.176)*SEMITONE_CONST
The following table details the frequency and note value for each musical note in a single octave. Double the values to raise the frequency and note values by one octave. Divide the values by two to reduce the frequency and note values by one octave. For example, C in the next higher octave is 523.2 Hz, while C in the next lower octave is 130.8 Hz. Note that C4 (Middle C) and SILENCE have both been defined as constants.
| Musical Note | Frequency (Hz) | Note Value |
|---|---|---|
| Middle C | 261.6 | 60 |
| C# / Db | 277.2 | 61 |
| D | 293.6 | 62 |
| D# / Eb | 311.1 | 63 |
| E | 329.6 | 64 |
| F | 349.2 | 65 |
| F#/ Gb | 370.0 | 66 |
| G | 392.0 | 67 |
| G#/ Ab | 415.3 | 68 |
| A | 440.0 | 69 |
| A# / Bb | 466.1 | 70 |
| B | 493.8 | 71 |
| Note Length | American Name | British Name | Duration | Duration with Resolution set to 64 (default) |
|---|---|---|---|---|
| ½ | Double whole note | Breve | 2 | 128 |
| 1 | Whole note | Semibreve | 1 | 64 |
| 2 | Half note | Minim | ½ | 32 |
| 4 | Quarter note | Crotchet | ¼ | 16 |
| 8 | Eighth note | Quaver | 1/8 | 8 |
| 16 | Sixteenth note | Semiquaver | 1/16 | 4 |
| 32 | Thirty-second note | Demisemiquaver | 1/32 | 2 |
| 64 | Sixty-fourth note | Hemidemisemiquaver | 1/64 | 1 |
| 128 | One hundred and twenty-eighth note | Quasihemidemisemiquaver | 1/128 |
| Tempo Name | Description | Beats per Minute (bpm) | Tempo Modifier (divide bpm by 4) |
|---|---|---|---|
| Grave | Very slow and solemn | 40 or slower | 5-10 |
| Larghissimo | Extremely slow | 40 or slower | 5-10 |
| Largo | Slowly and broadly | 40-60 | 10-15 |
| Larghetto | A little less slow than largo | 60-66 | 15-17 |
| Adagio | Slow but not as slow as largo | 66-76 | 17-19 |
| Maestoso | Majestic or stately | 70-80 | 18-20 |
| Andante | At walking pace | 76-108 | 19-27 |
| Moderato | At a moderate tempo | 108-120 | 27-30 |
| Allegro | Quick, lively and bright | 120-168 | 30-42 |
| Presto | Fast | 168-200 | 42-50 |
| Prestissimo | Very fast | 200-208 | 50-52 |
As shown above, tempo is expressed in beats per minute, where 1 beat is equal to 1/4 note. The tempo is formed by multiplying the tempo modifier by 4 to keep it within the byte range of 1 to 127. Tempos in the range of 20 bpm to 508 bpm equate to a tempo modifier range of 5 to 127.
Example
// "Mary Had A Little Lamb" has "ABAC" structure
// Use block to repeat "A" section
// 30 x 4 = tempo of 120 bpm
byte tempo = 30;
// note length 8 (quaver) = 1/8th of a note duration
byte duration = 8;
byte C4 = ToneControl.C4; // C note value = 60 (middle C)
byte D4 = (byte)(C4 + 2); // D note value = 62 (a whole step)
byte E4 = (byte)(C4 + 4); // E note value = 64 (a major third)
byte G4 = (byte)(C4 + 7); // G note value = 67 (a fifth)
byte rest = ToneControl.SILENCE; // rest
byte[] mySequence = {
ToneControl.VERSION, 1, // version 1
ToneControl.TEMPO, tempo, // set tempo
//
// start define "A" section
ToneControl.BLOCK_START, 0,
//
// content of "A" section
E4, duration, D4, duration, C4, duration, E4, duration,
E4, duration, E4, duration, E4, duration, rest, duration,
//
// end define "A" section
ToneControl.BLOCK_END, 0,
//
// play "A" section
ToneControl.PLAY_BLOCK, 0,
//
// play "B" section
D4, duration, D4, duration, D4, duration, rest, duration,
E4, duration, G4, duration, G4, duration, rest, duration,
//
// repeat "A" section
ToneControl.PLAY_BLOCK, 0,
//
// play "C" section
D4, duration, D4, duration, E4, duration, D4, duration,
C4, duration
};
try{
Player p = Manager.createPlayer(Manager.TONE_DEVICE_LOCATOR);
p.realize();
ToneControl c = (ToneControl)p.getControl("ToneControl");
c.setSequence(mySequence);
p.start();
} catch (IOException ioe) {
} catch (MediaException me) { }
VolumeControl
An interface for adjusting the volume of a Player. The output volume can be to levels from 0 to 100 where 0 is silence and 100 is maximum volume. Setting mute on or off does not change the volume level returned by getLevel(). The Player does not generate an audio signal when mute is set to true. A VOLUME_CHANGED event is delivered through the PlayerListener when the state of the VolumeControl changes. Note that this control cannot be used with Tone Players.
void setMute
(boolean mute)
Mute or unmute the Player associated with this VolumeControl. Calling setMute(true) on a muted Player, and calling setMute(false) on audible Player, is ignored. This method does not change the volume level returned by getLevel(). A VOLUME_CHANGED event will be delivered through the PlayerListener when setMute() changes the audible state.
boolean isMuted()
Returns true if the VolumeControl has been muted
int setLevel
(int level)
Sets the volume of a VolumeControl. If a volume change occurs, a VOLUME_CHANGED event will be delivered through the PlayerListener. The "level" parameter is the new volume level in the range of 0 and 100 where 0 is silence and 100 is the loudest level. A passed level less than 0 will be reset to 0 and a passed level greater than 100 will be reset to 100. This method returns the volume level set
int getLevel()
Returns the current volume level or -1 if the Player is in the REALIZED state and setLevel() has not yet been called.
In Summary
The Media API provides developers with a toolkit that can be used to enhance user experience. The Media API implementation on BlackBerry devices opens up the world of audio playback. With luck you will find this article useful in developing your own killer apps. If so, please send us an article detailing your experiences so that other developers can learn from you.
Please email your comments, suggestions and editorial submissions to