channels/rdpsnd: start alsa async mode

This commit is contained in:
Marc-André Moreau 2013-02-21 03:38:36 -05:00
parent 385d0daced
commit 4df376739d
2 changed files with 104 additions and 45 deletions

View File

@ -37,12 +37,16 @@
#include "rdpsnd_main.h" #include "rdpsnd_main.h"
//#define RDPSND_ALSA_ASYNC 1
typedef struct rdpsnd_alsa_plugin rdpsndAlsaPlugin; typedef struct rdpsnd_alsa_plugin rdpsndAlsaPlugin;
struct rdpsnd_alsa_plugin struct rdpsnd_alsa_plugin
{ {
rdpsndDevicePlugin device; rdpsndDevicePlugin device;
HANDLE mutex;
HANDLE thread;
char* device_name; char* device_name;
snd_pcm_t* pcm_handle; snd_pcm_t* pcm_handle;
snd_mixer_t* mixer_handle; snd_mixer_t* mixer_handle;
@ -60,7 +64,6 @@ struct rdpsnd_alsa_plugin
UINT32 audio_data_left; UINT32 audio_data_left;
snd_pcm_uframes_t period_size; snd_pcm_uframes_t period_size;
snd_async_handler_t* pcm_callback; snd_async_handler_t* pcm_callback;
FREERDP_DSP_CONTEXT* dsp_context; FREERDP_DSP_CONTEXT* dsp_context;
}; };
@ -76,7 +79,9 @@ static void rdpsnd_alsa_set_params(rdpsndAlsaPlugin* alsa)
snd_pcm_drop(alsa->pcm_handle); snd_pcm_drop(alsa->pcm_handle);
#ifdef RDPSND_ALSA_ASYNC
//snd_async_add_pcm_handler(&alsa->pcm_callback, alsa->pcm_handle, rdpsnd_alsa_async_handler, (void*) alsa); //snd_async_add_pcm_handler(&alsa->pcm_callback, alsa->pcm_handle, rdpsnd_alsa_async_handler, (void*) alsa);
#endif
status = snd_pcm_hw_params_malloc(&hw_params); status = snd_pcm_hw_params_malloc(&hw_params);
@ -235,7 +240,7 @@ static void rdpsnd_alsa_open(rdpsndDevicePlugin* device, rdpsndFormat* format, i
int status; int status;
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device; rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
if (alsa->pcm_handle != 0) if (alsa->pcm_handle)
return; return;
DEBUG_SVC("opening"); DEBUG_SVC("opening");
@ -261,7 +266,7 @@ static void rdpsnd_alsa_close(rdpsndDevicePlugin* device)
{ {
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*)device; rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*)device;
if (alsa->pcm_handle != 0) if (alsa->pcm_handle)
{ {
DEBUG_SVC("close"); DEBUG_SVC("close");
snd_pcm_drain(alsa->pcm_handle); snd_pcm_drain(alsa->pcm_handle);
@ -289,6 +294,8 @@ static void rdpsnd_alsa_free(rdpsndDevicePlugin* device)
freerdp_dsp_context_free(alsa->dsp_context); freerdp_dsp_context_free(alsa->dsp_context);
CloseHandle(alsa->mutex);
free(alsa); free(alsa);
} }
@ -359,6 +366,55 @@ static void rdpsnd_alsa_set_volume(rdpsndDevicePlugin* device, UINT32 value)
} }
} }
void rdpsnd_alsa_process_audio_data(rdpsndAlsaPlugin* alsa)
{
int length;
int status;
BYTE* end;
BYTE* pindex;
int rbytes_per_frame;
int sbytes_per_frame;
sbytes_per_frame = alsa->source_channels * alsa->bytes_per_channel;
rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_channel;
WaitForSingleObject(alsa->mutex, INFINITE);
pindex = alsa->audio_data;
end = pindex + alsa->audio_data_left;
while (pindex + alsa->period_size * rbytes_per_frame <= end)
{
length = end - pindex;
status = snd_pcm_writei(alsa->pcm_handle, pindex, alsa->period_size);
if (status == -EPIPE)
{
snd_pcm_recover(alsa->pcm_handle, status, 0);
status = 0;
}
else if (status < 0)
{
DEBUG_WARN("snd_pcm_writei status %d", status);
snd_pcm_close(alsa->pcm_handle);
alsa->pcm_handle = NULL;
rdpsnd_alsa_open((rdpsndDevicePlugin*) alsa, NULL, alsa->latency);
break;
}
pindex += status * rbytes_per_frame;
}
if ((pindex <= end) && (pindex != alsa->audio_data))
{
CopyMemory(alsa->audio_data, pindex, end - pindex);
alsa->audio_data_left = end - pindex;
}
ReleaseMutex(alsa->mutex);
}
void rdpsnd_alsa_async_handler(snd_async_handler_t* pcm_callback) void rdpsnd_alsa_async_handler(snd_async_handler_t* pcm_callback)
{ {
snd_pcm_t* pcm_handle; snd_pcm_t* pcm_handle;
@ -369,27 +425,26 @@ void rdpsnd_alsa_async_handler(snd_async_handler_t* pcm_callback)
alsa = (rdpsndAlsaPlugin*) snd_async_handler_get_callback_private(pcm_callback); alsa = (rdpsndAlsaPlugin*) snd_async_handler_get_callback_private(pcm_callback);
avail = snd_pcm_avail_update(pcm_handle); avail = snd_pcm_avail_update(pcm_handle);
rdpsnd_alsa_process_audio_data(alsa);
} }
static void rdpsnd_alsa_play(rdpsndDevicePlugin* device, BYTE* data, int size) static void rdpsnd_alsa_play(rdpsndDevicePlugin* device, BYTE* data, int size)
{ {
BYTE* src; BYTE* src;
int len;
int status;
int frames; int frames;
int rbytes_per_frame; int rbytes_per_frame;
int sbytes_per_frame; int sbytes_per_frame;
BYTE* pindex;
BYTE* end;
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device; rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
if (alsa->pcm_handle == 0) if (!alsa->pcm_handle)
return; return;
if (alsa->wformat == WAVE_FORMAT_ADPCM) if (alsa->wformat == WAVE_FORMAT_ADPCM)
{ {
alsa->dsp_context->decode_ms_adpcm(alsa->dsp_context, alsa->dsp_context->decode_ms_adpcm(alsa->dsp_context,
data, size, alsa->source_channels, alsa->block_size); data, size, alsa->source_channels, alsa->block_size);
size = alsa->dsp_context->adpcm_size; size = alsa->dsp_context->adpcm_size;
src = alsa->dsp_context->adpcm_buffer; src = alsa->dsp_context->adpcm_buffer;
} }
@ -397,6 +452,7 @@ static void rdpsnd_alsa_play(rdpsndDevicePlugin* device, BYTE* data, int size)
{ {
alsa->dsp_context->decode_ima_adpcm(alsa->dsp_context, alsa->dsp_context->decode_ima_adpcm(alsa->dsp_context,
data, size, alsa->source_channels, alsa->block_size); data, size, alsa->source_channels, alsa->block_size);
size = alsa->dsp_context->adpcm_size; size = alsa->dsp_context->adpcm_size;
src = alsa->dsp_context->adpcm_buffer; src = alsa->dsp_context->adpcm_buffer;
} }
@ -432,6 +488,8 @@ static void rdpsnd_alsa_play(rdpsndDevicePlugin* device, BYTE* data, int size)
src = alsa->dsp_context->resampled_buffer; src = alsa->dsp_context->resampled_buffer;
} }
WaitForSingleObject(alsa->mutex, INFINITE);
if (alsa->audio_data_left + size > alsa->audio_data_size) if (alsa->audio_data_left + size > alsa->audio_data_size)
{ {
alsa->audio_data = realloc(alsa->audio_data, alsa->audio_data_left + size); alsa->audio_data = realloc(alsa->audio_data, alsa->audio_data_left + size);
@ -441,36 +499,11 @@ static void rdpsnd_alsa_play(rdpsndDevicePlugin* device, BYTE* data, int size)
CopyMemory(alsa->audio_data + alsa->audio_data_left, src, size); CopyMemory(alsa->audio_data + alsa->audio_data_left, src, size);
alsa->audio_data_left += size; alsa->audio_data_left += size;
pindex = alsa->audio_data; ReleaseMutex(alsa->mutex);
end = pindex + alsa->audio_data_left;
while (pindex + alsa->period_size * rbytes_per_frame <= end) #ifndef RDPSND_ALSA_ASYNC
{ rdpsnd_alsa_process_audio_data(alsa);
len = end - pindex; #endif
status = snd_pcm_writei(alsa->pcm_handle, pindex, alsa->period_size);
if (status == -EPIPE)
{
snd_pcm_recover(alsa->pcm_handle, status, 0);
status = 0;
}
else if (status < 0)
{
DEBUG_WARN("snd_pcm_writei status %d", status);
snd_pcm_close(alsa->pcm_handle);
alsa->pcm_handle = 0;
rdpsnd_alsa_open(device, NULL, alsa->latency);
break;
}
pindex += status * rbytes_per_frame;
}
if ((pindex <= end) && (pindex != alsa->audio_data))
{
CopyMemory(alsa->audio_data, pindex, end - pindex);
alsa->audio_data_left = end - pindex;
}
} }
static void rdpsnd_alsa_start(rdpsndDevicePlugin* device) static void rdpsnd_alsa_start(rdpsndDevicePlugin* device)
@ -483,6 +516,21 @@ static void rdpsnd_alsa_start(rdpsndDevicePlugin* device)
snd_pcm_start(alsa->pcm_handle); snd_pcm_start(alsa->pcm_handle);
} }
void* rdpsnd_alsa_thread(void* arg)
{
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) arg;
while (1)
{
if (alsa->pcm_handle)
rdpsnd_alsa_process_audio_data(alsa);
Sleep(10);
}
return NULL;
}
COMMAND_LINE_ARGUMENT_A rdpsnd_alsa_args[] = COMMAND_LINE_ARGUMENT_A rdpsnd_alsa_args[] =
{ {
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" }, { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" },
@ -543,6 +591,12 @@ int freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pE
args = pEntryPoints->args; args = pEntryPoints->args;
rdpsnd_alsa_parse_addin_args((rdpsndDevicePlugin*) alsa, args); rdpsnd_alsa_parse_addin_args((rdpsndDevicePlugin*) alsa, args);
alsa->mutex = CreateMutex(NULL, FALSE, NULL);
#ifdef RDPSND_ALSA_ASYNC
alsa->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) rdpsnd_alsa_thread, (void*) alsa, 0, NULL);
#endif
if (!alsa->device_name) if (!alsa->device_name)
alsa->device_name = _strdup("default"); alsa->device_name = _strdup("default");

