From 251244ce9c6f0377ec7bd9e13d85edc6e4196a5d Mon Sep 17 00:00:00 2001
From: Paul Kaplan <pkaplan@media.mit.edu>
Date: Mon, 3 Apr 2017 09:33:23 -0400
Subject: [PATCH 1/3] Expose VM#addSound publicly

---
 src/import/load-sound.js | 29 +++++++++++++++++++++++++++++
 src/import/sb2import.js  | 28 +---------------------------
 src/virtual-machine.js   | 13 +++++++++++++
 test/unit/spec.js        |  1 +
 4 files changed, 44 insertions(+), 27 deletions(-)
 create mode 100644 src/import/load-sound.js

diff --git a/src/import/load-sound.js b/src/import/load-sound.js
new file mode 100644
index 000000000..364c71be0
--- /dev/null
+++ b/src/import/load-sound.js
@@ -0,0 +1,29 @@
+var AssetType = require('scratch-storage').AssetType;
+var log = require('../util/log');
+
+/**
+ * Load a sound's asset into memory asynchronously.
+ * @param {!object} sound - the Scratch sound object.
+ * @property {string} md5 - the MD5 and extension of the sound to be loaded.
+ * @property {Buffer} data - sound data will be written here once loaded.
+ * @param {!Runtime} runtime - Scratch runtime, used to access the storage module.
+ * @returns {?Promise} - a promise which will resolve after sound is loaded, or null on error.
+ */
+var loadSound = function (sound, runtime) {
+    if (!runtime.storage) {
+        log.error('No storage module present; cannot load sound asset: ', sound.md5);
+        return Promise.resolve(null);
+    }
+    if (!runtime.audioEngine) {
+        log.error('No audio engine present; cannot load sound asset: ', sound.md5);
+        return Promise.resolve(null);
+    }
+    var idParts = sound.md5.split('.');
+    var md5 = idParts[0];
+    return runtime.storage.load(AssetType.Sound, md5).then(function (soundAsset) {
+        sound.data = soundAsset.data;
+        return runtime.audioEngine.decodeSound(sound);
+    });
+};
+
+module.exports = loadSound;
diff --git a/src/import/sb2import.js b/src/import/sb2import.js
index bc5208344..097ce74a7 100644
--- a/src/import/sb2import.js
+++ b/src/import/sb2import.js
@@ -5,9 +5,6 @@
  * scratch-vm runtime structures.
  */
 
-var ScratchStorage = require('scratch-storage');
-var AssetType = ScratchStorage.AssetType;
-
 var Blocks = require('../engine/blocks');
 var RenderedTarget = require('../sprites/rendered-target');
 var Sprite = require('../sprites/sprite');
@@ -19,6 +16,7 @@ var Variable = require('../engine/variable');
 var List = require('../engine/list');
 
 var loadCostume = require('./load-costume.js');
