mackron / miniaudio

Audio playback and capture library written in C, in a single source file.
https://miniaud.io
Other
4.05k stars 359 forks source link

Music Loop possibility #152

Closed harmeets43 closed 4 years ago

harmeets43 commented 4 years ago

Hi really like this library and was hoping to use this for one of the project, Is there any chance that we can some more examples on how to stream and loop the music stream, I would greatly appreciate this :)

mackron commented 4 years ago

By the music stream, I assume you mean the ma_decoder object? That's easy - all you need to do is check the return value of ma_decoder_read_pcm_frames(). This will return the number of PCM frames that were actually decoded. If it's less than the number you requested it means you reached the end and you can loop back to the start by simply seeking with ma_decoder_seek_to_pcm_frame(&myDecoder, 0). If you're calling this in the data callback and you want a seamless loop transition, you will need to ensure you fill every output frame which means you'll need to loop (untested):

#define OFFSET_PTR(p, offset) (((ma_uint8*)(p)) + (offset))

...

ma_uint64 pcmFramesRemaining = frameCount; /* frameCount is the argument from the data callback. */

while (pcmFramesRemaining > 0) {
    void* pRunningOutput = OFFSET_PTR(pOutput, (frameCount - pcmFramesRemaining) * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels));
    ma_uint64 decodedPCMFrameCount = ma_decoder_read_pcm_frames(&myDecoder, pRunningOutput, pcmFramesRemaining);

    /* Loop back to the start if we've reached the end. */
    if (decodedPCMFrameCount< pcmFramesRemaining) {
        ma_decoder_seek_to_pcm_frame(&myDecoder, 0);
    }

    pcmFramesRemaining -= decodedPCMFrameCount;
}

I'll consider writing adding an example to the examples folder at some point.

harmeets43 commented 4 years ago

Thanks for your prompt response, I tested the code, it worked liked a charm. Thanks for your help. I really like the idea behind this lib, I played with openal and got the ogg and wav stream working, honestly looping a file is straightforward, but it lacks support for mp3 and flac so I would have to do this myself. I am glad that I found this lib while looking into raudio module. :)

PS: Just a small suggestion, it would greatly help if there were some more examples like irrklang has, your library is already delivers almost same functionality. I know one could go go through the 43k long file but honestly that would take too much time.

mackron commented 4 years ago

How did you do Ogg and WAV with OpenAL? Did you use separate libraries, or is it built-in? I just mention it because I have single file libraries for FLAC and MP3 called dr_flac and dr_mp3 which might help (miniaudio uses these internally for FLAC and MP3 decoding).

With the irrKlang stuff, they're doing a more high level stuff than miniaudio is currently doing (though, I've got plans to work on this in the future). Mainly 3D spatialization, effects and resource management.

Also, the documentation at the top of miniaudio.h is probably the best place to get an idea on how to do things. It's far from perfect, but it's getting there. Always happy to take suggestions on board for improving documentation if you have any. I've added some extra details for determining when the end has been reached and how to loop back to the start (dev branch): https://github.com/dr-soft/miniaudio/commit/845ca227107d5d3a570b86d5476919987fbf715b. I think I'd rather leave this to the documentation rather than do a whole example because it's just a little bit too specific to justify a dedicated example in my opinion.

harmeets43 commented 4 years ago

Just for reference : this is the link that I followed while I was making a 2d game using opengl 3.3, I was not using your lib in that project.

https://indiegamedev.net/2020/02/15/the-complete-guide-to-openal-with-c-part-1-playing-a-sound/#Playing_your_first_sound this covers ogg and wav playing and streaming. Ogg however required libogg to work with openal, wav is inbuilt in openal.

Thanks for pointing out the location for documentation will take a look again. Thanks for adding those comments in header file so quickly.

mackron commented 4 years ago

OK, as I was suspecting Ogg and WAV are not built into OpenAL (I thought at first you might have been using an extension or something). That tutorial's WAV loading is done manually (and badly) - it's not built into OpenAL.

harmeets43 commented 4 years ago

I get what you are saying, but there are very less resources(there are lot of books but no tutorial) if someone wants to do audio engine design, can you share some resources which were helpful for making this library. Can you explain why the loading is bad ?(Sorry I am C++ and C# guy so have not touch any big c projects yet.)

mackron commented 4 years ago

I don't have any specific resources - I just pick up on things as I go (I still have a lot to learn). For the platform specific backend stuff I use official documentation, which usually has some sample code, and then just experiment with it and just figure it out. I can't test all possible configurations, so often the community will report errors in which case I'll just fix them as they're reported.

For WAV stuff there's some good resources out there. This is the best reference I've found, though: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html. If you want a description of some of the less common chunk types, this is the best reference I've found: https://sites.google.com/site/musicgapi/technical-documents/wav-file-format

Can you explain why the loading is bad?

The way the tutorial is doing their WAV loading is just too basic and makes too many wrong assumptions is all. Nothing to do with the language choice. Just quickly scanning through the code:

// this is always 16, the size of the fmt data chunk

That's not a true statement at all. It can be greater than 16 bytes in which case there'll be some extra details present. The correct way to do it is to check if the size if greater than 16, and if so, read an extra two bytes of data (provided the data is available, otherwise it's an invalid file). That 2 bytes specifies the size of an extra little bit of data. If the data is not required for your purposes, you need to seek past that number of bytes. If you're curious and want to learn how to do it properly, see drwav__read_fmt() in dr_wav: https://github.com/mackron/dr_libs/blob/master/dr_wav.h#L1631

They are then assuming the "data" chunk is next. That's an incorrect assumption - the "data" chunk can be anywhere after the "fmt " chunk, not necessarily the very next one. The correct way to do this is to keep reading the chunks in a loop. You read the headers, which are standard, and if you need the chunk, handle it, otherwise seek past it: https://github.com/mackron/dr_libs/blob/master/dr_wav.h#L1894

Also, every chunk is padded to make it aligned to 2 bytes. Not usually a big issue, but it's required if you want to handle chunks robustly. The article has no mention of such padding (though it doesn't actually affect this tutorial specifically because they are only handling standard chunks and aren't reading beyond the "data" chunk). If, however, you did want to read chunks other than "data", you would need to handle it.

It's not the biggest deal in the world or anything because it's a game development oriented blog, and in any given game input files will almost always be in a consistent format. If their loading logic works for your game's specific input files, that's fine. However, in my opinion, if they want to teach OpenAL, then focus on teaching OpenAL and refer to a good quality library for loading WAV files (which they do as well). It's too easy for someone to come along and just assume their WAV loading logic is completely correct and run with it, only to later get a random crash because they loaded a file with an "fmt " chunk larger than 16 bytes.

mackron commented 4 years ago

By the way, I've got mid to long term plans to do some more general audio documentation for things like resource management, mixing, etc. That'll take a bit of time, though.

harmeets43 commented 4 years ago

I totally get what you are saying, so it turns out there are some assumptions that the other author made with the wav format which might not be correct and thanks for the detailed resources that you provided. I am currently working on integrating your lib in a wxwidget project as a hobby project for my self. I will let you know how it goes. As for the wav format assumption I found out the hard way when I tried to convert a mp3 into a wav file and the code the other blog did not handle it correctly. So to me it looks like in the world, there is much to learn. It took me a while to get the Opengl concepts, learning audio with graphics is a nightmare. Thanks anyways will stay in touch.