///
module TDev.RT {
//? Play, stop or resume songs, ...
export module Player
{
var _rt: Runtime;
var _activeSong: Song;
var _songs: Songs = undefined;
var _audio: HTMLAudioElement;
var _source: HTMLSourceElement;
var _shuffled: boolean;
var _onActiveSongChanged : Event_;
var _onPlayerStateChanged : Event_;
export function rt_start(rt: Runtime): void
{
_rt = rt;
_activeSong = null;
_songs = null;
_onActiveSongChanged = undefined;
_onPlayerStateChanged = undefined;
if (rt.eventEnabled("active song changed"))
Player.addActiveSongChangedEvent(rt);
if (rt.eventEnabled("player state changed"))
Player.addPlayerStateChangedEvent(rt);
}
export function rt_stop(rt:Runtime)
{
Player.removeActiveSongChangedEvent();
Player.removePlayerStateChangedEvent();
_rt = null;
_activeSong = null;
_songs = null;
_onActiveSongChanged = undefined;
_onPlayerStateChanged = undefined;
}
export function addActiveSongChangedEvent(rt : Runtime) {}
export function removeActiveSongChangedEvent() {}
export function addPlayerStateChangedEvent(rt: Runtime) {}
export function removePlayerStateChangedEvent() { }
//? Gets the active song if any
//@ quickAsync readsMutable returns(Song)
//@ embedsLink("Player", "Song")
export function active_song(r : ResumeCtx) // : Song
{
r.resumeVal(_activeSong);
}
//? Attaches a handler when the active song changes
//@ ignoreReturnValue
export function on_active_song_changed(changed : Action) : EventBinding {
if (_rt && !_onActiveSongChanged) {
_onActiveSongChanged = new Event_();
Player.addActiveSongChangedEvent(_rt);
}
return _onActiveSongChanged.addHandler(changed);
}
function raiseActiveSongChanged() {
if (_rt) {
if (_rt.eventEnabled("player state changed"))
_rt.queueEvent("active song changed", null, []);
if (_onActiveSongChanged && _onActiveSongChanged.handlers)
_rt.queueLocalEvent(_onActiveSongChanged, [], false);
}
}
//? Attaches a handler when the player state changes
//@ ignoreReturnValue
export function on_player_state_changed(changed : Action) : EventBinding {
if (_rt && !_onPlayerStateChanged) {
_onPlayerStateChanged = new Event_();
Player.addPlayerStateChangedEvent(_rt);
}
return _onPlayerStateChanged.addHandler(changed);
}
function raisePlayerStateChanged() {
if (_rt) {
if (_rt.eventEnabled("player state changed"))
_rt.queueEvent("player state changed", null, []);
if (_onPlayerStateChanged && _onPlayerStateChanged.handlers)
_rt.queueLocalEvent(_onPlayerStateChanged, [], false);
}
}
function ensureAudio()
{
if (!_audio) {
_audio = document.createElement("audio");
(_audio).crossorigin = "anonymous";
_audio.style.display = 'none';
_audio.onpause = () => {
raisePlayerStateChanged();
};
_audio.onplay = _audio.onpause;
_audio.onplaying = _audio.onpause;
_audio.onended = () => {
raisePlayerStateChanged();
if (_songs) {
if (_shuffled) {
var i = (Math_.random(_songs.count() - 1) + 1);
Player.playOne(_songs.at(i));
}
else
Player.next();
}
};
_source = document.createElement("source");
_audio.appendChild(_source);
document.body.appendChild(_audio);
}
}
//? Moves to the next song in the queue of playing songs
//@ cap(musicandsounds)
//@ writesMutable
export function next(): void
{
if (_songs) {
var i = _songs.indexOf(_activeSong);
i++;
if (i < _songs.count()) {
Player.playOne(_songs.at(i));
}
}
}
//? Moves to the previous song in the queue of playing songs
//@ cap(musicandsounds)
//@ writesMutable
export function previous(): void
{
if (_songs) {
var i = _songs.indexOf(_activeSong);
i--;
if (i > -1) {
Player.playOne(_songs.at(i));
}
}
}
//? Pauses the currently playing song
//@ cap(musicandsounds)
//@ writesMutable
export function pause(): void
{
if (_audio) {
_audio.pause();
}
}
export function playOne(song: Song)
{
if (_activeSong != song) {
ensureAudio();
_activeSong = song;
var url = song.url();
_source.src = url;
if (RuntimeSettings.sounds()) {
HTML.showProgressNotification(lf("playing song..."));
_audio.play();
}
raiseActiveSongChanged();
}
}
//? Plays a Song
//@ cap(musicandsounds)
//@ writesMutable [song].readsMutable [song].writesMutable
export function play(song: Song)
{
_songs = undefined; // clear song list
Player.playOne(song);
}
//? Plays a collection of songs
//@ cap(musicandsounds)
//@ writesMutable
//@ embedsLink("Player", "Songs")
export function play_many(songs: Songs): void
{
if (songs.count() == 0) {
_songs = undefined;
Player.stop();
}
else {
_songs = songs;
_activeSong = undefined;
Player.playOne(_songs.at(0));
}
}
//? Gets the position in seconds whithin the active song
//@ returns(number)
//@ readsMutable quickAsync
export function play_position(r: ResumeCtx) /*: number*/
{
r.resumeVal(_audio ? _audio.currentTime : undefined);
}
//? Resumes a paused song
//@ cap(musicandsounds)
//@ writesMutable
export function resume(): void {
if (_audio && RuntimeSettings.sounds()) {
_audio.play();
}
}
//? Stops playing a song
//@ cap(musicandsounds)
//@ writesMutable
export function stop(): void
{
if (_audio) {
_source.src = null;
_audio.load();
}
}
//? Indicates if the player is muted
//@ readsMutable returns(boolean) quickAsync
export function is_muted(r : ResumeCtx) // : boolean
{
r.resumeVal(_audio ? _audio.muted : false);
}
//? Gets the sound volume for sounds from 0 (silent) to 1 (current volume)
//@ stub cap(media)
//@ readsMutable
export function sound_volume() : number
{ return undefined; }
//? Sets the sound volume level from 0 (silent) to 1 (current volume)
//@ stub cap(media)
export function set_sound_volume(x:number) : void
{ }
//? Indicates if the player is repeating
//@ readsMutable returns(boolean) quickAsync
export function is_repeating(r : ResumeCtx) //: boolean
{
r.resumeVal(_audio ? _audio.loop : false);
}
//? Sets the repeating on and off
//@ writesMutable quickAsync
//@ [repeating].defl(true)
export function set_repeating(repeating:boolean, r : ResumeCtx) : void
{
ensureAudio();
_audio.loop = repeating;
r.resume();
}
//? Indicates if the player is shuffled
//@ readsMutable returns(boolean) quickAsync
export function is_shuffled(r : ResumeCtx) // : boolean
{
r.resumeVal(_shuffled);
}
//? Sets the shuffling on and off
//@ writesMutable quickAsync
//@ [shuffled].defl(true)
export function set_shuffled(shuffled: boolean, r : ResumeCtx): void
{
_shuffled = shuffled;
r.resume();
}
//? Indicates if the player is stopped
//@ readsMutable returns(boolean) quickAsync
export function is_stopped(r : ResumeCtx) // : boolean
{
r.resumeVal(_audio ? _audio.ended : true);
}
//? Indicates if the player is playing a song
//@ readsMutable returns(boolean) quickAsync
export function is_playing(r : ResumeCtx) // : boolean
{
r.resumeVal(_audio ? (!_audio.ended && !_audio.paused) : false);
}
//? Indicates if the player is paused
//@ readsMutable returns(boolean) quickAsync
export function is_paused(r : ResumeCtx) //: boolean
{
r.resumeVal(_audio ? _audio.paused : false);
}
//? Plays an audio/video file from the home network
//@ writesMutable cap(home)
export function play_home_media(media: MediaLink): void {
switch (media.kind()) {
case 'song':
var s = Song.mk(media.url(), 'media', media.title());
Player.play(s);
break;
default:
Web.play_media(media.url());
break;
}
}
//? Volume is no longer supported.
//@ obsolete
//@ readsMutable
export function volume(): number { return 0; }
}
}