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

Is there an existing or planned way to *divide* a signal with another one? #1156

Closed
JohnWeisz opened this Issue Feb 28, 2017 · 8 comments

Comments

Projects
None yet
6 participants
@JohnWeisz

JohnWeisz commented Feb 28, 2017

I'm wondering if there is a way to divide an audio signal by another one without having to resort to scripted audio processing (which, unfortunately, is really not usable for anything slightly more complex than a tech demo, at least when doing live audio playback).

I managed to implement all other basic math ops (addition, subtraction, multiplication), but I'm struggling to achieve division. Is it possible at all?


For clarification about what I mean about math ops between audio signals, consider two audio sources, two OscillatorNodes for example, oscA and oscB:

var oscA = audioCtx.createOscillator();
var oscB = audioCtx.createOscillator();

Now, consider that these oscillators are LFOs, both with low (i.e. <20Hz) frequencies, and that their signals are used to control a single destination AudioParam, for example, the gain of a GainNode. Through various routing setups, we can define mathematical operations between these two signals.

Addition

If oscA and oscB are both directly connected to the destination AudioParam, their outputs are added together:

var dest = audioCtx.createGain();

oscA.connect(dest.gain);
oscB.connect(dest.gain);

Subtraction

If the output of oscB is first routed through another GainNode with a gain of -1, which is then connected to the destination AudioParam, then the output of oscB is effectively subtracted from that of oscA, because we are effectively doing an oscA + -oscB op. Using this setup we can subtract one signal from another one:

var dest = audioCtx.createGain();
var inverter = audioCtx.createGain();

oscA.connect(dest.gain);

oscB.connect(inverter);
inverter.gain = -1;
inverter.connect(dest.gain);

Multiplication

Similarly, if the output of oscA is connected to another GainNode, and the output of oscB is connected to the gain AudioParam of that GainNode, then oscB is multiplying the signal of oscA (amplitude-wise):

var dest = audioCtx.createGain();
var multiplier = audioCtx.createGain();
     
oscA.connect(multiplier);
oscB.connect(multiplier.gain);
     
multiplier.connect(dest.gain);

So is there a way to do division in the Web Audio API?

@pendragon-andyh

This comment has been minimized.

Show comment
Hide comment
@pendragon-andyh

pendragon-andyh Feb 28, 2017

pendragon-andyh commented Feb 28, 2017

@rtoy

This comment has been minimized.

Show comment
Hide comment
@rtoy

rtoy Feb 28, 2017

Contributor

Yes, using a WaveShaperNode is the only way.

Also, in the multiplication example, you have to be sure to set the gain attribute of the gain multiplier to 0. Otherwise the output will default to 1 plus the product. (I always forget this.)

Contributor

rtoy commented Feb 28, 2017

Yes, using a WaveShaperNode is the only way.

Also, in the multiplication example, you have to be sure to set the gain attribute of the gain multiplier to 0. Otherwise the output will default to 1 plus the product. (I always forget this.)

@JohnWeisz

This comment has been minimized.

Show comment
Hide comment
@JohnWeisz

JohnWeisz Mar 1, 2017

@pendragon-andyh

You should be able to create a waveshaper node that applies a reciprocal.

@rtoy

Yes, using a WaveShaperNode is the only way.

Thank you, the help of WaveShaperNode seems to enable virtually all calculations to be done. Perfect for these use cases.

EDIT: Well, not perfect, but it gets the job done.


@rtoy

be sure to set the gain attribute of the gain multiplier to 0

Yes, thank you, that's taken care of, but I accidentally left it out from the example.

JohnWeisz commented Mar 1, 2017

@pendragon-andyh

You should be able to create a waveshaper node that applies a reciprocal.

@rtoy

Yes, using a WaveShaperNode is the only way.

Thank you, the help of WaveShaperNode seems to enable virtually all calculations to be done. Perfect for these use cases.

EDIT: Well, not perfect, but it gets the job done.


@rtoy

be sure to set the gain attribute of the gain multiplier to 0

Yes, thank you, that's taken care of, but I accidentally left it out from the example.

@rtoy

This comment has been minimized.

Show comment
Hide comment
@rtoy

rtoy Mar 1, 2017

Contributor

Based on the comments, I think we can close this. I don't expect that a new feature or node will be added to support division.

Contributor

rtoy commented Mar 1, 2017

Based on the comments, I think we can close this. I don't expect that a new feature or node will be added to support division.

@padenot

This comment has been minimized.

Show comment
Hide comment
@padenot

padenot Mar 7, 2017

Member

Tone.js used to have code to do exactly this, but it's the division is now deprecated. Here is how it used to be done:

