Major addition of functions from J. Oakley per I16 library. Thanks!

master
boblark 1 year ago
parent 4983644314
commit d5419e0af1
  1. 237
      play_queue_f32.cpp
  2. 113
      play_queue_f32.h

@ -5,82 +5,217 @@
* Extended from on Teensy Audio Library * Extended from on Teensy Audio Library
* *
* License: MIT License. Use at your own risk. * 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" #include "play_queue_f32.h"
#include "utility/dspinst.h" //#include "utility/dspinst.h"
// =================================================
//#include <Arduino.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) bool AudioPlayQueue_F32::available(void)
{ {
if (userblock) return true; if (userblock) return true;
userblock = allocate_f32(); userblock = AudioStream_F32::allocate_f32();
if (userblock) return true; if (userblock) return true;
return false; return false;
} }
/* getBuffer() returns a pointer to the data area of an AudioBlock_32 /* Get address of current data buffer, newly allocated if necessary.
* that can be loaded in the .INO. There is only one of these at a * With behaviour == ORIGINAL this will stall (calling yield()) until an audio block
* time, and they hold 128 float32_t. allocate_f32 will hold up * becomes available - there's no real guarantee this will ever happen...
* a return from getBuffer() if Audio memory is not available. This will * With behaviour == NON_STALLING this will never stall, and will conform to the published
* be freed up by update(). * 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) float32_t* AudioPlayQueue_F32::getBuffer(void)
{ {
if (userblock) return userblock->data; if (NULL == userblock) // not got one: try to get one
while (1) { {
userblock = allocate_f32(); switch (behaviour)
if (userblock) return userblock->data; {
yield(); 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;
} }
/* playBuffer() can be called anytime after data is /* Queue userblock for later playback in update().
* loaded to the data block pointed to by getBuffer). * If there's no user block in use then we presume success: this means it's
* This function then enters the pointer to the queue, * safe to keep calling playBuffer() regularly even if we've not been
* waiting to be sent in turn. If the queue is full, * creating audio to be played.
* this function waits until a spot in the queue is opened * return 0 for success, 1 for re-try required */
* up by update() (called by interrupts). uint32_t AudioPlayQueue_F32::playBuffer(void)
*/
void AudioPlayQueue_F32::playBuffer(void)
{ {
uint32_t h; uint32_t result = 0;
uint32_t h;
if (!userblock) return; if (userblock) // only need to queue if we have a user block!
h = head + 1; {
if (h >= 32) h = 0; // Find place for next queue entry
while (tail == h) ; // wait until space in the queue h = head + 1;
queue[h] = userblock; if (h >= max_buffers) h = 0;
head = h; // Wait for space, or return "please re-try", depending on behaviour
userblock = NULL; 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;
} }
void AudioPlayQueue_F32::update(void) /* 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)
{ {
audio_block_f32_t *block; uint32_t result = 1;
uint32_t t; float32_t* buf = getBuffer();
do
t = tail; {
if (t != head) { // a data block is available to transmit out if (NULL == buf) // no buffer, failed already
if (++t >= 32) t = 0; // tail is advanced by one, circularly break;
block = queue[t]; // pointer to next block if (uptr >= AUDIO_BLOCK_SAMPLES) // buffer is full, we're re-called: try again
tail = t; {
transmit(block); if (0 == playBuffer()) // success emitting old buffer...
release(block); {
} 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;
} }
//assume user already has an audio_block that was NOT allocated by this /*
//playBuffer. Here, you hand it your buffer. This object takes ownership * Put multiple samples to buffer(s), and queue if buffer(s) full.
//of it and puts it into the queue * 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) { void AudioPlayQueue_F32::playAudioBlock(audio_block_f32_t *audio_block) {
uint32_t h; uint32_t h;
if (!audio_block) return; if (!audio_block) return;
h = head + 1; h = head + 1;
if (h >= 32) h = 0; if (h >= max_buffers) h = 0;
while (tail == h) ; // wait until space in the queue while (tail == h) ; // wait until space in the queue
queue[h] = audio_block; queue[h] = audio_block;
audio_block->ref_count++; //take ownership of this block audio_block->ref_count++; //take ownership of this block
head = h; head = h;
//userblock = NULL; 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
}
} }

@ -1,29 +1,39 @@
/* /*
* AudioPlayQueue_F32 * AudioPlayQueue_F32
* *
* Created: Chip Audette (OpenAudio), Feb 2017 * Created: Chip Audette (OpenAudio), Feb 2017
* Extended from on Teensy Audio Library * Extended from Audio Library for Teensy 3.X
* * License: MIT License. Use at your own risk.
* 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
/* Notes from Paul Stoffregen (from 4320 LED Video+Sound Project) * Teensy Audio library. Thanks toJonathan Oakley for the improvements.
* * Bob Larkin bob@janbob.com
* AudioPlayQueue - Play audio data provided by the Arduino sketch. *
* This object provides functions to allow the sketch code to push data * Audio Library for Teensy 3.X
* into the audio system. * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
* *
* getBuffer(); * Development of this audio library was funded by PJRC.COM, LLC by sales of
* Returns a pointer to an array of 128 int16. * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* This buffer is within the audio library memory pool, providing the most * open source software by purchasing Teensy or other PJRC products.
* efficient way to input data to the audio system. The buffer is likely *
* to be populated by previously used data, so the entire 128 words should * Permission is hereby granted, free of charge, to any person obtaining a copy
* be written before calling playBuffer(). Only a single buffer should be * of this software and associated documentation files (the "Software"), to deal
* requested at a time. This function may return NULL if no memory * in the Software without restriction, including without limitation the rights
* is available. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* * copies of the Software, and to permit persons to whom the Software is
* playBuffer(); * furnished to do so, subject to the following conditions:
* Transmit the buffer previously obtained from getBuffer(). *
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/ */
#ifndef play_queue_f32_h_ #ifndef play_queue_f32_h_
@ -35,26 +45,43 @@
class AudioPlayQueue_F32 : public AudioStream_F32 class AudioPlayQueue_F32 : public AudioStream_F32
{ {
//GUI: inputs:0, outputs:1 //this line used for automatic generation of GUI node //GUI: inputs:0, outputs:1 //this line used for automatic generation of GUI node
private:
#if defined(__IMXRT1062__) || defined(__MK66FX1M0__) || defined(__MK64FX512__)
static const unsigned int MAX_BUFFERS = 80;
#else
static const unsigned int MAX_BUFFERS = 32;
#endif
public: public:
AudioPlayQueue_F32(void) : AudioStream_F32(0, NULL), AudioPlayQueue_F32(void) : AudioStream_F32(0, NULL),
userblock(NULL), head(0), tail(0) { } userblock(NULL), uptr(0), head(0), tail(0), max_buffers(MAX_BUFFERS) { }
AudioPlayQueue_F32(const AudioSettings_F32 &settings) : AudioStream_F32(0, NULL), AudioPlayQueue_F32(const AudioSettings_F32 &settings) : AudioStream_F32(0, NULL),
userblock(NULL), head(0), tail(0) { } userblock(NULL), uptr(0), head(0), tail(0), max_buffers(MAX_BUFFERS) { }
//void play(int16_t data);
//void play(const int16_t *data, uint32_t len); uint32_t play(float32_t data);
//void play(float32_t data); uint32_t play(const float32_t *data, uint32_t len);
//void play(const float32_t *data, uint32_t len); void playAudioBlock(audio_block_f32_t *audio_block);
void playAudioBlock(audio_block_f32_t *); // Not in I16 library bool available(void);
bool available(void); // Returns a pointer to an array of AUDIO_BLOCK_SAMPLES (usually 128)
float32_t * getBuffer(void); // float32_t. This buffer is within the audio library memory pool.
void playBuffer(void); // Only a single buffer is allocated at any one time: repeated calls
void stop(void); // to getBuffer() without calling playBuffer() will yield the same address.
//bool isPlaying(void) { return playing; } float32_t * getBuffer(void);
virtual void update(void); uint32_t playBuffer(void);
void stop(void);
void setMaxBuffers(uint8_t);
virtual void update(void);
enum behaviour_e {ORIGINAL,NON_STALLING};
void setBehaviour(behaviour_e behave)
{
behaviour = behave;
}
private: private:
audio_block_f32_t *queue[32]; audio_block_f32_t *queue[MAX_BUFFERS];
audio_block_f32_t *userblock; audio_block_f32_t *userblock;
volatile uint8_t head, tail; unsigned int uptr; // actually an index, NOT a pointer!
volatile uint8_t head, tail;
volatile uint8_t max_buffers;
behaviour_e behaviour;
}; };
#endif #endif

Loading…
Cancel
Save