Simple mono file playback question


#1

Hi -

I’ve gone through the “Build an Audio Player” tutorial (and The Audio Programmer’s YouTube Version) and if I’m playing back a mono file - it’s playing back double speed.

To my understanding, this is happening because the mono file (of course) only has 1 channel of information and the output buffer has 2. I’ve tried to create a new AudioSourceChannelInfo with my own single channel buffer, and copy that buffer into bufferToFill so both channels get the same data - but it still plays back fast. Here’s the code:

I’m sure this is way easier than I’m making it out to be, but I’m not quite sure what to do. Both channels are getting the same information, so my understanding of how this works in JUCE has to be wrong.

Thanks for any help!


#2

Allocating memory is quite a bad thing to do, as it can require some time which you might not have with the realtime thread (in getNextAudioBlock I guess).

However, for me the code looks quite fine, can’t detect the culprit there. Is it really double speed? Or could it be a sample rate issue? Has your mono-file the same sample rate as the playback device? If not you will need to use a ResamplingAudioSource to make the sample rates match. But maybe the tutorial also covers that (can’t see the full code of your method. btw: with ``` at the beginning and end you can insert your code here directly and it will be formated in a way that it’s easy to read).


#3

Btw: I think you don’t even need that AudioSource in between (tempBuffer), as your mono AudioSource transport will only fill the first channel of your stereo buffer. What you then do is just copy channel 0 (left) to channel 1 (right).


#4

It is not only, that you allocate memory in the audio thread, you are even leaking it.

AudioBuffer is not meant to be allocated on the heap memory. Create it on the stack as member.

Assigning anything to a raw pointer and handling the destruction manually is no longer accepted in any serious project. Use RAII techniques like std::unique_ptr or ScopedPointer for that.

Maybe have a look at the official tutorials: https://docs.juce.com/master/tutorial_playing_sound_files.html

Good luck!


#5

It looks like from the code snippet that even allocating an AudioSampleBuffer (AudioBuffer<float>) object on the stack in this context will still allocate heap memory on the audio thread.

But the playback speed issue is almost certainly what @danielrudrich mentions in that the sample rates are mismatched between the source audio and the playback rate.


#6

Sure, it does if the OP does it in processBlock or getNextAudioBlock(). Was maybe not clear enough about that.
If you need a temporary buffer, create a member for that and set the size in prepareToPlay(), that is meant for that kind of setup.

But like @danielrudrich says, in this situation (from what we can tell of that snippet) it can be probably avoided completely.

Like I said before, start with the tutorials provided on the juce page.


#7

Thank you all for the feedback - totally aware that the sample is horribly awful code. I’m still incredibly new to low-level audio, and for the most part was just attempting to make something output as I understood it should than trying use memory properly.

@danielrudrich thanks for reiterating sample rate. I followed along with a video for the original code and it misses part of the official JUCE tutorial code. Specifically everything after newsource.get() in the line below:

transportSource.setSource (newSource.get(), 0, nullptr, reader->sampleRate);

Got it working with that, thank you!


#8

Just to be clear, leaking memory in your getNextAudioBlock() method is not a style problem. It is an actual problem. Each second you are allocating 48 kB that you are not freeing. Keep your program for an hour and you lost 172 MB of memory, that you never get back before you close your program.
You must free memory that you allocated with new. That’s why it is necessary to be either very careful to call delete after you are done with that memory, but better to use RAII techniques, where you don’t have to deal with it yourself.

RAII works by creating a little structure/object on the stack, that knows about the allocated memory. When the stack is unwound, this triggers freeing the allocated memory automatically and foolproof.

Like said before, std::unique_ptr or ScopedPointer are for general objects, OwnedArray is for many objects and AudioBuffer (and AudioSampleBuffer is just an alias for AudioBuffer) is the same thing for samples.

Glad you got your problem sorted.