View File

@ -43,10 +43,10 @@ struct rdpsnd_pulse_plugin
rdpsndDevicePlugin device; rdpsndDevicePlugin device;
char* device_name; char* device_name;
pa_threaded_mainloop *mainloop; pa_threaded_mainloop* mainloop;
pa_context *context; pa_context* context;
pa_sample_spec sample_spec; pa_sample_spec sample_spec;
pa_stream *stream; pa_stream* stream;
int format; int format;
int block_size; int block_size;
int latency; int latency;
@ -106,13 +106,16 @@ static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device)
for (;;) for (;;)
{ {
state = pa_context_get_state(pulse->context); state = pa_context_get_state(pulse->context);
if (state == PA_CONTEXT_READY) if (state == PA_CONTEXT_READY)
break; break;
if (!PA_CONTEXT_IS_GOOD(state)) if (!PA_CONTEXT_IS_GOOD(state))
{ {
DEBUG_WARN("bad context state (%d)", pa_context_errno(pulse->context)); DEBUG_WARN("bad context state (%d)", pa_context_errno(pulse->context));
break; break;
} }
pa_threaded_mainloop_wait(pulse->mainloop); pa_threaded_mainloop_wait(pulse->mainloop);
} }
@ -139,7 +142,7 @@ static void rdpsnd_pulse_stream_success_callback(pa_stream* stream, int success,
static void rdpsnd_pulse_wait_for_operation(rdpsndPulsePlugin* pulse, pa_operation* operation) static void rdpsnd_pulse_wait_for_operation(rdpsndPulsePlugin* pulse, pa_operation* operation)
{ {
if (operation == NULL) if (!operation)
return; return;
while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
@ -225,7 +228,7 @@ static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, rdpsndFormat*
break; break;
case WAVE_FORMAT_ADPCM: case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM: /* IMA ADPCM */ case WAVE_FORMAT_DVI_ADPCM:
sample_spec.format = PA_SAMPLE_S16LE; sample_spec.format = PA_SAMPLE_S16LE;
break; break;
@ -281,7 +284,7 @@ static void rdpsnd_pulse_open(rdpsndDevicePlugin* device, rdpsndFormat* format,
return; return;
} }
/* install essential callbacks */ /* register essential callbacks */
pa_stream_set_state_callback(pulse->stream, rdpsnd_pulse_stream_state_callback, pulse); pa_stream_set_state_callback(pulse->stream, rdpsnd_pulse_stream_state_callback, pulse);
pa_stream_set_write_callback(pulse->stream, rdpsnd_pulse_stream_request_callback, pulse); pa_stream_set_write_callback(pulse->stream, rdpsnd_pulse_stream_request_callback, pulse);
@ -466,17 +469,19 @@ static void rdpsnd_pulse_play(rdpsndDevicePlugin* device, BYTE* data, int size)
if (!pulse->stream) if (!pulse->stream)
return; return;
if (pulse->format == 2) if (pulse->format == WAVE_FORMAT_ADPCM)
{ {
pulse->dsp_context->decode_ms_adpcm(pulse->dsp_context, pulse->dsp_context->decode_ms_adpcm(pulse->dsp_context,
data, size, pulse->sample_spec.channels, pulse->block_size); data, size, pulse->sample_spec.channels, pulse->block_size);
size = pulse->dsp_context->adpcm_size; size = pulse->dsp_context->adpcm_size;
src = pulse->dsp_context->adpcm_buffer; src = pulse->dsp_context->adpcm_buffer;
} }
else if (pulse->format == 0x11) else if (pulse->format == WAVE_FORMAT_DVI_ADPCM)
{ {
pulse->dsp_context->decode_ima_adpcm(pulse->dsp_context, pulse->dsp_context->decode_ima_adpcm(pulse->dsp_context,
data, size, pulse->sample_spec.channels, pulse->block_size); data, size, pulse->sample_spec.channels, pulse->block_size);
size = pulse->dsp_context->adpcm_size; size = pulse->dsp_context->adpcm_size;
src = pulse->dsp_context->adpcm_buffer; src = pulse->dsp_context->adpcm_buffer;
} }