Audio player

RFO-BASIC! provides an interface to the Android Media Player. This lets a BASIC program play music, podcasts, and stream audio from the Internet.

Related interfaces described in this article are:
 * The sound recorder, which uses the Android device's microphone as the source and a .3GP file as the destination.
 * The SoundPool, a pool of short sound effects that can play over a song, podcast, stream, or other sound effects.

The file types you can play depend on your device and the version of Android it runs. Here is a partial summary:
 * Supported file types

 

The Media Player cannot play .PLS files, though some audio programs can. These files are not media but playlists. It can play .3GP files, such as those produced by the BASIC sound recorder. A current list of supported file types is in the Android documentation.

Many BASIC programs use the Android Media Player to play music indefinitely while the device is unattended. The BASIC program must continue running; otherwise, Android releases all the resources it used, including the Media Player, and the music stops. The BASIC program can enter an infinite loop where it PAUSEs and occasionally checks for user input to end the play.
 * Stability issues

The behavior of the Media Player differs between Android versions and between makes of Android device. Once your audio application works correctly, you should test it on other versions of Android, and on other makes of device, especially before publishing it for general use.

The Media Player runs in the background using the Android AsyncTask class. This implementation avoids interruptions and warning dialogs. However, your program may stop playing music:
 * If it is in the foreground but the user switches off the display, such as by pressing a hardware button or closing a flip-phone.
 * If the user presses the HOME button and your program does not handle that event with the ONBACKGROUND: interrupt routine.
 * If it is running on a Samsung device that gets plugged into a recharger.

To avoid being interrupted, a music player program can have an ONBACKGROUND: and ONBACKKEY: interrupt handlers. It can use the timer interrupt with a NOTIFY statement, as the Android core will not stop apps that have an active notification. The user may be instructed to use Android developer's settings to select less active power management.

Audio player
The audio player plays audio files. The files can be of a variety of file types; see the start of this article. The program must load audio files into the audio file table (AFT) using AUDIO.LOAD before it can play the files. Each entry in the AFT has a unique index.

AUDIO.LOAD
Gets an audio file for playing

AUDIO.LOAD , 
 * Synopsis

The AUDIO.LOAD statement specifies an audio source, in , and loads it into the AFT so that it can be played. The statement returns, in , the index into the AFT. This number must be used in a subsequent AUDIO.PLAY statement to identify the audio file.
 * Description

The  can be any of the following:
 * The name of a file, which is assumed to reside in the  directory on the device on which RFO-BASIC! is installed (normally, the SD card).
 * The full URL of a podcast or a streaming audio source on the Internet.

If the file or stream cannot be loaded, AUDIO.LOAD sets  to 0. The BASIC program should test this variable to determine if the audio was loaded. If not, GETERROR$ will return information about the error.

AUDIO.LOAD aft1,"Blue Danube Waltz.mp3"
 * Examples

The above statement would load " /rfo-basic/data/Blue Danube Waltz.mp3".

AUDIO.LOAD aft2,"../../Music/Blue Danube Waltz.mp3"

The above statement loads an MP3 file in the Music directory in the visible root of the device on which RFO-BASIC! is installed. Instead of Music, files in the sibling directories Downloads and Podcasts might also be specified.

AUDIO.LENGTH
Get the length of an audio source

AUDIO.LENGTH , 
 * Synopsis

After AUDIO.LOAD puts an audio source into the AFT, the AUDIO.LENGTH statement obtains the length of that audio. The  parameter specifies an AFT entry and AUDIO.LENGTH sets  to its length, in milliseconds.
 * Description

If the specified AFT refers to an Internet audio stream, it does not have a length.

AUDIO.PLAY
Play an audio source

AUDIO.PLAY 
 * Synopsis

The AUDIO.PLAY statement selects one of the audio sources that the program has loaded into the audio file table (AFT) using AUDIO.LOAD and begins to play it.
 * Description

