parent
20a68c6ab3
commit
baa0a36268
@ -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…
Reference in new issue