buzz.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. "use strict";
  2. /// vwf/model/buzz.js is a sound driver
  3. ///
  4. /// @module vwf/model/buzz
  5. /// @requires vwf/model
  6. define( [
  7. "module",
  8. "vwf/model",
  9. "vwf/utility"
  10. ], function( module, model, utility ) {
  11. var modelDriver;
  12. return model.load( module, {
  13. // == Module Definition ====================================================================
  14. // -- initialize ---------------------------------------------------------------------------
  15. initialize: function( options ) {
  16. modelDriver = this;
  17. this.arguments = Array.prototype.slice.call( arguments );
  18. this.options = options || {};
  19. this.state = {
  20. "nodes": {},
  21. "stages": {},
  22. "prototypes": {},
  23. "createLocalNode": function( nodeID, childID, childExtendsID, childImplementsIDs,
  24. childSource, childType, childIndex, childName, callback ) {
  25. return {
  26. "parentID": nodeID,
  27. "ID": childID,
  28. "extendsID": childExtendsID,
  29. "implementsIDs": childImplementsIDs,
  30. "source": childSource,
  31. "type": childType,
  32. "name": childName,
  33. "prototypes": undefined,
  34. "delayedProperties": undefined,
  35. "soundObj": undefined,
  36. "playState": "unloaded",
  37. "playOnReady": false
  38. };
  39. },
  40. "is3DSoundComponent": function( prototypes ) {
  41. var found = false;
  42. if ( prototypes ) {
  43. for ( var i = 0; i < prototypes.length && !found; i++ ) {
  44. found = ( prototypes[ i ] === "http://vwf.example.com/sound3.vwf" );
  45. }
  46. }
  47. return found;
  48. },
  49. "isSoundComponent": function( prototypes ) {
  50. var found = false;
  51. if ( prototypes ) {
  52. for ( var i = 0; i < prototypes.length && !found; i++ ) {
  53. found = ( prototypes[ i ] === "http://vwf.example.com/sound.vwf" );
  54. }
  55. }
  56. return found;
  57. }
  58. };
  59. // turns on logger debugger console messages
  60. this.debug = {
  61. "creation": false,
  62. "initializing": false,
  63. "parenting": false,
  64. "deleting": false,
  65. "properties": false,
  66. "setting": false,
  67. "getting": false,
  68. "method": false,
  69. "prototypes": false
  70. };
  71. },
  72. creatingNode: function( nodeID, childID, childExtendsID, childImplementsIDs,
  73. childSource, childType, childIndex, childName, callback ) {
  74. var appID = this.kernel.application();
  75. if ( this.debug.creation ) {
  76. this.logger.infox( "creatingNode", nodeID, childID, childExtendsID, childImplementsIDs, childSource, childType, childName );
  77. }
  78. // If the node being created is a prototype, construct it and add it to the array of prototypes,
  79. // and then return
  80. var prototypeID = utility.ifPrototypeGetId( appID, this.state.prototypes, nodeID, childID );
  81. if ( prototypeID !== undefined ) {
  82. if ( this.debug.prototypes ) {
  83. this.logger.infox( "prototype: ", prototypeID );
  84. }
  85. this.state.prototypes[ prototypeID ] = {
  86. parentID: nodeID,
  87. ID: childID,
  88. extendsID: childExtendsID,
  89. implementsID: childImplementsIDs,
  90. source: childSource,
  91. type: childType,
  92. name: childName
  93. };
  94. return;
  95. }
  96. var protos = getPrototypes( this.kernel, childExtendsID );
  97. var node;
  98. if ( this.state.isSoundComponent( protos ) ) {
  99. // Create the local copy of the node properties
  100. if ( this.state.nodes[ childID ] === undefined ){
  101. this.state.nodes[ childID ] = this.state.createLocalNode( nodeID, childID,
  102. childExtendsID, childImplementsIDs, childSource,
  103. childType, childIndex, childName, callback );
  104. }
  105. node = this.state.nodes[ childID ];
  106. node.prototypes = protos;
  107. node.is3DSound = this.state.is3DSoundComponent( protos );
  108. }
  109. },
  110. initializingNode: function( nodeID, childID, childExtendsID, childImplementsIDs,
  111. childSource, childType, childIndex, childName ) {
  112. if ( this.debug.initializing ) {
  113. this.logger.infox( "initializingNode", nodeID, childID, childExtendsID, childImplementsIDs, childSource, childType, childName );
  114. }
  115. var node = this.state.nodes[ childID ];
  116. if ( node !== undefined ) {
  117. if ( utility.validObject( childSource ) ) {
  118. createSound( node, childSource );
  119. }
  120. }
  121. },
  122. deletingNode: function( nodeID ) {
  123. if ( this.debug.deleting ) {
  124. this.logger.infox( "deletingNode", nodeID );
  125. }
  126. if ( this.state.nodes[ nodeID ] ) {
  127. delete this.state.nodes[ nodeID ];
  128. }
  129. },
  130. // addingChild: function( nodeID, childID, childName ) {
  131. // if ( this.debug.parenting ) {
  132. // this.logger.infox( "addingChild", nodeID, childID, childName );
  133. // }
  134. // },
  135. // movingChild: function( nodeID, childID, childName ) {
  136. // if ( this.debug.parenting ) {
  137. // this.logger.infox( "movingChild", nodeID, childID, childName );
  138. // }
  139. // },
  140. // removingChild: function( nodeID, childID, childName ) {
  141. // if ( this.debug.parenting ) {
  142. // this.logger.infox( "removingChild", nodeID, childID, childName );
  143. // }
  144. // },
  145. // -- creatingProperty ---------------------------------------------------------------------
  146. creatingProperty: function( nodeID, propertyName, propertyValue ) {
  147. if ( this.debug.properties ) {
  148. this.logger.infox( "C === creatingProperty ", nodeID, propertyName, propertyValue );
  149. }
  150. return this.settingProperty( nodeID, propertyName, propertyValue );
  151. },
  152. // -- initializingProperty -----------------------------------------------------------------
  153. initializingProperty: function( nodeID, propertyName, propertyValue ) {
  154. if ( this.debug.properties ) {
  155. this.logger.infox( " I === initializingProperty ", nodeID, propertyName, propertyValue );
  156. }
  157. return this.settingProperty( nodeID, propertyName, propertyValue );
  158. },
  159. // -- settingProperty ----------------------------------------------------------------------
  160. settingProperty: function( nodeID, propertyName, propertyValue ) {
  161. if ( this.debug.properties || this.debug.setting ) {
  162. this.logger.infox( " S === settingProperty ", nodeID, propertyName, propertyValue );
  163. }
  164. var node = this.state.nodes[ nodeID ]; // { name: childName, glgeObject: undefined }
  165. var value = undefined;
  166. //this driver has no representation of this node, so there is nothing to do.
  167. if( node === undefined ) return;
  168. if ( propertyName !== "url" && node.soundObj === undefined ) {
  169. if ( node.delayedProperties === undefined ) {
  170. node.delayedProperties = {};
  171. }
  172. node.delayedProperties[ propertyName ] = propertyValue;
  173. } else {
  174. value = propertyValue;
  175. switch ( propertyName ) {
  176. case "url":
  177. if ( node.soundObj !== undefined ) {
  178. node.soundObj.set( "src", propertyValue );
  179. } else {
  180. createSound( node, propertyValue )
  181. }
  182. break;
  183. case "time":
  184. if ( node.soundObj !== undefined ) {
  185. node.soundObj.setTime( parseFloat( propertyValue ) );
  186. }
  187. break;
  188. case "percent":
  189. if ( node.soundObj !== undefined ) {
  190. node.soundObj.setPercent( parseFloat( propertyValue ) );
  191. }
  192. break;
  193. case "speed":
  194. if ( node.soundObj !== undefined ) {
  195. node.soundObj.setSpeed( parseFloat( propertyValue ) );
  196. }
  197. break;
  198. case "muted":
  199. if ( node.soundObj !== undefined ) {
  200. if ( Boolean( propertyValue ) ) {
  201. node.soundObj.mute();
  202. } else {
  203. node.soundObj.unmute();
  204. }
  205. }
  206. break;
  207. case "volume":
  208. if ( node.soundObj !== undefined ) {
  209. var newVolume = Number( propertyValue );
  210. if ( newVolume >= 0 && newVolume <= 100 ) {
  211. node.soundObj.setVolume( newVolume );
  212. }
  213. }
  214. break;
  215. case "loop":
  216. if ( node.soundObj !== undefined ) {
  217. if ( Boolean( propertyValue ) ) {
  218. node.soundObj.loop();
  219. } else {
  220. node.soundObj.unloop();
  221. }
  222. }
  223. break;
  224. default:
  225. value = undefined;
  226. break;
  227. }
  228. }
  229. return value;
  230. },
  231. // -- gettingProperty ----------------------------------------------------------------------
  232. gettingProperty: function( nodeID, propertyName ) {
  233. if ( this.debug.properties || this.debug.getting ) {
  234. this.logger.infox( " G === gettingProperty ", nodeID, propertyName );
  235. }
  236. var node = this.state.nodes[ nodeID ]; // { name: childName, glgeObject: undefined }
  237. var value = undefined;
  238. //this driver has no representation of this node, so there is nothing to do.
  239. if( node === undefined || node.soundObj === undefined ) return;
  240. switch ( propertyName ) {
  241. case "url":
  242. value = node.soundObj.get( "src" );
  243. break;
  244. case "playState":
  245. value = node.playState;
  246. break;
  247. case "time":
  248. value = node.soundObj.getTime();
  249. break;
  250. case "percent":
  251. value = node.soundObj.getPercent();
  252. break;
  253. case "speed":
  254. value = node.soundObj.getSpeed();
  255. break;
  256. case "muted":
  257. value = node.soundObj.isMuted();
  258. break;
  259. case "volume":
  260. value = node.soundObj.getVolume();
  261. break;
  262. case "loop":
  263. value = node.soundObj.get( "loop" );
  264. break;
  265. }
  266. return value;
  267. },
  268. // TODO: deletingMethod
  269. // -- callingMethod --------------------------------------------------------------------------
  270. callingMethod: function( nodeID, methodName, methodParameters ) {
  271. if ( this.debug.method ) {
  272. this.logger.infox( " G === gettingProperty ", nodeID, propertyName );
  273. }
  274. var node = this.state.nodes[ nodeID ];
  275. var value = undefined;
  276. if ( node !== undefined && node.soundObj !== undefined ) {
  277. if ( node.playState !== "unloaded" ) {
  278. switch( methodName ) {
  279. case "play":
  280. if ( node.soundObj !== undefined ) {
  281. node.soundObj.play();
  282. }
  283. break;
  284. case "stop":
  285. if ( node.soundObj !== undefined ) {
  286. node.soundObj.stop();
  287. }
  288. break;
  289. case "pause":
  290. if ( node.soundObj !== undefined ) {
  291. node.soundObj.pause();
  292. }
  293. break;
  294. }
  295. } else {
  296. node.playOnReady = ( methodName === 'play' );
  297. }
  298. }
  299. },
  300. // TODO: creatingEvent, deltetingEvent, firingEvent
  301. // -- executing ------------------------------------------------------------------------------
  302. // executing: function( nodeID, scriptText, scriptType ) {
  303. // return undefined;
  304. // },
  305. // == ticking =============================================================================
  306. // ticking: function( vwfTime ) {
  307. // }
  308. } );
  309. function getPrototypes( kernel, extendsID ) {
  310. var prototypes = [];
  311. var id = extendsID;
  312. while ( id !== undefined ) {
  313. prototypes.push( id );
  314. id = kernel.prototype( id );
  315. }
  316. return prototypes;
  317. }
  318. function createSound( node, url ) {
  319. var soundProps;
  320. if ( url.indexOf( 'data:audio' ) === 0 ) {
  321. soundProps = {
  322. "preload": "metadata"
  323. };
  324. } else if ( require( "vwf/utility" ).hasFileType( url ) ) {
  325. var fType = require( "vwf/utility" ).fileType( url );
  326. soundProps= {
  327. "preload": "metadata",
  328. "formats": [ fType ]
  329. };
  330. } else {
  331. soundProps= {
  332. "preload": "metadata",
  333. "formats": [ "ogg", "mp3", "aac", "wav" ]
  334. };
  335. }
  336. if ( node.delayedProperties !== undefined ) {
  337. for ( var prop in node.delayedProperties ) {
  338. switch ( prop ) {
  339. default:
  340. soundProps[ prop ] = node.delayedProperties[ prop ];
  341. break;
  342. }
  343. }
  344. node.delayedProperties = undefined;
  345. }
  346. var buzz = require( "vwf/model/buzz/buzz.min" );
  347. node.soundObj = new buzz.sound( url, soundProps );
  348. // http://buzz.jaysalvat.com/documentation/events/
  349. node.soundObj.bind( "ended", function( e ) {
  350. if ( node.playState === "playing" ) {
  351. node.playState = "stopped";
  352. }
  353. modelDriver.kernel.fireEvent( node.ID, "soundEnded", [ true ] );
  354. } );
  355. node.soundObj.bind( "playing", function( e ) {
  356. node.playState = "playing";
  357. modelDriver.kernel.fireEvent( node.ID, "soundPlaying", [ true ] );
  358. } );
  359. node.soundObj.bind( "canplay", function( e ) {
  360. node.playState = "loaded";
  361. if ( node.playOnReady ) {
  362. node.soundObj.play();
  363. node.playOnReady = false;
  364. }
  365. modelDriver.kernel.fireEvent( node.ID, "soundReady", [ true ] );
  366. } );
  367. // "play" is sent when the sound had been paused
  368. node.soundObj.bind( "play", function( e ) {
  369. node.playState = "playing";
  370. modelDriver.kernel.fireEvent( node.ID, "soundReady", [ true ] );
  371. } );
  372. node.soundObj.bind( "pause", function( e ) {
  373. node.playState = "paused";
  374. modelDriver.kernel.fireEvent( node.ID, "soundReady", [ true ] );
  375. } );
  376. }
  377. } );