Audio player

From RFO-BASIC! Manual
Manual contents (Statement index)
Language features The basicsArraysData structuresInterrupt routinesUser-defined functions
Interfaces Audio playerBluetoothFilesGraphicsHTMLKeyboardSocketsSQLTelecom
Programming notes Coexisting with Android

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.
Supported file types

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

Media file type Played by Android version
FLAC 3.1+
AAC-ELD 4.1+
MKV 5.0+

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.

Stability issues

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.

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[edit]

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.


Gets an audio file for playing

AUDIO.LOAD <aft_nvar>, <filename_sexp>

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

The <filename_sexp> can be any of the following:

  • The name of a file, which is assumed to reside in the <pref base drive>/rfo-basic/data/ 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 <aft_nvar> 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"

The above statement would load "<pref base drive>/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.


Get the length of an audio source

AUDIO.LENGTH <length_nvar>, <aft_nexp>

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

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


Play an audio source

AUDIO.PLAY <aft_nexp>

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.

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

Exception — paused audio

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, <aft_nexp> is ignored.


Delete an entry from the audio file table

AUDIO.RELEASE <aft_nexp>

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

Managing play[edit]

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.


Check whether play is complete

AUDIO.ISDONE <done_lvar>

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.


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.

 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

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.


Stop play


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.


Pause play


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.


Set the current audio to auto-repeat


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.

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.

Random access to audio[edit]


Get the current position in the audio


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.


Move to a specified position in the audio


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


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

LET p = MAX(p - 5000, 0)

Playback volume[edit]


Adjust the playback volume

AUDIO.VOLUME <left_nexp>, <right_nexp>

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.

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:

FN.DEF FadeOut(CurrentVolume)
 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)

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
A[40] = 0.00         % Final setting selects total silence

Sound recorder[edit]

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.


Start recording sound

AUDIO.RECORD.START <filename_sexp>

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) .3GP. The file is created in the data directory on the device on which RFO-BASIC! was installed (normally, the SD card).

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.


Stop recording sound


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


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.


Open a SoundPool

SOUNDPOOL.OPEN <maxstreams_nexp>

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.


Load a stream into a SoundPool

SOUNDPOOL.LOAD <index_nvar>, <file_path_sexp>

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 <pref base drive>/rfo-basic/data/ 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.

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


Unload a stream from a SoundPool


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


Start a stream playing

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

Starts the specified sound ID playing.

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:

  • <rvol_nexp>, <lvol_nexp> — The playback volume on the right and left speakers, respectively. These values can range from 0.0 (silence) through 0.99 (loudest).
  • <priority_nexp> — 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.
  • <loop_nexp> — 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.
  • <rate_nexp> — 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.


Release all SoundPool resources


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

Modifying the play of a SoundPool stream[edit]

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.

Statement Effect
SOUNDPOOL.STOP <streamID_nexp> Stop the playing of the stream.
SOUNDPOOL.SETVOLUME <streamID_nexp>, <lvol_nexp>, <rvol_nexp> Change the sound volume of the left and right speaker.
SOUNDPOOL.SETRATE <streamID_nexp>, <rate_nexp> Change the rate (speed) of playback.
SOUNDPOOL.SETPRIORITY <streamID_nexp>, <priority_nexp> Change the priority of the stream.
SOUNDPOOL.PAUSE <streamID_nexp> Pauses the playing of the stream. SOUNDPOOL.PAUSE 0 pauses all streams.
SOUNDPOOL.RESUME <streamID_nexp> Resumes the playing of the stream. SOUNDPOOL.RESUME 0 resumes the playing of all streams that SOUNDPOOL.PAUSE had paused.

Editor's queries[edit]

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