2018-09-02 19:22:28 +00:00
"use strict" ;
2018-12-02 15:07:43 +00:00
var Uint64 = require ( "../wType/uint64" ) ;
var Address = require ( "../wType/address" ) ;
2018-09-02 19:22:28 +00:00
var Controller = require ( "./controller" ) ;
var Button = require ( "./button" ) ;
2018-12-02 15:07:43 +00:00
var ImageById = require ( "./imageById" ) ;
2018-09-02 19:22:28 +00:00
var Vocabulary = require ( "./vocabulary" ) ;
2018-12-17 17:15:58 +00:00
var Audio = require ( "./file/audio" ) ;
2018-09-02 19:22:28 +00:00
var Enum = require ( "../utils/enum" ) ;
2018-12-17 17:15:58 +00:00
var StateMachine = require ( "../utils/stateMachine" ) ;
2018-09-02 19:22:28 +00:00
var Player = Controller . inherit ( {
className : "Player" ,
constructor : function ( addr ) {
Controller . fn . constructor . call ( this , addr ) ;
this . controls = Object . create ( null ) ;
this . views = Object . create ( null ) ;
2018-12-02 15:07:43 +00:00
this . mode = PlayerMode . straight . playback ;
2018-12-17 17:15:58 +00:00
this . _audio = null ;
this . _sound = new window . Audio ( ) ;
this . _ctx = new AudioContext ( ) ;
this . _currentTime = 0 ;
this . _createStateMachine ( ) ;
this . _proxySchedule = this . _schedule . bind ( this ) ;
2018-09-02 19:22:28 +00:00
this . addHandler ( "get" ) ;
this . addHandler ( "viewsChange" ) ;
2018-12-17 17:15:58 +00:00
this . addHandler ( "play" ) ;
this . addHandler ( "pause" ) ;
} ,
destructor : function ( ) {
this . _fsm . destructor ( ) ;
this . _sound . pause ( ) ;
this . _ctx . close ( ) ;
Controller . fn . destructor . call ( this ) ;
2018-09-02 19:22:28 +00:00
} ,
_addControl : function ( type , address ) {
var t = type . valueOf ( ) ;
if ( this . controls [ t ] !== undefined ) {
throw new Error ( "An attempt to add multiple instances of " + ItemType . reversed [ t ] + " into Player" ) ;
}
if ( ItemType . reversed [ t ] !== undefined ) {
switch ( t ) {
case ItemType . straight . playPause :
var btn = new Button ( address . clone ( ) ) ;
btn . itemType = t ;
this . controls [ t ] = btn ;
this . addController ( btn ) ;
2018-10-28 21:32:44 +00:00
this . trigger ( "newElement" , btn , t ) ;
2018-09-02 19:22:28 +00:00
break ;
default :
this . trigger ( "serviceMessage" , "An attempt to add ItemType " + ItemType . reversed [ t ] + " to controls of the Player, but it's not qualified to be a control" ) ;
}
} else {
this . trigger ( "serviceMessage" , "An unrecgnized item ItemType in Player: " + t ) ;
}
} ,
_addView : function ( type , address ) {
var t = type . valueOf ( ) ;
2018-12-02 15:07:43 +00:00
var ctrl ;
var supported = false ;
2018-09-02 19:22:28 +00:00
if ( this . views [ t ] !== undefined ) {
throw new Error ( "An attempt to add multiple instances of " + ItemType . reversed [ t ] + " into Player" ) ;
}
if ( ItemType . reversed [ t ] !== undefined ) {
switch ( t ) {
case ItemType . straight . queue :
this . trigger ( "serviceMessage" , "Queue is not supported yet in Player" ) ;
break ;
case ItemType . straight . currentPlayback :
2018-12-02 15:07:43 +00:00
ctrl = new Vocabulary ( address . clone ( ) ) ;
ctrl . on ( "newElement" , this . _onNewPlayBackElement , this ) ;
ctrl . on ( "removeElement" , this . _onNewRemoveBackElement , this ) ;
supported = true ;
break ;
case ItemType . straight . picture :
ctrl = new ImageById ( null , address . back ( ) ) ;
ctrl . ItemType = t ;
this . views [ t ] = ctrl ;
this . trigger ( "newElement" , ctrl , t ) ;
supported = false ; //just to avoid adding with addController, since ImageById is not a controller
2018-10-28 21:32:44 +00:00
break ;
2018-09-02 19:22:28 +00:00
default :
this . trigger ( "serviceMessage" , "An attempt to add ItemType " + ItemType . reversed [ t ] + " to views of the Player, but it's not qualified to be a view" ) ;
}
} else {
this . trigger ( "serviceMessage" , "An unrecgnized item ItemType in Player: " + t ) ;
}
2018-12-02 15:07:43 +00:00
if ( supported ) {
ctrl . ItemType = t ;
this . views [ t ] = ctrl ;
this . addController ( ctrl ) ;
this . trigger ( "newElement" , ctrl , t ) ;
}
2018-09-02 19:22:28 +00:00
} ,
2018-12-17 17:15:58 +00:00
_createStateMachine : function ( ) {
this . _fsm = new StateMachine ( "initial" , graphs [ this . mode ] ) ;
this . _fsm . on ( "stateChanged" , this . _onStateChanged , this ) ;
} ,
2018-09-02 19:22:28 +00:00
_h _get : function ( ev ) {
var data = ev . getData ( ) ;
var controls = data . at ( "controls" ) ;
var views = data . at ( "views" ) ;
2018-12-02 15:07:43 +00:00
var mode = data . at ( "mode" ) . valueOf ( ) ;
2018-09-02 19:22:28 +00:00
var size , i , vc ;
size = controls . length ( ) ;
for ( i = 0 ; i < size ; ++ i ) {
vc = controls . at ( i ) ;
this . _addControl ( vc . at ( "type" ) , vc . at ( "address" ) ) ;
}
size = views . length ( ) ;
for ( i = 0 ; i < size ; ++ i ) {
vc = views . at ( i ) ;
2018-10-28 21:32:44 +00:00
this . _addView ( vc . at ( "type" ) , vc . at ( "address" ) ) ;
2018-09-02 19:22:28 +00:00
}
2018-10-28 21:32:44 +00:00
2018-12-02 15:07:43 +00:00
if ( this . mode !== mode ) {
if ( PlayerMode . reversed [ mode ] === undefined ) {
throw new Error ( "Unsupported mode of player: " + mode ) ;
}
this . mode = mode ;
}
2018-10-28 21:32:44 +00:00
this . initialized = true ;
this . trigger ( "data" ) ;
2018-09-02 19:22:28 +00:00
} ,
2018-12-17 17:15:58 +00:00
_h _pause : function ( ev ) {
this . _fsm . manipulation ( "plause" ) ;
} ,
_h _play : function ( ev ) {
this . _fsm . manipulation ( "play" ) ;
} ,
2018-09-02 19:22:28 +00:00
_h _viewsChange : function ( ev ) {
var data = ev . getData ( ) ;
var add = data . at ( "add" ) ;
var remove = data . at ( "remove" ) ;
var size , i , vc ;
size = remove . length ( ) ;
for ( i = 0 ; i < size ; ++ i ) {
this . _removeView ( remove . at ( i ) ) ;
}
size = add . length ( ) ;
for ( i = 0 ; i < size ; ++ i ) {
vc = add . at ( i ) ;
2018-10-28 21:32:44 +00:00
this . _addView ( vc . at ( "type" ) , vc . at ( "address" ) ) ;
2018-09-02 19:22:28 +00:00
}
} ,
2018-12-17 17:15:58 +00:00
_onAudioNewFrames : function ( frames ) {
this . _ctx . decodeAudioData ( frames . valueOf ( ) , this . _proxySchedule ) ;
this . _fsm . manipulation ( "newFrames" ) ;
if ( this . _audio . hasMore ( ) ) {
this . _audio . requestMore ( ) ;
} else {
this . _fsm . manipulation ( "noMoreFrames" ) ;
}
} ,
2018-12-02 15:07:43 +00:00
_onNewPlayBackElement : function ( key , element ) {
switch ( key ) {
case "image" :
var address = new Address ( [ "images" , element . toString ( ) ] ) ;
this . _addView ( new Uint64 ( ItemType . straight . picture ) , address ) ;
address . destructor ( ) ;
break ;
2018-12-17 17:15:58 +00:00
case "audio" :
if ( this . mode === PlayerMode . straight . playback ) {
this . _audio = new Audio ( new Address ( [ "music" , element . toString ( ) ] ) ) ;
this . addForeignController ( "Corax" , this . _audio ) ;
this . _audio . on ( "newFrames" , this . _onAudioNewFrames , this ) ;
this . _fsm . manipulation ( "controller" ) ;
}
break ;
2018-12-02 15:07:43 +00:00
}
} ,
_onNewRemoveBackElement : function ( key ) {
switch ( key ) {
case "image" :
this . _removeView ( new Uint64 ( ItemType . straight . picture ) ) ;
break ;
2018-12-17 17:15:58 +00:00
case "audio" :
this . removeForeignController ( this . _audio ) ;
this . _audio . destructor ( ) ;
this . _audio = null ;
}
} ,
_onStateChanged : function ( e ) {
switch ( e . newState ) {
case "initial" :
break ;
case "initialPlaying" :
break ;
case "hasController" :
break ;
case "hasControllerPlaying" :
if ( this . _audio . hasMore ( ) ) {
this . _audio . requestMore ( ) ;
} else {
this . _fsm . manipulation ( "noMoreFrames" ) ;
}
break ;
case "paused" :
switch ( e . oldState ) {
case "playing" :
this . _sound . pause ( ) ;
break ;
}
break ;
case "pausedAllLoaded" :
switch ( e . oldState ) {
case "playingAllLoaded" :
this . _sound . pause ( ) ;
break ;
}
break ;
case "playing" :
this . _sound . play ( ) ;
break ;
case "playingAllLoaded" :
switch ( e . oldState ) {
case "pausedAllLoaded" :
this . _sound . play ( ) ;
break ;
}
break ;
2018-12-02 15:07:43 +00:00
}
} ,
2018-09-02 19:22:28 +00:00
_removeControl : function ( type ) {
//TODO
} ,
_removeView : function ( type ) {
//TODO
2018-12-17 17:15:58 +00:00
} ,
_schedule : function ( buffer ) {
var source = this . _ctx . createBufferSource ( ) ;
source . buffer = buffer ;
source . connect ( this . _ctx . destination ) ;
source . start ( this . _currentTime ) ;
this . _currentTime += buffer . duration ;
2018-09-02 19:22:28 +00:00
}
} ) ;
var ItemType = new Enum ( "ItemType" ) ;
ItemType . add ( "playPause" ) ;
ItemType . add ( "currentPlayback" ) ;
ItemType . add ( "queue" ) ;
2018-12-02 15:07:43 +00:00
ItemType . add ( "picture" ) ;
var PlayerMode = new Enum ( "PlayerMode" ) ;
PlayerMode . add ( "playback" ) ;
2018-09-02 19:22:28 +00:00
Player . ItemType = ItemType ;
2018-12-17 17:15:58 +00:00
var graphs = Object . create ( null ) ;
graphs [ PlayerMode . straight . playback ] = {
"initial" : {
controller : "hasController" ,
play : "initialPlaying"
} ,
"initialPlaying" : {
pause : "initial" ,
controller : "hasControllerPlaying"
} ,
"hasController" : {
newFrames : "paused" ,
play : "hasControllerPlaying"
} ,
"hasControllerPlaying" : {
newFrames : "playing" ,
pause : "hasController"
} ,
"paused" : {
play : "playing" ,
noMoreFrames : "pausedAllLoaded"
} ,
"pausedAllLoaded" : {
play : "playingAllLoaded"
} ,
"playing" : {
pause : "pause" ,
noMoreFrames : "playingAllLoaded"
} ,
"playingAllLoaded" : {
pause : "pausedAllLoaded"
}
}
2018-09-02 19:22:28 +00:00
module . exports = Player ;