Add some NotUsed false starts

feature_setBlockSize
Chip Audette 8 years ago
parent 20a68c6ab3
commit baa0a36268
  1. 247
      NotUsed/AudioStream_Base.cpp
  2. 184
      NotUsed/AudioStream_Base.h
  3. 68
      NotUsed/memcpy_interleave.S
  4. 18
      NotUsed/memcpy_interleave.h

@ -0,0 +1,247 @@
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2013 PJRC.COM, LLC.
*
* 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:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* 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.
*/
#include <string.h> // for memcpy
#include "AudioStream.h"
audio_block_t * AudioStream::memory_pool;
uint32_t AudioStream::memory_pool_available_mask[6];
uint16_t AudioStream::cpu_cycles_total = 0;
uint16_t AudioStream::cpu_cycles_total_max = 0;
uint8_t AudioStream::memory_used = 0;
uint8_t AudioStream::memory_used_max = 0;
// Set up the pool of audio data blocks
// placing them all onto the free list
void AudioStream::initialize_memory(audio_block_t *data, unsigned int num)
{
unsigned int i;
//Serial.println("AudioStream initialize_memory");
//delay(10);
if (num > 192) num = 192;
__disable_irq();
memory_pool = data;
for (i=0; i < 6; i++) {
memory_pool_available_mask[i] = 0;
}
for (i=0; i < num; i++) {
memory_pool_available_mask[i >> 5] |= (1 << (i & 0x1F));
}
for (i=0; i < num; i++) {
data[i].memory_pool_index = i;
}
__enable_irq();
}
// Allocate 1 audio data block. If successful
// the caller is the only owner of this new block
audio_block_t * AudioStream::allocate(void)
{
uint32_t n, index, avail;
uint32_t *p;
audio_block_t *block;
uint8_t used;
p = memory_pool_available_mask;
__disable_irq();
do {
avail = *p; if (avail) break;
p++; avail = *p; if (avail) break;
p++; avail = *p; if (avail) break;
p++; avail = *p; if (avail) break;
p++; avail = *p; if (avail) break;
p++; avail = *p; if (avail) break;
__enable_irq();
//Serial.println("alloc:null");
return NULL;
} while (0);
n = __builtin_clz(avail);
*p = avail & ~(0x80000000 >> n);
used = memory_used + 1;
memory_used = used;
__enable_irq();
index = p - memory_pool_available_mask;
block = memory_pool + ((index << 5) + (31 - n));
block->ref_count = 1;
if (used > memory_used_max) memory_used_max = used;
//Serial.print("alloc:");
//Serial.println((uint32_t)block, HEX);
return block;
}
// Release ownership of a data block. If no
// other streams have ownership, the block is
// returned to the free pool
void AudioStream::release(audio_block_t *block)
{
uint32_t mask = (0x80000000 >> (31 - (block->memory_pool_index & 0x1F)));
uint32_t index = block->memory_pool_index >> 5;
__disable_irq();
if (block->ref_count > 1) {
block->ref_count--;
} else {
//Serial.print("reles:");
//Serial.println((uint32_t)block, HEX);
memory_pool_available_mask[index] |= mask;
memory_used--;
}
__enable_irq();
}
// Transmit an audio data block
// to all streams that connect to an output. The block
// becomes owned by all the recepients, but also is still
// owned by this object. Normally, a block must be released
// by the caller after it's transmitted. This allows the
// caller to transmit to same block to more than 1 output,
// and then release it once after all transmit calls.
void AudioStream::transmit(audio_block_t *block, unsigned char index)
{
for (AudioConnection *c = destination_list; c != NULL; c = c->next_dest) {
if (c->src_index == index) {
if (c->dst.inputQueue[c->dest_index] == NULL) {
c->dst.inputQueue[c->dest_index] = block;
block->ref_count++;
}
}
}
}
// Receive block from an input. The block's data
// may be shared with other streams, so it must not be written
audio_block_t * AudioStream::receiveReadOnly(unsigned int index)
{
audio_block_t *in;
if (index >= num_inputs) return NULL;
in = inputQueue[index];
inputQueue[index] = NULL;
return in;
}
// Receive block from an input. The block will not
// be shared, so its contents may be changed.
audio_block_t * AudioStream::receiveWritable(unsigned int index)
{
audio_block_t *in, *p;
if (index >= num_inputs) return NULL;
in = inputQueue[index];
inputQueue[index] = NULL;
if (in && in->ref_count > 1) {
p = allocate();
if (p) memcpy(p->data, in->data, sizeof(p->data));
in->ref_count--;
in = p;
}
return in;
}
void AudioConnection::connect(void)
{
AudioConnection *p;
if (dest_index > dst.num_inputs) return;
__disable_irq();
p = src.destination_list;
if (p == NULL) {
src.destination_list = this;
} else {
while (p->next_dest) p = p->next_dest;
p->next_dest = this;
}
src.active = true;
dst.active = true;
__enable_irq();
}
// When an object has taken responsibility for calling update_all()
// at each block interval (approx 2.9ms), this variable is set to
// true. Objects that are capable of calling update_all(), typically
// input and output based on interrupts, must check this variable in
// their constructors.
bool AudioStream::update_scheduled = false;
bool AudioStream::update_setup(void)
{
if (update_scheduled) return false;
NVIC_SET_PRIORITY(IRQ_SOFTWARE, 208); // 255 = lowest priority
NVIC_ENABLE_IRQ(IRQ_SOFTWARE);
update_scheduled = true;
return true;
}
void AudioStream::update_stop(void)
{
NVIC_DISABLE_IRQ(IRQ_SOFTWARE);
update_scheduled = false;
}
AudioStream * AudioStream::first_update = NULL;
void software_isr(void) // AudioStream::update_all()
{
AudioStream *p;
ARM_DEMCR |= ARM_DEMCR_TRCENA;
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
uint32_t totalcycles = ARM_DWT_CYCCNT;
//digitalWriteFast(2, HIGH);
for (p = AudioStream::first_update; p; p = p->next_update) {
if (p->active) {
uint32_t cycles = ARM_DWT_CYCCNT;
p->update();
// TODO: traverse inputQueueArray and release
// any input blocks that weren't consumed?
cycles = (ARM_DWT_CYCCNT - cycles) >> 4;
p->cpu_cycles = cycles;
if (cycles > p->cpu_cycles_max) p->cpu_cycles_max = cycles;
}
}
//digitalWriteFast(2, LOW);
totalcycles = (ARM_DWT_CYCCNT - totalcycles) >> 4;;
AudioStream::cpu_cycles_total = totalcycles;
if (totalcycles > AudioStream::cpu_cycles_total_max)
AudioStream::cpu_cycles_total_max = totalcycles;
}

