mirror of
https://github.com/google/blockly.git
synced 2026-01-07 17:10:11 +01:00
Move audio code to a new file (#1122)
* move audio code to a new file * dispose * null check
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -884,7 +884,7 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate) {
|
||||
* Play some UI effects (sound, animation) when disposing of a block.
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.disposeUiEffect = function() {
|
||||
this.workspace.playAudio('delete');
|
||||
this.workspace.getAudioManager().play('delete');
|
||||
|
||||
var xy = this.workspace.getSvgXY(/** @type {!Element} */ (this.svgGroup_));
|
||||
// Deeply clone the current block.
|
||||
@@ -933,7 +933,7 @@ Blockly.BlockSvg.disposeUiStep_ = function(clone, rtl, start, workspaceScale) {
|
||||
* Play some UI effects (sound, ripple) after a connection has been established.
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.connectionUiEffect = function() {
|
||||
this.workspace.playAudio('click');
|
||||
this.workspace.getAudioManger().play('click');
|
||||
if (this.workspace.scale < 1) {
|
||||
return; // Too small to care about visual effects.
|
||||
}
|
||||
@@ -981,7 +981,7 @@ Blockly.BlockSvg.connectionUiStep_ = function(ripple, start, workspaceScale) {
|
||||
* Play some UI effects (sound, animation) when disconnecting a block.
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.disconnectUiEffect = function() {
|
||||
this.workspace.playAudio('disconnect');
|
||||
this.workspace.getAudioManager().play('disconnect');
|
||||
if (this.workspace.scale < 1) {
|
||||
return; // Too small to care about visual effects.
|
||||
}
|
||||
|
||||
@@ -362,15 +362,16 @@ Blockly.inject.bindDocumentEvents_ = function() {
|
||||
* @private
|
||||
*/
|
||||
Blockly.inject.loadSounds_ = function(pathToMedia, workspace) {
|
||||
workspace.loadAudio_(
|
||||
var audioMgr = workspace.getAudioManager();
|
||||
audioMgr.load(
|
||||
[pathToMedia + 'click.mp3',
|
||||
pathToMedia + 'click.wav',
|
||||
pathToMedia + 'click.ogg'], 'click');
|
||||
workspace.loadAudio_(
|
||||
audioMgr.load(
|
||||
[pathToMedia + 'disconnect.wav',
|
||||
pathToMedia + 'disconnect.mp3',
|
||||
pathToMedia + 'disconnect.ogg'], 'disconnect');
|
||||
workspace.loadAudio_(
|
||||
audioMgr.load(
|
||||
[pathToMedia + 'delete.mp3',
|
||||
pathToMedia + 'delete.ogg',
|
||||
pathToMedia + 'delete.wav'], 'delete');
|
||||
@@ -381,7 +382,7 @@ Blockly.inject.loadSounds_ = function(pathToMedia, workspace) {
|
||||
while (soundBinds.length) {
|
||||
Blockly.unbindEvent_(soundBinds.pop());
|
||||
}
|
||||
workspace.preloadAudio_();
|
||||
audioMgr.preload();
|
||||
};
|
||||
|
||||
// These are bound on mouse/touch events with Blockly.bindEventWithChecks_, so
|
||||
|
||||
154
core/workspace_audio.js
Normal file
154
core/workspace_audio.js
Normal file
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Object in charge of loading, storing, and playing audio for a
|
||||
* workspace.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.WorkspaceAudio');
|
||||
|
||||
/**
|
||||
* Class for loading, storing, and playing audio for a workspace.
|
||||
* @param {Blockly.WorkspaceSvg} parentWorkspace The parent of the workspace
|
||||
* this audio object belongs to, or null.
|
||||
*/
|
||||
Blockly.WorkspaceAudio = function(parentWorkspace) {
|
||||
|
||||
/**
|
||||
* The parent of the workspace this object belongs to, or null. May be
|
||||
* checked for sounds that this object can't find.
|
||||
* @type {Blockly.WorkspaceSvg}
|
||||
* @private
|
||||
*/
|
||||
this.parentWorkspace_ = parentWorkspace;
|
||||
|
||||
/**
|
||||
* Database of pre-loaded sounds.
|
||||
* @private
|
||||
* @const
|
||||
*/
|
||||
this.SOUNDS_ = Object.create(null);
|
||||
};
|
||||
|
||||
/**
|
||||
* Time that the last sound was played.
|
||||
* @type {Date}
|
||||
* @private
|
||||
*/
|
||||
Blockly.WorkspaceAudio.prototype.lastSound_ = null;
|
||||
|
||||
/**
|
||||
* Dispose of this audio manager.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceAudio.prototype.dispose = function() {
|
||||
this.parentWorkspace_ = null;
|
||||
this.SOUNDS_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load an audio file. Cache it, ready for instantaneous playing.
|
||||
* @param {!Array.<string>} filenames List of file types in decreasing order of
|
||||
* preference (i.e. increasing size). E.g. ['media/go.mp3', 'media/go.wav']
|
||||
* Filenames include path from Blockly's root. File extensions matter.
|
||||
* @param {string} name Name of sound.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceAudio.prototype.load = function(filenames, name) {
|
||||
if (!filenames.length) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var audioTest = new window['Audio']();
|
||||
} catch (e) {
|
||||
// No browser support for Audio.
|
||||
// IE can throw an error even if the Audio object exists.
|
||||
return;
|
||||
}
|
||||
var sound;
|
||||
for (var i = 0; i < filenames.length; i++) {
|
||||
var filename = filenames[i];
|
||||
var ext = filename.match(/\.(\w+)$/);
|
||||
if (ext && audioTest.canPlayType('audio/' + ext[1])) {
|
||||
// Found an audio format we can play.
|
||||
sound = new window['Audio'](filename);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sound && sound.play) {
|
||||
this.SOUNDS_[name] = sound;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Preload all the audio files so that they play quickly when asked for.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceAudio.prototype.preload = function() {
|
||||
for (var name in this.SOUNDS_) {
|
||||
var sound = this.SOUNDS_[name];
|
||||
sound.volume = .01;
|
||||
sound.play();
|
||||
sound.pause();
|
||||
// iOS can only process one sound at a time. Trying to load more than one
|
||||
// corrupts the earlier ones. Just load one and leave the others uncached.
|
||||
if (goog.userAgent.IPAD || goog.userAgent.IPHONE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Play a named sound at specified volume. If volume is not specified,
|
||||
* use full volume (1).
|
||||
* @param {string} name Name of sound.
|
||||
* @param {number=} opt_volume Volume of sound (0-1).
|
||||
*/
|
||||
Blockly.WorkspaceAudio.prototype.play = function(name, opt_volume) {
|
||||
var sound = this.SOUNDS_[name];
|
||||
if (sound) {
|
||||
// Don't play one sound on top of another.
|
||||
var now = new Date;
|
||||
if (this.lastSound_ != null &&
|
||||
now - this.lastSound_ < Blockly.SOUND_LIMIT) {
|
||||
return;
|
||||
}
|
||||
this.lastSound_ = now;
|
||||
var mySound;
|
||||
var ie9 = goog.userAgent.DOCUMENT_MODE &&
|
||||
goog.userAgent.DOCUMENT_MODE === 9;
|
||||
if (ie9 || goog.userAgent.IPAD || goog.userAgent.ANDROID) {
|
||||
// Creating a new audio node causes lag in IE9, Android and iPad. Android
|
||||
// and IE9 refetch the file from the server, iPad uses a singleton audio
|
||||
// node which must be deleted and recreated for each new audio tag.
|
||||
mySound = sound;
|
||||
} else {
|
||||
mySound = sound.cloneNode();
|
||||
}
|
||||
mySound.volume = (opt_volume === undefined ? 1 : opt_volume);
|
||||
mySound.play();
|
||||
} else if (this.parentWorkspace_) {
|
||||
// Maybe a workspace on a lower level knows about this sound.
|
||||
this.parentWorkspace_.getAudioManager().play(name, opt_volume);
|
||||
}
|
||||
};
|
||||
@@ -36,6 +36,7 @@ goog.require('Blockly.ScrollbarPair');
|
||||
goog.require('Blockly.Touch');
|
||||
goog.require('Blockly.Trashcan');
|
||||
goog.require('Blockly.Workspace');
|
||||
goog.require('Blockly.WorkspaceAudio');
|
||||
goog.require('Blockly.WorkspaceDragSurfaceSvg');
|
||||
goog.require('Blockly.Xml');
|
||||
goog.require('Blockly.ZoomControls');
|
||||
@@ -77,12 +78,6 @@ Blockly.WorkspaceSvg = function(options, opt_blockDragSurface, opt_wsDragSurface
|
||||
this.useWorkspaceDragSurface_ =
|
||||
this.workspaceDragSurface_ && Blockly.utils.is3dSupported();
|
||||
|
||||
/**
|
||||
* Database of pre-loaded sounds.
|
||||
* @private
|
||||
* @const
|
||||
*/
|
||||
this.SOUNDS_ = Object.create(null);
|
||||
/**
|
||||
* List of currently highlighted blocks. Block highlighting is often used to
|
||||
* visually mark blocks currently being executed.
|
||||
@@ -91,6 +86,13 @@ Blockly.WorkspaceSvg = function(options, opt_blockDragSurface, opt_wsDragSurface
|
||||
*/
|
||||
this.highlightedBlocks_ = [];
|
||||
|
||||
/**
|
||||
* Object in charge of loading, storing, and playing audio for a workspace.
|
||||
* @type {Blockly.WorkspaceAudio}
|
||||
* @private
|
||||
*/
|
||||
this.audioManager_ = new Blockly.WorkspaceAudio(options.parentWorkspace);
|
||||
|
||||
this.registerToolboxCategoryCallback(Blockly.VARIABLE_CATEGORY_NAME,
|
||||
Blockly.Variables.flyoutCategory);
|
||||
this.registerToolboxCategoryCallback(Blockly.PROCEDURE_CATEGORY_NAME,
|
||||
@@ -222,13 +224,6 @@ Blockly.WorkspaceSvg.prototype.useWorkspaceDragSurface_ = false;
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.isDragSurfaceActive_ = false;
|
||||
|
||||
/**
|
||||
* Time that the last sound was played.
|
||||
* @type {Date}
|
||||
* @private
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.lastSound_ = null;
|
||||
|
||||
/**
|
||||
* Last known position of the page scroll.
|
||||
* This is used to determine whether we have recalculated screen coordinate
|
||||
@@ -428,6 +423,11 @@ Blockly.WorkspaceSvg.prototype.dispose = function() {
|
||||
this.zoomControls_ = null;
|
||||
}
|
||||
|
||||
if (this.audioManager_) {
|
||||
this.audioManager_.dispose();
|
||||
this.audioManager_ = null;
|
||||
}
|
||||
|
||||
if (this.toolboxCategoryCallbacks_) {
|
||||
this.toolboxCategoryCallbacks_ = null;
|
||||
}
|
||||
@@ -1212,92 +1212,6 @@ Blockly.WorkspaceSvg.prototype.showContextMenu_ = function(e) {
|
||||
Blockly.ContextMenu.show(e, menuOptions, this.RTL);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load an audio file. Cache it, ready for instantaneous playing.
|
||||
* @param {!Array.<string>} filenames List of file types in decreasing order of
|
||||
* preference (i.e. increasing size). E.g. ['media/go.mp3', 'media/go.wav']
|
||||
* Filenames include path from Blockly's root. File extensions matter.
|
||||
* @param {string} name Name of sound.
|
||||
* @private
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.loadAudio_ = function(filenames, name) {
|
||||
if (!filenames.length) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var audioTest = new window['Audio']();
|
||||
} catch (e) {
|
||||
// No browser support for Audio.
|
||||
// IE can throw an error even if the Audio object exists.
|
||||
return;
|
||||
}
|
||||
var sound;
|
||||
for (var i = 0; i < filenames.length; i++) {
|
||||
var filename = filenames[i];
|
||||
var ext = filename.match(/\.(\w+)$/);
|
||||
if (ext && audioTest.canPlayType('audio/' + ext[1])) {
|
||||
// Found an audio format we can play.
|
||||
sound = new window['Audio'](filename);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sound && sound.play) {
|
||||
this.SOUNDS_[name] = sound;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Preload all the audio files so that they play quickly when asked for.
|
||||
* @private
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.preloadAudio_ = function() {
|
||||
for (var name in this.SOUNDS_) {
|
||||
var sound = this.SOUNDS_[name];
|
||||
sound.volume = .01;
|
||||
sound.play();
|
||||
sound.pause();
|
||||
// iOS can only process one sound at a time. Trying to load more than one
|
||||
// corrupts the earlier ones. Just load one and leave the others uncached.
|
||||
if (goog.userAgent.IPAD || goog.userAgent.IPHONE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Play a named sound at specified volume. If volume is not specified,
|
||||
* use full volume (1).
|
||||
* @param {string} name Name of sound.
|
||||
* @param {number=} opt_volume Volume of sound (0-1).
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.playAudio = function(name, opt_volume) {
|
||||
var sound = this.SOUNDS_[name];
|
||||
if (sound) {
|
||||
// Don't play one sound on top of another.
|
||||
var now = new Date;
|
||||
if (now - this.lastSound_ < Blockly.SOUND_LIMIT) {
|
||||
return;
|
||||
}
|
||||
this.lastSound_ = now;
|
||||
var mySound;
|
||||
var ie9 = goog.userAgent.DOCUMENT_MODE &&
|
||||
goog.userAgent.DOCUMENT_MODE === 9;
|
||||
if (ie9 || goog.userAgent.IPAD || goog.userAgent.ANDROID) {
|
||||
// Creating a new audio node causes lag in IE9, Android and iPad. Android
|
||||
// and IE9 refetch the file from the server, iPad uses a singleton audio
|
||||
// node which must be deleted and recreated for each new audio tag.
|
||||
mySound = sound;
|
||||
} else {
|
||||
mySound = sound.cloneNode();
|
||||
}
|
||||
mySound.volume = (opt_volume === undefined ? 1 : opt_volume);
|
||||
mySound.play();
|
||||
} else if (this.options.parentWorkspace) {
|
||||
// Maybe a workspace on a lower level knows about this sound.
|
||||
this.options.parentWorkspace.playAudio(name, opt_volume);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Modify the block tree on the existing toolbox.
|
||||
* @param {Node|string} tree DOM tree of blocks, or text representation of same.
|
||||
@@ -1806,6 +1720,15 @@ Blockly.WorkspaceSvg.prototype.cancelCurrentGesture = function() {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the audio manager for this workspace.
|
||||
* @return {Blockly.WorkspaceAudio} The audio manager for this workspace.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.getAudioManager = function() {
|
||||
return this.audioManager_;
|
||||
};
|
||||
|
||||
// Export symbols that would otherwise be renamed by Closure compiler.
|
||||
Blockly.WorkspaceSvg.prototype['setVisible'] =
|
||||
Blockly.WorkspaceSvg.prototype.setVisible;
|
||||
|
||||
Reference in New Issue
Block a user