Reorganised the source tree. We now have src/ with a couple of subdirs:

* daemon/ - Contains the files specific to the polypaudio daemon.
 * modules/ - All loadable modules.
 * polyp/ - Files that are part of the public, application interface or
   are only used in libpolyp.
 * polypcore/ - All other shared files.
 * tests/ - Test programs.
 * utils/ - Utility programs.


git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@487 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
Pierre Ossman 2006-02-16 19:19:58 +00:00
parent 5b881e6228
commit e205b25d65
246 changed files with 724 additions and 689 deletions

124
src/polypcore/alsa-util.c Normal file
View file

@ -0,0 +1,124 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/types.h>
#include <asoundlib.h>
#include "alsa-util.h"
#include <polyp/sample.h>
#include "xmalloc.h"
#include "log.h"
/* Set the hardware parameters of the given ALSA device. Returns the
* selected fragment settings in *period and *period_size */
int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, const pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size) {
int ret = -1;
snd_pcm_uframes_t buffer_size;
snd_pcm_hw_params_t *hwparams = NULL;
static const snd_pcm_format_t format_trans[] = {
[PA_SAMPLE_U8] = SND_PCM_FORMAT_U8,
[PA_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW,
[PA_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW,
[PA_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE,
[PA_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE,
[PA_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE,
[PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
};
assert(pcm_handle && ss && periods && period_size);
if (snd_pcm_hw_params_malloc(&hwparams) < 0 ||
snd_pcm_hw_params_any(pcm_handle, hwparams) < 0 ||
snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 ||
snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[ss->format]) < 0 ||
snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &ss->rate, NULL) < 0 ||
snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss->channels) < 0 ||
(*periods > 0 && snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, periods, NULL) < 0) ||
(*period_size > 0 && snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, period_size, NULL) < 0) ||
snd_pcm_hw_params(pcm_handle, hwparams) < 0)
goto finish;
if (snd_pcm_prepare(pcm_handle) < 0)
goto finish;
if (snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size) < 0 ||
snd_pcm_hw_params_get_period_size(hwparams, period_size, NULL) < 0)
goto finish;
assert(buffer_size > 0);
assert(*period_size > 0);
*periods = buffer_size / *period_size;
assert(*periods > 0);
ret = 0;
finish:
if (hwparams)
snd_pcm_hw_params_free(hwparams);
return ret;
}
/* Allocate an IO event for every ALSA poll descriptor for the
* specified ALSA device. Return a pointer to such an array in
* *io_events. Store the length of that array in *n_io_events. Use the
* specified callback function and userdata. The array has to be freed
* with pa_free_io_events(). */
int pa_create_io_events(snd_pcm_t *pcm_handle, pa_mainloop_api* m, pa_io_event ***io_events, unsigned *n_io_events, void (*cb)(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata), void *userdata) {
unsigned i;
struct pollfd *pfds, *ppfd;
pa_io_event **ios;
assert(pcm_handle && m && io_events && n_io_events && cb);
*n_io_events = snd_pcm_poll_descriptors_count(pcm_handle);
pfds = pa_xmalloc(sizeof(struct pollfd) * *n_io_events);
if (snd_pcm_poll_descriptors(pcm_handle, pfds, *n_io_events) < 0) {
pa_xfree(pfds);
return -1;
}
*io_events = pa_xmalloc(sizeof(void*) * *n_io_events);
for (i = 0, ios = *io_events, ppfd = pfds; i < *n_io_events; i++, ios++, ppfd++) {
*ios = m->io_new(m, ppfd->fd,
((ppfd->events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
((ppfd->events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0), cb, userdata);
assert(*ios);
}
pa_xfree(pfds);
return 0;
}
/* Free the memory allocated by pa_create_io_events() */
void pa_free_io_events(pa_mainloop_api* m, pa_io_event **io_events, unsigned n_io_events) {
unsigned i;
pa_io_event **ios;
assert(m && io_events);
for (ios = io_events, i = 0; i < n_io_events; i++, ios++)
m->io_free(*ios);
pa_xfree(io_events);
}

35
src/polypcore/alsa-util.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef fooalsautilhfoo
#define fooalsautilhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <asoundlib.h>
#include <polyp/sample.h>
#include <polyp/mainloop-api.h>
int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, const pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size);
int pa_create_io_events(snd_pcm_t *pcm_handle, pa_mainloop_api *m, pa_io_event ***io_events, unsigned *n_io_events, void (*cb)(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata), void *userdata);
void pa_free_io_events(pa_mainloop_api* m, pa_io_event **io_sources, unsigned n_io_sources);
#endif

View file

@ -0,0 +1,87 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <assert.h>
#include <string.h>
#include "xmalloc.h"
#include "authkey-prop.h"
#include "props.h"
#include "log.h"
struct authkey_data {
int ref;
size_t length;
};
int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len) {
struct authkey_data *a;
assert(c && name && data && len > 0);
if (!(a = pa_property_get(c, name)))
return -1;
assert(a->length == len);
memcpy(data, a+1, len);
return 0;
}
int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len) {
struct authkey_data *a;
assert(c && name);
if (pa_property_get(c, name))
return -1;
a = pa_xmalloc(sizeof(struct authkey_data) + len);
a->ref = 1;
a->length = len;
memcpy(a+1, data, len);
pa_property_set(c, name, a);
return 0;
}
void pa_authkey_prop_ref(pa_core *c, const char *name) {
struct authkey_data *a;
assert(c && name);
a = pa_property_get(c, name);
assert(a && a->ref >= 1);
a->ref++;
}
void pa_authkey_prop_unref(pa_core *c, const char *name) {
struct authkey_data *a;
assert(c && name);
a = pa_property_get(c, name);
assert(a && a->ref >= 1);
if (!(--a->ref)) {
pa_property_remove(c, name);
pa_xfree(a);
}
}

View file

@ -0,0 +1,43 @@
#ifndef fooauthkeyprophfoo
#define fooauthkeyprophfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "core.h"
/* The authkey-prop uses a central property to store a previously
* loaded cookie in memory. Useful for sharing the same cookie between
* several modules. */
/* Return the data of the specified authorization key property. Doesn't alter the refernce count of the key */
int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len);
/* Store data in the specified authorization key property. The initial reference count is set to 1 */
int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len);
/* Increase the reference count of the specified authorization key */
void pa_authkey_prop_ref(pa_core *c, const char *name);
/* Decrease the reference count of the specified authorization key */
void pa_authkey_prop_unref(pa_core *c, const char *name);
#endif

201
src/polypcore/authkey.c Normal file
View file

@ -0,0 +1,201 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <inttypes.h>
#include <stdlib.h>
#include <time.h>
#include <limits.h>
#include <sys/stat.h>
#include "authkey.h"
#include "util.h"
#include "log.h"
#include "random.h"
/* Generate a new authorization key, store it in file fd and return it in *data */
static int generate(int fd, void *ret_data, size_t length) {
ssize_t r;
assert(fd >= 0 && ret_data && length);
pa_random(ret_data, length);
lseek(fd, 0, SEEK_SET);
ftruncate(fd, 0);
if ((r = pa_loop_write(fd, ret_data, length)) < 0 || (size_t) r != length) {
pa_log(__FILE__": failed to write cookie file: %s\n", strerror(errno));
return -1;
}
return 0;
}
/* Load an euthorization cookie from file fn and store it in data. If
* the cookie file doesn't exist, create it */
static int load(const char *fn, void *data, size_t length) {
int fd = -1;
int writable = 1;
int unlock = 0, ret = -1;
ssize_t r;
assert(fn && data && length);
if ((fd = open(fn, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
if (errno != EACCES || (fd = open(fn, O_RDONLY)) < 0) {
pa_log(__FILE__": failed to open cookie file '%s': %s\n", fn, strerror(errno));
goto finish;
} else
writable = 0;
}
unlock = pa_lock_fd(fd, 1) >= 0;
if ((r = pa_loop_read(fd, data, length)) < 0) {
pa_log(__FILE__": failed to read cookie file '%s': %s\n", fn, strerror(errno));
goto finish;
}
if ((size_t) r != length) {
if (!writable) {
pa_log(__FILE__": unable to write cookie to read only file\n");
goto finish;
}
if (generate(fd, data, length) < 0)
goto finish;
}
ret = 0;
finish:
if (fd >= 0) {
if (unlock)
pa_lock_fd(fd, 0);
close(fd);
}
return ret;
}
/* Load a cookie from a cookie file. If the file doesn't exist, create it. */
int pa_authkey_load(const char *path, void *data, size_t length) {
int ret;
assert(path && data && length);
ret = load(path, data, length);
if (ret < 0)
pa_log(__FILE__": Failed to load authorization key '%s': %s\n", path,
(ret == -1) ? strerror(errno) : "file corrupt");
return ret;
}
/* If the specified file path starts with / return it, otherwise
* return path prepended with home directory */
static const char *normalize_path(const char *fn, char *s, size_t l) {
assert(fn && s && l > 0);
#ifndef OS_IS_WIN32
if (fn[0] != '/') {
#else
if (strlen(fn) < 3 || !isalpha(fn[0]) || fn[1] != ':' || fn[2] != '\\') {
#endif
char homedir[PATH_MAX];
if (!pa_get_home_dir(homedir, sizeof(homedir)))
return NULL;
#ifndef OS_IS_WIN32
snprintf(s, l, "%s/%s", homedir, fn);
#else
snprintf(s, l, "%s\\%s", homedir, fn);
#endif
return s;
}
return fn;
}
/* Load a cookie from a file in the home directory. If the specified
* path starts with /, use it as absolute path instead. */
int pa_authkey_load_auto(const char *fn, void *data, size_t length) {
char path[PATH_MAX];
const char *p;
assert(fn && data && length);
if (!(p = normalize_path(fn, path, sizeof(path))))
return -2;
return pa_authkey_load(p, data, length);
}
/* Store the specified cookie in the speicified cookie file */
int pa_authkey_save(const char *fn, const void *data, size_t length) {
int fd = -1;
int unlock = 0, ret = -1;
ssize_t r;
char path[PATH_MAX];
const char *p;
assert(fn && data && length);
if (!(p = normalize_path(fn, path, sizeof(path))))
return -2;
if ((fd = open(p, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
pa_log(__FILE__": failed to open cookie file '%s': %s\n", fn, strerror(errno));
goto finish;
}
unlock = pa_lock_fd(fd, 1) >= 0;
if ((r = pa_loop_write(fd, data, length)) < 0 || (size_t) r != length) {
pa_log(__FILE__": failed to read cookie file '%s': %s\n", fn, strerror(errno));
goto finish;
}
ret = 0;
finish:
if (fd >= 0) {
if (unlock)
pa_lock_fd(fd, 0);
close(fd);
}
return ret;
}

32
src/polypcore/authkey.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef fooauthkeyhfoo
#define fooauthkeyhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <sys/types.h>
int pa_authkey_load(const char *path, void *data, size_t len);
int pa_authkey_load_auto(const char *fn, void *data, size_t length);
int pa_authkey_save(const char *path, const void *data, size_t length);
#endif

180
src/polypcore/autoload.c Normal file
View file

@ -0,0 +1,180 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "autoload.h"
#include "module.h"
#include "xmalloc.h"
#include "memchunk.h"
#include "sound-file.h"
#include "log.h"
#include "scache.h"
#include "subscribe.h"
static void entry_free(pa_autoload_entry *e) {
assert(e);
pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_REMOVE, PA_INVALID_INDEX);
pa_xfree(e->name);
pa_xfree(e->module);
pa_xfree(e->argument);
pa_xfree(e);
}
static void entry_remove_and_free(pa_autoload_entry *e) {
assert(e && e->core);
pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL);
pa_hashmap_remove(e->core->autoload_hashmap, e->name);
entry_free(e);
}
static pa_autoload_entry* entry_new(pa_core *c, const char *name) {
pa_autoload_entry *e = NULL;
assert(c && name);
if (c->autoload_hashmap && (e = pa_hashmap_get(c->autoload_hashmap, name)))
return NULL;
e = pa_xmalloc(sizeof(pa_autoload_entry));
e->core = c;
e->name = pa_xstrdup(name);
e->module = e->argument = NULL;
e->in_action = 0;
if (!c->autoload_hashmap)
c->autoload_hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
assert(c->autoload_hashmap);
pa_hashmap_put(c->autoload_hashmap, e->name, e);
if (!c->autoload_idxset)
c->autoload_idxset = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
pa_idxset_put(c->autoload_idxset, e, &e->index);
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_NEW, e->index);
return e;
}
int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx) {
pa_autoload_entry *e = NULL;
assert(c && name && module && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE));
if (!(e = entry_new(c, name)))
return -1;
e->module = pa_xstrdup(module);
e->argument = pa_xstrdup(argument);
e->type = type;
if (idx)
*idx = e->index;
return 0;
}
int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {
pa_autoload_entry *e;
assert(c && name && type);
if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)
return -1;
entry_remove_and_free(e);
return 0;
}
int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) {
pa_autoload_entry *e;
assert(c && idx != PA_IDXSET_INVALID);
if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))
return -1;
entry_remove_and_free(e);
return 0;
}
void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type) {
pa_autoload_entry *e;
pa_module *m;
assert(c && name);
if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || (e->type != type))
return;
if (e->in_action)
return;
e->in_action = 1;
if (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE) {
if ((m = pa_module_load(c, e->module, e->argument)))
m->auto_unload = 1;
}
e->in_action = 0;
}
static void free_func(void *p, PA_GCC_UNUSED void *userdata) {
pa_autoload_entry *e = p;
pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL);
entry_free(e);
}
void pa_autoload_free(pa_core *c) {
if (c->autoload_hashmap) {
pa_hashmap_free(c->autoload_hashmap, free_func, NULL);
c->autoload_hashmap = NULL;
}
if (c->autoload_idxset) {
pa_idxset_free(c->autoload_idxset, NULL, NULL);
c->autoload_idxset = NULL;
}
}
const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {
pa_autoload_entry *e;
assert(c && name);
if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)
return NULL;
return e;
}
const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx) {
pa_autoload_entry *e;
assert(c && idx != PA_IDXSET_INVALID);
if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))
return NULL;
return e;
}

58
src/polypcore/autoload.h Normal file
View file

@ -0,0 +1,58 @@
#ifndef fooautoloadhfoo
#define fooautoloadhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "namereg.h"
/* Using the autoloading facility, modules by be loaded on-demand and
* synchronously. The user may register a "ghost sink" or "ghost
* source". Whenever this sink/source is requested but not available a
* specified module is loaded. */
/* An autoload entry, or "ghost" sink/source */
typedef struct pa_autoload_entry {
pa_core *core;
uint32_t index;
char *name;
pa_namereg_type_t type; /* Type of the autoload entry */
int in_action; /* Currently loaded */
char *module, *argument;
} pa_autoload_entry;
/* Add a new autoload entry of the given time, with the speicified
* sink/source name, module name and argument. Return the entry's
* index in *index */
int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx);
/* Free all autoload entries */
void pa_autoload_free(pa_core *c);
int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type);
int pa_autoload_remove_by_index(pa_core *c, uint32_t idx);
/* Request an autoload entry by its name, effectively causing a module to be loaded */
void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type);
const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type);
const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx);
#endif

834
src/polypcore/cli-command.c Normal file
View file

@ -0,0 +1,834 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include "cli-command.h"
#include "module.h"
#include "sink.h"
#include "source.h"
#include "client.h"
#include "sink-input.h"
#include "source-output.h"
#include "tokenizer.h"
#include "strbuf.h"
#include "namereg.h"
#include "cli-text.h"
#include "scache.h"
#include "sample-util.h"
#include "sound-file.h"
#include "play-memchunk.h"
#include "autoload.h"
#include "xmalloc.h"
#include "sound-file-stream.h"
#include "props.h"
#include "util.h"
struct command {
const char *name;
int (*proc) (pa_core *c, pa_tokenizer*t, pa_strbuf *buf, int *fail);
const char *help;
unsigned args;
};
#define INCLUDE_META ".include"
#define FAIL_META ".fail"
#define NOFAIL_META ".nofail"
/* Prototypes for all available commands */
static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
/* A method table for all available commands */
static const struct command commands[] = {
{ "exit", pa_cli_command_exit, "Terminate the daemon", 1 },
{ "help", pa_cli_command_help, "Show this help", 1 },
{ "list-modules", pa_cli_command_modules, "List loaded modules", 1 },
{ "list-sinks", pa_cli_command_sinks, "List loaded sinks", 1 },
{ "list-sources", pa_cli_command_sources, "List loaded sources", 1 },
{ "list-clients", pa_cli_command_clients, "List loaded clients", 1 },
{ "list-sink-inputs", pa_cli_command_sink_inputs, "List sink inputs", 1 },
{ "list-source-outputs", pa_cli_command_source_outputs, "List source outputs", 1 },
{ "stat", pa_cli_command_stat, "Show memory block statistics", 1 },
{ "info", pa_cli_command_info, "Show comprehensive status", 1 },
{ "ls", pa_cli_command_info, NULL, 1 },
{ "list", pa_cli_command_info, NULL, 1 },
{ "load-module", pa_cli_command_load, "Load a module (args: name, arguments)", 3},
{ "unload-module", pa_cli_command_unload, "Unload a module (args: index)", 2},
{ "set-sink-volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3},
{ "set-sink-input-volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index|name, volume)", 3},
{ "set-default-sink", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2},
{ "set-default-source", pa_cli_command_source_default, "Set the default source (args: index|name)", 2},
{ "kill-client", pa_cli_command_kill_client, "Kill a client (args: index)", 2},
{ "kill-sink-input", pa_cli_command_kill_sink_input, "Kill a sink input (args: index)", 2},
{ "kill-source-output", pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2},
{ "list-samples", pa_cli_command_scache_list, "List all entries in the sample cache", 1},
{ "play-sample", pa_cli_command_scache_play, "Play a sample from the sample cache (args: name, sink|index)", 3},
{ "remove-sample", pa_cli_command_scache_remove, "Remove a sample from the sample cache (args: name)", 2},
{ "load-sample", pa_cli_command_scache_load, "Load a sound file into the sample cache (args: name, filename)", 3},
{ "load-sample-lazy", pa_cli_command_scache_load, "Lazily load a sound file into the sample cache (args: name, filename)", 3},
{ "load-sample-dir-lazy", pa_cli_command_scache_load_dir, "Lazily load all files in a directory into the sample cache (args: pathname)", 2},
{ "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3},
{ "list-autoload", pa_cli_command_autoload_list, "List autoload entries", 1},
{ "add-autoload-sink", pa_cli_command_autoload_add, "Add autoload entry for a sink (args: sink, module name, arguments)", 4},
{ "add-autoload-source", pa_cli_command_autoload_add, "Add autoload entry for a source (args: source, module name, arguments)", 4},
{ "remove-autoload-sink", pa_cli_command_autoload_remove, "Remove autoload entry for a sink (args: name)", 2},
{ "remove-autoload-source", pa_cli_command_autoload_remove, "Remove autoload entry for a source (args: name)", 2},
{ "dump", pa_cli_command_dump, "Dump daemon configuration", 1},
{ "list-props", pa_cli_command_list_props, NULL, 1},
{ NULL, NULL, NULL, 0 }
};
static const char whitespace[] = " \t\n\r";
static const char linebreak[] = "\n\r";
static uint32_t parse_index(const char *n) {
uint32_t idx;
if (pa_atou(n, &idx) < 0)
return (uint32_t) PA_IDXSET_INVALID;
return idx;
}
static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, PA_GCC_UNUSED pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
assert(c && c->mainloop && t);
c->mainloop->quit(c->mainloop, 0);
return 0;
}
static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const struct command*command;
assert(c && t && buf);
pa_strbuf_puts(buf, "Available commands:\n");
for (command = commands; command->name; command++)
if (command->help)
pa_strbuf_printf(buf, " %-25s %s\n", command->name, command->help);
return 0;
}
static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char *s;
assert(c && t);
s = pa_module_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char *s;
assert(c && t);
s = pa_client_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char *s;
assert(c && t);
s = pa_sink_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char *s;
assert(c && t);
s = pa_source_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char *s;
assert(c && t);
s = pa_sink_input_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char *s;
assert(c && t);
s = pa_source_output_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char s[256];
assert(c && t);
pa_bytes_snprint(s, sizeof(s), c->memblock_stat->total_size);
pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n",
c->memblock_stat->total,
s);
pa_bytes_snprint(s, sizeof(s), c->memblock_stat->allocated_size);
pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n",
c->memblock_stat->allocated,
s);
pa_bytes_snprint(s, sizeof(s), pa_scache_total_size(c));
pa_strbuf_printf(buf, "Total sample cache size: %s.\n", s);
pa_sample_spec_snprint(s, sizeof(s), &c->default_sample_spec);
pa_strbuf_printf(buf, "Default sample spec: %s\n", s);
pa_strbuf_printf(buf, "Default sink name: %s\n"
"Default source name: %s\n",
pa_namereg_get_default_sink_name(c),
pa_namereg_get_default_source_name(c));
return 0;
}
static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
assert(c && t);
pa_cli_command_stat(c, t, buf, fail);
pa_cli_command_modules(c, t, buf, fail);
pa_cli_command_sinks(c, t, buf, fail);
pa_cli_command_sources(c, t, buf, fail);
pa_cli_command_clients(c, t, buf, fail);
pa_cli_command_sink_inputs(c, t, buf, fail);
pa_cli_command_source_outputs(c, t, buf, fail);
pa_cli_command_scache_list(c, t, buf, fail);
pa_cli_command_autoload_list(c, t, buf, fail);
return 0;
}
static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
pa_module *m;
const char *name;
assert(c && t);
if (!(name = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify the module name and optionally arguments.\n");
return -1;
}
if (!(m = pa_module_load(c, name, pa_tokenizer_get(t, 2)))) {
pa_strbuf_puts(buf, "Module load failed.\n");
return -1;
}
return 0;
}
static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
pa_module *m;
uint32_t idx;
const char *i;
char *e;
assert(c && t);
if (!(i = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify the module index.\n");
return -1;
}
idx = (uint32_t) strtoul(i, &e, 10);
if (*e || !(m = pa_idxset_get_by_index(c->modules, idx))) {
pa_strbuf_puts(buf, "Invalid module index.\n");
return -1;
}
pa_module_unload_request(m);
return 0;
}
static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const char *n, *v;
pa_sink *sink;
uint32_t volume;
pa_cvolume cvolume;
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
return -1;
}
if (!(v = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
return -1;
}
if (pa_atou(v, &volume) < 0) {
pa_strbuf_puts(buf, "Failed to parse volume.\n");
return -1;
}
if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
pa_strbuf_puts(buf, "No sink found by this name or index.\n");
return -1;
}
pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &cvolume);
return 0;
}
static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const char *n, *v;
pa_sink_input *si;
pa_volume_t volume;
pa_cvolume cvolume;
uint32_t idx;
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
return -1;
}
if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
pa_strbuf_puts(buf, "Failed to parse index.\n");
return -1;
}
if (!(v = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
return -1;
}
if (pa_atou(v, &volume) < 0) {
pa_strbuf_puts(buf, "Failed to parse volume.\n");
return -1;
}
if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
pa_strbuf_puts(buf, "No sink input found with this index.\n");
return -1;
}
pa_cvolume_set(&cvolume, si->sample_spec.channels, volume);
pa_sink_input_set_volume(si, &cvolume);
return 0;
}
static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const char *n;
assert(c && t);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
return -1;
}
pa_namereg_set_default(c, n, PA_NAMEREG_SINK);
return 0;
}
static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const char *n;
assert(c && t);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
return -1;
}
pa_namereg_set_default(c, n, PA_NAMEREG_SOURCE);
return 0;
}
static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const char *n;
pa_client *client;
uint32_t idx;
assert(c && t);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a client by its index.\n");
return -1;
}
if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
pa_strbuf_puts(buf, "Failed to parse index.\n");
return -1;
}
if (!(client = pa_idxset_get_by_index(c->clients, idx))) {
pa_strbuf_puts(buf, "No client found by this index.\n");
return -1;
}
pa_client_kill(client);
return 0;
}
static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const char *n;
pa_sink_input *sink_input;
uint32_t idx;
assert(c && t);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
return -1;
}
if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
pa_strbuf_puts(buf, "Failed to parse index.\n");
return -1;
}
if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx))) {
pa_strbuf_puts(buf, "No sink input found by this index.\n");
return -1;
}
pa_sink_input_kill(sink_input);
return 0;
}
static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const char *n;
pa_source_output *source_output;
uint32_t idx;
assert(c && t);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
return -1;
}
if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
pa_strbuf_puts(buf, "Failed to parse index.\n");
return -1;
}
if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx))) {
pa_strbuf_puts(buf, "No source output found by this index.\n");
return -1;
}
pa_source_output_kill(source_output);
return 0;
}
static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char *s;
assert(c && t);
s = pa_scache_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *n, *sink_name;
pa_sink *sink;
assert(c && t && buf && fail);
if (!(n = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a sample name and a sink name.\n");
return -1;
}
if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
pa_strbuf_puts(buf, "No sink by that name.\n");
return -1;
}
if (pa_scache_play_item(c, n, sink, NULL) < 0) {
pa_strbuf_puts(buf, "Failed to play sample.\n");
return -1;
}
return 0;
}
static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *n;
assert(c && t && buf && fail);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sample name.\n");
return -1;
}
if (pa_scache_remove_item(c, n) < 0) {
pa_strbuf_puts(buf, "Failed to remove sample.\n");
return -1;
}
return 0;
}
static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *fname, *n;
int r;
assert(c && t && buf && fail);
if (!(fname = pa_tokenizer_get(t, 2)) || !(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a file name and a sample name.\n");
return -1;
}
if (strstr(pa_tokenizer_get(t, 0), "lazy"))
r = pa_scache_add_file_lazy(c, n, fname, NULL);
else
r = pa_scache_add_file(c, n, fname, NULL);
if (r < 0)
pa_strbuf_puts(buf, "Failed to load sound file.\n");
return 0;
}
static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *pname;
assert(c && t && buf && fail);
if (!(pname = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a path name.\n");
return -1;
}
if (pa_scache_add_directory_lazy(c, pname) < 0) {
pa_strbuf_puts(buf, "Failed to load directory.\n");
return -1;
}
return 0;
}
static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *fname, *sink_name;
pa_sink *sink;
assert(c && t && buf && fail);
if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a file name and a sink name.\n");
return -1;
}
if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
pa_strbuf_puts(buf, "No sink by that name.\n");
return -1;
}
return pa_play_file(sink, fname, NULL);
}
static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *a, *b;
assert(c && t && buf && fail);
if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n");
return -1;
}
pa_autoload_add(c, a, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, b, pa_tokenizer_get(t, 3), NULL);
return 0;
}
static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *name;
assert(c && t && buf && fail);
if (!(name = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a device name\n");
return -1;
}
if (pa_autoload_remove_by_name(c, name, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE) < 0) {
pa_strbuf_puts(buf, "Failed to remove autload entry\n");
return -1;
}
return 0;
}
static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char *s;
assert(c && t);
s = pa_autoload_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
assert(c && t);
pa_property_dump(c, buf);
return 0;
}
static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
pa_module *m;
pa_sink *s;
int nl;
const char *p;
uint32_t idx;
char txt[256];
time_t now;
void *i;
pa_autoload_entry *a;
assert(c && t);
time(&now);
#ifdef HAVE_CTIME_R
pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime_r(&now, txt));
#else
pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now));
#endif
for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
if (m->auto_unload)
continue;
pa_strbuf_printf(buf, "load-module %s", m->name);
if (m->argument)
pa_strbuf_printf(buf, " %s", m->argument);
pa_strbuf_puts(buf, "\n");
}
nl = 0;
for (s = pa_idxset_first(c->sinks, &idx); s; s = pa_idxset_next(c->sinks, &idx)) {
if (s->owner && s->owner->auto_unload)
continue;
if (!nl) {
pa_strbuf_puts(buf, "\n");
nl = 1;
}
pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", s->name, pa_cvolume_avg(pa_sink_get_volume(s, PA_MIXER_HARDWARE)));
}
if (c->autoload_hashmap) {
nl = 0;
i = NULL;
while ((a = pa_hashmap_iterate(c->autoload_hashmap, &i, NULL))) {
if (!nl) {
pa_strbuf_puts(buf, "\n");
nl = 1;
}
pa_strbuf_printf(buf, "add-autoload-%s %s %s", a->type == PA_NAMEREG_SINK ? "sink" : "source", a->name, a->module);
if (a->argument)
pa_strbuf_printf(buf, " %s", a->argument);
pa_strbuf_puts(buf, "\n");
}
}
nl = 0;
if ((p = pa_namereg_get_default_sink_name(c))) {
if (!nl) {
pa_strbuf_puts(buf, "\n");
nl = 1;
}
pa_strbuf_printf(buf, "set-default-sink %s\n", p);
}
if ((p = pa_namereg_get_default_source_name(c))) {
if (!nl) {
pa_strbuf_puts(buf, "\n");
nl = 1;
}
pa_strbuf_printf(buf, "set-default-source %s\n", p);
}
pa_strbuf_puts(buf, "\n### EOF\n");
return 0;
}
int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail) {
const char *cs;
cs = s+strspn(s, whitespace);
if (*cs == '#' || !*cs)
return 0;
else if (*cs == '.') {
if (!strcmp(cs, FAIL_META))
*fail = 1;
else if (!strcmp(cs, NOFAIL_META))
*fail = 0;
else {
size_t l;
l = strcspn(cs, whitespace);
if (l == sizeof(INCLUDE_META)-1 && !strncmp(cs, INCLUDE_META, l)) {
const char *filename = cs+l+strspn(cs+l, whitespace);
if (pa_cli_command_execute_file(c, filename, buf, fail) < 0)
if (*fail) return -1;
} else {
pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs);
if (*fail) return -1;
}
}
} else {
const struct command*command;
int unknown = 1;
size_t l;
l = strcspn(cs, whitespace);
for (command = commands; command->name; command++)
if (strlen(command->name) == l && !strncmp(cs, command->name, l)) {
int ret;
pa_tokenizer *t = pa_tokenizer_new(cs, command->args);
assert(t);
ret = command->proc(c, t, buf, fail);
pa_tokenizer_free(t);
unknown = 0;
if (ret < 0 && *fail)
return -1;
break;
}
if (unknown) {
pa_strbuf_printf(buf, "Unknown command: %s\n", cs);
if (*fail)
return -1;
}
}
return 0;
}
int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail) {
char line[256];
FILE *f = NULL;
int ret = -1;
assert(c && fn && buf);
if (!(f = fopen(fn, "r"))) {
pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, strerror(errno));
if (!*fail)
ret = 0;
goto fail;
}
while (fgets(line, sizeof(line), f)) {
char *e = line + strcspn(line, linebreak);
*e = 0;
if (pa_cli_command_execute_line(c, line, buf, fail) < 0 && *fail)
goto fail;
}
ret = 0;
fail:
if (f)
fclose(f);
return ret;
}
int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail) {
const char *p;
assert(c && s && buf && fail);
p = s;
while (*p) {
size_t l = strcspn(p, linebreak);
char *line = pa_xstrndup(p, l);
if (pa_cli_command_execute_line(c, line, buf, fail) < 0&& *fail) {
pa_xfree(line);
return -1;
}
pa_xfree(line);
p += l;
p += strspn(p, linebreak);
}
return 0;
}

View file

@ -0,0 +1,40 @@
#ifndef fooclicommandhfoo
#define fooclicommandhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "strbuf.h"
#include "core.h"
/* Execute a single CLI command. Write the results to the string
* buffer *buf. If *fail is non-zero the function will return -1 when
* one or more of the executed commands failed. *fail
* may be modified by the function call. */
int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail);
/* Execute a whole file of CLI commands */
int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail);
/* Split the specified string into lines and run pa_cli_command_execute_line() for each. */
int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail);
#endif

385
src/polypcore/cli-text.c Normal file
View file

