|
Packit |
3ae693 |
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/***
|
|
Packit |
3ae693 |
This file is part of libcanberra.
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
Copyright 2008 Lennart Poettering
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
libcanberra is free software; you can redistribute it and/or modify
|
|
Packit |
3ae693 |
it under the terms of the GNU Lesser General Public License as
|
|
Packit |
3ae693 |
published by the Free Software Foundation, either version 2.1 of the
|
|
Packit |
3ae693 |
License, or (at your option) any later version.
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
libcanberra is distributed in the hope that it will be useful, but
|
|
Packit |
3ae693 |
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
3ae693 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
3ae693 |
Lesser General Public License for more details.
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
You should have received a copy of the GNU Lesser General Public
|
|
Packit |
3ae693 |
License along with libcanberra. If not, see
|
|
Packit |
3ae693 |
<http://www.gnu.org/licenses/>.
|
|
Packit |
3ae693 |
***/
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
#ifdef HAVE_CONFIG_H
|
|
Packit |
3ae693 |
#include <config.h>
|
|
Packit |
3ae693 |
#endif
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
#include <errno.h>
|
|
Packit |
3ae693 |
#include <stdlib.h>
|
|
Packit |
3ae693 |
#include <pthread.h>
|
|
Packit |
3ae693 |
#include <semaphore.h>
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
#include <alsa/asoundlib.h>
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
#include "canberra.h"
|
|
Packit |
3ae693 |
#include "common.h"
|
|
Packit |
3ae693 |
#include "driver.h"
|
|
Packit |
3ae693 |
#include "llist.h"
|
|
Packit |
3ae693 |
#include "read-sound-file.h"
|
|
Packit |
3ae693 |
#include "sound-theme-spec.h"
|
|
Packit |
3ae693 |
#include "malloc.h"
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
struct private;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
struct outstanding {
|
|
Packit |
3ae693 |
CA_LLIST_FIELDS(struct outstanding);
|
|
Packit |
3ae693 |
ca_bool_t dead;
|
|
Packit |
3ae693 |
uint32_t id;
|
|
Packit |
3ae693 |
ca_finish_callback_t callback;
|
|
Packit |
3ae693 |
void *userdata;
|
|
Packit |
3ae693 |
ca_sound_file *file;
|
|
Packit |
3ae693 |
snd_pcm_t *pcm;
|
|
Packit |
3ae693 |
int pipe_fd[2];
|
|
Packit |
3ae693 |
ca_context *context;
|
|
Packit |
3ae693 |
};
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
struct private {
|
|
Packit |
3ae693 |
ca_theme_data *theme;
|
|
Packit |
3ae693 |
ca_mutex *outstanding_mutex;
|
|
Packit |
3ae693 |
ca_bool_t signal_semaphore;
|
|
Packit |
3ae693 |
sem_t semaphore;
|
|
Packit |
3ae693 |
ca_bool_t semaphore_allocated;
|
|
Packit |
3ae693 |
CA_LLIST_HEAD(struct outstanding, outstanding);
|
|
Packit |
3ae693 |
};
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
#define PRIVATE(c) ((struct private *) ((c)->private))
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
static void outstanding_free(struct outstanding *o) {
|
|
Packit |
3ae693 |
ca_assert(o);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (o->pipe_fd[1] >= 0)
|
|
Packit |
3ae693 |
close(o->pipe_fd[1]);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (o->pipe_fd[0] >= 0)
|
|
Packit |
3ae693 |
close(o->pipe_fd[0]);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (o->file)
|
|
Packit |
3ae693 |
ca_sound_file_close(o->file);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (o->pcm)
|
|
Packit |
3ae693 |
snd_pcm_close(o->pcm);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_free(o);
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_open(ca_context *c) {
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(!c->driver || ca_streq(c->driver, "alsa"), CA_ERROR_NODRIVER);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(!PRIVATE(c), CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (!(c->private = p = ca_new0(struct private, 1)))
|
|
Packit |
3ae693 |
return CA_ERROR_OOM;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (!(p->outstanding_mutex = ca_mutex_new())) {
|
|
Packit |
3ae693 |
driver_destroy(c);
|
|
Packit |
3ae693 |
return CA_ERROR_OOM;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (sem_init(&p->semaphore, 0, 0) < 0) {
|
|
Packit |
3ae693 |
driver_destroy(c);
|
|
Packit |
3ae693 |
return CA_ERROR_OOM;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p->semaphore_allocated = TRUE;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_SUCCESS;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_destroy(ca_context *c) {
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
struct outstanding *out;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c->private, CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p = PRIVATE(c);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (p->outstanding_mutex) {
|
|
Packit |
3ae693 |
ca_mutex_lock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* Tell all player threads to terminate */
|
|
Packit |
3ae693 |
for (out = p->outstanding; out; out = out->next) {
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (out->dead)
|
|
Packit |
3ae693 |
continue;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
out->dead = TRUE;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (out->callback)
|
|
Packit |
3ae693 |
out->callback(c, out->id, CA_ERROR_DESTROYED, out->userdata);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* This will cause the thread to wakeup and terminate */
|
|
Packit |
3ae693 |
if (out->pipe_fd[1] >= 0) {
|
|
Packit |
3ae693 |
close(out->pipe_fd[1]);
|
|
Packit |
3ae693 |
out->pipe_fd[1] = -1;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (p->semaphore_allocated) {
|
|
Packit |
3ae693 |
/* Now wait until all players are destroyed */
|
|
Packit |
3ae693 |
p->signal_semaphore = TRUE;
|
|
Packit |
3ae693 |
while (p->outstanding) {
|
|
Packit |
3ae693 |
ca_mutex_unlock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
sem_wait(&p->semaphore);
|
|
Packit |
3ae693 |
ca_mutex_lock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_mutex_unlock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
ca_mutex_free(p->outstanding_mutex);
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (p->theme)
|
|
Packit |
3ae693 |
ca_theme_data_free(p->theme);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (p->semaphore_allocated)
|
|
Packit |
3ae693 |
sem_destroy(&p->semaphore);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_free(p);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
c->private = NULL;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
snd_config_update_free_global();
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_SUCCESS;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_change_device(ca_context *c, const char *device) {
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c->private, CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_SUCCESS;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_change_props(ca_context *c, ca_proplist *changed, ca_proplist *merged) {
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(changed, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(merged, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_SUCCESS;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_cache(ca_context *c, ca_proplist *proplist) {
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(proplist, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_ERROR_NOTSUPPORTED;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
static int translate_error(int error) {
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
switch (error) {
|
|
Packit |
3ae693 |
case -ENODEV:
|
|
Packit |
3ae693 |
case -ENOENT:
|
|
Packit |
3ae693 |
return CA_ERROR_NOTFOUND;
|
|
Packit |
3ae693 |
case -EACCES:
|
|
Packit |
3ae693 |
case -EPERM:
|
|
Packit |
3ae693 |
return CA_ERROR_ACCESS;
|
|
Packit |
3ae693 |
case -ENOMEM:
|
|
Packit |
3ae693 |
return CA_ERROR_OOM;
|
|
Packit |
3ae693 |
case -EBUSY:
|
|
Packit |
3ae693 |
return CA_ERROR_NOTAVAILABLE;
|
|
Packit |
3ae693 |
case -EINVAL:
|
|
Packit |
3ae693 |
return CA_ERROR_INVALID;
|
|
Packit |
3ae693 |
case -ENOSYS:
|
|
Packit |
3ae693 |
return CA_ERROR_NOTSUPPORTED;
|
|
Packit |
3ae693 |
default:
|
|
Packit |
3ae693 |
if (ca_debug())
|
|
Packit |
3ae693 |
fprintf(stderr, "Got unhandled error from ALSA: %s\n", snd_strerror(error));
|
|
Packit |
3ae693 |
return CA_ERROR_IO;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
static const snd_pcm_format_t sample_type_table[] = {
|
|
Packit |
3ae693 |
#ifdef WORDS_BIGENDIAN
|
|
Packit |
3ae693 |
[CA_SAMPLE_S16NE] = SND_PCM_FORMAT_S16_BE,
|
|
Packit |
3ae693 |
[CA_SAMPLE_S16RE] = SND_PCM_FORMAT_S16_LE,
|
|
Packit |
3ae693 |
#else
|
|
Packit |
3ae693 |
[CA_SAMPLE_S16NE] = SND_PCM_FORMAT_S16_LE,
|
|
Packit |
3ae693 |
[CA_SAMPLE_S16RE] = SND_PCM_FORMAT_S16_BE,
|
|
Packit |
3ae693 |
#endif
|
|
Packit |
3ae693 |
[CA_SAMPLE_U8] = SND_PCM_FORMAT_U8
|
|
Packit |
3ae693 |
};
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
static int open_alsa(ca_context *c, struct outstanding *out) {
|
|
Packit |
3ae693 |
int ret;
|
|
Packit |
3ae693 |
snd_pcm_hw_params_t *hwparams;
|
|
Packit |
3ae693 |
unsigned rate;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
snd_pcm_hw_params_alloca(&hwparams);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c->private, CA_ERROR_STATE);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(out, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* In ALSA we need to open different devices for doing
|
|
Packit |
3ae693 |
* multichannel audio. This cnnot be done in a backend-independant
|
|
Packit |
3ae693 |
* wa, hence we limit ourselves to mono/stereo only. */
|
|
Packit |
3ae693 |
ca_return_val_if_fail(ca_sound_file_get_nchannels(out->file) <= 2, CA_ERROR_NOTSUPPORTED);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = snd_pcm_open(&out->pcm, c->device ? c->device : "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = snd_pcm_hw_params_any(out->pcm, hwparams)) < 0)
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = snd_pcm_hw_params_set_access(out->pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = snd_pcm_hw_params_set_format(out->pcm, hwparams, sample_type_table[ca_sound_file_get_sample_type(out->file)])) < 0)
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
rate = ca_sound_file_get_rate(out->file);
|
|
Packit |
3ae693 |
if ((ret = snd_pcm_hw_params_set_rate_near(out->pcm, hwparams, &rate, 0)) < 0)
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = snd_pcm_hw_params_set_channels(out->pcm, hwparams, ca_sound_file_get_nchannels(out->file))) < 0)
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = snd_pcm_hw_params(out->pcm, hwparams)) < 0)
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = snd_pcm_prepare(out->pcm)) < 0)
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_SUCCESS;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
finish:
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return translate_error(ret);
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
#define BUFSIZE (16*1024)
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
static void* thread_func(void *userdata) {
|
|
Packit |
3ae693 |
struct outstanding *out = userdata;
|
|
Packit |
3ae693 |
int ret;
|
|
Packit |
3ae693 |
void *data, *d = NULL;
|
|
Packit |
3ae693 |
size_t fs, data_size;
|
|
Packit |
3ae693 |
size_t nbytes = 0;
|
|
Packit |
3ae693 |
struct pollfd *pfd = NULL;
|
|
Packit |
3ae693 |
nfds_t n_pfd;
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p = PRIVATE(out->context);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
pthread_detach(pthread_self());
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
fs = ca_sound_file_frame_size(out->file);
|
|
Packit |
3ae693 |
data_size = (BUFSIZE/fs)*fs;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (!(data = ca_malloc(data_size))) {
|
|
Packit |
3ae693 |
ret = CA_ERROR_OOM;
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = snd_pcm_poll_descriptors_count(out->pcm)) < 0) {
|
|
Packit |
3ae693 |
ret = translate_error(ret);
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
n_pfd = (nfds_t) ret + 1;
|
|
Packit |
3ae693 |
if (!(pfd = ca_new(struct pollfd, n_pfd))) {
|
|
Packit |
3ae693 |
ret = CA_ERROR_OOM;
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = snd_pcm_poll_descriptors(out->pcm, pfd+1, (unsigned) n_pfd-1)) < 0) {
|
|
Packit |
3ae693 |
ret = translate_error(ret);
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
pfd[0].fd = out->pipe_fd[0];
|
|
Packit |
3ae693 |
pfd[0].events = POLLIN;
|
|
Packit |
3ae693 |
pfd[0].revents = 0;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
for (;;) {
|
|
Packit |
3ae693 |
unsigned short revents;
|
|
Packit |
3ae693 |
snd_pcm_sframes_t sframes;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (out->dead)
|
|
Packit |
3ae693 |
break;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (poll(pfd, n_pfd, -1) < 0) {
|
|
Packit |
3ae693 |
ret = CA_ERROR_SYSTEM;
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* We have been asked to shut down */
|
|
Packit |
3ae693 |
if (pfd[0].revents)
|
|
Packit |
3ae693 |
break;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = snd_pcm_poll_descriptors_revents(out->pcm, pfd+1, (unsigned) n_pfd-1, &revents)) < 0) {
|
|
Packit |
3ae693 |
ret = translate_error(ret);
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (revents != POLLOUT) {
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
switch (snd_pcm_state(out->pcm)) {
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
case SND_PCM_STATE_XRUN:
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = snd_pcm_recover(out->pcm, -EPIPE, 1)) != 0) {
|
|
Packit |
3ae693 |
ret = translate_error(ret);
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
break;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
case SND_PCM_STATE_SUSPENDED:
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = snd_pcm_recover(out->pcm, -ESTRPIPE, 1)) != 0) {
|
|
Packit |
3ae693 |
ret = translate_error(ret);
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
break;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
default:
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
snd_pcm_drop(out->pcm);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = snd_pcm_prepare(out->pcm)) < 0) {
|
|
Packit |
3ae693 |
ret = translate_error(ret);
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
break;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
continue;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (nbytes <= 0) {
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
nbytes = data_size;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = ca_sound_file_read_arbitrary(out->file, data, &nbytes)) < 0)
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
d = data;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (nbytes <= 0) {
|
|
Packit |
3ae693 |
snd_pcm_drain(out->pcm);
|
|
Packit |
3ae693 |
break;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((sframes = snd_pcm_writei(out->pcm, d, nbytes/fs)) < 0) {
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = snd_pcm_recover(out->pcm, (int) sframes, 1)) < 0) {
|
|
Packit |
3ae693 |
ret = translate_error(ret);
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
continue;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
nbytes -= (size_t) sframes*fs;
|
|
Packit |
3ae693 |
d = (uint8_t*) d + (size_t) sframes*fs;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ret = CA_SUCCESS;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
finish:
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_free(data);
|
|
Packit |
3ae693 |
ca_free(pfd);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (!out->dead)
|
|
Packit |
3ae693 |
if (out->callback)
|
|
Packit |
3ae693 |
out->callback(out->context, out->id, ret, out->userdata);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_mutex_lock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
CA_LLIST_REMOVE(struct outstanding, p->outstanding, out);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (!p->outstanding && p->signal_semaphore)
|
|
Packit |
3ae693 |
sem_post(&p->semaphore);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
outstanding_free(out);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_mutex_unlock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return NULL;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_callback_t cb, void *userdata) {
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
struct outstanding *out = NULL;
|
|
Packit |
3ae693 |
int ret;
|
|
Packit |
3ae693 |
pthread_t thread;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(proplist, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(!userdata || cb, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c->private, CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p = PRIVATE(c);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (!(out = ca_new0(struct outstanding, 1))) {
|
|
Packit |
3ae693 |
ret = CA_ERROR_OOM;
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
out->context = c;
|
|
Packit |
3ae693 |
out->id = id;
|
|
Packit |
3ae693 |
out->callback = cb;
|
|
Packit |
3ae693 |
out->userdata = userdata;
|
|
Packit |
3ae693 |
out->pipe_fd[0] = out->pipe_fd[1] = -1;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (pipe(out->pipe_fd) < 0) {
|
|
Packit |
3ae693 |
ret = CA_ERROR_SYSTEM;
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = ca_lookup_sound(&out->file, NULL, &p->theme, c->props, proplist)) < 0)
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = open_alsa(c, out)) < 0)
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* OK, we're ready to go, so let's add this to our list */
|
|
Packit |
3ae693 |
ca_mutex_lock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
CA_LLIST_PREPEND(struct outstanding, p->outstanding, out);
|
|
Packit |
3ae693 |
ca_mutex_unlock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (pthread_create(&thread, NULL, thread_func, out) < 0) {
|
|
Packit |
3ae693 |
ret = CA_ERROR_OOM;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_mutex_lock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
CA_LLIST_REMOVE(struct outstanding, p->outstanding, out);
|
|
Packit |
3ae693 |
ca_mutex_unlock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
goto finish;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ret = CA_SUCCESS;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
finish:
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* We keep the outstanding struct around if we need clean up later to */
|
|
Packit |
3ae693 |
if (ret != CA_SUCCESS)
|
|
Packit |
3ae693 |
outstanding_free(out);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return ret;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_cancel(ca_context *c, uint32_t id) {
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
struct outstanding *out;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c->private, CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p = PRIVATE(c);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_mutex_lock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
for (out = p->outstanding; out; out = out->next) {
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (out->id != id)
|
|
Packit |
3ae693 |
continue;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (out->dead)
|
|
Packit |
3ae693 |
continue;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
out->dead = TRUE;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (out->callback)
|
|
Packit |
3ae693 |
out->callback(c, out->id, CA_ERROR_CANCELED, out->userdata);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* This will cause the thread to wakeup and terminate */
|
|
Packit |
3ae693 |
if (out->pipe_fd[1] >= 0) {
|
|
Packit |
3ae693 |
close(out->pipe_fd[1]);
|
|
Packit |
3ae693 |
out->pipe_fd[1] = -1;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_mutex_unlock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_SUCCESS;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_playing(ca_context *c, uint32_t id, int *playing) {
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
struct outstanding *out;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c->private, CA_ERROR_STATE);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(playing, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p = PRIVATE(c);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
*playing = 0;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_mutex_lock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
for (out = p->outstanding; out; out = out->next) {
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (out->dead ||
|
|
Packit |
3ae693 |
out->id != id)
|
|
Packit |
3ae693 |
continue;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
*playing = 1;
|
|
Packit |
3ae693 |
break;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_mutex_unlock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_SUCCESS;
|
|
Packit |
3ae693 |
}
|