diff --git a/Phaser.sublime-workspace b/Phaser.sublime-workspace index 6161cc60..5c8d6953 100644 --- a/Phaser.sublime-workspace +++ b/Phaser.sublime-workspace @@ -3,6 +3,70 @@ { "selected_items": [ + [ + "bot", + "bottomLeft" + ], + [ + "be", + "beginPath" + ], + [ + "show", + "showBounds" + ], + [ + "cam", + "cameraVisible" + ], + [ + "fr", + "frameHeight (variable)" + ], + [ + "bott", + "bottomRight (variable)" + ], + [ + "up", + "updateBounds" + ], + [ + "top", + "topRight" + ], + [ + "bottom", + "bottomLeft" + ], + [ + "renderp", + "renderPoint" + ], + [ + "ren", + "renderRectangle (function)" + ], + [ + "sc", + "scaleY" + ], + [ + "scal", + "scaleX" + ], + [ + "frame", + "frameIndex" + ], + [ + "_p", + "_py" + ], + [ + "upda", + "updateOnVisible" + ], [ "scale", "scale1Y" @@ -23,10 +87,6 @@ "displ", "displayObject" ], - [ - "bott", - "bottomRight" - ], [ "off", "offset" @@ -175,10 +235,6 @@ "onpause", "onPausedCallback" ], - [ - "upda", - "updateSetTimeout" - ], [ "inter", "intersectsRaw" @@ -395,18 +451,10 @@ "let", "letter1" ], - [ - "sc", - "scanIt" - ], [ "pro", "progressFill" ], - [ - "up", - "update" - ], [ "deli", "delivery1" @@ -466,100 +514,11 @@ [ "scr", "scrollUp" - ], - [ - "shad", - "shadowFloatY" - ], - [ - "pau", - "pauseScreen" - ], - [ - "ne", - "nextRound" - ], - [ - "sha", - "shadow" - ], - [ - "flo", - "floatOff" - ], - [ - "pip", - "piplin1" - ], - [ - "tw", - "tween" - ], - [ - "rou", - "round" - ], - [ - "time", - "timeToShine" - ], - [ - "shado", - "shadowY" - ], - [ - "shi", - "shine" - ], - [ - "shin", - "shineY" ] ] }, "buffers": [ - { - "file": "src/gameobjects/Sprite.js", - "settings": - { - "buffer_size": 9442, - "line_ending": "Windows" - } - }, - { - "file": "src/core/World.js", - "settings": - { - "buffer_size": 2868, - "line_ending": "Windows" - } - }, - { - "file": "TS Source/math/QuadTree.js", - "settings": - { - "buffer_size": 18663, - "line_ending": "Windows" - } - }, - { - "file": "src/math/QuadTree.js", - "settings": - { - "buffer_size": 1014, - "line_ending": "Windows", - "name": "Phaser.QuadTree = function () {" - } - }, - { - "file": "src/pixi/display/Sprite.js", - "settings": - { - "buffer_size": 4817, - "line_ending": "Windows" - } - } ], "build_system": "", "command_palette": @@ -589,23 +548,29 @@ }, "file_history": [ + "/D/wamp/www/phaser/src/math/QuadTree.js", + "/D/wamp/www/phaser/TS Source/math/QuadTree.js", + "/D/wamp/www/phaser/src/animation/AnimationManager.js", + "/D/wamp/www/phaser/src/gameobjects/Sprite.js", + "/D/wamp/www/phaser/src/utils/Debug.js", + "/D/wamp/www/phaser/src/core/World.js", + "/D/wamp/www/phaser/src/pixi/display/Stage.js", + "/D/wamp/www/phaser/examples/camera_cull1.php", + "/D/wamp/www/phaser/src/pixi/renderers/canvas/CanvasRenderer.js", + "/D/wamp/www/phaser/src/pixi/display/DisplayObjectContainer.js", + "/D/wamp/www/phaser/src/pixi/display/DisplayObject.js", + "/D/wamp/www/phaser/src/pixi/display/Sprite.js", + "/D/wamp/www/bbc-wolfblood/assets/1024/help/help_titles.json", "/D/wamp/www/phaser/src/math/LinkedList.js", "/D/wamp/www/phaser/TS Source/math/LinkedList.js", - "/D/wamp/www/phaser/src/pixi/display/DisplayObject.js", - "/D/wamp/www/phaser/src/pixi/display/DisplayObjectContainer.js", - "/D/wamp/www/phaser/src/pixi/display/Sprite.js", - "/D/wamp/www/phaser/src/animation/AnimationManager.js", "/D/wamp/www/phaser/src/animation/Frame.js", "/D/wamp/www/phaser/src/animation/Parser.js", "/D/wamp/www/phaser/examples/camera4.php", "/D/wamp/www/phaser/src/core/Game.js", "/D/wamp/www/phaser/examples/sprite3.php", "/D/wamp/www/phaser/examples/camera5.php", - "/D/wamp/www/phaser/src/utils/Debug.js", "/D/wamp/www/phaser/src/pixi/InteractionManager.js", "/D/wamp/www/pixi.js/examples/example 6 - Interactivity/index.html", - "/D/wamp/www/phaser/src/pixi/renderers/canvas/CanvasRenderer.js", - "/D/wamp/www/phaser/src/core/World.js", "/D/wamp/www/phaser/src/math/Math.js", "/D/wamp/www/phaser/src/geom/Point.js", "/D/wamp/www/phaser/src/input/Input.js", @@ -629,7 +594,6 @@ "/D/wamp/www/phaser/TS Source/input/Pointer.js", "/D/wamp/www/phaser/TS Source/input/Touch.js", "/D/wamp/www/phaser/TS Source/input/MSPointer.js", - "/D/wamp/www/phaser/src/gameobjects/Sprite.js", "/D/wamp/www/phaser/TS Source/cameras/Camera.js", "/D/wamp/www/phaser/examples/camera1.php", "/D/wamp/www/phaser/TS Source/input/Mouse.js", @@ -677,7 +641,6 @@ "/D/wamp/www/phaser/TS Source/World.js", "/D/wamp/www/phaser/src/Game.js", "/D/wamp/www/phaser/src/Stage.js", - "/D/wamp/www/phaser/src/pixi/display/Stage.js", "/D/wamp/www/phaser/examples/pixi 1.html", "/D/wamp/www/phaser/src/system/Canvas.js", "/D/wamp/www/phaser/src/Phaser.js", @@ -712,11 +675,7 @@ "/D/wamp/www/phaser/src/time/Time.js", "/D/wamp/www/phaser/src/pixi/utils/Utils.js", "/D/wamp/www/phaser/TS Source/system/RequestAnimationFrame.ts", - "/D/wamp/www/phaser/todo/Tween.js", - "/D/wamp/www/phaser/TS Source/tweens/TweenManager.js", - "/D/wamp/www/phaser/TS Source/utils/RectangleUtils.js", - "/D/wamp/www/phaser/TS Source/geom/Rectangle.js", - "/D/wamp/www/phaser/examples/point.html" + "/D/wamp/www/phaser/todo/Tween.js" ], "find": { @@ -724,7 +683,7 @@ }, "find_in_files": { - "height": 0.0, + "height": 90.0, "where_history": [ "D:\\wamp\\www\\phaser\\src\\pixi", @@ -752,6 +711,25 @@ "case_sensitive": false, "find_history": [ + "game.camera", + "\n ", + "moveTo", + "showText", + "sh", + "sw", + "this._dirty", + "X", + "x", + "X", + "x", + "this._", + "this._y", + "this._x", + "x", + "updateOnVisible", + "updateTransform", + "worldTransform", + "world", "s.", "actual", "worldTransform", @@ -860,26 +838,7 @@ "public ", "startSt", "_this", - "Phaser.Math", - "_raf", - "raf", - "delta", - "_raf", - "RequestAnimationFrame.prototype.", - "TweenManager.prototype.", - "Phaser.Rectangle.", - "a.", - "RectangleUtils", - "defineProperty(Rectangle.prototype", - "});", - "x", - "Rectangle.prototype.", - "this.android", - "dest", - "this", - "a", - "*", - "Point.prototype." + "Phaser.Math" ], "highlight": true, "in_selection": false, @@ -887,6 +846,9 @@ "regex": false, "replace_history": [ + "this._cache.dirty", + "this.y", + "this.x", "Phaser.MSPointer", "Phaser.Pointer", "Phaser.Touch", @@ -948,660 +910,8 @@ "groups": [ { - "selected": 2, "sheets": [ - { - "buffer": 0, - "file": "src/gameobjects/Sprite.js", - "settings": - { - "buffer_size": 9442, - "regions": - { - }, - "selection": - [ - [ - 0, - 0 - ] - ], - "settings": - { - "codeintel": true, - "codeintel_config": - { - "JavaScript": - { - "codeintel_max_recursive_dir_depth": 2, - "codeintel_scan_files_in_project": false, - "javascriptExtraPaths": - [ - ] - }, - "PHP": - { - "codeintel_max_recursive_dir_depth": 5, - "codeintel_scan_files_in_project": false, - "phpExtraPaths": - [ - ] - }, - "Python": - { - "env": - { - } - } - }, - "codeintel_enabled_languages": - [ - "JavaScript", - "Mason", - "XBL", - "XUL", - "RHTML", - "SCSS", - "Python", - "HTML", - "Ruby", - "Python3", - "XML", - "Sass", - "XSLT", - "Django", - "HTML5", - "Perl", - "CSS", - "Twig", - "Less", - "Smarty", - "Node.js", - "Tcl", - "TemplateToolkit", - "PHP" - ], - "codeintel_live": true, - "codeintel_live_enabled_languages": - [ - "JavaScript", - "Mason", - "XBL", - "XUL", - "RHTML", - "SCSS", - "Python", - "HTML", - "Ruby", - "Python3", - "XML", - "Sass", - "XSLT", - "Django", - "HTML5", - "Perl", - "CSS", - "Twig", - "Less", - "Smarty", - "Node.js", - "Tcl", - "TemplateToolkit", - "PHP" - ], - "codeintel_max_recursive_dir_depth": 10, - "codeintel_scan_exclude_dir": - { - "JavaScript": - [ - "/build/", - "/min/" - ] - }, - "codeintel_scan_files_in_project": true, - "codeintel_selected_catalogs": - [ - "PyWin32", - "jQuery", - "Rails" - ], - "codeintel_snippets": true, - "codeintel_syntax_map": - { - "Python Django": "Python" - }, - "sublime_auto_complete": true, - "syntax": "Packages/JavaScript/JavaScript.tmLanguage", - "tab_size": 4, - "translate_tabs_to_spaces": true - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "type": "text" - }, - { - "buffer": 1, - "file": "src/core/World.js", - "settings": - { - "buffer_size": 2868, - "regions": - { - }, - "selection": - [ - [ - 149, - 149 - ] - ], - "settings": - { - "codeintel": true, - "codeintel_config": - { - "JavaScript": - { - "codeintel_max_recursive_dir_depth": 2, - "codeintel_scan_files_in_project": false, - "javascriptExtraPaths": - [ - ] - }, - "PHP": - { - "codeintel_max_recursive_dir_depth": 5, - "codeintel_scan_files_in_project": false, - "phpExtraPaths": - [ - ] - }, - "Python": - { - "env": - { - } - } - }, - "codeintel_enabled_languages": - [ - "JavaScript", - "Mason", - "XBL", - "XUL", - "RHTML", - "SCSS", - "Python", - "HTML", - "Ruby", - "Python3", - "XML", - "Sass", - "XSLT", - "Django", - "HTML5", - "Perl", - "CSS", - "Twig", - "Less", - "Smarty", - "Node.js", - "Tcl", - "TemplateToolkit", - "PHP" - ], - "codeintel_live": true, - "codeintel_live_enabled_languages": - [ - "JavaScript", - "Mason", - "XBL", - "XUL", - "RHTML", - "SCSS", - "Python", - "HTML", - "Ruby", - "Python3", - "XML", - "Sass", - "XSLT", - "Django", - "HTML5", - "Perl", - "CSS", - "Twig", - "Less", - "Smarty", - "Node.js", - "Tcl", - "TemplateToolkit", - "PHP" - ], - "codeintel_max_recursive_dir_depth": 10, - "codeintel_scan_exclude_dir": - { - "JavaScript": - [ - "/build/", - "/min/" - ] - }, - "codeintel_scan_files_in_project": true, - "codeintel_selected_catalogs": - [ - "PyWin32", - "jQuery", - "Rails" - ], - "codeintel_snippets": true, - "codeintel_syntax_map": - { - "Python Django": "Python" - }, - "sublime_auto_complete": true, - "syntax": "Packages/JavaScript/JavaScript.tmLanguage" - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "type": "text" - }, - { - "buffer": 2, - "file": "TS Source/math/QuadTree.js", - "settings": - { - "buffer_size": 18663, - "regions": - { - }, - "selection": - [ - [ - 1291, - 1583 - ] - ], - "settings": - { - "codeintel": true, - "codeintel_config": - { - "JavaScript": - { - "codeintel_max_recursive_dir_depth": 2, - "codeintel_scan_files_in_project": false, - "javascriptExtraPaths": - [ - ] - }, - "PHP": - { - "codeintel_max_recursive_dir_depth": 5, - "codeintel_scan_files_in_project": false, - "phpExtraPaths": - [ - ] - }, - "Python": - { - "env": - { - } - } - }, - "codeintel_enabled_languages": - [ - "JavaScript", - "Mason", - "XBL", - "XUL", - "RHTML", - "SCSS", - "Python", - "HTML", - "Ruby", - "Python3", - "XML", - "Sass", - "XSLT", - "Django", - "HTML5", - "Perl", - "CSS", - "Twig", - "Less", - "Smarty", - "Node.js", - "Tcl", - "TemplateToolkit", - "PHP" - ], - "codeintel_live": true, - "codeintel_live_enabled_languages": - [ - "JavaScript", - "Mason", - "XBL", - "XUL", - "RHTML", - "SCSS", - "Python", - "HTML", - "Ruby", - "Python3", - "XML", - "Sass", - "XSLT", - "Django", - "HTML5", - "Perl", - "CSS", - "Twig", - "Less", - "Smarty", - "Node.js", - "Tcl", - "TemplateToolkit", - "PHP" - ], - "codeintel_max_recursive_dir_depth": 10, - "codeintel_scan_exclude_dir": - { - "JavaScript": - [ - "/build/", - "/min/" - ] - }, - "codeintel_scan_files_in_project": true, - "codeintel_selected_catalogs": - [ - "PyWin32", - "jQuery", - "Rails" - ], - "codeintel_snippets": true, - "codeintel_syntax_map": - { - "Python Django": "Python" - }, - "sublime_auto_complete": true, - "syntax": "Packages/JavaScript/JavaScript.tmLanguage", - "tab_size": 4, - "translate_tabs_to_spaces": true - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "type": "text" - }, - { - "buffer": 3, - "file": "src/math/QuadTree.js", - "settings": - { - "buffer_size": 1014, - "regions": - { - }, - "selection": - [ - [ - 829, - 829 - ] - ], - "settings": - { - "auto_name": "Phaser.QuadTree = function () {", - "codeintel": true, - "codeintel_config": - { - "JavaScript": - { - "codeintel_max_recursive_dir_depth": 2, - "codeintel_scan_files_in_project": false, - "javascriptExtraPaths": - [ - ] - }, - "PHP": - { - "codeintel_max_recursive_dir_depth": 5, - "codeintel_scan_files_in_project": false, - "phpExtraPaths": - [ - ] - }, - "Python": - { - "env": - { - } - } - }, - "codeintel_enabled_languages": - [ - "JavaScript", - "Mason", - "XBL", - "XUL", - "RHTML", - "SCSS", - "Python", - "HTML", - "Ruby", - "Python3", - "XML", - "Sass", - "XSLT", - "Django", - "HTML5", - "Perl", - "CSS", - "Twig", - "Less", - "Smarty", - "Node.js", - "Tcl", - "TemplateToolkit", - "PHP" - ], - "codeintel_live": true, - "codeintel_live_enabled_languages": - [ - "JavaScript", - "Mason", - "XBL", - "XUL", - "RHTML", - "SCSS", - "Python", - "HTML", - "Ruby", - "Python3", - "XML", - "Sass", - "XSLT", - "Django", - "HTML5", - "Perl", - "CSS", - "Twig", - "Less", - "Smarty", - "Node.js", - "Tcl", - "TemplateToolkit", - "PHP" - ], - "codeintel_max_recursive_dir_depth": 10, - "codeintel_scan_exclude_dir": - { - "JavaScript": - [ - "/build/", - "/min/" - ] - }, - "codeintel_scan_files_in_project": true, - "codeintel_selected_catalogs": - [ - "PyWin32", - "jQuery", - "Rails" - ], - "codeintel_snippets": true, - "codeintel_syntax_map": - { - "Python Django": "Python" - }, - "sublime_auto_complete": true, - "syntax": "Packages/JavaScript/JavaScript.tmLanguage" - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "type": "text" - }, - { - "buffer": 4, - "file": "src/pixi/display/Sprite.js", - "settings": - { - "buffer_size": 4817, - "regions": - { - }, - "selection": - [ - [ - 1861, - 1861 - ] - ], - "settings": - { - "codeintel": true, - "codeintel_config": - { - "JavaScript": - { - "codeintel_max_recursive_dir_depth": 2, - "codeintel_scan_files_in_project": false, - "javascriptExtraPaths": - [ - ] - }, - "PHP": - { - "codeintel_max_recursive_dir_depth": 5, - "codeintel_scan_files_in_project": false, - "phpExtraPaths": - [ - ] - }, - "Python": - { - "env": - { - } - } - }, - "codeintel_enabled_languages": - [ - "JavaScript", - "Mason", - "XBL", - "XUL", - "RHTML", - "SCSS", - "Python", - "HTML", - "Ruby", - "Python3", - "XML", - "Sass", - "XSLT", - "Django", - "HTML5", - "Perl", - "CSS", - "Twig", - "Less", - "Smarty", - "Node.js", - "Tcl", - "TemplateToolkit", - "PHP" - ], - "codeintel_live": true, - "codeintel_live_enabled_languages": - [ - "JavaScript", - "Mason", - "XBL", - "XUL", - "RHTML", - "SCSS", - "Python", - "HTML", - "Ruby", - "Python3", - "XML", - "Sass", - "XSLT", - "Django", - "HTML5", - "Perl", - "CSS", - "Twig", - "Less", - "Smarty", - "Node.js", - "Tcl", - "TemplateToolkit", - "PHP" - ], - "codeintel_max_recursive_dir_depth": 10, - "codeintel_scan_exclude_dir": - { - "JavaScript": - [ - "/build/", - "/min/" - ] - }, - "codeintel_scan_files_in_project": true, - "codeintel_selected_catalogs": - [ - "PyWin32", - "jQuery", - "Rails" - ], - "codeintel_snippets": true, - "codeintel_syntax_map": - { - "Python Django": "Python" - }, - "sublime_auto_complete": true, - "syntax": "Packages/JavaScript/JavaScript.tmLanguage", - "translate_tabs_to_spaces": false - }, - "translation.x": 0.0, - "translation.y": 900.0, - "zoom_level": 1.0 - }, - "type": "text" - } ] } ], diff --git a/examples/js.php b/examples/js.php index dfab7fa2..9b85d154 100644 --- a/examples/js.php +++ b/examples/js.php @@ -92,6 +92,9 @@ + + + diff --git a/examples/sound1.php b/examples/sound1.php new file mode 100644 index 00000000..794a9d5d --- /dev/null +++ b/examples/sound1.php @@ -0,0 +1,56 @@ + + + + phaser.js - a new beginning + + + + + + + + \ No newline at end of file diff --git a/src/core/Game.js b/src/core/Game.js index 9f8b9fdb..1f08c8e7 100644 --- a/src/core/Game.js +++ b/src/core/Game.js @@ -295,7 +295,7 @@ Phaser.Game.prototype = { this.time = new Phaser.Time(this); this.tweens = new Phaser.TweenManager(this); this.input = new Phaser.Input(this); - // this.sound = new Phaser.SoundManager(this); + this.sound = new Phaser.SoundManager(this); // this.physics = new Phaser.Physics.PhysicsManager(this); this.plugins = new Phaser.PluginManager(this, this); this.net = new Phaser.Net(this); @@ -379,7 +379,7 @@ Phaser.Game.prototype = { this.input.update(); this.tweens.update(); // this.stage.update(); - // this.sound.update(); + this.sound.update(); // this.physics.update(); this.world.update(); this.state.update(); diff --git a/src/gameobjects/GameObjectFactory.js b/src/gameobjects/GameObjectFactory.js index b58b3342..0e8ee585 100644 --- a/src/gameobjects/GameObjectFactory.js +++ b/src/gameobjects/GameObjectFactory.js @@ -41,6 +41,16 @@ Phaser.GameObjectFactory.prototype = { return this.game.tweens.create(obj, localReference); + }, + + audio: function (key, volume, loop) { + + volume = volume || 1; + loop = loop || false; + + return this.game.sound.add(key, volume, loop); + } + }; \ No newline at end of file diff --git a/src/sound/Sound.js b/src/sound/Sound.js new file mode 100644 index 00000000..3d1cf13e --- /dev/null +++ b/src/sound/Sound.js @@ -0,0 +1,542 @@ +Phaser.Sound = function (game, key, volume, loop) { + + volume = volume || 1; + loop = loop || false; + + this.game = game; + this.key = key; + this._volume = volume; + this.loop = loop; + + this.usingWebAudio = this.game.sound.usingWebAudio; + this.usingAudioTag = this.game.sound.usingAudioTag; + + if (this.usingWebAudio) + { + this.context = this.game.sound.context; + this.masterGainNode = this.game.sound.masterGain; + + if (typeof this.context.createGain === 'undefined') + { + this.gainNode = this.context.createGainNode(); + } + else + { + this.gainNode = this.context.createGain(); + } + + this.gainNode.gain.value = volume * this.game.sound.volume; + this.gainNode.connect(this.masterGainNode); + } + else + { + if (this.game.cache.getSound(key) && this.game.cache.getSound(key).locked == false) + { + this._sound = this.game.cache.getSoundData(key); + this.totalDuration = this._sound.duration; + } + else + { + this.game.cache.onSoundUnlock.add(this.soundHasUnlocked, this); + } + } + + this.onDecoded = new Phaser.Signal; + this.onPlay = new Phaser.Signal; + this.onPause = new Phaser.Signal; + this.onResume = new Phaser.Signal; + this.onLoop = new Phaser.Signal; + this.onStop = new Phaser.Signal; + this.onMute = new Phaser.Signal; + this.onMarkerComplete = new Phaser.Signal; + +}; + +Phaser.Sound.prototype = { + + game: null, + + /** + * Reference to AudioContext instance. + */ + context: null, + + /** + * Decoded data buffer / Audio tag. + */ + _buffer: null, + + _muted: false, + + key: '', + name: '', + markers: {}, + + usingWebAudio: false, + usingAudioTag: false, + autoplay: false, + totalDuration: 0, + startTime: 0, + currentTime: 0, + duration: 0, + stopTime: 0, + paused: false, + loop: false, + isPlaying: false, + currentMarker: '', + pendingPlayback: false, + override: false, + + onDecoded: null, + onPlay: null, + onPause: null, + onResume: null, + onLoop: null, + onStop: null, + onMute: null, + onMarkerComplete: null, + + soundHasUnlocked: function (key) { + + if (key == this.key) + { + this._sound = this.game.cache.getSoundData(this.key); + this.totalDuration = this._sound.duration; + console.log('sound has unlocked', this._sound); + } + + }, + + addMarker: function (name, start, stop, volume, loop) { + + volume = volume || 1; + loop = loop || false; + + this.markers[name] = { + name: name, + start: start, + stop: stop, + volume: volume, + duration: stop - start, + loop: loop + }; + + }, + + removeMarker: function (name) { + + delete this.markers[name]; + + }, + + update: function () { + + if (this.pendingPlayback && this.game.cache.isSoundReady(this.key)) + { + this.pendingPlayback = false; + this.play(this._tempMarker, this._tempPosition, this._tempVolume, this._tempLoop); + } + + if (this.isPlaying) + { + this.currentTime = this.game.time.now - this.startTime; + + if (this.currentTime >= this.duration) + { + //console.log(this.currentMarker, 'has hit duration'); + if (this.usingWebAudio) + { + if (this.loop) + { + //console.log('loop1'); + // won't work with markers, needs to reset the position + this.onLoop.dispatch(this); + + if (this.currentMarker == '') + { + //console.log('loop2'); + this.currentTime = 0; + this.startTime = this.game.time.now; + } + else + { + //console.log('loop3'); + this.play(this.currentMarker, 0, this.volume, true, true); + } + } + else + { + //console.log('stopping, no loop for marker'); + this.stop(); + } + } + else + { + if (this.loop) + { + this.onLoop.dispatch(this); + this.play(this.currentMarker, 0, this.volume, true, true); + } + else + { + this.stop(); + } + } + } + } + }, + + /** + * Play this sound, or a marked section of it. + * @param marker {string} Assets key of the sound you want to play. + * @param [volume] {number} volume of the sound you want to play. + * @param [loop] {bool} loop when it finished playing? (Default to false) + * @return {Sound} The playing sound object. + */ + play: function (marker, position, volume, loop, forceRestart) { + + marker = marker || ''; + position = position || 0; + volume = volume || 1; + loop = loop || false; + forceRestart = forceRestart || false; + + console.log('play', marker, 'current is', this.currentMarker); + + if (this.isPlaying == true && forceRestart == false && this.override == false) + { + // Use Restart instead + return; + } + + if (this.isPlaying && this.override) + { + //console.log('asked to play', marker, 'but already playing', this.currentMarker); + + if (this.usingWebAudio) + { + if (typeof this._sound.stop === 'undefined') + { + this._sound.noteOff(0); + } + else + { + this._sound.stop(0); + } + } + else if (this.usingAudioTag) + { + this._sound.pause(); + this._sound.currentTime = 0; + } + } + + this.currentMarker = marker; + + if (marker !== '' && this.markers[marker]) + { + this.position = this.markers[marker].start; + this.volume = this.markers[marker].volume; + this.loop = this.markers[marker].loop; + this.duration = this.markers[marker].duration * 1000; + + //console.log('marker info loaded', this.loop, this.duration); + this._tempMarker = marker; + this._tempPosition = this.position; + this._tempVolume = this.volume; + this._tempLoop = this.loop; + } + else + { + this.position = position; + this.volume = volume; + this.loop = loop; + this.duration = 0; + + this._tempMarker = marker; + this._tempPosition = position; + this._tempVolume = volume; + this._tempLoop = loop; + } + + if (this.usingWebAudio) + { + // Does the sound need decoding? + if (this.game.cache.isSoundDecoded(this.key)) + { + // Do we need to do this every time we play? How about just if the buffer is empty? + if (this._buffer == null) + { + this._buffer = this.game.cache.getSoundData(this.key); + } + + this._sound = this.context.createBufferSource(); + this._sound.buffer = this._buffer; + this._sound.connect(this.gainNode); + this.totalDuration = this._sound.buffer.duration; + + if (this.duration == 0) + { + this.duration = this.totalDuration * 1000; + } + + if (this.loop && marker == '') + { + this._sound.loop = true; + } + + // Useful to cache this somewhere perhaps? + if (typeof this._sound.start === 'undefined') + { + this._sound.noteGrainOn(0, this.position, this.duration / 1000); + //this._sound.noteOn(0); // the zero is vitally important, crashes iOS6 without it + } + else + { + this._sound.start(0, this.position, this.duration / 1000); + } + + this.isPlaying = true; + this.startTime = this.game.time.now; + this.currentTime = 0; + this.stopTime = this.startTime + this.duration; + this.onPlay.dispatch(this); + + //console.log('playing, start', this.startTime, 'stop'); + } + else + { + this.pendingPlayback = true; + + if (this.game.cache.getSound(this.key) && this.game.cache.getSound(this.key).isDecoding == false) + { + this.game.sound.decode(this.key, this); + } + } + } + else + { + //console.log('Sound play Audio'); + if (this.game.cache.getSound(this.key) && this.game.cache.getSound(this.key).locked) + { + //console.log('tried playing locked sound, pending set, reload started'); + this.game.cache.reloadSound(this.key); + this.pendingPlayback = true; + } + else + { + //console.log('sound not locked, state?', this._sound.readyState); + if (this._sound && this._sound.readyState == 4) + { + if (this.duration == 0) + { + this.duration = this.totalDuration * 1000; + } + + //console.log('playing', this._sound); + this._sound.currentTime = this.position; + this._sound.muted = this._muted; + + if (this._muted) + { + this._sound.volume = 0; + } + else + { + this._sound.volume = this._volume; + } + + this._sound.play(); + this.isPlaying = true; + this.startTime = this.game.time.now; + this.currentTime = 0; + this.stopTime = this.startTime + this.duration; + this.onPlay.dispatch(this); + } + else + { + this.pendingPlayback = true; + } + } + } + }, + + restart: function (marker, position, volume, loop) { + + marker = marker || ''; + position = position || 0; + volume = volume || 1; + loop = loop || false; + + this.play(marker, position, volume, loop, true); + + }, + + pause: function () { + + if (this.isPlaying && this._sound) + { + this.stop(); + this.isPlaying = false; + this.paused = true; + this.onPause.dispatch(this); + } + + }, + + resume: function () { + + if (this.paused && this._sound) + { + if (this.usingWebAudio) + { + if (typeof this._sound.start === 'undefined') + { + this._sound.noteGrainOn(0, this.position, this.duration); + //this._sound.noteOn(0); // the zero is vitally important, crashes iOS6 without it + } + else + { + this._sound.start(0, this.position, this.duration); + } + } + else + { + this._sound.play(); + } + + this.isPlaying = true; + this.paused = false; + this.onResume.dispatch(this); + } + + }, + + /** + * Stop playing this sound. + */ + stop: function () { + + if (this.isPlaying && this._sound) + { + if (this.usingWebAudio) + { + if (typeof this._sound.stop === 'undefined') + { + this._sound.noteOff(0); + } + else + { + this._sound.stop(0); + } + } + else if(this.usingAudioTag) + { + this._sound.pause(); + this._sound.currentTime = 0; + } + } + + this.isPlaying = false; + var prevMarker = this.currentMarker; + + this.currentMarker = ''; + this.onStop.dispatch(this, prevMarker); + + } + +}; + +Object.defineProperty(Phaser.Sound.prototype, "isDecoding", { + + get: function () { + return this.game.cache.getSound(this.key).isDecoding; + }, + + enumerable: true, + configurable: true +}); + +Object.defineProperty(Phaser.Sound.prototype, "isDecoded", { + + get: function () { + return this.game.cache.isSoundDecoded(this.key); + }, + + enumerable: true, + configurable: true +}); + +Object.defineProperty(Phaser.Sound.prototype, "mute", { + + /** + * Mute sounds. + */ + get: function () { + return this._muted; + }, + + set: function (value) { + + value = value || null; + + if (value) + { + this._muted = true; + + if (this.usingWebAudio) + { + this._muteVolume = this.gainNode.gain.value; + this.gainNode.gain.value = 0; + } + else if (this.usingAudioTag && this._sound) + { + this._muteVolume = this._sound.volume; + this._sound.volume = 0; + } + } + else + { + this._muted = false; + + if (this.usingWebAudio) + { + this.gainNode.gain.value = this._muteVolume; + } + else if (this.usingAudioTag && this._sound) + { + this._sound.volume = this._muteVolume; + } + } + + this.onMute.dispatch(this); + + }, + + enumerable: true, + configurable: true +}); + +Object.defineProperty(Phaser.Sound.prototype, "volume", { + + get: function () { + return this._volume; + }, + + set: function (value) { + + this._volume = value; + + if (this.usingWebAudio) + { + this.gainNode.gain.value = value; + } + else if(this.usingAudioTag && this._sound) + { + this._sound.volume = value; + } + }, + + enumerable: true, + configurable: true +}); diff --git a/src/sound/SoundManager.js b/src/sound/SoundManager.js new file mode 100644 index 00000000..ed21d3b3 --- /dev/null +++ b/src/sound/SoundManager.js @@ -0,0 +1,351 @@ +/** +* Phaser - SoundManager +* +*/ +Phaser.SoundManager = function (game) { + + this.game = game; + + this.onSoundDecode = new Phaser.Signal; + + if (this.game.device.iOS && this.game.device.webAudio == false) + { + this.channels = 1; + } + + if (game.device.iOS || (window['PhaserGlobal'] && window['PhaserGlobal'].fakeiOSTouchLock)) + { + this.game.input.touch.callbackContext = this; + this.game.input.touch.touchStartCallback = this.unlock; + this.game.input.mouse.callbackContext = this; + this.game.input.mouse.mouseDownCallback = this.unlock; + this.touchLocked = true; + } + else + { + // What about iOS5? + this.touchLocked = false; + } + + if (window['PhaserGlobal']) + { + // Check to see if all audio playback is disabled (i.e. handled by a 3rd party class) + if (window['PhaserGlobal'].disableAudio == true) + { + this.usingWebAudio = false; + this.noAudio = true; + return; + } + + // Check if the Web Audio API is disabled (for testing Audio Tag playback during development) + if (window['PhaserGlobal'].disableWebAudio == true) + { + this.usingWebAudio = false; + this.usingAudioTag = true; + this.noAudio = false; + return; + } + } + + if (!!window['AudioContext']) + { + this.context = new window['AudioContext'](); + } + else if(!!window['webkitAudioContext']) + { + this.context = new window['webkitAudioContext'](); + } + else if(!!window['Audio']) + { + this.usingWebAudio = false; + this.usingAudioTag = true; + } + else + { + this.usingWebAudio = false; + this.noAudio = true; + } + + if (this.context !== null) + { + if (typeof this.context.createGain === 'undefined') + { + this.masterGain = this.context.createGainNode(); + } + else + { + this.masterGain = this.context.createGain(); + } + + this.masterGain.gain.value = 1; + this.masterGain.connect(this.context.destination); + } + +}; + +Phaser.SoundManager.prototype = { + + game: null, + + _muted: false, + _unlockSource: null, + _volume: 1, + _muted: false, + _sounds: [], + + context: null, + usingWebAudio: true, + usingAudioTag: false, + noAudio: false, + + touchLocked: false, + onSoundDecode: null, + + channels: 32, + + unlock: function () { + + if (this.touchLocked == false) + { + return; + } + + console.log('SoundManager touch unlocked'); + + if (this.game.device.webAudio && (window['PhaserGlobal'] && window['PhaserGlobal'].disableWebAudio == false)) + { + // Create empty buffer and play it + var buffer = this.context.createBuffer(1, 1, 22050); + this._unlockSource = this.context.createBufferSource(); + this._unlockSource.buffer = buffer; + this._unlockSource.connect(this.context.destination); + this._unlockSource.noteOn(0); + } + else + { + // Create an Audio tag? + this.touchLocked = false; + this._unlockSource = null; + this.game.input.touch.callbackContext = null; + this.game.input.touch.touchStartCallback = null; + this.game.input.mouse.callbackContext = null; + this.game.input.mouse.mouseDownCallback = null; + } + + }, + + stopAll: function () { + + for (var i = 0; i < this._sounds.length; i++) + { + if (this._sounds[i]) + { + this._sounds[i].stop(); + } + } + + }, + + pauseAll: function () { + + for (var i = 0; i < this._sounds.length; i++) + { + if (this._sounds[i]) + { + this._sounds[i].pause(); + } + } + + }, + + resumeAll: function () { + + for (var i = 0; i < this._sounds.length; i++) + { + if (this._sounds[i]) + { + this._sounds[i].resume(); + } + } + + }, + + /** + * Decode a sound with its assets key. + * @param key {string} Assets key of the sound to be decoded. + * @param [sound] {Sound} its bufer will be set to decoded data. + */ + decode: function (key, sound) { + + sound = sound || null; + + var soundData = this.game.cache.getSoundData(key); + + if (soundData) + { + if (this.game.cache.isSoundDecoded(key) === false) + { + this.game.cache.updateSound(key, 'isDecoding', true); + + var that = this; + + this.context.decodeAudioData(soundData, function (buffer) { + that.game.cache.decodedSound(key, buffer); + if (sound) + { + that.onSoundDecode.dispatch(sound); + } + }); + } + } + + }, + + update: function () { + + if (this.touchLocked) + { + if (this.game.device.webAudio && this._unlockSource !== null) + { + if ((this._unlockSource.playbackState === this._unlockSource.PLAYING_STATE || this._unlockSource.playbackState === this._unlockSource.FINISHED_STATE)) + { + this.touchLocked = false; + this._unlockSource = null; + this.game.input.touch.callbackContext = null; + this.game.input.touch.touchStartCallback = null; + } + } + } + + for (var i = 0; i < this._sounds.length; i++) + { + this._sounds[i].update(); + } + + }, + + add: function (key, volume, loop) { + + volume = volume || 1; + loop = loop || false; + + var sound = new Phaser.Sound(this.game, key, volume, loop); + + this._sounds.push(sound); + + return sound; + + } + +}; + +Object.defineProperty(Phaser.SoundManager.prototype, "mute", { + + /** + * A global audio mute toggle. + */ + get: function () { + + return this._muted; + + }, + + set: function (value) { + + value = value || null; + + if (value) + { + if (this._muted) + { + return; + } + + this._muted = true; + + if (this.usingWebAudio) + { + this._muteVolume = this.masterGain.gain.value; + this.masterGain.gain.value = 0; + } + + // Loop through sounds + for (var i = 0; i < this._sounds.length; i++) + { + if (this._sounds[i].usingAudioTag) + { + this._sounds[i].mute = true; + } + } + } + else + { + if (this._muted == false) + { + return; + } + + this._muted = false; + + if (this.usingWebAudio) + { + this.masterGain.gain.value = this._muteVolume; + } + + // Loop through sounds + for (var i = 0; i < this._sounds.length; i++) + { + if (this._sounds[i].usingAudioTag) + { + this._sounds[i].mute = false; + } + } + } + }, + + enumerable: true, + configurable: true +}); + +Object.defineProperty(Phaser.SoundManager.prototype, "volume", { + + get: function () { + + if (this.usingWebAudio) + { + return this.masterGain.gain.value; + } + else + { + return this._volume; + } + + }, + + /** + * The global audio volume. A value between 0 (silence) and 1 (full volume) + */ + set: function (value) { + + value = this.game.math.clamp(value, 1, 0); + + this._volume = value; + + if (this.usingWebAudio) + { + this.masterGain.gain.value = value; + } + + // Loop through the sound cache and change the volume of all html audio tags + for (var i = 0; i < this._sounds.length; i++) + { + if (this._sounds[i].usingAudioTag) + { + this._sounds[i].volume = this._sounds[i].volume * value; + } + } + + }, + enumerable: true, + configurable: true +}); diff --git a/src/utils/Debug.js b/src/utils/Debug.js index db3b67f7..9dc49737 100644 --- a/src/utils/Debug.js +++ b/src/utils/Debug.js @@ -186,6 +186,8 @@ Phaser.Utils.Debug.prototype = { this.line('Position: ' + sound.position); } + this.stop(); + }, /**