@ -0,0 +1,385 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <string.h>
#include "cli-text.h"
#include "module.h"
#include "client.h"
#include "sink.h"
#include "source.h"
#include "sink-input.h"
#include "source-output.h"
#include "strbuf.h"
#include "sample-util.h"
#include "scache.h"
#include "autoload.h"
#include "xmalloc.h"
#include <polyp/volume.h>
char *pa_module_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_module *m;
uint32_t idx = PA_IDXSET_INVALID;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_size(c->modules));
for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx))
pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\targument: <%s>\n\tused: %i\n\tauto unload: %s\n", m->index, m->name, m->argument, m->n_used, m->auto_unload ? "yes" : "no");
return pa_strbuf_tostring_free(s);
}
char *pa_client_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_client *client;
uint32_t idx = PA_IDXSET_INVALID;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(c->clients));
for (client = pa_idxset_first(c->clients, &idx); client; client = pa_idxset_next(c->clients, &idx)) {
pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\tdriver: <%s>\n", client->index, client->name, client->driver);
if (client->owner)
pa_strbuf_printf(s, "\towner module: <%u>\n", client->owner->index);
}
return pa_strbuf_tostring_free(s);
}
char *pa_sink_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_sink *sink;
uint32_t idx = PA_IDXSET_INVALID;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks));
for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_strbuf_printf(
s,
" %c index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
"\tvolume: <%s>\n"
"\tlatency: <%0.0f usec>\n"
"\tmonitor_source: <%u>\n"
"\tsample spec: <%s>\n"
"\tchannel map: <%s>\n",
c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ',
sink->index, sink->name,
sink->driver,
pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, PA_MIXER_HARDWARE)),
(double) pa_sink_get_latency(sink),
sink->monitor_source->index,
pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map));
if (sink->owner)
pa_strbuf_printf(s, "\towner module: <%u>\n", sink->owner->index);
if (sink->description)
pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description);
}
return pa_strbuf_tostring_free(s);
}
char *pa_source_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_source *source;
uint32_t idx = PA_IDXSET_INVALID;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources));
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_strbuf_printf(
s,
" %c index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
"\tlatency: <%0.0f usec>\n"
"\tsample spec: <%s>\n"
"\tchannel map: <%s>\n",
c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ',
source->index,
source->name,
source->driver,
(double) pa_source_get_latency(source),
pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map));
if (source->monitor_of)
pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index);
if (source->owner)
pa_strbuf_printf(s, "\towner module: <%u>\n", source->owner->index);
if (source->description)
pa_strbuf_printf(s, "\tdescription: <%s>\n", source->description);
}
return pa_strbuf_tostring_free(s);
}
char *pa_source_output_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_source_output *o;
uint32_t idx = PA_IDXSET_INVALID;
static const char* const state_table[] = {
"RUNNING",
"CORKED",
"DISCONNECTED"
};
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_size(c->source_outputs));
for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
assert(o->source);
pa_strbuf_printf(
s,
" index: %u\n"
"\tname: '%s'\n"
"\tdriver: <%s>\n"
"\tstate: %s\n"
"\tsource: <%u> '%s'\n"
"\tsample spec: <%s>\n"
"\tchannel map: <%s>\n"
"\tresample method: %s\n",
o->index,
o->name,
o->driver,
state_table[o->state],
o->source->index, o->source->name,
pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map),
pa_resample_method_to_string(pa_source_output_get_resample_method(o)));
if (o->owner)
pa_strbuf_printf(s, "\towner module: <%u>\n", o->owner->index);
if (o->client)
pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", o->client->index, o->client->name);
}
return pa_strbuf_tostring_free(s);
}
char *pa_sink_input_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_sink_input *i;
uint32_t idx = PA_IDXSET_INVALID;
static const char* const state_table[] = {
"RUNNING",
"CORKED",
"DISCONNECTED"
};
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs));
for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
assert(i->sink);
pa_strbuf_printf(
s,
" index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
"\tstate: %s\n"
"\tsink: <%u> '%s'\n"
"\tvolume: <%s>\n"
"\tlatency: <%0.0f usec>\n"
"\tsample spec: <%s>\n"
"\tchannel map: <%s>\n"
"\tresample method: %s\n",
i->index,
i->name,
i->driver,
state_table[i->state],
i->sink->index, i->sink->name,
pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)),
(double) pa_sink_input_get_latency(i),
pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
pa_resample_method_to_string(pa_sink_input_get_resample_method(i)));
if (i->owner)
pa_strbuf_printf(s, "\towner module: <%u>\n", i->owner->index);
if (i->client)
pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", i->client->index, i->client->name);
}
return pa_strbuf_tostring_free(s);
}
char *pa_scache_list_to_string(pa_core *c) {
pa_strbuf *s;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u cache entries available.\n", c->scache ? pa_idxset_size(c->scache) : 0);
if (c->scache) {
pa_scache_entry *e;
uint32_t idx = PA_IDXSET_INVALID;
for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) {
double l = 0;
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
if (e->memchunk.memblock) {
pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec);
pa_channel_map_snprint(cm, sizeof(cm), &e->channel_map);
l = (double) e->memchunk.length / pa_bytes_per_second(&e->sample_spec);
}
pa_strbuf_printf(
s,
" name: <%s>\n"
"\tindex: <%u>\n"
"\tsample spec: <%s>\n"
"\tchannel map: <%s>\n"
"\tlength: <%u>\n"
"\tduration: <%0.1fs>\n"
"\tvolume: <%s>\n"
"\tlazy: %s\n"
"\tfilename: %s\n",
e->name,
e->index,
ss,
cm,
e->memchunk.memblock ? e->memchunk.length : 0,
l,
pa_cvolume_snprint(cv, sizeof(cv), &e->volume),
e->lazy ? "yes" : "no",
e->filename ? e->filename : "n/a");
}
}
return pa_strbuf_tostring_free(s);
}
char *pa_autoload_list_to_string(pa_core *c) {
pa_strbuf *s;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u autoload entries available.\n", c->autoload_hashmap ? pa_hashmap_size(c->autoload_hashmap) : 0);
if (c->autoload_hashmap) {
pa_autoload_entry *e;
void *state = NULL;
while ((e = pa_hashmap_iterate(c->autoload_hashmap, &state, NULL))) {
pa_strbuf_printf(
s, " name: <%s>\n\ttype: <%s>\n\tindex: <%u>\n\tmodule_name: <%s>\n\targuments: <%s>\n",
e->name,
e->type == PA_NAMEREG_SOURCE ? "source" : "sink",
e->index,
e->module,
e->argument);
}
}
return pa_strbuf_tostring_free(s);
}
char *pa_full_status_string(pa_core *c) {
pa_strbuf *s;
int i;
s = pa_strbuf_new();
for (i = 0; i < 8; i++) {
char *t = NULL;
switch (i) {
case 0:
t = pa_sink_list_to_string(c);
break;
case 1:
t = pa_source_list_to_string(c);
break;
case 2:
t = pa_sink_input_list_to_string(c);
break;
case 3:
t = pa_source_output_list_to_string(c);
break;
case 4:
t = pa_client_list_to_string(c);
break;
case 5:
t = pa_module_list_to_string(c);
break;
case 6:
t = pa_scache_list_to_string(c);
break;
case 7:
t = pa_autoload_list_to_string(c);
break;
}
pa_strbuf_puts(s, t);
pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
}

42
src/polypcore/cli-text.h Normal file
View file

@ -0,0 +1,42 @@
#ifndef fooclitexthfoo
#define fooclitexthfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "core.h"
/* Some functions to generate pretty formatted listings of
* entities. The returned strings have to be freed manually. */
char *pa_sink_input_list_to_string(pa_core *c);
char *pa_source_output_list_to_string(pa_core *c);
char *pa_sink_list_to_string(pa_core *core);
char *pa_source_list_to_string(pa_core *c);
char *pa_client_list_to_string(pa_core *c);
char *pa_module_list_to_string(pa_core *c);
char *pa_scache_list_to_string(pa_core *c);
char *pa_autoload_list_to_string(pa_core *c);
char *pa_full_status_string(pa_core *c);
#endif

147
src/polypcore/cli.c Normal file
View file

@ -0,0 +1,147 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include "ioline.h"
#include "cli.h"
#include "module.h"
#include "sink.h"
#include "source.h"
#include "client.h"
#include "sink-input.h"
#include "source-output.h"
#include "tokenizer.h"
#include "strbuf.h"
#include "namereg.h"
#include "cli-text.h"
#include "cli-command.h"
#include "xmalloc.h"
#include "log.h"
#define PROMPT ">>> "
struct pa_cli {
pa_core *core;
pa_ioline *line;
void (*eof_callback)(pa_cli *c, void *userdata);
void *userdata;
pa_client *client;
int fail, kill_requested, defer_kill;
};
static void line_callback(pa_ioline *line, const char *s, void *userdata);
static void client_kill(pa_client *c);
pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {
char cname[256];
pa_cli *c;
assert(io);
c = pa_xmalloc(sizeof(pa_cli));
c->core = core;
c->line = pa_ioline_new(io);
assert(c->line);
c->userdata = NULL;
c->eof_callback = NULL;
pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
c->client = pa_client_new(core, __FILE__, cname);
assert(c->client);
c->client->kill = client_kill;
c->client->userdata = c;
c->client->owner = m;
pa_ioline_set_callback(c->line, line_callback, c);
pa_ioline_puts(c->line, "Welcome to polypaudio! Use \"help\" for usage information.\n"PROMPT);
c->fail = c->kill_requested = c->defer_kill = 0;
return c;
}
void pa_cli_free(pa_cli *c) {
assert(c);
pa_ioline_close(c->line);
pa_ioline_unref(c->line);
pa_client_free(c->client);
pa_xfree(c);
}
static void client_kill(pa_client *client) {
pa_cli *c;
assert(client && client->userdata);
c = client->userdata;
pa_log_debug(__FILE__": CLI client killed.\n");
if (c->defer_kill)
c->kill_requested = 1;
else {
if (c->eof_callback)
c->eof_callback(c, c->userdata);
}
}
static void line_callback(pa_ioline *line, const char *s, void *userdata) {
pa_strbuf *buf;
pa_cli *c = userdata;
char *p;
assert(line && c);
if (!s) {
pa_log_debug(__FILE__": CLI got EOF from user.\n");
if (c->eof_callback)
c->eof_callback(c, c->userdata);
return;
}
buf = pa_strbuf_new();
assert(buf);
c->defer_kill++;
pa_cli_command_execute_line(c->core, s, buf, &c->fail);
c->defer_kill--;
pa_ioline_puts(line, p = pa_strbuf_tostring_free(buf));
pa_xfree(p);
if (c->kill_requested) {
if (c->eof_callback)
c->eof_callback(c, c->userdata);
} else
pa_ioline_puts(line, PROMPT);
}
void pa_cli_set_eof_callback(pa_cli *c, void (*cb)(pa_cli*c, void *userdata), void *userdata) {
assert(c);
c->eof_callback = cb;
c->userdata = userdata;
}

38
src/polypcore/cli.h Normal file
View file

@ -0,0 +1,38 @@
#ifndef fooclihfoo
#define fooclihfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "iochannel.h"
#include "core.h"
#include "module.h"
typedef struct pa_cli pa_cli;
/* Create a new command line session on the specified io channel owned by the specified module */
pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m);
void pa_cli_free(pa_cli *cli);
/* Set a callback function that is called whenever the command line session is terminated */
void pa_cli_set_eof_callback(pa_cli *cli, void (*cb)(pa_cli*c, void *userdata), void *userdata);
#endif

91
src/polypcore/client.c Normal file
View file

@ -0,0 +1,91 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "client.h"
#include "xmalloc.h"
#include "subscribe.h"
#include "log.h"
pa_client *pa_client_new(pa_core *core, const char *name, const char *driver) {
pa_client *c;
int r;
assert(core);
c = pa_xmalloc(sizeof(pa_client));
c->name = pa_xstrdup(name);
c->driver = pa_xstrdup(driver);
c->owner = NULL;
c->core = core;
c->kill = NULL;
c->userdata = NULL;
r = pa_idxset_put(core->clients, c, &c->index);
assert(c->index != PA_IDXSET_INVALID && r >= 0);
pa_log_info(__FILE__": created %u \"%s\"\n", c->index, c->name);
pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index);
pa_core_check_quit(core);
return c;
}
void pa_client_free(pa_client *c) {
assert(c && c->core);
pa_idxset_remove_by_data(c->core->clients, c, NULL);
pa_core_check_quit(c->core);
pa_log_info(__FILE__": freed %u \"%s\"\n", c->index, c->name);
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
pa_xfree(c->name);
pa_xfree(c->driver);
pa_xfree(c);
}
void pa_client_kill(pa_client *c) {
assert(c);
if (!c->kill) {
pa_log_warn(__FILE__": kill() operation not implemented for client %u\n", c->index);
return;
}
c->kill(c);
}
void pa_client_set_name(pa_client *c, const char *name) {
assert(c);
pa_xfree(c->name);
c->name = pa_xstrdup(name);
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
}

57
src/polypcore/client.h Normal file
View file

@ -0,0 +1,57 @@
#ifndef fooclienthfoo
#define fooclienthfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "core.h"
#include "module.h"
/* Every connection to the server should have a pa_client
* attached. That way the user may generate a listing of all connected
* clients easily and kill them if he wants.*/
typedef struct pa_client pa_client;
struct pa_client {
uint32_t index;
pa_module *owner;
char *name, *driver;
pa_core *core;
void (*kill)(pa_client *c);
void *userdata;
};
pa_client *pa_client_new(pa_core *c, const char *name, const char *driver);
/* This function should be called only by the code that created the client */
void pa_client_free(pa_client *c);
/* Code that didn't create the client should call this function to
* request destruction of the client */
void pa_client_kill(pa_client *c);
/* Rename the client */
void pa_client_set_name(pa_client *c, const char *name);
#endif

176
src/polypcore/conf-parser.c Normal file
View file

@ -0,0 +1,176 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "conf-parser.h"
#include "log.h"
#include "util.h"
#include "xmalloc.h"
#define WHITESPACE " \t\n"
#define COMMENTS "#;\n"
/* Run the user supplied parser for an assignment */
static int next_assignment(const char *filename, unsigned line, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) {
assert(filename && t && lvalue && rvalue);
for (; t->parse; t++)
if (!strcmp(lvalue, t->lvalue))
return t->parse(filename, line, lvalue, rvalue, t->data, userdata);
pa_log(__FILE__": [%s:%u] Unknown lvalue '%s'.\n", filename, line, lvalue);
return -1;
}
/* Returns non-zero when c is contained in s */
static int in_string(char c, const char *s) {
assert(s);
for (; *s; s++)
if (*s == c)
return 1;
return 0;
}
/* Remove all whitepsapce from the beginning and the end of *s. *s may
* be modified. */
static char *strip(char *s) {
char *b = s+strspn(s, WHITESPACE);
char *e, *l = NULL;
for (e = b; *e; e++)
if (!in_string(*e, WHITESPACE))
l = e;
if (l)
*(l+1) = 0;
return b;
}
/* Parse a variable assignment line */
static int parse_line(const char *filename, unsigned line, const pa_config_item *t, char *l, void *userdata) {
char *e, *c, *b = l+strspn(l, WHITESPACE);
if ((c = strpbrk(b, COMMENTS)))
*c = 0;
if (!*b)
return 0;
if (!(e = strchr(b, '='))) {
pa_log(__FILE__": [%s:%u] Missing '='.\n", filename, line);
return -1;
}
*e = 0;
e++;
return next_assignment(filename, line, t, strip(b), strip(e), userdata);
}
/* Go through the file and parse each line */
int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata) {
int r = -1;
unsigned line = 0;
int do_close = !f;
assert(filename && t);
if (!f && !(f = fopen(filename, "r"))) {
if (errno == ENOENT) {
r = 0;
goto finish;
}
pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s\n", filename, strerror(errno));
goto finish;
}
while (!feof(f)) {
char l[256];
if (!fgets(l, sizeof(l), f)) {
if (feof(f))
break;
pa_log(__FILE__": WARNING: failed to read configuration file '%s': %s\n", filename, strerror(errno));
goto finish;
}
if (parse_line(filename, ++line, t, l, userdata) < 0)
goto finish;
}
r = 0;
finish:
if (do_close && f)
fclose(f);
return r;
}
int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
int *i = data;
int32_t k;
assert(filename && lvalue && rvalue && data);
if (pa_atoi(rvalue, &k) < 0) {
pa_log(__FILE__": [%s:%u] Failed to parse numeric value: %s\n", filename, line, rvalue);
return -1;
}
*i = (int) k;
return 0;
}
int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
int *b = data, k;
assert(filename && lvalue && rvalue && data);
if ((k = pa_parse_boolean(rvalue)) < 0) {
pa_log(__FILE__": [%s:%u] Failed to parse boolean value: %s\n", filename, line, rvalue);
return -1;
}
*b = k;
return 0;
}
int pa_config_parse_string(const char *filename, PA_GCC_UNUSED unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
char **s = data;
assert(filename && lvalue && rvalue && data);
pa_xfree(*s);
*s = *rvalue ? pa_xstrdup(rvalue) : NULL;
return 0;
}

View file

@ -0,0 +1,47 @@
#ifndef fooconfparserhfoo
#define fooconfparserhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <stdio.h>
/* An abstract parser for simple, line based, shallow configuration
* files consisting of variable assignments only. */
/* Wraps info for parsing a specific configuration variable */
typedef struct pa_config_item {
const char *lvalue; /* name of the variable */
int (*parse)(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); /* Function that is called to parse the variable's value */
void *data; /* Where to store the variable's data */
} pa_config_item;
/* The configuration file parsing routine. Expects a table of
* pa_config_items in *t that is terminated by an item where lvalue is
* NULL */
int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata);
/* Generic parsers for integers, booleans and strings */
int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
#endif

157
src/polypcore/core.c Normal file
View file

@ -0,0 +1,157 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <signal.h>
#include "core.h"
#include "module.h"
#include "sink.h"
#include "source.h"
#include "namereg.h"
#include "util.h"
#include "scache.h"
#include "autoload.h"
#include "xmalloc.h"
#include "subscribe.h"
#include "props.h"
#include "random.h"
pa_core* pa_core_new(pa_mainloop_api *m) {
pa_core* c;
c = pa_xmalloc(sizeof(pa_core));
c->mainloop = m;
c->clients = pa_idxset_new(NULL, NULL);
c->sinks = pa_idxset_new(NULL, NULL);
c->sources = pa_idxset_new(NULL, NULL);
c->source_outputs = pa_idxset_new(NULL, NULL);
c->sink_inputs = pa_idxset_new(NULL, NULL);
c->default_source_name = c->default_sink_name = NULL;
c->modules = NULL;
c->namereg = NULL;
c->scache = NULL;
c->autoload_idxset = NULL;
c->autoload_hashmap = NULL;
c->running_as_daemon = 0;
c->default_sample_spec.format = PA_SAMPLE_S16NE;
c->default_sample_spec.rate = 44100;
c->default_sample_spec.channels = 2;
c->module_auto_unload_event = NULL;
c->module_defer_unload_event = NULL;
c->scache_auto_unload_event = NULL;
c->subscription_defer_event = NULL;
c->subscription_event_queue = NULL;
c->subscriptions = NULL;
c->memblock_stat = pa_memblock_stat_new();
c->disallow_module_loading = 0;
c->quit_event = NULL;
c->exit_idle_time = -1;
c->module_idle_time = 20;
c->scache_idle_time = 20;
c->resample_method = PA_RESAMPLER_SRC_SINC_FASTEST;
pa_property_init(c);
pa_random(&c->cookie, sizeof(c->cookie));
#ifdef SIGPIPE
pa_check_signal_is_blocked(SIGPIPE);
#endif
return c;
}
void pa_core_free(pa_core *c) {
assert(c);
pa_module_unload_all(c);
assert(!c->modules);
assert(pa_idxset_isempty(c->clients));
pa_idxset_free(c->clients, NULL, NULL);
assert(pa_idxset_isempty(c->sinks));
pa_idxset_free(c->sinks, NULL, NULL);
assert(pa_idxset_isempty(c->sources));
pa_idxset_free(c->sources, NULL, NULL);
assert(pa_idxset_isempty(c->source_outputs));
pa_idxset_free(c->source_outputs, NULL, NULL);
assert(pa_idxset_isempty(c->sink_inputs));
pa_idxset_free(c->sink_inputs, NULL, NULL);
pa_scache_free(c);
pa_namereg_free(c);
pa_autoload_free(c);
pa_subscription_free_all(c);
if (c->quit_event)
c->mainloop->time_free(c->quit_event);
pa_xfree(c->default_source_name);
pa_xfree(c->default_sink_name);
pa_memblock_stat_unref(c->memblock_stat);
pa_property_cleanup(c);
pa_xfree(c);
}
static void quit_callback(pa_mainloop_api*m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
pa_core *c = userdata;
assert(c->quit_event = e);
m->quit(m, 0);
}
void pa_core_check_quit(pa_core *c) {
assert(c);
if (!c->quit_event && c->exit_idle_time >= 0 && pa_idxset_size(c->clients) == 0) {
struct timeval tv;
pa_gettimeofday(&tv);
tv.tv_sec+= c->exit_idle_time;
c->quit_event = c->mainloop->time_new(c->mainloop, &tv, quit_callback, c);
} else if (c->quit_event && pa_idxset_size(c->clients) > 0) {
c->mainloop->time_free(c->quit_event);
c->quit_event = NULL;
}
}

82
src/polypcore/core.h Normal file
View file

@ -0,0 +1,82 @@
#ifndef foocorehfoo
#define foocorehfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
typedef struct pa_core pa_core;
#include "idxset.h"
#include "hashmap.h"
#include <polyp/mainloop-api.h>
#include <polyp/sample.h>
#include "memblock.h"
#include "resampler.h"
#include "queue.h"
#include "subscribe.h"
/* The core structure of polypaudio. Every polypaudio daemon contains
* exactly one of these. It is used for storing kind of global
* variables for the daemon. */
struct pa_core {
/* A random value which may be used to identify this instance of
* polypaudio. Not cryptographically secure in any way. */
uint32_t cookie;
pa_mainloop_api *mainloop;
/* idxset of all kinds of entities */
pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *autoload_idxset;
/* Some hashmaps for all sorts of entities */
pa_hashmap *namereg, *autoload_hashmap, *properties;
/* The name of the default sink/source */
char *default_source_name, *default_sink_name;
pa_sample_spec default_sample_spec;
pa_time_event *module_auto_unload_event;
pa_defer_event *module_defer_unload_event;
pa_defer_event *subscription_defer_event;
pa_queue *subscription_event_queue;
pa_subscription *subscriptions;
pa_memblock_stat *memblock_stat;
int disallow_module_loading, running_as_daemon;
int exit_idle_time, module_idle_time, scache_idle_time;
pa_time_event *quit_event;
pa_time_event *scache_auto_unload_event;
pa_resample_method_t resample_method;
};
pa_core* pa_core_new(pa_mainloop_api *m);
void pa_core_free(pa_core*c);
/* Check whether noone is connected to this core */
void pa_core_check_quit(pa_core *c);
#endif

46
src/polypcore/dllmain.c Normal file
View file

@ -0,0 +1,46 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef OS_IS_WIN32
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>
extern pa_set_root(HANDLE handle);
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
if (fdwReason != DLL_PROCESS_ATTACH)
return TRUE;
if (!pa_set_root(hinstDLL))
return FALSE;
return TRUE;
}
#endif /* OS_IS_WIN32 */

102
src/polypcore/dynarray.c Normal file
View file

@ -0,0 +1,102 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include "dynarray.h"
#include "xmalloc.h"
/* If the array becomes to small, increase its size by 100 entries */
#define INCREASE_BY 100
struct pa_dynarray {
void **data;
unsigned n_allocated, n_entries;
};
pa_dynarray* pa_dynarray_new(void) {
pa_dynarray *a;
a = pa_xnew(pa_dynarray, 1);
a->data = NULL;
a->n_entries = 0;
a->n_allocated = 0;
return a;
}
void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata) {
unsigned i;
assert(a);
if (func)
for (i = 0; i < a->n_entries; i++)
if (a->data[i])
func(a->data[i], userdata);
pa_xfree(a->data);
pa_xfree(a);
}
void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p) {
assert(a);
if (i >= a->n_allocated) {
unsigned n;
if (!p)
return;
n = i+INCREASE_BY;
a->data = pa_xrealloc(a->data, sizeof(void*)*n);
memset(a->data+a->n_allocated, 0, sizeof(void*)*(n-a->n_allocated));
a->n_allocated = n;
}
a->data[i] = p;
if (i >= a->n_entries)
a->n_entries = i+1;
}
unsigned pa_dynarray_append(pa_dynarray*a, void *p) {
unsigned i = a->n_entries;
pa_dynarray_put(a, i, p);
return i;
}
void *pa_dynarray_get(pa_dynarray*a, unsigned i) {
assert(a);
if (i >= a->n_allocated)
return NULL;
assert(a->data);
return a->data[i];
}
unsigned pa_dynarray_size(pa_dynarray*a) {
assert(a);
return a->n_entries;
}

49
src/polypcore/dynarray.h Normal file
View file

@ -0,0 +1,49 @@
#ifndef foodynarrayhfoo
#define foodynarrayhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
typedef struct pa_dynarray pa_dynarray;
/* Implementation of a simple dynamically sized array. The array
* expands if required, but doesn't shrink if possible. Memory
* management of the array's entries is the user's job. */
pa_dynarray* pa_dynarray_new(void);
/* Free the array calling the specified function for every entry in
* the array. The function may be NULL. */
void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata);
/* Store p at position i in the array */
void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p);
/* Store p a the first free position in the array. Returns the index
* of that entry. If entries are removed from the array their position
* are not filled any more by this function. */
unsigned pa_dynarray_append(pa_dynarray*a, void *p);
void *pa_dynarray_get(pa_dynarray*a, unsigned i);
unsigned pa_dynarray_size(pa_dynarray*a);
#endif

View file

@ -0,0 +1,77 @@
#ifndef fooendianmacroshfoo
#define fooendianmacroshfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define INT16_SWAP(x) ( (int16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) )
#define UINT16_SWAP(x) ( (uint16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) )
#define INT32_SWAP(x) ( (int32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) )
#define UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) )
#define MAYBE_INT32_SWAP(c,x) ((c) ? INT32_SWAP(x) : x)
#define MAYBE_UINT32_SWAP(c,x) ((c) ? UINT32_SWAP(x) : x)
#ifdef WORDS_BIGENDIAN
#define INT16_FROM_LE(x) INT16_SWAP(x)
#define INT16_FROM_BE(x) ((int16_t)(x))
#define INT16_TO_LE(x) INT16_SWAP(x)
#define INT16_TO_BE(x) ((int16_t)(x))
#define UINT16_FROM_LE(x) UINT16_SWAP(x)
#define UINT16_FROM_BE(x) ((uint16_t)(x))
#define INT32_FROM_LE(x) INT32_SWAP(x)
#define INT32_FROM_BE(x) ((int32_t)(x))
#define UINT32_FROM_LE(x) UINT32_SWAP(x)
#define UINT32_FROM_BE(x) ((uint32_t)(x))
#define UINT32_TO_LE(x) UINT32_SWAP(x)
#define UINT32_TO_BE(x) ((uint32_t)(x))
#else
#define INT16_FROM_LE(x) ((int16_t)(x))
#define INT16_FROM_BE(x) INT16_SWAP(x)
#define INT16_TO_LE(x) ((int16_t)(x))
#define INT16_TO_BE(x) INT16_SWAP(x)
#define UINT16_FROM_LE(x) ((uint16_t)(x))
#define UINT16_FROM_BE(x) UINT16_SWAP(x)
#define INT32_FROM_LE(x) ((int32_t)(x))
#define INT32_FROM_BE(x) INT32_SWAP(x)
#define UINT32_FROM_LE(x) ((uint32_t)(x))
#define UINT32_FROM_BE(x) UINT32_SWAP(x)
#define UINT32_TO_LE(x) ((uint32_t)(x))
#define UINT32_TO_BE(x) UINT32_SWAP(x)
#endif
#endif

209
src/polypcore/esound.h Normal file
View file

@ -0,0 +1,209 @@
#ifndef fooesoundhfoo
#define fooesoundhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* Most of the following is blatantly stolen from esound. */
/* path and name of the default EsounD domain socket */
#define ESD_UNIX_SOCKET_DIR "/tmp/.esd"
#define ESD_UNIX_SOCKET_NAME "/tmp/.esd/socket"
/* length of the audio buffer size */
#define ESD_BUF_SIZE (4 * 1024)
/* maximum size we can write(). Otherwise we might overflow */
#define ESD_MAX_WRITE_SIZE (21 * 4096)
/* length of the authorization key, octets */
#define ESD_KEY_LEN (16)
/* default port for the EsounD server */
#define ESD_DEFAULT_PORT (16001)
/* default sample rate for the EsounD server */
#define ESD_DEFAULT_RATE (44100)
/* maximum length of a stream/sample name */
#define ESD_NAME_MAX (128)
/* a magic number to identify the relative endianness of a client */
#define ESD_ENDIAN_KEY ((uint32_t) (('E' << 24) + ('N' << 16) + ('D' << 8) + ('N')))
#define ESD_VOLUME_BASE (256)
/*************************************/
/* what can we do to/with the EsounD */
enum esd_proto {
ESD_PROTO_CONNECT, /* implied on inital client connection */
/* pseudo "security" functionality */
ESD_PROTO_LOCK, /* disable "foreign" client connections */
ESD_PROTO_UNLOCK, /* enable "foreign" client connections */
/* stream functionality: play, record, monitor */
ESD_PROTO_STREAM_PLAY, /* play all following data as a stream */
ESD_PROTO_STREAM_REC, /* record data from card as a stream */
ESD_PROTO_STREAM_MON, /* send mixed buffer output as a stream */
/* sample functionality: cache, free, play, loop, EOL, kill */
ESD_PROTO_SAMPLE_CACHE, /* cache a sample in the server */
ESD_PROTO_SAMPLE_FREE, /* release a sample in the server */
ESD_PROTO_SAMPLE_PLAY, /* play a cached sample */
ESD_PROTO_SAMPLE_LOOP, /* loop a cached sample, til eoloop */
ESD_PROTO_SAMPLE_STOP, /* stop a looping sample when done */
ESD_PROTO_SAMPLE_KILL, /* stop the looping sample immed. */
/* free and reclaim /dev/dsp functionality */
ESD_PROTO_STANDBY, /* release /dev/dsp and ignore all data */
ESD_PROTO_RESUME, /* reclaim /dev/dsp and play sounds again */
/* TODO: move these to a more logical place. NOTE: will break the protocol */
ESD_PROTO_SAMPLE_GETID, /* get the ID for an already-cached sample */
ESD_PROTO_STREAM_FILT, /* filter mixed buffer output as a stream */
/* esd remote management */
ESD_PROTO_SERVER_INFO, /* get server info (ver, sample rate, format) */
ESD_PROTO_ALL_INFO, /* get all info (server info, players, samples) */
ESD_PROTO_SUBSCRIBE, /* track new and removed players and samples */
ESD_PROTO_UNSUBSCRIBE, /* stop tracking updates */
/* esd remote control */
ESD_PROTO_STREAM_PAN, /* set stream panning */
ESD_PROTO_SAMPLE_PAN, /* set default sample panning */
/* esd status */
ESD_PROTO_STANDBY_MODE, /* see if server is in standby, autostandby, etc */
/* esd latency */
ESD_PROTO_LATENCY, /* retrieve latency between write()'s and output */
ESD_PROTO_MAX /* for bounds checking */
};
/******************/
/* The EsounD api */
/* the properties of a sound buffer are logically or'd */
/* bits of stream/sample data */
#define ESD_MASK_BITS ( 0x000F )
#define ESD_BITS8 ( 0x0000 )
#define ESD_BITS16 ( 0x0001 )
/* how many interleaved channels of data */
#define ESD_MASK_CHAN ( 0x00F0 )
#define ESD_MONO ( 0x0010 )
#define ESD_STEREO ( 0x0020 )
/* whether it's a stream or a sample */
#define ESD_MASK_MODE ( 0x0F00 )
#define ESD_STREAM ( 0x0000 )
#define ESD_SAMPLE ( 0x0100 )
#define ESD_ADPCM ( 0x0200 ) /* TODO: anyone up for this? =P */
/* the function of the stream/sample, and common functions */
#define ESD_MASK_FUNC ( 0xF000 )
#define ESD_PLAY ( 0x1000 )
/* functions for streams only */
#define ESD_MONITOR ( 0x0000 )
#define ESD_RECORD ( 0x2000 )
/* functions for samples only */
#define ESD_STOP ( 0x0000 )
#define ESD_LOOP ( 0x2000 )
typedef int esd_format_t;
typedef int esd_proto_t;
/*******************************************************************/
/* esdmgr.c - functions to implement a "sound manager" for esd */
/* structures to retrieve information about streams/samples from the server */
typedef struct esd_server_info {
int version; /* server version encoded as an int */
esd_format_t format; /* magic int with the format info */
int rate; /* sample rate */
} esd_server_info_t;
typedef struct esd_player_info {
struct esd_player_info *next; /* point to next entry in list */
esd_server_info_t *server; /* the server that contains this stream */
int source_id; /* either a stream fd or sample id */
char name[ ESD_NAME_MAX ]; /* name of stream for remote control */
int rate; /* sample rate */
int left_vol_scale; /* volume scaling */
int right_vol_scale;
esd_format_t format; /* magic int with the format info */
} esd_player_info_t;
typedef struct esd_sample_info {
struct esd_sample_info *next; /* point to next entry in list */
esd_server_info_t *server; /* the server that contains this sample */
int sample_id; /* either a stream fd or sample id */
char name[ ESD_NAME_MAX ]; /* name of stream for remote control */
int rate; /* sample rate */
int left_vol_scale; /* volume scaling */
int right_vol_scale;
esd_format_t format; /* magic int with the format info */
int length; /* total buffer length */
} esd_sample_info_t;
typedef struct esd_info {
esd_server_info_t *server;
esd_player_info_t *player_list;
esd_sample_info_t *sample_list;
} esd_info_t;
enum esd_standby_mode {
ESM_ERROR, ESM_ON_STANDBY, ESM_ON_AUTOSTANDBY, ESM_RUNNING
};
typedef int esd_standby_mode_t;
enum esd_client_state {
ESD_STREAMING_DATA, /* data from here on is streamed data */
ESD_CACHING_SAMPLE, /* midway through caching a sample */
ESD_NEEDS_REQDATA, /* more data needed to complere request */
ESD_NEXT_REQUEST, /* proceed to next request */
ESD_CLIENT_STATE_MAX /* place holder */
};
typedef int esd_client_state_t;
/* the endian key is transferred in binary, if it's read into int, */
/* and matches ESD_ENDIAN_KEY (ENDN), then the endianness of the */
/* server and the client match; if it's SWAP_ENDIAN_KEY, swap data */
#define ESD_SWAP_ENDIAN_KEY (UINT32_SWAP(ESD_ENDIAN_KEY))
#endif

