Implement non-mmap ALSA capture

This commit is contained in:
Chris Robinson 2008-09-29 17:24:50 -07:00
parent 6567cdd7b5
commit a2568409fc

View File

@ -40,6 +40,9 @@ typedef struct {
ALvoid *buffer; ALvoid *buffer;
ALsizei size; ALsizei size;
RingBuffer *ring;
int doCapture;
volatile int killNow; volatile int killNow;
ALvoid *thread; ALvoid *thread;
} alsa_data; } alsa_data;
@ -67,6 +70,7 @@ MAKE_FUNC(snd_pcm_hw_params_set_rate_near);
MAKE_FUNC(snd_pcm_hw_params_set_rate); MAKE_FUNC(snd_pcm_hw_params_set_rate);
MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_near); MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_near);
MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_min); MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_min);
MAKE_FUNC(snd_pcm_hw_params_get_buffer_size);
MAKE_FUNC(snd_pcm_hw_params_get_period_size); MAKE_FUNC(snd_pcm_hw_params_get_period_size);
MAKE_FUNC(snd_pcm_hw_params_get_access); MAKE_FUNC(snd_pcm_hw_params_get_access);
MAKE_FUNC(snd_pcm_hw_params); MAKE_FUNC(snd_pcm_hw_params);
@ -79,6 +83,7 @@ MAKE_FUNC(snd_pcm_avail_update);
MAKE_FUNC(snd_pcm_areas_silence); MAKE_FUNC(snd_pcm_areas_silence);
MAKE_FUNC(snd_pcm_mmap_begin); MAKE_FUNC(snd_pcm_mmap_begin);
MAKE_FUNC(snd_pcm_mmap_commit); MAKE_FUNC(snd_pcm_mmap_commit);
MAKE_FUNC(snd_pcm_readi);
MAKE_FUNC(snd_pcm_writei); MAKE_FUNC(snd_pcm_writei);
MAKE_FUNC(snd_pcm_drain); MAKE_FUNC(snd_pcm_drain);
MAKE_FUNC(snd_pcm_info_malloc); MAKE_FUNC(snd_pcm_info_malloc);
@ -274,6 +279,42 @@ static ALuint ALSANoMMapProc(ALvoid *ptr)
return 0; return 0;
} }
static ALuint ALSANoMMapCaptureProc(ALvoid *ptr)
{
ALCdevice *pDevice = (ALCdevice*)ptr;
alsa_data *data = (alsa_data*)pDevice->ExtraData;
snd_pcm_sframes_t avail;
while(!data->killNow)
{
avail = (snd_pcm_uframes_t)data->size / psnd_pcm_frames_to_bytes(data->pcmHandle, 1);
avail = psnd_pcm_readi(data->pcmHandle, data->buffer, avail);
switch(avail)
{
case -EAGAIN:
continue;
case -ESTRPIPE:
while((avail=psnd_pcm_resume(data->pcmHandle)) == -EAGAIN)
Sleep(1);
break;
case -EPIPE:
break;
default:
if (avail >= 0 && data->doCapture)
WriteRingBuffer(data->ring, data->buffer, avail);
break;
}
if(avail < 0)
{
avail = psnd_pcm_prepare(data->pcmHandle);
if(avail < 0)
AL_PRINT("prepare error: %s\n", psnd_strerror(avail));
}
}
return 0;
}
static ALCboolean alsa_open_playback(ALCdevice *device, const ALCchar *deviceName) static ALCboolean alsa_open_playback(ALCdevice *device, const ALCchar *deviceName)
{ {
snd_pcm_uframes_t bufferSizeInFrames; snd_pcm_uframes_t bufferSizeInFrames;
@ -449,6 +490,7 @@ open_alsa:
data->thread = StartThread(ALSAProc, device); data->thread = StartThread(ALSAProc, device);
if(data->thread == NULL) if(data->thread == NULL)
{ {
AL_PRINT("Could not create playback thread\n");
psnd_pcm_close(data->pcmHandle); psnd_pcm_close(data->pcmHandle);
device->ExtraData = NULL; device->ExtraData = NULL;
free(data->buffer); free(data->buffer);
@ -477,8 +519,11 @@ static ALCboolean alsa_open_capture(ALCdevice *pDevice, const ALCchar *deviceNam
snd_pcm_format_t alsaFormat; snd_pcm_format_t alsaFormat;
snd_pcm_hw_params_t *p; snd_pcm_hw_params_t *p;
snd_pcm_uframes_t bufferSizeInFrames; snd_pcm_uframes_t bufferSizeInFrames;
snd_pcm_access_t access;
const char *str;
alsa_data *data; alsa_data *data;
char driver[64]; char driver[64];
int allowmmap;
char *err; char *err;
int i; int i;
@ -542,12 +587,18 @@ open_alsa:
AL_PRINT("Unknown format?! %x\n", format); AL_PRINT("Unknown format?! %x\n", format);
} }
bufferSizeInFrames = SampleSize; str = GetConfigValue("alsa", "mmap", "true");
allowmmap = (strcasecmp(str, "true") == 0 ||
strcasecmp(str, "yes") == 0 ||
strcasecmp(str, "on") == 0 ||
atoi(str) != 0);
bufferSizeInFrames = SampleSize;
psnd_pcm_hw_params_malloc(&p); psnd_pcm_hw_params_malloc(&p);
#define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1)) #define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1))
/* start with the largest configuration space possible */ /* start with the largest configuration space possible */
if(!(ok(psnd_pcm_hw_params_any(data->pcmHandle, p), "any") && if(!(allowmmap &&
ok(psnd_pcm_hw_params_any(data->pcmHandle, p), "any") &&
/* set interleaved access */ /* set interleaved access */
ok(psnd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED), "set access") && ok(psnd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED), "set access") &&
/* set format (implicitly sets sample bits) */ /* set format (implicitly sets sample bits) */
@ -560,6 +611,17 @@ open_alsa:
ok(psnd_pcm_hw_params_set_buffer_size_min(data->pcmHandle, p, &bufferSizeInFrames), "set buffer size min") && ok(psnd_pcm_hw_params_set_buffer_size_min(data->pcmHandle, p, &bufferSizeInFrames), "set buffer size min") &&
/* install and prepare hardware configuration */ /* install and prepare hardware configuration */
ok(psnd_pcm_hw_params(data->pcmHandle, p), "set params"))) ok(psnd_pcm_hw_params(data->pcmHandle, p), "set params")))
{
if(i < 0)
AL_PRINT("%s failed: %s\n", err, psnd_strerror(i));
bufferSizeInFrames = SampleSize;
if(!(ok(psnd_pcm_hw_params_any(data->pcmHandle, p), "any") &&
ok(psnd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED), "set access") &&
ok(psnd_pcm_hw_params_set_format(data->pcmHandle, p, alsaFormat), "set format") &&
ok(psnd_pcm_hw_params_set_channels(data->pcmHandle, p, aluChannelsFromFormat(pDevice->Format)), "set channels") &&
ok(psnd_pcm_hw_params_set_rate(data->pcmHandle, p, frequency, 0), "set rate") &&
ok(psnd_pcm_hw_params_set_buffer_size_near(data->pcmHandle, p, &bufferSizeInFrames), "set buffer size near") &&
ok(psnd_pcm_hw_params(data->pcmHandle, p), "set params")))
{ {
AL_PRINT("%s failed: %s\n", err, psnd_strerror(i)); AL_PRINT("%s failed: %s\n", err, psnd_strerror(i));
psnd_pcm_hw_params_free(p); psnd_pcm_hw_params_free(p);
@ -567,9 +629,68 @@ open_alsa:
free(data); free(data);
return ALC_FALSE; return ALC_FALSE;
} }
}
#undef ok #undef ok
if((i=psnd_pcm_hw_params_get_access(p, &access)) < 0)
{
AL_PRINT("get_access failed: %s\n", psnd_strerror(i));
psnd_pcm_hw_params_free(p);
psnd_pcm_close(data->pcmHandle);
free(data);
return ALC_FALSE;
}
if((i=psnd_pcm_hw_params_get_period_size(p, &bufferSizeInFrames, NULL)) < 0)
{
AL_PRINT("get size failed: %s\n", psnd_strerror(i));
psnd_pcm_hw_params_free(p);
psnd_pcm_close(data->pcmHandle);
free(data);
return ALC_FALSE;
}
psnd_pcm_hw_params_free(p); psnd_pcm_hw_params_free(p);
if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
{
ALuint frameSize = aluChannelsFromFormat(pDevice->Format);
frameSize *= aluBytesFromFormat(pDevice->Format);
data->ring = CreateRingBuffer(frameSize, SampleSize);
if(!data->ring)
{
AL_PRINT("ring buffer create failed\n");
psnd_pcm_close(data->pcmHandle);
free(data);
return ALC_FALSE;
}
data->size = psnd_pcm_frames_to_bytes(data->pcmHandle, bufferSizeInFrames);
data->buffer = malloc(data->size);
if(!data->buffer)
{
AL_PRINT("buffer malloc failed\n");
psnd_pcm_close(data->pcmHandle);
DestroyRingBuffer(data->ring);
free(data);
return ALC_FALSE;
}
pDevice->ExtraData = data;
data->thread = StartThread(ALSANoMMapCaptureProc, pDevice);
if(data->thread == NULL)
{
AL_PRINT("Could not create capture thread\n");
pDevice->ExtraData = NULL;
psnd_pcm_close(data->pcmHandle);
DestroyRingBuffer(data->ring);
free(data->buffer);
free(data);
return ALC_FALSE;
}
}
else
{
i = psnd_pcm_prepare(data->pcmHandle); i = psnd_pcm_prepare(data->pcmHandle);
if(i < 0) if(i < 0)
{ {
@ -578,14 +699,22 @@ open_alsa:
free(data); free(data);
return ALC_FALSE; return ALC_FALSE;
} }
pDevice->ExtraData = data; pDevice->ExtraData = data;
}
return ALC_TRUE; return ALC_TRUE;
} }
static void alsa_close_capture(ALCdevice *pDevice) static void alsa_close_capture(ALCdevice *pDevice)
{ {
alsa_data *data = (alsa_data*)pDevice->ExtraData; alsa_data *data = (alsa_data*)pDevice->ExtraData;
if(data->thread)
{
data->killNow = 1;
StopThread(data->thread);
DestroyRingBuffer(data->ring);
}
psnd_pcm_close(data->pcmHandle); psnd_pcm_close(data->pcmHandle);
free(data); free(data);
@ -595,13 +724,21 @@ static void alsa_close_capture(ALCdevice *pDevice)
static void alsa_start_capture(ALCdevice *pDevice) static void alsa_start_capture(ALCdevice *pDevice)
{ {
alsa_data *data = (alsa_data*)pDevice->ExtraData; alsa_data *data = (alsa_data*)pDevice->ExtraData;
if(data->thread)
data->doCapture = 1;
else
{
psnd_pcm_prepare(data->pcmHandle); psnd_pcm_prepare(data->pcmHandle);
psnd_pcm_start(data->pcmHandle); psnd_pcm_start(data->pcmHandle);
} }
}
static void alsa_stop_capture(ALCdevice *pDevice) static void alsa_stop_capture(ALCdevice *pDevice)
{ {
alsa_data *data = (alsa_data*)pDevice->ExtraData; alsa_data *data = (alsa_data*)pDevice->ExtraData;
if(data->thread)
data->doCapture = 0;
else
psnd_pcm_drain(data->pcmHandle); psnd_pcm_drain(data->pcmHandle);
} }
@ -613,6 +750,15 @@ static void alsa_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint l
snd_pcm_uframes_t size, offset; snd_pcm_uframes_t size, offset;
int err; int err;
if(data->thread)
{
if(lSamples <= (ALCuint)RingBufferSize(data->ring))
ReadRingBuffer(data->ring, pBuffer, lSamples);
else
SetALCError(ALC_INVALID_VALUE);
return;
}
frames = psnd_pcm_avail_update(data->pcmHandle); frames = psnd_pcm_avail_update(data->pcmHandle);
if(frames < 0) if(frames < 0)
{ {
@ -668,7 +814,12 @@ static void alsa_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint l
static ALCuint alsa_available_samples(ALCdevice *pDevice) static ALCuint alsa_available_samples(ALCdevice *pDevice)
{ {
alsa_data *data = (alsa_data*)pDevice->ExtraData; alsa_data *data = (alsa_data*)pDevice->ExtraData;
snd_pcm_sframes_t frames = psnd_pcm_avail_update(data->pcmHandle); snd_pcm_sframes_t frames;
if(data->thread)
return RingBufferSize(data->ring);
frames = psnd_pcm_avail_update(data->pcmHandle);
if(frames < 0) if(frames < 0)
{ {
int err = xrun_recovery(data->pcmHandle, frames); int err = xrun_recovery(data->pcmHandle, frames);
@ -744,6 +895,7 @@ LOAD_FUNC(snd_pcm_hw_params_set_rate_near);
LOAD_FUNC(snd_pcm_hw_params_set_rate); LOAD_FUNC(snd_pcm_hw_params_set_rate);
LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_near); LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_near);
LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_min); LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_min);
LOAD_FUNC(snd_pcm_hw_params_get_buffer_size);
LOAD_FUNC(snd_pcm_hw_params_get_period_size); LOAD_FUNC(snd_pcm_hw_params_get_period_size);
LOAD_FUNC(snd_pcm_hw_params_get_access); LOAD_FUNC(snd_pcm_hw_params_get_access);
LOAD_FUNC(snd_pcm_hw_params); LOAD_FUNC(snd_pcm_hw_params);
@ -756,6 +908,7 @@ LOAD_FUNC(snd_pcm_avail_update);
LOAD_FUNC(snd_pcm_areas_silence); LOAD_FUNC(snd_pcm_areas_silence);
LOAD_FUNC(snd_pcm_mmap_begin); LOAD_FUNC(snd_pcm_mmap_begin);
LOAD_FUNC(snd_pcm_mmap_commit); LOAD_FUNC(snd_pcm_mmap_commit);
LOAD_FUNC(snd_pcm_readi);
LOAD_FUNC(snd_pcm_writei); LOAD_FUNC(snd_pcm_writei);
LOAD_FUNC(snd_pcm_drain); LOAD_FUNC(snd_pcm_drain);