forked from ConnorChristie/Audio-Mixer
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f706994
commit 300ea95
Showing
12 changed files
with
320 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from './mixer'; | ||
export * from './mixer-interleaved'; | ||
export * from './input'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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")); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/// <reference types="node" /> | ||
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { Mixer } from './mixer'; | ||
export declare class InterleavedMixer extends Mixer { | ||
_read(): void; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/// <reference types="node" /> | ||
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters