Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to render audio buffer in offline? #1

Open
floydback opened this issue Mar 28, 2019 · 10 comments
Open

How to render audio buffer in offline? #1

floydback opened this issue Mar 28, 2019 · 10 comments

Comments

@floydback
Copy link

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
Copy link
Author

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

@jarbacoa
Copy link

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

@floydback
Copy link
Author

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

@jarbacoa
Copy link

jarbacoa commented Apr 1, 2020

@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
Copy link

jarbacoa commented Apr 2, 2020

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

@watch-janick
Copy link

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

@shuado
Copy link

shuado commented Nov 6, 2020

How can I export the edited audio to a file

@goto920
Copy link

goto920 commented Mar 9, 2021

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/ ).

@cutterbl
Copy link
Owner

cutterbl commented Mar 9, 2021

@goto920 Nice! Some good stuff in there.

@benwiley4000
Copy link

benwiley4000 commented Oct 20, 2023

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants