AudioPlaySystem.cpp
1 #include "emuapi.h" 2 3 #ifdef HAS_SND 4 5 #include "AudioPlaySystem.h" 6 #include <Arduino.h> 7 #define SAMPLERATE AUDIO_SAMPLE_RATE_EXACT 8 #define CLOCKFREQ 985248 9 10 #ifndef CUSTOM_SND 11 PROGMEM static const short square[]={ 12 32767,32767,32767,32767, 13 32767,32767,32767,32767, 14 32767,32767,32767,32767, 15 32767,32767,32767,32767, 16 32767,32767,32767,32767, 17 32767,32767,32767,32767, 18 32767,32767,32767,32767, 19 32767,32767,32767,32767, 20 -32767,-32767,-32767,-32767, 21 -32767,-32767,-32767,-32767, 22 -32767,-32767,-32767,-32767, 23 -32767,-32767,-32767,-32767, 24 -32767,-32767,-32767,-32767, 25 -32767,-32767,-32767,-32767, 26 -32767,-32767,-32767,-32767, 27 -32767,-32767,-32767,-32767, 28 }; 29 30 PROGMEM const short noise[] { 31 -32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767, 32 -32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,32767,-32767, 33 -32767,-32767,32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,32767,-32767, 34 -32767,-32767,32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,32767,-32767,-32767,32767,32767,-32767, 35 -32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,32767,-32767,-32767,32767,32767,-32767, 36 -32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,32767,-32767,32767,32767,32767,-32767, 37 32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,32767,-32767,32767,32767,32767,-32767, 38 32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,32767,32767,32767,32767,32767,-32767, 39 32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,32767,32767,32767,32767,32767,-32767, 40 32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,-32767,32767,32767,32767,-32767,-32767, 41 32767,-32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,-32767,32767,32767,32767,32767,32767,-32767, 42 32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,32767,-32767,32767,32767,32767,-32767,-32767, 43 32767,32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,-32767,32767,32767,32767,32767,32767,-32767, 44 32767,-32767,32767,-32767,-32767,32767,32767,-32767,32767,32767,-32767,32767,-32767,32767,-32767,-32767, 45 32767,32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,-32767,32767,32767,32767,32767,32767,-32767, 46 32767,-32767,32767,-32767,-32767,32767,32767,32767,32767,32767,-32767,32767,-32767,32767,-32767,-32767, 47 }; 48 49 #define NOISEBSIZE 0x100 50 51 typedef struct 52 { 53 unsigned int spos; 54 unsigned int sinc; 55 unsigned int vol; 56 } Channel; 57 58 static Channel chan[6] = { 59 {0,0,0}, 60 {0,0,0}, 61 {0,0,0}, 62 {0,0,0}, 63 {0,0,0}, 64 {0,0,0} }; 65 66 #endif 67 68 volatile bool playing = false; 69 70 71 static void snd_Reset(void) 72 { 73 #ifndef CUSTOM_SND 74 chan[0].vol = 0; 75 chan[1].vol = 0; 76 chan[2].vol = 0; 77 chan[3].vol = 0; 78 chan[4].vol = 0; 79 chan[5].vol = 0; 80 chan[0].sinc = 0; 81 chan[1].sinc = 0; 82 chan[2].sinc = 0; 83 chan[3].sinc = 0; 84 chan[4].sinc = 0; 85 chan[5].sinc = 0; 86 #endif 87 } 88 89 90 #ifdef CUSTOM_SND 91 //extern "C" { 92 void SND_Process(void *sndbuffer, int sndn); 93 //} 94 #endif 95 96 97 FASTRUN void AudioPlaySystem::snd_Mixer(short * stream, int len ) 98 { 99 if (playing) 100 { 101 #ifdef CUSTOM_SND 102 SND_Process((void*)stream, len); 103 #else 104 int i; 105 long s; 106 len = len >> 1; 107 short v0=chan[0].vol; 108 short v1=chan[1].vol; 109 short v2=chan[2].vol; 110 short v3=chan[3].vol; 111 short v4=chan[4].vol; 112 short v5=chan[5].vol; 113 for (i=0;i<len;i++) 114 { 115 s =((v0*square[(chan[0].spos>>8)&0x3f])>>11); 116 s+=((v1*square[(chan[1].spos>>8)&0x3f])>>11); 117 s+=((v2*square[(chan[2].spos>>8)&0x3f])>>11); 118 s+=((v3*noise[(chan[3].spos>>8)&(NOISEBSIZE-1)])>>11); 119 s+=((v4*noise[(chan[4].spos>>8)&(NOISEBSIZE-1)])>>11); 120 s+=((v5*noise[(chan[5].spos>>8)&(NOISEBSIZE-1)])>>11); 121 *stream++ = (short)(s); 122 *stream++ = (short)(s); 123 chan[0].spos += chan[0].sinc; 124 chan[1].spos += chan[1].sinc; 125 chan[2].spos += chan[2].sinc; 126 chan[3].spos += chan[3].sinc; 127 chan[4].spos += chan[4].sinc; 128 chan[5].spos += chan[5].sinc; 129 } 130 #endif 131 } 132 } 133 134 void AudioPlaySystem::begin(void) 135 { 136 this->reset(); 137 } 138 139 void AudioPlaySystem::start(void) 140 { 141 playing = true; 142 } 143 144 void AudioPlaySystem::setSampleParameters(float clockfreq, float samplerate) { 145 } 146 147 void AudioPlaySystem::reset(void) 148 { 149 snd_Reset(); 150 } 151 152 void AudioPlaySystem::stop(void) 153 { 154 //__disable_irq(); 155 playing = false; 156 //__enable_irq(); 157 } 158 159 bool AudioPlaySystem::isPlaying(void) 160 { 161 return playing; 162 } 163 164 165 166 void AudioPlaySystem::sound(int C, int F, int V) { 167 #ifndef CUSTOM_SND 168 if (C < 6) { 169 chan[C].vol = V; 170 chan[C].sinc = F>>1; 171 } 172 #endif 173 } 174 175 void AudioPlaySystem::step(void) { 176 } 177 178 179 /******************************************************************* 180 Experimental I2S interrupt based sound driver for PCM51xx !!! 181 *******************************************************************/ 182 183 FLASHMEM static void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv, bool force) // sets PLL4 184 { 185 if (!force && (CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_ENABLE)) return; 186 187 CCM_ANALOG_PLL_AUDIO = CCM_ANALOG_PLL_AUDIO_BYPASS | CCM_ANALOG_PLL_AUDIO_ENABLE 188 | CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2) // 2: 1/4; 1: 1/2; 0: 1/1 189 | CCM_ANALOG_PLL_AUDIO_DIV_SELECT(nfact); 190 191 CCM_ANALOG_PLL_AUDIO_NUM = nmult & CCM_ANALOG_PLL_AUDIO_NUM_MASK; 192 CCM_ANALOG_PLL_AUDIO_DENOM = ndiv & CCM_ANALOG_PLL_AUDIO_DENOM_MASK; 193 194 CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_POWERDOWN;//Switch on PLL 195 while (!(CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK)) {}; //Wait for pll-lock 196 197 const int div_post_pll = 1; // other values: 2,4 198 CCM_ANALOG_MISC2 &= ~(CCM_ANALOG_MISC2_DIV_MSB | CCM_ANALOG_MISC2_DIV_LSB); 199 if(div_post_pll>1) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_LSB; 200 if(div_post_pll>3) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_MSB; 201 202 CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_BYPASS;//Disable Bypass 203 } 204 205 #define AUDIO_SAMPLE_RATE_EXACT 11025.0 //44117.64706 //11025.0 //22050.0 //44117.64706 //31778.0 206 207 FLASHMEM static void config_sai1() 208 { 209 CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON); 210 double fs = AUDIO_SAMPLE_RATE_EXACT; 211 // PLL between 27*24 = 648MHz und 54*24=1296MHz 212 int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4 213 int n2 = 1 + (24000000 * 27) / (fs * 256 * n1); 214 double C = (fs * 256 * n1 * n2) / 24000000; 215 int c0 = C; 216 int c2 = 10000; 217 int c1 = C * c2 - (c0 * c2); 218 219 set_audioClock(c0, c1, c2, true); 220 // clear SAI1_CLK register locations 221 CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK)) 222 | CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4 223 224 n1 = n1 / 2; //Double Speed for TDM 225 226 CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK)) 227 | CCM_CS1CDR_SAI1_CLK_PRED(n1 - 1) // &0x07 228 | CCM_CS1CDR_SAI1_CLK_PODF(n2 - 1); // &0x3f 229 230 IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK)) 231 | (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK 232 233 234 // configure transmitter 235 int rsync = 0; 236 int tsync = 1; 237 238 I2S1_TMR = 0; 239 I2S1_TCR1 = I2S_TCR1_RFW(1); 240 I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async; 241 | (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1)); 242 I2S1_TCR3 = I2S_TCR3_TCE; 243 I2S1_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF 244 | I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP; 245 I2S1_TCR5 = I2S_TCR5_WNW((32-1)) | I2S_TCR5_W0W((32-1)) | I2S_TCR5_FBT((32-1)); 246 247 248 I2S1_RMR = 0; 249 I2S1_RCR1 = I2S_RCR1_RFW(1); 250 I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async; 251 | (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1)); 252 I2S1_RCR3 = I2S_RCR3_RCE; 253 I2S1_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF 254 | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD; 255 I2S1_RCR5 = I2S_RCR5_WNW((32-1)) | I2S_RCR5_W0W((32-1)) | I2S_RCR5_FBT((32-1)); 256 257 //CORE_PIN23_CONFIG = 3; // MCLK 258 CORE_PIN21_CONFIG = 3; // RX_BCLK 259 CORE_PIN20_CONFIG = 3; // RX_SYNC 260 CORE_PIN7_CONFIG = 3; // TX_DATA0 261 I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE; 262 I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE ;//<-- not using DMA */; 263 } 264 265 FLASHMEM static void config_pt8211() 266 { 267 CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON); 268 double fs = AUDIO_SAMPLE_RATE_EXACT; 269 // PLL between 27*24 = 648MHz und 54*24=1296MHz 270 int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4 271 int n2 = 1 + (24000000 * 27) / (fs * 256 * n1); 272 double C = (fs * 256 * n1 * n2) / 24000000; 273 int c0 = C; 274 int c2 = 10000; 275 int c1 = C * c2 - (c0 * c2); 276 277 set_audioClock(c0, c1, c2, true); 278 // clear SAI1_CLK register locations 279 CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK)) 280 | CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4 281 282 //n1 = n1 / 2; //Double Speed for TDM 283 284 CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK)) 285 | CCM_CS1CDR_SAI1_CLK_PRED(n1 - 1) // &0x07 286 | CCM_CS1CDR_SAI1_CLK_PODF(n2 - 1); // &0x3f 287 288 IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK)) 289 | (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK 290 291 292 // configure transmitter 293 int rsync = 0; 294 int tsync = 1; 295 296 I2S1_TMR = 0; 297 I2S1_TCR1 = I2S_TCR1_RFW(0); 298 I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(1); 299 I2S1_TCR3 = I2S_TCR3_TCE; 300 I2S1_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(15) | I2S_TCR4_MF | I2S_TCR4_FSD /*| I2S_TCR4_FSE*/ | I2S_TCR4_FSP ; //PT8211 301 I2S1_TCR5 = I2S_TCR5_WNW(15) | I2S_TCR5_W0W(15) | I2S_TCR5_FBT(15); 302 303 I2S1_RMR = 0; 304 I2S1_RCR1 = I2S_RCR1_RFW(0); 305 I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP | I2S_RCR2_MSEL(1)| I2S_RCR2_BCD | I2S_RCR2_DIV(1) ; 306 I2S1_RCR3 = I2S_RCR3_RCE; 307 I2S1_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(15) | I2S_RCR4_MF /*| I2S_RCR4_FSE*/ | I2S_RCR4_FSP | I2S_RCR4_FSD; //PT8211 308 I2S1_RCR5 = I2S_RCR5_WNW(15) | I2S_RCR5_W0W(15) | I2S_RCR5_FBT(15); 309 310 CORE_PIN21_CONFIG = 3; // RX_BCLK 311 CORE_PIN20_CONFIG = 3; // RX_SYNC 312 CORE_PIN7_CONFIG = 3; // TX_DATA0 313 I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE; 314 I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE ;//<-- not using DMA */; 315 } 316 317 //DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx[1024]; 318 319 static bool fillfirsthalf = true; 320 static uint16_t cnt = 0; 321 static uint16_t sampleBufferSize = 0; 322 323 static void (*fillsamples)(short * stream, int len) = nullptr; 324 325 static uint32_t * i2s_tx_buffer __attribute__((aligned(32))); 326 static uint16_t * i2s_tx_buffer16; 327 static uint16_t * txreg = (uint16_t *)((uint32_t)&I2S1_TDR0 + 2); 328 329 FASTRUN void AudioPlaySystem::AUDIO_isr() { 330 331 *txreg = i2s_tx_buffer16[cnt]; 332 cnt = cnt + 1; 333 cnt = cnt & (sampleBufferSize*2-1); 334 335 if (cnt == 0) { 336 fillfirsthalf = false; 337 NVIC_SET_PENDING(IRQ_SOFTWARE); 338 } 339 else if (cnt == sampleBufferSize) { 340 fillfirsthalf = true; 341 NVIC_SET_PENDING(IRQ_SOFTWARE); 342 } 343 /* 344 I2S1_TDR0 = i2s_tx_buffer[cnt]; 345 cnt = cnt + 1; 346 cnt = cnt & (sampleBufferSize-1); 347 if (cnt == 0) { 348 fillfirsthalf = false; 349 NVIC_SET_PENDING(IRQ_SOFTWARE); 350 } 351 else if (cnt == sampleBufferSize/2) { 352 fillfirsthalf = true; 353 NVIC_SET_PENDING(IRQ_SOFTWARE); 354 } 355 */ 356 } 357 358 FASTRUN void AudioPlaySystem::SOFTWARE_isr() { 359 //Serial.println("x"); 360 if (fillfirsthalf) { 361 fillsamples((short *)i2s_tx_buffer, sampleBufferSize); 362 arm_dcache_flush_delete((void*)i2s_tx_buffer, (sampleBufferSize/2)*sizeof(uint32_t)); 363 } 364 else { 365 fillsamples((short *)&i2s_tx_buffer[sampleBufferSize/2], sampleBufferSize); 366 arm_dcache_flush_delete((void*)&i2s_tx_buffer[sampleBufferSize/2], (sampleBufferSize/2)*sizeof(uint32_t)); 367 } 368 } 369 370 // display VGA image 371 FLASHMEM void AudioPlaySystem::begin_audio(int samplesize, void (*callback)(short * stream, int len)) 372 { 373 fillsamples = callback; 374 i2s_tx_buffer = (uint32_t*)malloc(samplesize*sizeof(uint32_t)); //&i2s_tx[0]; 375 376 if (i2s_tx_buffer == NULL) { 377 Serial.println("could not allocate audio samples"); 378 return; 379 } 380 memset((void*)i2s_tx_buffer,0, samplesize*sizeof(uint32_t)); 381 arm_dcache_flush_delete((void*)i2s_tx_buffer, samplesize*sizeof(uint32_t)); 382 i2s_tx_buffer16 = (uint16_t*)i2s_tx_buffer; 383 384 sampleBufferSize = samplesize; 385 386 #ifdef PT8211 387 txreg = (uint16_t *)((uint32_t)&I2S1_TDR0); 388 config_pt8211(); 389 #else 390 txreg = (uint16_t *)((uint32_t)&I2S1_TDR0 + 2); 391 config_sai1(); 392 #endif 393 394 attachInterruptVector(IRQ_SAI1, AUDIO_isr); 395 NVIC_ENABLE_IRQ(IRQ_SAI1); 396 NVIC_SET_PRIORITY(IRQ_QTIMER3, 0); // 0 highest priority, 255 = lowest priority 397 NVIC_SET_PRIORITY(IRQ_SAI1, 127); 398 attachInterruptVector(IRQ_SOFTWARE, SOFTWARE_isr); 399 NVIC_SET_PRIORITY(IRQ_SOFTWARE, 208); 400 NVIC_ENABLE_IRQ(IRQ_SOFTWARE); 401 402 I2S1_TCSR |= 1<<8; // start generating TX FIFO interrupts 403 404 Serial.print("Audio sample buffer = "); 405 Serial.println(samplesize); 406 } 407 408 FLASHMEM void AudioPlaySystem::end_audio() 409 { 410 if (i2s_tx_buffer != NULL) { 411 free(i2s_tx_buffer); 412 } 413 } 414 415 #endif