#include "sound.h" #include "os_generic.h" #include "parameters.h" #include #define BUFFERSETS 4 #define BLOCKING struct SoundDriverAlsa { void (*CloseFn)( struct SoundDriverAlsa * object ); int (*SoundStateFn)( struct SoundDriverAlsa * object ); SoundCBType callback; int channelsPlay; int spsPlay; int channelsRec; int spsRec; snd_pcm_uframes_t buffer; og_thread_t thread; snd_pcm_t *playback_handle; snd_pcm_t *record_handle; //More fields may exist on a per-sound-driver basis }; static struct SoundDriverAlsa* InitASound( struct SoundDriverAlsa * r ); void CloseSoundAlsa( struct SoundDriverAlsa * r ); int SoundStateAlsa( struct SoundDriverAlsa * soundobject ) { return ((soundobject->playback_handle)?1:0) | ((soundobject->record_handle)?2:0); } void CloseSoundAlsa( struct SoundDriverAlsa * r ) { if( r ) { if( r->playback_handle ) snd_pcm_close (r->playback_handle); if( r->record_handle ) snd_pcm_close (r->record_handle); #ifdef BLOCKING OGUSleep(2000); OGCancelThread( r->thread ); #endif free( r ); } } static int SetHWParams( snd_pcm_t * handle, int * samplerate, int * channels, snd_pcm_uframes_t * buffer ) { int err; snd_pcm_hw_params_t *hw_params; if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror (err)); return -1; } if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) { fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror (err)); goto fail; } if ((err = snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { fprintf (stderr, "cannot set access type (%s)\n", snd_strerror (err)); goto fail; } if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_FLOAT )) < 0) { fprintf (stderr, "cannot set sample format (%s)\n", snd_strerror (err)); goto fail; } if ((err = snd_pcm_hw_params_set_rate_near (handle, hw_params, (unsigned int*)samplerate, 0)) < 0) { fprintf (stderr, "cannot set sample rate (%s)\n", snd_strerror (err)); goto fail; } if ((err = snd_pcm_hw_params_set_channels (handle, hw_params, *channels)) < 0) { fprintf (stderr, "cannot set channel count (%s)\n", snd_strerror (err)); goto fail; } int dir = 0; if( (err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, buffer, &dir)) < 0 ) { fprintf( stderr, "cannot set period size. (%s)\n", snd_strerror(err) ); goto fail; } if ((err = snd_pcm_hw_params (handle, hw_params)) < 0) { fprintf (stderr, "cannot set parameters (%s)\n", snd_strerror (err)); goto fail; } snd_pcm_hw_params_free (hw_params); return 0; fail: snd_pcm_hw_params_free (hw_params); return -2; } static int SetSWParams( snd_pcm_t * handle, int isrec ) { snd_pcm_sw_params_t *sw_params; int err; //Time for software parameters: if( !isrec ) { if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) { fprintf (stderr, "cannot allocate software parameters structure (%s)\n", snd_strerror (err)); goto failhard; } if ((err = snd_pcm_sw_params_current (handle, sw_params)) < 0) { fprintf (stderr, "cannot initialize software parameters structure (%s) (%p)\n", snd_strerror (err), handle); goto fail; } if ((err = snd_pcm_sw_params_set_avail_min (handle, sw_params, GetParameterI( "minavailcount", 2048 ) )) < 0) { fprintf (stderr, "cannot set minimum available count (%s)\n", snd_strerror (err)); goto fail; } if ((err = snd_pcm_sw_params_set_stop_threshold(handle, sw_params, GetParameterI( "stopthresh", 512 ))) < 0) { fprintf (stderr, "cannot set minimum available count (%s)\n", snd_strerror (err)); goto fail; } if ((err = snd_pcm_sw_params_set_start_threshold(handle, sw_params, GetParameterI( "startthresh", 2048 ))) < 0) { fprintf (stderr, "cannot set minimum available count (%s)\n", snd_strerror (err)); goto fail; } if ((err = snd_pcm_sw_params (handle, sw_params)) < 0) { fprintf (stderr, "cannot set software parameters (%s)\n", snd_strerror (err)); goto fail; } } if ((err = snd_pcm_prepare (handle)) < 0) { fprintf (stderr, "cannot prepare audio interface for use (%s)\n", snd_strerror (err)); goto fail; } return 0; fail: if( !isrec ) { snd_pcm_sw_params_free (sw_params); } failhard: return -1; } #ifdef BLOCKING static void * SoundThread( void * v ) { int i; struct SoundDriverAlsa * a = (struct SoundDriverAlsa*)v; float * bufr[BUFFERSETS]; float * bufp[BUFFERSETS]; for(i = 0; i < BUFFERSETS; i++ ) { bufr[i] = malloc( a->buffer * sizeof(float) * a->channelsRec ); bufp[i] = malloc( a->buffer * sizeof(float) * a->channelsPlay ); } while( a->record_handle || a->playback_handle ) { int err; i = (i+1)%BUFFERSETS; if( a->record_handle ) { if( (err = snd_pcm_readi (a->record_handle, bufr[i], a->buffer)) != a->buffer) { fprintf (stderr, "read from audio interface failed (%s)\n", snd_strerror (err)); if( a->record_handle ) snd_pcm_close (a->record_handle); a->record_handle = 0; } else { //has_rec = 1; } } //Do our callback. int playbacksamples = 0; a->callback( bufp[i], bufr[i], a->buffer, &playbacksamples, (struct SoundDriver*)a ); //playbacksamples *= sizeof(float) * a->channelsPlay; if( a->playback_handle ) { if ((err = snd_pcm_writei (a->playback_handle, bufp[i], playbacksamples)) != playbacksamples) { fprintf (stderr, "write to audio interface failed (%s)\n", snd_strerror (err)); if( a->playback_handle ) snd_pcm_close (a->playback_handle); a->playback_handle = 0; } } } //Fault happened, re-initialize? InitASound( a ); return 0; } #else //Handle callback static struct SoundDriverAlsa * reccb; static int record_callback (snd_pcm_sframes_t nframes) { int err; // printf ("playback callback called with %u frames\n", nframes); /* ... fill buf with data ... */ if ((err = snd_pcm_writei (playback_handle, buf, nframes)) < 0) { fprintf (stderr, "write failed (%s)\n", snd_strerror (err)); } return err; } #endif static struct SoundDriverAlsa * InitASound( struct SoundDriverAlsa * r ) { int err; if( GetParameterI( "play", 0 ) ) { if ((err = snd_pcm_open (&r->playback_handle, GetParameterS( "devplay", "default" ), SND_PCM_STREAM_PLAYBACK, 0)) < 0) { fprintf (stderr, "cannot open output audio device (%s)\n", snd_strerror (err)); goto fail; } } if( GetParameterI( "record", 1 ) ) { if ((err = snd_pcm_open (&r->record_handle, GetParameterS( "devrecord", "default" ), SND_PCM_STREAM_CAPTURE, 0)) < 0) { fprintf (stderr, "cannot open input audio device (%s)\n", snd_strerror (err)); goto fail; } } if( r->playback_handle ) { if( SetHWParams( r->playback_handle, &r->spsPlay, &r->channelsPlay, &r->buffer ) < 0 ) goto fail; if( SetSWParams( r->playback_handle, 0 ) < 0 ) goto fail; } if( r->record_handle ) { if( SetHWParams( r->record_handle, &r->spsRec, &r->channelsRec, &r->buffer ) < 0 ) goto fail; if( SetSWParams( r->record_handle, 1 ) < 0 ) goto fail; } if( r->playback_handle && r->record_handle ) { snd_pcm_link ( r->playback_handle, r->record_handle ); } #ifdef BLOCKING r->thread = OGCreateThread( SoundThread, r ); #else reccb = r; //handle interrupt #endif return r; fail: if( r ) { if( r->playback_handle ) snd_pcm_close (r->playback_handle); if( r->record_handle ) snd_pcm_close (r->record_handle); free( r ); } return 0; } void * InitSoundAlsa( SoundCBType cb ) { struct SoundDriverAlsa * r = malloc( sizeof( struct SoundDriverAlsa ) ); r->CloseFn = CloseSoundAlsa; r->SoundStateFn = SoundStateAlsa; r->callback = cb; r->spsPlay = GetParameterI( "samplerate", 44100 ); r->channelsPlay = GetParameterI( "channels", 2 ); r->spsRec = r->spsPlay; r->channelsRec = r->channelsPlay; r->playback_handle = 0; r->record_handle = 0; r->buffer = GetParameterI( "buffer", 1024 ); return InitASound(r); } EXECUTE_AT_BOOT( AlsaSoundReg, RegSound( 10, "ALSA", InitSoundAlsa ) );