Tonejs / Tone.js

A Web Audio framework for making interactive music in the browser.
https://tonejs.github.io
MIT License
13.41k stars 977 forks source link

setting pitch of Tone.FeedbackCombFilter #299

Closed bgarton closed 4 years ago

bgarton commented 6 years ago

Hi --

(totally new to this!)

This may be ignorance on my part of how time is handled in Tone.js, but I'm trying to set a comb filter to a particular pitch. If I initialize it like this:

var comb = new Tone.FeedbackCombFilter(1/440, 0.99);

I think I should get a 440 Hz pitch. It's closer to 184 Hz, which is pretty weird, so I'm suspecting that the delayTime is being interpreted somehow?

brad

tambien commented 6 years ago

This is a good question. I believe this issue has to do with a limitation of Web Audio. Since the web audio block-size is fixed at 128 samples (defined in the spec), that's also the smallest delayTime that the DelayNode can feedback on itself. So by my calculation, the highest note that it could produce would be 1/(128*sampleRate). At 48k that would be about 375Hz, which is about an octave away from the 184Hz that you measured.

Before i had implemented the CombFilters using multiple DelayNodes to get around this limitation. But this added computation and latency, so i took it out a few version ago. I'm planning to reimplement the comb filters with the AudioWorklet when that becomes available. This same issue makes the Tone.js Karplus-Strong synth very out of tune for high frequencies with that same (relatively low) frequency cap.

I'm going to leave this open to remind myself to fix it.

RichCaloggero commented 5 years ago

Related question

WHat is the smallest delay time that can truly be set with webaudio? This isn't clear to me from the webaudio spec nor Mozilla's docs.

Thanx for any info you have on this...

tambien commented 5 years ago

the DelayNode can have a delayTime of 0, but the smallest feedback delay time is currently 128 samples which is the size of the processing block defined in the Web Audio spec.

RichCaloggero commented 5 years ago

I should rephrase my question -- what is the smallest delay size step (i.e. the shortest delay other than zero)?

rain-sk commented 5 years ago

With 44100 samples per second, and the smallest delay block being 128 samples, that's 128/44100 = 0.0029024943 seconds.

On Wed, Oct 31, 2018, 1:47 PM Rich Caloggero <notifications@github.com wrote:

I should rephrase my question -- what is the smallest delay size step (i.e. the shortest delay other than zero)?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/Tonejs/Tone.js/issues/299#issuecomment-434798303, or mute the thread https://github.com/notifications/unsubscribe-auth/AC7ifzyDRk7KFjmLr-aIB0nD7iRPwp_gks5uqfBRgaJpZM4R6KkN .

tambien commented 5 years ago

the block size limitation that i mentioned only applies to feedback delays. the spec doesn't mention any limitations or minimum delayTime (though i haven't ever tried measuring it myself). that would imply to me that the DelayNode should be capable of single sample delay... just not single sample feedback delay.

rain-sk commented 5 years ago

Ah, thank you for clarifying.

How would you go about testing something like the minimum possible delayTime?

On Wed, Oct 31, 2018, 2:11 PM Yotam Mann <notifications@github.com wrote:

the block size limitation that i mentioned only applies to feedback delays. the spec https://webaudio.github.io/web-audio-api/#dom-delaynode-delaytime doesn't mention any limitations or minimum delayTime (though i haven't ever tried measuring it myself). that would imply to me that the DelayNode should be capable of single sample delay... just not single sample feedback delay.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/Tonejs/Tone.js/issues/299#issuecomment-434806567, or mute the thread https://github.com/notifications/unsubscribe-auth/AC7if1KCtQ6uwNTDP5i7xt_Ja0HF7Bttks5uqfXwgaJpZM4R6KkN .

tambien commented 5 years ago

@spencerudnick i would use the OfflineAudioContext. For example, you could schedule a pulse (using the ConstantSourceNode) to start at time 0, feed it through a DelayNode with a delayTime of one sample, and then take a look at the output buffer. If it's capable of single sample delay then the first sample should be 0 and the second sample should be 1.

smrq commented 5 years ago

I've done some testing by looking at the impulse response from the filter when tuned to some various frequencies. From my experiments (tested at 44.1kHz only), it looks like the underlying delay node adds a fixed 128 samples of delay to any delay time (which checks out with the calculations above). The result of this is that not only is 344.5Hz (128 samples) the highest frequency that you can set the filter to, but also that you also have to take the 128 samples into account for tuning at other frequencies. E.g. in order to filter at 100Hz (441 samples), you have to set the delay time to 313 samples (441 - 128).

It's actually possible to do an arbitrary comb filter by using ConvolutionNode instead, by generating the impulse response programmatically. This works great if you aren't changing the frequency or resonance parameters. Any change to those, and you would have to recalculate the impulse response, so that's probably not a great solution.

Audio worklets are probably a better solution still :)

1valdis commented 5 years ago

In case you're still on to AudioWorklets, they're implemented in Chrome as for now and are pretty much working (I'm messing around them last few weeks)

tambien commented 4 years ago

This is one of the oldest issue that's still open! AudioWorkletNode support and polyfills are finally at a place where i felt i could reimplement the FeedbackCombFilter with it. And now the FeedbackCombFilter is capable of hitting pitches below the sampling block of 128 samples.

Here's a little validation of the pluck synth at higher frequencies: https://codesandbox.io/s/pitched-pluck-synth-7y7tt