AUDIO.PLAY produces a run-time error if audio is already playing. To avoid this, AUDIO.PLAY can be preceded by AUDIO.STOP.

If the program has used AUDIO.PAUSE to pause play, then calling AUDIO.PLAY resumes play from the point at which it was paused. In this case,  is ignored.
 * Exception — paused audio

AUDIO.RELEASE
Delete an entry from the audio file table

AUDIO.RELEASE 
 * Synopsis

The AUDIO.RELEASE statement releases the resources used by the AFT entry pointed to by . The file must not be currently playing. The specified entry will no longer be playable.
 * Description

Managing play
The BASIC statements that manage the playing of audio do not refer to the AFT. A BASIC program can play only one sound source, and these statements all implicitly refer to that one activity.

AUDIO.ISDONE
Check whether play is complete

AUDIO.ISDONE <done_lvar>
 * Synopsis

If sound is currently playing, then AUDIO.ISDONE sets <done_lvar> to FALSE (0). If playing is complete, it is set to TRUE (1). If the sound selected for playing is an Internet stream, it might never be done.
 * Description

The BASIC program must continue running to keep sound playing. A typical thing to do is wait in a loop for the playing to end.
 * Example

AUDIO.PLAY aft1 DO AUDIO.ISDONE done IF done THEN D_U.BREAK       % Exit DO loop if playing is done IF KeyPressed THEN D_U.BREAK % Perhaps also test a variable set by interrupt handler PAUSE 1000 UNTIL 0                       % Repeat forever unless BREAK takes effect AUDIO.STOP

Other statements in this article let a BASIC audio player pan between the left and right speakers, adjust the volume, sense the current point in the audio (which it might display on the screen), and use a slider on the screen to let the user jump to any desired point in the audio. These operations would typically also be put inside the loop that waits for the player to be done.

AUDIO.STOP
Stop play

AUDIO.STOP
 * Synopsis

The AUDIO.STOP statement terminates the playing of sound. The statement has no effect if sound was not already playing. It is safest to precede any AUDIO.PLAY command with AUDIO.STOP.
 * Description

AUDIO.PAUSE
Pause play

AUDIO.PAUSE
 * Synopsis

The AUDIO.PAUSE statement pauses play. The immediate effect is the same as AUDIO.STOP. However, a subsequent call to AUDIO.PLAY resumes play from the point at which it was paused.
 * Description

AUDIO.LOOP
Set the current audio to auto-repeat

AUDIO.LOOP
 * Synopsis

The AUDIO.LOOP statement sets a mode in which the current audio source auto-repeats. When it reaches end-of-file, it jumps to the start of the file and plays again. There must be audio playing when AUDIO.LOOP is executed.
 * Description

There is no way to clear this mode for the current audio except to execute AUDIO.STOP and restart the audio. If an Internet stream is playing, AUDIO.LOOP has no effect.

AUDIO.POSITION.CURRENT
Get the current position in the audio

AUDIO.POSITION.CURRENT <pos_nvar>
 * Synopsis

The AUDIO.POSITION.CURRENT statement returns, in <pos_nvar>, the current position (in milliseconds past the start of the audio) of the currently playing audio.
 * Description

AUDIO.POSITION.SEEK
Move to a specified position in the audio

AUDIO.POSITION.SEEK <pos_nexp>
 * Synopsis

The AUDIO.POSITION.SEEK statement moves to the location specified by <pos_nexp> in the currently playing audio.
 * Description

The following code executes a 5-second backward jump in an audio file, without jumping before the start of the file.
 * Example

AUDIO.POSITION.CURRENT p LET p = MAX(p - 5000, 0) AUDIO.POSITION.SEEK p

AUDIO.VOLUME
Adjust the playback volume

AUDIO.VOLUME <left_nexp>, <right_nexp>
 * Synopsis

The AUDIO.VOLUME statement changes the playback volume. The <left_nexp> parameter specifies the new volume of the left channel, and <right_nexp> specifies the new volume of the right channel. There must be a currently playing file when this command is executed.
 * Description

