/ MCUME_teensy41 / teensysms / AudioPlaySystem.cpp
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