From 7bfee5f6645d06d8975b141ffdbf1863d3553873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakob=20Cs=C3=B6rgei=20Gustavsson?= Date: Wed, 10 May 2017 22:59:59 +0200 Subject: [PATCH 1/3] Longest param for mix to keep mixing until both buffers end --- index.js | 47 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 996e085..bace4ac 100644 --- a/index.js +++ b/index.js @@ -562,10 +562,11 @@ function trimInternal(buffer, level, trimLeft, trimRight) { * is reduced amount of calculations and flexibility. * If required, the cloning can be done before mixing, which will be the same. */ -function mix (bufferA, bufferB, ratio, offset) { +function mix (bufferA, bufferB, ratio, offset, longest) { validate(bufferA); validate(bufferB); + if (ratio == null) ratio = 0.5; var fn = ratio instanceof Function ? ratio : function (a, b) { return a * (1 - ratio) + b * ratio; @@ -573,17 +574,55 @@ function mix (bufferA, bufferB, ratio, offset) { if (offset == null) offset = 0; else if (offset < 0) offset += bufferA.length; + + if (longest) { + return mixLongest(bufferA, bufferB, fn, offset); + } else { + for (var channel = 0; channel < bufferA.numberOfChannels; channel++) { + var aData = bufferA.getChannelData(channel); + var bData = bufferB.getChannelData(channel); + + for (var i = offset, j = 0; i < bufferA.length && j < bufferB.length; i++, j++) { + aData[i] = fn.call(bufferA, aData[i], bData[j], j, channel); + } + } + + return bufferA; + } +} + +/** + * Mix as far as possible. This means to make the resulting buffer as large as + * the max of (offset + bufferB) or bufferA. So if offset + bufferB finishes + * after bufferA, keep mixing in bufferB after bufferA has finished. + */ +function mixLongest(bufferA, bufferB, fn, offset) { + const newLen = Math.max(offset + bufferB.length, bufferA.length); + var newBuf = create(newLen, bufferA.numberOfChannels, bufferA.sampleRate); + + // Copy bufferA so we can start mixing at the offset later + copy(bufferA, newBuf); for (var channel = 0; channel < bufferA.numberOfChannels; channel++) { + var newData = newBuf.getChannelData(channel); var aData = bufferA.getChannelData(channel); var bData = bufferB.getChannelData(channel); - for (var i = offset, j = 0; i < bufferA.length && j < bufferB.length; i++, j++) { - aData[i] = fn.call(bufferA, aData[i], bData[j], j, channel); + for (var i = 0; i < newLen; i++) { + var a = aData[i+offset] === undefined ? 0 : aData[i+offset]; + var b = bData[i] === undefined ? 0 : bData[i]; + + if(a === 0) { + newData[i+offset] = b; + } else if (b === 0) { + newData[i+offset] = a; + } else { + newData[i+offset] = fn.call(bufferA, a, b, i, channel); + } } } - return bufferA; + return newBuf; } From c75a09eb397838984b068d41489359ac50cdeabe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakob=20Cs=C3=B6rgei=20Gustavsson?= Date: Wed, 10 May 2017 23:00:21 +0200 Subject: [PATCH 2/3] Tests for mix longest and mixing with offset --- test.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test.js b/test.js index 7a35415..b5a5801 100644 --- a/test.js +++ b/test.js @@ -607,6 +607,30 @@ test('mix', function (t) { t.throws(function () { util.mix([1,2,3], [4,5,6], 0.1); }); + + // Offset mix + // Starting with normal mix to see normal result + var a = AudioBuffer(2, [1,1,1,1]); + var b = AudioBuffer(2, [0,0,0,0]); + util.mix(a, b, null); + t.deepEqual(a.getChannelData(0), [0.5,0.5]) + t.deepEqual(a.getChannelData(1), [0.5,0.5]) + + // Mix in at offset one, i.e. keep a's data until index 2 + var a = AudioBuffer(2, [1,1,1,1]); + var b = AudioBuffer(2, [0,0,0,0]); + util.mix(a, b, null,1); + t.deepEqual(a.getChannelData(0), [1,0.5]) + t.deepEqual(a.getChannelData(1), [1,0.5]) + + // Mix with longest setting. Will keep b's data after + // a finishes with respect to offset + var a = AudioBuffer(2, [1,1,1,1]); + var b = AudioBuffer(2, [0,0,0,0,1,1,1,1]); + var newBuf = util.mix(a, b, null, 1, true); + t.deepEqual(newBuf.getChannelData(0), [1,1,0,0,0]) + t.deepEqual(newBuf.getChannelData(1), [1,1,1,1,1]) + t.end() }); From 90ee8b3eb5b55b793e27aee9e46b330c200cd322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakob=20Cs=C3=B6rgei=20Gustavsson?= Date: Wed, 10 May 2017 23:08:26 +0200 Subject: [PATCH 3/3] Update README.md More docs for new mix function --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f7f4e60..83839c7 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Utility functions for [_AudioBuffers_](https://github.com/audiojs/audio-buffer) * [util.normalize(buf, dst?, start?, end?)](#utilnormalizebuffer-target-start0-end-0) * [util.removeStatic(buf, dst?, start?, end?)](#utilremovestaticbuffer-target-start0-end-0) * [util.trim(buf, lvl)](#utiltrimbuffer-threshold0) -* [util.mix(a, b, amt?, off?)](#utilmixbuffera-bufferb-ratiovala-valb-i-channelval-offset0) +* [util.mix(a, b, amt?, off?, longest?)](#utilmixbuffera-bufferb-ratiovala-valb-i-channelval-offset0) * [util.size(buf)](#utilsizebuffer) * [util.data(buf, dst?)](#utildatabuffer-data) @@ -194,8 +194,8 @@ a.getChannelData(1) // [-.1, .1] Create buffer with trimmed zeros from the start and/or end, by the threshold amplitude. -### `util.mix(bufferA, bufferB, ratio|(valA, valB, i, channel)=>val?, offset=0)` -Mix second buffer into the first one. Pass optional weight value or mixing function. +### `util.mix(bufferA, bufferB, ratio|(valA, valB, i, channel)=>val?, offset=0, longest=false)` +Mix second buffer into the first one. Pass optional weight value or mixing function. If longest parameter is passed, function will keep mixing until the longest of either `(bufferA)` or `(offset + bufferB)` has finished. For example, if `bufferA.length = 10`, `offset = 5` and `bufferB.length = 7`, then the total length will be `12` since `(offset + bufferB.length)` overruns `bufferA.length` by `2`. ### `util.size(buffer)` Return buffer size, in bytes. Use [pretty-bytes](https://npmjs.org/package/pretty-bytes) package to format bytes to a string, if needed.