| 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)); |