If the Android device has only one speaker, the effect is device-specific. The speaker volume may be only one of the specified values, or it may be the average of the two values.

Valid volume levels range between 0.0 (silence) and 1.0 (loudest). The scale is logarithmic. A BASIC program trying to achieve fade-in or fade-out by repeated additions or subtractions will produce an effect where the sound starts or stops suddenly, because the jump from 0.0 to 0.1 is much more drastic than the jump from 0.9 to 1.0. Instead, it should achieve fade-in and fade-out by repeated multiplications or divisions. Multiplying a volume level by 0.89 reduces the volume by 1 dB. (The human ear perceives each 10 dB increase as twice as loud.)

The following program fades out audio smoothly in no more than four seconds:
 * Example

FN.DEF FadeOut(CurrentVolume) DO AUDIO.VOLUME(CurrentVolume, CurrentVolume) PAUSE 100                   % Wait 0.1 second IF KeyPressed THEN FN.RET CurrentVolume % Perhaps abort if var. set by interrupt handler LET CurrentVolume *= 0.89   % Reduce by 1 decibel (caller's variable doesn't change) UNTIL CurrentVolume < 0.01  % Pretty quiet AUDIO.VOLUME(0, 0)           % End in total silence FN.RTN 0                     % Report success (complete silence without user abort) FN.END

A program could pre-compute a table of volume levels to map logarithmic volume levels to a slider on the graphic screen. By touching one of 40 zones on the slider, the user would perceive a smooth change in volume.

DIM Vol[40] a = 1.00 FOR i = 1 to 39 Vol[i] = a         % First setting selects full volume a *= 0.89 NEXT i A[40] = 0.00        % Final setting selects total silence

Sound recorder
Additional BASIC statements let you write your own sound recorder. Recording always uses the device's microphone as the source and a file as the destination.

Your sound recorder program should either be simple or monitor its recording mode, as it is a run-time error to start recording if it is already underway, or stop recording if it is already stopped.

AUDIO.RECORD.START
Start recording sound

AUDIO.RECORD.START <filename_sexp>
 * Synopsis

Private version

In OLI-BASIC, AUDIO.RECORD.START permits additional output file types. The AUDIO.RECORD.START statement takes additional optional parameters that govern the recording. Once recording is complete, an AUDIO.RECORD.PEAK statement returns the peak volume level of the recording. </DIV> The AUDIO.RECORD.START statement begins audio recording. The single parameter is a string expression that specifies the name of the file to contain the audio recording. This file must have the file type (the string must end with). The file is created in the  directory on the device on which RFO-BASIC! was installed (normally, the SD card).
 * Description

While the recording is in progress, the BASIC program can take arbitrary action; it can pass time in a PAUSE statement, but it should periodically monitor for user input, such as a screen touch, that is a command to stop recording.

AUDIO.RECORD.STOP
Stop recording sound

AUDIO.RECORD.STOP
 * Synopsis

The AUDIO.RECORD.STOP statement stops recording that had been started using AUDIO.RECORD.START, and closes the  file.
 * Description

SoundPool
A SoundPool is a collection of short sound streams that are preloaded and ready for instantaneous play. SoundPool streams can be played while other sounds are playing, either by the audio player or played over other streams. In a game, the media player would produce the background music while the SoundPool streams would be the game sounds (Bang, Pow, Screech, etc.).

The SoundPool specifies the maximum number of streams that can be played at one time. The programmer can use this limit to avoid a cluttered effect.

As when using the audio player, a BASIC program must preload any sound streams it will use. The SOUNDPOOL.LOAD statement takes a string expression specifying the name of a file containing audio, and returns a unique index by which the program can refer to that stream later. When loading a stream, the programmer can specify many attributes, including its priority.

The program starts a stream playing with the SOUNDPOOL.PLAY statement. It specifies the stream using the index, and SOUNDPOOL.PLAY returns a stream ID. If a SoundPool is already playing its maximum number of streams, playing another stream with the SOUNDPOOL.PLAY statement causes the lowest-priority stream to stop. The program uses the stream ID to control the progress of playback of streams, either individually or as a group.

