New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

speaker

Package Overview
Dependencies
Maintainers
2
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

speaker - npm Package Compare versions

Comparing version 0.3.1 to 0.4.0

59

examples/sine.js

@@ -0,1 +1,2 @@

'use strict'

@@ -7,56 +8,54 @@ /**

var Readable = require('stream').Readable;
var Speaker = require('../');
const Readable = require('stream').Readable
const bufferAlloc = require('buffer-alloc')
const Speaker = require('../')
// node v0.8.x compat
if (!Readable) Readable = require('readable-stream/readable');
// the frequency to play
var freq = parseFloat(process.argv[2], 10) || 440.0; // Concert A, default tone
const freq = parseFloat(process.argv[2], 10) || 440.0 // Concert A, default tone
// seconds worth of audio data to generate before emitting "end"
var duration = parseFloat(process.argv[3], 10) || 2.0;
const duration = parseFloat(process.argv[3], 10) || 2.0
console.log('generating a %dhz sine wave for %d seconds', freq, duration);
console.log('generating a %dhz sine wave for %d seconds', freq, duration)
// A SineWaveGenerator readable stream
var sine = new Readable();
sine.bitDepth = 16;
sine.channels = 2;
sine.sampleRate = 44100;
sine.samplesGenerated = 0;
sine._read = read;
const sine = new Readable()
sine.bitDepth = 16
sine.channels = 2
sine.sampleRate = 44100
sine.samplesGenerated = 0
sine._read = read
// create a SineWaveGenerator instance and pipe it to the speaker
sine.pipe(new Speaker());
sine.pipe(new Speaker())
// the Readable "_read()" callback function
function read (n) {
var sampleSize = this.bitDepth / 8;
var blockAlign = sampleSize * this.channels;
var numSamples = n / blockAlign | 0;
var buf = new Buffer(numSamples * blockAlign);
var amplitude = 32760; // Max amplitude for 16-bit audio
const sampleSize = this.bitDepth / 8
const blockAlign = sampleSize * this.channels
const numSamples = n / blockAlign | 0
const buf = bufferAlloc(numSamples * blockAlign)
const amplitude = 32760 // Max amplitude for 16-bit audio
// the "angle" used in the function, adjusted for the number of
// channels and sample rate. This value is like the period of the wave.
var t = (Math.PI * 2 * freq) / this.sampleRate;
const t = (Math.PI * 2 * freq) / this.sampleRate
for (var i = 0; i < numSamples; i++) {
for (let i = 0; i < numSamples; i++) {
// fill with a simple sine wave at max amplitude
for (var channel = 0; channel < this.channels; channel++) {
var s = this.samplesGenerated + i;
var val = Math.round(amplitude * Math.sin(t * s)); // sine wave
var offset = (i * sampleSize * this.channels) + (channel * sampleSize);
buf['writeInt' + this.bitDepth + 'LE'](val, offset);
for (let channel = 0; channel < this.channels; channel++) {
const s = this.samplesGenerated + i
const val = Math.round(amplitude * Math.sin(t * s)) // sine wave
const offset = (i * sampleSize * this.channels) + (channel * sampleSize)
buf[`writeInt${this.bitDepth}LE`](val, offset)
}
}
this.push(buf);
this.push(buf)
this.samplesGenerated += numSamples;
this.samplesGenerated += numSamples
if (this.samplesGenerated >= this.sampleRate * duration) {
// after generating "duration" second of audio, emit "end"
this.push(null);
this.push(null)
}
}

@@ -0,1 +1,2 @@

'use strict'

@@ -6,5 +7,5 @@ /**

var Speaker = require('../');
const Speaker = require('../')
var speaker = new Speaker();
process.stdin.pipe(speaker);
const speaker = new Speaker()
process.stdin.pipe(speaker)

@@ -0,1 +1,2 @@

'use strict'

@@ -6,348 +7,344 @@ /**

var os = require('os');
var debug = require('debug')('speaker');
var binding = require('bindings')('binding');
var inherits = require('util').inherits;
var Writable = require('readable-stream/writable');
const os = require('os')
const debug = require('debug')('speaker')
const binding = require('bindings')('binding')
const bufferAlloc = require('buffer-alloc')
const Writable = require('readable-stream/writable')
// determine the native host endianness, the only supported playback endianness
var endianness = 'function' == os.endianness ?
os.endianness() :
'LE'; // assume little-endian for older versions of node.js
const endianness = os.endianness()
/**
* Module exports.
* The `Speaker` class accepts raw PCM data written to it, and then sends that data
* to the default output device of the OS.
*
* @param {Object} opts options object
* @api public
*/
exports = module.exports = Speaker;
class Speaker extends Writable {
constructor (opts) {
// default lwm and hwm to 0
if (!opts) opts = {}
if (opts.lowWaterMark == null) opts.lowWaterMark = 0
if (opts.highWaterMark == null) opts.highWaterMark = 0
/**
* Export information about the `mpg123_module_t` being used.
*/
super(opts)
exports.api_version = binding.api_version;
exports.description = binding.description;
exports.module_name = binding.name;
// chunks are sent over to the backend in "samplesPerFrame * blockAlign" size.
// this is necessary because if we send too big of chunks at once, then there
// won't be any data ready when the audio callback comes (experienced with the
// CoreAudio backend)
this.samplesPerFrame = 1024
/**
* Returns the `MPG123_ENC_*` constant that corresponds to the given "format"
* object, or `null` if the format is invalid.
*
* @param {Object} format - format object with `channels`, `sampleRate`, `bitDepth`, etc.
* @return {Number} MPG123_ENC_* constant, or `null`
* @api public
*/
// the `audio_output_t` struct pointer Buffer instance
this.audio_handle = null
exports.getFormat = function getFormat (format) {
var f = null;
if (format.bitDepth == 32 && format.float && format.signed) {
f = binding.MPG123_ENC_FLOAT_32;
} else if (format.bitDepth == 64 && format.float && format.signed) {
f = binding.MPG123_ENC_FLOAT_64;
} else if (format.bitDepth == 8 && format.signed) {
f = binding.MPG123_ENC_SIGNED_8;
} else if (format.bitDepth == 8 && !format.signed) {
f = binding.MPG123_ENC_UNSIGNED_8;
} else if (format.bitDepth == 16 && format.signed) {
f = binding.MPG123_ENC_SIGNED_16;
} else if (format.bitDepth == 16 && !format.signed) {
f = binding.MPG123_ENC_UNSIGNED_16;
} else if (format.bitDepth == 24 && format.signed) {
f = binding.MPG123_ENC_SIGNED_24;
} else if (format.bitDepth == 24 && !format.signed) {
f = binding.MPG123_ENC_UNSIGNED_24;
} else if (format.bitDepth == 32 && format.signed) {
f = binding.MPG123_ENC_SIGNED_32;
} else if (format.bitDepth == 32 && !format.signed) {
f = binding.MPG123_ENC_UNSIGNED_32;
// flipped after close() is called, no write() calls allowed after
this._closed = false
// set PCM format
this._format(opts)
// bind event listeners
this._format = this._format.bind(this)
this.on('finish', this._flush)
this.on('pipe', this._pipe)
this.on('unpipe', this._unpipe)
}
return f;
}
/**
* Returns `true` if the given "format" is playable via the "output module"
* that was selected during compilation, or `false` if not playable.
*
* @param {Number} format - MPG123_ENC_* format constant
* @return {Boolean} true if the format is playable, false otherwise
* @api public
*/
/**
* Calls the audio backend's `open()` function, and then emits an "open" event.
*
* @api private
*/
exports.isSupported = function isSupported (format) {
if ('number' !== typeof format) format = exports.getFormat(format);
return (binding.formats & format) === format;
}
_open () {
debug('open()')
if (this.audio_handle) {
throw new Error('_open() called more than once!')
}
// set default options, if not set
if (this.channels == null) {
debug('setting default %o: %o', 'channels', 2)
this.channels = 2
}
if (this.bitDepth == null) {
const depth = this.float ? 32 : 16
debug('setting default %o: %o', 'bitDepth', depth)
this.bitDepth = depth
}
if (this.sampleRate == null) {
debug('setting default %o: %o', 'sampleRate', 44100)
this.sampleRate = 44100
}
if (this.signed == null) {
debug('setting default %o: %o', 'signed', this.bitDepth !== 8)
this.signed = this.bitDepth !== 8
}
/**
* The `Speaker` class accepts raw PCM data written to it, and then sends that data
* to the default output device of the OS.
*
* @param {Object} opts options object
* @api public
*/
const format = Speaker.getFormat(this)
if (format == null) {
throw new Error('invalid PCM format specified')
}
function Speaker (opts) {
if (!(this instanceof Speaker)) return new Speaker(opts);
if (!Speaker.isSupported(format)) {
throw new Error(`specified PCM format is not supported by "${binding.name}" backend`)
}
// default lwm and hwm to 0
if (!opts) opts = {};
if (null == opts.lowWaterMark) opts.lowWaterMark = 0;
if (null == opts.highWaterMark) opts.highWaterMark = 0;
// calculate the "block align"
this.blockAlign = this.bitDepth / 8 * this.channels
Writable.call(this, opts);
// initialize the audio handle
// TODO: open async?
this.audio_handle = bufferAlloc(binding.sizeof_audio_output_t)
const r = binding.open(this.audio_handle, this.channels, this.sampleRate, format)
if (r !== 0) {
throw new Error(`open() failed: ${r}`)
}
// chunks are sent over to the backend in "samplesPerFrame * blockAlign" size.
// this is necessary because if we send too big of chunks at once, then there
// won't be any data ready when the audio callback comes (experienced with the
// CoreAudio backend)
this.samplesPerFrame = 1024;
this.emit('open')
return this.audio_handle
}
// the `audio_output_t` struct pointer Buffer instance
this.audio_handle = null;
/**
* Set given PCM formatting options. Called during instantiation on the passed in
* options object, on the stream given to the "pipe" event, and a final time if
* that stream emits a "format" event.
*
* @param {Object} opts
* @api private
*/
// flipped after close() is called, no write() calls allowed after
this._closed = false;
_format (opts) {
debug('format(object keys = %o)', Object.keys(opts))
if (opts.channels != null) {
debug('setting %o: %o', 'channels', opts.channels)
this.channels = opts.channels
}
if (opts.bitDepth != null) {
debug('setting %o: %o', 'bitDepth', opts.bitDepth)
this.bitDepth = opts.bitDepth
}
if (opts.sampleRate != null) {
debug('setting %o: %o', 'sampleRate', opts.sampleRate)
this.sampleRate = opts.sampleRate
}
if (opts.float != null) {
debug('setting %o: %o', 'float', opts.float)
this.float = opts.float
}
if (opts.signed != null) {
debug('setting %o: %o', 'signed', opts.signed)
this.signed = opts.signed
}
if (opts.samplesPerFrame != null) {
debug('setting %o: %o', 'samplesPerFrame', opts.samplesPerFrame)
this.samplesPerFrame = opts.samplesPerFrame
}
if (opts.endianness == null || endianness === opts.endianness) {
// no "endianness" specified or explicit native endianness
this.endianness = endianness
} else {
// only native endianness is supported...
this.emit('error', new Error(`only native endianness ("${endianness}") is supported, got "${opts.endianness}"`))
}
}
// set PCM format
this._format(opts);
/**
* `_write()` callback for the Writable base class.
*
* @param {Buffer} chunk
* @param {String} encoding
* @param {Function} done
* @api private
*/
// bind event listeners
this._format = this._format.bind(this);
this.on('finish', this._flush);
this.on('pipe', this._pipe);
this.on('unpipe', this._unpipe);
}
inherits(Speaker, Writable);
_write (chunk, encoding, done) {
debug('_write() (%o bytes)', chunk.length)
/**
* Calls the audio backend's `open()` function, and then emits an "open" event.
*
* @api private
*/
if (this._closed) {
// close() has already been called. this should not be called
return done(new Error('write() call after close() call'))
}
let b
let left = chunk
let handle = this.audio_handle
if (!handle) {
// this is the first time write() is being called; need to _open()
try {
handle = this._open()
} catch (e) {
return done(e)
}
}
const chunkSize = this.blockAlign * this.samplesPerFrame
Speaker.prototype._open = function () {
debug('open()');
if (this.audio_handle) {
throw new Error('_open() called more than once!');
}
// set default options, if not set
if (null == this.channels) {
debug('setting default %o: %o', 'channels', 2);
this.channels = 2;
}
if (null == this.bitDepth) {
var depth = this.float ? 32 : 16;
debug('setting default %o: %o', 'bitDepth', depth);
this.bitDepth = depth;
}
if (null == this.sampleRate) {
debug('setting default %o: %o', 'sampleRate', 44100);
this.sampleRate = 44100;
}
if (null == this.signed) {
debug('setting default %o: %o', 'signed', this.bitDepth != 8);
this.signed = this.bitDepth != 8;
}
const write = () => {
if (this._closed) {
debug('aborting remainder of write() call (%o bytes), since speaker is `_closed`', left.length)
return done()
}
b = left
if (b.length > chunkSize) {
const t = b
b = t.slice(0, chunkSize)
left = t.slice(chunkSize)
} else {
left = null
}
debug('writing %o byte chunk', b.length)
binding.write(handle, b, b.length, onwrite)
}
var format = exports.getFormat(this);
if (null == format) {
throw new Error('invalid PCM format specified');
const onwrite = (r) => {
debug('wrote %o bytes', r)
if (r !== b.length) {
done(new Error(`write() failed: ${r}`))
} else if (left) {
debug('still %o bytes left in this chunk', left.length)
write()
} else {
debug('done with this chunk')
done()
}
}
write()
}
if (!exports.isSupported(format)) {
throw new Error('specified PCM format is not supported by "' + binding.name + '" backend');
/**
* Called when this stream is pipe()d to from another readable stream.
* If the "sampleRate", "channels", "bitDepth", and "signed" properties are
* set, then they will be used over the currently set values.
*
* @api private
*/
_pipe (source) {
debug('_pipe()')
this._format(source)
source.once('format', this._format)
}
// calculate the "block align"
this.blockAlign = this.bitDepth / 8 * this.channels;
/**
* Called when this stream is pipe()d to from another readable stream.
* If the "sampleRate", "channels", "bitDepth", and "signed" properties are
* set, then they will be used over the currently set values.
*
* @api private
*/
// initialize the audio handle
// TODO: open async?
this.audio_handle = new Buffer(binding.sizeof_audio_output_t);
var r = binding.open(this.audio_handle, this.channels, this.sampleRate, format);
if (0 !== r) {
throw new Error('open() failed: ' + r);
_unpipe (source) {
debug('_unpipe()')
source.removeListener('format', this._format)
}
this.emit('open');
return this.audio_handle;
};
/**
* Emits a "flush" event and then calls the `.close()` function on
* this Speaker instance.
*
* @api private
*/
/**
* Set given PCM formatting options. Called during instantiation on the passed in
* options object, on the stream given to the "pipe" event, and a final time if
* that stream emits a "format" event.
*
* @param {Object} opts
* @api private
*/
Speaker.prototype._format = function (opts) {
debug('format(object keys = %o)', Object.keys(opts));
if (null != opts.channels) {
debug('setting %o: %o', 'channels', opts.channels);
this.channels = opts.channels;
_flush () {
debug('_flush()')
this.emit('flush')
this.close(false)
}
if (null != opts.bitDepth) {
debug('setting %o: %o', "bitDepth", opts.bitDepth);
this.bitDepth = opts.bitDepth;
}
if (null != opts.sampleRate) {
debug('setting %o: %o', "sampleRate", opts.sampleRate);
this.sampleRate = opts.sampleRate;
}
if (null != opts.float) {
debug('setting %o: %o', "float", opts.float);
this.float = opts.float;
}
if (null != opts.signed) {
debug('setting %o: %o', "signed", opts.signed);
this.signed = opts.signed;
}
if (null != opts.samplesPerFrame) {
debug('setting %o: %o', "samplesPerFrame", opts.samplesPerFrame);
this.samplesPerFrame = opts.samplesPerFrame;
}
if (null == opts.endianness || endianness == opts.endianness) {
// no "endianness" specified or explicit native endianness
this.endianness = endianness;
} else {
// only native endianness is supported...
this.emit('error', new Error('only native endianness ("' + endianness + '") is supported, got "' + opts.endianness + '"'));
}
};
/**
* `_write()` callback for the Writable base class.
*
* @param {Buffer} chunk
* @param {String} encoding
* @param {Function} done
* @api private
*/
/**
* Closes the audio backend. Normally this function will be called automatically
* after the audio backend has finished playing the audio buffer through the
* speakers.
*
* @param {Boolean} flush - if `false`, then don't call the `flush()` native binding call. Defaults to `true`.
* @api public
*/
Speaker.prototype._write = function (chunk, encoding, done) {
debug('_write() (%o bytes)', chunk.length);
close (flush) {
debug('close(%o)', flush)
if (this._closed) return debug('already closed...')
if (this._closed) {
// close() has already been called. this should not be called
return done(new Error('write() call after close() call'));
}
var b;
var self = this;
var left = chunk;
var handle = this.audio_handle;
if (!handle) {
// this is the first time write() is being called; need to _open()
try {
handle = this._open();
} catch (e) {
return done(e);
}
}
var chunkSize = this.blockAlign * this.samplesPerFrame;
if (this.audio_handle) {
if (flush !== false) {
// TODO: async most likely…
debug('invoking flush() native binding')
binding.flush(this.audio_handle)
}
function write () {
if (self._closed) {
debug('aborting remainder of write() call (%o bytes), since speaker is `_closed`', left.length);
return done();
}
b = left;
if (b.length > chunkSize) {
var t = b;
b = t.slice(0, chunkSize);
left = t.slice(chunkSize);
// TODO: async maybe?
debug('invoking close() native binding')
binding.close(this.audio_handle)
this.audio_handle = null
} else {
left = null;
debug('not invoking flush() or close() bindings since no `audio_handle`')
}
debug('writing %o byte chunk', b.length);
binding.write(handle, b, b.length, onwrite);
}
function onwrite (r) {
debug('wrote %o bytes', r);
if (r != b.length) {
done(new Error('write() failed: ' + r));
} else if (left) {
debug('still %o bytes left in this chunk', left.length);
write();
} else {
debug('done with this chunk');
done();
}
this._closed = true
this.emit('close')
}
}
write();
};
/**
* Called when this stream is pipe()d to from another readable stream.
* If the "sampleRate", "channels", "bitDepth", and "signed" properties are
* set, then they will be used over the currently set values.
*
* @api private
* Export information about the `mpg123_module_t` being used.
*/
Speaker.prototype._pipe = function (source) {
debug('_pipe()');
this._format(source);
source.once('format', this._format);
};
Speaker.api_version = binding.api_version
Speaker.description = binding.description
Speaker.module_name = binding.name
/**
* Called when this stream is pipe()d to from another readable stream.
* If the "sampleRate", "channels", "bitDepth", and "signed" properties are
* set, then they will be used over the currently set values.
* Returns the `MPG123_ENC_*` constant that corresponds to the given "format"
* object, or `null` if the format is invalid.
*
* @api private
* @param {Object} format - format object with `channels`, `sampleRate`, `bitDepth`, etc.
* @return {Number} MPG123_ENC_* constant, or `null`
* @api public
*/
Speaker.prototype._unpipe = function (source) {
debug('_unpipe()');
source.removeListener('format', this._format);
};
Speaker.getFormat = function getFormat (format) {
if (Number(format.bitDepth) === 32 && format.float && format.signed) {
return binding.MPG123_ENC_FLOAT_32
} else if (Number(format.bitDepth) === 64 && format.float && format.signed) {
return binding.MPG123_ENC_FLOAT_64
} else if (Number(format.bitDepth) === 8 && format.signed) {
return binding.MPG123_ENC_SIGNED_8
} else if (Number(format.bitDepth) === 8 && !format.signed) {
return binding.MPG123_ENC_UNSIGNED_8
} else if (Number(format.bitDepth) === 16 && format.signed) {
return binding.MPG123_ENC_SIGNED_16
} else if (Number(format.bitDepth) === 16 && !format.signed) {
return binding.MPG123_ENC_UNSIGNED_16
} else if (Number(format.bitDepth) === 24 && format.signed) {
return binding.MPG123_ENC_SIGNED_24
} else if (Number(format.bitDepth) === 24 && !format.signed) {
return binding.MPG123_ENC_UNSIGNED_24
} else if (Number(format.bitDepth) === 32 && format.signed) {
return binding.MPG123_ENC_SIGNED_32
} else if (Number(format.bitDepth) === 32 && !format.signed) {
return binding.MPG123_ENC_UNSIGNED_32
} else {
return null
}
}
/**
* Emits a "flush" event and then calls the `.close()` function on
* this Speaker instance.
* Returns `true` if the given "format" is playable via the "output module"
* that was selected during compilation, or `false` if not playable.
*
* @api private
* @param {Number} format - MPG123_ENC_* format constant
* @return {Boolean} true if the format is playable, false otherwise
* @api public
*/
Speaker.prototype._flush = function () {
debug('_flush()');
this.emit('flush');
this.close(false);
};
Speaker.isSupported = function isSupported (format) {
if (typeof format !== 'number') format = Speaker.getFormat(format)
return (binding.formats & format) === format
}
/**
* Closes the audio backend. Normally this function will be called automatically
* after the audio backend has finished playing the audio buffer through the
* speakers.
*
* @param {Boolean} flush - if `false`, then don't call the `flush()` native binding call. Defaults to `true`.
* @api public
* Module exports.
*/
Speaker.prototype.close = function (flush) {
debug('close(%o)', flush);
if (this._closed) return debug('already closed...');
if (this.audio_handle) {
if (false !== flush) {
// TODO: async most likely…
debug('invoking flush() native binding');
binding.flush(this.audio_handle);
}
// TODO: async maybe?
debug('invoking close() native binding');
binding.close(this.audio_handle);
this.audio_handle = null;
} else {
debug('not invoking flush() or close() bindings since no `audio_handle`');
}
this._closed = true;
this.emit('close');
};
exports = module.exports = Speaker
{
"name": "speaker",
"version": "0.4.0",
"license": "MIT",
"description": "Output PCM audio data to the speakers",
"author": "Nathan Rajlich <nathan@tootallnate.net> (http://tootallnate.net)",
"repository": "TooTallNate/node-speaker",
"scripts": {
"test": "standard && node-gyp rebuild --mpg123-backend=dummy && mocha --reporter spec"
},
"dependencies": {
"bindings": "^1.3.0",
"buffer-alloc": "^1.1.0",
"debug": "^3.0.1",
"nan": "^2.6.2",
"readable-stream": "^2.3.3"
},
"devDependencies": {
"mocha": "^3.5.0",
"standard": "^10.0.3"
},
"engines": {
"node": ">4"
},
"keywords": [

@@ -21,27 +42,3 @@ "pcm",

"mpg123"
],
"license": "MIT",
"version": "0.3.1",
"author": "Nathan Rajlich <nathan@tootallnate.net> (http://tootallnate.net)",
"repository": {
"type": "git",
"url": "git://github.com/TooTallNate/node-speaker.git"
},
"bugs": {
"url": "https://github.com/TooTallNate/node-speaker/issues"
},
"homepage": "https://github.com/TooTallNate/node-speaker",
"main": "./index.js",
"scripts": {
"test": "node-gyp rebuild --mpg123-backend=dummy && mocha --reporter spec"
},
"dependencies": {
"bindings": "^1.2.1",
"debug": "^2.2.0",
"nan": "^2.2.0",
"readable-stream": "^2.0.5"
},
"devDependencies": {
"mocha": "^2.1.0"
}
]
}

@@ -1,8 +0,8 @@

node-speaker
============
### Output [PCM audio][pcm] data to the speakers
# node-speaker
## Output [PCM audio][pcm] data to the speakers
[![Build Status](https://secure.travis-ci.org/TooTallNate/node-speaker.svg)](https://travis-ci.org/TooTallNate/node-speaker)
[![Build Status](https://ci.appveyor.com/api/projects/status/wix7wml3v55670kw?svg=true)](https://ci.appveyor.com/project/TooTallNate/node-speaker)
A Writable stream instance that accepts [PCM audio][pcm] data and outputs it

@@ -13,10 +13,8 @@ to the speakers. The output is backed by `mpg123`'s audio output modules, which

## Installation
Installation
------------
Simply compile and install `node-speaker` using `npm`:
``` bash
$ npm install speaker
```sh
npm install speaker
```

@@ -27,18 +25,16 @@

``` bash
$ sudo apt-get install libasound2-dev
```sh
sudo apt-get install libasound2-dev
```
## Example
Example
-------
Here's an example of piping `stdin` to the speaker, which should be 2 channel,
16-bit audio at 44,100 samples per second (a.k.a CD quality audio).
``` javascript
var Speaker = require('speaker');
```javascript
const Speaker = require('speaker');
// Create the Speaker instance
var speaker = new Speaker({
const speaker = new Speaker({
channels: 2, // 2 channels

@@ -53,10 +49,8 @@ bitDepth: 16, // 16-bit samples

## API
API
---
`require('speaker')` directly returns the `Speaker` constructor. It is the only
interface exported by `node-speaker`.
### new Speaker([ format ]) -> Speaker instance;
### new Speaker([ format ]) -> Speaker instance

@@ -67,8 +61,8 @@ Creates a new `Speaker` instance, which is a writable stream that you can pipe

* `channels` - The number of audio channels. PCM data must be interleaved. Defaults to `2`.
* `bitDepth` - The number of bits per sample. Defaults to `16` (16-bit).
* `sampleRate` - The number of samples per second per channel. Defaults to `44100`.
* `signed` - Boolean specifying if the samples are signed or unsigned. Defaults to `true` when bit depth is 8-bit, `false` otherwise.
* `float` - Boolean specifying if the samples are floating-point values. Defaults to `false`.
* `samplesPerFrame` - The number of samples to send to the audio backend at a time. You likely don't need to mess with this value. Defaults to `1024`.
* `channels` - The number of audio channels. PCM data must be interleaved. Defaults to `2`.
* `bitDepth` - The number of bits per sample. Defaults to `16` (16-bit).
* `sampleRate` - The number of samples per second per channel. Defaults to `44100`.
* `signed` - Boolean specifying if the samples are signed or unsigned. Defaults to `true` when bit depth is 8-bit, `false` otherwise.
* `float` - Boolean specifying if the samples are floating-point values. Defaults to `false`.
* `samplesPerFrame` - The number of samples to send to the audio backend at a time. You likely don't need to mess with this value. Defaults to `1024`.

@@ -90,6 +84,4 @@ #### "open" event

## Audio Backend Selection
Audio Backend Selection
-----------------------
`node-speaker` is backed by `mpg123`'s "output modules", which in turn use one of

@@ -109,4 +101,4 @@ many popular audio backends like ALSA, OSS, SDL, and lots more. The default

``` bash
$ npm install speaker --mpg123-backend=openal
```sh
npm install speaker --mpg123-backend=openal
```

@@ -113,0 +105,0 @@

@@ -0,2 +1,5 @@

/* eslint-env mocha */
'use strict'
/**

@@ -6,116 +9,116 @@ * Module dependencies.

var os = require('os');
var assert = require('assert');
var Speaker = require('../');
var endianness = 'function' == os.endianness ? os.endianness() : 'LE';
var opposite = endianness == 'LE' ? 'BE' : 'LE';
const os = require('os')
const assert = require('assert')
const bufferAlloc = require('buffer-alloc')
const Speaker = require('../')
const endianness = os.endianness()
const opposite = endianness === 'LE' ? 'BE' : 'LE'
describe('exports', function () {
it('should export a Function', function () {
assert.equal('function', typeof Speaker);
});
assert.equal('function', typeof Speaker)
})
it('should have an "api_version" property', function () {
assert(Speaker.hasOwnProperty('api_version'));
assert('number', typeof Speaker.api_version);
});
assert(Speaker.hasOwnProperty('api_version'))
assert('number', typeof Speaker.api_version)
})
it('should have a "description" property', function () {
assert(Speaker.hasOwnProperty('description'));
assert('string', typeof Speaker.description);
});
assert(Speaker.hasOwnProperty('description'))
assert('string', typeof Speaker.description)
})
it('should have a "module_name" property', function () {
assert(Speaker.hasOwnProperty('module_name'));
assert('string', typeof Speaker.module_name);
});
assert(Speaker.hasOwnProperty('module_name'))
assert('string', typeof Speaker.module_name)
})
})
});
describe('Speaker', function () {
it('should return a Speaker instance', function () {
var s = new Speaker();
assert(s instanceof Speaker);
});
const s = new Speaker()
assert(s instanceof Speaker)
})
it('should be a writable stream', function () {
var s = new Speaker();
assert.equal(s.writable, true);
assert.notEqual(s.readable, true);
});
const s = new Speaker()
assert.equal(s.writable, true)
assert.notEqual(s.readable, true)
})
it('should emit an "open" event after the first write()', function (done) {
var s = new Speaker();
var called = false;
const s = new Speaker()
let called = false
s.on('open', function () {
called = true;
done();
});
assert.equal(called, false);
s.write(Buffer(0));
});
called = true
done()
})
assert.equal(called, false)
s.write(bufferAlloc(0))
})
it('should emit a "flush" event after end()', function (done) {
var s = new Speaker();
var called = false;
const s = new Speaker()
let called = false
s.on('flush', function () {
called = true;
done();
});
assert.equal(called, false);
s.end(Buffer(0));
});
called = true
done()
})
assert.equal(called, false)
s.end(bufferAlloc(0))
})
it('should emit a "close" event after end()', function (done) {
this.slow(1000);
var s = new Speaker();
var called = false;
this.slow(1000)
const s = new Speaker()
let called = false
s.on('close', function () {
called = true;
done();
});
assert.equal(called, false);
s.end(Buffer(0));
});
called = true
done()
})
assert.equal(called, false)
s.end(bufferAlloc(0))
})
it('should only emit one "close" event', function (done) {
var s = new Speaker();
var count = 0;
const s = new Speaker()
let count = 0
s.on('close', function () {
count++;
});
assert.equal(0, count);
s.close();
assert.equal(1, count);
s.close();
assert.equal(1, count);
done();
});
count++
})
assert.equal(0, count)
s.close()
assert.equal(1, count)
s.close()
assert.equal(1, count)
done()
})
it('should not throw an Error if native "endianness" is specified', function () {
assert.doesNotThrow(function () {
new Speaker({ endianness: endianness });
});
});
// eslint-disable-next-line no-new
new Speaker({ endianness: endianness })
})
})
it('should throw an Error if non-native "endianness" is specified', function () {
assert.throws(function () {
new Speaker({ endianness: opposite });
});
});
// eslint-disable-next-line no-new
new Speaker({ endianness: opposite })
})
})
it('should throw an Error if a non-supported "format" is specified', function (done) {
var speaker = new Speaker({
const speaker = new Speaker({
bitDepth: 31,
signed: true
});
speaker.once('error', function (err) {
assert.equal('invalid PCM format specified', err.message);
done();
});
speaker.write('a');
});
});
})
speaker.once('error', (err) => {
assert.equal('invalid PCM format specified', err.message)
done()
})
speaker.write('a')
})
})

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc