SoundTouchJS icon indicating copy to clipboard operation
SoundTouchJS copied to clipboard

How to render audio buffer in offline?

Open floydback opened this issue 6 years ago • 10 comments

Great work! Seems it's work even faster then it's old version by https://github.com/also

I try to render audio with OfflineAudioContext and it's work fine on Chrome and Opera. But Safari and Firefox rendering silence (with same duration). I used OfflineAudioContext but may be there is another way to do this?

ps I suppose the problem is in getWebAudioNode when using OfflineAudioContext.startRendering

floydback avatar Mar 28 '19 00:03 floydback

The problem is with Safari/Firefox has a bug with OfflineAudioContext + ScriptProcessorNode After startRendering renderedBuffer is empty

https://stackoverflow.com/questions/47048141/dont-work-scriptprocessornode-with-offlinecontext

floydback avatar Mar 29 '19 13:03 floydback

@floydback were you ever able to get this working with OfflineAudioContext?

jarbacoa avatar Mar 31 '20 23:03 jarbacoa

@sharad-s yes, it works fine with Chrome/Opera. To make audio render in Safari/Firefox I had to use web workers.

floydback avatar Apr 01 '20 01:04 floydback

@floydback Do you have an implementation I can look through?

Been spending a lot of time looking through various implementations of Soundtouch, all of them use AudioContext.

My end-goal is to have a "click-to-download" button that would instantaneously render the time/pitchshifted audio as an mp3/wav. It looks like OfflineAudioContext is what I will need to make that happen.

An example would be much appreciated!

jarbacoa avatar Apr 01 '20 01:04 jarbacoa

@floydback any help would be much appreciated, I am pretty lost on integrating SoundTouch with an OfflineAudioContext 😔

jarbacoa avatar Apr 02 '20 07:04 jarbacoa

@floydback Hello, could you post your implementation of SoundTouchJS for web workers?

watch-janick avatar Apr 07 '20 14:04 watch-janick

How can I export the edited audio to a file

shuado avatar Nov 06 '20 11:11 shuado

Thanks for the great work.

I finally got soudtouchjs to work as an intermediate ScriptProcessornode, and render the source with OfflineAudioContext by adding two JS files (MyPitchShifter.js and MyFilter.js).

See the example if you are interested in it. I just started this project two weeks ago and there must be bugs though.

https://github.com/goto920/simple-mixer

Note: OfflineAudioContext does not work on iOS but worked on my Mac mini (Safari or Chrome). Export function is included in MyPitchShifter.js (in src/ ).

goto920 avatar Mar 09 '21 03:03 goto920

@goto920 Nice! Some good stuff in there.

cutterbl avatar Mar 09 '21 18:03 cutterbl

Hi there, I came up with a function that does this without the Web Audio API:

const EXTRA_LONG_BUFFER_LENGTH_SECS = 200;

const extraLongAudioBuffer = new AudioBuffer({
  numberOfChannels: 1,
  length: EXTRA_LONG_BUFFER_LENGTH_SECS * 31250,
  sampleRate: 31250
});

/**
 * Use SoundTouchJS to write a timestretched copy to destBuffer
 * @param {AudioBuffer} srcBuffer
 * @param {AudioBuffer} destBuffer
 * @param {number} stretchFactor
 */
function writeTimestretchedAudio(srcBuffer, destBuffer, stretchFactor) {
  extraLongAudioBuffer.copyToChannel(srcBuffer.getChannelData(0), 0);
  const source = new WebAudioBufferSource(extraLongAudioBuffer);

  const channelData = destBuffer.getChannelData(0);

  const soundTouch = new SoundTouch();
  soundTouch.tempo = stretchFactor;

  const filter = new SimpleFilter(source, soundTouch);

  const bufferSize = 4096;
  const tempBuffer = new Float32Array(bufferSize * 2);
  let frames = 0;
  let offset = 0;
  do {
    frames = filter.extract(tempBuffer, bufferSize);
    if (!frames) break;
    for (let i = 0; i < frames && offset + i < channelData.length; i++) {
      channelData[offset + i] = tempBuffer[i * 2]; // read left channel only
    }
    offset += frames;
  } while (frames && offset < channelData.length);
}

As you can see I'm copying my input buffer into a very large audio buffer before sending it to SoundTouchJS. I found that if I didn't do this, SoundTouch would stop working early and the end of my timestretched audio buffer would be silence. I spent awhile debugging but couldn't really figure out why.

I assume that in the Web Audio context, soundtouch is assuming that all the audio will make it through eventually. Is it possible to force Soundtouch to finish working without sending a giant buffer?

benwiley4000 avatar Oct 20 '23 23:10 benwiley4000