+var loadSound = require('./load-sound.js');
 
 /**
  * Parse a single "Scratch object" and create all its in-memory VM objects.
@@ -150,30 +148,6 @@ var parseScratchObject = function (object, runtime, topLevel) {
     return target;
 };
 
-/**
- * Load a sound's asset into memory asynchronously.
- * @param {!object} sound - the Scratch sound object.
- * @property {string} md5 - the MD5 and extension of the sound to be loaded.
- * @property {Buffer} data - sound data will be written here once loaded.
- * @param {!Runtime} runtime - Scratch runtime, used to access the storage module.
- */
-var loadSound = function (sound, runtime) {
-    if (!runtime.storage) {
-        log.error('No storage module present; cannot load sound asset: ', sound.md5);
-        return;
-    }
-    if (!runtime.audioEngine) {
-        log.error('No audio engine present; cannot load sound asset: ', sound.md5);
-        return;
-    }
-    var idParts = sound.md5.split('.');
-    var md5 = idParts[0];
-    runtime.storage.load(AssetType.Sound, md5).then(function (soundAsset) {
-        sound.data = soundAsset.data;
-        runtime.audioEngine.decodeSound(sound);
-    });
-};
-
 /**
  * Top-level handler. Parse provided JSON,
  * and process the top-level object (the stage object).
diff --git a/src/virtual-machine.js b/src/virtual-machine.js
index 296cd45cf..b85705e37 100644
--- a/src/virtual-machine.js
+++ b/src/virtual-machine.js
@@ -8,6 +8,7 @@ var sb2import = require('./import/sb2import');
 var StringUtil = require('./util/string-util');
 
 var loadCostume = require('./import/load-costume.js');
+var loadSound = require('./import/load-sound.js');
 
 var RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_'];
 
@@ -209,6 +210,18 @@ VirtualMachine.prototype.addCostume = function (md5ext, costumeObject) {
     }.bind(this));
 };
 
+/**
+ * Add a sound to the current editing target.
+ * @param {!object} soundObject Object representing the costume.
+ * @returns {?Promise} - a promise that resolves when the sound has been decoded and added
+ */
+VirtualMachine.prototype.addSound = function (soundObject) {
+    return loadSound(soundObject, this.runtime).then(function () {
+        this.editingTarget.sprite.sounds.push(soundObject);
+        this.emitTargetsUpdate();
+    }.bind(this));
+};
+
 /**
  * Add a backdrop to the stage.
  * @param {string} md5ext - the MD5 and extension of the backdrop to be loaded.
diff --git a/test/unit/spec.js b/test/unit/spec.js
index 68485bf9d..b16f181f0 100644
--- a/test/unit/spec.js
+++ b/test/unit/spec.js
@@ -18,6 +18,7 @@ test('interface', function (t) {
     t.type(vm.addSprite2, 'function');
     t.type(vm.addCostume, 'function');
     t.type(vm.addBackdrop, 'function');
+    t.type(vm.addSound, 'function');
     t.type(vm.renameSprite, 'function');
     t.type(vm.deleteSprite, 'function');
 

From 33f436811c932264c5f1d05ed57bffb309a47890 Mon Sep 17 00:00:00 2001
From: Paul Kaplan <pkaplan@media.mit.edu>
Date: Mon, 3 Apr 2017 16:00:31 -0400
Subject: [PATCH 2/3] return sound in promise

---
 src/import/load-sound.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/import/load-sound.js b/src/import/load-sound.js
index 364c71be0..d696a2194 100644
--- a/src/import/load-sound.js
+++ b/src/import/load-sound.js
@@ -12,11 +12,11 @@ var log = require('../util/log');
 var loadSound = function (sound, runtime) {
     if (!runtime.storage) {
         log.error('No storage module present; cannot load sound asset: ', sound.md5);
-        return Promise.resolve(null);
+        return Promise.resolve(sound);
     }
     if (!runtime.audioEngine) {
         log.error('No audio engine present; cannot load sound asset: ', sound.md5);
-        return Promise.resolve(null);
+        return Promise.resolve(sound);
     }
     var idParts = sound.md5.split('.');
     var md5 = idParts[0];

From 2d6eaae01ef73a81070cafc5c4c7baafdb2dec2d Mon Sep 17 00:00:00 2001
From: Paul Kaplan <pkaplan@media.mit.edu>
Date: Mon, 3 Apr 2017 16:00:58 -0400
Subject: [PATCH 3/3] Update jsdoc

---
 src/import/load-sound.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/import/load-sound.js b/src/import/load-sound.js
index d696a2194..74856326a 100644
--- a/src/import/load-sound.js
+++ b/src/import/load-sound.js
@@ -7,7 +7,7 @@ var log = require('../util/log');
  * @property {string} md5 - the MD5 and extension of the sound to be loaded.
  * @property {Buffer} data - sound data will be written here once loaded.
  * @param {!Runtime} runtime - Scratch runtime, used to access the storage module.
- * @returns {?Promise} - a promise which will resolve after sound is loaded, or null on error.
+ * @returns {!Promise} - a promise which will resolve after sound is loaded
  */
 var loadSound = function (sound, runtime) {
     if (!runtime.storage) {