diff options
Diffstat (limited to 'src/oss.c')
-rw-r--r-- | src/oss.c | 612 |
1 files changed, 307 insertions, 305 deletions
@@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + /*** This file is part of libcanberra. @@ -57,474 +59,474 @@ struct private; struct outstanding { - CA_LLIST_FIELDS(struct outstanding); - ca_bool_t dead; - uint32_t id; - ca_finish_callback_t callback; - void *userdata; - ca_sound_file *file; - int pcm; - int pipe_fd[2]; - ca_context *context; + CA_LLIST_FIELDS(struct outstanding); + ca_bool_t dead; + uint32_t id; + ca_finish_callback_t callback; + void *userdata; + ca_sound_file *file; + int pcm; + int pipe_fd[2]; + ca_context *context; }; struct private { - ca_theme_data *theme; - ca_mutex *outstanding_mutex; - ca_bool_t signal_semaphore; - sem_t semaphore; - ca_bool_t semaphore_allocated; - CA_LLIST_HEAD(struct outstanding, outstanding); + ca_theme_data *theme; + ca_mutex *outstanding_mutex; + ca_bool_t signal_semaphore; + sem_t semaphore; + ca_bool_t semaphore_allocated; + CA_LLIST_HEAD(struct outstanding, outstanding); }; #define PRIVATE(c) ((struct private *) ((c)->private)) static void outstanding_free(struct outstanding *o) { - ca_assert(o); + ca_assert(o); - if (o->pipe_fd[1] >= 0) - close(o->pipe_fd[1]); + if (o->pipe_fd[1] >= 0) + close(o->pipe_fd[1]); - if (o->pipe_fd[0] >= 0) - close(o->pipe_fd[0]); + if (o->pipe_fd[0] >= 0) + close(o->pipe_fd[0]); - if (o->file) - ca_sound_file_close(o->file); + if (o->file) + ca_sound_file_close(o->file); - if (o->pcm >= 0) { - close(o->pcm); - o->pcm = -1; - } + if (o->pcm >= 0) { + close(o->pcm); + o->pcm = -1; + } - ca_free(o); + ca_free(o); } int driver_open(ca_context *c) { - struct private *p; + struct private *p; - ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_return_val_if_fail(!c->driver || ca_streq(c->driver, "oss"), CA_ERROR_NODRIVER); - ca_return_val_if_fail(!PRIVATE(c), CA_ERROR_STATE); + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(!c->driver || ca_streq(c->driver, "oss"), CA_ERROR_NODRIVER); + ca_return_val_if_fail(!PRIVATE(c), CA_ERROR_STATE); - if (!(c->private = p = ca_new0(struct private, 1))) - return CA_ERROR_OOM; + if (!(c->private = p = ca_new0(struct private, 1))) + return CA_ERROR_OOM; - if (!(p->outstanding_mutex = ca_mutex_new())) { - driver_destroy(c); - return CA_ERROR_OOM; - } + if (!(p->outstanding_mutex = ca_mutex_new())) { + driver_destroy(c); + return CA_ERROR_OOM; + } - if (sem_init(&p->semaphore, 0, 0) < 0) { - driver_destroy(c); - return CA_ERROR_OOM; - } + if (sem_init(&p->semaphore, 0, 0) < 0) { + driver_destroy(c); + return CA_ERROR_OOM; + } - p->semaphore_allocated = TRUE; + p->semaphore_allocated = TRUE; - return CA_SUCCESS; + return CA_SUCCESS; } int driver_destroy(ca_context *c) { - struct private *p; - struct outstanding *out; + struct private *p; + struct outstanding *out; - ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_return_val_if_fail(c->private, CA_ERROR_STATE); + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(c->private, CA_ERROR_STATE); - p = PRIVATE(c); + p = PRIVATE(c); - if (p->outstanding_mutex) { - ca_mutex_lock(p->outstanding_mutex); + if (p->outstanding_mutex) { + ca_mutex_lock(p->outstanding_mutex); - /* Tell all player threads to terminate */ - for (out = p->outstanding; out; out = out->next) { + /* Tell all player threads to terminate */ + for (out = p->outstanding; out; out = out->next) { - if (out->dead) - continue; + if (out->dead) + continue; - out->dead = TRUE; + out->dead = TRUE; - if (out->callback) - out->callback(c, out->id, CA_ERROR_DESTROYED, out->userdata); + if (out->callback) + out->callback(c, out->id, CA_ERROR_DESTROYED, out->userdata); - /* This will cause the thread to wakeup and terminate */ - if (out->pipe_fd[1] >= 0) { - close(out->pipe_fd[1]); - out->pipe_fd[1] = -1; - } - } + /* This will cause the thread to wakeup and terminate */ + if (out->pipe_fd[1] >= 0) { + close(out->pipe_fd[1]); + out->pipe_fd[1] = -1; + } + } + + if (p->semaphore_allocated) { + /* Now wait until all players are destroyed */ + p->signal_semaphore = TRUE; + while (p->outstanding) { + ca_mutex_unlock(p->outstanding_mutex); + sem_wait(&p->semaphore); + ca_mutex_lock(p->outstanding_mutex); + } + } - if (p->semaphore_allocated) { - /* Now wait until all players are destroyed */ - p->signal_semaphore = TRUE; - while (p->outstanding) { ca_mutex_unlock(p->outstanding_mutex); - sem_wait(&p->semaphore); - ca_mutex_lock(p->outstanding_mutex); - } + ca_mutex_free(p->outstanding_mutex); } - ca_mutex_unlock(p->outstanding_mutex); - ca_mutex_free(p->outstanding_mutex); - } - - if (p->theme) - ca_theme_data_free(p->theme); + if (p->theme) + ca_theme_data_free(p->theme); - if (p->semaphore_allocated) - sem_destroy(&p->semaphore); + if (p->semaphore_allocated) + sem_destroy(&p->semaphore); - ca_free(p); + ca_free(p); - c->private = NULL; + c->private = NULL; - return CA_SUCCESS; + return CA_SUCCESS; } int driver_change_device(ca_context *c, const char *device) { - ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_return_val_if_fail(c->private, CA_ERROR_STATE); + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(c->private, CA_ERROR_STATE); - return CA_SUCCESS; + return CA_SUCCESS; } int driver_change_props(ca_context *c, ca_proplist *changed, ca_proplist *merged) { - ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_return_val_if_fail(changed, CA_ERROR_INVALID); - ca_return_val_if_fail(merged, CA_ERROR_INVALID); + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(changed, CA_ERROR_INVALID); + ca_return_val_if_fail(merged, CA_ERROR_INVALID); - return CA_SUCCESS; + return CA_SUCCESS; } int driver_cache(ca_context *c, ca_proplist *proplist) { - ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_return_val_if_fail(proplist, CA_ERROR_INVALID); + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(proplist, CA_ERROR_INVALID); - return CA_ERROR_NOTSUPPORTED; + return CA_ERROR_NOTSUPPORTED; } static int translate_error(int error) { - switch (error) { + switch (error) { case ENODEV: case ENOENT: - return CA_ERROR_NOTFOUND; + return CA_ERROR_NOTFOUND; case EACCES: case EPERM: - return CA_ERROR_ACCESS; + return CA_ERROR_ACCESS; case ENOMEM: - return CA_ERROR_OOM; + return CA_ERROR_OOM; case EBUSY: - return CA_ERROR_NOTAVAILABLE; + return CA_ERROR_NOTAVAILABLE; case EINVAL: - return CA_ERROR_INVALID; + return CA_ERROR_INVALID; case ENOSYS: - return CA_ERROR_NOTSUPPORTED; + return CA_ERROR_NOTSUPPORTED; default: - if (ca_debug()) - fprintf(stderr, "Got unhandled error from OSS: %s\n", strerror(error)); - return CA_ERROR_IO; - } + if (ca_debug()) + fprintf(stderr, "Got unhandled error from OSS: %s\n", strerror(error)); + return CA_ERROR_IO; + } } static int open_oss(ca_context *c, struct outstanding *out) { - struct private *p; - int mode, val, test, ret; + struct private *p; + int mode, val, test, ret; - ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_return_val_if_fail(c->private, CA_ERROR_STATE); - ca_return_val_if_fail(out, CA_ERROR_INVALID); + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(c->private, CA_ERROR_STATE); + ca_return_val_if_fail(out, CA_ERROR_INVALID); - /* In OSS we have no way to configure a channel mapping for - * multichannel streams. We cannot support those files hence */ - ca_return_val_if_fail(ca_sound_file_get_nchannels(out->file) <= 2, CA_ERROR_NOTSUPPORTED); + /* In OSS we have no way to configure a channel mapping for + * multichannel streams. We cannot support those files hence */ + ca_return_val_if_fail(ca_sound_file_get_nchannels(out->file) <= 2, CA_ERROR_NOTSUPPORTED); - p = PRIVATE(c); + p = PRIVATE(c); - if ((out->pcm = open(c->device ? c->device : "/dev/dsp", O_WRONLY | O_NONBLOCK, 0)) < 0) - goto finish_errno; + if ((out->pcm = open(c->device ? c->device : "/dev/dsp", O_WRONLY | O_NONBLOCK, 0)) < 0) + goto finish_errno; - if ((mode = fcntl(out->pcm, F_GETFL)) < 0) - goto finish_errno; + if ((mode = fcntl(out->pcm, F_GETFL)) < 0) + goto finish_errno; - mode &= ~O_NONBLOCK; + mode &= ~O_NONBLOCK; - if (fcntl(out->pcm, F_SETFL, mode) < 0) - goto finish_errno; + if (fcntl(out->pcm, F_SETFL, mode) < 0) + goto finish_errno; - switch (ca_sound_file_get_sample_type(out->file)) { + switch (ca_sound_file_get_sample_type(out->file)) { case CA_SAMPLE_U8: - val = AFMT_U8; - break; + val = AFMT_U8; + break; case CA_SAMPLE_S16NE: - val = AFMT_S16_NE; - break; + val = AFMT_S16_NE; + break; case CA_SAMPLE_S16RE: #if __BYTE_ORDER == __LITTLE_ENDIAN - val = AFMT_S16_BE; + val = AFMT_S16_BE; #else - val = AFMT_S16_LE; + val = AFMT_S16_LE; #endif - break; - } + break; + } - test = val; - if (ioctl(out->pcm, SNDCTL_DSP_SETFMT, &val) < 0) - goto finish_errno; + test = val; + if (ioctl(out->pcm, SNDCTL_DSP_SETFMT, &val) < 0) + goto finish_errno; - if (val != test) { - ret = CA_ERROR_NOTSUPPORTED; - goto finish_ret; - } + if (val != test) { + ret = CA_ERROR_NOTSUPPORTED; + goto finish_ret; + } - test = val = (int) ca_sound_file_get_nchannels(out->file); - if (ioctl(out->pcm, SNDCTL_DSP_CHANNELS, &val) < 0) - goto finish_errno; + test = val = (int) ca_sound_file_get_nchannels(out->file); + if (ioctl(out->pcm, SNDCTL_DSP_CHANNELS, &val) < 0) + goto finish_errno; - if (val != test) { - ret = CA_ERROR_NOTSUPPORTED; - goto finish_ret; - } + if (val != test) { + ret = CA_ERROR_NOTSUPPORTED; + goto finish_ret; + } - test = val = (int) ca_sound_file_get_rate(out->file); - if (ioctl(out->pcm, SNDCTL_DSP_SPEED, &val) < 0) - goto finish_errno; + test = val = (int) ca_sound_file_get_rate(out->file); + if (ioctl(out->pcm, SNDCTL_DSP_SPEED, &val) < 0) + goto finish_errno; - /* Check to make sure the configured rate is close enough to the - * requested rate. */ - if (fabs((double) (val - test)) > test * 0.05) { - ret = CA_ERROR_NOTSUPPORTED; - goto finish_ret; - } + /* Check to make sure the configured rate is close enough to the + * requested rate. */ + if (fabs((double) (val - test)) > test * 0.05) { + ret = CA_ERROR_NOTSUPPORTED; + goto finish_ret; + } - return CA_SUCCESS; + return CA_SUCCESS; finish_errno: - return translate_error(errno); + return translate_error(errno); finish_ret: - return ret; + return ret; } #define BUFSIZE (4*1024) static void* thread_func(void *userdata) { - struct outstanding *out = userdata; - int ret; - void *data, *d = NULL; - size_t fs, data_size; - size_t nbytes = 0; - struct pollfd pfd[2]; - nfds_t n_pfd = 2; - struct private *p; - - p = PRIVATE(out->context); - - pthread_detach(pthread_self()); - - fs = ca_sound_file_frame_size(out->file); - data_size = (BUFSIZE/fs)*fs; - - if (!(data = ca_malloc(data_size))) { - ret = CA_ERROR_OOM; - goto finish; - } - - pfd[0].fd = out->pipe_fd[0]; - pfd[0].events = POLLIN; - pfd[0].revents = 0; - pfd[1].fd = out->pcm; - pfd[1].events = POLLOUT; - pfd[1].revents = 0; - - for (;;) { - ssize_t bytes_written; - - if (out->dead) - break; - - if (poll(pfd, n_pfd, -1) < 0) { - ret = CA_ERROR_SYSTEM; - goto finish; - } + struct outstanding *out = userdata; + int ret; + void *data, *d = NULL; + size_t fs, data_size; + size_t nbytes = 0; + struct pollfd pfd[2]; + nfds_t n_pfd = 2; + struct private *p; - /* We have been asked to shut down */ - if (pfd[0].revents) - break; + p = PRIVATE(out->context); - if (pfd[1].revents != POLLOUT) { - ret = CA_ERROR_IO; - goto finish; - } + pthread_detach(pthread_self()); - if (nbytes <= 0) { - nbytes = data_size; + fs = ca_sound_file_frame_size(out->file); + data_size = (BUFSIZE/fs)*fs; - if ((ret = ca_sound_file_read_arbitrary(out->file, data, &nbytes)) < 0) + if (!(data = ca_malloc(data_size))) { + ret = CA_ERROR_OOM; goto finish; - - d = data; } - if (nbytes <= 0) - break; + pfd[0].fd = out->pipe_fd[0]; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + pfd[1].fd = out->pcm; + pfd[1].events = POLLOUT; + pfd[1].revents = 0; - if ((bytes_written = write(out->pcm, d, nbytes)) <= 0) { - ret = translate_error(errno); - goto finish; - } + for (;;) { + ssize_t bytes_written; + + if (out->dead) + break; + + if (poll(pfd, n_pfd, -1) < 0) { + ret = CA_ERROR_SYSTEM; + goto finish; + } + + /* We have been asked to shut down */ + if (pfd[0].revents) + break; + + if (pfd[1].revents != POLLOUT) { + ret = CA_ERROR_IO; + goto finish; + } + + if (nbytes <= 0) { + nbytes = data_size; + + if ((ret = ca_sound_file_read_arbitrary(out->file, data, &nbytes)) < 0) + goto finish; + + d = data; + } + + if (nbytes <= 0) + break; - nbytes -= (size_t) bytes_written; - d = (uint8_t*) d + (size_t) bytes_written; - } + if ((bytes_written = write(out->pcm, d, nbytes)) <= 0) { + ret = translate_error(errno); + goto finish; + } - ret = CA_SUCCESS; + nbytes -= (size_t) bytes_written; + d = (uint8_t*) d + (size_t) bytes_written; + } + + ret = CA_SUCCESS; finish: - ca_free(data); + ca_free(data); - if (!out->dead) - if (out->callback) - out->callback(out->context, out->id, ret, out->userdata); + if (!out->dead) + if (out->callback) + out->callback(out->context, out->id, ret, out->userdata); - ca_mutex_lock(p->outstanding_mutex); + ca_mutex_lock(p->outstanding_mutex); - CA_LLIST_REMOVE(struct outstanding, p->outstanding, out); + CA_LLIST_REMOVE(struct outstanding, p->outstanding, out); - if (!p->outstanding && p->signal_semaphore) - sem_post(&p->semaphore); + if (!p->outstanding && p->signal_semaphore) + sem_post(&p->semaphore); - outstanding_free(out); + outstanding_free(out); - ca_mutex_unlock(p->outstanding_mutex); + ca_mutex_unlock(p->outstanding_mutex); - return NULL; + return NULL; } int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_callback_t cb, void *userdata) { - struct private *p; - struct outstanding *out = NULL; - int ret; - pthread_t thread; - - ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_return_val_if_fail(proplist, CA_ERROR_INVALID); - ca_return_val_if_fail(!userdata || cb, CA_ERROR_INVALID); - ca_return_val_if_fail(c->private, CA_ERROR_STATE); - - p = PRIVATE(c); + struct private *p; + struct outstanding *out = NULL; + int ret; + pthread_t thread; - if (!(out = ca_new0(struct outstanding, 1))) { - ret = CA_ERROR_OOM; - goto finish; - } + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(proplist, CA_ERROR_INVALID); + ca_return_val_if_fail(!userdata || cb, CA_ERROR_INVALID); + ca_return_val_if_fail(c->private, CA_ERROR_STATE); - out->context = c; - out->id = id; - out->callback = cb; - out->userdata = userdata; - out->pipe_fd[0] = out->pipe_fd[1] = -1; - out->pcm = -1; + p = PRIVATE(c); - if (pipe(out->pipe_fd) < 0) { - ret = CA_ERROR_SYSTEM; - goto finish; - } + if (!(out = ca_new0(struct outstanding, 1))) { + ret = CA_ERROR_OOM; + goto finish; + } - if ((ret = ca_lookup_sound(&out->file, NULL, &p->theme, c->props, proplist)) < 0) - goto finish; + out->context = c; + out->id = id; + out->callback = cb; + out->userdata = userdata; + out->pipe_fd[0] = out->pipe_fd[1] = -1; + out->pcm = -1; - if ((ret = open_oss(c, out)) < 0) - goto finish; + if (pipe(out->pipe_fd) < 0) { + ret = CA_ERROR_SYSTEM; + goto finish; + } - /* OK, we're ready to go, so let's add this to our list */ - ca_mutex_lock(p->outstanding_mutex); - CA_LLIST_PREPEND(struct outstanding, p->outstanding, out); - ca_mutex_unlock(p->outstanding_mutex); + if ((ret = ca_lookup_sound(&out->file, NULL, &p->theme, c->props, proplist)) < 0) + goto finish; - if (pthread_create(&thread, NULL, thread_func, out) < 0) { - ret = CA_ERROR_OOM; + if ((ret = open_oss(c, out)) < 0) + goto finish; + /* OK, we're ready to go, so let's add this to our list */ ca_mutex_lock(p->outstanding_mutex); - CA_LLIST_REMOVE(struct outstanding, p->outstanding, out); + CA_LLIST_PREPEND(struct outstanding, p->outstanding, out); ca_mutex_unlock(p->outstanding_mutex); - goto finish; - } + if (pthread_create(&thread, NULL, thread_func, out) < 0) { + ret = CA_ERROR_OOM; - ret = CA_SUCCESS; + ca_mutex_lock(p->outstanding_mutex); + CA_LLIST_REMOVE(struct outstanding, p->outstanding, out); + ca_mutex_unlock(p->outstanding_mutex); + + goto finish; + } + + ret = CA_SUCCESS; finish: - /* We keep the outstanding struct around if we need clean up later to */ - if (ret != CA_SUCCESS) - outstanding_free(out); + /* We keep the outstanding struct around if we need clean up later to */ + if (ret != CA_SUCCESS) + outstanding_free(out); - return ret; + return ret; } int driver_cancel(ca_context *c, uint32_t id) { - struct private *p; - struct outstanding *out; + struct private *p; + struct outstanding *out; - ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_return_val_if_fail(c->private, CA_ERROR_STATE); + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(c->private, CA_ERROR_STATE); - p = PRIVATE(c); + p = PRIVATE(c); - ca_mutex_lock(p->outstanding_mutex); + ca_mutex_lock(p->outstanding_mutex); - for (out = p->outstanding; out; out = out->next) { + for (out = p->outstanding; out; out = out->next) { - if (out->id != id) - continue; + if (out->id != id) + continue; - if (out->dead) - continue; + if (out->dead) + continue; - out->dead = TRUE; + out->dead = TRUE; - if (out->callback) - out->callback(c, out->id, CA_ERROR_CANCELED, out->userdata); + if (out->callback) + out->callback(c, out->id, CA_ERROR_CANCELED, out->userdata); - /* This will cause the thread to wakeup and terminate */ - if (out->pipe_fd[1] >= 0) { - close(out->pipe_fd[1]); - out->pipe_fd[1] = -1; + /* This will cause the thread to wakeup and terminate */ + if (out->pipe_fd[1] >= 0) { + close(out->pipe_fd[1]); + out->pipe_fd[1] = -1; + } } - } - ca_mutex_unlock(p->outstanding_mutex); + ca_mutex_unlock(p->outstanding_mutex); - return CA_SUCCESS; + return CA_SUCCESS; } int driver_playing(ca_context *c, uint32_t id, int *playing) { - struct private *p; - struct outstanding *out; + struct private *p; + struct outstanding *out; - ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_return_val_if_fail(c->private, CA_ERROR_STATE); - ca_return_val_if_fail(playing, CA_ERROR_INVALID); + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(c->private, CA_ERROR_STATE); + ca_return_val_if_fail(playing, CA_ERROR_INVALID); - p = PRIVATE(c); + p = PRIVATE(c); - *playing = 0; + *playing = 0; - ca_mutex_lock(p->outstanding_mutex); + ca_mutex_lock(p->outstanding_mutex); - for (out = p->outstanding; out; out = out->next) { + for (out = p->outstanding; out; out = out->next) { - if (out->dead || - out->id != id) - continue; + if (out->dead || + out->id != id) + continue; - *playing = 1; - break; - } + *playing = 1; + break; + } - ca_mutex_unlock(p->outstanding_mutex); + ca_mutex_unlock(p->outstanding_mutex); - return CA_SUCCESS; + return CA_SUCCESS; } |