TooTallNate / node-speaker

Output PCM audio data to the speakers
650 stars 147 forks source link

Getting `illegal hardware instruction` on speaker.end() #92

Open goibon opened 7 years ago

goibon commented 7 years ago

I have a script for playing a remote mp3 source through the Speaker module which is working fine. However if I want to stop playing the mp3 stream I am encountering two issues:

If I stop streaming the remote source, eg. by calling stream.pause() as in line 11 of the code below then stdout is flooded with a warning:

[../deps/mpg123/src/output/coreaudio.c:81] warning: Didn't have any audio data in callback (buffer underflow)

The warning in itself makes sense because I'm not providing it with any data anymore, but it is outputting it very frequently which is a big issue because I want to use it for CLI app.

If I attempt to end the speaker calling speaker.end() as in line 13 of the code below then I get the following error:

[1] 8950 illegal hardware instruction node index.js

Am I not supposed to call end() on the speaker instance and if not then how can I close it properly?

I'm running Node v7.2.0, npm v4.0.3 and Speaker v.0.3.0 on macOS v10.12.1

const request = require('request')
const lame = require('lame')
const Speaker = require('speaker')
const url = 'http://aasr-stream.iha.dk:9870/dtr.mp3'
var decoder = new lame.Decoder()
var speaker = new Speaker()

decoder.pipe(speaker)
var req = request.get(url)
var stream = req.pipe(decoder)

setTimeout(() => {
  stream.pause()
  stream.unpipe()
  stream.end()
  speaker.end()
}, 1000)
Capevace commented 7 years ago

Having the same issue... Any solutions?

ghost commented 7 years ago

same problem.. "Illegal Instruction 4" and the Node program just crashes and exits..

any ideas???

clarkdave commented 7 years ago

I'm still getting this error, in node 7.4 and 7.5. A hacky but functional workaround is to spawn a child node process which runs speaker. It doesn't matter then if the child node process exits with the error, as the calling process can ignore it.

I used the above approach in a sample app with AWS Polly, if it's useful to anyone: https://github.com/clarkdave/polly-say

Capevace commented 7 years ago

The Issue has to do with this libmpg123 version being quite old. Updating has not been done in a long time as #68 is still open.

domelvil commented 7 years ago

+1

cara commented 7 years ago

+1

netyaroze commented 7 years ago

+1

cara commented 7 years ago

My workaround:

var AudioStream = new Stream.Readable()
AudioStream._read = function () {}
AudioStream.pipe(Player)
...
if (data.AudioStream instanceof Buffer) {
  AudioStream.push(data.AudioStream)
}

instead of:

      if (data.AudioStream instanceof Buffer) {
        // Initiate the source
        var bufferStream = new Stream.PassThrough()
          // convert AudioStream into a readable stream
        bufferStream.end(data.AudioStream)
          // Pipe into Player
        bufferStream.pipe(Player)
      }

no more crashes

atishn commented 7 years ago

@cara

Your given solution ended up with this flood of error

[../deps/mpg123/src/output/coreaudio.c:81] warning: Didn't have any audio data in callback (buffer underflow)
[../deps/mpg123/src/output/coreaudio.c:81] warning: Didn't have any audio data in callback (buffer underflow)
[../deps/mpg123/src/output/coreaudio.c:81] warning: Didn't have any audio data in callback (buffer underflow)
[../deps/mpg123/src/output/coreaudio.c:81] warning: Didn't have any audio data in callback (buffer underflow)
F1LT3R commented 7 years ago

I'm noticing that sometimes this works and sometimes it does not. Maybe some kind of race condition in the way the binding is being used. I think maybe it's the buffer flushing not working correctly.

If you write the length of the buffer that gets written after speaker.close(true)...

deps/mpg123/src/output/coreaudio.o:

static int write_coreaudio(audio_output_t *ao, unsigned char *buf, int len)
{
    mpg123_coreaudio_t* ca = (mpg123_coreaudio_t*)ao->userptr;
    int written;

    /* If there is no room, then sleep for half the length of the FIFO */
    while (sfifo_space( &ca->fifo ) < len ) {
        warning("sleeping");
        usleep( (FIFO_DURATION/2) * 1000000 );
    }

    /* Store converted audio in ring buffer */
    written = sfifo_write( &ca->fifo, (char*)buf, len);
    warning2("%u %u", written, len)
[../deps/mpg123/src/output/coreaudio.c:264] warning: 4096 4096
[../deps/mpg123/src/output/coreaudio.c:264] warning: 4096 4096
[../deps/mpg123/src/output/coreaudio.c:264] warning: 1024 1024
[../deps/mpg123/src/output/coreaudio.c:264] warning: 4096 4096
[../deps/mpg123/src/output/coreaudio.c:258] warning: sleeping
[../deps/mpg123/src/output/coreaudio.c:286] warning: closing CORE AUDIO
[../deps/mpg123/src/output/coreaudio.c:264] warning: 4294967277 4096
[../deps/mpg123/src/output/coreaudio.c:267] warning: Failed to write audio to ring buffer

But when speaker.close(false)... I still get...

[../deps/mpg123/src/output/coreaudio.c:264] warning: 1024 1024
[../deps/mpg123/src/output/coreaudio.c:264] warning: 4096 4096
[../deps/mpg123/src/output/coreaudio.c:264] warning: 4096 4096
[../deps/mpg123/src/output/coreaudio.c:264] warning: 4096 4096
[../deps/mpg123/src/output/coreaudio.c:264] warning: 1536 1536
[../deps/mpg123/src/output/coreaudio.c:258] warning: sleeping
[../deps/mpg123/src/output/coreaudio.c:286] warning: closing CORE AUDIO
[../deps/mpg123/src/output/coreaudio.c:264] warning: 4096 4096
[1]    62834 illegal hardware instruction  node test2.js
F1LT3R commented 7 years ago

I'm beginning to think the problem might be in cordaudio.c. It seems like the buffer:written gets large when you flush it, and my best guess is that the usleep() time inwrite_coreaudio()is based on aFIFO_DURATION` that becomes the wrong amount of time when then buffer gets flushed.

You can get it working editing node_modules/speaker/deps/mpg123/src/output/coreaudio.c:258

-       usleep( (FIFO_DURATION/2) * 1000000 );
+       usleep( (FIFO_DURATION/2) * 100000 );

Then in the node_modules/speaker/ directory, run:

node-gyp build

And you should be able to use:

const request = require('request')
const lame = require('lame')
const Speaker = require('speaker')
const url = 'http://aasr-stream.iha.dk:9870/dtr.mp3'
const decoder = new lame.Decoder()
let speaker = new Speaker()

decoder.pipe(speaker)

const req = request.get(url)
var stream = req.pipe(decoder)

setTimeout(() => {
    console.log('Closing Speaker')
    speaker.close()
}, 2000)

setTimeout(() => {
    console.log('Closing Node')
}, 4000)
surajsharma commented 4 years ago

any recent workarounds?