block = receiveReadOnly(); | block = receiveReadOnly(); | ||||
if (!block) return; | if (!block) return; | ||||
#if defined(KINETISK) | |||||
switch (state) { | switch (state) { | ||||
case 0: | case 0: | ||||
blocklist[0] = block; | blocklist[0] = block; | ||||
state = 4; | state = 4; | ||||
break; | break; | ||||
} | } | ||||
#else | |||||
release(block); | |||||
#endif | |||||
} | } | ||||
dc_average = sum >> 10; | dc_average = sum >> 10; | ||||
// set the programmable delay block to trigger the ADC at 44.1 kHz | // set the programmable delay block to trigger the ADC at 44.1 kHz | ||||
#if defined(KINETISK) | |||||
if (!(SIM_SCGC6 & SIM_SCGC6_PDB) | if (!(SIM_SCGC6 & SIM_SCGC6_PDB) | ||||
|| (PDB0_SC & PDB_CONFIG) != PDB_CONFIG | || (PDB0_SC & PDB_CONFIG) != PDB_CONFIG | ||||
|| PDB0_MOD != PDB_PERIOD | || PDB0_MOD != PDB_PERIOD | ||||
PDB0_SC = PDB_CONFIG | PDB_SC_SWTRIG; | PDB0_SC = PDB_CONFIG | PDB_SC_SWTRIG; | ||||
PDB0_CH0C1 = 0x0101; | PDB0_CH0C1 = 0x0101; | ||||
} | } | ||||
#endif | |||||
// enable the ADC for hardware trigger and DMA | // enable the ADC for hardware trigger and DMA | ||||
ADC0_SC2 |= ADC_SC2_ADTRG | ADC_SC2_DMAEN; | ADC0_SC2 |= ADC_SC2_ADTRG | ADC_SC2_DMAEN; | ||||
// set up a DMA channel to store the ADC data | // set up a DMA channel to store the ADC data | ||||
dma.begin(true); | dma.begin(true); | ||||
#if defined(KINETISK) | |||||
dma.TCD->SADDR = &ADC0_RA; | dma.TCD->SADDR = &ADC0_RA; | ||||
dma.TCD->SOFF = 0; | dma.TCD->SOFF = 0; | ||||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | ||||
dma.TCD->DLASTSGA = -sizeof(analog_rx_buffer); | dma.TCD->DLASTSGA = -sizeof(analog_rx_buffer); | ||||
dma.TCD->BITER_ELINKNO = sizeof(analog_rx_buffer) / 2; | dma.TCD->BITER_ELINKNO = sizeof(analog_rx_buffer) / 2; | ||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | ||||
#endif | |||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0); | dma.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0); | ||||
update_responsibility = update_setup(); | update_responsibility = update_setup(); | ||||
dma.enable(); | dma.enable(); | ||||
uint16_t *dest_left; | uint16_t *dest_left; | ||||
audio_block_t *left; | audio_block_t *left; | ||||
#if defined(KINETISK) | |||||
daddr = (uint32_t)(dma.TCD->DADDR); | daddr = (uint32_t)(dma.TCD->DADDR); | ||||
#endif | |||||
dma.clearInterrupt(); | dma.clearInterrupt(); | ||||
if (daddr < (uint32_t)analog_rx_buffer + sizeof(analog_rx_buffer) / 2) { | if (daddr < (uint32_t)analog_rx_buffer + sizeof(analog_rx_buffer) / 2) { |
#include "mixer.h" | #include "mixer.h" | ||||
#include "utility/dspinst.h" | #include "utility/dspinst.h" | ||||
void applyGain(int16_t *data, int32_t mult) | |||||
#if defined(KINETISK) | |||||
#define MULTI_UNITYGAIN 65536 | |||||
static void applyGain(int16_t *data, int32_t mult) | |||||
{ | { | ||||
uint32_t *p = (uint32_t *)data; | uint32_t *p = (uint32_t *)data; | ||||
const uint32_t *end = (uint32_t *)(data + AUDIO_BLOCK_SAMPLES); | const uint32_t *end = (uint32_t *)(data + AUDIO_BLOCK_SAMPLES); | ||||
int32_t val2 = signed_multiply_32x16t(mult, tmp32); | int32_t val2 = signed_multiply_32x16t(mult, tmp32); | ||||
val1 = signed_saturate_rshift(val1, 16, 0); | val1 = signed_saturate_rshift(val1, 16, 0); | ||||
val2 = signed_saturate_rshift(val2, 16, 0); | val2 = signed_saturate_rshift(val2, 16, 0); | ||||
*p++ = pack_16x16(val2, val1); | |||||
*p++ = pack_16b_16b(val2, val1); | |||||
} while (p < end); | } while (p < end); | ||||
} | } | ||||
// page 133 | |||||
void applyGainThenAdd(int16_t *data, const int16_t *in, int32_t mult) | |||||
static void applyGainThenAdd(int16_t *data, const int16_t *in, int32_t mult) | |||||
{ | { | ||||
uint32_t *dst = (uint32_t *)data; | uint32_t *dst = (uint32_t *)data; | ||||
const uint32_t *src = (uint32_t *)in; | const uint32_t *src = (uint32_t *)in; | ||||
const uint32_t *end = (uint32_t *)(data + AUDIO_BLOCK_SAMPLES); | const uint32_t *end = (uint32_t *)(data + AUDIO_BLOCK_SAMPLES); | ||||
if (mult == 65536) { | |||||
if (mult == MULTI_UNITYGAIN) { | |||||
do { | do { | ||||
uint32_t tmp32 = *dst; | uint32_t tmp32 = *dst; | ||||
*dst++ = signed_add_16_and_16(tmp32, *src++); | *dst++ = signed_add_16_and_16(tmp32, *src++); | ||||
int32_t val2 = signed_multiply_32x16t(mult, tmp32); | int32_t val2 = signed_multiply_32x16t(mult, tmp32); | ||||
val1 = signed_saturate_rshift(val1, 16, 0); | val1 = signed_saturate_rshift(val1, 16, 0); | ||||
val2 = signed_saturate_rshift(val2, 16, 0); | val2 = signed_saturate_rshift(val2, 16, 0); | ||||
tmp32 = pack_16x16(val2, val1); | |||||
tmp32 = pack_16b_16b(val2, val1); | |||||
uint32_t tmp32b = *dst; | uint32_t tmp32b = *dst; | ||||
*dst++ = signed_add_16_and_16(tmp32, tmp32b); | *dst++ = signed_add_16_and_16(tmp32, tmp32b); | ||||
} while (dst < end); | } while (dst < end); | ||||
} | } | ||||
} | } | ||||
#elif defined(KINETISL) | |||||
#define MULTI_UNITYGAIN 256 | |||||
static void applyGain(int16_t *data, int32_t mult) | |||||
{ | |||||
const int16_t *end = data + AUDIO_BLOCK_SAMPLES; | |||||
do { | |||||
int32_t val = *data * mult; | |||||
*data++ = signed_saturate_rshift(val, 16, 0); | |||||
} while (data < end); | |||||
} | |||||
static void applyGainThenAdd(int16_t *dst, const int16_t *src, int32_t mult) | |||||
{ | |||||
const int16_t *end = dst + AUDIO_BLOCK_SAMPLES; | |||||
if (mult == MULTI_UNITYGAIN) { | |||||
do { | |||||
int32_t val = *dst + *src++; | |||||
*dst++ = signed_saturate_rshift(val, 16, 0); | |||||
} while (dst < end); | |||||
} else { | |||||
do { | |||||
int32_t val = *dst + ((*src++ * mult) >> 8); // overflow possible?? | |||||
*dst++ = signed_saturate_rshift(val, 16, 0); | |||||
} while (dst < end); | |||||
} | |||||
} | |||||
#endif | |||||
void AudioMixer4::update(void) | void AudioMixer4::update(void) | ||||
{ | { | ||||
out = receiveWritable(channel); | out = receiveWritable(channel); | ||||
if (out) { | if (out) { | ||||
int32_t mult = multiplier[channel]; | int32_t mult = multiplier[channel]; | ||||
if (mult != 65536) applyGain(out->data, mult); | |||||
if (mult != MULTI_UNITYGAIN) applyGain(out->data, mult); | |||||
} | } | ||||
} else { | } else { | ||||
in = receiveReadOnly(channel); | in = receiveReadOnly(channel); |
class AudioMixer4 : public AudioStream | class AudioMixer4 : public AudioStream | ||||
{ | { | ||||
#if defined(KINETISK) | |||||
public: | public: | ||||
AudioMixer4(void) : AudioStream(4, inputQueueArray) { | AudioMixer4(void) : AudioStream(4, inputQueueArray) { | ||||
for (int i=0; i<4; i++) multiplier[i] = 65536; | for (int i=0; i<4; i++) multiplier[i] = 65536; | ||||
private: | private: | ||||
int32_t multiplier[4]; | int32_t multiplier[4]; | ||||
audio_block_t *inputQueueArray[4]; | audio_block_t *inputQueueArray[4]; | ||||
#elif defined(KINETISL) | |||||
public: | |||||
AudioMixer4(void) : AudioStream(4, inputQueueArray) { | |||||
for (int i=0; i<4; i++) multiplier[i] = 256; | |||||
} | |||||
virtual void update(void); | |||||
void gain(unsigned int channel, float gain) { | |||||
if (channel >= 4) return; | |||||
if (gain > 127.0f) gain = 127.0f; | |||||
else if (gain < 0.0f) gain = 0.0f; | |||||
multiplier[channel] = gain * 256.0f; // TODO: proper roundoff? | |||||
} | |||||
private: | |||||
int16_t multiplier[4]; | |||||
audio_block_t *inputQueueArray[4]; | |||||
#endif | |||||
}; | }; | ||||
#endif | #endif |
config_i2s(); | config_i2s(); | ||||
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 | CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 | ||||
#if defined(KINETISK) | |||||
dma.TCD->SADDR = i2s_tx_buffer; | dma.TCD->SADDR = i2s_tx_buffer; | ||||
dma.TCD->SOFF = 2; | dma.TCD->SOFF = 2; | ||||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | ||||
dma.TCD->DLASTSGA = 0; | dma.TCD->DLASTSGA = 0; | ||||
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | ||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | ||||
#endif | |||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | ||||
update_responsibility = update_setup(); | update_responsibility = update_setup(); | ||||
dma.enable(); | dma.enable(); | ||||
audio_block_t *block; | audio_block_t *block; | ||||
uint32_t saddr, offset; | uint32_t saddr, offset; | ||||
#if defined(KINETISK) | |||||
saddr = (uint32_t)(dma.TCD->SADDR); | saddr = (uint32_t)(dma.TCD->SADDR); | ||||
#endif | |||||
dma.clearInterrupt(); | dma.clearInterrupt(); | ||||
if (saddr < (uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2) { | if (saddr < (uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2) { | ||||
// DMA is transmitting the first half of the buffer | // DMA is transmitting the first half of the buffer | ||||
AudioOutputI2Sslave::config_i2s(); | AudioOutputI2Sslave::config_i2s(); | ||||
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 | CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 | ||||
#if defined(KINETISK) | |||||
dma.TCD->SADDR = i2s_tx_buffer; | dma.TCD->SADDR = i2s_tx_buffer; | ||||
dma.TCD->SOFF = 2; | dma.TCD->SOFF = 2; | ||||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | ||||
dma.TCD->DLASTSGA = 0; | dma.TCD->DLASTSGA = 0; | ||||
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | ||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | ||||
#endif | |||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | ||||
update_responsibility = update_setup(); | update_responsibility = update_setup(); | ||||
dma.enable(); | dma.enable(); |
static inline int32_t signed_saturate_rshift(int32_t val, int bits, int rshift) __attribute__((always_inline, unused)); | static inline int32_t signed_saturate_rshift(int32_t val, int bits, int rshift) __attribute__((always_inline, unused)); | ||||
static inline int32_t signed_saturate_rshift(int32_t val, int bits, int rshift) | static inline int32_t signed_saturate_rshift(int32_t val, int bits, int rshift) | ||||
{ | { | ||||
#if defined(KINETISK) | |||||
int32_t out; | int32_t out; | ||||
asm volatile("ssat %0, %1, %2, asr %3" : "=r" (out) : "I" (bits), "r" (val), "I" (rshift)); | asm volatile("ssat %0, %1, %2, asr %3" : "=r" (out) : "I" (bits), "r" (val), "I" (rshift)); | ||||
return out; | return out; | ||||
#elif defined(KINETISL) | |||||
int32_t out, max; | |||||
out = val >> rshift; | |||||
max = 1 << (bits - 1); | |||||
if (out >= 0) { | |||||
if (out > max - 1) out = max - 1; | |||||
} else { | |||||
if (out < -max) out = -max; | |||||
} | |||||
return out; | |||||
#endif | |||||
} | } | ||||
// computes ((a[31:0] * b[15:0]) >> 16) | // computes ((a[31:0] * b[15:0]) >> 16) | ||||
static inline int32_t signed_multiply_32x16b(int32_t a, uint32_t b) __attribute__((always_inline, unused)); | static inline int32_t signed_multiply_32x16b(int32_t a, uint32_t b) __attribute__((always_inline, unused)); | ||||
static inline int32_t signed_multiply_32x16b(int32_t a, uint32_t b) | static inline int32_t signed_multiply_32x16b(int32_t a, uint32_t b) | ||||
{ | { | ||||
#if defined(KINETISK) | |||||
int32_t out; | int32_t out; | ||||
asm volatile("smulwb %0, %1, %2" : "=r" (out) : "r" (a), "r" (b)); | asm volatile("smulwb %0, %1, %2" : "=r" (out) : "r" (a), "r" (b)); | ||||
return out; | return out; | ||||
#elif defined(KINETISL) | |||||
return ((int64_t)a * (int16_t)(b & 0xFFFF)) >> 16; | |||||
#endif | |||||
} | } | ||||
// computes ((a[31:0] * b[31:16]) >> 16) | // computes ((a[31:0] * b[31:16]) >> 16) | ||||
static inline int32_t signed_multiply_32x16t(int32_t a, uint32_t b) __attribute__((always_inline, unused)); | static inline int32_t signed_multiply_32x16t(int32_t a, uint32_t b) __attribute__((always_inline, unused)); | ||||
static inline int32_t signed_multiply_32x16t(int32_t a, uint32_t b) | static inline int32_t signed_multiply_32x16t(int32_t a, uint32_t b) | ||||
{ | { | ||||
#if defined(KINETISK) | |||||
int32_t out; | int32_t out; | ||||
asm volatile("smulwt %0, %1, %2" : "=r" (out) : "r" (a), "r" (b)); | asm volatile("smulwt %0, %1, %2" : "=r" (out) : "r" (a), "r" (b)); | ||||
return out; | return out; | ||||
#elif defined(KINETISL) | |||||
return ((int64_t)a * (int16_t)(b >> 16)) >> 16; | |||||
#endif | |||||
} | } | ||||
// computes (((int64_t)a[31:0] * (int64_t)b[31:0]) >> 32) | // computes (((int64_t)a[31:0] * (int64_t)b[31:0]) >> 32) | ||||
static inline uint32_t pack_16t_16t(int32_t a, int32_t b) __attribute__((always_inline, unused)); | static inline uint32_t pack_16t_16t(int32_t a, int32_t b) __attribute__((always_inline, unused)); | ||||
static inline uint32_t pack_16t_16t(int32_t a, int32_t b) | static inline uint32_t pack_16t_16t(int32_t a, int32_t b) | ||||
{ | { | ||||
#if defined(KINETISK) | |||||
int32_t out; | int32_t out; | ||||
asm volatile("pkhtb %0, %1, %2, asr #16" : "=r" (out) : "r" (a), "r" (b)); | asm volatile("pkhtb %0, %1, %2, asr #16" : "=r" (out) : "r" (a), "r" (b)); | ||||
return out; | return out; | ||||
#elif defined(KINETISL) | |||||
return (a & 0xFFFF0000) | ((uint32_t)b >> 16); | |||||
#endif | |||||
} | } | ||||
// computes (a[31:16] | b[15:0]) | // computes (a[31:16] | b[15:0]) | ||||
static inline uint32_t pack_16t_16b(int32_t a, int32_t b) __attribute__((always_inline, unused)); | static inline uint32_t pack_16t_16b(int32_t a, int32_t b) __attribute__((always_inline, unused)); | ||||
static inline uint32_t pack_16t_16b(int32_t a, int32_t b) | static inline uint32_t pack_16t_16b(int32_t a, int32_t b) | ||||
{ | { | ||||
#if defined(KINETISK) | |||||
int32_t out; | int32_t out; | ||||
asm volatile("pkhtb %0, %1, %2" : "=r" (out) : "r" (a), "r" (b)); | asm volatile("pkhtb %0, %1, %2" : "=r" (out) : "r" (a), "r" (b)); | ||||
return out; | return out; | ||||
#elif defined(KINETISL) | |||||
return (a & 0xFFFF0000) | (b & 0x0000FFFF); | |||||
#endif | |||||
} | } | ||||
// computes ((a[15:0] << 16) | b[15:0]) | // computes ((a[15:0] << 16) | b[15:0]) | ||||
static inline uint32_t pack_16b_16b(int32_t a, int32_t b) __attribute__((always_inline, unused)); | static inline uint32_t pack_16b_16b(int32_t a, int32_t b) __attribute__((always_inline, unused)); | ||||
static inline uint32_t pack_16b_16b(int32_t a, int32_t b) | static inline uint32_t pack_16b_16b(int32_t a, int32_t b) | ||||
{ | { | ||||
#if defined(KINETISK) | |||||
int32_t out; | int32_t out; | ||||
asm volatile("pkhbt %0, %1, %2, lsl #16" : "=r" (out) : "r" (b), "r" (a)); | asm volatile("pkhbt %0, %1, %2, lsl #16" : "=r" (out) : "r" (b), "r" (a)); | ||||
return out; | return out; | ||||
#elif defined(KINETISL) | |||||
return (a << 16) | (b & 0x0000FFFF); | |||||
#endif | |||||
} | } | ||||
// computes ((a[15:0] << 16) | b[15:0]) | // computes ((a[15:0] << 16) | b[15:0]) | ||||
/* | |||||
static inline uint32_t pack_16x16(int32_t a, int32_t b) __attribute__((always_inline, unused)); | static inline uint32_t pack_16x16(int32_t a, int32_t b) __attribute__((always_inline, unused)); | ||||
static inline uint32_t pack_16x16(int32_t a, int32_t b) | static inline uint32_t pack_16x16(int32_t a, int32_t b) | ||||
{ | { | ||||
asm volatile("pkhbt %0, %1, %2, lsl #16" : "=r" (out) : "r" (b), "r" (a)); | asm volatile("pkhbt %0, %1, %2, lsl #16" : "=r" (out) : "r" (b), "r" (a)); | ||||
return out; | return out; | ||||
} | } | ||||
*/ | |||||
// computes (((a[31:16] + b[31:16]) << 16) | (a[15:0 + b[15:0])) | // computes (((a[31:16] + b[31:16]) << 16) | (a[15:0 + b[15:0])) | ||||
static inline uint32_t signed_add_16_and_16(uint32_t a, uint32_t b) __attribute__((always_inline, unused)); | static inline uint32_t signed_add_16_and_16(uint32_t a, uint32_t b) __attribute__((always_inline, unused)); |