2

I enjoy using Howler.js for my (Meteor) application. However, the playback rate function is causing a pitch shift that I don't want (I just want the time stretch, and to preserve the pitch). My solution was to therefore implement a pitch shift to it to 'correct' the pitch. Seemed simple enough, which is why I chose to use https://tonejs.github.io/

Only problem is, I cannot for the life of me get it to work correctly. After hours of reading up on Web Audio API, and Tone.js documentation, and online discussion/troubleshoot forums, the closest to a potential solution I got was something like so (called during render of my application, just in case the issue had to do with loading prematurely):

Tone.setContext(Howler.ctx); //set tone's context to the Howler.js audiocontext
var pShift = new Tone.PitchShift(3); //create the PitchShift effect, +3 semi-tones transposition
pShift.context = Howler.ctx; //set the PitchShift's context to the Howler.js audiocontext
pShift.connect(Howler.ctx.destination); //connect the PitchShift's output to the Howler's destination
Howler.masterGain.connect(pShift); //connect the Howler's master GainNode output to the PitchShift effect

//For debugging purposes:
console.log(Howler.masterGain)
console.log(pShift);

When I do run this I get this error message:

Exception from Tracker afterFlush function: meteor.js?hash=857dafb4b9dff17e29ed8498a22ea5b1a3d6b41d:1059 TypeError: Failed to execute 'connect' on 'AudioNode': Overload resolution failed.

I also noticed that the console.log() commands below those don't even show up in the console, strangely enough. They do, however, when I remove the last line (mastergain.connect to the pShift).

I tried a few other techniques, such as https://github.com/mmckegg/soundbank-pitch-shift/ (which worked but it played both the pitch shifted sound and the non-pitch shifted sound, no matter what settings I put it at), Or simply using AudioBufferSourceNode.detune (I couldn't figure out how to get it to work with Howler.js because Howler only has functions that can expose the GainNode and the AudioContext, wasn't able to figure out how to read output from there while still using Howler).

Any help/leads would be greatly appreciated!

2 Answers2

3

I think you don't need the 3rd line in your snippet. Since your first line is telling Tone.js to use the AudioContext created by howler.js already. Therefore pShift.context should be equal to Howler.ctx. But it might make sense to double check.

console.assert(pShift.context === Howler.ctx);

The masterGain exposed by howler.js is a native audio node. That means it can't be connected to a node created with Tone.js directly since these are not native audio nodes. But Tone.js offers a helper to do that.

Tone.connect(Howler.masterGain, pShift);

I think you also need to call disconnect() on the masterGain to remove any existing connections.

The following snippet should work.

Tone.setContext(Howler.ctx);

const pShift = new Tone.PitchShift(3);

Howler.masterGain.disconnect();

Tone.connect(Howler.masterGain, pShift);
pShift.toDestination();
chrisguttandin
  • 7,025
  • 15
  • 21
  • Thank you, you are a life-saver!! Is it possible to make certain sounds not subjected to the pitch change? When I try to quickly shift the sounds I don't want shifted before playing them, then revert after, it leaves this 'double sound' where one sound lags behind a tiny fraction of a second. – Patrick Abbey Oct 22 '21 at 01:35
  • I'm not sure if I understand your question. `Tone.PitchShift` causes a little delay. Maybe that's what you are experiencing. There is also an option to bypass the effect using the `wet` param. But I guess it's best to turn that into a new question so everybody here can answer. – chrisguttandin Oct 22 '21 at 12:21
  • Fair. I will do another question then, thank you for your help. – Patrick Abbey Oct 23 '21 at 19:18
2

Just wanted to add that if you set the html5: true option then the browser will automatically fix that for you

Petros Kyriakou
  • 5,214
  • 4
  • 43
  • 82
  • The only issue with that is that I needed the sound to be instantaneous/without lag, and when I do HTML5 sound it has to buffer for about a quarter second before playing the first time through. – Patrick Abbey Mar 05 '22 at 23:32