Giter VIP home page Giter VIP logo

Comments (7)

mackron avatar mackron commented on May 18, 2024

The ring buffer stuff is new and I've just not had the time to document it. I also never looked at any references or anything so I may have used a few unorthodox ideas in the API design. But the idea is that you acquire a portion of the buffer which returns a pointer for you to read/write, and then release it which moves the cursor forward. Also keep in mind that it's single producer, single consumer.

What specifically were you having trouble with?

from miniaudio.

rdanbrook avatar rdanbrook commented on May 18, 2024

ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut);
ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut);

Not exactly sure what to pass in to these functions. Not sure what the third argument actually is. Is it where the read/write cursor is going to be stored when the function returns (and size is stored in the second param)? Seems so when I look at the code, but after segfaulting when trying to operate on it I ended up using my own ring buffer implementation with a mutex (I'd rather use yours if it's lock-free).

If you want to see my quick and dirty solution using a mutex: https://github.com/0ldsk00l/smsplus/blob/master/shell/smsplus.c

edit: Yeah, I am only concerned with single producer/consumer for my projects which are mostly emulation-based.

from miniaudio.

mackron avatar mackron commented on May 18, 2024

You use it like this:

size_t sizeInBytes = 1024; // Initialize this to the number of bytes you want.
void* pBuffer;
result = ma_rb_acquire_read(pRB, &sizeInBytes, &pBuffer);
if (result != MA_SUCCESS) {
    // Error
}

// At this point, sizeInBytes is set to the number of bytes _actually_ acquired. pBuffer is
// a pointer to the buffer you would read from. Note that sizeInBytes may be less than
// what you originally requested.
memcpy(pSomeBuffer, pBuffer, sizeInBytes); // <-- Do something with the data.

result = ma_rb_commit_read(pRB, sizeInBytes, pBuffer);
if (result != MA_SUCCESS) {
    // Error
}

The general flow is acquire/map a portion of the ring buffer, do something with the returned data, then commit/unmap.

If it looks like you're doing everything correct, I'd be interested to see a portion of your code if at all possible just to check if there's an underlying bug with miniaudio's ring buffer implementation since it is fairly new code.

from miniaudio.

rdanbrook avatar rdanbrook commented on May 18, 2024

Thanks for posting that example, it helped me make sense of everything and I've managed to get something up and running using the built-in ring buffer. Works great! I may create a sine wave example of my own that uses it and post it later in case it's useful to other people.

from miniaudio.

acidtonic avatar acidtonic commented on May 18, 2024

I too am a bit curious how this is used. In my case I am trying to keep a small buffer of past PCM frames such that if some later processing (within the next 5 seconds) decides it wants a snippet of that audio as a wav, I can seek backwards in the PCM buffer, start up a WAV encoder and pass those frames to the encoder to get a WAV from that snippet in time.

Is the ring buffer the best way to achieve this? I am still passing frames in the callback to do some FFT work and won't know if I need to go back in time to save the stream until a few seconds later. Are there any existing examples of doing something like this or storing PCM frames into a ring buffer?

from miniaudio.

orcmid avatar orcmid commented on May 18, 2024

To have a look-behind of some fixed number of frames, using a ring buffer, you need to have a way to prevent the writer from over-taking the reader and instead be blocked at some number of frames behind the reader.

It might be better to store the read frames into a separate ring cache where new reads overlay old ones based on the size of the cache ring and how fast things are going. The size of the cache ring determines what is keeps to that predetermined depth.

That's technically not a ring buffer, because you are chasing your own tail and allowed to catch and over-run it. In this game, the oldest cache item is just in front of the moving cache filler. (This should all be on the reader's thread of course.)

The reader being cached in a ring might be obtaining its data from a ring buffer though.

MORE ON RING BUFFERS

The use of a ring buffer is usually to coordinate a reader and a writer, where the reader chases (but does not pass) the writer, and the writer chases (but does not pass) the reader.

The buffer should be large enough that the reader rarely catches the writer (and then has to wait, drop its own outputs, stall other things, etc). Similarly, if the writer catches the reader, it may end up having to drop data until the reader makes more room in the buffer.

This sort of thing can happen with any kind of buffering, of course. The ring buffer just happens to be a cool structure that works like a speedway and doesn't have to be choppy such as buffers that have to be emptied quickly and that writers can fill, with readers needing to gobble a ready buffer quickly so the writer is not blocked either.

Ring buffers can be made expandable using list structures although reducing them later can be tricky.

With regard to audio, the ring buffer provides a form of caching for smoothing operation between producer and consumer. However, there can be latency issues as a consequence. Any latency should not be so bad as having a buffer that must be filled and then must be drained, wholesale.

from miniaudio.

mackron avatar mackron commented on May 18, 2024

@acidtonic No, I don't think a ring buffer would be appropriate for your case. The ring buffer is a very specific data structure. It's good when you have one thread writing some data (the producer) and another thread reading the data (the consumer) and you want that access to be lock-free. It's useful in audio because you'll sometimes end up in a situation where you have some capturing thread writing to the ring buffer, while at the same time on a different thread you have something that is consuming that data. It allows those two threads to be completely decoupled.

In your case I would consider just using a standard old buffer.

from miniaudio.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.