@ -0,0 +1,184 @@
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2013 PJRC.COM, LLC.
*
* 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:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* 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 AudioStream_Base_h
#define AudioStream_Base_h
#ifndef __ASSEMBLER__
#include <stdio.h> // for NULL
#include <string.h> // for memcpy
#include "kinetis.h"
#endif
// AUDIO_BLOCK_SAMPLES determines how many samples the audio library processes
// per update. It may be reduced to achieve lower latency response to events,
// at the expense of higher interrupt and DMA setup overhead.
//
// Less than 32 may not work with some input & output objects. Multiples of 16
// should be used, since some synthesis objects generate 16 samples per loop.
//
// Some parts of the audio library may have hard-coded dependency on 128 samples.
// Please report these on the forum with reproducible test cases.
#define MAX_AUDIO_BLOCK_SAMPLES 128
#define DEFAULT_AUDIO_SAMPLE_RATE 44117.64706
#define DEFAULT_AUDIO_SAMPLE_RATE_EXACT 44117.64706 // 48 MHz / 1088, or 96 MHz * 2 / 17 / 256
//define AUDIO_SAMPLE_RATE 24000
//define AUDIO_SAMPLE_RATE_EXACT 23999 // 48 MHz / 1088, or 96 MHz * 2 / 17 / 256
class AudioStream;
class AudioConnection;
//typedef struct audio_block_struct {}
class AudioBlockBase {
public:
class audio_block_base(void);
unsigned char ref_count;
unsigned char memory_pool_index;
unsigned char reserved1;
unsigned char reserved2;
int length = MAX_AUDIO_BLOCK_SAMPLES;
const int max_len = MAX_AUDIO_BLOCK_SAMPLES;
float fs_Hz =DEFAULT_AUDIO_SAMPLE_RATE;
}
class audio_block_i16_t : AudioBlockBase {
public:
audio_block_i16_t(void);
audio_block_i16_t(const float _fs, const int _len) {
fs_Hz = _fs;
if (_len <= max_len) {
length = _length;
}
}
int16_t data[max_len];
};
class AudioConnection_I16
{
public:
AudioConnection_16(AudioStream_I16 &source, unsigned char sourceOutput,
AudioStream_I16 &destination, unsigned char destinationInput) :
src(source), dst(destination),
src_index(sourceOutput), dest_index(destinationInput),
next_dest(NULL)
{ connect(); }
friend class AudioStreamBase;
protected:
void connect(void);
AudioStream_I16 &src;
AudioStream_I16 &dst;
unsigned char src_index;
unsigned char dest_index;
AudioConnection_I16 *next_dest;
};
#define AudioMemory_I16(num) ({ \
static DMAMEM audio_block_i16_t data[num]; \
AudioStream_I16::initialize_memory(data, num); \
})
#define CYCLE_COUNTER_APPROX_PERCENT(n,fs,len) (((n) + (F_CPU / 32 / fs * len / 100)) / (F_CPU / 16 / fs * len / 100))
#define AudioProcessorUsage() (CYCLE_COUNTER_APPROX_PERCENT(AudioStream::cpu_cycles_total))
#define AudioProcessorUsageMax() (CYCLE_COUNTER_APPROX_PERCENT(AudioStream::cpu_cycles_total_max))
#define AudioProcessorUsageMaxReset() (AudioStream::cpu_cycles_total_max = AudioStream::cpu_cycles_total)
#define AudioMemoryUsage() (AudioStream::memory_used)
#define AudioMemoryUsageMax() (AudioStream::memory_used_max)
#define AudioMemoryUsageMaxReset() (AudioStream::memory_used_max = AudioStream::memory_used)
class AudioStreamBase
{
public:
AudioStream(
}
class AudioStream
{
public:
AudioStream(unsigned char ninput, audio_block_t **iqueue) :
num_inputs(ninput), inputQueue(iqueue) {
active = false;
destination_list = NULL;
for (int i=0; i < num_inputs; i++) {
inputQueue[i] = NULL;
}
// add to a simple list, for update_all
// TODO: replace with a proper data flow analysis in update_all
if (first_update == NULL) {
first_update = this;
} else {
AudioStream *p;
for (p=first_update; p->next_update; p = p->next_update) ;
p->next_update = this;
}
next_update = NULL;
cpu_cycles = 0;
cpu_cycles_max = 0;
}
static void initialize_memory(audio_block_t *data, unsigned int num);
int processorUsage(void) { return CYCLE_COUNTER_APPROX_PERCENT(cpu_cycles); }
int processorUsageMax(void) { return CYCLE_COUNTER_APPROX_PERCENT(cpu_cycles_max); }
void processorUsageMaxReset(void) { cpu_cycles_max = cpu_cycles; }
uint16_t cpu_cycles;
uint16_t cpu_cycles_max;
static uint16_t cpu_cycles_total;
static uint16_t cpu_cycles_total_max;
static uint8_t memory_used;
static uint8_t memory_used_max;
protected:
bool active;
unsigned char num_inputs;
static audio_block_t * allocate(void);
static void release(audio_block_t * block);
void transmit(audio_block_t *block, unsigned char index = 0);
audio_block_t * receiveReadOnly(unsigned int index = 0);
audio_block_t * receiveWritable(unsigned int index = 0);
static bool update_setup(void);
static void update_stop(void);
static void update_all(void) { NVIC_SET_PENDING(IRQ_SOFTWARE); }
friend void software_isr(void);
friend class AudioConnection;
private:
AudioConnection *destination_list;
audio_block_t **inputQueue;
static bool update_scheduled;
virtual void update(void) = 0;
static AudioStream *first_update; // for update_all
AudioStream *next_update; // for update_all
static audio_block_t *memory_pool;
static uint32_t memory_pool_available_mask[6];
};
#endif

@ -0,0 +1,68 @@
#include <AudioStream.h>
.cpu cortex-m4
.syntax unified
.thumb
.text
.align 2
/* void memcpy_tointerleaveLRwLen(short *dst, short *srcL, short *srcR, short len); */
.global memcpy_tointerleaveLRwLen
.thumb_func
memcpy_tointerleaveLRwLen:
@ r0: dst
@ r1: srcL
@ r2: srcR
@ r3: len
#if AUDIO_BLOCK_SAMPLES > 8
push {r4-r11,r14}
// add r14,r0,#(AUDIO_BLOCK_SAMPLES*2)
add r14,r0,r3 //add the number of samples (which is 1/4 of the 16-bit words to move)
add r14,r14,r3 //again (half)
add r14,r14,r3 //again (3/4)
add r14,r14,r3 //again (all)
.align 2
.loopLR:
//Load 2*4 words
ldmia r1!, {r5,r7,r9,r11} //1+4
ldmia r2!, {r6,r8,r10,r12} //1+4
pkhbt r3,r5,r6,LSL #16 //1
pkhtb r4,r6,r5,ASR #16 //1
pkhbt r5,r7,r8,LSL #16 //1
pkhtb r6,r8,r7,ASR #16 //1
pkhbt r7,r9,r10,LSL #16 //1
pkhtb r8,r10,r9,ASR #16 //1
pkhbt r9,r11,r12,LSL #16 //1
pkhtb r10,r12,r11,ASR #16 //1
//Write 8 Words
stmia r0!, {r3,r4,r5,r6,r7,r8,r9,r10} //1+8 -> 5+5+8+9 = 27 Cycles to interleave 32 bytes.
cmp r14, r0
bne .loopLR
pop {r4-r11,r14}
#elif AUDIO_BLOCK_SAMPLES == 8
push {r4-r8,r14}
ldmia r1!, {r5,r7}
ldmia r2!, {r6,r8}
pkhbt r3,r5,r6,LSL #16
pkhtb r4,r6,r5,ASR #16
pkhbt r5,r7,r8,LSL #16
pkhtb r6,r8,r7,ASR #16
stmia r0!, {r3,r4,r5,r6}
pop {r4-r8,r14}
#endif
BX lr

@ -0,0 +1,18 @@
#ifndef memcopy_interleave_h_
#define memcopy_interleave_h_
#ifdef __cplusplus
extern "C" {
#endif
void memcpy_tointerleaveLRwLen(int16_t *dst, const int16_t *srcL, const int16_t *srcR, const int16_t len);
//void memcpy_tointerleaveL(int16_t *dst, const int16_t *srcL);
//void memcpy_tointerleaveR(int16_t *dst, const int16_t *srcR);
//void memcpy_tointerleaveQuad(int16_t *dst, const int16_t *src1, const int16_t *src2,
// const int16_t *src3, const int16_t *src4);
#ifdef __cplusplus
}
#endif
#endif
Loading…
Cancel
Save