2531
src/polypcore/g711.c Normal file

File diff suppressed because it is too large Load diff

40
src/polypcore/g711.h Normal file
View file

@ -0,0 +1,40 @@
#ifndef foog711hfoo
#define foog711hfoo
/* g711.h - include for G711 u-law and a-law conversion routines
**
** Copyright (C) 2001 Chris Bagwell
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation. This software is provided "as is" without express or
** implied warranty.
*/
/** Copied from sox -- Lennart Poettring*/
#include <inttypes.h>
#ifdef FAST_ALAW_CONVERSION
extern uint8_t _st_13linear2alaw[0x2000];
extern int16_t _st_alaw2linear16[256];
#define st_13linear2alaw(sw) (_st_13linear2alaw[(sw + 0x1000)])
#define st_alaw2linear16(uc) (_st_alaw2linear16[uc])
#else
unsigned char st_13linear2alaw(int16_t pcm_val);
int16_t st_alaw2linear16(unsigned char);
#endif
#ifdef FAST_ULAW_CONVERSION
extern uint8_t _st_14linear2ulaw[0x4000];
extern int16_t _st_ulaw2linear16[256];
#define st_14linear2ulaw(sw) (_st_14linear2ulaw[(sw + 0x2000)])
#define st_ulaw2linear16(uc) (_st_ulaw2linear16[uc])
#else
unsigned char st_14linear2ulaw(int16_t pcm_val);
int16_t st_ulaw2linear16(unsigned char);
#endif
#endif

53
src/polypcore/gccmacro.h Normal file
View file

@ -0,0 +1,53 @@
#ifndef foogccmacrohfoo
#define foogccmacrohfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef __GNUC__
#define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b)))
#else
/** If we're in GNU C, use some magic for detecting invalid format strings */
#define PA_GCC_PRINTF_ATTR(a,b)
#endif
#if defined(__GNUC__) && (__GNUC__ >= 4)
#define PA_GCC_SENTINEL __attribute__ ((sentinel))
#else
/** Macro for usage of GCC's sentinel compilation warnings */
#define PA_GCC_SENTINEL
#endif
#ifdef __GNUC__
#define PA_GCC_NORETURN __attribute__((noreturn))
#else
/** Macro for no-return functions */
#define PA_GCC_NORETURN
#endif
#ifdef __GNUC__
#define PA_GCC_UNUSED __attribute__ ((unused))
#else
/** Macro for not used parameter */
#define PA_GCC_UNUSED
#endif
#endif

194
src/polypcore/hashmap.c Normal file
View file

@ -0,0 +1,194 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "hashmap.h"
#include "idxset.h"
#include "xmalloc.h"
#include "log.h"
#define BUCKETS 1023
struct hashmap_entry {
struct hashmap_entry *next, *previous, *bucket_next, *bucket_previous;
unsigned hash;
const void *key;
void *value;
};
struct pa_hashmap {
unsigned size;
struct hashmap_entry **data;
struct hashmap_entry *first_entry;
unsigned n_entries;
unsigned (*hash_func) (const void *p);
int (*compare_func) (const void*a, const void*b);
};
pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) {
pa_hashmap *h;
h = pa_xmalloc(sizeof(pa_hashmap));
h->data = pa_xmalloc0(sizeof(struct hashmap_entry*)*(h->size = BUCKETS));
h->first_entry = NULL;
h->n_entries = 0;
h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
return h;
}
static void remove(pa_hashmap *h, struct hashmap_entry *e) {
assert(e);
if (e->next)
e->next->previous = e->previous;
if (e->previous)
e->previous->next = e->next;
else
h->first_entry = e->next;
if (e->bucket_next)
e->bucket_next->bucket_previous = e->bucket_previous;
if (e->bucket_previous)
e->bucket_previous->bucket_next = e->bucket_next;
else {
assert(e->hash < h->size);
h->data[e->hash] = e->bucket_next;
}
pa_xfree(e);
h->n_entries--;
}
void pa_hashmap_free(pa_hashmap*h, void (*free_func)(void *p, void *userdata), void *userdata) {
assert(h);
while (h->first_entry) {
if (free_func)
free_func(h->first_entry->value, userdata);
remove(h, h->first_entry);
}
pa_xfree(h->data);
pa_xfree(h);
}
static struct hashmap_entry *get(pa_hashmap *h, unsigned hash, const void *key) {
struct hashmap_entry *e;
assert(h && hash < h->size);
for (e = h->data[hash]; e; e = e->bucket_next)
if (h->compare_func(e->key, key) == 0)
return e;
return NULL;
}
int pa_hashmap_put(pa_hashmap *h, const void *key, void *value) {
struct hashmap_entry *e;
unsigned hash;
assert(h);
hash = h->hash_func(key) % h->size;
if ((e = get(h, hash, key)))
return -1;
e = pa_xmalloc(sizeof(struct hashmap_entry));
e->hash = hash;
e->key = key;
e->value = value;
e->previous = NULL;
e->next = h->first_entry;
if (h->first_entry)
h->first_entry->previous = e;
h->first_entry = e;
e->bucket_previous = NULL;
e->bucket_next = h->data[hash];
if (h->data[hash])
h->data[hash]->bucket_previous = e;
h->data[hash] = e;
h->n_entries ++;
return 0;
}
void* pa_hashmap_get(pa_hashmap *h, const void *key) {
unsigned hash;
struct hashmap_entry *e;
assert(h && key);
hash = h->hash_func(key) % h->size;
if (!(e = get(h, hash, key)))
return NULL;
return e->value;
}
void* pa_hashmap_remove(pa_hashmap *h, const void *key) {
struct hashmap_entry *e;
unsigned hash;
void *data;
assert(h && key);
hash = h->hash_func(key) % h->size;
if (!(e = get(h, hash, key)))
return NULL;
data = e->value;
remove(h, e);
return data;
}
unsigned pa_hashmap_size(pa_hashmap *h) {
return h->n_entries;
}
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) {
assert(h && state);
if (!*state)
*state = h->first_entry;
else
*state = ((struct hashmap_entry*) *state)->next;
if (!*state) {
if (key)
*key = NULL;
return NULL;
}
if (key)
*key = ((struct hashmap_entry*) *state)->key;
return ((struct hashmap_entry*) *state)->value;
}

53
src/polypcore/hashmap.h Normal file
View file

@ -0,0 +1,53 @@
#ifndef foohashmaphfoo
#define foohashmaphfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* Simple Implementation of a hash table. Memory management is the
* user's job. It's a good idea to have the key pointer point to a
* string in the value data. */
typedef struct pa_hashmap pa_hashmap;
/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map */
pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b));
/* Free the hash table. Calls the specified function for every value in the table. The function may be NULL */
void pa_hashmap_free(pa_hashmap*, void (*free_func)(void *p, void *userdata), void *userdata);
/* Returns non-zero when the entry already exists */
int pa_hashmap_put(pa_hashmap *h, const void *key, void *value);
void* pa_hashmap_get(pa_hashmap *h, const void *key);
/* Returns the data of the entry while removing */
void* pa_hashmap_remove(pa_hashmap *h, const void *key);
unsigned pa_hashmap_size(pa_hashmap *h);
/* May be used to iterate through the hashmap. Initially the opaque
pointer *state has to be set to NULL. The hashmap may not be
modified during iteration. The key of the entry is returned in
*key, if key is non-NULL. After the last entry in the hashmap NULL
is returned. */
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key);
#endif

116
src/polypcore/howl-wrap.c Normal file
View file

@ -0,0 +1,116 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
polypaudio 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 Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <assert.h>
#include "howl-wrap.h"
#include "log.h"
#include "xmalloc.h"
#include "props.h"
#define HOWL_PROPERTY "howl"
pa_howl_wrapper {
pa_core *core;
int ref;
pa_io_event *io_event;
sw_discovery discovery;
};
static void howl_io_event(pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags f, void *userdata) {
pa_howl_wrapper *w = userdata;
assert(m && e && fd >= 0 && w && w->ref >= 1);
if (f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR))
goto fail;
if (sw_discovery_read_socket(w->discovery) != SW_OKAY)
goto fail;
return;
fail:
pa_log(__FILE__": howl connection died.\n");
w->core->mainloop->io_free(w->io_event);
w->io_event = NULL;
}
static pa_howl_wrapper* howl_wrapper_new(pa_core *c) {
pa_howl_wrapper *h;
sw_discovery session;
assert(c);
if (sw_discovery_init(&session) != SW_OKAY) {
pa_log("sw_discovery_init() failed.\n");
return NULL;
}
h = pa_xmalloc(sizeof(pa_howl_wrapper));
h->core = c;
h->ref = 1;
h->discovery = session;
h->io_event = c->mainloop->io_new(c->mainloop, sw_discovery_socket(session), PA_IO_EVENT_INPUT, howl_io_event, h);
return h;
}
static void howl_wrapper_free(pa_howl_wrapper *h) {
assert(h);
sw_discovery_fina(h->discovery);
if (h->io_event)
h->core->mainloop->io_free(h->io_event);
pa_xfree(h);
}
pa_howl_wrapper* pa_howl_wrapper_get(pa_core *c) {
pa_howl_wrapper *h;
assert(c);
if ((h = pa_property_get(c, HOWL_PROPERTY)))
return pa_howl_wrapper_ref(h);
return howl_wrapper_new(c);
}
pa_howl_wrapper* pa_howl_wrapper_ref(pa_howl_wrapper *h) {
assert(h && h->ref >= 1);
h->ref++;
return h;
}
void pa_howl_wrapper_unref(pa_howl_wrapper *h) {
assert(h && h->ref >= 1);
if (!(--h->ref))
howl_wrapper_free(h);
}
sw_discovery pa_howl_wrapper_get_discovery(pa_howl_wrapper *h) {
assert(h && h->ref >= 1);
return h->discovery;
}

37
src/polypcore/howl-wrap.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef foohowlwrapperhfoo
#define foohowlwrapperhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
polypaudio 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 Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <howl.h>
#include "core.h"
pa_howl_wrapper;
pa_howl_wrapper* pa_howl_wrapper_get(pa_core *c);
pa_howl_wrapper* pa_howl_wrapper_ref(pa_howl_wrapper *h);
void pa_howl_wrapper_unref(pa_howl_wrapper *h);
sw_discovery pa_howl_wrapper_get_discovery(pa_howl_wrapper *h);
#endif

390
src/polypcore/idxset.c Normal file
View file

@ -0,0 +1,390 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "idxset.h"
#include "xmalloc.h"
typedef struct idxset_entry {
void *data;
uint32_t index;
unsigned hash_value;
struct idxset_entry *hash_prev, *hash_next;
struct idxset_entry* iterate_prev, *iterate_next;
} idxset_entry;
struct pa_idxset {
unsigned (*hash_func) (const void *p);
int (*compare_func)(const void *a, const void *b);
unsigned hash_table_size, n_entries;
idxset_entry **hash_table, **array, *iterate_list_head, *iterate_list_tail;
uint32_t index, start_index, array_size;
};
unsigned pa_idxset_string_hash_func(const void *p) {
unsigned hash = 0;
const char *c;
for (c = p; *c; c++)
hash = 31 * hash + *c;
return hash;
}
int pa_idxset_string_compare_func(const void *a, const void *b) {
return strcmp(a, b);
}
unsigned pa_idxset_trivial_hash_func(const void *p) {
return (unsigned) p;
}
int pa_idxset_trivial_compare_func(const void *a, const void *b) {
return a != b;
}
pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) {
pa_idxset *s;
s = pa_xnew(pa_idxset, 1);
s->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
s->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
s->hash_table_size = 1023;
s->hash_table = pa_xmalloc0(sizeof(idxset_entry*)*s->hash_table_size);
s->array = NULL;
s->array_size = 0;
s->index = 0;
s->start_index = 0;
s->n_entries = 0;
s->iterate_list_head = s->iterate_list_tail = NULL;
return s;
}
void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata) {
assert(s);
while (s->iterate_list_head) {
idxset_entry *e = s->iterate_list_head;
s->iterate_list_head = s->iterate_list_head->iterate_next;
if (free_func)
free_func(e->data, userdata);
pa_xfree(e);
}
pa_xfree(s->hash_table);
pa_xfree(s->array);
pa_xfree(s);
}
static idxset_entry* hash_scan(pa_idxset *s, idxset_entry* e, const void *p) {
assert(p);
assert(s->compare_func);
for (; e; e = e->hash_next)
if (s->compare_func(e->data, p) == 0)
return e;
return NULL;
}
static void extend_array(pa_idxset *s, uint32_t idx) {
uint32_t i, j, l;
idxset_entry** n;
assert(idx >= s->start_index);
if (idx < s->start_index + s->array_size)
return;
for (i = 0; i < s->array_size; i++)
if (s->array[i])
break;
l = idx - s->start_index - i + 100;
n = pa_xnew0(idxset_entry*, l);
for (j = 0; j < s->array_size-i; j++)
n[j] = s->array[i+j];
pa_xfree(s->array);
s->array = n;
s->array_size = l;
s->start_index += i;
}
static idxset_entry** array_index(pa_idxset*s, uint32_t idx) {
if (idx >= s->start_index + s->array_size)
return NULL;
if (idx < s->start_index)
return NULL;
return s->array + (idx - s->start_index);
}
int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {
unsigned h;
idxset_entry *e, **a;
assert(s && p);
assert(s->hash_func);
h = s->hash_func(p) % s->hash_table_size;
assert(s->hash_table);
if ((e = hash_scan(s, s->hash_table[h], p))) {
if (idx)
*idx = e->index;
return -1;
}
e = pa_xmalloc(sizeof(idxset_entry));
e->data = p;
e->index = s->index++;
e->hash_value = h;
/* Insert into hash table */
e->hash_next = s->hash_table[h];
e->hash_prev = NULL;
if (s->hash_table[h])
s->hash_table[h]->hash_prev = e;
s->hash_table[h] = e;
/* Insert into array */
extend_array(s, e->index);
a = array_index(s, e->index);
assert(a && !*a);
*a = e;
/* Insert into linked list */
e->iterate_next = NULL;
e->iterate_prev = s->iterate_list_tail;
if (s->iterate_list_tail) {
assert(s->iterate_list_head);
s->iterate_list_tail->iterate_next = e;
} else {
assert(!s->iterate_list_head);
s->iterate_list_head = e;
}
s->iterate_list_tail = e;
s->n_entries++;
assert(s->n_entries >= 1);
if (idx)
*idx = e->index;
return 0;
}
void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) {
idxset_entry **a;
assert(s);
if (!(a = array_index(s, idx)))
return NULL;
if (!*a)
return NULL;
return (*a)->data;
}
void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) {
unsigned h;
idxset_entry *e;
assert(s && p);
assert(s->hash_func);
h = s->hash_func(p) % s->hash_table_size;
assert(s->hash_table);
if (!(e = hash_scan(s, s->hash_table[h], p)))
return NULL;
if (idx)
*idx = e->index;
return e->data;
}
static void remove_entry(pa_idxset *s, idxset_entry *e) {
idxset_entry **a;
assert(s && e);
/* Remove from array */
a = array_index(s, e->index);
assert(a && *a && *a == e);
*a = NULL;
/* Remove from linked list */
if (e->iterate_next)
e->iterate_next->iterate_prev = e->iterate_prev;
else
s->iterate_list_tail = e->iterate_prev;
if (e->iterate_prev)
e->iterate_prev->iterate_next = e->iterate_next;
else
s->iterate_list_head = e->iterate_next;
/* Remove from hash table */
if (e->hash_next)
e->hash_next->hash_prev = e->hash_prev;
if (e->hash_prev)
e->hash_prev->hash_next = e->hash_next;
else
s->hash_table[e->hash_value] = e->hash_next;
pa_xfree(e);
assert(s->n_entries >= 1);
s->n_entries--;
}
void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) {
idxset_entry **a;
void *data;
assert(s);
if (!(a = array_index(s, idx)))
return NULL;
data = (*a)->data;
remove_entry(s, *a);
return data;
}
void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) {
idxset_entry *e;
unsigned h;
void *r;
assert(s->hash_func);
h = s->hash_func(data) % s->hash_table_size;
assert(s->hash_table);
if (!(e = hash_scan(s, s->hash_table[h], data)))
return NULL;
r = e->data;
if (idx)
*idx = e->index;
remove_entry(s, e);
return r;
}
void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) {
idxset_entry **a, *e = NULL;
assert(s && idx);
if ((a = array_index(s, *idx)) && *a)
e = (*a)->iterate_next;
if (!e)
e = s->iterate_list_head;
if (!e)
return NULL;
*idx = e->index;
return e->data;
}
void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
assert(s);
if (!s->iterate_list_head)
return NULL;
if (idx)
*idx = s->iterate_list_head->index;
return s->iterate_list_head->data;
}
void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
idxset_entry **a, *e = NULL;
assert(s && idx);
if ((a = array_index(s, *idx)) && *a)
e = (*a)->iterate_next;
if (e) {
*idx = e->index;
return e->data;
} else {
*idx = PA_IDXSET_INVALID;
return NULL;
}
}
int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata) {
idxset_entry *e;
assert(s && func);
e = s->iterate_list_head;
while (e) {
int del = 0, r;
idxset_entry *n = e->iterate_next;
r = func(e->data, e->index, &del, userdata);
if (del)
remove_entry(s, e);
if (r < 0)
return r;
e = n;
}
return 0;
}
unsigned pa_idxset_size(pa_idxset*s) {
assert(s);
return s->n_entries;
}
int pa_idxset_isempty(pa_idxset *s) {
assert(s);
return s->n_entries == 0;
}

94
src/polypcore/idxset.h Normal file
View file

@ -0,0 +1,94 @@
#ifndef fooidxsethfoo
#define fooidxsethfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
/* A combination of a set and a dynamic array. Entries are indexable
* both through a numeric automatically generated index and the entry's
* data pointer. As usual, memory management is the user's job. */
/* A special index value denoting the invalid index. */
#define PA_IDXSET_INVALID ((uint32_t) -1)
/* Generic implementations for hash and comparison functions. Just
* compares the pointer or calculates the hash value directly from the
* pointer value. */
unsigned pa_idxset_trivial_hash_func(const void *p);
int pa_idxset_trivial_compare_func(const void *a, const void *b);
/* Generic implementations for hash and comparison functions for strings. */
unsigned pa_idxset_string_hash_func(const void *p);
int pa_idxset_string_compare_func(const void *a, const void *b);
typedef struct pa_idxset pa_idxset;
/* Instantiate a new idxset with the specified hash and comparison functions */
pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b));
/* Free the idxset. When the idxset is not empty the specified function is called for every entry contained */
void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata);
/* Store a new item in the idxset. The index of the item is returned in *idx */
int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx);
/* Get the entry by its idx */
void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx);
/* Get the entry by its data. The idx is returned in *index */
void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx);
/* Similar to pa_idxset_get_by_index(), but removes the entry from the idxset. */
void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx);
/* Similar to pa_idxset_get_by_data(), but removes the entry from the idxset */
void* pa_idxset_remove_by_data(pa_idxset*s, const void *p, uint32_t *idx);
/* This may be used to iterate through all entries. When called with
an invalid index value it returns the first entry, otherwise the
next following. The function is best called with *idx =
PA_IDXSET_VALID first. It is safe to manipulate the idxset between
the calls. It is not guaranteed that all entries have already been
returned before the an entry is returned the second time.*/
void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx);
/* Return the oldest entry in the idxset. Fill in its index in *idx. */
void* pa_idxset_first(pa_idxset *s, uint32_t *idx);
/* Return the entry following the entry indexed by *idx. After the
* call *index contains the index of the returned
* object. pa_idxset_first() and pa_idxset_next() may be used to
* iterate through the set.*/
void *pa_idxset_next(pa_idxset *s, uint32_t *idx);
/* Call a function for every item in the set. If the callback function
returns -1, the loop is terminated. If *del is set to non-zero that
specific item is removed. It is not safe to call any other
functions on the idxset while pa_idxset_foreach is executed. */
int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata);
unsigned pa_idxset_size(pa_idxset*s);
int pa_idxset_isempty(pa_idxset *s);
#endif

80
src/polypcore/inet_ntop.c Normal file
View file

@ -0,0 +1,80 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#ifndef HAVE_INET_NTOP
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include "winsock.h"
#include "inet_ntop.h"
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) {
struct in_addr *in = (struct in_addr*)src;
struct in6_addr *in6 = (struct in6_addr*)src;
assert(src && dst);
switch (af) {
case AF_INET:
snprintf(dst, cnt, "%d.%d.%d.%d",
#ifdef WORDS_BIGENDIAN
(int)(in->s_addr >> 24) & 0xff,
(int)(in->s_addr >> 16) & 0xff,
(int)(in->s_addr >> 8) & 0xff,
(int)(in->s_addr >> 0) & 0xff);
#else
(int)(in->s_addr >> 0) & 0xff,
(int)(in->s_addr >> 8) & 0xff,
(int)(in->s_addr >> 16) & 0xff,
(int)(in->s_addr >> 24) & 0xff);
#endif
break;
case AF_INET6:
snprintf(dst, cnt, "%x:%x:%x:%x:%x:%x:%x:%x",
in6->s6_addr[ 0] << 8 | in6->s6_addr[ 1],
in6->s6_addr[ 2] << 8 | in6->s6_addr[ 3],
in6->s6_addr[ 4] << 8 | in6->s6_addr[ 5],
in6->s6_addr[ 6] << 8 | in6->s6_addr[ 7],
in6->s6_addr[ 8] << 8 | in6->s6_addr[ 9],
in6->s6_addr[10] << 8 | in6->s6_addr[11],
in6->s6_addr[12] << 8 | in6->s6_addr[13],
in6->s6_addr[14] << 8 | in6->s6_addr[15]);
break;
default:
errno = EAFNOSUPPORT;
return NULL;
}
return dst;
}
#endif /* INET_NTOP */

12
src/polypcore/inet_ntop.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef fooinet_ntophfoo
#define fooinet_ntophfoo
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include "winsock.h"
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
#endif

292
src/polypcore/iochannel.c Normal file
View file

@ -0,0 +1,292 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include "winsock.h"
#include "iochannel.h"
#include "util.h"
#include "socket-util.h"
#include "xmalloc.h"
struct pa_iochannel {
int ifd, ofd;
pa_mainloop_api* mainloop;
pa_iochannel_callback_t callback;
void*userdata;
int readable;
int writable;
int hungup;
int no_close;
pa_io_event* input_event, *output_event;
};
static void enable_mainloop_sources(pa_iochannel *io) {
assert(io);
if (io->input_event == io->output_event && io->input_event) {
pa_io_event_flags_t f = PA_IO_EVENT_NULL;
assert(io->input_event);
if (!io->readable)
f |= PA_IO_EVENT_INPUT;
if (!io->writable)
f |= PA_IO_EVENT_OUTPUT;
io->mainloop->io_enable(io->input_event, f);
} else {
if (io->input_event)
io->mainloop->io_enable(io->input_event, io->readable ? PA_IO_EVENT_NULL : PA_IO_EVENT_INPUT);
if (io->output_event)
io->mainloop->io_enable(io->output_event, io->writable ? PA_IO_EVENT_NULL : PA_IO_EVENT_OUTPUT);
}
}
static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
pa_iochannel *io = userdata;
int changed = 0;
assert(m);
assert(e);
assert(fd >= 0);
assert(userdata);
if ((f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) && !io->hungup) {
io->hungup = 1;
changed = 1;
if (e == io->input_event) {
io->mainloop->io_free(io->input_event);
io->input_event = NULL;
if (io->output_event == e)
io->output_event = NULL;
} else if (e == io->output_event) {
io->mainloop->io_free(io->output_event);
io->output_event = NULL;
}
} else {
if ((f & PA_IO_EVENT_INPUT) && !io->readable) {
io->readable = 1;
changed = 1;
assert(e == io->input_event);
}
if ((f & PA_IO_EVENT_OUTPUT) && !io->writable) {
io->writable = 1;
changed = 1;
assert(e == io->output_event);
}
}
if (changed) {
enable_mainloop_sources(io);
if (io->callback)
io->callback(io, io->userdata);
}
}
pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) {
pa_iochannel *io;
assert(m);
assert(ifd >= 0 || ofd >= 0);
io = pa_xnew(pa_iochannel, 1);
io->ifd = ifd;
io->ofd = ofd;
io->mainloop = m;
io->userdata = NULL;
io->callback = NULL;
io->readable = 0;
io->writable = 0;
io->hungup = 0;
io->no_close = 0;
io->input_event = io->output_event = NULL;
if (ifd == ofd) {
assert(ifd >= 0);
pa_make_nonblock_fd(io->ifd);
io->input_event = io->output_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT|PA_IO_EVENT_OUTPUT, callback, io);
} else {
if (ifd >= 0) {
pa_make_nonblock_fd(io->ifd);
io->input_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT, callback, io);
}
if (ofd >= 0) {
pa_make_nonblock_fd(io->ofd);
io->output_event = m->io_new(m, ofd, PA_IO_EVENT_OUTPUT, callback, io);
}
}
return io;
}
void pa_iochannel_free(pa_iochannel*io) {
assert(io);
if (io->input_event)
io->mainloop->io_free(io->input_event);
if (io->output_event && (io->output_event != io->input_event))
io->mainloop->io_free(io->output_event);
if (!io->no_close) {
if (io->ifd >= 0)
close(io->ifd);
if (io->ofd >= 0 && io->ofd != io->ifd)
close(io->ofd);
}
pa_xfree(io);
}
int pa_iochannel_is_readable(pa_iochannel*io) {
assert(io);
return io->readable || io->hungup;
}
int pa_iochannel_is_writable(pa_iochannel*io) {
assert(io);
return io->writable && !io->hungup;
}
int pa_iochannel_is_hungup(pa_iochannel*io) {
assert(io);
return io->hungup;
}
ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) {
ssize_t r;
assert(io);
assert(data);
assert(l);
assert(io->ofd >= 0);
#ifdef OS_IS_WIN32
r = send(io->ofd, data, l, 0);
if (r < 0) {
if (WSAGetLastError() != WSAENOTSOCK) {
errno = WSAGetLastError();
return r;
}
}
if (r < 0)
#endif
r = write(io->ofd, data, l);
if (r >= 0) {
io->writable = 0;
enable_mainloop_sources(io);
}
return r;
}
ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) {
ssize_t r;
assert(io);
assert(data);
assert(io->ifd >= 0);
#ifdef OS_IS_WIN32
r = recv(io->ifd, data, l, 0);
if (r < 0) {
if (WSAGetLastError() != WSAENOTSOCK) {
errno = WSAGetLastError();
return r;
}
}
if (r < 0)
#endif
r = read(io->ifd, data, l);
if (r >= 0) {
io->readable = 0;
enable_mainloop_sources(io);
}
return r;
}
void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_callback_t _callback, void *userdata) {
assert(io);
io->callback = _callback;
io->userdata = userdata;
}
void pa_iochannel_set_noclose(pa_iochannel*io, int b) {
assert(io);
io->no_close = b;
}
void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l) {
assert(io);
assert(s);
assert(l);
pa_socket_peer_to_string(io->ifd, s, l);
}
int pa_iochannel_socket_set_rcvbuf(pa_iochannel *io, size_t l) {
assert(io);
return pa_socket_set_rcvbuf(io->ifd, l);
}
int pa_iochannel_socket_set_sndbuf(pa_iochannel *io, size_t l) {
assert(io);
return pa_socket_set_sndbuf(io->ofd, l);
}
pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io) {
assert(io);
return io->mainloop;
}

72
src/polypcore/iochannel.h Normal file
View file

@ -0,0 +1,72 @@
#ifndef fooiochannelhfoo
#define fooiochannelhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <sys/types.h>
#include <polyp/mainloop-api.h>
/* A wrapper around UNIX file descriptors for attaching them to the a
main event loop. Everytime new data may be read or be written to
the channel a callback function is called. It is safe to destroy
the calling iochannel object from the callback */
/* When pa_iochannel_is_readable() returns non-zero, the user has to
* call this function in a loop until it is no longer set or EOF
* reached. Otherwise strange things may happen when an EOF is
* reached. */
typedef struct pa_iochannel pa_iochannel;
/* Create a new IO channel for the specified file descriptors for
input resp. output. It is safe to pass the same file descriptor for
both parameters (in case of full-duplex channels). For a simplex
channel specify -1 for the other direction. */
pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd);
void pa_iochannel_free(pa_iochannel*io);
ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l);
ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l);
int pa_iochannel_is_readable(pa_iochannel*io);
int pa_iochannel_is_writable(pa_iochannel*io);
int pa_iochannel_is_hungup(pa_iochannel*io);
/* Don't close the file descirptors when the io channel is freed. By
* default the file descriptors are closed. */
void pa_iochannel_set_noclose(pa_iochannel*io, int b);
/* Set the callback function that is called whenever data becomes available for read or write */
typedef void (*pa_iochannel_callback_t)(pa_iochannel*io, void *userdata);
void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_callback_t callback, void *userdata);
/* In case the file descriptor is a socket, return a pretty-printed string in *s which describes the peer connected */
void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l);
/* Use setsockopt() to tune the recieve and send buffers of TCP sockets */
int pa_iochannel_socket_set_rcvbuf(pa_iochannel*io, size_t l);
int pa_iochannel_socket_set_sndbuf(pa_iochannel*io, size_t l);
pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io);
#endif

368
src/polypcore/ioline.c Normal file
View file

