소스 검색

fix(Memory): Fix memory leak in sample-player dependency (#78, code by Charlottecuc)

Previously, repeated playback of the same sheet would add a few megabytes of memory on every playthrough.
Wtih this patch, memory usage stays constant no matter how many times the sheet is played back.

Big thanks to @Charlottecuc for finding and sharing the fix!
see https://github.com/opensheetmusicdisplay/osmd-extended/issues/78
sschmidTU 2 년 전
부모
커밋
3fa92af430
3개의 변경된 파일38개의 추가작업 그리고 7개의 파일을 삭제
  1. 2 1
      package.json
  2. 5 2
      src/SamplePlayerPatch/lib/events.js
  3. 31 4
      src/SamplePlayerPatch/lib/player.js

+ 2 - 1
package.json

@@ -10,9 +10,10 @@
     "lint": "npm run eslint",
     "test": "karma start --single-run --no-auto-watch",
     "test:watch": "karma start --no-single-run --auto-watch --browsers ChromeNoSecurity",
-    "prebuild": "npm-run-all prebuildVexflow prebuildKarma",
+    "prebuild": "npm-run-all prebuildVexflow prebuildKarma prebuildSamplePlayer",
     "prebuildVexflow": "ncp src/VexFlowPatch/src/ node_modules/vexflow/src/",
     "prebuildKarma": "ncp src/KarmaWebpackPatch/lib/ node_modules/karma-webpack/lib/",
+    "prebuildSamplePlayer": "ncp src/SamplePlayerPatch/lib/ node_modules/sample-player/lib/",
     "prepare": "npm run build",
     "build": "npm-run-all lint build:webpack",
     "build:webpack": "webpack --progress --config webpack.prod.js",

+ 5 - 2
src/SamplePlayerPatch/lib/events.js

@@ -14,8 +14,11 @@ module.exports = function (player) {
   player.on = function (event, cb) {
     if (arguments.length === 1 && typeof event === 'function') return player.on('event', event)
     var prop = 'on' + event
-    var old = player[prop]
-    player[prop] = old ? chain(old, cb) : cb
+    if (player[prop]) {
+      player[prop].push(cb)
+    } else {
+      player[prop] = [cb]
+    }
     return player
   }
   return player

+ 31 - 4
src/SamplePlayerPatch/lib/player.js

@@ -80,8 +80,17 @@ function SamplePlayer (ac, source, options) {
     var opts = options || EMPTY
     when = Math.max(ac.currentTime, when || 0)
     player.emit('start', when, name, opts)
-    var node = createNode(name, buffer, opts)
-    node.id = track(name, node)
+    // cache
+    var node
+    for (var iterator in tracked) {
+      if (buffer === tracked[iterator].source.buffer) {
+        node = resetNode(tracked[iterator], buffer, opts)
+      }
+    }
+    if (!node) {
+      node = createNode(name, buffer, opts)
+      node.id = track(name, node)
+    }
     node.env.start(when)
     node.source.start(when)
     player.emit('started', when, node.id, node)
@@ -142,8 +151,8 @@ function SamplePlayer (ac, source, options) {
 
   player.emit = function (event, when, obj, opts) {
     if (player.onevent) player.onevent(event, when, obj, opts)
-    var fn = player['on' + event]
-    if (fn) fn(when, obj, opts)
+    var fnList = player['on' + event]
+    if (fnList) fnList.forEach((fn) => { fn(when, obj, opts) })
   }
 
   return player
@@ -186,6 +195,24 @@ function SamplePlayer (ac, source, options) {
     }
     return node
   }
+
+  function resetNode(oldNode, buffer, options) {
+    var node = oldNode
+    node.gain.value = 0 // the envelope will control the gain
+    node.connect(out)
+
+    node.env = envelope(ac, options, opts)
+    node.env.connect(node.gain)
+
+    node.source = ac.createBufferSource()
+    node.source.buffer = buffer
+    node.source.connect(node)
+    node.source.loop = options.loop || opts.loop
+    node.source.playbackRate.value = centsToRate(options.cents || opts.cents)
+    node.source.loopStart = options.loopStart || opts.loopStart
+    node.source.loopEnd = options.loopEnd || opts.loopEnd
+    return node
+  }
 }
 
 function isNum (x) { return typeof x === 'number' }