Git Product home page Git Product logo

sox-stream's Introduction

sox-stream

A stream wrapper around SoX. Transcode audio streams easily!

Build Status

Why

The other implementations I found felt klunky to use. This module implements a streaming interface. (If you don't know how to use streams, I recommend reading the stream handbook.)

Examples

Simple transcode:

var sox = require('sox-stream')
var fs  = require('fs')

fs.createReadStream('song.wav')
	.pipe( sox({ output: { type: 'flac' } }) )
	.pipe( fs.createWriteStream('song.flac') )

Lower volume:

var sox = require('sox-stream')
var fs  = require('fs')

var src = fs.createReadStream('song.flac')
var lowerVolume = sox({ input: { volume: 0.8 }, output: { type: flac }})
var dest = fs.createWriteStream('song2.flac')

src.pipe(lowerVolume).pipe(dest)

Transcode with options and error handling:

var sox = require('sox-stream')
var fs  = require('fs')

var src = fs.createReadStream('song.ogg')
var transcode = sox({
	output: {
		bits: 16,
		rate: 44100,
		channels: 2,
		type: 'wav'
	}
})
var dest = fs.createWriteStream('song.wav')
src.pipe(transcode).pipe(dest)

transcode.on('error', function (err) {
	console.log('oh no! ' + err.message)
})

API

var sox = require('sox-stream')

var transform = sox(options)

  • options object required - The following parameters are supported:
    • soxPath string optional - The path to SoX. E.g. 'C:\Program Files\Sox\sox.exe'. Defaults to 'sox', which works if the SoX binary is in your path.
    • global object optional - Global SoX options
    • input object optional - These options will be used to interpret the incoming stream.
    • output object required - These options will be used to format the outgoing stream. When an output option isn't supplied, the output file will have the same format as the input file where possible. (Except type, which you must always pass in.)
    • effects string|array of strings/numbers optional
  • Returns transform, a stream to pipe data through. The stream emits 'error' events when there is an error.

options object

An object of options. Every option is optional except options.output.type is required.

If you want an exhaustive list of each SoX option in depth, take a look at the SoX documentation.

Internally, these options are transformed into the command-line arguments passed to a SoX child process.

options.global object|array of strings/numbers

You can supply an array of strings/numbers, or an object that will be transformed into an array of strings/numbers using hash-to-array.

Currently, sox-stream only supports one input file, so some of these options don't really make sense.

Command(s) Functionality
{ buffer: BYTES } Set the size of all processing buffers (default 8192)
{ combine: 'concatenate' } Concatenate all input files (default for sox, rec)
{ combine: 'sequence' } Sequence all input files (default for play)
'-m', { m: true }, { combine: 'mix' } Mix multiple input files (instead of concatenating)
{ combine: 'mix-power' } Mix to equal power (instead of concatenating)
'-M', { M: true }, { combine: 'merge' } Merge multiple input files (instead of concatenating)
'-T', { T: true }, { combine: 'multiply' } Multiply samples of corresponding channels from all input files (instead of concatenating)
'-D', { D: true }, { 'no-dither': true } Don't dither automatically
{ 'effects-file': FILENAME } File containing effects and options
'-G', { G: true }, { guard: true } Use temporary files to guard against clipping
{ input-buffer: BYTES } Override the input buffer size (default: same as --buffer; 8192)
'--norm', { norm: true } Guard (see --guard) & normalise
{ play-rate-arg: ARG } Default rate argument for auto-resample with `play'
{ plot: 'gnuplot', 'octave' } Generate script to plot response of filter effect
{ 'replay-gain': 'track', 'album', 'off' } Default: 'off' (sox, rec), track (play)
'-R', { R: true } Use default random numbers (same on each run of SoX)
'--single-threaded', { 'single-threaded': true } Disable parallel effects channels processing
{ temp: DIRECTORY } Specify the directory to use for temporary files
var transform = sox({
	global: {
		buffer: 4096,
		norm: true,
		'single-threaded': true
	},
	output: { type: 'ogg' }
})

options.input / options.output object|array of strings/numbers

You can supply an array of strings/numbers, or an object that will be transformed into an array of strings/numbers using hash-to-array.

Input Output Command(s) Functionality
X { v: FACTOR }, { volume: FACTOR } Input file volume adjustment factor (Floating point number between 0 and 1)
X { ignore-length: true } Ignore input file length given in header; read to EOF
{ t: FILETYPE }, { type: FILETYPE } Audio file type. E.g. 'wav', 'ogg'
{ e: ENCODING }, { encoding: ENCODING } ENCODING can be 'signed-integer', 'unsigned-integer', 'floating-point', 'mu-law', 'a-law', 'ima-adpcm', 'ms-adpcm', or 'gsm-full-rate'
'-b', { b: BITS }, { bits: BITS } Encoded sample size in bits, A.K.A. the bit depth. E.g. 16, 24. (Not applicable to complex encodings such as MP3 or GSM.)
'-N', { N: true }, { 'reverse-nibbles': true } Encoded nibble-order
'-X', { X: true }, { 'reverse-bits': true } Encoded bit-order
'-L', { endian: 'little' }, { L: true } Encoded byte-order: Little endian
'-B', { endian: 'big' }, { B: true } Encoded byte-order: Big endian
'-x', { endian: 'swap' }, { x: true } Encoded byte-order: swap means opposite to default
{ c: CHANNELS }, { channels: CHANNELS } Number of channels of audio data. E.g. 2 for stereo
'--no-glob', { 'no-glob': true } Don't `glob' wildcard match the following filename
{ r: RATE }, { rate: RATE } Sample rate of audio. E.g. 44100, 48000
X { C: FACTOR }, { compression: FACTOR } Compression factor. (Might be bitrate, depending on the file type.) See SoX format docs for more info.
X { 'add-comment': TEXT } Append output file comment
X { comment: TEXT } Specify comment text for the output file
X { 'comment-file': FILENAME } File containing comment text for the output file
var transform = sox({
	input: [
		[ '--volume', 1.1 ],
		[ '--endian', 'big' ],
		[ '--no-glob' ]
	],
	output: { type: 'ogg' }
})
// same as
var transform = sox({
	input: {
		volume: 1.1,
		endian: 'big',
		'no-glob': true
	],
	output: [
		'--type', 'ogg'
	]
})
// same as
var transform = sox({
	input: '--volume 1.1 --endian big --no-glob',
	output: '--type ogg'
})

options.effects string|array of strings/numbers/arrays

Please see the SoX Documentation on Effects to see all the options.

Pass them into sox-stream like this:

var transform = sox({
	input: { type: 'ogg' },
	output: { type: 'wav' },


	effects: 'speed 1.5 swap'
	// same as
	effects: [
		'speed 1.5 swap'
	]
	// same as
	effects: [
		'speed', 1.5,
		'swap'
	]
	// same as
	effects: [
		[ 'speed', '1.5' ],
		'swap'
	]
})

Install

  • Install SoX. You can download it, or you can do apt-get install sox
  • Install this package with npm: npm install sox-stream

Test

To run the tests, you must have SoX in your PATH. Then run: npm test

I run the tests using SoX 14.4.2, but other versions of SoX should work as well.

Codec Support

FLAC

  • Problem: FLAC was disabled accidentally in 14.4.1 (SourceForge default). [Stack Overflow]
  • Solution: Install SoX 14.4.2.

MP3

License

MIT

sox-stream's People

Contributors

artskydj avatar prior99 avatar rijnhard avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sox-stream's Issues

VBR on MP3 files

If I set the following parameters, SoX should use the best VBR and a low quality (I'm dealing with voice recordings).

output: {
  type: 'mp3',
  compression: -9.8
}

However, I get the following errors:

Error: unable to write VBR tag because we can't seek
Error: ENOENT: no such file or directory, unlink '/tmp/db9a3eba-3f09-45d0-886e-3f29ed214394'
Error: ENOENT: no such file or directory, unlink '/tmp/db9a3eba-3f09-45d0-886e-3f29ed214394'

So it looks like the temp file may be being unlinked at the wrong stage.

Is this a known issue? Is there any work-around?

Thanks.

sox-stream does not work with file descriptor

I'm trying to pass the 'raw' audio-stream from fd: to nodejs sox-stream, but transcode result are always empty, without any warn/errors messages ((

stream = fs.createReadStream(null, {fd: 3, autoClose: false }); 
var transcode = sox({
     global:  { guard: true,  'no-dither': true, norm: true, temp: '/tmpfs' },
     input:   { type: 'raw', 'ignore-length': true, encoding: 'signed-integer', bits: 16, rate: 8000, channels: 1 },
     output:  { type: 'raw' },
});
var dest  = fs.createWriteStream('/tmpfs/song.raw');
stream.pipe(transcode).pipe(dest);

But if I divide into 2 operations, all is well:

// 1 - 'from fd: to file'

stream.pipe(dest);

// 2 - 'from file to transcode'


 var src  = fs.createReadStream('/tmpfs/song.raw'); 
           src.pipe( transcode ).pipe(dest2);

Why sox-stream doesn't work with file descriptor (only with file) ? How to make it work? Thank you!

Problem with returning the object from require

In order to convert a bunch of objects, a paradigm like

SoX = require('sox-stream');
sox = new SoX(input_options, output_options, file);

where sox is a duplex stream.

Otherwise multiple calls to require('sox-stream'); are going to return the same stream.

How to pass additional parameter (ex. the "-s" for "sox tempo -s 0.5")

Hi,
Thank you for this project, it's super useful.
I'm trying to run "sox tempo -s 1.5" with the lib, but I can't manage to pass the "-s" parameter. It's working without it.
I know this parameter "-s" is working, because when I run it in the terminal it works: sox normal.mp3 slow.mp3 tempo -s 0.5

I feel I tried everything:

var transform = sox({

            input: { type: 'mp3' },

            output: { type: 'mp3' },

            effects: [//The −s option is used for speech processing.

                'tempo', speedCoeff //working but without the -s, it sounds very robotic

                //'tempo', 's', speedCoeff //not working, crash

                 //'tempo', '-s', speedCoeff //not working crash

                //'tempo -s '+speedCoeff //not working, hang

            ]
    });

Thanks in advance for your help

How to connect additional output streams?

Hi, thanks for maintaining this wrapper!
While it's obvious how to pipe audio input and audio output, how do I attach my streams for additional output like spectrogram or noiseprof?
I run node.js from within a bash shell, so I could probably solve the sox side by giving filenames like /dev/fd/14 and /dev/fd/15 (at least I hope that feature passes though node.js), but the wrapper would have to spawn the sox process with these file descriptiors connected to either my streams, or its own streams that I can pipe to mine.

spawn sox ENOENT

I'm trying to execute this code:

`exports.pipe = (res, assetUrl) => {
const r = request({
url: assetUrl,
method: "GET"
});

const transcode = sox({
	output: {
		bits: 16,
		rate: 44100,
		channels: 2,
		type: 'mp3'
	}
});

var lowerVolume = sox({ input: { volume: 0.2 }, output: { type: 'mp3' }})

r.on("error", function(e) {
	res.status(500).end("Ocorreu um erro interno enquanto tentava tocar a música.")
})	
r.pipe(lowerVolume).pipe(res);

}

sox: invalid option -- -

Hi,
I have been trying to convert a wav file to a ogg file, but is raising this next error

Excepcion encontrada: Error: sox: invalid option -- -
sox: invalid option -- -
sox: Unknown input file format for 'wav': File type 'ype' is not known

sox: Unknown input file format for 'wav':  File type 'ype' is not known 
at emitOne (events.js:77:13)
at Socket.emit (events.js:169:7)
at readableAddChunk (_stream_readable.js:153:18)
at Socket.Readable.push (_stream_readable.js:111:10)
at Pipe.onread (net.js:537:20)

The code I'm using is the next

` this.convert = (data) => {
return new Promise(function(resolve, reject) {
var wavFilePath = path.join(spoolPath, data.wavFile);
var convertedFile = path.join(spoolPath, data.filename);
var wavFileReader = null;

        fs.stat( wavFilePath, (err, stats) => {
            if (err) {
                reject(err);
            } else {
                wavFileReader = fs.createReadStream(wavFilePath);

                wavFileReader
                 .on('close', () => {
                     resolve(true);
                 })
                 .on('err', (err) => {
                     log && log.error("Transcode::convert: At reader file.", err);
                     reject(err);
                });

                wavFileReader
                 .pipe( sox({
                     input: {type: data.exten.toLowerCase() },
                     output: {type: 'vorbis'}
                 }) )
                 .pipe( fs.createWriteStream(convertedFile) )
                 .close();
            }
        });


    });
};

`

hope you can help me with this. If I try transcode manual using the sox commando I don't have a problem.

Thanks for you time.

Problem with MP3 files in OSX

I tried to give input file as MP3, which gives a odd error as

sox FAIL formats: can't determine type of file

After debuging and also checking through the issues noted at #14, I found out that sox will understand MIME type from extensions for MP3 file only.

when I try to do

soxi ./test.mp3

I get a valid response, but when I do

soxi ./test

(obviously I renamed the file to without an extension)

thats the same error I get, i.e.

sox FAIL formats: can't determine type of file

I have tested it with various mp3 files, even files from online sources as sample mp3 for sound testing. Tested many other formats as well, but the problem seems to come only for MP3, which leads

Now the problem after looking at the source code is, as all is done through streams, it is very hard to get extension unless the input is from fs.createWriteStream or fs.createReadStream as noted in here(stackoverflow).

For my use case, I have solved it by adding an extension to the the function call for createTempFile()
as static, but Please look into this, as it is a undocumented bug.

Pipe to on data

Hi there,
I'd really appreciate some pointers on the following setup:
I'm grabbing radio feeds with icy, checking the stream for codec details which I'm using to put together the sox input. The objective is to transcode to 8k mono wav to then feed to another setup via websocket.

Being a radio feed there is no EOF, the setup grabs portions of the audio to process on the fly for the next step to take action, but my on 'data' is only getting the buffers if I interrupt the process, meaning sox seems to be waiting for an EOF to pipe the whole temp file rather than letting it flow. Any ideas on how to accomplish this?

BTW I also tried just the same as the example, piping to a fs write stream, and got the same error: ENOENT: no such file or directory, unlink '...' :(

Error: sox WARN mp3-util

Hey!

Now I'm having this error on my ubuntu server

Error: sox WARN mp3-util:
at Socket.<anonymous> (/opt/bitnami/apache2/htdocs/node_modules/sox-stream/index.js:33:23)                                                           at Socket.emit (events.js:182:13)
main.js:442:20)
at addChunk (_stream_readable.js:277:12)
at readableAddChunk (_stream_readable.js:262:11)
at Socket.Readable.push (_stream_readable.js:217:10)
at Pipe.onread (net.js:638:20)                                     x
Error: ENOENT: no such file or directory, unlink '/tmp/490cd0d9-1eea-4bfe-a6de-6647b36c9ea2'
Error: ENOENT: no such file or directory, unlink '/tmp/490cd0d9-1eea-4bfe-a6de-6647b36c9ea2'

Segmentation fault in sox results in a zero-byte stream.

Transcoding a .vox file to .wav to be streamed to a browser to be played resulted in no content in the http response.

      var transcode = sox({
        // equivalent to: play -t raw -r 8000 -c 1 -e mu-law -b 8
        input: { type: 'raw', rate: 8000, encoding: 'mu-law', bits: 8, channels: 1 },
        output: { type: 'wav' }
      });
...
      new stream.PassThrough().end(data.Body)
      .on('error', err => reject(err))
      .pipe(transcode)
      .pipe(res);

It was discovered that the sox binary was causing a segmentation fault. However, the code did not trigger an error. Even having a transcode.on('error', handler) did not call the handler.

Although rare, a segmentation fault in sox library should trigger the 'error' event handler in the library.

nodejs: v12.8.0
sox-stream: v2.0.3
docker image: 12.8.0-alpine
sox: 14.4.2-r3

How create realtime reading stream ?

Hi.
I have createing radio stream.

in your exmple i see how convert file.

var src = fs.createReadStream('radio.mp3')
var transcode = sox({
    input: {
      type: 'radio.mp3'.split(".").pop()
    },
    output: {
        bits: 16,
        rate: 44100,
        channels: 2,
        type: 'wav'
    }
})
var dest = fs.createWriteStream('song.wav')
src.pipe(transcode).pipe(dest)

but how can I start playing the file, receive interest until the end of playback, samples if possible and transfer to the stream. So that people would open the channel and hear what is currently playing ?

sox FAIL formats: no handler for detected file type `opus'

I am currently struggling with Ogg Opus files I receive from a telegram bot. I already installed ffmpeg, opus_tools, libogg0 and libopus0, but it didn't help. I also downloaded the file and checked if it was corrupted, but it wasn't.
Any ideas on how to solve this?

Failure causes infinite loop

This line is causing an infinite loop.
The problem comes when a failure condition is created for example when being sent an mp3 file and telling sox it’s a wav file. (Aside from that being a bad thing, the module must handle it gracefully.)

Sox throws an error as it should.
The tmpFile does not exist so cleanup throws an error and you’re in an infinite loop.

function emitErr(err) {
    tmpFile.cleanup( duplex.emit.bind(duplex, 'error', err) )
}

The suggested way to deal with it is in this style, but forgive me for not knowing how to take the above line and implement it like the below line.

function emitErr(err) {
if (err.code !== 'ENOENT') { // Avoid infinite loops
tmpFile.cleanup(duplex.emit.bind(duplex, 'error', err));
}
}

Error: write EPIPE on use of trim effect

Sox seems to be exiting, or at least, the stdin stream is closing prematurely, whenever the following sox Options are used:

var soxOptions={ input: {type: 'mp3' }, output: {type: 'wav'}, effects: ['trim',10] };

Trimming any amount yields the same result. Bumping up the verbosity to -V3 yields no useful data with regard to a failing of the sox command line, but it's unclear whether this is an issue of Node's internal streams handling, something within Duplexer or sox-stream itself. The issue appears 100% reproducible on Linux and Mac though primary testing was done on Mac. Any guidance would be helpful. Thanks.

Streams & Pipe resulting in ENOENT

Hello

I am trying to implement on-the-fly transcoding using Sox in NodeJS, but the code is crashing with:

Error: spawn sox ENOENT at Process.ChildProcess._handle.onexit (internal/child_process.js:267:19) at onErrorNT (internal/child_process.js:469:16) at processTicksAndRejections (internal/process/task_queues.js:84:21) { errno: 'ENOENT', code: 'ENOENT', syscall: 'spawn sox', path: 'sox', spawnargs: [ '/tmp/981152e3-6091-4a32-a7e6-81f1b9157b90', '--type', 'flac', '-' ] }

The issue can be run directly from here : https://repl.it/@VianneyLejeune/Sox.

For your convenience, here is the code:

var sox = require('sox-stream'); // https://npm.io/package/sox-stream
var stream = require("stream");

function TranscodeAudioFromURL(url, callback) {
    var request = require('request');
    return new Promise((resolve, reject) => {
        request({
            url: url,
            encoding: null
        }, function(err, res, body) {
            if (!err && res.statusCode == 200) {
                const src = new stream.Readable(); // Stream for input
                const dest = new stream.Writable(); // Stream for output
                src.push(body); //push of the input audio file to the stream  
                src.push(null); // ending the stream
                src // Piping the output stream to the transcoder
                    .pipe(sox({
                        output: {
                            type: 'flac'
                        }
                    }))
                    .pipe(dest);               
                return resolve(dest); // return converted audio file as stream
            } else
                reject(err);
        });

    });
};

// Calling the transcoder
TranscodeAudioFromURL('https://s3.amazonaws.com/appforest_uf/f1611567418463x165993112458673100/ttsMP3.com_VoiceText_2021-1-25_10_25_1.mp3').then((outputStream) => {
    // "Success!"
    console.log("Transcoded binary"+JSON.stringify(outputStream))
}, reason => {
    // failure
    console.log(reason);
});

Am I missing something?

Get it works with bpm.js

Can you take a look at this?
monstercat/bpm.js#5

I'm trying to make this module works with bpm.js.

I'm trying to do with sox-stream the equivalent of this:

var spawn = require('child_process').spawn

function createAudioStream(filename) {
  var args = "-t raw -r 44100 -e float -c 1 -".split(" ")
  args.unshift(filename)
  var sox = spawn("sox", args)
  return sox.stdout
}

Empty file

Hi!

I'm trying to transcode an audio http stream from ogg to wav.

Here is my code:

const fs = require('fs')
const http = require('http')
const sox = require('sox-stream')

const transcode = sox({
  input: {
    type: 'ogg'
  },
  output: {
    type: 'wav',
    rate: 44100
  }
})

const dest = fs.createWriteStream('transcoded.wav')

http.get('http://listen.oma-radio.fr/paj.ogg', res => res.pipe(transcode).pipe(dest))

transcode.on('error', err => console.log('Transcode:', err))

The output file remain empty with no error message in the console.

I've tried to pipe directly the http stream to the file, it's working.

I've tried to convert a real ogg file from file system with this code:

const fs = require('fs')
const http = require('http')
const sox = require('sox-stream')

const transcode = sox({
  input: {
    type: 'ogg'
  },
  output: {
    type: 'wav',
    rate: 44100
  }
})

const src = fs.createReadStream('source.ogg')

const dest = fs.createWriteStream('transcoded.wav')

src.pipe(transcode).pipe(dest)

transcode.on('error', err => console.log('Transcode:', err))

I encounter the following errors in my terminal:

▶ npm start

> [email protected] start /run/media/nicolas/DATA/ovp/icecast-transcode
> node server.js

Transcode: Error: sox WARN wav: Length in output .wav header will be wrong since can't seek to fix it

    at Socket.<anonymous> (/run/media/nicolas/DATA/ovp/icecast-transcode/node_modules/sox-stream/index.js:33:23)
    at emitOne (events.js:115:13)
    at Socket.emit (events.js:210:7)
    at addChunk (_stream_readable.js:264:12)
    at readableAddChunk (_stream_readable.js:251:11)
    at Socket.Readable.push (_stream_readable.js:209:10)
    at Pipe.onread (net.js:587:20)
Transcode: { Error: ENOENT: no such file or directory, unlink '/tmp/ff65d55d-9f22-431e-836c-153c3fa5969a'
  errno: -2,
  code: 'ENOENT',
  syscall: 'unlink',
  path: '/tmp/ff65d55d-9f22-431e-836c-153c3fa5969a' }
Transcode: { Error: ENOENT: no such file or directory, unlink '/tmp/ff65d55d-9f22-431e-836c-153c3fa5969a'
  errno: -2,
  code: 'ENOENT',
  syscall: 'unlink',
  path: '/tmp/ff65d55d-9f22-431e-836c-153c3fa5969a' }

And the file re'mains empty

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.