@ -0,0 +1,368 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "ioline.h"
#include "xmalloc.h"
#include "log.h"
#define BUFFER_LIMIT (64*1024)
#define READ_SIZE (1024)
struct pa_ioline {
pa_iochannel *io;
pa_defer_event *defer_event;
pa_mainloop_api *mainloop;
int ref;
int dead;
char *wbuf;
size_t wbuf_length, wbuf_index, wbuf_valid_length;
char *rbuf;
size_t rbuf_length, rbuf_index, rbuf_valid_length;
void (*callback)(pa_ioline*io, const char *s, void *userdata);
void *userdata;
int defer_close;
};
static void io_callback(pa_iochannel*io, void *userdata);
static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata);
pa_ioline* pa_ioline_new(pa_iochannel *io) {
pa_ioline *l;
assert(io);
l = pa_xmalloc(sizeof(pa_ioline));
l->io = io;
l->dead = 0;
l->wbuf = NULL;
l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0;
l->rbuf = NULL;
l->rbuf_length = l->rbuf_index = l->rbuf_valid_length = 0;
l->callback = NULL;
l->userdata = NULL;
l->ref = 1;
l->mainloop = pa_iochannel_get_mainloop_api(io);
l->defer_event = l->mainloop->defer_new(l->mainloop, defer_callback, l);
l->mainloop->defer_enable(l->defer_event, 0);
l->defer_close = 0;
pa_iochannel_set_callback(io, io_callback, l);
return l;
}
static void ioline_free(pa_ioline *l) {
assert(l);
if (l->io)
pa_iochannel_free(l->io);
if (l->defer_event)
l->mainloop->defer_free(l->defer_event);
pa_xfree(l->wbuf);
pa_xfree(l->rbuf);
pa_xfree(l);
}
void pa_ioline_unref(pa_ioline *l) {
assert(l && l->ref >= 1);
if ((--l->ref) <= 0)
ioline_free(l);
}
pa_ioline* pa_ioline_ref(pa_ioline *l) {
assert(l && l->ref >= 1);
l->ref++;
return l;
}
void pa_ioline_close(pa_ioline *l) {
assert(l && l->ref >= 1);
l->dead = 1;
if (l->io) {
pa_iochannel_free(l->io);
l->io = NULL;
}
if (l->defer_event) {
l->mainloop->defer_free(l->defer_event);
l->defer_event = NULL;
}
}
void pa_ioline_puts(pa_ioline *l, const char *c) {
size_t len;
assert(l && c && l->ref >= 1 && !l->dead);
pa_ioline_ref(l);
len = strlen(c);
if (len > BUFFER_LIMIT - l->wbuf_valid_length)
len = BUFFER_LIMIT - l->wbuf_valid_length;
if (len) {
assert(l->wbuf_length >= l->wbuf_valid_length);
/* In case the allocated buffer is too small, enlarge it. */
if (l->wbuf_valid_length + len > l->wbuf_length) {
size_t n = l->wbuf_valid_length+len;
char *new = pa_xmalloc(n);
if (l->wbuf) {
memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
pa_xfree(l->wbuf);
}
l->wbuf = new;
l->wbuf_length = n;
l->wbuf_index = 0;
} else if (l->wbuf_index + l->wbuf_valid_length + len > l->wbuf_length) {
/* In case the allocated buffer fits, but the current index is too far from the start, move it to the front. */
memmove(l->wbuf, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
l->wbuf_index = 0;
}
assert(l->wbuf_index + l->wbuf_valid_length + len <= l->wbuf_length);
/* Append the new string */
memcpy(l->wbuf + l->wbuf_index + l->wbuf_valid_length, c, len);
l->wbuf_valid_length += len;
l->mainloop->defer_enable(l->defer_event, 1);
}
pa_ioline_unref(l);
}
void pa_ioline_set_callback(pa_ioline*l, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata) {
assert(l && l->ref >= 1);
l->callback = callback;
l->userdata = userdata;
}
static void failure(pa_ioline *l) {
assert(l && l->ref >= 1 && !l->dead);
pa_ioline_close(l);
if (l->callback) {
l->callback(l, NULL, l->userdata);
l->callback = NULL;
}
}
static void scan_for_lines(pa_ioline *l, size_t skip) {
assert(l && l->ref >= 1 && skip < l->rbuf_valid_length);
while (!l->dead && l->rbuf_valid_length > skip) {
char *e, *p;
size_t m;
if (!(e = memchr(l->rbuf + l->rbuf_index + skip, '\n', l->rbuf_valid_length - skip)))
break;
*e = 0;
p = l->rbuf + l->rbuf_index;
m = strlen(p);
l->rbuf_index += m+1;
l->rbuf_valid_length -= m+1;
/* A shortcut for the next time */
if (l->rbuf_valid_length == 0)
l->rbuf_index = 0;
if (l->callback)
l->callback(l, p, l->userdata);
skip = 0;
}
/* If the buffer became too large and still no newline was found, drop it. */
if (l->rbuf_valid_length >= BUFFER_LIMIT)
l->rbuf_index = l->rbuf_valid_length = 0;
}
static int do_write(pa_ioline *l);
static int do_read(pa_ioline *l) {
assert(l && l->ref >= 1);
while (!l->dead && pa_iochannel_is_readable(l->io)) {
ssize_t r;
size_t len;
len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
/* Check if we have to enlarge the read buffer */
if (len < READ_SIZE) {
size_t n = l->rbuf_valid_length+READ_SIZE;
if (n >= BUFFER_LIMIT)
n = BUFFER_LIMIT;
if (l->rbuf_length >= n) {
/* The current buffer is large enough, let's just move the data to the front */
if (l->rbuf_valid_length)
memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
} else {
/* Enlarge the buffer */
char *new = pa_xmalloc(n);
if (l->rbuf_valid_length)
memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
pa_xfree(l->rbuf);
l->rbuf = new;
l->rbuf_length = n;
}
l->rbuf_index = 0;
}
len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
assert(len >= READ_SIZE);
/* Read some data */
r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len);
if (r == 0) {
/* Got an EOF, so fake an exit command. */
l->rbuf_index = 0;
snprintf (l->rbuf, l->rbuf_length, "exit\n");
r = 5;
pa_ioline_puts(l, "\nExiting.\n");
do_write(l);
} else if (r < 0) {
pa_log(__FILE__": read() failed: %s\n", strerror(errno));
failure(l);
return -1;
}
l->rbuf_valid_length += r;
/* Look if a line has been terminated in the newly read data */
scan_for_lines(l, l->rbuf_valid_length - r);
}
return 0;
}
/* Try to flush the buffer */
static int do_write(pa_ioline *l) {
ssize_t r;
assert(l && l->ref >= 1);
while (!l->dead && pa_iochannel_is_writable(l->io) && l->wbuf_valid_length) {
if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0) {
pa_log(__FILE__": write() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
failure(l);
return -1;
}
l->wbuf_index += r;
l->wbuf_valid_length -= r;
/* A shortcut for the next time */
if (l->wbuf_valid_length == 0)
l->wbuf_index = 0;
}
return 0;
}
/* Try to flush read/write data */
static void do_work(pa_ioline *l) {
assert(l && l->ref >= 1);
pa_ioline_ref(l);
l->mainloop->defer_enable(l->defer_event, 0);
if (!l->dead)
do_write(l);
if (!l->dead)
do_read(l);
if (l->defer_close && !l->wbuf_valid_length)
failure(l);
pa_ioline_unref(l);
}
static void io_callback(pa_iochannel*io, void *userdata) {
pa_ioline *l = userdata;
assert(io && l && l->ref >= 1);
do_work(l);
}
static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata) {
pa_ioline *l = userdata;
assert(l && l->ref >= 1 && l->mainloop == m && l->defer_event == e);
do_work(l);
}
void pa_ioline_defer_close(pa_ioline *l) {
assert(l);
l->defer_close = 1;
if (!l->wbuf_valid_length)
l->mainloop->defer_enable(l->defer_event, 1);
}
void pa_ioline_printf(pa_ioline *s, const char *format, ...) {
char *t;
va_list ap;
va_start(ap, format);
t = pa_vsprintf_malloc(format, ap);
va_end(ap);
pa_ioline_puts(s, t);
pa_xfree(t);
}

51
src/polypcore/ioline.h Normal file
View file

@ -0,0 +1,51 @@
#ifndef fooiolinehfoo
#define fooiolinehfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "iochannel.h"
#include "util.h"
/* An ioline wraps an iochannel for line based communication. A
* callback function is called whenever a new line has been recieved
* from the client */
typedef struct pa_ioline pa_ioline;
pa_ioline* pa_ioline_new(pa_iochannel *io);
void pa_ioline_unref(pa_ioline *l);
pa_ioline* pa_ioline_ref(pa_ioline *l);
void pa_ioline_close(pa_ioline *l);
/* Write a string to the channel */
void pa_ioline_puts(pa_ioline *s, const char *c);
/* Write a string to the channel */
void pa_ioline_printf(pa_ioline *s, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
/* Set the callback function that is called for every recieved line */
void pa_ioline_set_callback(pa_ioline*io, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata);
/* Make sure to close the ioline object as soon as the send buffer is emptied */
void pa_ioline_defer_close(pa_ioline *io);
#endif

69
src/polypcore/llist.h Normal file
View file

@ -0,0 +1,69 @@
#ifndef foollistfoo
#define foollistfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
polypaudio 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 Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* Some macros for maintaining doubly linked lists */
/* The head of the linked list. Use this in the structure that shall
* contain the head of the linked list */
#define PA_LLIST_HEAD(t,name) t *name
/* The pointers in the linked list's items. Use this in the item structure */
#define PA_LLIST_FIELDS(t) t *next, *prev
/* Initialize the list's head */
#define PA_LLIST_HEAD_INIT(t,item) do { (item) = NULL; } while(0)
/* Initialize a list item */
#define PA_LLIST_INIT(t,item) do { \
t *_item = (item); \
assert(_item); \
_item->prev = _item->next = NULL; \
} while(0)
/* Prepend an item to the list */
#define PA_LLIST_PREPEND(t,head,item) do { \
t **_head = &(head), *_item = (item); \
assert(_item); \
if ((_item->next = *_head)) \
_item->next->prev = _item; \
_item->prev = NULL; \
*_head = _item; \
} while (0)
/* Remove an item from the list */
#define PA_LLIST_REMOVE(t,head,item) do { \
t **_head = &(head), *_item = (item); \
assert(_item); \
if (_item->next) \
_item->next->prev = _item->prev; \
if (_item->prev) \
_item->prev->next = _item->next; \
else {\
assert(*_head == _item); \
*_head = _item->next; \
} \
_item->next = _item->prev = NULL; \
} while(0)
#endif

150
src/polypcore/log.c Normal file
View file

@ -0,0 +1,150 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
polypaudio 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 Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif
#include "log.h"
#include "xmalloc.h"
#include "util.h"
#define ENV_LOGLEVEL "POLYP_LOG"
static char *log_ident = NULL;
static pa_log_target_t log_target = PA_LOG_STDERR;
static void (*user_log_func)(pa_log_level_t l, const char *s) = NULL;
static pa_log_level_t maximal_level = PA_LOG_NOTICE;
#ifdef HAVE_SYSLOG_H
static const int level_to_syslog[] = {
[PA_LOG_ERROR] = LOG_ERR,
[PA_LOG_WARN] = LOG_WARNING,
[PA_LOG_NOTICE] = LOG_NOTICE,
[PA_LOG_INFO] = LOG_INFO,
[PA_LOG_DEBUG] = LOG_DEBUG
};
#endif
void pa_log_set_ident(const char *p) {
if (log_ident)
pa_xfree(log_ident);
log_ident = pa_xstrdup(p);
}
void pa_log_set_maximal_level(pa_log_level_t l) {
assert(l < PA_LOG_LEVEL_MAX);
maximal_level = l;
}
void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t l, const char*s)) {
assert(t == PA_LOG_USER || !func);
log_target = t;
user_log_func = func;
}
void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) {
const char *e;
assert(level < PA_LOG_LEVEL_MAX);
if ((e = getenv(ENV_LOGLEVEL)))
maximal_level = atoi(e);
if (level > maximal_level)
return;
switch (log_target) {
case PA_LOG_STDERR:
vfprintf(stderr, format, ap);
break;
#ifdef HAVE_SYSLOG_H
case PA_LOG_SYSLOG:
openlog(log_ident ? log_ident : "???", LOG_PID, LOG_USER);
vsyslog(level_to_syslog[level], format, ap);
closelog();
break;
#endif
case PA_LOG_USER: {
char *t = pa_vsprintf_malloc(format, ap);
assert(user_log_func);
user_log_func(level, t);
pa_xfree(t);
}
case PA_LOG_NULL:
default:
break;
}
}
void pa_log_level(pa_log_level_t level, const char *format, ...) {
va_list ap;
va_start(ap, format);
pa_log_levelv(level, format, ap);
va_end(ap);
}
void pa_log_debug(const char *format, ...) {
va_list ap;
va_start(ap, format);
pa_log_levelv(PA_LOG_DEBUG, format, ap);
va_end(ap);
}
void pa_log_info(const char *format, ...) {
va_list ap;
va_start(ap, format);
pa_log_levelv(PA_LOG_INFO, format, ap);
va_end(ap);
}
void pa_log_notice(const char *format, ...) {
va_list ap;
va_start(ap, format);
pa_log_levelv(PA_LOG_INFO, format, ap);
va_end(ap);
}
void pa_log_warn(const char *format, ...) {
va_list ap;
va_start(ap, format);
pa_log_levelv(PA_LOG_WARN, format, ap);
va_end(ap);
}
void pa_log_error(const char *format, ...) {
va_list ap;
va_start(ap, format);
pa_log_levelv(PA_LOG_ERROR, format, ap);
va_end(ap);
}

69
src/polypcore/log.h Normal file
View file

@ -0,0 +1,69 @@
#ifndef foologhfoo
#define foologhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <stdarg.h>
#include "gccmacro.h"
/* A simple logging subsystem */
/* Where to log to */
typedef enum pa_log_target {
PA_LOG_STDERR, /* default */
PA_LOG_SYSLOG,
PA_LOG_USER, /* to user specified function */
PA_LOG_NULL /* to /dev/null */
} pa_log_target_t;
typedef enum pa_log_level {
PA_LOG_ERROR = 0, /* Error messages */
PA_LOG_WARN = 1, /* Warning messages */
PA_LOG_NOTICE = 2, /* Notice messages */
PA_LOG_INFO = 3, /* Info messages */
PA_LOG_DEBUG = 4, /* debug message */
PA_LOG_LEVEL_MAX
} pa_log_level_t;
/* Set an identification for the current daemon. Used when logging to syslog. */
void pa_log_set_ident(const char *p);
/* Set another log target. If t is PA_LOG_USER you may specify a function that is called every log string */
void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t t, const char*s));
/* Minimal log level */
void pa_log_set_maximal_level(pa_log_level_t l);
/* Do a log line */
void pa_log_debug(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
void pa_log_info(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
void pa_log_notice(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
void pa_log_warn(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
void pa_log_error(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
void pa_log_level(pa_log_level_t level, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap);
#define pa_log pa_log_error
#endif

188
src/polypcore/mcalign.c Normal file
View file

@ -0,0 +1,188 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "mcalign.h"
#include "xmalloc.h"
struct pa_mcalign {
size_t base;
pa_memchunk leftover, current;
pa_memblock_stat *memblock_stat;
};
pa_mcalign *pa_mcalign_new(size_t base, pa_memblock_stat *s) {
pa_mcalign *m;
assert(base);
m = pa_xnew(pa_mcalign, 1);
m->base = base;
pa_memchunk_reset(&m->leftover);
pa_memchunk_reset(&m->current);
m->memblock_stat = s;
return m;
}
void pa_mcalign_free(pa_mcalign *m) {
assert(m);
if (m->leftover.memblock)
pa_memblock_unref(m->leftover.memblock);
if (m->current.memblock)
pa_memblock_unref(m->current.memblock);
pa_xfree(m);
}
void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) {
assert(m && c && c->memblock && c->length);
/* Append to the leftover memory block */
if (m->leftover.memblock) {
assert(!m->current.memblock);
/* Try to merge */
if (m->leftover.memblock == c->memblock &&
m->leftover.index + m->leftover.length == c->index) {
/* Merge */
m->leftover.length += c->length;
/* If the new chunk is larger than m->base, move it to current */
if (m->leftover.length >= m->base) {
m->current = m->leftover;
pa_memchunk_reset(&m->leftover);
}
} else {
size_t l;
/* We have to copy */
assert(m->leftover.length < m->base);
l = m->base - m->leftover.length;
if (l > c->length)
l = c->length;
/* Can we use the current block? */
pa_memchunk_make_writable(&m->leftover, m->memblock_stat, m->base);
memcpy((uint8_t*) m->leftover.memblock->data + m->leftover.index + m->leftover.length, (uint8_t*) c->memblock->data + c->index, l);
m->leftover.length += l;
assert(m->leftover.length <= m->base && m->leftover.length <= m->leftover.memblock->length);
if (c->length > l) {
/* Save the remainder of the memory block */
m->current = *c;
m->current.index += l;
m->current.length -= l;
pa_memblock_ref(m->current.memblock);
}
}
} else {
assert(!m->leftover.memblock && !m->current.memblock);
/* Nothing to merge or copy, just store it */
if (c->length >= m->base)
m->current = *c;
else
m->leftover = *c;
pa_memblock_ref(c->memblock);
}
}
int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {
assert(m && c);
/* First test if there's a leftover memory block available */
if (m->leftover.memblock) {
assert(m->leftover.length > 0 && m->leftover.length <= m->base);
/* The leftover memory block is not yet complete */
if (m->leftover.length < m->base)
return -1;
/* Return the leftover memory block */
*c = m->leftover;
pa_memchunk_reset(&m->leftover);
/* If the current memblock is too small move it the leftover */
if (m->current.memblock && m->current.length < m->base) {
m->leftover = m->current;
pa_memchunk_reset(&m->current);
}
return 0;
}
/* Now let's see if there is other data available */
if (m->current.memblock) {
size_t l;
assert(m->current.length >= m->base);
/* The length of the returned memory block */
l = m->current.length;
l /= m->base;
l *= m->base;
assert(l > 0);
/* Prepare the returned block */
*c = m->current;
pa_memblock_ref(c->memblock);
c->length = l;
/* Drop that from the current memory block */
assert(l <= m->current.length);
m->current.index += l;
m->current.length -= l;
/* In case the whole block was dropped ... */
if (m->current.length == 0)
pa_memblock_unref(m->current.memblock);
else {
/* Move the raimainder to leftover */
assert(m->current.length < m->base && !m->leftover.memblock);
m->leftover = m->current;
}
pa_memchunk_reset(&m->current);
return 0;
}
/* There's simply nothing */
return -1;
}

77
src/polypcore/mcalign.h Normal file
View file

@ -0,0 +1,77 @@
#ifndef foomcalignhfoo
#define foomcalignhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "memblock.h"
#include "memchunk.h"
/* An alignment object, used for aligning memchunks to multiples of
* the frame size. */
/* Method of operation: the user creates a new mcalign object by
* calling pa_mcalign_new() with the appropriate aligning
* granularity. After that he may call pa_mcalign_push() for an input
* memchunk. After exactly one memchunk the user has to call
* pa_mcalign_pop() until it returns -1. If pa_mcalign_pop() returns
* 0, the memchunk *c is valid and aligned to the granularity. Some
* pseudocode illustrating this:
*
* pa_mcalign *a = pa_mcalign_new(4, NULL);
*
* for (;;) {
* pa_memchunk input;
*
* ... fill input ...
*
* pa_mcalign_push(m, &input);
* pa_memblock_unref(input.memblock);
*
* for (;;) {
* pa_memchunk output;
*
* if (pa_mcalign_pop(m, &output) < 0)
* break;
*
* ... consume output ...
*
* pa_memblock_unref(output.memblock);
* }
* }
*
* pa_memchunk_free(a);
* */
typedef struct pa_mcalign pa_mcalign;
pa_mcalign *pa_mcalign_new(size_t base, pa_memblock_stat *s);
void pa_mcalign_free(pa_mcalign *m);
/* Push a new memchunk into the aligner. The caller of this routine
* has to free the memchunk by himself. */
void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c);
/* Pop a new memchunk from the aligner. Returns 0 when sucessful,
* nonzero otherwise. */
int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c);
#endif

169
src/polypcore/memblock.c Normal file
View file

@ -0,0 +1,169 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "memblock.h"
#include "xmalloc.h"
static void stat_add(pa_memblock*m, pa_memblock_stat *s) {
assert(m);
if (!s) {
m->stat = NULL;
return;
}
m->stat = pa_memblock_stat_ref(s);
s->total++;
s->allocated++;
s->total_size += m->length;
s->allocated_size += m->length;
}
static void stat_remove(pa_memblock *m) {
assert(m);
if (!m->stat)
return;
m->stat->total--;
m->stat->total_size -= m->length;
pa_memblock_stat_unref(m->stat);
m->stat = NULL;
}
pa_memblock *pa_memblock_new(size_t length, pa_memblock_stat*s) {
pa_memblock *b = pa_xmalloc(sizeof(pa_memblock)+length);
b->type = PA_MEMBLOCK_APPENDED;
b->ref = 1;
b->length = length;
b->data = b+1;
b->free_cb = NULL;
b->read_only = 0;
stat_add(b, s);
return b;
}
pa_memblock *pa_memblock_new_dynamic(void *d, size_t length, pa_memblock_stat*s) {
pa_memblock *b = pa_xmalloc(sizeof(pa_memblock));
b->type = PA_MEMBLOCK_DYNAMIC;
b->ref = 1;
b->length = length;
b->data = d;
b->free_cb = NULL;
b->read_only = 0;
stat_add(b, s);
return b;
}
pa_memblock *pa_memblock_new_fixed(void *d, size_t length, int read_only, pa_memblock_stat*s) {
pa_memblock *b = pa_xmalloc(sizeof(pa_memblock));
b->type = PA_MEMBLOCK_FIXED;
b->ref = 1;
b->length = length;
b->data = d;
b->free_cb = NULL;
b->read_only = read_only;
stat_add(b, s);
return b;
}
pa_memblock *pa_memblock_new_user(void *d, size_t length, void (*free_cb)(void *p), int read_only, pa_memblock_stat*s) {
pa_memblock *b;
assert(d && length && free_cb);
b = pa_xmalloc(sizeof(pa_memblock));
b->type = PA_MEMBLOCK_USER;
b->ref = 1;
b->length = length;
b->data = d;
b->free_cb = free_cb;
b->read_only = read_only;
stat_add(b, s);
return b;
}
pa_memblock* pa_memblock_ref(pa_memblock*b) {
assert(b && b->ref >= 1);
b->ref++;
return b;
}
void pa_memblock_unref(pa_memblock*b) {
assert(b && b->ref >= 1);
if ((--(b->ref)) == 0) {
stat_remove(b);
if (b->type == PA_MEMBLOCK_USER) {
assert(b->free_cb);
b->free_cb(b->data);
} else if (b->type == PA_MEMBLOCK_DYNAMIC)
pa_xfree(b->data);
pa_xfree(b);
}
}
void pa_memblock_unref_fixed(pa_memblock *b) {
assert(b && b->ref >= 1 && b->type == PA_MEMBLOCK_FIXED);
if (b->ref == 1)
pa_memblock_unref(b);
else {
b->data = pa_xmemdup(b->data, b->length);
b->type = PA_MEMBLOCK_DYNAMIC;
b->ref--;
}
}
pa_memblock_stat* pa_memblock_stat_new(void) {
pa_memblock_stat *s;
s = pa_xmalloc(sizeof(pa_memblock_stat));
s->ref = 1;
s->total = s->total_size = s->allocated = s->allocated_size = 0;
return s;
}
void pa_memblock_stat_unref(pa_memblock_stat *s) {
assert(s && s->ref >= 1);
if (!(--(s->ref))) {
assert(!s->total);
pa_xfree(s);
}
}
pa_memblock_stat * pa_memblock_stat_ref(pa_memblock_stat *s) {
assert(s);
s->ref++;
return s;
}

87
src/polypcore/memblock.h Normal file
View file

@ -0,0 +1,87 @@
#ifndef foomemblockhfoo
#define foomemblockhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <sys/types.h>
#include <inttypes.h>
/* A pa_memblock is a reference counted memory block. Polypaudio
* passed references to pa_memblocks around instead of copying
* data. See pa_memchunk for a structure that describes parts of
* memory blocks. */
/* The type of memory this block points to */
typedef enum pa_memblock_type {
PA_MEMBLOCK_FIXED, /* data is a pointer to fixed memory that needs not to be freed */
PA_MEMBLOCK_APPENDED, /* The most common kind: the data is appended to the memory block */
PA_MEMBLOCK_DYNAMIC, /* data is a pointer to some memory allocated with pa_xmalloc() */
PA_MEMBLOCK_USER /* User supplied memory, to be freed with free_cb */
} pa_memblock_type_t;
/* A structure of keeping memory block statistics */
/* Maintains statistics about memory blocks */
typedef struct pa_memblock_stat {
int ref;
unsigned total;
unsigned total_size;
unsigned allocated;
unsigned allocated_size;
} pa_memblock_stat;
typedef struct pa_memblock {
pa_memblock_type_t type;
unsigned ref; /* the reference counter */
int read_only; /* boolean */
size_t length;
void *data;
void (*free_cb)(void *p); /* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */
pa_memblock_stat *stat;
} pa_memblock;
/* Allocate a new memory block of type PA_MEMBLOCK_APPENDED */
pa_memblock *pa_memblock_new(size_t length, pa_memblock_stat*s);
/* Allocate a new memory block of type PA_MEMBLOCK_DYNAMIC. The pointer data is to be maintained be the memory block */
pa_memblock *pa_memblock_new_dynamic(void *data, size_t length, pa_memblock_stat*s);
/* Allocate a new memory block of type PA_MEMBLOCK_FIXED */
pa_memblock *pa_memblock_new_fixed(void *data, size_t length, int read_only, pa_memblock_stat*s);
/* Allocate a new memory block of type PA_MEMBLOCK_USER */
pa_memblock *pa_memblock_new_user(void *data, size_t length, void (*free_cb)(void *p), int read_only, pa_memblock_stat*s);
void pa_memblock_unref(pa_memblock*b);
pa_memblock* pa_memblock_ref(pa_memblock*b);
/* This special unref function has to be called by the owner of the
memory of a static memory block when he wants to release all
references to the memory. This causes the memory to be copied and
converted into a PA_MEMBLOCK_DYNAMIC type memory block */
void pa_memblock_unref_fixed(pa_memblock*b);
pa_memblock_stat* pa_memblock_stat_new(void);
void pa_memblock_stat_unref(pa_memblock_stat *s);
pa_memblock_stat * pa_memblock_stat_ref(pa_memblock_stat *s);
#endif

343
src/polypcore/memblockq.c Normal file
View file

@ -0,0 +1,343 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "memblockq.h"
#include "xmalloc.h"
#include "log.h"
#include "mcalign.h"
struct memblock_list {
struct memblock_list *next, *prev;
pa_memchunk chunk;
};
struct pa_memblockq {
struct memblock_list *blocks, *blocks_tail;
unsigned n_blocks;
size_t current_length, maxlength, tlength, base, prebuf, orig_prebuf, minreq;
pa_mcalign *mcalign;
pa_memblock_stat *memblock_stat;
};
pa_memblockq* pa_memblockq_new(size_t maxlength, size_t tlength, size_t base, size_t prebuf, size_t minreq, pa_memblock_stat *s) {
pa_memblockq* bq;
assert(maxlength && base && maxlength);
bq = pa_xmalloc(sizeof(pa_memblockq));
bq->blocks = bq->blocks_tail = 0;
bq->n_blocks = 0;
bq->current_length = 0;
pa_log_debug(__FILE__": memblockq requested: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", maxlength, tlength, base, prebuf, minreq);
bq->base = base;
bq->maxlength = ((maxlength+base-1)/base)*base;
assert(bq->maxlength >= base);
bq->tlength = ((tlength+base-1)/base)*base;
if (!bq->tlength || bq->tlength >= bq->maxlength)
bq->tlength = bq->maxlength;
bq->minreq = (minreq/base)*base;
if (bq->minreq == 0)
bq->minreq = 1;
bq->prebuf = (prebuf == (size_t) -1) ? bq->maxlength/2 : prebuf;
bq->prebuf = (bq->prebuf/base)*base;
if (bq->prebuf > bq->maxlength)
bq->prebuf = bq->maxlength;
if (bq->prebuf > bq->tlength - bq->minreq)
bq->prebuf = bq->tlength - bq->minreq;
bq->orig_prebuf = bq->prebuf;
pa_log_debug(__FILE__": memblockq sanitized: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", bq->maxlength, bq->tlength, bq->base, bq->prebuf, bq->minreq);
bq->mcalign = NULL;
bq->memblock_stat = s;
return bq;
}
void pa_memblockq_free(pa_memblockq* bq) {
assert(bq);
pa_memblockq_flush(bq);
if (bq->mcalign)
pa_mcalign_free(bq->mcalign);
pa_xfree(bq);
}
void pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta) {
struct memblock_list *q;
assert(bq && chunk && chunk->memblock && chunk->length && (chunk->length % bq->base) == 0);
pa_memblockq_seek(bq, delta);
if (bq->blocks_tail && bq->blocks_tail->chunk.memblock == chunk->memblock) {
/* Try to merge memory chunks */
if (bq->blocks_tail->chunk.index+bq->blocks_tail->chunk.length == chunk->index) {
bq->blocks_tail->chunk.length += chunk->length;
bq->current_length += chunk->length;
return;
}
}
q = pa_xmalloc(sizeof(struct memblock_list));
q->chunk = *chunk;
pa_memblock_ref(q->chunk.memblock);
assert(q->chunk.index+q->chunk.length <= q->chunk.memblock->length);
q->next = NULL;
if ((q->prev = bq->blocks_tail))
bq->blocks_tail->next = q;
else
bq->blocks = q;
bq->blocks_tail = q;
bq->n_blocks++;
bq->current_length += chunk->length;
pa_memblockq_shorten(bq, bq->maxlength);
}
int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
assert(bq && chunk);
if (!bq->blocks || bq->current_length < bq->prebuf)
return -1;
bq->prebuf = 0;
*chunk = bq->blocks->chunk;
pa_memblock_ref(chunk->memblock);
return 0;
}
void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length) {
assert(bq && chunk && length);
if (!bq->blocks || memcmp(&bq->blocks->chunk, chunk, sizeof(pa_memchunk)))
return;
assert(length <= bq->blocks->chunk.length);
pa_memblockq_skip(bq, length);
}
static void remove_block(pa_memblockq *bq, struct memblock_list *q) {
assert(bq && q);
if (q->prev)
q->prev->next = q->next;
else {
assert(bq->blocks == q);
bq->blocks = q->next;
}
if (q->next)
q->next->prev = q->prev;
else {
assert(bq->blocks_tail == q);
bq->blocks_tail = q->prev;
}
pa_memblock_unref(q->chunk.memblock);
pa_xfree(q);
bq->n_blocks--;
}
void pa_memblockq_skip(pa_memblockq *bq, size_t length) {
assert(bq && length && (length % bq->base) == 0);
while (length > 0) {
size_t l = length;
assert(bq->blocks && bq->current_length >= length);
if (l > bq->blocks->chunk.length)
l = bq->blocks->chunk.length;
bq->blocks->chunk.index += l;
bq->blocks->chunk.length -= l;
bq->current_length -= l;
if (!bq->blocks->chunk.length)
remove_block(bq, bq->blocks);
length -= l;
}
}
void pa_memblockq_shorten(pa_memblockq *bq, size_t length) {
size_t l;
assert(bq);
if (bq->current_length <= length)
return;
/*pa_log(__FILE__": Warning! pa_memblockq_shorten()\n");*/
l = bq->current_length - length;
l /= bq->base;
l *= bq->base;
pa_memblockq_skip(bq, l);
}
void pa_memblockq_empty(pa_memblockq *bq) {
assert(bq);
pa_memblockq_shorten(bq, 0);
}
int pa_memblockq_is_readable(pa_memblockq *bq) {
assert(bq);
return bq->current_length && (bq->current_length >= bq->prebuf);
}
int pa_memblockq_is_writable(pa_memblockq *bq, size_t length) {
assert(bq);
return bq->current_length + length <= bq->tlength;
}
uint32_t pa_memblockq_get_length(pa_memblockq *bq) {
assert(bq);
return bq->current_length;
}
uint32_t pa_memblockq_missing(pa_memblockq *bq) {
size_t l;
assert(bq);
if (bq->current_length >= bq->tlength)
return 0;
l = bq->tlength - bq->current_length;
assert(l);
return (l >= bq->minreq) ? l : 0;
}
void pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta) {
pa_memchunk rchunk;
assert(bq && chunk && bq->base);
if (bq->base == 1) {
pa_memblockq_push(bq, chunk, delta);
return;
}
if (!bq->mcalign) {
bq->mcalign = pa_mcalign_new(bq->base, bq->memblock_stat);
assert(bq->mcalign);
}
pa_mcalign_push(bq->mcalign, chunk);
while (pa_mcalign_pop(bq->mcalign, &rchunk) >= 0) {
pa_memblockq_push(bq, &rchunk, delta);
pa_memblock_unref(rchunk.memblock);
delta = 0;
}
}
uint32_t pa_memblockq_get_minreq(pa_memblockq *bq) {
assert(bq);
return bq->minreq;
}
void pa_memblockq_prebuf_disable(pa_memblockq *bq) {
assert(bq);
bq->prebuf = 0;
}
void pa_memblockq_prebuf_reenable(pa_memblockq *bq) {
assert(bq);
bq->prebuf = bq->orig_prebuf;
}
void pa_memblockq_seek(pa_memblockq *bq, size_t length) {
assert(bq);
if (!length)
return;
while (length >= bq->base) {
size_t l = length;
if (!bq->current_length)
return;
assert(bq->blocks_tail);
if (l > bq->blocks_tail->chunk.length)
l = bq->blocks_tail->chunk.length;
bq->blocks_tail->chunk.length -= l;
bq->current_length -= l;
if (bq->blocks_tail->chunk.length == 0)
remove_block(bq, bq->blocks);
length -= l;
}
}
void pa_memblockq_flush(pa_memblockq *bq) {
struct memblock_list *l;
assert(bq);
while ((l = bq->blocks)) {
bq->blocks = l->next;
pa_memblock_unref(l->chunk.memblock);
pa_xfree(l);
}
bq->blocks_tail = NULL;
bq->n_blocks = 0;
bq->current_length = 0;
}
uint32_t pa_memblockq_get_tlength(pa_memblockq *bq) {
assert(bq);
return bq->tlength;
}

104
src/polypcore/memblockq.h Normal file
View file

