using namespace std; #include #include #include #include #include #include #include #define ALSA_PCM_NEW_HW_PARAMS_API #define ALSA_PCM_NEW_SW_PARAMS_API #include #include #include "memory.h" #include typedef unsigned int unsigned4; typedef signed long long signed8; typedef signed short signed2; snd_pcm_t *dsp; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; unsigned4 * buffer; unsigned4 filled; int arg_latency; char * arg_dev; const int err_dsp=1; const int err_none=0; void Info(const char* script, ...) { char toexecute[1024]; va_list ap; va_start(ap,script); vsnprintf(toexecute,1024,script,ap); va_end(ap); cout << toexecute << '\n'; fflush(stdout); }; void dsp_write(unsigned4 *value) { int err = 0; while(!err) { err = snd_pcm_writei(dsp,value,1); } assert(err!=-EAGAIN); assert(err!=-ESTRPIPE); if (err==-EPIPE) { Info("underrun occured..."); err = snd_pcm_prepare(dsp); if (err < 0) Info("cant recover from underrun: %s",snd_strerror(err)); return; } } int dsp_open() { int err; unsigned int buffer_time, period_time; snd_pcm_hw_params_t *hparams; snd_pcm_sw_params_t *sparams; snd_output_t * output = NULL; err = snd_output_stdio_attach(&output,stdout,0); if (err < 0 ) { Info("attaching to stdio didn't succeed: %s",snd_strerror(err)); return err_dsp; } err = snd_pcm_open(&dsp, arg_dev, SND_PCM_STREAM_PLAYBACK, 0); if (err < 0) { Info("opening alsa device failed : %s",snd_strerror(err)); return err_dsp; } snd_pcm_sw_params_alloca(&sparams); snd_pcm_hw_params_alloca(&hparams); err = snd_pcm_hw_params_any(dsp, hparams); if (err<0) { Info("Broken configuration file for pcm : %s",snd_strerror(err)); return err_dsp; } err = snd_pcm_hw_params_set_access(dsp, hparams, SND_PCM_ACCESS_RW_INTERLEAVED); if (err<0) { Info("Unable to set interleaved access to pcm device: %s",snd_strerror(err)); return err_dsp; } err = snd_pcm_hw_params_set_format(dsp, hparams, SND_PCM_FORMAT_S16); if (err<0) { Info("Unable to set sample format to standard 16 bit: %s",snd_strerror(err)); return err_dsp; } err = snd_pcm_hw_params_set_channels(dsp, hparams, 2); if (err < 0) { Info("setting dsp to 2 channels failed : %s",snd_strerror(err)); return err_dsp; } unsigned int q = 44100; err = snd_pcm_hw_params_set_rate_near(dsp, hparams, &q, 0); if (err < 0) { Info("setting dsp speed (%d) failed",q); return err_dsp; } if (q != 44100) Info("setting dsp speed (%d) failed, resulting rate = %d ",44100, q); period_time = arg_latency * 1000; buffer_time = period_time *2; { unsigned int t = buffer_time; int dir = 1; err = snd_pcm_hw_params_set_buffer_time_near(dsp,hparams,&t,&dir); if (err<0) { Info("dsp-alsa: Impossible to set pcm buffer time to %i (%i): %s", buffer_time,t,snd_strerror(err)); return err_dsp; } err = snd_pcm_hw_params_get_buffer_size(hparams,&buffer_size); if (err<0) { Info(" impossible to obtain buffer size: %s",snd_strerror(err)); return err_dsp; } t = period_time; err = snd_pcm_hw_params_set_period_time_near(dsp, hparams,&t,&dir); if (err<0) { Info(" impossible to set pcm period time to %i (%i): %s", period_time,t,snd_strerror(err)); return err_dsp; } err = snd_pcm_hw_params_get_period_size(hparams,&period_size,&dir); if (err<0) { Info(" ompossible to obtain period data size: %s",snd_strerror(err)); return err_dsp; } if (period_size*2 - 1>buffer_size) { Info("The alsa driver cannot allocate sufficiently large buffers due to the " "large requested latency. (playbuffer size = %li, " "total buffer size = %li). Try decreasing the latency.",period_size,buffer_size); return err_dsp; } dir = 0; } err = snd_pcm_hw_params(dsp,hparams); if (err < 0) { Info("unable to set hw parameters for pcm: %s",snd_strerror(err)); return err_dsp; } err = snd_pcm_sw_params_current(dsp,sparams); if (err < 0) { Info("unable to determine sw parameters for pcm: %s",snd_strerror(err)); return err_dsp; } err = snd_pcm_sw_params_set_start_threshold(dsp,sparams,buffer_size); if (err < 0) { Info("unable to set start to treshold mode: %s",snd_strerror(err)); return err_dsp; } err = snd_pcm_sw_params_set_avail_min(dsp,sparams,period_size); if (err < 0 ) { Info("unable to set minimum start size: %s",snd_strerror(err)); return err_dsp; } err = snd_pcm_sw_params(dsp,sparams); if (err < 0) { Info("unable to set software parameters: %s",snd_strerror(err)); return err_dsp; } snd_pcm_dump(dsp,output); buffer = (unsigned4*)malloc(period_size*sizeof(unsigned4)); return err_none; } void dsp_close() { int err = snd_pcm_close(dsp); if (err < 0) Info("cant close pcm device: %s",snd_strerror(err)); dsp=0; } void stream_wave() { for(int t=10; t>=0; t--) { cerr << "Parent plays (" << t << "s left)\n"; for(int i = 0 ; i < 44100; i++) { float x=i; float y=16384.*sin(x*2*M_PI*220./44100.)/4; struct {signed2 l; signed2 r;} lr; lr.l=y; lr.r=y; dsp_write((unsigned4*)&lr); } } } /** * This example first shows that a close in the child will * affect the playback in the parent. Sometimes the parents * just hangs in the write call, sometimes it just skips * all samples. * * However, the device in the parent is actually not * closed at all since we cannot reopen it. */ int main (int argc, char* argv[]) { arg_dev = "hw:0"; dsp = NULL; filled = 0; buffer_size = 0; period_size = 0; arg_latency = 150; // first open the DSP device. Info("Opening DSP Device in parent thread"); dsp_open(); int childpid=fork(); assert(childpid>-1); if (childpid) { // the parent thread now waits two seconds and the closes the device Info("Parent thread playing"); stream_wave(); Info("Parent tries to reopen the device"); dsp_open(); stream_wave(); } else { sleep(5); Info("Child closes the dsp device"); dsp_close(); } }