/***************************************************************************** "skeleton" driver *****************************************************************************/ #include "sound.h" /* DEBUG(X), among others */ /* DSP buffers -- large for few interrupts, small for responsiveness */ #define NUM_BUFS 2 #define LG2_BUF_SIZE 10 #define BUF_SIZE (1u << (LG2_BUF_SIZE)) static bool _is_open; static volatile bool _playback_in_progress; /* THIS IS WRONG: static volatile unsigned char far *_dsp_buff_out; */ static unsigned char far * volatile _dsp_buff_out; static unsigned char far *_dsp_buff_mem, *_dsp_buff_base, *_dsp_buff_in; /***************************************************************************** *****************************************************************************/ static void skel_set_volume(unsigned char level) { DEBUG(printf("skel_set_volume: %u/255\n", level);) } /***************************************************************************** *****************************************************************************/ static long skel_find_closest_rate(long rate) { DEBUG(printf("skel_find_closest_rate: %ld -> %ld\n", rate, rate);) return rate; } /***************************************************************************** *****************************************************************************/ static int skel_set_fmt(unsigned char depth, unsigned char channels, long rate) { switch(depth) { case 8: break; case 16: break; default: printf("skel_set_fmt: error: bits/sample (%u) " "is not 8 nor 16\n", depth); return -1; } switch(channels) { case 1: break; case 2: break; default: printf("skel_set_fmt: error: channels (%u) " "is not 1 nor 2\n", channels); return -1; } switch(rate) { case 8000: case 11025: case 22050: case 44100u: break; default: printf("skel_set_fmt: error: unsupported " "sample rate %ld\n", rate); return -1; } return 0; } /***************************************************************************** *****************************************************************************/ static void skel_start_playback(unsigned long count) { _playback_in_progress = true; } /***************************************************************************** *****************************************************************************/ static void skel_stop_playback(bool wait) { if(wait) { DEBUG(printf("waiting...\n");) while(_playback_in_progress) /* nothing */; } else { _playback_in_progress = false; } } /***************************************************************************** *****************************************************************************/ static int write_sample(short left, short right) { unsigned short in_buf, out_buf, new_in_buf, count; unsigned char depth, channels; bool wait = false; depth = 8; channels = 1; /* to which buffer are we writing? */ in_buf = _dsp_buff_in - _dsp_buff_base; in_buf >>= LG2_BUF_SIZE; while(1) /* from which buffer are we playing? */ { out_buf = _dsp_buff_out - _dsp_buff_base; out_buf >>= LG2_BUF_SIZE; /* if they are the same, and if playback is active, then wait until playback of this buffer is complete */ if(out_buf != in_buf) break; if(!_playback_in_progress) break; if(!wait) { DEBUG(printf("waiting...");) wait = true; } } /* store sample and advance pointer */ if(depth == 16) { /* assumes LITTLE ENDIAN 16-bit samples for this sound hardware */ write_le16(_dsp_buff_in, left); _dsp_buff_in += 2; if(channels == 2) { write_le16(_dsp_buff_in, right); _dsp_buff_in += 2; count = BUF_SIZE >> 2; } else count = BUF_SIZE >> 1; } else /* if(depth == 8) */ { left >>= 8; *_dsp_buff_in = left; _dsp_buff_in++; if(channels == 2) { right >>= 8; *_dsp_buff_in = right; _dsp_buff_in++; count = BUF_SIZE >> 1; } else count = BUF_SIZE; } if(_dsp_buff_in >= _dsp_buff_base + NUM_BUFS * BUF_SIZE) _dsp_buff_in = _dsp_buff_base; /* did we cross over into a new buffer? */ new_in_buf = _dsp_buff_in - _dsp_buff_base; new_in_buf >>= LG2_BUF_SIZE; /* if yes, and if not currently playing, then kick-start playback */ if((new_in_buf != in_buf) && !_playback_in_progress) { /* you should see this message ONCE, when playback starts see it again if you pause/unpause the playback if you see it many times, something's wrong */ printf("*** kicking playback ***\n"); skel_start_playback(count); } return 0; } /***************************************************************************** detect sound hardware, allocate DSP buffers, install interrupt hanadler *****************************************************************************/ static int skel_open(void) { return 0; } /***************************************************************************** *****************************************************************************/ static void skel_close(void) { DEBUG(printf("skel_close\n");) /* already closed? */ if(!_is_open) { printf("skel_close: sound hardware already closed\n"); return; } /* abort playback */ if(_playback_in_progress) { DEBUG(printf("aborting playback...\n");) skel_stop_playback(false); } /* free DSP buffers; restore old interrupt vector */ _is_open = false; } /***************************************************************************** *****************************************************************************/ sound_t skel_drv = { skel_open, skel_find_closest_rate, skel_set_fmt, skel_set_volume, write_sample, skel_stop_playback, skel_close, };