2012-08-08 23:25:53 +02:00
|
|
|
/*
|
|
|
|
* simpleaudio-alsa.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 Kamal Mostafa <kamal@whence.com>
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if USE_ALSA
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2012-10-05 03:10:33 +02:00
|
|
|
#include <stdlib.h>
|
2012-08-08 23:25:53 +02:00
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include <alsa/asoundlib.h>
|
|
|
|
|
|
|
|
#include "simpleaudio.h"
|
|
|
|
#include "simpleaudio_internal.h"
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ALSA backend for simpleaudio
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static ssize_t
|
2012-08-12 23:39:23 +02:00
|
|
|
sa_alsa_read( simpleaudio *sa, void *buf, size_t nframes )
|
2012-08-08 23:25:53 +02:00
|
|
|
{
|
|
|
|
ssize_t frames_read = 0;
|
|
|
|
snd_pcm_t *pcm = (snd_pcm_t *)sa->backend_handle;
|
|
|
|
while ( frames_read < nframes ) {
|
|
|
|
ssize_t r;
|
2012-10-02 04:02:39 +02:00
|
|
|
void * data = buf+frames_read*sa->backend_framesize;
|
|
|
|
ssize_t count = nframes-frames_read;
|
|
|
|
r = snd_pcm_readi(pcm, data, count);
|
|
|
|
if ( r >= 0 ) {
|
|
|
|
frames_read += r;
|
|
|
|
if ( r != count )
|
|
|
|
fprintf(stderr, "#short+%zd#\n", r);
|
|
|
|
continue;
|
2012-08-08 23:25:53 +02:00
|
|
|
}
|
2012-10-02 04:02:39 +02:00
|
|
|
if (r == -EPIPE) { // Underrun
|
|
|
|
fprintf(stderr, "#");
|
|
|
|
snd_pcm_prepare(pcm);
|
|
|
|
} else {
|
2012-08-08 23:25:53 +02:00
|
|
|
fprintf(stderr, "snd_pcm_readi: %s\n", snd_strerror(r));
|
2012-10-02 04:02:39 +02:00
|
|
|
if (r == -EAGAIN || r== -ESTRPIPE)
|
|
|
|
snd_pcm_wait(pcm, 1000);
|
|
|
|
else
|
|
|
|
return r;
|
2012-08-08 23:25:53 +02:00
|
|
|
}
|
|
|
|
}
|
2012-10-02 04:02:39 +02:00
|
|
|
// fprintf(stderr,("[%zd]\n"), frames_read);
|
2012-08-08 23:25:53 +02:00
|
|
|
return frames_read;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static ssize_t
|
2012-08-12 23:39:23 +02:00
|
|
|
sa_alsa_write( simpleaudio *sa, void *buf, size_t nframes )
|
2012-08-08 23:25:53 +02:00
|
|
|
{
|
2012-08-09 01:24:44 +02:00
|
|
|
ssize_t frames_written = 0;
|
2012-08-08 23:25:53 +02:00
|
|
|
snd_pcm_t *pcm = (snd_pcm_t *)sa->backend_handle;
|
2012-08-09 01:24:44 +02:00
|
|
|
while ( frames_written < nframes ) {
|
|
|
|
ssize_t r;
|
|
|
|
r = snd_pcm_writei(pcm, buf+frames_written*sa->backend_framesize, nframes-frames_written);
|
|
|
|
if (r < 0) {
|
2012-08-09 03:56:01 +02:00
|
|
|
/* recover from e.g. underruns, and try once more */
|
2012-08-09 01:24:44 +02:00
|
|
|
snd_pcm_recover(pcm, r, 0 /*silent*/);
|
|
|
|
r = snd_pcm_writei(pcm, buf+frames_written*sa->backend_framesize, nframes-frames_written);
|
|
|
|
}
|
|
|
|
if (r < 0) {
|
|
|
|
fprintf(stderr, "E: %s\n", snd_strerror(frames_written));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
frames_written += r;
|
2012-08-08 23:25:53 +02:00
|
|
|
}
|
2012-08-09 01:24:44 +02:00
|
|
|
assert (frames_written == nframes);
|
2012-08-08 23:25:53 +02:00
|
|
|
return frames_written;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
sa_alsa_close( simpleaudio *sa )
|
|
|
|
{
|
|
|
|
snd_pcm_drain(sa->backend_handle);
|
|
|
|
snd_pcm_close(sa->backend_handle);
|
|
|
|
}
|
|
|
|
|
2012-08-12 23:24:55 +02:00
|
|
|
static int
|
|
|
|
sa_alsa_open_stream(
|
|
|
|
simpleaudio *sa,
|
2012-09-10 00:24:01 +02:00
|
|
|
const char *backend_device,
|
2012-08-12 23:24:55 +02:00
|
|
|
sa_direction_t sa_stream_direction,
|
|
|
|
sa_format_t sa_format,
|
2012-08-11 19:07:12 +02:00
|
|
|
unsigned int rate, unsigned int channels,
|
2012-08-08 23:25:53 +02:00
|
|
|
char *app_name, char *stream_name )
|
|
|
|
{
|
|
|
|
snd_pcm_t *pcm;
|
|
|
|
int error;
|
|
|
|
|
2012-09-10 00:24:35 +02:00
|
|
|
char *be_device;
|
|
|
|
if ( ! backend_device ) {
|
|
|
|
be_device = "default";
|
|
|
|
} else {
|
|
|
|
be_device = alloca(32);
|
|
|
|
if ( strchr(backend_device, ':') )
|
|
|
|
snprintf(be_device, 32, "%s", backend_device);
|
|
|
|
else if ( strchr(backend_device, ',') )
|
|
|
|
snprintf(be_device, 32, "plughw:%s", backend_device);
|
|
|
|
else
|
|
|
|
snprintf(be_device, 32, "plughw:%s,0", backend_device);
|
|
|
|
}
|
2012-09-10 00:24:01 +02:00
|
|
|
|
2012-08-08 23:25:53 +02:00
|
|
|
error = snd_pcm_open(&pcm,
|
2012-09-10 00:24:35 +02:00
|
|
|
be_device,
|
2012-08-08 23:25:53 +02:00
|
|
|
sa_stream_direction == SA_STREAM_RECORD ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
|
|
|
|
0 /*mode*/);
|
|
|
|
if (error) {
|
|
|
|
fprintf(stderr, "E: Cannot create ALSA stream: %s\n", snd_strerror(error));
|
2012-08-12 23:24:55 +02:00
|
|
|
return 0;
|
2012-08-08 23:25:53 +02:00
|
|
|
}
|
|
|
|
|
2012-08-13 00:39:04 +02:00
|
|
|
snd_pcm_format_t pcm_format;
|
|
|
|
|
|
|
|
switch ( sa->format ) {
|
|
|
|
case SA_SAMPLE_FORMAT_FLOAT:
|
|
|
|
pcm_format = SND_PCM_FORMAT_FLOAT;
|
|
|
|
break;
|
|
|
|
case SA_SAMPLE_FORMAT_S16:
|
|
|
|
pcm_format = SND_PCM_FORMAT_S16;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
}
|
2012-08-12 06:00:37 +02:00
|
|
|
|
2012-08-09 01:24:44 +02:00
|
|
|
/* set up ALSA hardware params */
|
2012-08-08 23:25:53 +02:00
|
|
|
error = snd_pcm_set_params(pcm,
|
2012-08-13 00:39:04 +02:00
|
|
|
pcm_format,
|
2012-08-08 23:25:53 +02:00
|
|
|
SND_PCM_ACCESS_RW_INTERLEAVED,
|
|
|
|
channels,
|
|
|
|
rate,
|
2012-08-30 06:26:47 +02:00
|
|
|
1 /* soft_resample (allow) */,
|
2015-04-16 23:29:27 +02:00
|
|
|
100000 /* latency (us) */);
|
2012-08-08 23:25:53 +02:00
|
|
|
if (error) {
|
|
|
|
fprintf(stderr, "E: %s\n", snd_strerror(error));
|
|
|
|
snd_pcm_close(pcm);
|
2012-08-12 23:24:55 +02:00
|
|
|
return 0;
|
2012-08-08 23:25:53 +02:00
|
|
|
}
|
|
|
|
|
2012-08-09 03:53:24 +02:00
|
|
|
#if 0
|
2012-08-09 01:24:44 +02:00
|
|
|
snd_pcm_sw_params_t *swparams;
|
|
|
|
snd_pcm_sw_params_alloca(&swparams);
|
|
|
|
error = snd_pcm_sw_params_current(pcm, swparams);
|
|
|
|
if (error) {
|
|
|
|
fprintf(stderr, "E: %s\n", snd_strerror(error));
|
|
|
|
snd_pcm_close(pcm);
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-08-09 03:53:24 +02:00
|
|
|
snd_pcm_sw_params_set_start_threshold(pcm, swparams, NFRAMES_VAL);
|
|
|
|
snd_pcm_sw_params_set_stop_threshold(pcm, swparams, NFRAMES_VAL);
|
|
|
|
snd_pcm_sw_params_set_silence_threshold(pcm, swparams, NFRAMES_VAL);
|
2012-08-09 01:24:44 +02:00
|
|
|
error = snd_pcm_sw_params(pcm, swparams);
|
|
|
|
if (error) {
|
|
|
|
fprintf(stderr, "E: %s\n", snd_strerror(error));
|
|
|
|
snd_pcm_close(pcm);
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-08-09 03:53:24 +02:00
|
|
|
#endif
|
2012-08-09 01:24:44 +02:00
|
|
|
|
2012-08-08 23:25:53 +02:00
|
|
|
sa->backend_handle = pcm;
|
2012-08-12 05:40:20 +02:00
|
|
|
sa->backend_framesize = sa->channels * sa->samplesize;
|
2012-08-08 23:25:53 +02:00
|
|
|
|
2012-08-12 23:24:55 +02:00
|
|
|
return 1;
|
2012-08-08 23:25:53 +02:00
|
|
|
}
|
|
|
|
|
2012-08-12 23:24:55 +02:00
|
|
|
|
|
|
|
const struct simpleaudio_backend simpleaudio_backend_alsa = {
|
|
|
|
sa_alsa_open_stream,
|
|
|
|
sa_alsa_read,
|
|
|
|
sa_alsa_write,
|
|
|
|
sa_alsa_close,
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif /* USE_ALSA */
|