void sound_play_sample(U8 *data, U32 length, U32 freq, int vol) { if (data == (U8 *) 0 || length == 0) return; // Calculate the clock divisor based upon the recorded sample frequency */ if (freq == 0) freq = DEFRATE; if (freq > MAXRATE) freq = MAXRATE; if (freq < MINRATE) freq = MINRATE; U32 cdiv = (OSC/(2*SAMPBITS) + freq/2)/freq; set_vol(vol); // Turn off ints while we update shared values sound_interrupt_disable(); sound_mode = SOUND_MODE_PCM; sample.count = length; sample.ptr = data; sample.len = PDM_BUFFER_LENGTH; sample.clock_div = cdiv; // re-enable and wait for the current sample to complete sound_interrupt_enable(AT91C_SSC_TXBUFE); *AT91C_SSC_PTCR = AT91C_PDC_TXTEN; }
int sound_add_sample(U8 *data, U32 length, U32 freq, int vol) { // Add a set of samples into the playback queue, if not currently // playing start the playback process. // If this is the first sample simply start to play it if (sound_mode != SOUND_MODE_PCM || sample.ptr != sample.sample_buf) { if (sample.sample_buf == NULL) { sample.sample_buf = system_allocate(SAMPLEBUFSZ); if (sample.sample_buf == NULL) return -1; } if (length > SAMPLEBUFSZ - 1) length = SAMPLEBUFSZ - 1; memcpy(sample.sample_buf, data, length); sound_play_sample(sample.sample_buf, length, freq, vol); return length; } // Otherwise add the data to the buffer // Turn off ints while we update shared values sound_interrupt_disable(); // add the data U8 *sbuf = sample.ptr; U32 in = sample.in_index; U32 out = sample.out_index; int cnt = (int) ((out - in - 1) & (SAMPLEBUFSZ - 1)); if (cnt > length) cnt = length; length = cnt; while (cnt-- > 0) { sbuf[in] = *data++; in = (in + 1) & (SAMPLEBUFSZ - 1); } sample.in_index = in; sample.count = (((in - out) & (SAMPLEBUFSZ - 1)) + SAMPPERBUF - 1)/SAMPPERBUF; // re-enable and wait for the current sample to complete sound_interrupt_enable(AT91C_SSC_ENDTX); *AT91C_SSC_PTCR = AT91C_PDC_TXTEN; return length; }
void sound_isr_C() { //U64 s = systick_get_ns(); if (sample.count > 0) { // refill the buffer, and adjust any clocks *AT91C_SSC_CMR = sample.clock_div; sound_enable(); if (*AT91C_SSC_TCR == 0) { if (sound_mode == SOUND_MODE_PCM) { sound_fill_sample_buffer(); *AT91C_SSC_TPR = (unsigned int)sample.buf[sample.buf_id]; } else *AT91C_SSC_TPR = (unsigned int)sample.ptr; *AT91C_SSC_TCR = sample.len; sample.count--; } if (sound_mode == SOUND_MODE_PCM) { sound_fill_sample_buffer(); *AT91C_SSC_TNPR = (unsigned int)sample.buf[sample.buf_id]; } else *AT91C_SSC_TNPR = (unsigned int)sample.ptr; *AT91C_SSC_TNCR = sample.len; sample.count--; // If this is the last sample wait for it to complete, otherwise wait // to switch buffers sound_interrupt_enable(AT91C_SSC_ENDTX); } else { sound_disable(); sound_interrupt_disable(); } //ttime += (systick_get_ns() - s); }
void sound_freq(U32 freq, U32 ms, int vol) { // Set things up ready to go, note we avoid using anything that may // be used by the interupt routine because ints may still be enabled // at this point int len; // we use longer samples for lower frequencies if (freq > 1000) len = 16; else if (freq < 500) len = 64; else len = 32; sound_mode = SOUND_MODE_TONE; // Update the volume lookup table if we need to set_vol(vol); int buf = sample.buf_id^1; create_tone(sine, sizeof(sine), sample.buf[buf], len); // The note gneration takes approx 1ms, to ensure that we do not get gaps // when playing a series of tones we extend the requested period to cover // this 1ms cost. ms += TONE_OVERHEAD; // Turn of ints while we update shared values sound_interrupt_disable(); /* Genrate the pdm wave of the correct amplitude */ sample.clock_div = (OSC/(len*32*2) + freq/2)/freq; // Calculate actual frequency and use this for length calc freq = (OSC/(2*sample.clock_div))/(len*32); if (ms <= TONE_OVERHEAD) sample.count = 0; else sample.count = (freq*ms + 1000-1)/1000; sample.len = len; sample.ptr = (U8 *)sample.buf[buf]; sample.buf_id = buf; *AT91C_SSC_PTCR = AT91C_PDC_TXTEN; sound_mode = SOUND_MODE_TONE; sample.in_index = sample.out_index; sound_interrupt_enable(AT91C_SSC_TXBUFE); }