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

linearize fade-ins on mix points #1001

Open
catfact opened this issue Feb 2, 2020 · 4 comments
Open

linearize fade-ins on mix points #1001

catfact opened this issue Feb 2, 2020 · 4 comments
Assignees
Labels

Comments

@catfact
Copy link
Collaborator

@catfact catfact commented Feb 2, 2020

problem: level smoothing is implemented with a simple 1-pole lowpass on the linear amplitude.

for fade-outs, this is fine; the exponential decay becomes a linear loudness ramp

linear_down
db_down

but for fade-ins, the exponential attack becomes a doubly-exponential loudness ramp:
linear_up
db_up

so... need to make a slightly smarter smoother class particularly for levels, where ramp-up is logarithmic, and ramp-down remains exponential. (preferably without killing performance by performing lin-log conversions each sample.)

@catfact catfact added the crone label Feb 2, 2020
@catfact
Copy link
Collaborator Author

@catfact catfact commented Feb 2, 2020

maybe it's also worth saying up front:

if we were dealing with fixed endpoints, this would be a simple problem of using ramps or exponential segments and transforming them arbitrarily. but the application has other requirements:

  • need to be able to change target values during a slew and maintain continuity of derivative(s).
  • target values must be specified in real units of output; user needs to specify actual amplitude, not position in some taper.
  • computation time is quite critical: there are a lot of these mixpoints and their execution is a substantial proportion of CPU time in crone.

so... i'm not really seeing a good way to satisfy all this with "classic" envelope constructs.

intuitively, it feels like we should be able to just construct an IIR filter with (approximately) a logarithmically-shaped impulse response, which would be ideal. so that's what i'm looking at right now. would be curious to hear if anyone has encountered such a thing before.

@catfact
Copy link
Collaborator Author

@catfact catfact commented Feb 2, 2020

other idea: maybe if we can assume X in [0,1] we can do something clever with inverting the domain, filtering, re-inverting... in the case when (target value) > (current value).

... nah, don't think so

@catfact
Copy link
Collaborator Author

@catfact catfact commented Feb 2, 2020

ok well, initial explorations actually look kinda promising... i'm surprised

made a small python script to design IIR filter coefficients matching a desired impulse response, by numerical optimization.

with a 3rd-order filter, Nelder-Mead search on the 6 coefficients gives -80db mean-squared error between target and actual IRs. (2nd order is much worse, 4th order isn't significantly better.)

still need to try incorporating this into an actual signal processor class to see if it does what i think it will. (execute this filter if value is increasing, equivalent simple onepole if decreasing, should give perceptually linear and equivalent fadeins and fadeouts.)

if that looks ok, the last challenge is making this modulateable. unless some smart person can come with an analytical solution (entirely possible), i guess i would be reduced to performing many searches for different time constants, and using 6 lookup tables to get the coefficients at runtime.

@catfact
Copy link
Collaborator Author

@catfact catfact commented Feb 3, 2020

hrm, well the filter design approach is super interesting and does actually work. and now there's a tool for designing IIR filters based on desired impulse response, if anyone wants such a thing.

but after evaluation i don't think it's the right solution here, mainly cause i don't think it's performant enough to justify its simplicity. the "log impulse" filter requires something like 30 operations per sample, and we have something like 20 of these things in crone and another 40 or something in softcut.

at that cost, we can do a classic envelope thing instead:

  • linear ramps between values
  • arbitrary easing function to shape the linear ramps
  • when target changes, pivot from current shaped value to new target.

this gives us flexibility (e.g. maybe raised-cosine fades are nicest) and is straightforward. possible downside is that it's relatively hard to maintain continuous derivatives, but i'm not sure that matters so much unless the fades are super long and liable to overlap.

@catfact catfact self-assigned this Jul 14, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
1 participant