colorchord/sound_alsa.c

340 lines
8 KiB
C
Raw Normal View History

2015-01-07 04:51:39 +01:00
#include "sound.h"
#include "os_generic.h"
#include "parameters.h"
#include <alsa/asoundlib.h>
#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 ) );