soundplayer — n_pbt_soundplayer #
← Component reference · Guide contents
Plays a sound or a piece of music — a file of the workstation or a URL — asynchronously (events) or synchronously (the call returns when the sound is over), and a pure beep without any file. Every format the WebView2 engine decodes: mp3, wav, ogg, opus, flac, aac, m4a, mp4, webm. No third-party DLL, no
PlaySoundlimited to wav.
▶ See it live — Demo application, Sound player tile: the sounds, the code that plays them and this page, side by side.
At a glance #
| Nonvisual object | n_pbt_soundplayer |
| Used for | A chime before a message, an alert, a waiting music, an mp3 picked by the user, a stream from the Internet |
| Principle | A hidden page carries a player; of_play returns at once and the events tell the rest, of_play_sync waits for the end of the sound |
| Dependency | The WebView2 runtime, already required by the library — nothing else |
Quick start #
// In the window : the object
n_pbt_soundplayer inv_sound
inv_sound = create n_pbt_soundplayer
// Synchronous : the next line runs after the chime
inv_sound.of_play_sync(/*source*/ "C:\Windows\Media\chimes.wav")
MessageBox("Orders", "Order 4152 shipped.")
// Asynchronous : a music from the Internet, the window stays alive. The ue_started /
// ue_ended events arrive on their own - nothing else to wire.
inv_sound.ii_volume = 60
inv_sound.of_play(/*source*/ "https://example.org/music/lobby.mp3")
The ue_* events are delivered on their own — the component drains them itself while the player is open and raises them on the component: nothing to wire, no receiver and no timer.
File or URL, and why a long piece starts at once #
- A file of the workstation is any path PowerBuilder can name — absolute, relative to the application folder, network. The DLL serves it to the player as a stream, in slices, with the range requests a player emits to move within the piece: nothing is loaded in memory, and
of_seekis immediate. - An
httpsURL is passed as it is: the sound starts once its beginning has arrived. Anhttp://address is refused (ue_failed,REASON_INSECURE): the player's page is served over https, and the engine would upgrade the address to https or block it — an intranet server without TLS would look missing. Download it first (restclient.of_downloadto a temporary file). A media is not subject to CORS, unlike an API call — seerestclientfor that case. - A beep (
of_beep,of_beep_sync) needs no file: an oscillator of the audio engine, at the frequency and for the duration you give.
Properties #
| Property | Type | Default | Role |
|---|---|---|---|
ii_volume | integer | 100 | Volume in percent, 0 to 100, handed over with the verb that starts a sound — and it is the volume of THAT sound: of_play, of_play_sync and of_crossfade set the main sound; of_play_over, of_beep and of_play_named play their own at it without touching the main sound (an alert at 100 over a background lowered to 20); a sound queued by of_enqueue keeps it for its turn. Changed while a sound plays, it waits for the next verb — only of_fade_to changes the sound playing |
ib_loop | boolean | false | True to play the sound again and again until of_stop — a ring, a background music. Handed over with the next sound. of_play_sync ignores it: a synchronous sound plays once, a loop has no end to wait for |
is_last_error | string | "" | Why the last call failed: missing file, undecodable format, http:// address, silent URL, no audio output. Filled by ue_failed too. After an of_play_sync that returned 0: "stopped" when the sound was cut rather than played to its end |
il_timeout_ms | long | 300000 | The longest a synchronous sound (of_play_sync, of_beep_sync, of_play_named_sync) may last; five minutes by default. Past it the call returns -4 and the sound is cut (ue_stopped follows). 0 or less: no limit |
ii_rate | integer | 100 | Speed in percent (25 to 400), handed over with the verb that starts a sound, for THAT sound: the main sound, or an overlay (of_play_over) without changing the main one |
ii_pan | integer | 0 | Balance of the MAIN sound, from -100 (left) to 100 (right), handed over with of_play, of_play_sync or of_crossfade, for FILES; a URL plays unbalanced |
il_fade_ms | long | 0 | Fade in milliseconds: every start rises from silence, every stop or pause dies out |
ipo_owner | powerobject | null | The visual object this player works for: the licence is checked on its class. Needed only in the demonstration application; a development or runtime key unlocks the player without it. Not unlocked, the player runs in demo mode — only the first 10 seconds of a sound are played |
Methods #
| Method | Role |
|---|---|
of_open ( ) → long | Creates the player. Optional — every method does it — but calling it when the window opens pays the cost once. Returns the handle (> 0), or -2 when the player could not be created (is_last_error says why) |
of_is_open ( ) → boolean | True once the player exists |
of_play (string as_source {, long al_start_ms}) → long | Plays without waiting: a file of the workstation or an https URL (an http:// address is refused, REASON_INSECURE), the end comes through ue_ended. al_start_ms starts the sound that far in (in demo mode, never past the first 10 seconds). The main sound playing is replaced; a beep or a named sound goes on. Returns 0 once started, -2 when the player could not be created, -4 when the page did not answer |
of_play_sync (string as_source) → long | Plays and waits for the end: the call returns when the sound is over (or stopped). The window keeps painting, but past 250 ms every PBToolboxAI component of the application shows the busy cursor and ignores the mouse until the call returns: keep it for short sounds. Plays once, whatever ib_loop says. Returns 0 once the sound is over (is_last_error = "stopped" when it was cut), -2 when the player could not be created, -4 when it could not play, when il_timeout_ms elapsed (the sound is cut) or when another synchronous sound is already waiting |
of_beep (long al_hz, long al_ms) → long | A pure tone without a file: al_hz (20 to 20,000) for al_ms milliseconds (10 to 5,000), at ii_volume. A channel of its own: of_play does not cut it, of_stop does. Returns at once; ue_ended follows. Returns 0 once started, -2 when the player could not be created |
of_beep_sync (long al_hz, long al_ms) → long | The same tone, and the call returns once it is over: two in a row make a two-note signal. Returns 0, -2 when the player could not be created, or -4 without an audio output or when another synchronous sound is already waiting (is_last_error) |
of_stop ( ) | Stops everything the player plays — the sound, a beep, a named sound, the overlays, a crossfade — and empties the queue; one ue_stopped follows |
of_pause ( ) → long | Suspends the sound where it is; of_resume picks it up. Returns 0 once done, -2 when the player could not be created |
of_resume ( ) → long | Picks the sound up where of_pause left it. Returns 0 once done, -2 when the player could not be created |
of_seek (long al_ms) → long | Moves to a position of the sound, in milliseconds from its start. In demo mode, never past the first 10 seconds. To START from a position, of_play takes one. Returns 0 once done, -5 when no sound is playing (nothing to move), -2 when the player could not be created, -4 when the page did not answer |
of_is_playing ( ) → boolean | True while a sound, a beep, a named sound or an overlay is under way — a paused sound counts, it has not finished |
of_is_paused ( ) → boolean | True while the sound is suspended by of_pause |
of_duration ( ) → long | The length of the sound in milliseconds, 0 while unknown; ue_started carries it too |
of_position ( ) → long | Where the sound is, in milliseconds — poll it from a timer to move a progress bar of yours |
of_source ( ) → string | What of_play received last, verbatim |
of_play_over (string as_source) → long | Plays a sound ON TOP of what plays, without cutting it, at ii_volume and ii_rate — its own: the background keeps its own (a background lowered by of_fade_to stays low under an alert at 100); ue_ended names it by its source, of_stop stops it with the rest. Returns 0 once done, -2 when the player could not be created |
of_enqueue (string as_source) → long | QUEUES a sound: played at once when the player is idle, after the current sound (or beep, or named sound) otherwise, never cut. The queued sound keeps ii_volume, ii_rate and ib_loop as they are at the call, for its turn. A sound that cannot play raises ue_failed and the queue goes on. Returns 0 once done, -2 when the player could not be created |
of_clear_queue ( ) → long | Forgets the sounds waiting, without cutting the one playing; ue_queue_done still follows its end, it is the last sound of the queue. Returns 0 once done, -2 when the player could not be created |
of_queue_count ( ) → long | Returns the number of sounds still waiting |
of_preload (string as_source) → long | Loads a sound without playing it: the first of_play of that source starts at once. An unreadable source is reported by that of_play, through ue_failed. Returns 0 once done, -2 when the player could not be created |
of_fade_to (integer ai_volume, long al_ms) → long | Brings the volume to ai_volume (0 to 100) in al_ms ms on the sound playing; the target becomes ii_volume, and a sound played over it meanwhile does not interrupt the fade. Returns 0 once done, -2 when the player could not be created |
of_play_named (string as_name) → long | A SYNTHESISED sound, no file, at ii_volume: SOUND_SUCCESS, SOUND_ERROR, SOUND_WARNING, SOUND_INFO, SOUND_NOTIFY. A channel of its own: of_play does not cut it, of_stop does. Returns 0 once started, -2 when the player could not be created; an unknown name comes through ue_failed (REASON_UNKNOWN) |
of_play_named_sync (string as_name) → long | The same sound, and the call returns at its end. Returns 0, -2 when the player could not be created, -4 on an unknown name, without an audio output or when another synchronous sound is already waiting (is_last_error) |
of_has_output ( ) → boolean | True when the workstation has an audio output at all: to ask BEFORE an alert. The sound always goes to the default output of Windows |
of_equalizer (boolean ab_on {, integer ai_gains[]}) → long | 5-band equalizer (60 / 230 / 910 / 3600 / 14000 Hz, of_eq_frequencies gives the centers), gain in dB from -24 to 24 (a value outside is brought back to the nearest bound); the overload sets all gains, of_equalizer_band sets one live. LOCAL files. Returns 0 once done, -2 when the player could not be created |
of_equalizer_band (integer ai_band, integer ai_gain) → long | Sets the gain of ONE band (ai_band 1 to 5, ai_gain in dB, -24 to 24) and applies it at once to the sound playing — a slider of yours moved live; the other bands keep their gain. Returns 0, -5 when the band is out of range, -2 when the player could not be created |
of_eq_frequencies (ref long al_freqs[]) → long | Fills al_freqs with the center frequency of each band, in Hz and in order (60, 230, 910, 3600, 14000) — what a band label of yours shows. Returns how many bands there are, 5 |
of_meter (boolean ab_on {, integer ai_bars}) → long | Turns a VU meter / analyser on: then poll of_level (0 to 100) and of_spectrum (ai_bars bars) from a timer to draw your own visualiser. LOCAL files. Returns 0 once done, -2 when the player could not be created |
of_level ( ) → long | Returns the loudness right now, 0 to 100, read live — a VU meter needle; 0 when the meter (of_meter) is off or nothing plays. Poll it from a timer |
of_spectrum (ref long al_bars[]) → long | Fills al_bars with the spectrum, read live: as many values as of_meter asked for, 0 to 100 each, low frequencies first — the bars of an analyser of yours. Returns how many bars there are, 0 with the meter off |
of_crossfade (string as_source, long al_ms) → long | Crossfade: the current sound dies out over al_ms while the new source rises — a change of track with no gap; 0 switches at once. The new sound takes ii_volume, ii_rate, ii_pan and ib_loop, and an of_fade_to during the rise takes it over. of_stop during the fade stops both, and an of_play_sync waiting for the first sound returns. Returns 0 once started, -2 when the player could not be created; an empty source comes through ue_failed |
of_process_events ( ) | Drains the event queue and raises them on the object. The component's own pump calls it for you while the player is open — you never call it |
of_close ( ) | Releases the player, stopping the sound first: a sound that was playing ends on its ue_stopped, raised before the call returns; done for you when the object is destroyed |
of_reset ( ) | Stops the sound and puts every setting back to its default |
Events #
| Event | When |
|---|---|
ue_started (string as_source, long al_duration_ms) | The sound really starts — a URL, once enough data has arrived. al_duration_ms is the length when the file says it, 0 otherwise |
ue_ended (string as_source, boolean ab_truncated) | The sound is over, of itself. ab_truncated is true when the demo limit cut it, never with a licence. A looping sound never ends: it stops |
ue_failed (string as_source, string as_message, string as_reason) | The sound could not play. as_reason is a word to test, a REASON_* constant: REASON_INSECURE (http:// address), REASON_FORMAT (format refused by its name), REASON_UNSUPPORTED (missing file, malformed file:// address), REASON_NETWORK, REASON_DECODE, REASON_NO_SOURCE, REASON_UNKNOWN (no such named sound), REASON_UNAVAILABLE (no audio output), REASON_FAILED; as_message is the sentence to show. A queued sound that fails does not stop the queue |
ue_stopped ( ) | Raised once when of_stop (or of_close, or the end of il_timeout_ms) cut what was playing — not when a sound ends of itself, that is ue_ended |
ue_paused ( ) / ue_resumed ( ) | The sound is suspended, then picks up again |
ue_progress (long al_position_ms, long al_duration_ms) | Once a second of real time while playing, whatever ii_rate: position and length |
ue_queue_done ( ) | The last sound of the queue (of_enqueue) has ended |
Several sounds, fades, queue #
- The main channel and the overlay.
of_play,of_pause,of_seek,of_stopdrive ONE sound: a piece of music, a ringing.of_play_overplays a second one on top, cutting nothing — an alert over a background.ue_endednames each by its source. Each verb sets ITS sound:ii_volumeandii_ratehanded over withof_play_overare the overlay's, not the background's — an alert at 100 over a background lowered to 20 byof_fade_to, the background stays at 20. Beeps and named sounds are channels of their own:of_playdoes not cut them, onlyof_stopcuts everything. - The queue.
of_enqueuewaits for the end of the current sound;ue_queue_donesays when the last one is over. A sound that fails (ue_failed) does not silence the next ones.of_playstays the verb that cuts. - The fades.
il_fade_msmakes every start rise from silence and every stop die out;of_fade_tolowers the background during an announcement. Nothing abrupt. - The named sounds.
of_play_named(SOUND_SUCCESS)and its four siblings are synthesised: nothing to ship, nothing to look for on the disk. - Before the alert.
of_preloadopens the file ahead,of_has_outputsays whether the workstation has an output at all. The sound goes to the default output of Windows: the engine gives the id of an output only to a page granted media access, which an ordinary workstation does not grant — so choosing an output is not offered. - The balance (
ii_pan) holds for files of the workstation. A URL plays unbalanced: the browser refuses to feed a sound from another site into its audio graph (CORS), and that is not a setting. - No recording. Capturing the microphone is another component; this one plays.
// A background in a loop, faded in ; an alert over it, the background lowered meanwhile
inv_sound.il_fade_ms = 1500
inv_sound.ib_loop = true
inv_sound.of_play(/*source*/ "C:\Windows\Media\Ring05.wav")
inv_sound.of_fade_to(/*volume*/ 20, /*ms*/ 500)
// The alert has its own volume : the background stays at 20
inv_sound.ii_volume = 100
inv_sound.of_play_over(/*source*/ "C:\Windows\Media\Alarm01.wav")
// Announcements in a row : each one waits for the previous
inv_sound.of_enqueue(/*source*/ "order-4152.mp3")
inv_sound.of_enqueue(/*source*/ "order-4153.mp3")
// A confirmation with no file at all
inv_sound.of_play_named(/*name*/ n_pbt_soundplayer.SOUND_SUCCESS)
Examples #
A two-note signal, without any file #
// Each call waits for its tone : the second starts when the first is over
inv_sound.of_beep_sync(/*hz*/ 660, /*ms*/ 180)
inv_sound.of_beep_sync(/*hz*/ 990, /*ms*/ 220)
An mp3 picked by the user, with a progress bar #
// Local variables
string ls_path, ls_file
// Let the user pick a music file, then play it
if GetFileOpenName("Music", ls_path, ls_file, "mp3", "Music (*.mp3;*.wav;*.flac),*.mp3;*.wav;*.flac") = 1 then
inv_sound.of_play(/*source*/ ls_path) // returns at once ; ue_started brings the length
Timer(0.5) // in the window's timer event :
end if
// event timer of the window
if inv_sound.of_is_playing() then
hpb_progress.Position = inv_sound.of_position() * 100 / Max(inv_sound.of_duration(), 1)
end if
A ring in a loop until the user acts #
// A ring at 40 %, in a loop until it is stopped
inv_sound.ii_volume = 40
inv_sound.ib_loop = true
inv_sound.of_play(/*source*/ "C:\Windows\Media\Ring01.wav")
// ... later, in the button that acknowledges the call :
inv_sound.of_stop()
Good practice #
- One object per window, opened with it (
of_open): the hidden player costs a few hundred milliseconds the first time, nothing afterwards. of_play_syncblocks your script, and the mouse past 250 ms: the window repaints, but every PBToolboxAI component of the application shows the busy cursor and ignores clicks until the call returns; and the component's events as well as the timers of your windows keep arriving while waiting — a synchronous sound asked from one of them is refused (-4). Keep it for short sounds — a chime, a prompt — and useof_playfor music.- A missing file is not an exception:
of_play_syncreturns-4,of_playraisesue_failed, andis_last_errornames the file.