@ -0,0 +1,104 @@
#ifndef foomemblockqhfoo
#define foomemblockqhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
polypaudio 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 Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <sys/types.h>
#include "memblock.h"
#include "memchunk.h"
/* A memblockq is a queue of pa_memchunks (yepp, the name is not
* perfect). It is similar to the ring buffers used by most other
* audio software. In contrast to a ring buffer this memblockq data
* type doesn't need to copy any data around, it just maintains
* references to reference counted memory blocks. */
typedef struct pa_memblockq pa_memblockq;
/* Parameters:
- maxlength: maximum length of queue. If more data is pushed into the queue, data from the front is dropped
- length: the target length of the queue.
- base: a base value for all metrics. Only multiples of this value are popped from the queue
- prebuf: before passing the first byte out, make sure that enough bytes are in the queue
- minreq: pa_memblockq_missing() will only return values greater than this value
*/
pa_memblockq* pa_memblockq_new(size_t maxlength,
size_t tlength,
size_t base,
size_t prebuf,
size_t minreq,
pa_memblock_stat *s);
void pa_memblockq_free(pa_memblockq*bq);
/* Push a new memory chunk into the queue. Optionally specify a value for future cancellation. */
void pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta);
/* Same as pa_memblockq_push(), however chunks are filtered through a mcalign object, and thus aligned to multiples of base */
void pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta);
/* Return a copy of the next memory chunk in the queue. It is not removed from the queue */
int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk);
/* Drop the specified bytes from the queue, only valid aufter pa_memblockq_peek() */
void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length);
/* Drop the specified bytes from the queue */
void pa_memblockq_skip(pa_memblockq *bq, size_t length);
/* Shorten the pa_memblockq to the specified length by dropping data at the end of the queue */
void pa_memblockq_shorten(pa_memblockq *bq, size_t length);
/* Empty the pa_memblockq */
void pa_memblockq_empty(pa_memblockq *bq);
/* Test if the pa_memblockq is currently readable, that is, more data than base */
int pa_memblockq_is_readable(pa_memblockq *bq);
/* Test if the pa_memblockq is currently writable for the specified amount of bytes */
int pa_memblockq_is_writable(pa_memblockq *bq, size_t length);
/* Return the length of the queue in bytes */
uint32_t pa_memblockq_get_length(pa_memblockq *bq);
/* Return how many bytes are missing in queue to the specified fill amount */
uint32_t pa_memblockq_missing(pa_memblockq *bq);
/* Returns the minimal request value */
uint32_t pa_memblockq_get_minreq(pa_memblockq *bq);
/* Force disabling of pre-buf even when the pre-buffer is not yet filled */
void pa_memblockq_prebuf_disable(pa_memblockq *bq);
/* Reenable pre-buf to the initial level */
void pa_memblockq_prebuf_reenable(pa_memblockq *bq);
/* Manipulate the write pointer */
void pa_memblockq_seek(pa_memblockq *bq, size_t delta);
/* Flush the queue */
void pa_memblockq_flush(pa_memblockq *bq);
/* Get Target length */
uint32_t pa_memblockq_get_tlength(pa_memblockq *bq);
#endif

58
src/polypcore/memchunk.c Normal file
View file

@ -0,0 +1,58 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "memchunk.h"
#include "xmalloc.h"
void pa_memchunk_make_writable(pa_memchunk *c, pa_memblock_stat *s, size_t min) {
pa_memblock *n;
size_t l;
assert(c && c->memblock && c->memblock->ref >= 1);
if (c->memblock->ref == 1 && !c->memblock->read_only && c->memblock->length >= c->index+min)
return;
l = c->length;
if (l < min)
l = min;
n = pa_memblock_new(l, s);
memcpy(n->data, (uint8_t*) c->memblock->data + c->index, c->length);
pa_memblock_unref(c->memblock);
c->memblock = n;
c->index = 0;
}
void pa_memchunk_reset(pa_memchunk *c) {
assert(c);
c->memblock = NULL;
c->length = c->index = 0;
}

45
src/polypcore/memchunk.h Normal file
View file

@ -0,0 +1,45 @@
#ifndef foomemchunkhfoo
#define foomemchunkhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "memblock.h"
/* A memchunk describes a part of a memblock. In contrast to the memblock, a
* memchunk is not allocated dynamically or reference counted, instead
* it is usually stored on the stack and copied around */
typedef struct pa_memchunk {
pa_memblock *memblock;
size_t index, length;
} pa_memchunk;
/* Make a memchunk writable, i.e. make sure that the caller may have
* exclusive access to the memblock and it is not read_only. If needed
* the memblock in the structure is replaced by a copy. */
void pa_memchunk_make_writable(pa_memchunk *c, pa_memblock_stat *s, size_t min);
/* Invalidate a memchunk. This does not free the cotaining memblock,
* but sets all members to zero. */
void pa_memchunk_reset(pa_memchunk *c);
#endif

260
src/polypcore/modargs.c Normal file
View file

@ -0,0 +1,260 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <ctype.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "hashmap.h"
#include "modargs.h"
#include "idxset.h"
#include "sample-util.h"
#include "namereg.h"
#include "sink.h"
#include "source.h"
#include "xmalloc.h"
#include "util.h"
struct entry {
char *key, *value;
};
static int add_key_value(pa_hashmap *map, char *key, char *value, const char* const valid_keys[]) {
struct entry *e;
assert(map && key && value);
if (valid_keys) {
const char*const* v;
for (v = valid_keys; *v; v++)
if (strcmp(*v, key) == 0)
break;
if (!*v) {
pa_xfree(key);
pa_xfree(value);
return -1;
}
}
e = pa_xmalloc(sizeof(struct entry));
e->key = key;
e->value = value;
pa_hashmap_put(map, key, e);
return 0;
}
pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
pa_hashmap *map = NULL;
map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
assert(map);
if (args) {
enum { WHITESPACE, KEY, VALUE_START, VALUE_SIMPLE, VALUE_DOUBLE_QUOTES, VALUE_TICKS } state;
const char *p, *key, *value;
size_t key_len = 0, value_len = 0;
key = value = NULL;
state = WHITESPACE;
for (p = args; *p; p++) {
switch (state) {
case WHITESPACE:
if (*p == '=')
goto fail;
else if (!isspace(*p)) {
key = p;
state = KEY;
key_len = 1;
}
break;
case KEY:
if (*p == '=')
state = VALUE_START;
else
key_len++;
break;
case VALUE_START:
if (*p == '\'') {
state = VALUE_TICKS;
value = p+1;
value_len = 0;
} else if (*p == '"') {
state = VALUE_DOUBLE_QUOTES;
value = p+1;
value_len = 0;
} else if (isspace(*p)) {
if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0)
goto fail;
state = WHITESPACE;
} else {
state = VALUE_SIMPLE;
value = p;
value_len = 1;
}
break;
case VALUE_SIMPLE:
if (isspace(*p)) {
if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
goto fail;
state = WHITESPACE;
} else
value_len++;
break;
case VALUE_DOUBLE_QUOTES:
if (*p == '"') {
if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
goto fail;
state = WHITESPACE;
} else
value_len++;
break;
case VALUE_TICKS:
if (*p == '\'') {
if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
goto fail;
state = WHITESPACE;
} else
value_len++;
break;
}
}
if (state == VALUE_START) {
if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0)
goto fail;
} else if (state == VALUE_SIMPLE) {
if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(value), valid_keys) < 0)
goto fail;
} else if (state != WHITESPACE)
goto fail;
}
return (pa_modargs*) map;
fail:
if (map)
pa_modargs_free((pa_modargs*) map);
return NULL;
}
static void free_func(void *p, PA_GCC_UNUSED void*userdata) {
struct entry *e = p;
assert(e);
pa_xfree(e->key);
pa_xfree(e->value);
pa_xfree(e);
}
void pa_modargs_free(pa_modargs*ma) {
pa_hashmap *map = (pa_hashmap*) ma;
pa_hashmap_free(map, free_func, NULL);
}
const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *def) {
pa_hashmap *map = (pa_hashmap*) ma;
struct entry*e;
if (!(e = pa_hashmap_get(map, key)))
return def;
return e->value;
}
int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value) {
const char *v;
assert(ma && key && value);
if (!(v = pa_modargs_get_value(ma, key, NULL)))
return 0;
if (pa_atou(v, value) < 0)
return -1;
return 0;
}
int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value) {
const char *v;
assert(ma && key && value);
if (!(v = pa_modargs_get_value(ma, key, NULL)))
return 0;
if (pa_atoi(v, value) < 0)
return -1;
return 0;
}
int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value) {
const char *v;
int r;
assert(ma && key && value);
if (!(v = pa_modargs_get_value(ma, key, NULL)))
return 0;
if (!*v)
return -1;
if ((r = pa_parse_boolean(v)) < 0)
return -1;
*value = r;
return 0;
}
int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *rss) {
const char *format;
uint32_t channels;
pa_sample_spec ss;
assert(ma && rss);
/* DEBUG_TRAP;*/
ss = *rss;
if ((pa_modargs_get_value_u32(ma, "rate", &ss.rate)) < 0)
return -1;
channels = ss.channels;
if ((pa_modargs_get_value_u32(ma, "channels", &channels)) < 0)
return -1;
ss.channels = (uint8_t) channels;
if ((format = pa_modargs_get_value(ma, "format", NULL)))
if ((ss.format = pa_parse_sample_format(format)) < 0)
return -1;
if (!pa_sample_spec_valid(&ss))
return -1;
*rss = ss;
return 0;
}

49
src/polypcore/modargs.h Normal file
View file

@ -0,0 +1,49 @@
#ifndef foomodargshfoo
#define foomodargshfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
#include <polyp/sample.h>
#include "core.h"
typedef struct pa_modargs pa_modargs;
/* A generic parser for module arguments */
/* Parse the string args. The NULL-terminated array keys contains all valid arguments. */
pa_modargs *pa_modargs_new(const char *args, const char* const keys[]);
void pa_modargs_free(pa_modargs*ma);
/* Return the module argument for the specified name as a string. If
* the argument was not specified, return def instead.*/
const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *def);
/* Return a module argument as unsigned 32bit value in *value */
int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value);
int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value);
int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value);
/* Return sample spec data from the three arguments "rate", "format" and "channels" */
int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *ss);
#endif

84
src/polypcore/modinfo.c Normal file
View file

@ -0,0 +1,84 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
polypaudio 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 Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <ltdl.h>
#include <assert.h>
#include "xmalloc.h"
#include "util.h"
#include "modinfo.h"
#include "log.h"
#define PA_SYMBOL_AUTHOR "pa__get_author"
#define PA_SYMBOL_DESCRIPTION "pa__get_description"
#define PA_SYMBOL_USAGE "pa__get_usage"
#define PA_SYMBOL_VERSION "pa__get_version"
pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl) {
pa_modinfo *i;
const char* (*func)(void);
assert(dl);
i = pa_xmalloc0(sizeof(pa_modinfo));
if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_AUTHOR)))
i->author = pa_xstrdup(func());
if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_DESCRIPTION)))
i->description = pa_xstrdup(func());
if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_USAGE)))
i->usage = pa_xstrdup(func());
if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_VERSION)))
i->version = pa_xstrdup(func());
return i;
}
pa_modinfo *pa_modinfo_get_by_name(const char *name) {
lt_dlhandle dl;
pa_modinfo *i;
assert(name);
if (!(dl = lt_dlopenext(name))) {
pa_log(__FILE__": Failed to open module \"%s\": %s\n", name, lt_dlerror());
return NULL;
}
i = pa_modinfo_get_by_handle(dl);
lt_dlclose(dl);
return i;
}
void pa_modinfo_free(pa_modinfo *i) {
assert(i);
pa_xfree(i->author);
pa_xfree(i->description);
pa_xfree(i->usage);
pa_xfree(i->version);
pa_xfree(i);
}

43
src/polypcore/modinfo.h Normal file
View file

@ -0,0 +1,43 @@
#ifndef foomodinfohfoo
#define foomodinfohfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* Some functions for reading module meta data from Polypaudio modules */
typedef struct pa_modinfo {
char *author;
char *description;
char *usage;
char *version;
} pa_modinfo;
/* Read meta data from an libtool handle */
pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl);
/* Read meta data from a module file */
pa_modinfo *pa_modinfo_get_by_name(const char *name);
/* Free meta data */
void pa_modinfo_free(pa_modinfo *i);
#endif

275
src/polypcore/module.c Normal file
View file

@ -0,0 +1,275 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include "module.h"
#include "xmalloc.h"
#include "subscribe.h"
#include "log.h"
#include "util.h"
#define PA_SYMBOL_INIT "pa__init"
#define PA_SYMBOL_DONE "pa__done"
#define UNLOAD_POLL_TIME 2
static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
pa_core *c = userdata;
struct timeval ntv;
assert(c && c->mainloop == m && c->module_auto_unload_event == e);
pa_module_unload_unused(c);
pa_gettimeofday(&ntv);
ntv.tv_sec += UNLOAD_POLL_TIME;
m->time_restart(e, &ntv);
}
pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
pa_module *m = NULL;
int r;
assert(c && name);
if (c->disallow_module_loading)
goto fail;
m = pa_xmalloc(sizeof(pa_module));
m->name = pa_xstrdup(name);
m->argument = pa_xstrdup(argument);
if (!(m->dl = lt_dlopenext(name))) {
pa_log(__FILE__": Failed to open module \"%s\": %s\n", name, lt_dlerror());
goto fail;
}
if (!(m->init = (int (*)(pa_core *_c, pa_module*_m)) lt_dlsym(m->dl, PA_SYMBOL_INIT))) {
pa_log(__FILE__": Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.\n", name);
goto fail;
}
if (!(m->done = (void (*)(pa_core *_c, pa_module*_m)) lt_dlsym(m->dl, PA_SYMBOL_DONE))) {
pa_log(__FILE__": Failed to load module \"%s\": symbol \""PA_SYMBOL_DONE"\" not found.\n", name);
goto fail;
}
m->userdata = NULL;
m->core = c;
m->n_used = -1;
m->auto_unload = 0;
m->unload_requested = 0;
assert(m->init);
if (m->init(c, m) < 0) {
pa_log_error(__FILE__": Failed to load module \"%s\" (argument: \"%s\"): initialization failed.\n", name, argument ? argument : "");
goto fail;
}
if (!c->modules)
c->modules = pa_idxset_new(NULL, NULL);
if (!c->module_auto_unload_event) {
struct timeval ntv;
pa_gettimeofday(&ntv);
ntv.tv_sec += UNLOAD_POLL_TIME;
c->module_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);
}
assert(c->module_auto_unload_event);
assert(c->modules);
r = pa_idxset_put(c->modules, m, &m->index);
assert(r >= 0 && m->index != PA_IDXSET_INVALID);
pa_log_info(__FILE__": Loaded \"%s\" (index: #%u; argument: \"%s\").\n", m->name, m->index, m->argument ? m->argument : "");
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index);
return m;
fail:
if (m) {
pa_xfree(m->argument);
pa_xfree(m->name);
if (m->dl)
lt_dlclose(m->dl);
pa_xfree(m);
}
return NULL;
}
static void pa_module_free(pa_module *m) {
assert(m && m->done && m->core);
if (m->core->disallow_module_loading)
return;
pa_log_info(__FILE__": Unloading \"%s\" (index: #%u).\n", m->name, m->index);
m->done(m->core, m);
lt_dlclose(m->dl);
pa_log_info(__FILE__": Unloaded \"%s\" (index: #%u).\n", m->name, m->index);
pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_REMOVE, m->index);
pa_xfree(m->name);
pa_xfree(m->argument);
pa_xfree(m);
}
void pa_module_unload(pa_core *c, pa_module *m) {
assert(c && m);
assert(c->modules);
if (!(m = pa_idxset_remove_by_data(c->modules, m, NULL)))
return;
pa_module_free(m);
}
void pa_module_unload_by_index(pa_core *c, uint32_t idx) {
pa_module *m;
assert(c && idx != PA_IDXSET_INVALID);
assert(c->modules);
if (!(m = pa_idxset_remove_by_index(c->modules, idx)))
return;
pa_module_free(m);
}
static void free_callback(void *p, PA_GCC_UNUSED void *userdata) {
pa_module *m = p;
assert(m);
pa_module_free(m);
}
void pa_module_unload_all(pa_core *c) {
assert(c);
if (!c->modules)
return;
pa_idxset_free(c->modules, free_callback, NULL);
c->modules = NULL;
if (c->module_auto_unload_event) {
c->mainloop->time_free(c->module_auto_unload_event);
c->module_auto_unload_event = NULL;
}
if (c->module_defer_unload_event) {
c->mainloop->defer_free(c->module_defer_unload_event);
c->module_defer_unload_event = NULL;
}
}
static int unused_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void *userdata) {
pa_module *m = p;
time_t *now = userdata;
assert(p && del && now);
if (m->n_used == 0 && m->auto_unload && m->last_used_time+m->core->module_idle_time <= *now) {
pa_module_free(m);
*del = 1;
}
return 0;
}
void pa_module_unload_unused(pa_core *c) {
time_t now;
assert(c);
if (!c->modules)
return;
time(&now);
pa_idxset_foreach(c->modules, unused_callback, &now);
}
static int unload_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, PA_GCC_UNUSED void *userdata) {
pa_module *m = p;
assert(m);
if (m->unload_requested) {
pa_module_free(m);
*del = 1;
}
return 0;
}
static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
pa_core *core = userdata;
api->defer_enable(e, 0);
if (!core->modules)
return;
pa_idxset_foreach(core->modules, unload_callback, NULL);
}
void pa_module_unload_request(pa_module *m) {
assert(m);
m->unload_requested = 1;
if (!m->core->module_defer_unload_event)
m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core);
m->core->mainloop->defer_enable(m->core->module_defer_unload_event, 1);
}
void pa_module_set_used(pa_module*m, int used) {
assert(m);
if (m->n_used != used)
pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index);
if (m->n_used != used && used == 0)
time(&m->last_used_time);
m->n_used = used;
}
pa_modinfo *pa_module_get_info(pa_module *m) {
assert(m);
return pa_modinfo_get_by_handle(m->dl);
}

70
src/polypcore/module.h Normal file
View file

@ -0,0 +1,70 @@
#ifndef foomodulehfoo
#define foomodulehfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
#include <ltdl.h>
#include "core.h"
#include "modinfo.h"
typedef struct pa_module pa_module;
struct pa_module {
pa_core *core;
char *name, *argument;
uint32_t index;
lt_dlhandle dl;
int (*init)(pa_core *c, pa_module*m);
void (*done)(pa_core *c, pa_module*m);
void *userdata;
int n_used;
int auto_unload;
time_t last_used_time;
int unload_requested;
};
pa_module* pa_module_load(pa_core *c, const char *name, const char*argument);
void pa_module_unload(pa_core *c, pa_module *m);
void pa_module_unload_by_index(pa_core *c, uint32_t idx);
void pa_module_unload_all(pa_core *c);
void pa_module_unload_unused(pa_core *c);
void pa_module_unload_request(pa_module *m);
void pa_module_set_used(pa_module*m, int used);
#define PA_MODULE_AUTHOR(s) const char * pa__get_author(void) { return s; }
#define PA_MODULE_DESCRIPTION(s) const char * pa__get_description(void) { return s; }
#define PA_MODULE_USAGE(s) const char * pa__get_usage(void) { return s; }
#define PA_MODULE_VERSION(s) const char * pa__get_version(void) { return s; }
pa_modinfo *pa_module_get_info(pa_module *m);
#endif

212
src/polypcore/namereg.c Normal file
View file

@ -0,0 +1,212 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include "namereg.h"
#include "autoload.h"
#include "source.h"
#include "sink.h"
#include "xmalloc.h"
#include "subscribe.h"
#include "util.h"
struct namereg_entry {
pa_namereg_type_t type;
char *name;
void *data;
};
void pa_namereg_free(pa_core *c) {
assert(c);
if (!c->namereg)
return;
assert(pa_hashmap_size(c->namereg) == 0);
pa_hashmap_free(c->namereg, NULL, NULL);
}
const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail) {
struct namereg_entry *e;
char *n = NULL;
int r;
assert(c && name && data);
if (!c->namereg) {
c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
assert(c->namereg);
}
if ((e = pa_hashmap_get(c->namereg, name)) && fail)
return NULL;
if (!e)
n = pa_xstrdup(name);
else {
unsigned i;
size_t l = strlen(name);
n = pa_xmalloc(l+3);
for (i = 1; i <= 99; i++) {
snprintf(n, l+2, "%s%u", name, i);
if (!(e = pa_hashmap_get(c->namereg, n)))
break;
}
if (e) {
pa_xfree(n);
return NULL;
}
}
assert(n);
e = pa_xmalloc(sizeof(struct namereg_entry));
e->type = type;
e->name = n;
e->data = data;
r = pa_hashmap_put(c->namereg, e->name, e);
assert (r >= 0);
return e->name;
}
void pa_namereg_unregister(pa_core *c, const char *name) {
struct namereg_entry *e;
assert(c && name);
e = pa_hashmap_remove(c->namereg, name);
assert(e);
pa_xfree(e->name);
pa_xfree(e);
}
void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload) {
struct namereg_entry *e;
uint32_t idx;
assert(c);
if (!name) {
if (type == PA_NAMEREG_SOURCE)
name = pa_namereg_get_default_source_name(c);
else if (type == PA_NAMEREG_SINK)
name = pa_namereg_get_default_sink_name(c);
}
if (!name)
return NULL;
if (c->namereg && (e = pa_hashmap_get(c->namereg, name)))
if (e->type == e->type)
return e->data;
if (pa_atou(name, &idx) < 0) {
if (autoload) {
pa_autoload_request(c, name, type);
if (c->namereg && (e = pa_hashmap_get(c->namereg, name)))
if (e->type == e->type)
return e->data;
}
return NULL;
}
if (type == PA_NAMEREG_SINK)
return pa_idxset_get_by_index(c->sinks, idx);
else if (type == PA_NAMEREG_SOURCE)
return pa_idxset_get_by_index(c->sources, idx);
else if (type == PA_NAMEREG_SAMPLE && c->scache)
return pa_idxset_get_by_index(c->scache, idx);
return NULL;
}
void pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type) {
char **s;
assert(c && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE));
s = type == PA_NAMEREG_SINK ? &c->default_sink_name : &c->default_source_name;
assert(s);
if (!name && !*s)
return;
if (name && *s && !strcmp(name, *s))
return;
pa_xfree(*s);
*s = pa_xstrdup(name);
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
}
const char *pa_namereg_get_default_sink_name(pa_core *c) {
pa_sink *s;
assert(c);
if (c->default_sink_name)
return c->default_sink_name;
if ((s = pa_idxset_first(c->sinks, NULL)))
pa_namereg_set_default(c, s->name, PA_NAMEREG_SINK);
if (c->default_sink_name)
return c->default_sink_name;
return NULL;
}
const char *pa_namereg_get_default_source_name(pa_core *c) {
pa_source *s;
uint32_t idx;
assert(c);
if (c->default_source_name)
return c->default_source_name;
for (s = pa_idxset_first(c->sources, &idx); s; s = pa_idxset_next(c->sources, &idx))
if (!s->monitor_of) {
pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE);
break;
}
if (!c->default_source_name)
if ((s = pa_idxset_first(c->sources, NULL)))
pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE);
if (c->default_source_name)
return c->default_source_name;
return NULL;
}

43
src/polypcore/namereg.h Normal file
View file

@ -0,0 +1,43 @@
#ifndef foonamereghfoo
#define foonamereghfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "core.h"
typedef enum pa_namereg_type {
PA_NAMEREG_SINK,
PA_NAMEREG_SOURCE,
PA_NAMEREG_SAMPLE
} pa_namereg_type_t;
void pa_namereg_free(pa_core *c);
const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail);
void pa_namereg_unregister(pa_core *c, const char *name);
void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload);
void pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type);
const char *pa_namereg_get_default_sink_name(pa_core *c);
const char *pa_namereg_get_default_source_name(pa_core *c);
#endif

View file

@ -0,0 +1,114 @@
#ifndef foonativecommonhfoo
#define foonativecommonhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <polyp/cdecl.h>
#include <polyp/polyplib-def.h>
PA_C_DECL_BEGIN
enum {
PA_COMMAND_ERROR,
PA_COMMAND_TIMEOUT, /* pseudo command */
PA_COMMAND_REPLY,
PA_COMMAND_CREATE_PLAYBACK_STREAM,
PA_COMMAND_DELETE_PLAYBACK_STREAM,
PA_COMMAND_CREATE_RECORD_STREAM,
PA_COMMAND_DELETE_RECORD_STREAM,
PA_COMMAND_EXIT,
PA_COMMAND_REQUEST,
PA_COMMAND_AUTH,
PA_COMMAND_SET_CLIENT_NAME,
PA_COMMAND_LOOKUP_SINK,
PA_COMMAND_LOOKUP_SOURCE,
PA_COMMAND_DRAIN_PLAYBACK_STREAM,
PA_COMMAND_PLAYBACK_STREAM_KILLED,
PA_COMMAND_RECORD_STREAM_KILLED,
PA_COMMAND_STAT,
PA_COMMAND_GET_PLAYBACK_LATENCY,
PA_COMMAND_CREATE_UPLOAD_STREAM,
PA_COMMAND_DELETE_UPLOAD_STREAM,
PA_COMMAND_FINISH_UPLOAD_STREAM,
PA_COMMAND_PLAY_SAMPLE,
PA_COMMAND_REMOVE_SAMPLE,
PA_COMMAND_GET_SERVER_INFO,
PA_COMMAND_GET_SINK_INFO,
PA_COMMAND_GET_SINK_INFO_LIST,
PA_COMMAND_GET_SOURCE_INFO,
PA_COMMAND_GET_SOURCE_INFO_LIST,
PA_COMMAND_GET_MODULE_INFO,
PA_COMMAND_GET_MODULE_INFO_LIST,
PA_COMMAND_GET_CLIENT_INFO,
PA_COMMAND_GET_CLIENT_INFO_LIST,
PA_COMMAND_GET_SINK_INPUT_INFO,
PA_COMMAND_GET_SINK_INPUT_INFO_LIST,
PA_COMMAND_GET_SOURCE_OUTPUT_INFO,
PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST,
PA_COMMAND_GET_SAMPLE_INFO,
PA_COMMAND_GET_SAMPLE_INFO_LIST,
PA_COMMAND_SUBSCRIBE,
PA_COMMAND_SUBSCRIBE_EVENT,
PA_COMMAND_SET_SINK_VOLUME,
PA_COMMAND_SET_SINK_INPUT_VOLUME,
PA_COMMAND_CORK_PLAYBACK_STREAM,
PA_COMMAND_FLUSH_PLAYBACK_STREAM,
PA_COMMAND_TRIGGER_PLAYBACK_STREAM,
PA_COMMAND_SET_DEFAULT_SINK,
PA_COMMAND_SET_DEFAULT_SOURCE,
PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
PA_COMMAND_SET_RECORD_STREAM_NAME,
PA_COMMAND_KILL_CLIENT,
PA_COMMAND_KILL_SINK_INPUT,
PA_COMMAND_KILL_SOURCE_OUTPUT,
PA_COMMAND_LOAD_MODULE,
PA_COMMAND_UNLOAD_MODULE,
PA_COMMAND_ADD_AUTOLOAD,
PA_COMMAND_REMOVE_AUTOLOAD,
PA_COMMAND_GET_AUTOLOAD_INFO,
PA_COMMAND_GET_AUTOLOAD_INFO_LIST,
PA_COMMAND_GET_RECORD_LATENCY,
PA_COMMAND_CORK_RECORD_STREAM,
PA_COMMAND_FLUSH_RECORD_STREAM,
PA_COMMAND_PREBUF_PLAYBACK_STREAM,
PA_COMMAND_MAX
};
#define PA_NATIVE_COOKIE_LENGTH 256
#define PA_NATIVE_COOKIE_FILE ".polypaudio-cookie"
#define PA_NATIVE_DEFAULT_PORT 4713
#define PA_NATIVE_COOKIE_PROPERTY_NAME "protocol-native-cookie"
#define PA_NATIVE_SERVER_PROPERTY_NAME "protocol-native-server"
#define PA_NATIVE_DEFAULT_UNIX_SOCKET "native"
PA_C_DECL_END
#endif

167
src/polypcore/oss-util.c Normal file
View file

@ -0,0 +1,167 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "oss-util.h"
#include "util.h"
#include "log.h"
int pa_oss_open(const char *device, int *mode, int* pcaps) {
int fd = -1;
assert(device && mode && (*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY));
if (*mode == O_RDWR) {
if ((fd = open(device, O_RDWR|O_NDELAY)) >= 0) {
int dcaps, *tcaps;
ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
tcaps = pcaps ? pcaps : &dcaps;
if (ioctl(fd, SNDCTL_DSP_GETCAPS, tcaps) < 0) {
pa_log(__FILE__": SNDCTL_DSP_GETCAPS: %s\n", strerror(errno));
goto fail;
}
if (*tcaps & DSP_CAP_DUPLEX)
return fd;
goto fail;
}
if ((fd = open(device, (*mode = O_WRONLY)|O_NDELAY)) < 0) {
if ((fd = open(device, (*mode = O_RDONLY)|O_NDELAY)) < 0) {
pa_log(__FILE__": open('%s'): %s\n", device, strerror(errno));
goto fail;
}
}
} else {
if ((fd = open(device, *mode|O_NDELAY)) < 0) {
pa_log(__FILE__": open('%s'): %s\n", device, strerror(errno));
goto fail;
}
}
if (pcaps) {
if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {
pa_log(__FILE__": SNDCTL_DSP_GETCAPS: %s\n", strerror(errno));
goto fail;
}
}
pa_fd_set_cloexec(fd, 1);
return fd;
fail:
if (fd >= 0)
close(fd);
return -1;
}
int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
int format, channels, speed, reqformat;
static const int format_trans[PA_SAMPLE_MAX] = {
[PA_SAMPLE_U8] = AFMT_U8,
[PA_SAMPLE_ALAW] = AFMT_A_LAW,
[PA_SAMPLE_ULAW] = AFMT_MU_LAW,
[PA_SAMPLE_S16LE] = AFMT_S16_LE,
[PA_SAMPLE_S16BE] = AFMT_S16_BE,
[PA_SAMPLE_FLOAT32LE] = AFMT_QUERY, /* not supported */
[PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */
};
assert(fd >= 0 && ss);
reqformat = format = format_trans[ss->format];
if (reqformat == AFMT_QUERY || ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != reqformat) {
format = AFMT_S16_NE;
if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_S16_NE) {
int f = AFMT_S16_NE == AFMT_S16_LE ? AFMT_S16_BE : AFMT_S16_LE;
format = f;
if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != f) {
format = AFMT_U8;
if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_U8) {
pa_log(__FILE__": SNDCTL_DSP_SETFMT: %s\n", format != AFMT_U8 ? "No supported sample format" : strerror(errno));
return -1;
} else
ss->format = PA_SAMPLE_U8;
} else
ss->format = f == AFMT_S16_LE ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE;
} else
ss->format = PA_SAMPLE_S16NE;
}
channels = ss->channels;
if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) {
pa_log(__FILE__": SNDCTL_DSP_CHANNELS: %s\n", strerror(errno));
return -1;
}
assert(channels);
ss->channels = channels;
speed = ss->rate;
if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) {
pa_log(__FILE__": SNDCTL_DSP_SPEED: %s\n", strerror(errno));
return -1;
}
assert(speed);
ss->rate = speed;
return 0;
}
static int simple_log2(int v) {
int k = 0;
for (;;) {
v >>= 1;
if (!v) break;
k++;
}
return k;
}
int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {
int arg;
arg = ((int) nfrags << 16) | simple_log2(frag_size);
if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) {
pa_log(__FILE__": SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno));
return -1;
}
return 0;
}

32
src/polypcore/oss-util.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef fooossutilhfoo
#define fooossutilhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <polyp/sample.h>
int pa_oss_open(const char *device, int *mode, int* pcaps);
int pa_oss_auto_format(int fd, pa_sample_spec *ss);
int pa_oss_set_fragments(int fd, int frags, int frag_size);
#endif

69
src/polypcore/packet.c Normal file
View file

@ -0,0 +1,69 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include "packet.h"
#include "xmalloc.h"
pa_packet* pa_packet_new(size_t length) {
pa_packet *p;
assert(length);
p = pa_xmalloc(sizeof(pa_packet)+length);
p->ref = 1;
p->length = length;
p->data = (uint8_t*) (p+1);
p->type = PA_PACKET_APPENDED;
return p;
}
pa_packet* pa_packet_new_dynamic(uint8_t* data, size_t length) {
pa_packet *p;
assert(data && length);
p = pa_xmalloc(sizeof(pa_packet));
p->ref = 1;
p->length = length;
p->data = data;
p->type = PA_PACKET_DYNAMIC;
return p;
}
pa_packet* pa_packet_ref(pa_packet *p) {
assert(p && p->ref >= 1);
p->ref++;
return p;
}
void pa_packet_unref(pa_packet *p) {
assert(p && p->ref >= 1);
p->ref--;
if (p->ref == 0) {
if (p->type == PA_PACKET_DYNAMIC)
pa_xfree(p->data);
pa_xfree(p);
}
}

41
src/polypcore/packet.h Normal file
View file

@ -0,0 +1,41 @@
#ifndef foopackethfoo
#define foopackethfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <sys/types.h>
#include <inttypes.h>
typedef struct pa_packet {
enum { PA_PACKET_APPENDED, PA_PACKET_DYNAMIC } type;
unsigned ref;
size_t length;
uint8_t *data;
} pa_packet;
pa_packet* pa_packet_new(size_t length);
pa_packet* pa_packet_new_dynamic(uint8_t* data, size_t length);
pa_packet* pa_packet_ref(pa_packet *p);
void pa_packet_unref(pa_packet *p);
#endif

112
src/polypcore/parseaddr.c Normal file
View file

