softcut: routines for copying and grabbing content from buffer regions #1161
Conversation
| } | ||
|
|
||
| softCutClient->renderSamples(ch, argv[1]->f, argv[2]->f, sampleCt, | ||
| [=](float secPerSample, float start, size_t count, float* samples) { |
csboling
Jul 12, 2020
Author
Contributor
Here and here I demonstrate that I only pretend to know how to do Modern C++ things. All the other callbacks use regular function pointers and I understand that std::function is more costly, but I could not get this to compile with a function pointer when this lambda needs to value-capture the ch index. Other places in this file use addServerMethod with some capturing going on (I think?) but the compiler is not happy about this -- maybe because this is a lambda defined inside another lambda?
Here and here I demonstrate that I only pretend to know how to do Modern C++ things. All the other callbacks use regular function pointers and I understand that std::function is more costly, but I could not get this to compile with a function pointer when this lambda needs to value-capture the ch index. Other places in this file use addServerMethod with some capturing going on (I think?) but the compiler is not happy about this -- maybe because this is a lambda defined inside another lambda?
catfact
Jul 14, 2020
Collaborator
i think the overhead of the lambda is totally fine here. i don't think there are any cases of capturing elsewhere - there is just a lot of static storage.
i think the overhead of the lambda is totally fine here. i don't think there are any cases of capturing elsewhere - there is just a lot of static storage.
|
holy smokes!! this is huge--- looking forward to digging in--- i was just thinking the other day about buffer ops, and waveforms have been in the back of my head since the projects' inception... |
|
looks great - thanks for taking this on. i haven't got around to running on norns yet... if it works for others i'm good to merge |
| x = 1.f; | ||
| phi = 0.f; | ||
| } | ||
|
|
catfact
Jul 14, 2020
Collaborator
equal-power crossfade also an option, using x as phase:
static float raisedCosFadeIn(float unitphase) {
return 0.5f * (cosf(M_PI * (1.f + unitphase)) + 1.f);
};
static float raisedCosFadeOut(float unitphase) {
return 0.5f * (cosf(M_PI * unitphase) + 1.f);
};
equal-power crossfade also an option, using x as phase:
static float raisedCosFadeIn(float unitphase) {
return 0.5f * (cosf(M_PI * (1.f + unitphase)) + 1.f);
};
static float raisedCosFadeOut(float unitphase) {
return 0.5f * (cosf(M_PI * unitphase) + 1.f);
};
| } | ||
|
|
||
| softCutClient->renderSamples(ch, argv[1]->f, argv[2]->f, sampleCt, | ||
| [=](float secPerSample, float start, size_t count, float* samples) { |
catfact
Jul 14, 2020
Collaborator
i think the overhead of the lambda is totally fine here. i don't think there are any cases of capturing elsewhere - there is just a lot of static storage.
i think the overhead of the lambda is totally fine here. i don't think there are any cases of capturing elsewhere - there is just a lot of static storage.
|
| } | ||
|
|
||
| float BufDiskWorker::mixFade(float x, float y, float a, float b) { | ||
| return a * x + b * y; |
csboling
Jul 15, 2020
•
Author
Contributor
Split this out to a function so the mixing approach could be tweaked. Not sure if this should use the same mixing calculation as ReadWriteHead::mixFade, I don't think I understand how/why this works so I left it linear here. Also maybe need to think about clipping behavior?
Split this out to a function so the mixing approach could be tweaked. Not sure if this should use the same mixing calculation as ReadWriteHead::mixFade, I don't think I understand how/why this works so I left it linear here. Also maybe need to think about clipping behavior?
catfact
Jul 18, 2020
•
Collaborator
i actually think it's fine to exceed unity range in the buffer:
- for live output, the only thing that matters is the level at the final sink, and there are many gain stages left to go before the buffer contents make it to the DAC. if anything we should actually add a hardclip to the final ouput of
SoftcutClient, but i'm also jappy to let JACK/drivers handle it.
- for saving the buffer to disk, we let
libsndfile perform clipping at the same time as converting to the requested output sample format.
crossfade options are a whole huge topic that i'd love to discuss...
- for the (generally good[*1]) assumption that real-world signals are uncorrelated, we want the sum of each signal's power (squared amplitude) to remain constant.
- $sin^2 + cos^2 = 1$ is a convenient relation for building curves with that property.
- in crone/softcut we've used both "raised cosine" (for a gradual fade) and "half cosine" for a fast fade. [*2]
- tbh, neither is really ideal and don't create "perceptual linearity." [*3] the 2.0 revision has many new facilities for efficient table-based envelopes, and the final touch to that would be making some perceptually linear in/out tables for this purpose.
anyways maybe we can raise a TODO issue for updating the crossfades perfectly; it's definitely not a big deal at this point to leave them linear or whatever.
[*1] when signals are perfectly correlated, you want "equal voltage" instead of "equal power." it's prohibitive to be determining this in real time for all mix points of course; but you could actually do it for an offline operation.
[*2] (...but i should make sure that the phase arguments are correct in all cases; raised-cosine still sometimes being summed incorrectly...)
[*3] it's even weirder in context of softcut xfades, because overdub and record levels are also crossfading, so correlation with signals from the past comes into play.
i actually think it's fine to exceed unity range in the buffer:
- for live output, the only thing that matters is the level at the final sink, and there are many gain stages left to go before the buffer contents make it to the DAC. if anything we should actually add a hardclip to the final ouput of
SoftcutClient, but i'm also jappy to let JACK/drivers handle it. - for saving the buffer to disk, we let
libsndfileperform clipping at the same time as converting to the requested output sample format.
crossfade options are a whole huge topic that i'd love to discuss...
- for the (generally good[*1]) assumption that real-world signals are uncorrelated, we want the sum of each signal's power (squared amplitude) to remain constant.
- $sin^2 + cos^2 = 1$ is a convenient relation for building curves with that property.
- in crone/softcut we've used both "raised cosine" (for a gradual fade) and "half cosine" for a fast fade. [*2]
- tbh, neither is really ideal and don't create "perceptual linearity." [*3] the 2.0 revision has many new facilities for efficient table-based envelopes, and the final touch to that would be making some perceptually linear in/out tables for this purpose.
anyways maybe we can raise a TODO issue for updating the crossfades perfectly; it's definitely not a big deal at this point to leave them linear or whatever.
[*1] when signals are perfectly correlated, you want "equal voltage" instead of "equal power." it's prohibitive to be determining this in real time for all mix points of course; but you could actually do it for an offline operation.
[*2] (...but i should make sure that the phase arguments are correct in all cases; raised-cosine still sometimes being summed incorrectly...)
[*3] it's even weirder in context of softcut xfades, because overdub and record levels are also crossfading, so correlation with signals from the past comes into play.
this could be pulled off without a breaking change on the lua API side because a default arg could be assumed. i think we can change the OSC layer presently without treating it like an API layer--- this of course could change in the future if there was an alternative front-end to crone (instead of matron) but i don't think that's going to happen--- if anything there's been talk of changing the IPC from OSC to something else, though that's not super high priority. but yes, clear with fades with be a great feature. i'm very enthusiastic about these buffer ops! hoping to test this week, thank you for your efforts! also yeah--- probably makes sense for a separate PR :) |
re: OSC/lua API for this, i agree:
|
|
excellent. i'll do more testing tonight with live input, and attempt some high-stress (queue up a ton of buf workers) but i suspect this is probably good to go. there will be caveats as to how this functionality can be used, but i think that's reasonable. |
|
Testing last night I finally got it through my head that you get weird behavior if the source region and target region overlap, so it needs to use the |
|
good catch. i'll try to analyze similar edge cases |
|
Refs #1151
buffer_clear_region, plumbing includes mono and stereo variants.For debugging and demonstration I wrote this simple waveform visualizer script, presented with apologies for possible line ending issues and definite script-level bugs.
An assorted mash of remarks / questions / insecurities: