From baa0a362682e54552f355dd7ac35b42d7c26c517 Mon Sep 17 00:00:00 2001 From: Chip Audette Date: Sun, 19 Feb 2017 09:42:43 -0500 Subject: [PATCH] Add some NotUsed false starts --- NotUsed/AudioStream_Base.cpp | 247 +++++++++++++++++++++++++++++++++++ NotUsed/AudioStream_Base.h | 184 ++++++++++++++++++++++++++ NotUsed/memcpy_interleave.S | 68 ++++++++++ NotUsed/memcpy_interleave.h | 18 +++ 4 files changed, 517 insertions(+) create mode 100644 NotUsed/AudioStream_Base.cpp create mode 100644 NotUsed/AudioStream_Base.h create mode 100644 NotUsed/memcpy_interleave.S create mode 100644 NotUsed/memcpy_interleave.h diff --git a/NotUsed/AudioStream_Base.cpp b/NotUsed/AudioStream_Base.cpp new file mode 100644 index 0000000..b52acc4 --- /dev/null +++ b/NotUsed/AudioStream_Base.cpp @@ -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 // 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; +} + diff --git a/NotUsed/AudioStream_Base.h b/NotUsed/AudioStream_Base.h new file mode 100644 index 0000000..7c6899b --- /dev/null +++ b/NotUsed/AudioStream_Base.h @@ -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 // for NULL +#include // 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 + diff --git a/NotUsed/memcpy_interleave.S b/NotUsed/memcpy_interleave.S new file mode 100644 index 0000000..c9dca16 --- /dev/null +++ b/NotUsed/memcpy_interleave.S @@ -0,0 +1,68 @@ + +#include + +.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 + \ No newline at end of file diff --git a/NotUsed/memcpy_interleave.h b/NotUsed/memcpy_interleave.h new file mode 100644 index 0000000..d5b8195 --- /dev/null +++ b/NotUsed/memcpy_interleave.h @@ -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 \ No newline at end of file