diff --git a/.gitignore b/.gitignore
index 1041c72..621038f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,7 +31,6 @@ bower_components
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
-dist/
# Dependency directories
node_modules/
diff --git a/.npmignore b/.npmignore
index aa4856e..39dcd2b 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,12 +1,8 @@
**/node_modules/
-build/
-src/
*.tgz
debug.log
gulpfile.js
-**/tsconfig*.*
-tslint.json
webpack.*
**/.vscode
.npmignore
diff --git a/dist/index.d.ts b/dist/index.d.ts
new file mode 100644
index 0000000..c1ceb87
--- /dev/null
+++ b/dist/index.d.ts
@@ -0,0 +1,3 @@
+export * from './mixer';
+export * from './mixer-interleaved';
+export * from './input';
diff --git a/dist/index.js b/dist/index.js
new file mode 100644
index 0000000..976c832
--- /dev/null
+++ b/dist/index.js
@@ -0,0 +1,8 @@
+"use strict";
+function __export(m) {
+ for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
+}
+Object.defineProperty(exports, "__esModule", { value: true });
+__export(require("./mixer"));
+__export(require("./mixer-interleaved"));
+__export(require("./input"));
diff --git a/dist/input.d.ts b/dist/input.d.ts
new file mode 100644
index 0000000..da1731c
--- /dev/null
+++ b/dist/input.d.ts
@@ -0,0 +1,32 @@
+///
+import { Writable, WritableOptions } from 'stream';
+import { Mixer } from './mixer';
+export interface InputArguments extends WritableOptions {
+ channels?: number;
+ bitDepth?: number;
+ sampleRate?: number;
+ volume?: number;
+ clearInterval?: number;
+}
+export declare class Input extends Writable {
+ private mixer;
+ private args;
+ private buffer;
+ private sampleByteLength;
+ private readSample;
+ private writeSample;
+ hasData: boolean;
+ lastDataTime: number;
+ lastClearTime: number;
+ constructor(args: InputArguments);
+ setMixer(mixer: Mixer): void;
+ read(samples: any): Buffer;
+ readMono(samples: any): Buffer;
+ readStereo(samples: any): Buffer;
+ availableSamples(length?: number): number;
+ _write(chunk: any, encoding: any, next: any): void;
+ setVolume(volume: number): void;
+ getVolume(): number;
+ clear(force?: boolean): void;
+ destroy(): void;
+}
diff --git a/dist/input.js b/dist/input.js
new file mode 100644
index 0000000..9a319a5
--- /dev/null
+++ b/dist/input.js
@@ -0,0 +1,109 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const stream_1 = require("stream");
+class Input extends stream_1.Writable {
+ constructor(args) {
+ super(args);
+ if (args.channels !== 1 && args.channels !== 2) {
+ args.channels = 2;
+ }
+ if (args.sampleRate < 1) {
+ args.sampleRate = 44100;
+ }
+ if (args.volume < 0 || args.volume > 100) {
+ args.volume = 100;
+ }
+ if (args.channels === 1) {
+ this.readMono = this.read;
+ }
+ if (args.channels === 2) {
+ this.readStereo = this.read;
+ }
+ this.buffer = new Buffer(0);
+ if (args.bitDepth === 8) {
+ this.readSample = this.buffer.readInt8;
+ this.writeSample = this.buffer.writeInt8;
+ this.sampleByteLength = 1;
+ }
+ else if (args.bitDepth === 32) {
+ this.readSample = this.buffer.readInt32LE;
+ this.writeSample = this.buffer.writeInt32LE;
+ this.sampleByteLength = 4;
+ }
+ else {
+ args.bitDepth = 16;
+ this.readSample = this.buffer.readInt16LE;
+ this.writeSample = this.buffer.writeInt16LE;
+ this.sampleByteLength = 2;
+ }
+ this.args = args;
+ this.hasData = false;
+ this.lastClearTime = new Date().getTime();
+ }
+ setMixer(mixer) {
+ this.mixer = mixer;
+ }
+ read(samples) {
+ let bytes = samples * (this.args.bitDepth / 8) * this.args.channels;
+ if (this.buffer.length < bytes) {
+ bytes = this.buffer.length;
+ }
+ let sample = this.buffer.slice(0, bytes);
+ this.buffer = this.buffer.slice(bytes);
+ for (let i = 0; i < sample.length; i += 2) {
+ sample.writeInt16LE(Math.floor(this.args.volume * sample.readInt16LE(i) / 100), i);
+ }
+ return sample;
+ }
+ readMono(samples) {
+ let stereoBuffer = this.read(samples);
+ let monoBuffer = new Buffer(stereoBuffer.length / 2);
+ let availableSamples = this.availableSamples(stereoBuffer.length);
+ for (let i = 0; i < availableSamples; i++) {
+ let l = this.readSample.call(stereoBuffer, i * this.sampleByteLength * 2);
+ let r = this.readSample.call(stereoBuffer, (i * this.sampleByteLength * 2) + this.sampleByteLength);
+ this.writeSample.call(monoBuffer, Math.floor((l + r) / 2), i * this.sampleByteLength);
+ }
+ return monoBuffer;
+ }
+ readStereo(samples) {
+ let monoBuffer = this.read(samples);
+ let stereoBuffer = new Buffer(monoBuffer.length * 2);
+ let availableSamples = this.availableSamples(monoBuffer.length);
+ for (let i = 0; i < availableSamples; i++) {
+ let m = this.readSample.call(monoBuffer, i * this.sampleByteLength);
+ this.writeSample.call(stereoBuffer, m, i * this.sampleByteLength * 2);
+ this.writeSample.call(stereoBuffer, m, (i * this.sampleByteLength * 2) + this.sampleByteLength);
+ }
+ return stereoBuffer;
+ }
+ availableSamples(length) {
+ length = length || this.buffer.length;
+ return Math.floor(length / ((this.args.bitDepth / 8) * this.args.channels));
+ }
+ _write(chunk, encoding, next) {
+ if (!this.hasData) {
+ this.hasData = true;
+ }
+ this.buffer = Buffer.concat([this.buffer, chunk]);
+ next();
+ }
+ setVolume(volume) {
+ this.args.volume = Math.max(Math.min(volume, 100), 0);
+ }
+ getVolume() {
+ return this.args.volume;
+ }
+ clear(force) {
+ let now = new Date().getTime();
+ if (force || (this.args.clearInterval && now - this.lastClearTime >= this.args.clearInterval)) {
+ let length = 1024 * (this.args.bitDepth / 8) * this.args.channels;
+ this.buffer = this.buffer.slice(0, length);
+ this.lastClearTime = now;
+ }
+ }
+ destroy() {
+ this.buffer = new Buffer(0);
+ }
+}
+exports.Input = Input;
diff --git a/dist/mixer-interleaved.d.ts b/dist/mixer-interleaved.d.ts
new file mode 100644
index 0000000..5a35ab9
--- /dev/null
+++ b/dist/mixer-interleaved.d.ts
@@ -0,0 +1,4 @@
+import { Mixer } from './mixer';
+export declare class InterleavedMixer extends Mixer {
+ _read(): void;
+}
diff --git a/dist/mixer-interleaved.js b/dist/mixer-interleaved.js
new file mode 100644
index 0000000..f7f6b46
--- /dev/null
+++ b/dist/mixer-interleaved.js
@@ -0,0 +1,28 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const mixer_1 = require("./mixer");
+class InterleavedMixer extends mixer_1.Mixer {
+ _read() {
+ let samples = this.getMaxSamples();
+ if (samples > 0 && samples !== Number.MAX_VALUE) {
+ let mixedBuffer = new Buffer(samples * this.sampleByteLength * this.args.channels);
+ mixedBuffer.fill(0);
+ for (let c = 0; c < this.args.channels; c++) {
+ let input = this.inputs[c];
+ if (input !== undefined && input.hasData) {
+ let inputBuffer = input.readMono(samples);
+ for (let i = 0; i < samples; i++) {
+ let sample = this.readSample.call(inputBuffer, i * this.sampleByteLength);
+ this.writeSample.call(mixedBuffer, sample, (i * this.sampleByteLength * this.args.channels) + (c * this.sampleByteLength));
+ }
+ }
+ }
+ this.push(mixedBuffer);
+ }
+ else {
+ setImmediate(this._read.bind(this));
+ }
+ this.clearBuffers();
+ }
+}
+exports.InterleavedMixer = InterleavedMixer;
diff --git a/dist/mixer.d.ts b/dist/mixer.d.ts
new file mode 100644
index 0000000..22f0795
--- /dev/null
+++ b/dist/mixer.d.ts
@@ -0,0 +1,27 @@
+///
+import { Input, InputArguments } from './input';
+import { Readable, ReadableOptions } from 'stream';
+export interface MixerArguments extends ReadableOptions {
+ channels: number;
+ sampleRate: number;
+ bitDepth?: number;
+}
+export declare class Mixer extends Readable {
+ protected args: MixerArguments;
+ protected inputs: Input[];
+ protected sampleByteLength: number;
+ protected readSample: any;
+ protected writeSample: any;
+ protected needReadable: boolean;
+ private static INPUT_IDLE_TIMEOUT;
+ private _timer;
+ constructor(args: MixerArguments);
+ _read(): void;
+ input(args: InputArguments, channel?: number): Input;
+ removeInput(input: Input): void;
+ addInput(input: Input, channel?: number): void;
+ destroy(): void;
+ close(): void;
+ protected getMaxSamples(): number;
+ protected clearBuffers(): void;
+}
diff --git a/dist/mixer.js b/dist/mixer.js
new file mode 100644
index 0000000..2b7e92e
--- /dev/null
+++ b/dist/mixer.js
@@ -0,0 +1,107 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const input_1 = require("./input");
+const stream_1 = require("stream");
+const _ = require("underscore");
+class Mixer extends stream_1.Readable {
+ constructor(args) {
+ super(args);
+ this.needReadable = true;
+ this._timer = null;
+ if (args.sampleRate < 1) {
+ args.sampleRate = 44100;
+ }
+ let buffer = new Buffer(0);
+ if (args.bitDepth === 8) {
+ this.readSample = buffer.readInt8;
+ this.writeSample = buffer.writeInt8;
+ this.sampleByteLength = 1;
+ }
+ else if (args.bitDepth === 32) {
+ this.readSample = buffer.readInt32LE;
+ this.writeSample = buffer.writeInt32LE;
+ this.sampleByteLength = 4;
+ }
+ else {
+ args.bitDepth = 16;
+ this.readSample = buffer.readInt16LE;
+ this.writeSample = buffer.writeInt16LE;
+ this.sampleByteLength = 2;
+ }
+ this.args = args;
+ this.inputs = [];
+ }
+ _read() {
+ let samples = this.getMaxSamples();
+ if (samples > 0 && samples !== Number.MAX_VALUE) {
+ let mixedBuffer = new Buffer(samples * this.sampleByteLength * this.args.channels);
+ mixedBuffer.fill(0);
+ this.inputs.forEach((input) => {
+ if (input.hasData) {
+ let inputBuffer = this.args.channels === 1 ? input.readMono(samples) : input.readStereo(samples);
+ for (let i = 0; i < samples * this.args.channels; i++) {
+ let sample = this.readSample.call(mixedBuffer, i * this.sampleByteLength) + Math.floor(this.readSample.call(inputBuffer, i * this.sampleByteLength) / this.inputs.length);
+ this.writeSample.call(mixedBuffer, sample, i * this.sampleByteLength);
+ }
+ }
+ });
+ this.push(mixedBuffer);
+ }
+ else if (this.needReadable) {
+ clearImmediate(this._timer);
+ this._timer = setImmediate(this._read.bind(this));
+ }
+ this.clearBuffers();
+ }
+ input(args, channel) {
+ let input = new input_1.Input({
+ channels: args.channels || this.args.channels,
+ bitDepth: args.bitDepth || this.args.bitDepth,
+ sampleRate: args.sampleRate || this.args.sampleRate,
+ volume: args.volume || 100,
+ clearInterval: args.clearInterval
+ });
+ this.addInput(input, channel);
+ return input;
+ }
+ removeInput(input) {
+ this.inputs = _.without(this.inputs, input);
+ }
+ addInput(input, channel) {
+ if (channel && (channel < 0 || channel >= this.args.channels)) {
+ throw new Error("Channel number out of range");
+ }
+ input.setMixer(this);
+ this.inputs[channel || this.inputs.length] = input;
+ }
+ destroy() {
+ this.inputs = [];
+ }
+ close() {
+ this.needReadable = false;
+ }
+ getMaxSamples() {
+ let samples = Number.MAX_VALUE;
+ this.inputs.forEach((input) => {
+ let ias = input.availableSamples();
+ if (ias > 0) {
+ input.lastDataTime = new Date().getTime();
+ }
+ else if (ias <= 0 && new Date().getTime() - input.lastDataTime >= Mixer.INPUT_IDLE_TIMEOUT) {
+ input.hasData = false;
+ return;
+ }
+ if (input.hasData && ias < samples) {
+ samples = ias;
+ }
+ });
+ return samples;
+ }
+ clearBuffers() {
+ this.inputs.forEach((input) => {
+ input.clear();
+ });
+ }
+}
+Mixer.INPUT_IDLE_TIMEOUT = 250;
+exports.Mixer = Mixer;
diff --git a/package-lock.json b/package-lock.json
index 249cb56..2ef525c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "audio-mixer",
- "version": "2.0.2",
+ "version": "2.1.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index 0d610c7..04838e6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "audio-mixer",
- "version": "2.1.1",
+ "version": "2.1.2",
"description": "Allows mixing of PCM audio streams.",
"main": "dist/index.js",
"types": "dist/index.d.ts",