SOUNDPOOL.OPEN
Open a SoundPool

SOUNDPOOL.OPEN <maxstreams_nexp>
 * Synopsis

The program calls the SOUNDPOOL.OPEN statement first in order to enable use of the SoundPool feature. The <maxstreams_nexp> specifies the number of SoundPool streams that can be played at once. This number excludes any audio that might be playing via AUDIO.PLAY.
 * Description

SOUNDPOOL.LOAD
Load a stream into a SoundPool

SOUNDPOOL.LOAD <index_nvar>, <file_path_sexp>
 * Synopsis

It can take significant time to load the SoundPool streams. To be certain they are playable, follow SOUNDPOOL.LOAD with PAUSE 500, or pre-load the SoundPool before interacting with the user. </DIV> The SOUNDPOOL.LOAD statement loads the file named by <file_path_sexp> into the SoundPool. Files by this name are assumed to reside in the  directory on the device on which RFO-BASIC! is installed (normally, the SD card). The statement returns in <index_nvar> a unique index the program uses in SOUNDPOOL.PLAY to start this stream playing.
 * Description

If it is not possible to load the stream, SOUNDPOOL.LOAD sets <index_nvar> to 0.

SOUNDPOOL.UNLOAD
Unload a stream from a SoundPool

SOUNDPOOL.UNLOAD <index_nexp>
 * Synopsis

The SOUNDPOOL.UNLOAD statement unloads <index_nexp>, the index that SOUNDPOOL.LOAD had returned when loading the stream.
 * Description

SOUNDPOOL.PLAY
Start a stream playing

SOUNDPOOL.PLAY <streamID_nvar>, <index_nexp>, <rvol_nexp>, <lvol_nexp>, <priority_nexp>, <loop_nexp>, <rate_nexp>
 * Synopsis

Starts the specified sound ID playing.
 * Description

The SOUNDPOOL.PLAY statement starts to play the stream indentified by <index_nexp>. SOUNDPOOL.PLAY returns in <streamID_nvar> a unique stream ID the program uses to modify, pause, resume, and stop this stream. The caller specifies other aspects of the play by passing the following numeric expressions to SOUNDPOOL.PLAY:
 * — The playback volume on the right and left speakers, respectively. These values can range from 0.0 (silence) through 0.99 (loudest).
 * — The playback priority. This value can be 0 (lowest priority) and higher.  If a SOUNDPOOL.PLAY statement is called but the maximum number of streams specified in SOUNDPOOL.OPEN is already playing, SOUNDPOOL.PLAY halts the playing of the lowest-priority stream.
 * — The number of times this stream is to repeat. A value of 0 means it does not repeat; a value of 1 means it plays a total of 2 times, and so on.  A value of -1 plays the stream continuously.
 * — The speed at which to play the stream. A value of 1 means to play it at the rate at which it was recorded.  The value can range from 0.5 (half-speed) through 1.85.

If it is not possible to start the stream playing (for example, if one of the numeric parameters is out of bounds), then SOUNDPOOL.PLAY sets <streamID_nvar> to 0.

SOUNDPOOL.RELEASE
Release all SoundPool resources

SOUNDPOOL.RELEASE
 * Synopsis

The SOUNDPOOL.RELEASE statement releases all resources involved in a SoundPool, such as memory used to hold the audio information of the streams. The BASIC program can call SOUNDPOOL.OPEN again to set up a new SoundPool
 * Description

Modifying the play of a SoundPool stream
The program can use several SOUNDPOOL. statements to modify the play of a SoundPool stream in progress. The parameters of these statements have the same meanings as the corresponding parameters in the SOUNDPOOL.PLAY statement and have the same valid ranges.

Editor's queries

 * Are left and right speakers really specified backwards in SOUNDPOOL.PLAY versus SOUNDPOOL.SETVOLUME?