/* 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 "AudioStream.h" audio_block_t * AudioStream::memory_pool; uint8_t AudioStream::memory_pool_size = 0; uint32_t AudioStream::memory_pool_available_mask; 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) { //Serial.println("AudioStream initialize_memory"); memory_pool = data; if (num > 31) num = 31; memory_pool_size = num; memory_pool_available_mask = 0xFFFFFFFF; for (unsigned int i=0; i < num; i++) { data[i].memory_pool_index = i; } } // 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, avail; audio_block_t *block; uint8_t used; __disable_irq(); avail = memory_pool_available_mask; n = __builtin_clz(avail); if (n >= memory_pool_size) { __enable_irq(); return NULL; } memory_pool_available_mask = avail & ~(0x80000000 >> n); used = memory_used + 1; memory_used = used; __enable_irq(); block = memory_pool + n; block->ref_count = 1; if (used > memory_used_max) memory_used_max = used; 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 >> block->memory_pool_index); __disable_irq(); if (block->ref_count > 1) { block->ref_count--; } else { memory_pool_available_mask |= 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 is released // after it's transmitted. 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, 0xFF); // 0xFF = lowest priority NVIC_ENABLE_IRQ(IRQ_SOFTWARE); update_scheduled = true; return true; } 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; }