I felt inspired to try this at the lowest level, and rewrote the sound ISR to handle PCM samples. The result is this little piece of code in sound.c:
- Code: Select all
const U32 sample_conv[32] = {
0x00000000, 0x00100000, 0x00100010, 0x00101010,
0x10101010, 0x10221010, 0x10221022, 0x10222222,
0x22222222, 0x22522222, 0x22522252, 0x22525252,
0x52525252, 0x52555252, 0x52555255, 0x52555555,
0x55555555, 0x556b5555, 0x556b556b, 0x556b6b6b,
0x6b6b6b6b, 0x6b776b6b, 0x6b776b77, 0x6b777777,
0x77777777, 0x777f7777, 0x777f777f, 0x777f7f7f,
0x7f7f7f7f, 0x7fff7f7f, 0x7fff7fff, 0x7fffffff
/*
0x00000000, 0x00000001, 0x00000003, 0x00000007,
0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f,
0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff,
0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff,
0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff,
0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff
*/
};
U32 sample_count;
U8 sample_buf_id;
U8* sample_ptr;
U32 sample_buf1[16], sample_buf2[16];
void sound_play_sample(U8 *data, U32 length, U32 freq)
{
*AT91C_SSC_CMR = ((96109714 / 1024) / freq) + 1;
*AT91C_SSC_PTCR = AT91C_PDC_TXTEN;
sample_count = length;
sample_buf_id = 0;
sample_ptr = data;
memset(sample_buf1, 0, sizeof(sample_buf1));
memset(sample_buf2, 0, sizeof(sample_buf2));
sound_interrupt_enable();
}
void sound_isr_C()
{
if (sample_count > 0)
{
*AT91C_SSC_TNPR = (unsigned int)(sample_buf_id ? sample_buf1 : sample_buf2);
*AT91C_SSC_TNCR = 16;
sample_buf_id ^= 1;
U8 i;
U32 *sbuf = sample_buf_id ? sample_buf1 : sample_buf2;
for (i = 0; i < 16; i++)
{
*sbuf++ = sample_conv[*sample_ptr >> 3];
if (i & 1)
{
sample_count--;
sample_ptr++;
}
}
sound_enable();
}
else
{
sound_disable();
sound_interrupt_disable();
}
}
You can start playing a sample by calling sound_play_sample() with a pointer to the 8-bit unsigned PCM data, its length in bytes and the number of samples per second. It will play in the background. If the number of samples is not a multiple of 16, there's a great chance of an ugly audible click at the end for obvious reasons... Unfortunately, it is very noisy, and it doesn't seem to make much difference which conversion table you use. The louder the sound the better the quality, and even a bit of overamplification might help. The easiest way to do this is to rewrite the conversion table above into something nonlinear.
Note that this ISR kills the original tone playing functionality in its present form, but that's trivial to add anyway.