@ -25,9 +25,54 @@
*/
*/
# include "output_i2s_f32.h"
# include "output_i2s_f32.h"
# include "memcpy_audio.h"
//#include "input_i2s_f32.h"
//include "memcpy_audio.h"
//#include "memcpy_interleave.h"
# include <arm_math.h>
# include <arm_math.h>
//Here's the function to change the sample rate of the system (via changing the clocking of the I2S bus)
//https://forum.pjrc.com/threads/38753-Discussion-about-a-simple-way-to-change-the-sample-rate?p=121365&viewfull=1#post121365
float setI2SFreq ( const float freq_Hz ) {
int freq = ( int ) freq_Hz ;
typedef struct {
uint8_t mult ;
uint16_t div ;
} __attribute__ ( ( __packed__ ) ) tmclk ;
const int numfreqs = 16 ;
const int samplefreqs [ numfreqs ] = { 2000 , 8000 , 11025 , 16000 , 22050 , 24000 , 32000 , 44100 , 44117.64706 , 48000 , 88200 , 44117.64706 * 2 , 96000 , 176400 , 44117.64706 * 4 , 192000 } ;
# if (F_PLL==16000000)
const tmclk clkArr [ numfreqs ] = { { 4 , 125 } , { 16 , 125 } , { 148 , 839 } , { 32 , 125 } , { 145 , 411 } , { 48 , 125 } , { 64 , 125 } , { 151 , 214 } , { 12 , 17 } , { 96 , 125 } , { 151 , 107 } , { 24 , 17 } , { 192 , 125 } , { 127 , 45 } , { 48 , 17 } , { 255 , 83 } } ;
# elif (F_PLL==72000000)
const tmclk clkArr [ numfreqs ] = { { 832 , 1125 } , { 32 , 1125 } , { 49 , 1250 } , { 64 , 1125 } , { 49 , 625 } , { 32 , 375 } , { 128 , 1125 } , { 98 , 625 } , { 8 , 51 } , { 64 , 375 } , { 196 , 625 } , { 16 , 51 } , { 128 , 375 } , { 249 , 397 } , { 32 , 51 } , { 185 , 271 } } ;
# elif (F_PLL==96000000)
const tmclk clkArr [ numfreqs ] = { { 2 , 375 } , { 8 , 375 } , { 73 , 2483 } , { 16 , 375 } , { 147 , 2500 } , { 8 , 125 } , { 32 , 375 } , { 147 , 1250 } , { 2 , 17 } , { 16 , 125 } , { 147 , 625 } , { 4 , 17 } , { 32 , 125 } , { 151 , 321 } , { 8 , 17 } , { 64 , 125 } } ;
# elif (F_PLL==120000000)
const tmclk clkArr [ numfreqs ] = { { 8 , 1875 } , { 32 , 1875 } , { 89 , 3784 } , { 64 , 1875 } , { 147 , 3125 } , { 32 , 625 } , { 128 , 1875 } , { 205 , 2179 } , { 8 , 85 } , { 64 , 625 } , { 89 , 473 } , { 16 , 85 } , { 128 , 625 } , { 178 , 473 } , { 32 , 85 } , { 145 , 354 } } ;
# elif (F_PLL==144000000)
const tmclk clkArr [ numfreqs ] = { { 4 , 1125 } , { 16 , 1125 } , { 49 , 2500 } , { 32 , 1125 } , { 49 , 1250 } , { 16 , 375 } , { 64 , 1125 } , { 49 , 625 } , { 4 , 51 } , { 32 , 375 } , { 98 , 625 } , { 8 , 51 } , { 64 , 375 } , { 196 , 625 } , { 16 , 51 } , { 128 , 375 } } ;
# elif (F_PLL==180000000)
const tmclk clkArr [ numfreqs ] = { { 23 , 8086 } , { 46 , 4043 } , { 49 , 3125 } , { 73 , 3208 } , { 98 , 3125 } , { 37 , 1084 } , { 183 , 4021 } , { 196 , 3125 } , { 16 , 255 } , { 128 , 1875 } , { 107 , 853 } , { 32 , 255 } , { 219 , 1604 } , { 214 , 853 } , { 64 , 255 } , { 219 , 802 } } ;
# elif (F_PLL==192000000)
const tmclk clkArr [ numfreqs ] = { { 1 , 375 } , { 4 , 375 } , { 37 , 2517 } , { 8 , 375 } , { 73 , 2483 } , { 4 , 125 } , { 16 , 375 } , { 147 , 2500 } , { 1 , 17 } , { 8 , 125 } , { 147 , 1250 } , { 2 , 17 } , { 16 , 125 } , { 147 , 625 } , { 4 , 17 } , { 32 , 125 } } ;
# elif (F_PLL==216000000)
const tmclk clkArr [ numfreqs ] = { { 8 , 3375 } , { 32 , 3375 } , { 49 , 3750 } , { 64 , 3375 } , { 49 , 1875 } , { 32 , 1125 } , { 128 , 3375 } , { 98 , 1875 } , { 8 , 153 } , { 64 , 1125 } , { 196 , 1875 } , { 16 , 153 } , { 128 , 1125 } , { 226 , 1081 } , { 32 , 153 } , { 147 , 646 } } ;
# elif (F_PLL==240000000)
const tmclk clkArr [ numfreqs ] = { { 4 , 1875 } , { 16 , 1875 } , { 29 , 2466 } , { 32 , 1875 } , { 89 , 3784 } , { 16 , 625 } , { 64 , 1875 } , { 147 , 3125 } , { 4 , 85 } , { 32 , 625 } , { 205 , 2179 } , { 8 , 85 } , { 64 , 625 } , { 89 , 473 } , { 16 , 85 } , { 128 , 625 } } ;
# endif
for ( int f = 0 ; f < numfreqs ; f + + ) {
if ( freq = = samplefreqs [ f ] ) {
while ( I2S0_MCR & I2S_MCR_DUF ) ;
I2S0_MDR = I2S_MDR_FRACT ( ( clkArr [ f ] . mult - 1 ) ) | I2S_MDR_DIVIDE ( ( clkArr [ f ] . div - 1 ) ) ;
return ( float ) ( F_PLL / 256 * clkArr [ f ] . mult / clkArr [ f ] . div ) ;
}
}
return 0.0f ;
}
audio_block_t * AudioOutputI2S_F32 : : block_left_1st = NULL ;
audio_block_t * AudioOutputI2S_F32 : : block_left_1st = NULL ;
audio_block_t * AudioOutputI2S_F32 : : block_right_1st = NULL ;
audio_block_t * AudioOutputI2S_F32 : : block_right_1st = NULL ;
audio_block_t * AudioOutputI2S_F32 : : block_left_2nd = NULL ;
audio_block_t * AudioOutputI2S_F32 : : block_left_2nd = NULL ;
@ -35,9 +80,15 @@ audio_block_t * AudioOutputI2S_F32::block_right_2nd = NULL;
uint16_t AudioOutputI2S_F32 : : block_left_offset = 0 ;
uint16_t AudioOutputI2S_F32 : : block_left_offset = 0 ;
uint16_t AudioOutputI2S_F32 : : block_right_offset = 0 ;
uint16_t AudioOutputI2S_F32 : : block_right_offset = 0 ;
bool AudioOutputI2S_F32 : : update_responsibility = false ;
bool AudioOutputI2S_F32 : : update_responsibility = false ;
DMAMEM static uint32_t i2s_tx_buffer [ AUDIO_BLOCK_SAMPLES ] ;
DMAMEM static uint32_t i2s_tx_buffer [ AUDIO_BLOCK_SAMPLES ] ; //local audio_block_samples should be no larger than global AUDIO_BLOCK_SAMPLES
DMAChannel AudioOutputI2S_F32 : : dma ( false ) ;
DMAChannel AudioOutputI2S_F32 : : dma ( false ) ;
float AudioOutputI2S_F32 : : sample_rate_Hz = AUDIO_SAMPLE_RATE ;
int AudioOutputI2S_F32 : : audio_block_samples = AUDIO_BLOCK_SAMPLES ;
# define I2S_BUFFER_TO_USE_BYTES (AudioOutputI2S_F32::audio_block_samples*sizeof(i2s_tx_buffer[0]))
void AudioOutputI2S_F32 : : begin ( void )
void AudioOutputI2S_F32 : : begin ( void )
{
{
dma . begin ( true ) ; // Allocate the DMA channel first
dma . begin ( true ) ; // Allocate the DMA channel first
@ -47,6 +98,7 @@ void AudioOutputI2S_F32::begin(void)
// TODO: should we set & clear the I2S_TCSR_SR bit here?
// TODO: should we set & clear the I2S_TCSR_SR bit here?
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)
# if defined(KINETISK)
@ -54,12 +106,15 @@ void AudioOutputI2S_F32::begin(void)
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 - > NBYTES_MLNO = 2 ;
dma . TCD - > NBYTES_MLNO = 2 ;
dma . TCD - > SLAST = - sizeof ( i2s_tx_buffer ) ;
//dma.TCD->SLAST = -sizeof(i2s_tx_buffer); //original
dma . TCD - > SLAST = - I2S_BUFFER_TO_USE_BYTES ;
dma . TCD - > DADDR = & I2S0_TDR0 ;
dma . TCD - > DADDR = & I2S0_TDR0 ;
dma . TCD - > DOFF = 0 ;
dma . TCD - > DOFF = 0 ;
dma . TCD - > CITER_ELINKNO = sizeof ( i2s_tx_buffer ) / 2 ;
//dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; //original
dma . TCD - > CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2 ;
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; //original
dma . TCD - > BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2 ;
dma . TCD - > CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR ;
dma . TCD - > CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR ;
# endif
# endif
dma . triggerAtHardwareEvent ( DMAMUX_SOURCE_I2S0_TX ) ;
dma . triggerAtHardwareEvent ( DMAMUX_SOURCE_I2S0_TX ) ;
@ -69,6 +124,13 @@ void AudioOutputI2S_F32::begin(void)
I2S0_TCSR = I2S_TCSR_SR ;
I2S0_TCSR = I2S_TCSR_SR ;
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE ;
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE ;
dma . attachInterrupt ( isr ) ;
dma . attachInterrupt ( isr ) ;
// change the I2S frequencies to make the requested sample rate
setI2SFreq ( AudioOutputI2S_F32 : : sample_rate_Hz ) ;
enabled = 1 ;
//AudioInputI2S_F32::begin_guts();
}
}
@ -81,10 +143,12 @@ void AudioOutputI2S_F32::isr(void)
saddr = ( uint32_t ) ( dma . TCD - > SADDR ) ;
saddr = ( uint32_t ) ( dma . TCD - > SADDR ) ;
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) { //original
if ( saddr < ( uint32_t ) i2s_tx_buffer + I2S_BUFFER_TO_USE_BYTES / 2 ) {
// DMA is transmitting the first half of the buffer
// DMA is transmitting the first half of the buffer
// so we must fill the second half
// so we must fill the second half
dest = ( int16_t * ) & i2s_tx_buffer [ AUDIO_BLOCK_SAMPLES / 2 ] ;
//dest = (int16_t *)&i2s_tx_buffer[AUDIO_BLOCK_SAMPLES/2]; //original
dest = ( int16_t * ) & i2s_tx_buffer [ audio_block_samples / 2 ] ;
if ( AudioOutputI2S_F32 : : update_responsibility ) AudioStream_F32 : : update_all ( ) ;
if ( AudioOutputI2S_F32 : : update_responsibility ) AudioStream_F32 : : update_all ( ) ;
} else {
} else {
// DMA is transmitting the second half of the buffer
// DMA is transmitting the second half of the buffer
@ -97,6 +161,7 @@ void AudioOutputI2S_F32::isr(void)
offsetL = AudioOutputI2S_F32 : : block_left_offset ;
offsetL = AudioOutputI2S_F32 : : block_left_offset ;
offsetR = AudioOutputI2S_F32 : : block_right_offset ;
offsetR = AudioOutputI2S_F32 : : block_right_offset ;
/* Original
if ( blockL & & blockR ) {
if ( blockL & & blockR ) {
memcpy_tointerleaveLR ( dest , blockL - > data + offsetL , blockR - > data + offsetR ) ;
memcpy_tointerleaveLR ( dest , blockL - > data + offsetL , blockR - > data + offsetR ) ;
offsetL + = AUDIO_BLOCK_SAMPLES / 2 ;
offsetL + = AUDIO_BLOCK_SAMPLES / 2 ;
@ -111,8 +176,34 @@ void AudioOutputI2S_F32::isr(void)
memset ( dest , 0 , AUDIO_BLOCK_SAMPLES * 2 ) ;
memset ( dest , 0 , AUDIO_BLOCK_SAMPLES * 2 ) ;
return ;
return ;
}
}
*/
int16_t * d = dest ;
if ( blockL & & blockR ) {
//memcpy_tointerleaveLR(dest, blockL->data + offsetL, blockR->data + offsetR);
//memcpy_tointerleaveLRwLen(dest, blockL->data + offsetL, blockR->data + offsetR, audio_block_samples/2);
int16_t * pL = blockL - > data + offsetL ;
int16_t * pR = blockR - > data + offsetR ;
for ( int i = 0 ; i < audio_block_samples / 2 ; i + + ) { * d + + = * pL + + ; * d + + = * pR + + ; } //interleave
offsetL + = audio_block_samples / 2 ;
offsetR + = audio_block_samples / 2 ;
} else if ( blockL ) {
//memcpy_tointerleaveLR(dest, blockL->data + offsetL, blockR->data + offsetR);
int16_t * pL = blockL - > data + offsetL ;
for ( int i = 0 ; i < audio_block_samples / 2 * 2 ; i + = 2 ) { * ( d + i ) = * pL + + ; } //interleave
offsetL + = audio_block_samples / 2 ;
} else if ( blockR ) {
int16_t * pR = blockR - > data + offsetR ;
for ( int i = 0 ; i < audio_block_samples / 2 * 2 ; i + = 2 ) { * ( d + i ) = * pR + + ; } //interleave
offsetR + = audio_block_samples / 2 ;
} else {
//memset(dest,0,AUDIO_BLOCK_SAMPLES * 2);
memset ( dest , 0 , audio_block_samples * 2 ) ;
return ;
}
if ( offsetL < AUDIO_BLOCK_SAMPLES ) {
//if (offsetL < AUDIO_BLOCK_SAMPLES) { //original
if ( offsetL < ( uint16_t ) audio_block_samples ) {
AudioOutputI2S_F32 : : block_left_offset = offsetL ;
AudioOutputI2S_F32 : : block_left_offset = offsetL ;
} else {
} else {
AudioOutputI2S_F32 : : block_left_offset = 0 ;
AudioOutputI2S_F32 : : block_left_offset = 0 ;
@ -120,7 +211,8 @@ void AudioOutputI2S_F32::isr(void)
AudioOutputI2S_F32 : : block_left_1st = AudioOutputI2S_F32 : : block_left_2nd ;
AudioOutputI2S_F32 : : block_left_1st = AudioOutputI2S_F32 : : block_left_2nd ;
AudioOutputI2S_F32 : : block_left_2nd = NULL ;
AudioOutputI2S_F32 : : block_left_2nd = NULL ;
}
}
if ( offsetR < AUDIO_BLOCK_SAMPLES ) {
//if (offsetR < AUDIO_BLOCK_SAMPLES) {
if ( offsetR < ( uint16_t ) audio_block_samples ) {
AudioOutputI2S_F32 : : block_right_offset = offsetR ;
AudioOutputI2S_F32 : : block_right_offset = offsetR ;
} else {
} else {
AudioOutputI2S_F32 : : block_right_offset = 0 ;
AudioOutputI2S_F32 : : block_right_offset = 0 ;
@ -199,8 +291,9 @@ void AudioOutputI2S_F32::isr(void)
# endif
# endif
}
}
void AudioOutputI2S_F32 : : convert_f32_to_i16 ( float32_t * p_f32 , int16_t * p_i16 , int len ) {
for ( int i = 0 ; i < len ; i + + ) { * p_i16 + + = max ( - 32768 , min ( 32768 , ( int16_t ) ( ( * p_f32 + + ) * 32768.f ) ) ) ; }
}
void AudioOutputI2S_F32 : : update ( void )
void AudioOutputI2S_F32 : : update ( void )
{
{
@ -213,10 +306,18 @@ void AudioOutputI2S_F32::update(void)
audio_block_f32_t * block_f32 ;
audio_block_f32_t * block_f32 ;
block_f32 = receiveReadOnly_f32 ( 0 ) ; // input 0 = left channel
block_f32 = receiveReadOnly_f32 ( 0 ) ; // input 0 = left channel
if ( block_f32 ) {
if ( block_f32 ) {
if ( block_f32 - > length ! = audio_block_samples ) {
Serial . print ( " AudioOutputI2S_F32: *** WARNING ***: audio_block says len = " ) ;
Serial . print ( block_f32 - > length ) ;
Serial . print ( " , but I2S settings want it to be = " ) ;
Serial . println ( audio_block_samples ) ;
}
//Serial.print("AudioOutputI2S_F32: audio_block_samples = ");
//Serial.println(audio_block_samples);
//convert F32 to Int16
//convert F32 to Int16
block = AudioStream : : allocate ( ) ;
block = AudioStream : : allocate ( ) ;
arm_float_to_q15 ( ( float32_t * ) ( block_f32 - > data ) , ( q15_t * ) ( block - > data ) , AUDIO_BLOCK_SAMPLES ) ;
convert_f32_to_i16 ( block_f32 - > data , block - > data , audio_block_samples ) ;
AudioStream_F32 : : release ( block_f32 ) ;
AudioStream_F32 : : release ( block_f32 ) ;
//now process the data blocks
//now process the data blocks
@ -242,7 +343,7 @@ void AudioOutputI2S_F32::update(void)
if ( block_f32 ) {
if ( block_f32 ) {
//convert F32 to Int16
//convert F32 to Int16
block = AudioStream : : allocate ( ) ;
block = AudioStream : : allocate ( ) ;
arm_float_to_q15 ( ( float32_t * ) ( block_f32 - > data ) , ( q15_t * ) ( block - > data ) , AUDIO_BLOCK_SAMPLES ) ;
convert_f32_to_i16 ( block_f32 - > data , block - > data , audio_block_samples ) ;
AudioStream_F32 : : release ( block_f32 ) ;
AudioStream_F32 : : release ( block_f32 ) ;
__disable_irq ( ) ;
__disable_irq ( ) ;
@ -305,11 +406,11 @@ void AudioOutputI2S_F32::update(void)
# endif
# endif
# ifndef MCLK_SRC
# ifndef MCLK_SRC
# if F_CPU >= 20000000
# if ( F_CPU >= 20000000)
# define MCLK_SRC 3 // the PLL
# define MCLK_SRC 3 // the PLL
# else
# else
# define MCLK_SRC 0 // system clock
# define MCLK_SRC 0 // system clock
# endif
# endif
# endif
# endif
void AudioOutputI2S_F32 : : config_i2s ( void )
void AudioOutputI2S_F32 : : config_i2s ( void )
@ -433,3 +534,4 @@ void AudioOutputI2Sslave::config_i2s(void)
CORE_PIN11_CONFIG = PORT_PCR_MUX ( 6 ) ; // pin 11, PTC6, I2S0_MCLK
CORE_PIN11_CONFIG = PORT_PCR_MUX ( 6 ) ; // pin 11, PTC6, I2S0_MCLK
}
}
*/
*/