scrapjs / analyser

:mag_right: Analyser stream
28 stars 6 forks source link

Lag when compared to analyserNode #2

Open meandavejustice opened 7 years ago

meandavejustice commented 7 years ago

I've been playing around with this module coupled with audio-source and I am experiencing some lag when compared to using the native analyserNode. The apis supplied by audio-source, audio-play, and this module are pretty intriguing (Streams!), but I'd have to get to the root of this issue. I see that there hasn't been a commit to this repo in about a year, and it is the "lab" org so no one is required to maintain this. I was wondering if maybe someone had an idea/hint at where to start tracking down the issue, as I'd be happy to dig in and get to the bottom of this myself.

Here is some code for comparison:

const Source = require('audio-source/stream');
const Speaker = require('audio-speaker/stream');
const lena = require('audio-lena/buffer');
const Analyser = require('audio-analyser');

const context = require('audio-context');
const createWaveform = require('gl-waveform');

const analyser = new Analyser();

let wfData = new Float32Array(analyser.fftSize);

const wf = createWaveform({
  container: document.querySelector('.waveform-container'),
  samples: analyser.getFloatTimeDomainData(wfData)
});

function loop() {
  requestAnimationFrame(loop);
  render();
}

function render() {
  if (!analyser) return;

  analyser.getFloatTimeDomainData(wfData);
  wf.push(wfData);
}

Source(lena).pipe(analyser).pipe(Speaker());
loop();

analyserNode version

const lena = require('audio-lena/buffer');
const context = require('audio-context');
const createWaveform = require('gl-waveform');

const analyser = context.createAnalyser();

let wfData = new Float32Array(analyser.fftSize);

const wf = createWaveform({
  container: document.querySelector('.waveform-container'),
  samples: analyser.getFloatTimeDomainData(wfData)
});

function loop() {
  requestAnimationFrame(loop);
  render();
}

function render() {
  if (!analyser) return;

  analyser.getFloatTimeDomainData(wfData);
  wf.push(wfData);
}

function init() {
  const source = context.createBufferSource();
  source.buffer = lena;
  source.connect(analyser);
  analyser.connect(context.destination);
  source.start();
  loop();
}

init();

(I tried creating require-bins for these, but was getting lots of server errors 😢 )

System facts

Firefox 53.0a2 (Dev edition) Linux 4.40-62-generic Ubuntu 16.04.1 LTS xenial

dy commented 7 years ago

Hi @meandavejustice! Thanks for the interest. Yes, this module is a bit outdated and takes refactoring.

I see 2 potential bottlenecks.

First is slow samples conversion here. @audiojs streams are built with audio-through and process AudioBuffer chunks, whereas analyser is regular stream and attempts processing plain Buffers with pcm-encoded unsigned-int samples. Audio-source therefore recognizes it as simple transform stream and tries to convert AudioBuffer to Buffer, analyser in turn reads samples from that Buffer (slow!), converts them to floats and ensures the size of chunk (kind of slow also, better process per-chunk).

Second issue is ndarray-fft used here, which is about 4 times slower than fourier-transform, considering ndarray creation etc.

In all, fixing these issues may help to make analyser a decent component appropriate for @audiojs.

dy commented 7 years ago

Also might be interesting in this regard comparison of various stream latencies in stream-contest.