@ -0,0 +1,112 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include "xmalloc.h"
#include "util.h"
#include "parseaddr.h"
/* Parse addresses in one of the following forms:
* HOSTNAME
* HOSTNAME:PORT
* [HOSTNAME]
* [HOSTNAME]:PORT
*
* Return a newly allocated string of the hostname and fill in *ret_port if specified */
static char *parse_host(const char *s, uint16_t *ret_port) {
assert(s && ret_port);
if (*s == '[') {
char *e;
if (!(e = strchr(s+1, ']')))
return NULL;
if (e[1] == ':')
*ret_port = atoi(e+2);
else if (e[1] != 0)
return NULL;
return pa_xstrndup(s+1, e-s-1);
} else {
char *e;
if (!(e = strrchr(s, ':')))
return pa_xstrdup(s);
*ret_port = atoi(e+1);
return pa_xstrndup(s, e-s);
}
}
int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
const char *p;
assert(name && ret_p);
memset(ret_p, 0, sizeof(pa_parsed_address));
ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO;
if (*name == '{') {
char hn[256], *pfx;
/* The URL starts with a host specification for detecting local connections */
if (!pa_get_host_name(hn, sizeof(hn)))
return -1;
pfx = pa_sprintf_malloc("{%s}", hn);
if (!pa_startswith(name, pfx)) {
pa_xfree(pfx);
/* Not local */
return -1;
}
p = name + strlen(pfx);
pa_xfree(pfx);
} else
p = name;
if (*p == '/')
ret_p->type = PA_PARSED_ADDRESS_UNIX;
else if (pa_startswith(p, "unix:")) {
ret_p->type = PA_PARSED_ADDRESS_UNIX;
p += sizeof("unix:")-1;
} else if (pa_startswith(p, "tcp:") || pa_startswith(p, "tcp4:")) {
ret_p->type = PA_PARSED_ADDRESS_TCP4;
p += sizeof("tcp:")-1;
} else if (pa_startswith(p, "tcp6:")) {
ret_p->type = PA_PARSED_ADDRESS_TCP6;
p += sizeof("tcp6:")-1;
}
if (ret_p->type == PA_PARSED_ADDRESS_UNIX)
ret_p->path_or_host = pa_xstrdup(p);
else
if (!(ret_p->path_or_host = parse_host(p, &ret_p->port)))
return -1;
return 0;
}

42
src/polypcore/parseaddr.h Normal file
View file

@ -0,0 +1,42 @@
#ifndef fooparseaddrhfoo
#define fooparseaddrhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
typedef enum pa_parsed_address_type {
PA_PARSED_ADDRESS_UNIX,
PA_PARSED_ADDRESS_TCP4,
PA_PARSED_ADDRESS_TCP6,
PA_PARSED_ADDRESS_TCP_AUTO
} pa_parsed_address_type_t;
typedef struct pa_parsed_address {
pa_parsed_address_type_t type;
char *path_or_host;
uint16_t port;
} pa_parsed_address;
int pa_parse_address(const char *a, pa_parsed_address *ret_p);
#endif

295
src/polypcore/pdispatch.c Normal file
View file

@ -0,0 +1,295 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "pdispatch.h"
#include "native-common.h"
#include "xmalloc.h"
#include "llist.h"
#include "log.h"
#include "util.h"
/*#define DEBUG_OPCODES */
#ifdef DEBUG_OPCODES
static const char *command_names[PA_COMMAND_MAX] = {
[PA_COMMAND_ERROR] = "ERROR",
[PA_COMMAND_TIMEOUT] = "TIMEOUT",
[PA_COMMAND_REPLY] = "REPLY",
[PA_COMMAND_CREATE_PLAYBACK_STREAM] = "CREATE_PLAYBACK_STREAM",
[PA_COMMAND_DELETE_PLAYBACK_STREAM] = "DELETE_PLAYBACK_STREAM",
[PA_COMMAND_CREATE_RECORD_STREAM] = "CREATE_RECORD_STREAM",
[PA_COMMAND_DELETE_RECORD_STREAM] = "DELETE_RECORD_STREAM",
[PA_COMMAND_AUTH] = "AUTH",
[PA_COMMAND_REQUEST] = "REQUEST",
[PA_COMMAND_EXIT] = "EXIT",
[PA_COMMAND_SET_CLIENT_NAME] = "SET_CLIENT_NAME",
[PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK",
[PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE",
[PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM",
[PA_COMMAND_PLAYBACK_STREAM_KILLED] = "PLAYBACK_STREAM_KILLED",
[PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED",
[PA_COMMAND_STAT] = "STAT",
[PA_COMMAND_GET_PLAYBACK_LATENCY] = "PLAYBACK_LATENCY",
[PA_COMMAND_CREATE_UPLOAD_STREAM] = "CREATE_UPLOAD_STREAM",
[PA_COMMAND_DELETE_UPLOAD_STREAM] = "DELETE_UPLOAD_STREAM",
[PA_COMMAND_FINISH_UPLOAD_STREAM] = "FINISH_UPLOAD_STREAM",
[PA_COMMAND_PLAY_SAMPLE] = "PLAY_SAMPLE",
[PA_COMMAND_REMOVE_SAMPLE] = "REMOVE_SAMPLE",
[PA_COMMAND_GET_SERVER_INFO] = "GET_SERVER_INFO",
[PA_COMMAND_GET_SINK_INFO] = "GET_SINK_INFO",
[PA_COMMAND_GET_SINK_INFO_LIST] = "GET_SINK_INFO_LIST",
[PA_COMMAND_GET_SOURCE_INFO] = "GET_SOURCE_INFO",
[PA_COMMAND_GET_SOURCE_INFO_LIST] = "GET_SOURCE_INFO_LIST",
[PA_COMMAND_GET_MODULE_INFO] = "GET_MODULE_INFO",
[PA_COMMAND_GET_MODULE_INFO_LIST] = "GET_MODULE_INFO_LIST",
[PA_COMMAND_GET_CLIENT_INFO] = "GET_CLIENT_INFO",
[PA_COMMAND_GET_CLIENT_INFO_LIST] = "GET_CLIENT_INFO_LIST",
[PA_COMMAND_GET_SAMPLE_INFO] = "GET_SAMPLE_INFO",
[PA_COMMAND_GET_SAMPLE_INFO_LIST] = "GET_SAMPLE_INFO_LIST",
[PA_COMMAND_GET_SINK_INPUT_INFO] = "GET_SINK_INPUT_INFO",
[PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = "GET_SINK_INPUT_INFO_LIST",
[PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = "GET_SOURCE_OUTPUT_INFO",
[PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = "GET_SOURCE_OUTPUT_INFO_LIST",
[PA_COMMAND_SUBSCRIBE] = "SUBSCRIBE",
[PA_COMMAND_SUBSCRIBE_EVENT] = "SUBSCRIBE_EVENT",
[PA_COMMAND_SET_SINK_VOLUME] = "SET_SINK_VOLUME",
[PA_COMMAND_SET_SINK_INPUT_VOLUME] = "SET_SINK_INPUT_VOLUME",
[PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = "TRIGGER_PLAYBACK_STREAM",
[PA_COMMAND_FLUSH_PLAYBACK_STREAM] = "FLUSH_PLAYBACK_STREAM",
[PA_COMMAND_CORK_PLAYBACK_STREAM] = "CORK_PLAYBACK_STREAM",
[PA_COMMAND_GET_AUTOLOAD_INFO] = "GET_AUTOLOAD_INFO",
[PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = "GET_AUTOLOAD_INFO_LIST",
};
#endif
struct reply_info {
pa_pdispatch *pdispatch;
PA_LLIST_FIELDS(struct reply_info);
pa_pdispatch_callback callback;
void *userdata;
uint32_t tag;
pa_time_event *time_event;
};
struct pa_pdispatch {
int ref;
pa_mainloop_api *mainloop;
const pa_pdispatch_callback *callback_table;
unsigned n_commands;
PA_LLIST_HEAD(struct reply_info, replies);
pa_pdispatch_drain_callback drain_callback;
void *drain_userdata;
};
static void reply_info_free(struct reply_info *r) {
assert(r && r->pdispatch && r->pdispatch->mainloop);
if (r->time_event)
r->pdispatch->mainloop->time_free(r->time_event);
PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r);
pa_xfree(r);
}
pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_callback*table, unsigned entries) {
pa_pdispatch *pd;
assert(mainloop);
assert((entries && table) || (!entries && !table));
pd = pa_xmalloc(sizeof(pa_pdispatch));
pd->ref = 1;
pd->mainloop = mainloop;
pd->callback_table = table;
pd->n_commands = entries;
PA_LLIST_HEAD_INIT(pa_reply_info, pd->replies);
pd->drain_callback = NULL;
pd->drain_userdata = NULL;
return pd;
}
static void pdispatch_free(pa_pdispatch *pd) {
assert(pd);
while (pd->replies)
reply_info_free(pd->replies);
pa_xfree(pd);
}
static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) {
pa_pdispatch_callback callback;
void *userdata;
uint32_t tag;
assert(r);
pa_pdispatch_ref(pd);
callback = r->callback;
userdata = r->userdata;
tag = r->tag;
reply_info_free(r);
callback(pd, command, tag, ts, userdata);
if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
pd->drain_callback(pd, pd->drain_userdata);
pa_pdispatch_unref(pd);
}
int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, void *userdata) {
uint32_t tag, command;
pa_tagstruct *ts = NULL;
int ret = -1;
assert(pd && packet && packet->data);
pa_pdispatch_ref(pd);
if (packet->length <= 8)
goto finish;
ts = pa_tagstruct_new(packet->data, packet->length);
assert(ts);
if (pa_tagstruct_getu32(ts, &command) < 0 ||
pa_tagstruct_getu32(ts, &tag) < 0)
goto finish;
#ifdef DEBUG_OPCODES
{
char t[256];
char const *p;
if (!(p = command_names[command]))
snprintf((char*) (p = t), sizeof(t), "%u", command);
pa_log(__FILE__": Recieved opcode <%s>\n", p);
}
#endif
if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
struct reply_info *r;
for (r = pd->replies; r; r = r->next)
if (r->tag == tag)
break;
if (r)
run_action(pd, r, command, ts);
} else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) {
const pa_pdispatch_callback *c = pd->callback_table+command;
(*c)(pd, command, tag, ts, userdata);
} else {
pa_log(__FILE__": Recieved unsupported command %u\n", command);
goto finish;
}
ret = 0;
finish:
if (ts)
pa_tagstruct_free(ts);
pa_pdispatch_unref(pd);
return ret;
}
static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
struct reply_info*r = userdata;
assert(r && r->time_event == e && r->pdispatch && r->pdispatch->mainloop == m && r->callback);
run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);
}
void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_callback cb, void *userdata) {
struct reply_info *r;
struct timeval tv;
assert(pd && pd->ref >= 1 && cb);
r = pa_xmalloc(sizeof(struct reply_info));
r->pdispatch = pd;
r->callback = cb;
r->userdata = userdata;
r->tag = tag;
pa_gettimeofday(&tv);
tv.tv_sec += timeout;
r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r);
assert(r->time_event);
PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
}
int pa_pdispatch_is_pending(pa_pdispatch *pd) {
assert(pd);
return !!pd->replies;
}
void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) {
assert(pd);
assert(!cb || pa_pdispatch_is_pending(pd));
pd->drain_callback = cb;
pd->drain_userdata = userdata;
}
void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
struct reply_info *r, *n;
assert(pd);
for (r = pd->replies; r; r = n) {
n = r->next;
if (r->userdata == userdata)
reply_info_free(r);
}
}
void pa_pdispatch_unref(pa_pdispatch *pd) {
assert(pd && pd->ref >= 1);
if (!(--(pd->ref)))
pdispatch_free(pd);
}
pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
assert(pd && pd->ref >= 1);
pd->ref++;
return pd;
}

50
src/polypcore/pdispatch.h Normal file
View file

@ -0,0 +1,50 @@
#ifndef foopdispatchhfoo
#define foopdispatchhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
#include "tagstruct.h"
#include "packet.h"
#include <polyp/mainloop-api.h>
typedef struct pa_pdispatch pa_pdispatch;
typedef void (*pa_pdispatch_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, const pa_pdispatch_callback*table, unsigned entries);
void pa_pdispatch_unref(pa_pdispatch *pd);
pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd);
int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*p, void *userdata);
void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_callback callback, void *userdata);
int pa_pdispatch_is_pending(pa_pdispatch *pd);
typedef void (*pa_pdispatch_drain_callback)(pa_pdispatch *pd, void *userdata);
void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_callback callback, void *userdata);
/* Remove all reply slots with the give userdata parameter */
void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata);
#endif

286
src/polypcore/pid.c Normal file
View file

@ -0,0 +1,286 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
polypaudio 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 Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <limits.h>
#include <signal.h>
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include "pid.h"
#include "util.h"
#include "log.h"
/* Read the PID data from the file descriptor fd, and return it. If no
* pid could be read, return 0, on failure (pid_t) -1 */
static pid_t read_pid(const char *fn, int fd) {
ssize_t r;
char t[20], *e;
uint32_t pid;
assert(fn && fd >= 0);
if ((r = pa_loop_read(fd, t, sizeof(t)-1)) < 0) {
pa_log(__FILE__": WARNING: failed to read PID file '%s': %s\n", fn, strerror(errno));
return (pid_t) -1;
}
if (r == 0)
return (pid_t) 0;
t[r] = 0;
if ((e = strchr(t, '\n')))
*e = 0;
if (pa_atou(t, &pid) < 0) {
pa_log(__FILE__": WARNING: failed to parse PID file '%s'\n", fn);
return (pid_t) -1;
}
return (pid_t) pid;
}
static int open_pid_file(const char *fn, int mode) {
int fd = -1;
int lock = -1;
for (;;) {
struct stat st;
pa_make_secure_parent_dir(fn);
if ((fd = open(fn, mode, S_IRUSR|S_IWUSR)) < 0) {
if (mode != O_RDONLY || errno != ENOENT)
pa_log(__FILE__": WARNING: failed to open PID file '%s': %s\n", fn, strerror(errno));
goto fail;
}
/* Try to lock the file. If that fails, go without */
if (pa_lock_fd(fd, 1) < 0)
goto fail;
if (fstat(fd, &st) < 0) {
pa_log(__FILE__": Failed to fstat() PID file '%s': %s\n", fn, strerror(errno));
goto fail;
}
/* Does the file still exist in the file system? When ye, w're done, otherwise restart */
if (st.st_nlink >= 1)
break;
if (pa_lock_fd(fd, 0) < 0)
goto fail;
if (close(fd) < 0) {
pa_log(__FILE__": Failed to close file '%s': %s\n", fn, strerror(errno));
goto fail;
}
fd = -1;
}
return fd;
fail:
if (fd < 0) {
if (lock >= 0)
pa_lock_fd(fd, 0);
close(fd);
}
return -1;
}
/* Create a new PID file for the current process. */
int pa_pid_file_create(void) {
int fd = -1;
int ret = -1;
char fn[PATH_MAX];
char t[20];
pid_t pid;
size_t l;
#ifdef OS_IS_WIN32
HANDLE process;
#endif
pa_runtime_path("pid", fn, sizeof(fn));
if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0)
goto fail;
if ((pid = read_pid(fn, fd)) == (pid_t) -1)
pa_log(__FILE__": corrupt PID file, overwriting.\n");
else if (pid > 0) {
#ifdef OS_IS_WIN32
if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) {
CloseHandle(process);
#else
if (kill(pid, 0) >= 0 || errno != ESRCH) {
#endif
pa_log(__FILE__": daemon already running.\n");
goto fail;
}
pa_log(__FILE__": stale PID file, overwriting.\n");
}
/* Overwrite the current PID file */
if (lseek(fd, 0, SEEK_SET) == (off_t) -1 || ftruncate(fd, 0) < 0) {
pa_log(__FILE__": failed to truncate PID fil: %s.\n", strerror(errno));
goto fail;
}
snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid());
l = strlen(t);
if (pa_loop_write(fd, t, l) != (ssize_t) l) {
pa_log(__FILE__": failed to write PID file.\n");
goto fail;
}
ret = 0;
fail:
if (fd >= 0) {
pa_lock_fd(fd, 0);
close(fd);
}
return ret;
}
/* Remove the PID file, if it is ours */
int pa_pid_file_remove(void) {
int fd = -1;
char fn[PATH_MAX];
int ret = -1;
pid_t pid;
pa_runtime_path("pid", fn, sizeof(fn));
if ((fd = open_pid_file(fn, O_RDWR)) < 0) {
pa_log(__FILE__": WARNING: failed to open PID file '%s': %s\n", fn, strerror(errno));
goto fail;
}
if ((pid = read_pid(fn, fd)) == (pid_t) -1)
goto fail;
if (pid != getpid()) {
pa_log(__FILE__": WARNING: PID file '%s' not mine!\n", fn);
goto fail;
}
if (ftruncate(fd, 0) < 0) {
pa_log(__FILE__": failed to truncate PID file '%s': %s\n", fn, strerror(errno));
goto fail;
}
#ifdef OS_IS_WIN32
pa_lock_fd(fd, 0);
close(fd);
fd = -1;
#endif
if (unlink(fn) < 0) {
pa_log(__FILE__": failed to remove PID file '%s': %s\n", fn, strerror(errno));
goto fail;
}
ret = 0;
fail:
if (fd >= 0) {
pa_lock_fd(fd, 0);
close(fd);
}
return ret;
}
/* Check whether the daemon is currently running, i.e. if a PID file
* exists and the PID therein too. Returns 0 on succcess, -1
* otherwise. If pid is non-NULL and a running daemon was found,
* return its PID therein */
int pa_pid_file_check_running(pid_t *pid) {
return pa_pid_file_kill(0, pid);
}
#ifndef OS_IS_WIN32
/* Kill a current running daemon. Return non-zero on success, -1
* otherwise. If successful *pid contains the PID of the daemon
* process. */
int pa_pid_file_kill(int sig, pid_t *pid) {
int fd = -1;
char fn[PATH_MAX];
int ret = -1;
pid_t _pid;
if (!pid)
pid = &_pid;
pa_runtime_path("pid", fn, sizeof(fn));
if ((fd = open_pid_file(fn, O_RDONLY)) < 0)
goto fail;
if ((*pid = read_pid(fn, fd)) == (pid_t) -1)
goto fail;
ret = kill(*pid, sig);
fail:
if (fd >= 0) {
pa_lock_fd(fd, 0);
close(fd);
}
return ret;
}
#else /* OS_IS_WIN32 */
int pa_pid_file_kill(int sig, pid_t *pid) {
return -1;
}
#endif

30
src/polypcore/pid.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef foopidhfoo
#define foopidhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
polypaudio 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 Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
int pa_pid_file_create(void);
int pa_pid_file_remove(void);
int pa_pid_file_check_running(pid_t *pid);
int pa_pid_file_kill(int sig, pid_t *pid);
#endif

View file

@ -0,0 +1,118 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "play-memchunk.h"
#include "sink-input.h"
#include "xmalloc.h"
#include "gccmacro.h"
static void sink_input_kill(pa_sink_input *i) {
pa_memchunk *c;
assert(i && i->userdata);
c = i->userdata;
pa_sink_input_disconnect(i);
pa_sink_input_unref(i);
pa_memblock_unref(c->memblock);
pa_xfree(c);
}
static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
pa_memchunk *c;
assert(i && chunk && i->userdata);
c = i->userdata;
if (c->length <= 0)
return -1;
assert(c->memblock && c->memblock->length);
*chunk = *c;
pa_memblock_ref(c->memblock);
return 0;
}
static void si_kill(PA_GCC_UNUSED pa_mainloop_api *m, void *i) {
sink_input_kill(i);
}
static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) {
pa_memchunk *c;
assert(i && length && i->userdata);
c = i->userdata;
assert(!memcmp(chunk, c, sizeof(chunk)));
assert(length <= c->length);
c->length -= length;
c->index += length;
if (c->length <= 0)
pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i);
}
int pa_play_memchunk(
pa_sink *sink,
const char *name,
const pa_sample_spec *ss,
const pa_channel_map *map,
const pa_memchunk *chunk,
pa_cvolume *cvolume) {
pa_sink_input *si;
pa_memchunk *nchunk;
assert(sink);
assert(ss);
assert(chunk);
if (cvolume && pa_cvolume_is_muted(cvolume))
return 0;
if (!(si = pa_sink_input_new(sink, name, __FILE__, ss, map, 0, PA_RESAMPLER_INVALID)))
return -1;
if (cvolume)
si->volume = *cvolume;
si->peek = sink_input_peek;
si->drop = sink_input_drop;
si->kill = sink_input_kill;
si->userdata = nchunk = pa_xnew(pa_memchunk, 1);
*nchunk = *chunk;
pa_memblock_ref(chunk->memblock);
pa_sink_notify(sink);
return 0;
}

View file

@ -0,0 +1,36 @@
#ifndef fooplaychunkhfoo
#define fooplaychunkhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "sink.h"
#include "memchunk.h"
int pa_play_memchunk(
pa_sink *sink,
const char *name,
const pa_sample_spec *ss,
const pa_channel_map *map,
const pa_memchunk *chunk,
pa_cvolume *cvolume);
#endif

190
src/polypcore/poll.c Normal file
View file

@ -0,0 +1,190 @@
/* $Id$ */
/***
Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc.
Copyright (C) 2005, Cendio AB.
This file is part of polypaudio.
Based on work for the GNU C Library.
polypaudio is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
polypaudio 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with polypaudio; If not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
***/
/* Poll the file descriptors described by the NFDS structures starting at
FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for
an event to occur; if TIMEOUT is -1, block until an event occurs.
Returns the number of file descriptors with events, zero if timed out,
or -1 for errors. */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include "winsock.h"
#ifndef HAVE_SYS_POLL_H
#include "util.h"
#include "poll.h"
int poll (struct pollfd *fds, unsigned long int nfds, int timeout) {
struct timeval tv;
fd_set rset, wset, xset;
struct pollfd *f;
int ready;
int maxfd = 0;
char data[64];
FD_ZERO (&rset);
FD_ZERO (&wset);
FD_ZERO (&xset);
if (nfds == 0) {
if (timeout >= 0) {
pa_msleep(timeout);
return 0;
}
#ifdef OS_IS_WIN32
/*
* Windows does not support signals properly so waiting for them would
* mean a deadlock.
*/
pa_msleep(100);
return 0;
#else
return select(0, NULL, NULL, NULL, NULL);
#endif
}
for (f = fds; f < &fds[nfds]; ++f) {
if (f->fd != -1) {
if (f->events & POLLIN)
FD_SET (f->fd, &rset);
if (f->events & POLLOUT)
FD_SET (f->fd, &wset);
if (f->events & POLLPRI)
FD_SET (f->fd, &xset);
if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI)))
maxfd = f->fd;
}
}
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset,
SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset,
SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv));
if ((ready == -1) && (errno == EBADF)) {
ready = 0;
FD_ZERO (&rset);
FD_ZERO (&wset);
FD_ZERO (&xset);
maxfd = -1;
for (f = fds; f < &fds[nfds]; ++f) {
if (f->fd != -1) {
fd_set sngl_rset, sngl_wset, sngl_xset;
FD_ZERO (&sngl_rset);
FD_ZERO (&sngl_wset);
FD_ZERO (&sngl_xset);
if (f->events & POLLIN)
FD_SET (f->fd, &sngl_rset);
if (f->events & POLLOUT)
FD_SET (f->fd, &sngl_wset);
if (f->events & POLLPRI)
FD_SET (f->fd, &sngl_xset);
if (f->events & (POLLIN|POLLOUT|POLLPRI)) {
struct timeval singl_tv;
singl_tv.tv_sec = 0;
singl_tv.tv_usec = 0;
if (select((SELECT_TYPE_ARG1) f->fd, SELECT_TYPE_ARG234 &rset,
SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset,
SELECT_TYPE_ARG5 &singl_tv) != -1) {
if (f->events & POLLIN)
FD_SET (f->fd, &rset);
if (f->events & POLLOUT)
FD_SET (f->fd, &wset);
if (f->events & POLLPRI)
FD_SET (f->fd, &xset);
if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI)))
maxfd = f->fd;
++ready;
} else if (errno == EBADF)
f->revents |= POLLNVAL;
}
}
}
if (ready) {
/* Linux alters the tv struct... but it shouldn't matter here ...
* as we're going to be a little bit out anyway as we've just eaten
* more than a couple of cpu cycles above */
ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset,
SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset,
SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv));
}
}
#ifdef OS_IS_WIN32
errno = WSAGetLastError();
#endif
if (ready > 0) {
ready = 0;
for (f = fds; f < &fds[nfds]; ++f) {
f->revents = 0;
if (f->fd != -1) {
if (FD_ISSET (f->fd, &rset)) {
/* support for POLLHUP. An hung up descriptor does not
increase the return value! */
if (recv (f->fd, data, 64, MSG_PEEK) == -1) {
if (errno == ESHUTDOWN || errno == ECONNRESET ||
errno == ECONNABORTED || errno == ENETRESET) {
fprintf(stderr, "Hangup\n");
f->revents |= POLLHUP;
}
}
if (f->revents == 0)
f->revents |= POLLIN;
}
if (FD_ISSET (f->fd, &wset))
f->revents |= POLLOUT;
if (FD_ISSET (f->fd, &xset))
f->revents |= POLLPRI;
}
if (f->revents)
ready++;
}
}
return ready;
}
#endif /* HAVE_SYS_POLL_H */

57
src/polypcore/poll.h Normal file
View file

@ -0,0 +1,57 @@
/* $Id$ */
/***
Compatibility definitions for System V `poll' interface.
Copyright (C) 1994,96,97,98,99,2000,2001,2004 Free Software Foundation, Inc.
Copyright (C) 2005, Cendio AB.
This file is part of polypaudio.
Based on work for the GNU C Library.
polypaudio is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
polypaudio 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with polypaudio; If not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
***/
/* Event types that can be polled for. These bits may be set in `events'
to indicate the interesting event types; they will appear in `revents'
to indicate the status of the file descriptor. */
#define POLLIN 0x001 /* There is data to read. */
#define POLLPRI 0x002 /* There is urgent data to read. */
#define POLLOUT 0x004 /* Writing now will not block. */
/* Event types always implicitly polled for. These bits need not be set in
`events', but they will appear in `revents' to indicate the status of
the file descriptor. */
#define POLLERR 0x008 /* Error condition. */
#define POLLHUP 0x010 /* Hung up. */
#define POLLNVAL 0x020 /* Invalid polling request. */
/* Type used for the number of file descriptors. */
typedef unsigned long int nfds_t;
/* Data structure describing a polling request. */
struct pollfd
{
int fd; /* File descriptor to poll. */
short int events; /* Types of events poller cares about. */
short int revents; /* Types of events that actually occurred. */
};
/* Poll the file descriptors described by the NFDS structures starting at
FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for
an event to occur; if TIMEOUT is -1, block until an event occurs.
Returns the number of file descriptors with events, zero if timed out,
or -1 for errors. */
extern int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout);

119
src/polypcore/props.c Normal file
View file

@ -0,0 +1,119 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <assert.h>
#include "xmalloc.h"
#include "props.h"
#include "log.h"
typedef struct pa_property {
char *name; /* Points to memory allocated by the property subsystem */
void *data; /* Points to memory maintained by the caller */
} pa_property;
/* Allocate a new property object */
static pa_property* property_new(const char *name, void *data) {
pa_property* p;
assert(name && data);
p = pa_xmalloc(sizeof(pa_property));
p->name = pa_xstrdup(name);
p->data = data;
return p;
}
/* Free a property object */
static void property_free(pa_property *p) {
assert(p);
pa_xfree(p->name);
pa_xfree(p);
}
void* pa_property_get(pa_core *c, const char *name) {
pa_property *p;
assert(c && name && c->properties);
if (!(p = pa_hashmap_get(c->properties, name)))
return NULL;
return p->data;
}
int pa_property_set(pa_core *c, const char *name, void *data) {
pa_property *p;
assert(c && name && data && c->properties);
if (pa_hashmap_get(c->properties, name))
return -1;
p = property_new(name, data);
pa_hashmap_put(c->properties, p->name, p);
return 0;
}
int pa_property_remove(pa_core *c, const char *name) {
pa_property *p;
assert(c && name && c->properties);
if (!(p = pa_hashmap_remove(c->properties, name)))
return -1;
property_free(p);
return 0;
}
void pa_property_init(pa_core *c) {
assert(c);
c->properties = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
}
void pa_property_cleanup(pa_core *c) {
assert(c);
if (!c->properties)
return;
assert(!pa_hashmap_size(c->properties));
pa_hashmap_free(c->properties, NULL, NULL);
c->properties = NULL;
}
void pa_property_dump(pa_core *c, pa_strbuf *s) {
void *state = NULL;
pa_property *p;
assert(c && s);
while ((p = pa_hashmap_iterate(c->properties, &state, NULL)))
pa_strbuf_printf(s, "[%s] -> [%p]\n", p->name, p->data);
}
int pa_property_replace(pa_core *c, const char *name, void *data) {
assert(c && name);
pa_property_remove(c, name);
return pa_property_set(c, name, data);
}

58
src/polypcore/props.h Normal file
View file

@ -0,0 +1,58 @@
#ifndef foopropshfoo
#define foopropshfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "core.h"
#include "strbuf.h"
/* The property subsystem is to be used to share data between
* modules. Consider them to be kind of "global" variables for a
* core. Why not use the hashmap functions directly? The hashmap
* functions copy neither the key nor value, while this property
* system copies the key. Users of this system have to think about
* reference counting themselves. */
/* Return a pointer to the value of the specified property. */
void* pa_property_get(pa_core *c, const char *name);
/* Set the property 'name' to 'data'. This function fails in case a
* property by this name already exists. The property data is not
* copied or reference counted. This is the caller's job. */
int pa_property_set(pa_core *c, const char *name, void *data);
/* Remove the specified property. Return non-zero on failure */
int pa_property_remove(pa_core *c, const char *name);
/* A combination of pa_property_remove() and pa_property_set() */
int pa_property_replace(pa_core *c, const char *name, void *data);
/* Free all memory used by the property system */
void pa_property_cleanup(pa_core *c);
/* Initialize the properties subsystem */
void pa_property_init(pa_core *c);
/* Dump the current set of properties */
void pa_property_dump(pa_core *c, pa_strbuf *s);
#endif

View file

@ -0,0 +1,95 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include "protocol-cli.h"
#include "cli.h"
#include "xmalloc.h"
#include "log.h"
/* Don't allow more than this many concurrent connections */
#define MAX_CONNECTIONS 25
struct pa_protocol_cli {
pa_module *module;
pa_core *core;
pa_socket_server*server;
pa_idxset *connections;
};
static void cli_eof_cb(pa_cli*c, void*userdata) {
pa_protocol_cli *p = userdata;
assert(p);
pa_idxset_remove_by_data(p->connections, c, NULL);
pa_cli_free(c);
}
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_cli *p = userdata;
pa_cli *c;
assert(s && io && p);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS);
pa_iochannel_free(io);
return;
}
c = pa_cli_new(p->core, io, p->module);
assert(c);
pa_cli_set_eof_callback(c, cli_eof_cb, p);
pa_idxset_put(p->connections, c, NULL);
}
pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) {
pa_protocol_cli* p;
assert(core && server);
p = pa_xmalloc(sizeof(pa_protocol_cli));
p->module = m;
p->core = core;
p->server = server;
p->connections = pa_idxset_new(NULL, NULL);
pa_socket_server_set_callback(p->server, on_connection, p);
return p;
}
static void free_connection(void *p, PA_GCC_UNUSED void *userdata) {
assert(p);
pa_cli_free(p);
}
void pa_protocol_cli_free(pa_protocol_cli *p) {
assert(p);
pa_idxset_free(p->connections, free_connection, NULL);
pa_socket_server_unref(p->server);
pa_xfree(p);
}

View file

@ -0,0 +1,35 @@
#ifndef fooprotocolclihfoo
#define fooprotocolclihfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "core.h"
#include "socket-server.h"
#include "module.h"
#include "modargs.h"
typedef struct pa_protocol_cli pa_protocol_cli;
pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
void pa_protocol_cli_free(pa_protocol_cli *n);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,35 @@
#ifndef fooprotocolesoundhfoo
#define fooprotocolesoundhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "core.h"
#include "socket-server.h"
#include "module.h"
#include "modargs.h"
typedef struct pa_protocol_esound pa_protocol_esound;
pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
void pa_protocol_esound_free(pa_protocol_esound *p);
#endif

View file

