kupix / bungee

Audio time stretch and pitch shift library. Enables music tempo adjustment, transposition, "smooth scrub" and "live pause".
https://bungee.parabolaresearch.com/
Mozilla Public License 2.0
96 stars 3 forks source link

Ambigous instruction #7

Closed dhng22 closed 1 month ago

dhng22 commented 2 months ago

Hi, thanks for the awesome library. I'm a little bit struggling to make it work in code. I've looked at cmd/main.cpp but still pretty hard for me to understand. I'm currently using Rubberband to perform pitch-shift on audio buffer, and this is how i make it work in code:

Screenshot 2024-07-11 at 22 47 18

It's pretty simple and straightfoward. Can you provide more detail comments on the functions from Stretcher? It would be nice if you could also provide a simple code corresponding to the working example of Rubberband I've posted above. Thank you

mix359 commented 2 months ago

Hi, I would like to integrate this library as well in a realtime processing context like dhng22, but the API really confuse me. In particular it seam to me that the library have to take control on the transport/read position while usually the time-stretching libraries only do the processing on a block of audio that it's provided to it. There's any reason why the library need to have the control on the read position?

There's any way to use it in a chain of processors? Like in the example code showed by dhng22, usually in a processors chain a time-stretching library provide a "maximum input frames needed" to allocate an input buffer and some way to calculate how many input frames are needed to have a specific amount of output frames. Based on that information we usually feed some chunks of input data, call the processing and check if enough output data are available to fill an output buffer, reading the output into circular buffers to compensate for input/output discrepancy.

I've tried to understand how to do that with the current API but I'm missing a way to specify in the Request how many frames I need the library to process or any way to do some calculations on how many input samples are needed or how many output samples will be produced. Also the position parameters in the request confuse me, because I don't have a big audio buffer with all the audio to be stretched, but small chunks.

So I would like to join the request done by dhng22 of a clarification or an example on how to use the library in those scenarios.

Thanks again for your work!

kupix commented 2 months ago

Hello both,

Thank you for your interest in Bungee.

Because Bungee's input can move forwards, backwards, pause, seek and scrub through the input audio, the API does need to be a bit different to traditional modules that have an input and an output stream of audio. The native API works something like this, per grain:

Caller: I want the next grain to be centred at sample position 10,000 Bungee: sure, please show me audio frames from 7,952 to 12,047. Caller: here's a pointer to the input audio range that you asked for Bungee: ok, grain done, here are the next 512 frames of audio output. These correspond to input audio from 7,952 to 8,463 Caller: ok based up current playback speed and pitch, where should the next grain be centred? Bungee: the next grain should have position 10,512.

This API is based upon how Bungee works and it allows users to achieve the lowest possible latency, minimising data copies. It can work for any speed including zero and negative ones. Any mode of operation can be built upon this by adding input and/or output buffering.

Have you both seen the "push" API in Push.h? It is additional wrapper input buffering code that we created for cases where apps are driven by calls delivering input audio. Sample usage is here: https://github.com/kupix/bungee/blob/0083f748ab2f30ed5969a67602ee66582bd828ae/cmd/main.cpp#L32 please study the loop that follows this code and let me know whether you can drop that onto your code.

Please do share any further questions or suggestions.

dhng22 commented 2 months ago

@kupix Hi, thanks for the quick explaination. Can you explain what's the pushFrameCount at this line and how can I get it in my context? Thanks

dhng22 commented 2 months ago

I've managed to get the pushFrameCount, but there's more problem:

Screenshot 2024-07-14 at 00 50 16

I already have the audio data in the readPtr, but there isn't a constructor to directly pass the data in, instead I have to make a double loop to assign it. And there's also a lot of variables marked red that I don't know where to acquire as in the image above. Also all functions in Push::InputBuffer are not documented. So I don't really know what they do. That being said, I still can not produce a corresponding function to Rubberband

dhng22 commented 2 months ago

Here is my best attempt to make the API work:

Screenshot 2024-07-14 at 13 02 58

There's still the processor.write() to make the loop break but I'm not using cmd Processor

toaster217 commented 2 months ago

I also find it confusing when trying to integrate the library with my code. There's a lot of functions to just process an audio buffer from local source: preroll, specifyGrain, analyseGrain, synthesisGrain, next. After all of these I also couldn't get it to work

lehieu2602 commented 2 months ago

any update?

solace-re commented 1 month ago

@kupix Hi, is there any snippet for this issue? I'm impressed by your library and would love to integrate it with my current project! Thanks

kupix commented 1 month ago

The API is designed around the dataflow that the stretcher needs to achieve fully flexible time warping with minimum latency, supporting zero and negative speeds. It's also designed to avoid the need for a callback to get the necessary input data for each grain.

So some usecases will require additional buffering, and that's fine. Push.h provides input buffering. Output buffering should be straightforward to add, if needed.

I suggest looking at the sample main() function first: get that working in a debugger, understand the inputs and outputs then adapt to the dataflow of your own app.

dhng22 commented 1 month ago

@kupix Kindly asking if you could answer my question? It'd be great if you could have a quick look at the screenshot i posted above

toaster217 commented 1 month ago

If no progress can be made, this issue should be closed

kupix commented 1 month ago

@dhng22 sorry, I can't help with debugging your code. General point: Processor.write is user code that does two things, it outputs the chunk produced by bungee and it decides when processing is finished. You should replace it with code appropriate to your processing loop.

kupix commented 1 month ago

If no progress can be made, this issue should be closed

Agreed since there doesn't seem to be any problem with the bungee library itself perhaps we should close this. I am wondering whether we could use GitHub discussions for user/community support. If new samples, wrappers or documentation are required, let's get that clearly defined in a new issue.

dhng22 commented 1 month ago

@kupix I apologize for reopening this. Again, I've been trying to make it work in the past few weeks, yet I couldn't get it to work.

sorry, I can't help with debugging your code

I mean, come on, I'm not asking for detailed debugging or advanced things. I'm just a community member trying to utilize your library at the entry point and the doc itself isn't clear enough for me. I am grateful for your work, and it would be great if I could use it. If it works, I'd love to license my app with a commercial license too.

Again, I'm still trying to perform audio stretch, but the output is still the same: Screenshot 2024-08-07 at 01 26 20

It'd be great if you @kupix or anyone could help me out. Thanks a lot

toaster217 commented 1 month ago

@dhng22 So the output audio remains unchanged? Have you check the stretcherRequest speed and pitch parameters? or any abnormal execution inside the synthesiseGrain() loops?

dhng22 commented 1 month ago

@toaster217 Thanks for your response. I've set the stretcherRequest speed and pitch before the above code happens. And I did notice that the code inside the loops inputBuffer.inputFrameCountRequired() <= 0 only run like 4-5 times and then there are no more log comming from that part anymore. Wondering if I'm missing something

toaster217 commented 1 month ago

@dhng22 The code seems fine to me. I suggest opening a new issue and seeing if @kupix could help you out, since pretty much no one here can