From f24ebdf8ec33f02e86796a0b5c9c36d2eb5c6b3b Mon Sep 17 00:00:00 2001 From: Chip Audette Date: Sun, 19 Feb 2017 10:48:21 -0500 Subject: [PATCH] Clarify that the pink and white noise needs mods from AudioStream --- NotUsed/AudioStream_modWithDisconnect.cpp | 277 ++++++++++++++++++++++ NotUsed/AudioStream_modWithDisconnect.h | 173 ++++++++++++++ NotUsed/synth_pinknoise_f32.h | 6 +- NotUsed/synth_whitenoise_f32.h | 6 +- 4 files changed, 456 insertions(+), 6 deletions(-) create mode 100644 NotUsed/AudioStream_modWithDisconnect.cpp create mode 100644 NotUsed/AudioStream_modWithDisconnect.h diff --git a/NotUsed/AudioStream_modWithDisconnect.cpp b/NotUsed/AudioStream_modWithDisconnect.cpp new file mode 100644 index 0000000..943b8d3 --- /dev/null +++ b/NotUsed/AudioStream_modWithDisconnect.cpp @@ -0,0 +1,277 @@ +/* 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; +} + +void AudioStream::disconnectFromUpdateAll(void) { + //search through all objects to find this one + if (first_update == NULL) { + return; + } else { + AudioStream *p; + //step through all of the objects + for (p=first_update; p->next_update; p = p->next_update) { + if (p->next_update == this) { + //this object "p" points to the one that we want to remove + + //does the chain continue past the one we want to remove? + if (next_update != NULL) { + //it does continue. so, connect the next one to p instead + p->next_update = next_update; + } else { + //the chain does not continue past the one we want to remove + p->next_update = NULL; + } + + //set our own object to have no next + next_update = NULL; + + break; //break out of the for loop + } + + } + + } +} \ No newline at end of file diff --git a/NotUsed/AudioStream_modWithDisconnect.h b/NotUsed/AudioStream_modWithDisconnect.h new file mode 100644 index 0000000..8a1e308 --- /dev/null +++ b/NotUsed/AudioStream_modWithDisconnect.h @@ -0,0 +1,173 @@ +/* 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_h +#define AudioStream_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. + + +//#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) +#define AUDIO_BLOCK_SAMPLES 128 +#define AUDIO_SAMPLE_RATE 44117.64706 +#define 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 +//#elif defined(__MKL26Z64__) +//#define AUDIO_BLOCK_SAMPLES 64 +//#define AUDIO_SAMPLE_RATE 22058.82353 +//#define AUDIO_SAMPLE_RATE_EXACT 22058.82353 // 48 MHz / 2176, or 96 MHz * 1 / 17 / 256 +//#endif + +#ifndef __ASSEMBLER__ +class AudioStream; +class AudioConnection; + +typedef struct audio_block_struct { + unsigned char ref_count; + unsigned char memory_pool_index; + unsigned char reserved1; + unsigned char reserved2; + int16_t data[AUDIO_BLOCK_SAMPLES]; +} audio_block_t; + + +class AudioConnection +{ +public: + AudioConnection(AudioStream &source, AudioStream &destination) : + src(source), dst(destination), src_index(0), dest_index(0), + next_dest(NULL) + { connect(); } + AudioConnection(AudioStream &source, unsigned char sourceOutput, + AudioStream &destination, unsigned char destinationInput) : + src(source), dst(destination), + src_index(sourceOutput), dest_index(destinationInput), + next_dest(NULL) + { connect(); } + friend class AudioStream; +protected: + void connect(void); + AudioStream &src; + AudioStream &dst; + unsigned char src_index; + unsigned char dest_index; + AudioConnection *next_dest; +}; + + +#define AudioMemory(num) ({ \ + static DMAMEM audio_block_t data[num]; \ + AudioStream::initialize_memory(data, num); \ +}) + +#define CYCLE_COUNTER_APPROX_PERCENT(n) (((n) + (F_CPU / 32 / AUDIO_SAMPLE_RATE * AUDIO_BLOCK_SAMPLES / 100)) / (F_CPU / 16 / AUDIO_SAMPLE_RATE * AUDIO_BLOCK_SAMPLES / 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 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; + void disconnectFromUpdateAll(void); +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 +#endif diff --git a/NotUsed/synth_pinknoise_f32.h b/NotUsed/synth_pinknoise_f32.h index 9076b7b..b8259d1 100644 --- a/NotUsed/synth_pinknoise_f32.h +++ b/NotUsed/synth_pinknoise_f32.h @@ -20,9 +20,9 @@ class AudioSynthNoisePink_F32 : public AudioStream_F32 //GUI: shortName:pinknoise //this line used for automatic generation of GUI node public: AudioSynthNoisePink_F32() : AudioStream_F32(0, NULL) { - noise.disconnectFromUpdateAll(); - i16_to_f32.disconnectFromUpdateAll(); - output_queue.disconnectFromUpdateAll(); + noise.disconnectFromUpdateAll(); //requires modification to AudioStream.h + i16_to_f32.disconnectFromUpdateAll(); //requires modification to AudioStream.h + output_queue.disconnectFromUpdateAll(); //requires modification to AudioStream.h patchCord100 = new AudioConnection(noise, 0, i16_to_f32, 0); //noise is an Int16 audio object. So, convert it! patchCord101 = new AudioConnection_F32(i16_to_f32, 0, output_queue, 0); diff --git a/NotUsed/synth_whitenoise_f32.h b/NotUsed/synth_whitenoise_f32.h index 6029717..e5c45d0 100644 --- a/NotUsed/synth_whitenoise_f32.h +++ b/NotUsed/synth_whitenoise_f32.h @@ -20,9 +20,9 @@ class AudioSynthNoiseWhite_F32 : public AudioStream_F32 //GUI: shortName:whitenoise //this line used for automatic generation of GUI node public: AudioSynthNoiseWhite_F32() : AudioStream_F32(0, NULL) { - noise.disconnectFromUpdateAll(); - i16_to_f32.disconnectFromUpdateAll(); - output_queue.disconnectFromUpdateAll(); + noise.disconnectFromUpdateAll(); //requires modification to AudioStream.h + i16_to_f32.disconnectFromUpdateAll(); //requires modification to AudioStream.h + output_queue.disconnectFromUpdateAll(); //requires modification to AudioStream.h patchCord100 = new AudioConnection(noise, 0, i16_to_f32, 0); //noise is an Int16 audio object. So, convert it! patchCord101 = new AudioConnection_F32(i16_to_f32, 0, output_queue, 0);