@ -0,0 +1,265 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "protocol-http.h"
#include "ioline.h"
#include "xmalloc.h"
#include "log.h"
#include "namereg.h"
#include "cli-text.h"
/* Don't allow more than this many concurrent connections */
#define MAX_CONNECTIONS 10
#define internal_server_error(c) http_message((c), 500, "Internal Server Error", NULL)
#define URL_ROOT "/"
#define URL_CSS "/style"
#define URL_STATUS "/status"
struct connection {
pa_protocol_http *protocol;
pa_ioline *line;
enum { REQUEST_LINE, MIME_HEADER, DATA } state;
char *url;
};
struct pa_protocol_http {
pa_module *module;
pa_core *core;
pa_socket_server*server;
pa_idxset *connections;
};
static void http_response(struct connection *c, int code, const char *msg, const char *mime) {
char s[256];
assert(c);
assert(msg);
assert(mime);
snprintf(s, sizeof(s),
"HTTP/1.0 %i %s\n"
"Connection: close\n"
"Content-Type: %s\n"
"Cache-Control: no-cache\n"
"Expires: 0\n"
"Server: "PACKAGE_NAME"/"PACKAGE_VERSION"\n"
"\n", code, msg, mime);
pa_ioline_puts(c->line, s);
}
static void http_message(struct connection *c, int code, const char *msg, const char *text) {
char s[256];
assert(c);
http_response(c, code, msg, "text/html");
if (!text)
text = msg;
snprintf(s, sizeof(s),
"<?xml version=\"1.0\"?>\n"
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
"<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>%s</title></head>\n"
"<body>%s</body></html>\n",
text, text);
pa_ioline_puts(c->line, s);
pa_ioline_defer_close(c->line);
}
static void connection_free(struct connection *c, int del) {
assert(c);
if (c->url)
pa_xfree(c->url);
if (del)
pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
pa_ioline_unref(c->line);
pa_xfree(c);
}
static void line_callback(pa_ioline *line, const char *s, void *userdata) {
struct connection *c = userdata;
assert(line);
assert(c);
if (!s) {
/* EOF */
connection_free(c, 1);
return;
}
switch (c->state) {
case REQUEST_LINE: {
if (memcmp(s, "GET ", 4))
goto fail;
s +=4;
c->url = pa_xstrndup(s, strcspn(s, " \r\n\t?"));
c->state = MIME_HEADER;
break;
}
case MIME_HEADER: {
/* Ignore MIME headers */
if (strcspn(s, " \r\n") != 0)
break;
/* We're done */
c->state = DATA;
pa_log_info(__FILE__": request for %s\n", c->url);
if (!strcmp(c->url, URL_ROOT)) {
char txt[256];
http_response(c, 200, "OK", "text/html");
pa_ioline_puts(c->line,
"<?xml version=\"1.0\"?>\n"
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
"<html xmlns=\"http://www.w3.org/1999/xhtml\"><title>"PACKAGE_NAME" "PACKAGE_VERSION"</title>\n"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"style\"/></head><body>\n");
pa_ioline_puts(c->line,
"<h1>"PACKAGE_NAME" "PACKAGE_VERSION"</h1>\n"
"<table>");
#define PRINTF_FIELD(a,b) pa_ioline_printf(c->line, "<tr><td><b>%s</b></td><td>%s</td></tr>\n",(a),(b))
PRINTF_FIELD("User Name:", pa_get_user_name(txt, sizeof(txt)));
PRINTF_FIELD("Fully Qualified Domain Name:", pa_get_fqdn(txt, sizeof(txt)));
PRINTF_FIELD("Default Sample Specification:", pa_sample_spec_snprint(txt, sizeof(txt), &c->protocol->core->default_sample_spec));
PRINTF_FIELD("Default Sink:", pa_namereg_get_default_sink_name(c->protocol->core));
PRINTF_FIELD("Default Source:", pa_namereg_get_default_source_name(c->protocol->core));
pa_ioline_puts(c->line, "</table>");
pa_ioline_puts(c->line, "<p><a href=\"/status\">Click here</a> for an extensive server status report.</p>");
pa_ioline_puts(c->line, "</body></html>\n");
pa_ioline_defer_close(c->line);
} else if (!strcmp(c->url, URL_CSS)) {
http_response(c, 200, "OK", "text/css");
pa_ioline_puts(c->line,
"body { color: black; background-color: white; margin: 0.5cm; }\n"
"a:link, a:visited { color: #900000; }\n"
"p { margin-left: 0.5cm; margin-right: 0.5cm; }\n"
"h1 { color: #00009F; }\n"
"h2 { color: #00009F; }\n"
"ul { margin-left: .5cm; }\n"
"ol { margin-left: .5cm; }\n"
"pre { margin-left: .5cm; background-color: #f0f0f0; padding: 0.4cm;}\n"
".grey { color: #afafaf; }\n"
"table { margin-left: 1cm; border:1px solid lightgrey; padding: 0.2cm; }\n"
"td { padding-left:10px; padding-right:10px; }\n");
pa_ioline_defer_close(c->line);
} else if (!strcmp(c->url, URL_STATUS)) {
char *r;
http_response(c, 200, "OK", "text/plain");
r = pa_full_status_string(c->protocol->core);
pa_ioline_puts(c->line, r);
pa_xfree(r);
pa_ioline_defer_close(c->line);
} else
http_message(c, 404, "Not Found", NULL);
break;
}
default:
;
}
return;
fail:
internal_server_error(c);
}
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_http *p = userdata;
struct connection *c;
assert(s && io && p);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log_warn(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS);
pa_iochannel_free(io);
return;
}
c = pa_xmalloc(sizeof(struct connection));
c->protocol = p;
c->line = pa_ioline_new(io);
c->state = REQUEST_LINE;
c->url = NULL;
pa_ioline_set_callback(c->line, line_callback, c);
pa_idxset_put(p->connections, c, NULL);
}
pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) {
pa_protocol_http* p;
assert(core && server);
p = pa_xmalloc(sizeof(pa_protocol_http));
p->module = m;
p->core = core;
p->server = server;
p->connections = pa_idxset_new(NULL, NULL);
pa_socket_server_set_callback(p->server, on_connection, p);
return p;
}
static void free_connection(void *p, PA_GCC_UNUSED void *userdata) {
assert(p);
connection_free(p, 0);
}
void pa_protocol_http_free(pa_protocol_http *p) {
assert(p);
pa_idxset_free(p->connections, free_connection, NULL);
pa_socket_server_unref(p->server);
pa_xfree(p);
}

View file

@ -0,0 +1,35 @@
#ifndef fooprotocolhttphfoo
#define fooprotocolhttphfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "core.h"
#include "socket-server.h"
#include "module.h"
#include "modargs.h"
typedef struct pa_protocol_http pa_protocol_http;
pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
void pa_protocol_http_free(pa_protocol_http *n);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
#ifndef fooprotocolnativehfoo
#define fooprotocolnativehfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "core.h"
#include "socket-server.h"
#include "module.h"
#include "modargs.h"
typedef struct pa_protocol_native pa_protocol_native;
pa_protocol_native* pa_protocol_native_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
void pa_protocol_native_free(pa_protocol_native *n);
pa_protocol_native* pa_protocol_native_new_iochannel(pa_core*core, pa_iochannel *io, pa_module *m, pa_modargs *ma);
#endif

View file

@ -0,0 +1,453 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "sink-input.h"
#include "source-output.h"
#include "protocol-simple.h"
#include "client.h"
#include "sample-util.h"
#include "namereg.h"
#include "xmalloc.h"
#include "log.h"
/* Don't allow more than this many concurrent connections */
#define MAX_CONNECTIONS 10
struct connection {
pa_protocol_simple *protocol;
pa_iochannel *io;
pa_sink_input *sink_input;
pa_source_output *source_output;
pa_client *client;
pa_memblockq *input_memblockq, *output_memblockq;
pa_defer_event *defer_event;
struct {
pa_memblock *current_memblock;
size_t memblock_index, fragment_size;
} playback;
};
struct pa_protocol_simple {
pa_module *module;
pa_core *core;
pa_socket_server*server;
pa_idxset *connections;
enum {
RECORD = 1,
PLAYBACK = 2,
DUPLEX = 3
} mode;
pa_sample_spec sample_spec;
char *source_name, *sink_name;
};
#define PLAYBACK_BUFFER_SECONDS (.5)
#define PLAYBACK_BUFFER_FRAGMENTS (10)
#define RECORD_BUFFER_SECONDS (5)
#define RECORD_BUFFER_FRAGMENTS (100)
static void connection_free(struct connection *c) {
assert(c);
pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
if (c->playback.current_memblock)
pa_memblock_unref(c->playback.current_memblock);
if (c->sink_input) {
pa_sink_input_disconnect(c->sink_input);
pa_sink_input_unref(c->sink_input);
}
if (c->source_output) {
pa_source_output_disconnect(c->source_output);
pa_source_output_unref(c->source_output);
}
if (c->client)
pa_client_free(c->client);
if (c->io)
pa_iochannel_free(c->io);
if (c->input_memblockq)
pa_memblockq_free(c->input_memblockq);
if (c->output_memblockq)
pa_memblockq_free(c->output_memblockq);
if (c->defer_event)
c->protocol->core->mainloop->defer_free(c->defer_event);
pa_xfree(c);
}
static int do_read(struct connection *c) {
pa_memchunk chunk;
ssize_t r;
size_t l;
if (!c->sink_input || !(l = pa_memblockq_missing(c->input_memblockq)))
return 0;
if (l > c->playback.fragment_size)
l = c->playback.fragment_size;
if (c->playback.current_memblock)
if (c->playback.current_memblock->length - c->playback.memblock_index < l) {
pa_memblock_unref(c->playback.current_memblock);
c->playback.current_memblock = NULL;
c->playback.memblock_index = 0;
}
if (!c->playback.current_memblock) {
c->playback.current_memblock = pa_memblock_new(c->playback.fragment_size*2, c->protocol->core->memblock_stat);
assert(c->playback.current_memblock && c->playback.current_memblock->length >= l);
c->playback.memblock_index = 0;
}
if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) {
pa_log(__FILE__": read() failed: %s\n", r == 0 ? "EOF" : strerror(errno));
return -1;
}
chunk.memblock = c->playback.current_memblock;
chunk.index = c->playback.memblock_index;
chunk.length = r;
assert(chunk.memblock);
c->playback.memblock_index += r;
assert(c->input_memblockq);
pa_memblockq_push_align(c->input_memblockq, &chunk, 0);
assert(c->sink_input);
pa_sink_notify(c->sink_input->sink);
return 0;
}
static int do_write(struct connection *c) {
pa_memchunk chunk;
ssize_t r;
if (!c->source_output)
return 0;
assert(c->output_memblockq);
if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
return 0;
assert(chunk.memblock && chunk.length);
if ((r = pa_iochannel_write(c->io, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) {
pa_memblock_unref(chunk.memblock);
pa_log(__FILE__": write(): %s\n", strerror(errno));
return -1;
}
pa_memblockq_drop(c->output_memblockq, &chunk, r);
pa_memblock_unref(chunk.memblock);
return 0;
}
static void do_work(struct connection *c) {
assert(c);
assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
c->protocol->core->mainloop->defer_enable(c->defer_event, 0);
if (pa_iochannel_is_writable(c->io))
if (do_write(c) < 0)
goto fail;
if (pa_iochannel_is_readable(c->io))
if (do_read(c) < 0)
goto fail;
if (pa_iochannel_is_hungup(c->io))
c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
return;
fail:
connection_free(c);
}
/*** sink_input callbacks ***/
static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) {
struct connection*c;
assert(i && i->userdata && chunk);
c = i->userdata;
if (pa_memblockq_peek(c->input_memblockq, chunk) < 0)
return -1;
return 0;
}
static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
struct connection*c = i->userdata;
assert(i && c && length);
pa_memblockq_drop(c->input_memblockq, chunk, length);
/* do something */
assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
}
static void sink_input_kill_cb(pa_sink_input *i) {
assert(i && i->userdata);
connection_free((struct connection *) i->userdata);
}
static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) {
struct connection*c = i->userdata;
assert(i && c);
return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
}
/*** source_output callbacks ***/
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
struct connection *c = o->userdata;
assert(o && c && chunk);
pa_memblockq_push(c->output_memblockq, chunk, 0);
/* do something */
assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
}
static void source_output_kill_cb(pa_source_output *o) {
assert(o && o->userdata);
connection_free((struct connection *) o->userdata);
}
static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
struct connection*c = o->userdata;
assert(o && c);
return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
}
/*** client callbacks ***/
static void client_kill_cb(pa_client *c) {
assert(c && c->userdata);
connection_free((struct connection *) c->userdata);
}
/*** pa_iochannel callbacks ***/
static void io_callback(pa_iochannel*io, void *userdata) {
struct connection *c = userdata;
assert(io && c && c->io == io);
do_work(c);
}
/*** fixed callback ***/
static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
struct connection *c = userdata;
assert(a && c && c->defer_event == e);
do_work(c);
}
/*** socket_server callbacks ***/
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_simple *p = userdata;
struct connection *c = NULL;
char cname[256];
assert(s && io && p);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS);
pa_iochannel_free(io);
return;
}
c = pa_xmalloc(sizeof(struct connection));
c->io = io;
c->sink_input = NULL;
c->source_output = NULL;
c->defer_event = NULL;
c->input_memblockq = c->output_memblockq = NULL;
c->protocol = p;
c->playback.current_memblock = NULL;
c->playback.memblock_index = 0;
c->playback.fragment_size = 0;
pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
c->client = pa_client_new(p->core, __FILE__, cname);
assert(c->client);
c->client->owner = p->module;
c->client->kill = client_kill_cb;
c->client->userdata = c;
if (p->mode & PLAYBACK) {
pa_sink *sink;
size_t l;
if (!(sink = pa_namereg_get(p->core, p->sink_name, PA_NAMEREG_SINK, 1))) {
pa_log(__FILE__": Failed to get sink.\n");
goto fail;
}
if (!(c->sink_input = pa_sink_input_new(sink, __FILE__, c->client->name, &p->sample_spec, NULL, 0, -1))) {
pa_log(__FILE__": Failed to create sink input.\n");
goto fail;
}
c->sink_input->owner = p->module;
c->sink_input->client = c->client;
c->sink_input->peek = sink_input_peek_cb;
c->sink_input->drop = sink_input_drop_cb;
c->sink_input->kill = sink_input_kill_cb;
c->sink_input->get_latency = sink_input_get_latency_cb;
c->sink_input->userdata = c;
l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS);
c->input_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&p->sample_spec), l/2, l/PLAYBACK_BUFFER_FRAGMENTS, p->core->memblock_stat);
assert(c->input_memblockq);
pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5);
c->playback.fragment_size = l/10;
}
if (p->mode & RECORD) {
pa_source *source;
size_t l;
if (!(source = pa_namereg_get(p->core, p->source_name, PA_NAMEREG_SOURCE, 1))) {
pa_log(__FILE__": Failed to get source.\n");
goto fail;
}
c->source_output = pa_source_output_new(source, __FILE__, c->client->name, &p->sample_spec, NULL, -1);
if (!c->source_output) {
pa_log(__FILE__": Failed to create source output.\n");
goto fail;
}
c->source_output->owner = p->module;
c->source_output->client = c->client;
c->source_output->push = source_output_push_cb;
c->source_output->kill = source_output_kill_cb;
c->source_output->get_latency = source_output_get_latency_cb;
c->source_output->userdata = c;
l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS);
c->output_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&p->sample_spec), 0, 0, p->core->memblock_stat);
pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2);
}
pa_iochannel_set_callback(c->io, io_callback, c);
pa_idxset_put(p->connections, c, NULL);
c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c);
assert(c->defer_event);
p->core->mainloop->defer_enable(c->defer_event, 0);
return;
fail:
if (c)
connection_free(c);
}
pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma) {
pa_protocol_simple* p = NULL;
int enable;
assert(core && server && ma);
p = pa_xmalloc0(sizeof(pa_protocol_simple));
p->module = m;
p->core = core;
p->server = server;
p->connections = pa_idxset_new(NULL, NULL);
p->sample_spec = core->default_sample_spec;
if (pa_modargs_get_sample_spec(ma, &p->sample_spec) < 0) {
pa_log(__FILE__": Failed to parse sample type specification.\n");
goto fail;
}
p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
enable = 0;
if (pa_modargs_get_value_boolean(ma, "record", &enable) < 0) {
pa_log(__FILE__": record= expects a numeric argument.\n");
goto fail;
}
p->mode = enable ? RECORD : 0;
enable = 1;
if (pa_modargs_get_value_boolean(ma, "playback", &enable) < 0) {
pa_log(__FILE__": playback= expects a numeric argument.\n");
goto fail;
}
p->mode |= enable ? PLAYBACK : 0;
if ((p->mode & (RECORD|PLAYBACK)) == 0) {
pa_log(__FILE__": neither playback nor recording enabled for protocol.\n");
goto fail;
}
pa_socket_server_set_callback(p->server, on_connection, p);
return p;
fail:
if (p)
pa_protocol_simple_free(p);
return NULL;
}
void pa_protocol_simple_free(pa_protocol_simple *p) {
struct connection *c;
assert(p);
if (p->connections) {
while((c = pa_idxset_first(p->connections, NULL)))
connection_free(c);
pa_idxset_free(p->connections, NULL, NULL);
}
if (p->server)
pa_socket_server_unref(p->server);
pa_xfree(p);
}

View file

@ -0,0 +1,35 @@
#ifndef fooprotocolsimplehfoo
#define fooprotocolsimplehfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "socket-server.h"
#include "module.h"
#include "core.h"
#include "modargs.h"
typedef struct pa_protocol_simple pa_protocol_simple;
pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
void pa_protocol_simple_free(pa_protocol_simple *n);
#endif

View file

@ -0,0 +1,61 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include "native-common.h"
#include "pstream-util.h"
void pa_pstream_send_tagstruct(pa_pstream *p, pa_tagstruct *t) {
size_t length;
uint8_t *data;
pa_packet *packet;
assert(p);
assert(t);
data = pa_tagstruct_free_data(t, &length);
assert(data && length);
packet = pa_packet_new_dynamic(data, length);
assert(packet);
pa_pstream_send_packet(p, packet);
pa_packet_unref(packet);
}
void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error) {
pa_tagstruct *t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_ERROR);
pa_tagstruct_putu32(t, tag);
pa_tagstruct_putu32(t, error);
pa_pstream_send_tagstruct(p, t);
}
void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag) {
pa_tagstruct *t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_REPLY);
pa_tagstruct_putu32(t, tag);
pa_pstream_send_tagstruct(p, t);
}

View file

@ -0,0 +1,35 @@
#ifndef foopstreamutilhfoo
#define foopstreamutilhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
#include "pstream.h"
#include "tagstruct.h"
/* The tagstruct is freed!*/
void pa_pstream_send_tagstruct(pa_pstream *p, pa_tagstruct *t);
void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error);
void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag);
#endif

492
src/polypcore/pstream.c Normal file
View file

@ -0,0 +1,492 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#include "winsock.h"
#include "pstream.h"
#include "queue.h"
#include "xmalloc.h"
#include "log.h"
typedef enum pa_pstream_descriptor_index {
PA_PSTREAM_DESCRIPTOR_LENGTH,
PA_PSTREAM_DESCRIPTOR_CHANNEL,
PA_PSTREAM_DESCRIPTOR_DELTA,
PA_PSTREAM_DESCRIPTOR_MAX
} pa_pstream_descriptor_index;
typedef uint32_t pa_pstream_descriptor[PA_PSTREAM_DESCRIPTOR_MAX];
#define PA_PSTREAM_DESCRIPTOR_SIZE (PA_PSTREAM_DESCRIPTOR_MAX*sizeof(uint32_t))
#define FRAME_SIZE_MAX (1024*500) /* half a megabyte */
struct item_info {
enum { PA_PSTREAM_ITEM_PACKET, PA_PSTREAM_ITEM_MEMBLOCK } type;
/* memblock info */
pa_memchunk chunk;
uint32_t channel;
uint32_t delta;
/* packet info */
pa_packet *packet;
};
struct pa_pstream {
int ref;
pa_mainloop_api *mainloop;
pa_defer_event *defer_event;
pa_iochannel *io;
pa_queue *send_queue;
int dead;
void (*die_callback) (pa_pstream *p, void *userdata);
void *die_callback_userdata;
struct {
struct item_info* current;
pa_pstream_descriptor descriptor;
void *data;
size_t index;
} write;
struct {
pa_memblock *memblock;
pa_packet *packet;
pa_pstream_descriptor descriptor;
void *data;
size_t index;
} read;
void (*recieve_packet_callback) (pa_pstream *p, pa_packet *packet, void *userdata);
void *recieve_packet_callback_userdata;
void (*recieve_memblock_callback) (pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata);
void *recieve_memblock_callback_userdata;
void (*drain_callback)(pa_pstream *p, void *userdata);
void *drain_userdata;
pa_memblock_stat *memblock_stat;
};
static void do_write(pa_pstream *p);
static void do_read(pa_pstream *p);
static void do_something(pa_pstream *p) {
assert(p);
p->mainloop->defer_enable(p->defer_event, 0);
pa_pstream_ref(p);
if (!p->dead && pa_iochannel_is_readable(p->io))
do_read(p);
if (!p->dead && pa_iochannel_is_writable(p->io))
do_write(p);
/* In case the line was hungup, make sure to rerun this function
as soon as possible, until all data has been read. */
if (!p->dead && pa_iochannel_is_hungup(p->io))
p->mainloop->defer_enable(p->defer_event, 1);
pa_pstream_unref(p);
}
static void io_callback(pa_iochannel*io, void *userdata) {
pa_pstream *p = userdata;
assert(p && p->io == io);
do_something(p);
}
static void defer_callback(pa_mainloop_api *m, pa_defer_event *e, void*userdata) {
pa_pstream *p = userdata;
assert(p && p->defer_event == e && p->mainloop == m);
do_something(p);
}
pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_memblock_stat *s) {
pa_pstream *p;
assert(io);
p = pa_xmalloc(sizeof(pa_pstream));
p->ref = 1;
p->io = io;
pa_iochannel_set_callback(io, io_callback, p);
p->dead = 0;
p->die_callback = NULL;
p->die_callback_userdata = NULL;
p->mainloop = m;
p->defer_event = m->defer_new(m, defer_callback, p);
m->defer_enable(p->defer_event, 0);
p->send_queue = pa_queue_new();
assert(p->send_queue);
p->write.current = NULL;
p->write.index = 0;
p->read.memblock = NULL;
p->read.packet = NULL;
p->read.index = 0;
p->recieve_packet_callback = NULL;
p->recieve_packet_callback_userdata = NULL;
p->recieve_memblock_callback = NULL;
p->recieve_memblock_callback_userdata = NULL;
p->drain_callback = NULL;
p->drain_userdata = NULL;
p->memblock_stat = s;
pa_iochannel_socket_set_rcvbuf(io, 1024*8);
pa_iochannel_socket_set_sndbuf(io, 1024*8);
return p;
}
static void item_free(void *item, PA_GCC_UNUSED void *p) {
struct item_info *i = item;
assert(i);
if (i->type == PA_PSTREAM_ITEM_MEMBLOCK) {
assert(i->chunk.memblock);
pa_memblock_unref(i->chunk.memblock);
} else {
assert(i->type == PA_PSTREAM_ITEM_PACKET);
assert(i->packet);
pa_packet_unref(i->packet);
}
pa_xfree(i);
}
static void pstream_free(pa_pstream *p) {
assert(p);
pa_pstream_close(p);
pa_queue_free(p->send_queue, item_free, NULL);
if (p->write.current)
item_free(p->write.current, NULL);
if (p->read.memblock)
pa_memblock_unref(p->read.memblock);
if (p->read.packet)
pa_packet_unref(p->read.packet);
pa_xfree(p);
}
void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet) {
struct item_info *i;
assert(p && packet && p->ref >= 1);
if (p->dead)
return;
/* pa_log(__FILE__": push-packet %p\n", packet); */
i = pa_xmalloc(sizeof(struct item_info));
i->type = PA_PSTREAM_ITEM_PACKET;
i->packet = pa_packet_ref(packet);
pa_queue_push(p->send_queue, i);
p->mainloop->defer_enable(p->defer_event, 1);
}
void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk) {
struct item_info *i;
assert(p && channel != (uint32_t) -1 && chunk && p->ref >= 1);
if (p->dead)
return;
/* pa_log(__FILE__": push-memblock %p\n", chunk); */
i = pa_xmalloc(sizeof(struct item_info));
i->type = PA_PSTREAM_ITEM_MEMBLOCK;
i->chunk = *chunk;
i->channel = channel;
i->delta = delta;
pa_memblock_ref(i->chunk.memblock);
pa_queue_push(p->send_queue, i);
p->mainloop->defer_enable(p->defer_event, 1);
}
void pa_pstream_set_recieve_packet_callback(pa_pstream *p, void (*callback) (pa_pstream *p, pa_packet *packet, void *userdata), void *userdata) {
assert(p && callback);
p->recieve_packet_callback = callback;
p->recieve_packet_callback_userdata = userdata;
}
void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, void (*callback) (pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata), void *userdata) {
assert(p && callback);
p->recieve_memblock_callback = callback;
p->recieve_memblock_callback_userdata = userdata;
}
static void prepare_next_write_item(pa_pstream *p) {
assert(p);
if (!(p->write.current = pa_queue_pop(p->send_queue)))
return;
p->write.index = 0;
if (p->write.current->type == PA_PSTREAM_ITEM_PACKET) {
/*pa_log(__FILE__": pop-packet %p\n", p->write.current->packet);*/
assert(p->write.current->packet);
p->write.data = p->write.current->packet->data;
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->packet->length);
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl((uint32_t) -1);
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA] = 0;
} else {
assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK && p->write.current->chunk.memblock);
p->write.data = (uint8_t*) p->write.current->chunk.memblock->data + p->write.current->chunk.index;
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->chunk.length);
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl(p->write.current->channel);
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA] = htonl(p->write.current->delta);
}
}
static void do_write(pa_pstream *p) {
void *d;
size_t l;
ssize_t r;
assert(p);
if (!p->write.current)
prepare_next_write_item(p);
if (!p->write.current)
return;
assert(p->write.data);
if (p->write.index < PA_PSTREAM_DESCRIPTOR_SIZE) {
d = (uint8_t*) p->write.descriptor + p->write.index;
l = PA_PSTREAM_DESCRIPTOR_SIZE - p->write.index;
} else {
d = (uint8_t*) p->write.data + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE;
l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE);
}
if ((r = pa_iochannel_write(p->io, d, l)) < 0)
goto die;
p->write.index += r;
if (p->write.index >= PA_PSTREAM_DESCRIPTOR_SIZE+ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])) {
assert(p->write.current);
item_free(p->write.current, (void *) 1);
p->write.current = NULL;
if (p->drain_callback && !pa_pstream_is_pending(p))
p->drain_callback(p, p->drain_userdata);
}
return;
die:
p->dead = 1;
if (p->die_callback)
p->die_callback(p, p->die_callback_userdata);
}
static void do_read(pa_pstream *p) {
void *d;
size_t l;
ssize_t r;
assert(p);
if (p->read.index < PA_PSTREAM_DESCRIPTOR_SIZE) {
d = (uint8_t*) p->read.descriptor + p->read.index;
l = PA_PSTREAM_DESCRIPTOR_SIZE - p->read.index;
} else {
assert(p->read.data);
d = (uint8_t*) p->read.data + p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE;
l = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE);
}
if ((r = pa_iochannel_read(p->io, d, l)) <= 0)
goto die;
p->read.index += r;
if (p->read.index == PA_PSTREAM_DESCRIPTOR_SIZE) {
/* Reading of frame descriptor complete */
/* Frame size too large */
if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) > FRAME_SIZE_MAX) {
pa_log(__FILE__": Frame size too large\n");
goto die;
}
assert(!p->read.packet && !p->read.memblock);
if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]) == (uint32_t) -1) {
/* Frame is a packet frame */
p->read.packet = pa_packet_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]));
assert(p->read.packet);
p->read.data = p->read.packet->data;
} else {
/* Frame is a memblock frame */
p->read.memblock = pa_memblock_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]), p->memblock_stat);
assert(p->read.memblock);
p->read.data = p->read.memblock->data;
}
} else if (p->read.index > PA_PSTREAM_DESCRIPTOR_SIZE) {
/* Frame payload available */
if (p->read.memblock && p->recieve_memblock_callback) { /* Is this memblock data? Than pass it to the user */
l = (p->read.index - r) < PA_PSTREAM_DESCRIPTOR_SIZE ? p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE : (size_t) r;
if (l > 0) {
pa_memchunk chunk;
chunk.memblock = p->read.memblock;
chunk.index = p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE - l;
chunk.length = l;
if (p->recieve_memblock_callback)
p->recieve_memblock_callback(
p,
ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]),
ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA]),
&chunk,
p->recieve_memblock_callback_userdata);
}
}
/* Frame complete */
if (p->read.index >= ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) + PA_PSTREAM_DESCRIPTOR_SIZE) {
if (p->read.memblock) {
assert(!p->read.packet);
pa_memblock_unref(p->read.memblock);
p->read.memblock = NULL;
} else {
assert(p->read.packet);
if (p->recieve_packet_callback)
p->recieve_packet_callback(p, p->read.packet, p->recieve_packet_callback_userdata);
pa_packet_unref(p->read.packet);
p->read.packet = NULL;
}
p->read.index = 0;
}
}
return;
die:
p->dead = 1;
if (p->die_callback)
p->die_callback(p, p->die_callback_userdata);
}
void pa_pstream_set_die_callback(pa_pstream *p, void (*callback)(pa_pstream *p, void *userdata), void *userdata) {
assert(p && callback);
p->die_callback = callback;
p->die_callback_userdata = userdata;
}
int pa_pstream_is_pending(pa_pstream *p) {
assert(p);
if (p->dead)
return 0;
return p->write.current || !pa_queue_is_empty(p->send_queue);
}
void pa_pstream_set_drain_callback(pa_pstream *p, void (*cb)(pa_pstream *p, void *userdata), void *userdata) {
assert(p);
p->drain_callback = cb;
p->drain_userdata = userdata;
}
void pa_pstream_unref(pa_pstream*p) {
assert(p && p->ref >= 1);
if (!(--(p->ref)))
pstream_free(p);
}
pa_pstream* pa_pstream_ref(pa_pstream*p) {
assert(p && p->ref >= 1);
p->ref++;
return p;
}
void pa_pstream_close(pa_pstream *p) {
assert(p);
p->dead = 1;
if (p->io) {
pa_iochannel_free(p->io);
p->io = NULL;
}
if (p->defer_event) {
p->mainloop->defer_free(p->defer_event);
p->defer_event = NULL;
}
p->die_callback = NULL;
p->drain_callback = NULL;
p->recieve_packet_callback = NULL;
p->recieve_memblock_callback = NULL;
}

52
src/polypcore/pstream.h Normal file
View file

@ -0,0 +1,52 @@
#ifndef foopstreamhfoo
#define foopstreamhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
#include "packet.h"
#include "memblock.h"
#include "iochannel.h"
#include <polyp/mainloop-api.h>
#include "memchunk.h"
typedef struct pa_pstream pa_pstream;
pa_pstream* pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_memblock_stat *s);
void pa_pstream_unref(pa_pstream*p);
pa_pstream* pa_pstream_ref(pa_pstream*p);
void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet);
void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk);
void pa_pstream_set_recieve_packet_callback(pa_pstream *p, void (*callback) (pa_pstream *p, pa_packet *packet, void *userdata), void *userdata);
void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, void (*callback) (pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata), void *userdata);
void pa_pstream_set_drain_callback(pa_pstream *p, void (*cb)(pa_pstream *p, void *userdata), void *userdata);
void pa_pstream_set_die_callback(pa_pstream *p, void (*callback)(pa_pstream *p, void *userdata), void *userdata);
int pa_pstream_is_pending(pa_pstream *p);
void pa_pstream_close(pa_pstream *p);
#endif

108
src/polypcore/queue.c Normal file
View file

@ -0,0 +1,108 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include "queue.h"
#include "xmalloc.h"
struct queue_entry {
struct queue_entry *next;
void *data;
};
struct pa_queue {
struct queue_entry *front, *back;
unsigned length;
};
pa_queue* pa_queue_new(void) {
pa_queue *q = pa_xnew(pa_queue, 1);
q->front = q->back = NULL;
q->length = 0;
return q;
}
void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata) {
struct queue_entry *e;
assert(q);
e = q->front;
while (e) {
struct queue_entry *n = e->next;
if (destroy)
destroy(e->data, userdata);
pa_xfree(e);
e = n;
}
pa_xfree(q);
}
void pa_queue_push(pa_queue *q, void *p) {
struct queue_entry *e;
e = pa_xnew(struct queue_entry, 1);
e->data = p;
e->next = NULL;
if (q->back)
q->back->next = e;
else {
assert(!q->front);
q->front = e;
}
q->back = e;
q->length++;
}
void* pa_queue_pop(pa_queue *q) {
void *p;
struct queue_entry *e;
assert(q);
if (!(e = q->front))
return NULL;
q->front = e->next;
if (q->back == e)
q->back = NULL;
p = e->data;
pa_xfree(e);
q->length--;
return p;
}
int pa_queue_is_empty(pa_queue *q) {
assert(q);
return q->length == 0;
}

40
src/polypcore/queue.h Normal file
View file

@ -0,0 +1,40 @@
#ifndef fooqueuehfoo
#define fooqueuehfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
typedef struct pa_queue pa_queue;
/* A simple implementation of the abstract data type queue. Stores
* pointers as members. The memory has to be managed by the caller. */
pa_queue* pa_queue_new(void);
/* Free the queue and run the specified callback function for every remaining entry. The callback function may be NULL. */
void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata);
void pa_queue_push(pa_queue *q, void *p);
void* pa_queue_pop(pa_queue *q);
int pa_queue_is_empty(pa_queue *q);
#endif

71
src/polypcore/random.c Normal file
View file