https://github.com/Tonejs/Tone.js/blob/6c0c5d77af4cc48b84a4307551d0f549b824d912/Tone/signalT/Divide.js

I agree we can close this.

Member

padenot commented Mar 7, 2017

Tone.js used to have code to do exactly this, but it's the division is now deprecated. Here is how it used to be done:

https://github.com/Tonejs/Tone.js/blob/6c0c5d77af4cc48b84a4307551d0f549b824d912/Tone/signalT/Divide.js

I agree we can close this.

@padenot padenot closed this Mar 7, 2017

@mbylstra

This comment has been minimized.

Show comment
Hide comment
@mbylstra

mbylstra Jul 1, 2017

Sure there are hacks you can do to achieve this using nodes that weren't designed for this, but why not not just provide basic math operations to the Web Audio API? What's the harm in that? Surely it would be far more performant for these operations to be done completely natively. Would this really be a controversial addition to the Web Audio API? Would it be difficult to implement? Would it not just be a couple of lines of code containing / to implement this?

mbylstra commented Jul 1, 2017

Sure there are hacks you can do to achieve this using nodes that weren't designed for this, but why not not just provide basic math operations to the Web Audio API? What's the harm in that? Surely it would be far more performant for these operations to be done completely natively. Would this really be a controversial addition to the Web Audio API? Would it be difficult to implement? Would it not just be a couple of lines of code containing / to implement this?

@JohnWeisz

This comment has been minimized.

Show comment
Hide comment
@JohnWeisz

JohnWeisz Jul 3, 2017

@mbylstra
I've been looking at the code, it certainly would be trivial to implement.

The committee would have to come up with the specs, however, and I'm not sure they are ready to go full-time into control signal processing (after all, there are other implications when taking this route, such as reduced sample rate for improved efficiency, as you will likely not need 44.1 kHz to automate a gain audio param).

JohnWeisz commented Jul 3, 2017

@mbylstra
I've been looking at the code, it certainly would be trivial to implement.

The committee would have to come up with the specs, however, and I'm not sure they are ready to go full-time into control signal processing (after all, there are other implications when taking this route, such as reduced sample rate for improved efficiency, as you will likely not need 44.1 kHz to automate a gain audio param).

@sgentle

This comment has been minimized.

Show comment
Hide comment
@sgentle

sgentle Sep 21, 2017

There's one use case I'd like to bring up: finding the inverse of a frequency to get a wavelength. That's important if you want to delay one synth in a frequency-dependent way relative to another. When the frequencies are static, you can obviously just set them, but if you want to apply any LFO or envelope automation it gets trickier.

I specifically ran into this issue trying to create a pulse wave. I originally wanted to use @pendragon-andyh's solution (sawtooth into a thresholding WaveShaper), but ran into problems with bandlimiting outlined here: pendragon-andyh/WebAudio-PulseOscillator#2. So I then went with the subtract-one-offset-saw-from-another method, but that's tricky to automate because of the above issue and I wanted vibrato.

In the end, I used an inverting WaveShaper as in @padenot's Tone.js, but I'm left with lingering concerns. Looking at Inverse.js, it appears to do multiple rounds of gain and waveshaping depending on the input desired accuracy. I only did one round, and it's a bit tricky to reason about whether that's enough precision.

If the current best solution to "I want to delay one oscillator by a multiple of the period of another" is to rebuild an approximation on top of an arbitrary number of WaveShapers, I think it suggests a reciprocal or division node might be useful.

sgentle commented Sep 21, 2017

There's one use case I'd like to bring up: finding the inverse of a frequency to get a wavelength. That's important if you want to delay one synth in a frequency-dependent way relative to another. When the frequencies are static, you can obviously just set them, but if you want to apply any LFO or envelope automation it gets trickier.

I specifically ran into this issue trying to create a pulse wave. I originally wanted to use @pendragon-andyh's solution (sawtooth into a thresholding WaveShaper), but ran into problems with bandlimiting outlined here: pendragon-andyh/WebAudio-PulseOscillator#2. So I then went with the subtract-one-offset-saw-from-another method, but that's tricky to automate because of the above issue and I wanted vibrato.

In the end, I used an inverting WaveShaper as in @padenot's Tone.js, but I'm left with lingering concerns. Looking at Inverse.js, it appears to do multiple rounds of gain and waveshaping depending on the input desired accuracy. I only did one round, and it's a bit tricky to reason about whether that's enough precision.

If the current best solution to "I want to delay one oscillator by a multiple of the period of another" is to rebuild an approximation on top of an arbitrary number of WaveShapers, I think it suggests a reciprocal or division node might be useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment