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

pull/5/merge
boblark 2 years ago
parent 4983644314
commit d5419e0af1
  1. 237
      play_queue_f32.cpp
  2. 105
      play_queue_f32.h

@ -5,82 +5,217 @@
* 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"
#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)
{
if (userblock) return true;
userblock = allocate_f32();
if (userblock) return true;
return false;
if (userblock) return true;
userblock = AudioStream_F32::allocate_f32();
if (userblock) return true;
return false;
}
/* getBuffer() returns a pointer to the data area of an AudioBlock_32
* that can be loaded in the .INO. There is only one of these at a
* time, and they hold 128 float32_t. allocate_f32 will hold up
* a return from getBuffer() if Audio memory is not available. This will
* be freed up by update().
/* 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)
float32_t* AudioPlayQueue_F32::getBuffer(void)
{
if (userblock) return userblock->data;
while (1) {
userblock = allocate_f32();
if (userblock) return userblock->data;
yield();
}
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;
}
/* playBuffer() can be called anytime after data is
* loaded to the data block pointed to by getBuffer).
* This function then enters the pointer to the queue,
* waiting to be sent in turn. If the queue is full,
* this function waits until a spot in the queue is opened
* up by update() (called by interrupts).
*/
void AudioPlayQueue_F32::playBuffer(void)
/* 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 h;
uint32_t result = 0;
uint32_t h;
if (!userblock) return;
h = head + 1;
if (h >= 32) h = 0;
while (tail == h) ; // wait until space in the queue
queue[h] = userblock;
head = h;
userblock = NULL;
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;
}
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 t;
t = tail;
if (t != head) { // a data block is available to transmit out
if (++t >= 32) t = 0; // tail is advanced by one, circularly
block = queue[t]; // pointer to next block
tail = t;
transmit(block);
release(block);
}
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;
}
//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
/*
* 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 >= 32) h = 0;
while (tail == h) ; // wait until space in the queue
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;
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
*
* Created: Chip Audette (OpenAudio), Feb 2017
* Extended from on Teensy Audio Library
*
* License: MIT License. Use at your own risk.
*
*/
/* Notes from Paul Stoffregen (from 4320 LED Video+Sound Project)
* AudioPlayQueue_F32
*
* AudioPlayQueue - Play audio data provided by the Arduino sketch.
* This object provides functions to allow the sketch code to push data
* into the audio system.
* Created: Chip Audette (OpenAudio), Feb 2017
* Extended from Audio Library for Teensy 3.X
* License: MIT License. Use at your own risk.
*
* getBuffer();
* Returns a pointer to an array of 128 int16.
* This buffer is within the audio library memory pool, providing the most
* 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
* be written before calling playBuffer(). Only a single buffer should be
* requested at a time. This function may return NULL if no memory
* is available.
* 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 toJonathan Oakley for the improvements.
* Bob Larkin bob@janbob.com
*
* playBuffer();
* Transmit the buffer previously obtained from getBuffer().
* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* 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_
@ -35,26 +45,43 @@
class AudioPlayQueue_F32 : public AudioStream_F32
{
//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:
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),
userblock(NULL), head(0), tail(0) { }
//void play(int16_t data);
//void play(const int16_t *data, uint32_t len);
//void play(float32_t data);
//void play(const float32_t *data, uint32_t len);
void playAudioBlock(audio_block_f32_t *); // Not in I16 library
bool available(void);
float32_t * getBuffer(void);
void playBuffer(void);
void stop(void);
//bool isPlaying(void) { return playing; }
virtual void update(void);
userblock(NULL), uptr(0), head(0), tail(0), max_buffers(MAX_BUFFERS) { }
uint32_t play(float32_t data);
uint32_t play(const float32_t *data, uint32_t len);
void playAudioBlock(audio_block_f32_t *audio_block);
bool available(void);
// Returns a pointer to an array of AUDIO_BLOCK_SAMPLES (usually 128)
// float32_t. This buffer is within the audio library memory pool.
// Only a single buffer is allocated at any one time: repeated calls
// to getBuffer() without calling playBuffer() will yield the same address.
float32_t * getBuffer(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:
audio_block_f32_t *queue[32];
audio_block_f32_t *userblock;
volatile uint8_t head, tail;
audio_block_f32_t *queue[MAX_BUFFERS];
audio_block_f32_t *userblock;
unsigned int uptr; // actually an index, NOT a pointer!
volatile uint8_t head, tail;
volatile uint8_t max_buffers;
behaviour_e behaviour;
};
#endif

Loading…
Cancel
Save