@ -0,0 +1,71 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
#include "random.h"
#include "util.h"
#include "log.h"
#ifndef OS_IS_WIN32
#define RANDOM_DEVICE "/dev/urandom"
#endif
void pa_random(void *ret_data, size_t length) {
int fd;
ssize_t r = 0;
assert(ret_data && length);
#ifdef RANDOM_DEVICE
if ((fd = open(RANDOM_DEVICE, O_RDONLY)) >= 0) {
if ((r = pa_loop_read(fd, ret_data, length)) < 0 || (size_t) r != length)
pa_log_error(__FILE__": failed to read entropy from '%s'\n", RANDOM_DEVICE);
close(fd);
}
#endif
if ((size_t) r != length) {
uint8_t *p;
size_t l;
#ifdef RANDOM_DEVICE
pa_log_warn(__FILE__": WARNING: Failed to open entropy device '"RANDOM_DEVICE"': %s"
", falling back to unsecure pseudo RNG.\n", strerror(errno));
#endif
srand(time(NULL));
for (p = ret_data, l = length; l > 0; p++, l--)
*p = (uint8_t) rand();
}
}

27
src/polypcore/random.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef foorandomhfoo
#define foorandomhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
polypaudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
void pa_random(void *ret_data, size_t length);
#endif

614
src/polypcore/resampler.c Normal file
View file

@ -0,0 +1,614 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <string.h>
#include <samplerate.h>
#include <liboil/liboilfuncs.h>
#include <liboil/liboil.h>
#include "resampler.h"
#include "sconv.h"
#include "xmalloc.h"
#include "log.h"
struct pa_resampler {
pa_resample_method_t resample_method;
pa_sample_spec i_ss, o_ss;
pa_channel_map i_cm, o_cm;
size_t i_fz, o_fz;
pa_memblock_stat *memblock_stat;
void (*impl_free)(pa_resampler *r);
void (*impl_update_input_rate)(pa_resampler *r, uint32_t rate);
void (*impl_run)(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out);
void *impl_data;
};
struct impl_libsamplerate {
float* buf1, *buf2, *buf3, *buf4;
unsigned buf1_samples, buf2_samples, buf3_samples, buf4_samples;
pa_convert_to_float32ne_func_t to_float32ne_func;
pa_convert_from_float32ne_func_t from_float32ne_func;
SRC_STATE *src_state;
int map_table[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
int map_required;
};
struct impl_trivial {
unsigned o_counter;
unsigned i_counter;
};
static int libsamplerate_init(pa_resampler*r);
static int trivial_init(pa_resampler*r);
pa_resampler* pa_resampler_new(
const pa_sample_spec *a,
const pa_channel_map *am,
const pa_sample_spec *b,
const pa_channel_map *bm,
pa_memblock_stat *s,
pa_resample_method_t resample_method) {
pa_resampler *r = NULL;
assert(a);
assert(b);
assert(pa_sample_spec_valid(a));
assert(pa_sample_spec_valid(b));
assert(resample_method != PA_RESAMPLER_INVALID);
r = pa_xnew(pa_resampler, 1);
r->impl_data = NULL;
r->memblock_stat = s;
r->resample_method = resample_method;
r->impl_free = NULL;
r->impl_update_input_rate = NULL;
r->impl_run = NULL;
/* Fill sample specs */
r->i_ss = *a;
r->o_ss = *b;
if (am)
r->i_cm = *am;
else
pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels);
if (bm)
r->o_cm = *bm;
else
pa_channel_map_init_auto(&r->o_cm, r->o_ss.channels);
r->i_fz = pa_frame_size(a);
r->o_fz = pa_frame_size(b);
/* Choose implementation */
if (a->channels != b->channels ||
a->format != b->format ||
!pa_channel_map_equal(&r->i_cm, &r->o_cm) ||
resample_method != PA_RESAMPLER_TRIVIAL) {
/* Use the libsamplerate based resampler for the complicated cases */
if (resample_method == PA_RESAMPLER_TRIVIAL)
r->resample_method = PA_RESAMPLER_SRC_ZERO_ORDER_HOLD;
if (libsamplerate_init(r) < 0)
goto fail;
} else {
/* Use our own simple non-fp resampler for the trivial cases and when the user selects it */
if (trivial_init(r) < 0)
goto fail;
}
return r;
fail:
if (r)
pa_xfree(r);
return NULL;
}
void pa_resampler_free(pa_resampler *r) {
assert(r);
if (r->impl_free)
r->impl_free(r);
pa_xfree(r);
}
void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate) {
assert(r);
assert(rate > 0);
if (r->i_ss.rate == rate)
return;
r->i_ss.rate = rate;
if (r->impl_update_input_rate)
r->impl_update_input_rate(r, rate);
}
void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
assert(r && in && out && r->impl_run);
r->impl_run(r, in, out);
}
size_t pa_resampler_request(pa_resampler *r, size_t out_length) {
assert(r && (out_length % r->o_fz) == 0);
return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz;
}
pa_resample_method_t pa_resampler_get_method(pa_resampler *r) {
assert(r);
return r->resample_method;
}
static const char * const resample_methods[] = {
"src-sinc-best-quality",
"src-sinc-medium-quality",
"src-sinc-fastest",
"src-zero-order-hold",
"src-linear",
"trivial"
};
const char *pa_resample_method_to_string(pa_resample_method_t m) {
if (m < 0 || m >= PA_RESAMPLER_MAX)
return NULL;
return resample_methods[m];
}
pa_resample_method_t pa_parse_resample_method(const char *string) {
pa_resample_method_t m;
assert(string);
for (m = 0; m < PA_RESAMPLER_MAX; m++)
if (!strcmp(string, resample_methods[m]))
return m;
return PA_RESAMPLER_INVALID;
}
/*** libsamplerate based implementation ***/
static void libsamplerate_free(pa_resampler *r) {
struct impl_libsamplerate *u;
assert(r);
assert(r->impl_data);
u = r->impl_data;
if (u->src_state)
src_delete(u->src_state);
pa_xfree(u->buf1);
pa_xfree(u->buf2);
pa_xfree(u->buf3);
pa_xfree(u->buf4);
pa_xfree(u);
}
static void calc_map_table(pa_resampler *r) {
struct impl_libsamplerate *u;
unsigned oc;
assert(r);
assert(r->impl_data);
u = r->impl_data;
if (!(u->map_required = (!pa_channel_map_equal(&r->i_cm, &r->o_cm) || r->i_ss.channels != r->o_ss.channels)))
return;
for (oc = 0; oc < r->o_ss.channels; oc++) {
unsigned ic, i = 0;
for (ic = 0; ic < r->i_ss.channels; ic++) {
pa_channel_position_t a, b;
a = r->i_cm.map[ic];
b = r->o_cm.map[oc];
if (a == b ||
(a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_LEFT) ||
(a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_RIGHT) ||
(a == PA_CHANNEL_POSITION_LEFT && b == PA_CHANNEL_POSITION_MONO) ||
(a == PA_CHANNEL_POSITION_RIGHT && b == PA_CHANNEL_POSITION_MONO))
u->map_table[oc][i++] = ic;
}
/* Add an end marker */
if (i < PA_CHANNELS_MAX)
u->map_table[oc][i] = -1;
}
}
static float * convert_to_float(pa_resampler *r, float *input, unsigned n_frames) {
struct impl_libsamplerate *u;
unsigned n_samples;
assert(r);
assert(input);
assert(r->impl_data);
u = r->impl_data;
/* Convert the incoming sample into floats and place them in buf1 */
if (!u->to_float32ne_func)
return input;
n_samples = n_frames * r->i_ss.channels;
if (u->buf1_samples < n_samples)
u->buf1 = pa_xrealloc(u->buf1, sizeof(float) * (u->buf1_samples = n_samples));
u->to_float32ne_func(n_samples, input, u->buf1);
return u->buf1;
}
static float *remap_channels(pa_resampler *r, float *input, unsigned n_frames) {
struct impl_libsamplerate *u;
unsigned n_samples;
int i_skip, o_skip;
unsigned oc;
assert(r);
assert(input);
assert(r->impl_data);
u = r->impl_data;
/* Remap channels and place the result int buf2 */
if (!u->map_required)
return input;
n_samples = n_frames * r->o_ss.channels;
if (u->buf2_samples < n_samples)
u->buf2 = pa_xrealloc(u->buf2, sizeof(float) * (u->buf2_samples = n_samples));
memset(u->buf2, 0, n_samples * sizeof(float));
o_skip = sizeof(float) * r->o_ss.channels;
i_skip = sizeof(float) * r->i_ss.channels;
for (oc = 0; oc < r->o_ss.channels; oc++) {
unsigned i;
static const float one = 1.0;
for (i = 0; i < PA_CHANNELS_MAX && u->map_table[oc][i] >= 0; i++)
oil_vectoradd_f32(
u->buf2 + oc, o_skip,
u->buf2 + oc, o_skip,
input + u->map_table[oc][i], i_skip,
n_frames,
&one, &one);
}
return u->buf2;
}
static float *resample(pa_resampler *r, float *input, unsigned *n_frames) {
struct impl_libsamplerate *u;
SRC_DATA data;
unsigned out_n_frames, out_n_samples;
int ret;
assert(r);
assert(input);
assert(n_frames);
assert(r->impl_data);
u = r->impl_data;
/* Resample the data and place the result in buf3 */
if (!u->src_state)
return input;
out_n_frames = (*n_frames*r->o_ss.rate/r->i_ss.rate)+1024;
out_n_samples = out_n_frames * r->o_ss.channels;
if (u->buf3_samples < out_n_samples)
u->buf3 = pa_xrealloc(u->buf3, sizeof(float) * (u->buf3_samples = out_n_samples));
data.data_in = input;
data.input_frames = *n_frames;
data.data_out = u->buf3;
data.output_frames = out_n_frames;
data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate;
data.end_of_input = 0;
ret = src_process(u->src_state, &data);
assert(ret == 0);
assert((unsigned) data.input_frames_used == *n_frames);
*n_frames = data.output_frames_gen;
return u->buf3;
}
static float *convert_from_float(pa_resampler *r, float *input, unsigned n_frames) {
struct impl_libsamplerate *u;
unsigned n_samples;
assert(r);
assert(input);
assert(r->impl_data);
u = r->impl_data;
/* Convert the data into the correct sample type and place the result in buf4 */
if (!u->from_float32ne_func)
return input;
n_samples = n_frames * r->o_ss.channels;
if (u->buf4_samples < n_samples)
u->buf4 = pa_xrealloc(u->buf4, sizeof(float) * (u->buf4_samples = n_samples));
u->from_float32ne_func(n_samples, input, u->buf4);
return u->buf4;
}
static void libsamplerate_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
struct impl_libsamplerate *u;
float *buf, *input;
unsigned n_frames;
assert(r);
assert(in);
assert(out);
assert(in->length);
assert(in->memblock);
assert(in->length % r->i_fz == 0);
assert(r->impl_data);
u = r->impl_data;
buf = input = (float*) ((uint8_t*) in->memblock->data + in->index);
n_frames = in->length / r->i_fz;
assert(n_frames > 0);
buf = convert_to_float(r, buf, n_frames);
buf = remap_channels(r, buf, n_frames);
buf = resample(r, buf, &n_frames);
if (n_frames) {
buf = convert_from_float(r, buf, n_frames);
if (buf == input) {
/* Mm, no adjustment has been necessary, so let's return the original block */
out->memblock = pa_memblock_ref(in->memblock);
out->index = in->index;
out->length = in->length;
} else {
float **p = NULL;
out->length = n_frames * r->o_fz;
out->index = 0;
if (buf == u->buf1) {
p = &u->buf1;
u->buf1_samples = 0;
} else if (buf == u->buf2) {
p = &u->buf2;
u->buf2_samples = 0;
} else if (buf == u->buf3) {
p = &u->buf3;
u->buf3_samples = 0;
} else if (buf == u->buf4) {
p = &u->buf4;
u->buf4_samples = 0;
}
assert(p);
/* Take the existing buffer and make it a memblock */
out->memblock = pa_memblock_new_dynamic(*p, out->length, r->memblock_stat);
*p = NULL;
}
} else {
out->memblock = NULL;
out->index = out->length = 0;
}
}
static void libsamplerate_update_input_rate(pa_resampler *r, uint32_t rate) {
struct impl_libsamplerate *u;
assert(r);
assert(rate > 0);
assert(r->impl_data);
u = r->impl_data;
if (!u->src_state) {
int err;
u->src_state = src_new(r->resample_method, r->o_ss.channels, &err);
assert(u->src_state);
} else {
int ret = src_set_ratio(u->src_state, (double) r->o_ss.rate / rate);
assert(ret == 0);
}
}
static int libsamplerate_init(pa_resampler *r) {
struct impl_libsamplerate *u = NULL;
int err;
r->impl_data = u = pa_xnew(struct impl_libsamplerate, 1);
u->buf1 = u->buf2 = u->buf3 = u->buf4 = NULL;
u->buf1_samples = u->buf2_samples = u->buf3_samples = u->buf4_samples = 0;
if (r->i_ss.format == PA_SAMPLE_FLOAT32NE)
u->to_float32ne_func = NULL;
else if (!(u->to_float32ne_func = pa_get_convert_to_float32ne_function(r->i_ss.format)))
goto fail;
if (r->o_ss.format == PA_SAMPLE_FLOAT32NE)
u->from_float32ne_func = NULL;
else if (!(u->from_float32ne_func = pa_get_convert_from_float32ne_function(r->o_ss.format)))
goto fail;
if (r->o_ss.rate == r->i_ss.rate)
u->src_state = NULL;
else if (!(u->src_state = src_new(r->resample_method, r->o_ss.channels, &err)))
goto fail;
r->impl_free = libsamplerate_free;
r->impl_update_input_rate = libsamplerate_update_input_rate;
r->impl_run = libsamplerate_run;
calc_map_table(r);
return 0;
fail:
pa_xfree(u);
return -1;
}
/* Trivial implementation */
static void trivial_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
size_t fz;
unsigned n_frames;
struct impl_trivial *u;
assert(r);
assert(in);
assert(out);
assert(r->impl_data);
u = r->impl_data;
fz = r->i_fz;
assert(fz == r->o_fz);
n_frames = in->length/fz;
if (r->i_ss.rate == r->o_ss.rate) {
/* In case there's no diefference in sample types, do nothing */
*out = *in;
pa_memblock_ref(out->memblock);
u->o_counter += n_frames;
} else {
/* Do real resampling */
size_t l;
unsigned o_index;
/* The length of the new memory block rounded up */
l = ((((n_frames+1) * r->o_ss.rate) / r->i_ss.rate) + 1) * fz;
out->index = 0;
out->memblock = pa_memblock_new(l, r->memblock_stat);
for (o_index = 0;; o_index++, u->o_counter++) {
unsigned j;
j = (u->o_counter * r->i_ss.rate / r->o_ss.rate);
j = j > u->i_counter ? j - u->i_counter : 0;
if (j >= n_frames)
break;
assert(o_index*fz < out->memblock->length);
memcpy((uint8_t*) out->memblock->data + fz*o_index,
(uint8_t*) in->memblock->data + in->index + fz*j, fz);
}
out->length = o_index*fz;
}
u->i_counter += n_frames;
/* Normalize counters */
while (u->i_counter >= r->i_ss.rate) {
u->i_counter -= r->i_ss.rate;
assert(u->o_counter >= r->o_ss.rate);
u->o_counter -= r->o_ss.rate;
}
}
static void trivial_free(pa_resampler *r) {
assert(r);
pa_xfree(r->impl_data);
}
static void trivial_update_input_rate(pa_resampler *r, uint32_t rate) {
struct impl_trivial *u;
assert(r);
assert(rate > 0);
assert(r->impl_data);
u = r->impl_data;
u->i_counter = 0;
u->o_counter = 0;
}
static int trivial_init(pa_resampler*r) {
struct impl_trivial *u;
assert(r);
assert(r->i_ss.format == r->o_ss.format);
assert(r->i_ss.channels == r->o_ss.channels);
r->impl_data = u = pa_xnew(struct impl_trivial, 1);
u->o_counter = u->i_counter = 0;
r->impl_run = trivial_run;
r->impl_free = trivial_free;
r->impl_update_input_rate = trivial_update_input_rate;
return 0;
}

73
src/polypcore/resampler.h Normal file
View file

@ -0,0 +1,73 @@
#ifndef fooresamplerhfoo
#define fooresamplerhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <samplerate.h>
#include <polyp/sample.h>
#include "memblock.h"
#include "memchunk.h"
#include <polyp/channelmap.h>
typedef struct pa_resampler pa_resampler;
typedef enum pa_resample_method {
PA_RESAMPLER_INVALID = -1,
PA_RESAMPLER_SRC_SINC_BEST_QUALITY = SRC_SINC_BEST_QUALITY,
PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY = SRC_SINC_MEDIUM_QUALITY,
PA_RESAMPLER_SRC_SINC_FASTEST = SRC_SINC_FASTEST,
PA_RESAMPLER_SRC_ZERO_ORDER_HOLD = SRC_ZERO_ORDER_HOLD,
PA_RESAMPLER_SRC_LINEAR = SRC_LINEAR,
PA_RESAMPLER_TRIVIAL,
PA_RESAMPLER_MAX
} pa_resample_method_t;
pa_resampler* pa_resampler_new(
const pa_sample_spec *a,
const pa_channel_map *am,
const pa_sample_spec *b,
const pa_channel_map *bm,
pa_memblock_stat *s,
pa_resample_method_t resample_method);
void pa_resampler_free(pa_resampler *r);
/* Returns the size of an input memory block which is required to return the specified amount of output data */
size_t pa_resampler_request(pa_resampler *r, size_t out_length);
/* Pass the specified memory chunk to the resampler and return the newly resampled data */
void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out);
/* Change the input rate of the resampler object */
void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate);
/* Return the resampling method of the resampler object */
pa_resample_method_t pa_resampler_get_method(pa_resampler *r);
/* Try to parse the resampler method */
pa_resample_method_t pa_parse_resample_method(const char *string);
/* return a human readable string for the specified resampling method. Inverse of pa_parse_resample_method() */
const char *pa_resample_method_to_string(pa_resample_method_t m);
#endif

328
src/polypcore/sample-util.c Normal file
View file

@ -0,0 +1,328 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <liboil/liboilfuncs.h>
#include "log.h"
#include "sample-util.h"
pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) {
assert(b && b->data && spec);
pa_silence_memory(b->data, b->length, spec);
return b;
}
void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) {
assert(c && c->memblock && c->memblock->data && spec && c->length);
pa_silence_memory((uint8_t*) c->memblock->data+c->index, c->length, spec);
}
void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
uint8_t c = 0;
assert(p && length && spec);
switch (spec->format) {
case PA_SAMPLE_U8:
c = 0x80;
break;
case PA_SAMPLE_S16LE:
case PA_SAMPLE_S16BE:
case PA_SAMPLE_FLOAT32:
c = 0;
break;
case PA_SAMPLE_ALAW:
case PA_SAMPLE_ULAW:
c = 80;
break;
default:
assert(0);
}
memset(p, c, length);
}
size_t pa_mix(
const pa_mix_info streams[],
unsigned nstreams,
void *data,
size_t length,
const pa_sample_spec *spec,
const pa_cvolume *volume) {
assert(streams && data && length && spec);
switch (spec->format) {
case PA_SAMPLE_S16NE:{
size_t d;
unsigned channel = 0;
for (d = 0;; d += sizeof(int16_t)) {
int32_t sum = 0;
if (d >= length)
return d;
if (volume->values[channel] != PA_VOLUME_MUTED) {
unsigned i;
for (i = 0; i < nstreams; i++) {
int32_t v;
pa_volume_t cvolume = streams[i].volume.values[channel];
if (d >= streams[i].chunk.length)
return d;
if (cvolume == PA_VOLUME_MUTED)
v = 0;
else {
v = *((int16_t*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d));
if (cvolume != PA_VOLUME_NORM) {
v *= cvolume;
v /= PA_VOLUME_NORM;
}
}
sum += v;
}
if (volume->values[channel] != PA_VOLUME_NORM) {
sum *= volume->values[channel];
sum /= PA_VOLUME_NORM;
}
if (sum < -0x8000) sum = -0x8000;
if (sum > 0x7FFF) sum = 0x7FFF;
}
*((int16_t*) data) = sum;
data = (uint8_t*) data + sizeof(int16_t);
if (++channel >= spec->channels)
channel = 0;
}
}
case PA_SAMPLE_U8: {
size_t d;
unsigned channel = 0;
for (d = 0;; d ++) {
int32_t sum = 0;
if (d >= length)
return d;
if (volume->values[channel] != PA_VOLUME_MUTED) {
unsigned i;
for (i = 0; i < nstreams; i++) {
int32_t v;
pa_volume_t cvolume = streams[i].volume.values[channel];
if (d >= streams[i].chunk.length)
return d;
if (cvolume == PA_VOLUME_MUTED)
v = 0;
else {
v = (int32_t) *((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d) - 0x80;
if (cvolume != PA_VOLUME_NORM) {
v *= cvolume;
v /= PA_VOLUME_NORM;
}
}
sum += v;
}
if (volume->values[channel] != PA_VOLUME_NORM) {
sum *= volume->values[channel];
sum /= PA_VOLUME_NORM;
}
if (sum < -0x80) sum = -0x80;
if (sum > 0x7F) sum = 0x7F;
}
*((uint8_t*) data) = (uint8_t) (sum + 0x80);
data = (uint8_t*) data + 1;
if (++channel >= spec->channels)
channel = 0;
}
}
case PA_SAMPLE_FLOAT32NE: {
size_t d;
unsigned channel = 0;
for (d = 0;; d += sizeof(float)) {
float sum = 0;
if (d >= length)
return d;
if (volume->values[channel] != PA_VOLUME_MUTED) {
unsigned i;
for (i = 0; i < nstreams; i++) {
float v;
pa_volume_t cvolume = streams[i].volume.values[channel];
if (d >= streams[i].chunk.length)
return d;
if (cvolume == PA_VOLUME_MUTED)
v = 0;
else {
v = *((float*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d));
if (cvolume != PA_VOLUME_NORM) {
v *= cvolume;
v /= PA_VOLUME_NORM;
}
}
sum += v;
}
if (volume->values[channel] != PA_VOLUME_NORM) {
sum *= volume->values[channel];
sum /= PA_VOLUME_NORM;
}
}
*((float*) data) = sum;
data = (uint8_t*) data + sizeof(float);
if (++channel >= spec->channels)
channel = 0;
}
}
default:
abort();
}
}
void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvolume *volume) {
assert(c && spec && (c->length % pa_frame_size(spec) == 0));
assert(volume);
if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_NORM))
return;
if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_MUTED)) {
pa_silence_memchunk(c, spec);
return;
}
switch (spec->format) {
case PA_SAMPLE_S16NE: {
int16_t *d;
size_t n;
unsigned channel = 0;
for (d = (int16_t*) ((uint8_t*) c->memblock->data+c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) {
int32_t t = (int32_t)(*d);
t *= volume->values[channel];
t /= PA_VOLUME_NORM;
if (t < -0x8000) t = -0x8000;
if (t > 0x7FFF) t = 0x7FFF;
*d = (int16_t) t;
if (++channel >= spec->channels)
channel = 0;
}
break;
}
case PA_SAMPLE_U8: {
uint8_t *d;
size_t n;
unsigned channel = 0;
for (d = (uint8_t*) c->memblock->data + c->index, n = c->length; n > 0; d++, n--) {
int32_t t = (int32_t) *d - 0x80;
t *= volume->values[channel];
t /= PA_VOLUME_NORM;
if (t < -0x80) t = -0x80;
if (t > 0x7F) t = 0x7F;
*d = (uint8_t) (t + 0x80);
if (++channel >= spec->channels)
channel = 0;
}
break;
}
case PA_SAMPLE_FLOAT32NE: {
float *d;
int skip;
unsigned n;
unsigned channel;
d = (float*) ((uint8_t*) c->memblock->data + c->index);
skip = spec->channels * sizeof(float);
n = c->length/sizeof(float)/spec->channels;
for (channel = 0; channel < spec->channels ; channel ++) {
float v, *t;
if (volume->values[channel] == PA_VOLUME_NORM)
continue;
v = (float) volume->values[channel] / PA_VOLUME_NORM;
t = d + channel;
oil_scalarmult_f32(t, skip, t, skip, &v, n);
}
break;
}
default:
pa_log_error(__FILE__": ERROR: Unable to change volume of format %s.\n",
pa_sample_format_to_string(spec->format));
abort();
}
}

View file

@ -0,0 +1,53 @@
#ifndef foosampleutilhfoo
#define foosampleutilhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <polyp/sample.h>
#include "memblock.h"
#include "memchunk.h"
#include <polyp/volume.h>
pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec);
void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec);
void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec);
typedef struct pa_mix_info {
pa_memchunk chunk;
pa_cvolume volume;
void *userdata;
} pa_mix_info;
size_t pa_mix(
const pa_mix_info channels[],
unsigned nchannels,
void *data,
size_t length,
const pa_sample_spec *spec,
const pa_cvolume *volume);
void pa_volume_memchunk(
pa_memchunk*c,
const pa_sample_spec *spec,
const pa_cvolume *volume);
#endif

392
src/polypcore/scache.c Normal file
View file

@ -0,0 +1,392 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#ifdef HAVE_GLOB_H
#include <glob.h>
#endif
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include "scache.h"
#include "sink-input.h"
#include <polyp/mainloop.h>
#include "sample-util.h"
#include "play-memchunk.h"
#include "xmalloc.h"
#include "subscribe.h"
#include "namereg.h"
#include "sound-file.h"
#include "util.h"
#include "log.h"
#include <polyp/channelmap.h>
#include <polyp/volume.h>
#define UNLOAD_POLL_TIME 2
static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
pa_core *c = userdata;
struct timeval ntv;
assert(c && c->mainloop == m && c->scache_auto_unload_event == e);
pa_scache_unload_unused(c);
pa_gettimeofday(&ntv);
ntv.tv_sec += UNLOAD_POLL_TIME;
m->time_restart(e, &ntv);
}
static void free_entry(pa_scache_entry *e) {
assert(e);
pa_namereg_unregister(e->core, e->name);
pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_REMOVE, e->index);
pa_xfree(e->name);
pa_xfree(e->filename);
if (e->memchunk.memblock)
pa_memblock_unref(e->memchunk.memblock);
pa_xfree(e);
}
static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
pa_scache_entry *e;
assert(c && name);
if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) {
if (e->memchunk.memblock)
pa_memblock_unref(e->memchunk.memblock);
pa_xfree(e->filename);
assert(e->core == c);
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
} else {
e = pa_xmalloc(sizeof(pa_scache_entry));
if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, 1)) {
pa_xfree(e);
return NULL;
}
e->name = pa_xstrdup(name);
e->core = c;
if (!c->scache) {
c->scache = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
assert(c->scache);
}
pa_idxset_put(c->scache, e, &e->index);
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_NEW, e->index);
}
pa_cvolume_reset(&e->volume, PA_CHANNELS_MAX);
e->last_used_time = 0;
e->memchunk.memblock = NULL;
e->memchunk.index = e->memchunk.length = 0;
e->filename = NULL;
e->lazy = 0;
e->last_used_time = 0;
memset(&e->sample_spec, 0, sizeof(pa_sample_spec));
return e;
}
int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx) {
pa_scache_entry *e;
assert(c && name);
if (!(e = scache_add_item(c, name)))
return -1;
if (ss) {
e->sample_spec = *ss;
pa_channel_map_init_auto(&e->channel_map, ss->channels);
}
if (map)
e->channel_map = *map;
if (chunk) {
e->memchunk = *chunk;
pa_memblock_ref(e->memchunk.memblock);
}
if (idx)
*idx = e->index;
return 0;
}
int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx) {
pa_sample_spec ss;
pa_memchunk chunk;
int r;
#ifdef OS_IS_WIN32
char buf[MAX_PATH];
if (ExpandEnvironmentStrings(filename, buf, MAX_PATH))
filename = buf;
#endif
if (pa_sound_file_load(filename, &ss, &chunk, c->memblock_stat) < 0)
return -1;
r = pa_scache_add_item(c, name, &ss, NULL, &chunk, idx);
pa_memblock_unref(chunk.memblock);
return r;
}
int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx) {
pa_scache_entry *e;
#ifdef OS_IS_WIN32
char buf[MAX_PATH];
if (ExpandEnvironmentStrings(filename, buf, MAX_PATH))
filename = buf;
#endif
assert(c && name);
if (!(e = scache_add_item(c, name)))
return -1;
e->lazy = 1;
e->filename = pa_xstrdup(filename);
if (!c->scache_auto_unload_event) {
struct timeval ntv;
pa_gettimeofday(&ntv);
ntv.tv_sec += UNLOAD_POLL_TIME;
c->scache_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);
}
if (idx)
*idx = e->index;
return 0;
}
int pa_scache_remove_item(pa_core *c, const char *name) {
pa_scache_entry *e;
assert(c && name);
if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
return -1;
if (pa_idxset_remove_by_data(c->scache, e, NULL) != e)
assert(0);
free_entry(e);
return 0;
}
static void free_cb(void *p, PA_GCC_UNUSED void *userdata) {
pa_scache_entry *e = p;
assert(e);
free_entry(e);
}
void pa_scache_free(pa_core *c) {
assert(c);
if (c->scache) {
pa_idxset_free(c->scache, free_cb, NULL);
c->scache = NULL;
}
if (c->scache_auto_unload_event)
c->mainloop->time_free(c->scache_auto_unload_event);
}
int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, const pa_cvolume *volume) {
pa_scache_entry *e;
char *t;
pa_cvolume r;
assert(c && name && sink);
if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 1)))
return -1;
if (e->lazy && !e->memchunk.memblock) {
if (pa_sound_file_load(e->filename, &e->sample_spec, &e->memchunk, c->memblock_stat) < 0)
return -1;
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
}
if (!e->memchunk.memblock)
return -1;
t = pa_sprintf_malloc("sample:%s", name);
if (pa_play_memchunk(sink, t, &e->sample_spec, &e->channel_map, &e->memchunk, pa_sw_cvolume_multiply(&r, volume, &e->volume)) < 0) {
free(t);
return -1;
}
free(t);
if (e->lazy)
time(&e->last_used_time);
return 0;
}
const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
pa_scache_entry *e;
assert(c && id != PA_IDXSET_INVALID);
if (!c->scache || !(e = pa_idxset_get_by_index(c->scache, id)))
return NULL;
return e->name;
}
uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
pa_scache_entry *e;
assert(c && name);
if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
return PA_IDXSET_INVALID;
return e->index;
}
uint32_t pa_scache_total_size(pa_core *c) {
pa_scache_entry *e;
uint32_t idx, sum = 0;
assert(c);
if (!c->scache || !pa_idxset_size(c->scache))
return 0;
for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx))
if (e->memchunk.memblock)
sum += e->memchunk.length;
return sum;
}
void pa_scache_unload_unused(pa_core *c) {
pa_scache_entry *e;
time_t now;
uint32_t idx;
assert(c);
if (!c->scache || !pa_idxset_size(c->scache))
return;
time(&now);
for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) {
if (!e->lazy || !e->memchunk.memblock)
continue;
if (e->last_used_time + c->scache_idle_time > now)
continue;
pa_memblock_unref(e->memchunk.memblock);
e->memchunk.memblock = NULL;
e->memchunk.index = e->memchunk.length = 0;
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
}
}
static void add_file(pa_core *c, const char *pathname) {
struct stat st;
const char *e;
e = pa_path_get_filename(pathname);
if (stat(pathname, &st) < 0) {
pa_log(__FILE__": stat('%s') failed: %s\n", pathname, strerror(errno));
return;
}
#if defined(S_ISREG) && defined(S_ISLNK)
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
#endif
pa_scache_add_file_lazy(c, e, pathname, NULL);
}
int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
DIR *dir;
assert(c && pathname);
/* First try to open this as directory */
if (!(dir = opendir(pathname))) {
#ifdef HAVE_GLOB_H
glob_t p;
unsigned int i;
/* If that fails, try to open it as shell glob */
if (glob(pathname, GLOB_ERR|GLOB_NOSORT, NULL, &p) < 0) {
pa_log(__FILE__": Failed to open directory: %s\n", strerror(errno));
return -1;
}
for (i = 0; i < p.gl_pathc; i++)
add_file(c, p.gl_pathv[i]);
globfree(&p);
#else
return -1;
#endif
} else {
struct dirent *e;
while ((e = readdir(dir))) {
char p[PATH_MAX];
if (e->d_name[0] == '.')
continue;
snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name);
add_file(c, p);
}
}
closedir(dir);
return 0;
}

62
src/polypcore/scache.h Normal file
View file

@ -0,0 +1,62 @@
#ifndef fooscachehfoo
#define fooscachehfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
polypaudio 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 Lesser General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "core.h"
#include "memchunk.h"
#include "sink.h"
typedef struct pa_scache_entry {
pa_core *core;
uint32_t index;
char *name;
pa_cvolume volume;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_memchunk memchunk;
char *filename;
int lazy;
time_t last_used_time;
} pa_scache_entry;
int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx);
int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx);
int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx);
int pa_scache_add_directory_lazy(pa_core *c, const char *pathname);
int pa_scache_remove_item(pa_core *c, const char *name);
int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, const pa_cvolume *cvolume);
void pa_scache_free(pa_core *c);
const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id);
uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name);
uint32_t pa_scache_total_size(pa_core *c);
void pa_scache_unload_unused(pa_core *c);
#endif

Some files were not shown because too many files have changed in this diff Show more