You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OpenAudio_ArduinoLibrary/play_queue_f32.cpp

217 lines
6.0 KiB

/*
* AudioRecordQueue_F32
*
* Created: Chip Audette (OpenAudio), Feb 2017
* Extended from on Teensy Audio Library
*
* License: MIT License. Use at your own risk.
* Rebuilt Feb 2023 to include stall/non-stall behavior and max buffers.
* This is slightly adapted from the updated play_queue in the I16
* Teensy Audio library. Thanks to Jonathan Oakley for the improvements.
* See play_queue_f32.h for more details
*/
#include "play_queue_f32.h"
void AudioPlayQueue_F32::setMaxBuffers(uint8_t maxb)
{
if (maxb < 2)
maxb = 2 ;
if (maxb > MAX_BUFFERS)
maxb = MAX_BUFFERS ;
max_buffers = maxb ;
}
bool AudioPlayQueue_F32::available(void)
{
if (userblock) return true;
userblock = AudioStream_F32::allocate_f32();
if (userblock) return true;
return false;
}
/* Get address of current data buffer, newly allocated if necessary.
* With behaviour == ORIGINAL this will stall (calling yield()) until an audio block
* becomes available - there's no real guarantee this will ever happen...
* With behaviour == NON_STALLING this will never stall, and will conform to the published
* API by returning NULL if no audio block is available.
* return: NULL if buffer not available, else pointer to buffer
* of AUDIO_BLOCK_SAMPLES of float32_t
*/
float32_t* AudioPlayQueue_F32::getBuffer(void)
{
if (NULL == userblock) // not got one: try to get one
{
switch (behaviour)
{
default:
while (1)
{
userblock = AudioStream_F32::allocate_f32();
if (userblock)
break;
yield();
}
break;
case NON_STALLING:
userblock = AudioStream_F32::allocate_f32();
break;
}
}
return userblock == NULL
?NULL
:userblock->data;
}
/* Queue userblock for later playback in update().
* If there's no user block in use then we presume success: this means it's
* safe to keep calling playBuffer() regularly even if we've not been
* creating audio to be played.
* return 0 for success, 1 for re-try required */
uint32_t AudioPlayQueue_F32::playBuffer(void)
{
uint32_t result = 0;
uint32_t h;
if (userblock) // only need to queue if we have a user block!
{
// Find place for next queue entry
h = head + 1;
if (h >= max_buffers) h = 0;
// Wait for space, or return "please re-try", depending on behaviour
switch (behaviour)
{
default:
while (tail == h); // wait until space in the queue
break;
case NON_STALLING:
if (tail == h) // if no space...
result = 1; // ...return 1: user code must re-try later
break;
}
if (0 == result)
{
queue[h] = userblock; // block is queued for transmission
head = h; // head has changed
userblock = NULL; // block no longer available for filling
}
}
return result;
}
/* Put a single sample to buffer, and queue if buffer full.
* return 0 for success; 1: failed, data not stored, call again
* with same data. */
uint32_t AudioPlayQueue_F32::play(float32_t data)
{
uint32_t result = 1;
float32_t* buf = getBuffer();
do
{
if (NULL == buf) // no buffer, failed already
break;
if (uptr >= AUDIO_BLOCK_SAMPLES) // buffer is full, we're re-called: try again
{
if (0 == playBuffer()) // success emitting old buffer...
{
uptr = 0; // ...start at beginning...
buf = getBuffer(); // ...of new buffer
continue; // loop to check buffer and store the sample
}
}
else // non-full buffer
{
buf [uptr++] = data ;
result = 0;
if (uptr >= AUDIO_BLOCK_SAMPLES // buffer is full...
&& 0 == playBuffer()) // ... try to queue it
uptr = 0; // success!
}
} while (false);
return result;
}
/*
* Put multiple samples to buffer(s), and queue if buffer(s) full.
* return 0 for success; >0: failed, data not stored, call again with
* remaining data (return is unused data length) */
uint32_t AudioPlayQueue_F32::play(const float32_t *data, uint32_t len)
{
uint32_t result = len;
float32_t * buf = getBuffer();
do
{
unsigned int avail_in_userblock = AUDIO_BLOCK_SAMPLES - uptr ;
unsigned int to_copy = avail_in_userblock > len ? len : avail_in_userblock ;
if (NULL == buf) // no buffer, failed
break;
if (uptr >= AUDIO_BLOCK_SAMPLES) // buffer is full, we're re-called: try again
{
if (0 == playBuffer()) // success emitting old buffer...
{
uptr = 0; // ...start at beginning...
buf = getBuffer(); // ...of new buffer
continue; // loop to check buffer and store more samples
}
}
if (0 == len) // nothing left to do
break;
// we have a buffer and something to copy to it: do that
memcpy ((void*)(buf+uptr), (void*)data, to_copy * sizeof(float32_t)) ;
uptr += to_copy;
data += to_copy;
len -= to_copy;
result -= to_copy;
if (uptr >= AUDIO_BLOCK_SAMPLES) // buffer is full...
{
if (0 == playBuffer()) // ... try to queue it
{
uptr = 0; // success!
if (len > 0) // more to buffer...
buf = getBuffer(); // ...try to do that
}
else
break; // queue failed: exit and try again later
}
} while (len > 0);
return result;
}
// Assume user already has an audio_block that was NOT allocated by this
// playBuffer. Here, you hand it your buffer. This object takes ownership
// of it and puts it into the queue. This is not in I16 library.
void AudioPlayQueue_F32::playAudioBlock(audio_block_f32_t *audio_block) {
uint32_t h;
if (!audio_block) return;
h = head + 1;
if (h >= max_buffers) h = 0;
while (tail == h) ; // wait until space in the queue
queue[h] = audio_block;
audio_block->ref_count++; //take ownership of this block
head = h;
userblock = NULL;
}
void AudioPlayQueue_F32::update(void)
{
audio_block_f32_t *block;
uint32_t t;
t = tail;
if (t != head) { // a data block is available to transmit out
if (++t >= max_buffers) t = 0; // tail is advanced 1, circularly
block = queue[t]; // pointer to next block
tail = t;
AudioStream_F32::transmit(block);
AudioStream_F32::release(block); // we've lost interest in this block...
queue[t] = NULL; // ...forget it here, too
}
}