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

42
src/polyp/cdecl.h Normal file
View file

@ -0,0 +1,42 @@
#ifndef foocdeclhfoo
#define foocdeclhfoo
/* $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.
***/
/** \file
* C++ compatibility support */
#ifdef __cplusplus
/** If using C++ this macro enables C mode, otherwise does nothing */
#define PA_C_DECL_BEGIN extern "C" {
/** If using C++ this macros switches back to C++ mode, otherwise does nothing */
#define PA_C_DECL_END }
#else
/** If using C++ this macro enables C mode, otherwise does nothing */
#define PA_C_DECL_BEGIN
/** If using C++ this macros switches back to C++ mode, otherwise does nothing */
#define PA_C_DECL_END
#endif
#endif

202
src/polyp/channelmap.c Normal file
View file

@ -0,0 +1,202 @@
/* $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 "channelmap.h"
pa_channel_map* pa_channel_map_init(pa_channel_map *m) {
unsigned c;
assert(m);
m->channels = 0;
for (c = 0; c < PA_CHANNELS_MAX; c++)
m->map[c] = PA_CHANNEL_POSITION_INVALID;
return m;
}
pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) {
assert(m);
pa_channel_map_init(m);
m->channels = 1;
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
}
pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) {
assert(m);
pa_channel_map_init(m);
m->channels = 2;
m->map[0] = PA_CHANNEL_POSITION_LEFT;
m->map[1] = PA_CHANNEL_POSITION_RIGHT;
return m;
}
pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels) {
assert(m);
assert(channels > 0);
assert(channels <= PA_CHANNELS_MAX);
pa_channel_map_init(m);
m->channels = channels;
switch (channels) {
case 1:
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
case 8:
m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
/* Fall through */
case 6:
m->map[5] = PA_CHANNEL_POSITION_LFE;
/* Fall through */
case 5:
m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
/* Fall through */
case 4:
m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
/* Fall through */
case 2:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
return m;
default:
return NULL;
}
}
const char* pa_channel_position_to_string(pa_channel_position_t pos) {
const char *const table[] = {
[PA_CHANNEL_POSITION_MONO] = "mono",
[PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center",
[PA_CHANNEL_POSITION_FRONT_LEFT] = "front-left",
[PA_CHANNEL_POSITION_FRONT_RIGHT] = "front-right",
[PA_CHANNEL_POSITION_REAR_CENTER] = "rear-center",
[PA_CHANNEL_POSITION_REAR_LEFT] = "rear-left",
[PA_CHANNEL_POSITION_REAR_RIGHT] = "rear-right",
[PA_CHANNEL_POSITION_LFE] = "lfe",
[PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "front-left-of-center",
[PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "front-right-of-center",
[PA_CHANNEL_POSITION_SIDE_LEFT] = "side-left",
[PA_CHANNEL_POSITION_SIDE_RIGHT] = "side-right",
[PA_CHANNEL_POSITION_AUX1] = "aux1",
[PA_CHANNEL_POSITION_AUX2] = "aux2",
[PA_CHANNEL_POSITION_AUX3] = "aux3",
[PA_CHANNEL_POSITION_AUX4] = "aux4",
[PA_CHANNEL_POSITION_AUX5] = "aux5",
[PA_CHANNEL_POSITION_AUX6] = "aux6",
[PA_CHANNEL_POSITION_AUX7] = "aux7",
[PA_CHANNEL_POSITION_AUX8] = "aux8",
[PA_CHANNEL_POSITION_AUX9] = "aux9",
[PA_CHANNEL_POSITION_AUX10] = "aux10",
[PA_CHANNEL_POSITION_AUX11] = "aux11",
[PA_CHANNEL_POSITION_AUX12] = "aux12"
};
if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
return NULL;
return table[pos];
}
int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {
unsigned c;
assert(a);
assert(b);
if (a->channels != b->channels)
return 0;
for (c = 0; c < a->channels; c++)
if (a->map[c] != b->map[c])
return 0;
return 1;
}
char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {
unsigned channel;
int first = 1;
char *e;
assert(s);
assert(l > 0);
assert(map);
*(e = s) = 0;
for (channel = 0; channel < map->channels && l > 1; channel++) {
l -= snprintf(e, l, "%s%u:%s",
first ? "" : " ",
channel,
pa_channel_position_to_string(map->map[channel]));
e = strchr(e, 0);
first = 0;
}
return s;
}
int pa_channel_map_valid(const pa_channel_map *map) {
unsigned c;
assert(map);
if (map->channels <= 0 || map->channels > PA_CHANNELS_MAX)
return 0;
for (c = 0; c < map->channels; c++)
if (map->map[c] < 0 ||map->map[c] >= PA_CHANNEL_POSITION_MAX)
return 0;
return 1;
}

98
src/polyp/channelmap.h Normal file
View file

@ -0,0 +1,98 @@
#ifndef foochannelmaphfoo
#define foochannelmaphfoo
/* $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 <polyp/cdecl.h>
/** \file
* Constants and routines for channel mapping handling */
PA_C_DECL_BEGIN
typedef enum {
PA_CHANNEL_POSITION_INVALID = -1,
PA_CHANNEL_POSITION_MONO = 0,
PA_CHANNEL_POSITION_LEFT,
PA_CHANNEL_POSITION_RIGHT,
PA_CHANNEL_POSITION_FRONT_CENTER,
PA_CHANNEL_POSITION_FRONT_LEFT = PA_CHANNEL_POSITION_LEFT,
PA_CHANNEL_POSITION_FRONT_RIGHT = PA_CHANNEL_POSITION_RIGHT,
PA_CHANNEL_POSITION_REAR_CENTER,
PA_CHANNEL_POSITION_REAR_LEFT,
PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_LFE,
PA_CHANNEL_POSITION_SUBWOOFER = PA_CHANNEL_POSITION_LFE,
PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
PA_CHANNEL_POSITION_SIDE_LEFT,
PA_CHANNEL_POSITION_SIDE_RIGHT,
PA_CHANNEL_POSITION_AUX0,
PA_CHANNEL_POSITION_AUX1,
PA_CHANNEL_POSITION_AUX2,
PA_CHANNEL_POSITION_AUX3,
PA_CHANNEL_POSITION_AUX4,
PA_CHANNEL_POSITION_AUX5,
PA_CHANNEL_POSITION_AUX6,
PA_CHANNEL_POSITION_AUX7,
PA_CHANNEL_POSITION_AUX8,
PA_CHANNEL_POSITION_AUX9,
PA_CHANNEL_POSITION_AUX10,
PA_CHANNEL_POSITION_AUX11,
PA_CHANNEL_POSITION_AUX12,
PA_CHANNEL_POSITION_AUX13,
PA_CHANNEL_POSITION_AUX14,
PA_CHANNEL_POSITION_AUX15,
PA_CHANNEL_POSITION_MAX
} pa_channel_position_t;
typedef struct pa_channel_map {
uint8_t channels;
pa_channel_position_t map[PA_CHANNELS_MAX];
} pa_channel_map;
pa_channel_map* pa_channel_map_init(pa_channel_map *m);
pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m);
pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m);
pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels);
const char* pa_channel_position_to_string(pa_channel_position_t pos);
#define PA_CHANNEL_MAP_SNPRINT_MAX 64
char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map);
int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b);
int pa_channel_map_valid(const pa_channel_map *map);
PA_C_DECL_END
#endif

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-13071
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <assert.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include "client-conf-x11.h"
#include <polypcore/x11prop.h>
#include <polypcore/log.h>
#include <polypcore/xmalloc.h>
#include <polypcore/util.h>
int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) {
Display *d = NULL;
int ret = -1;
char t[1024];
if (!dname && !getenv("DISPLAY"))
goto finish;
if (!(d = XOpenDisplay(dname))) {
pa_log(__FILE__": XOpenDisplay() failed\n");
goto finish;
}
if (pa_x11_get_prop(d, "POLYP_SERVER", t, sizeof(t))) {
pa_xfree(c->default_server);
c->default_server = pa_xstrdup(t);
}
if (pa_x11_get_prop(d, "POLYP_SINK", t, sizeof(t))) {
pa_xfree(c->default_sink);
c->default_sink = pa_xstrdup(t);
}
if (pa_x11_get_prop(d, "POLYP_SOURCE", t, sizeof(t))) {
pa_xfree(c->default_source);
c->default_source = pa_xstrdup(t);
}
if (pa_x11_get_prop(d, "POLYP_COOKIE", t, sizeof(t))) {
uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
if (pa_parsehex(t, cookie, sizeof(cookie)) != sizeof(cookie)) {
pa_log(__FILE__": failed to parse cookie data\n");
goto finish;
}
assert(sizeof(cookie) == sizeof(c->cookie));
memcpy(c->cookie, cookie, sizeof(cookie));
c->cookie_valid = 1;
pa_xfree(c->cookie_file);
c->cookie_file = NULL;
}
ret = 0;
finish:
if (d)
XCloseDisplay(d);
return ret;
}

View file

@ -0,0 +1,31 @@
#ifndef fooclientconfx11hfoo
#define fooclientconfx11hfoo
/* $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/client-conf.h>
/* Load client configuration data from the specified X11 display,
* overwriting the current settings in *c */
int pa_client_conf_from_x11(pa_client_conf *c, const char *display);
#endif

191
src/polyp/client-conf.c Normal file
View file

@ -0,0 +1,191 @@
/* $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 <unistd.h>
#include <errno.h>
#include <string.h>
#include <polypcore/xmalloc.h>
#include <polypcore/log.h>
#include <polypcore/conf-parser.h>
#include <polypcore/util.h>
#include <polypcore/authkey.h>
#include <polyp/client-conf.h>
#ifndef DEFAULT_CONFIG_DIR
# ifndef OS_IS_WIN32
# define DEFAULT_CONFIG_DIR "/etc/polypaudio"
# else
# define DEFAULT_CONFIG_DIR "%POLYP_ROOT%"
# endif
#endif
#ifndef OS_IS_WIN32
# define PATH_SEP "/"
#else
# define PATH_SEP "\\"
#endif
#define DEFAULT_CLIENT_CONFIG_FILE DEFAULT_CONFIG_DIR PATH_SEP "client.conf"
#define DEFAULT_CLIENT_CONFIG_FILE_USER ".polypaudio" PATH_SEP "client.conf"
#define ENV_CLIENT_CONFIG_FILE "POLYP_CLIENTCONFIG"
#define ENV_DEFAULT_SINK "POLYP_SINK"
#define ENV_DEFAULT_SOURCE "POLYP_SOURCE"
#define ENV_DEFAULT_SERVER "POLYP_SERVER"
#define ENV_DAEMON_BINARY "POLYP_BINARY"
#define ENV_COOKIE_FILE "POLYP_COOKIE"
static const pa_client_conf default_conf = {
.daemon_binary = NULL,
.extra_arguments = NULL,
.default_sink = NULL,
.default_source = NULL,
.default_server = NULL,
.autospawn = 0,
.cookie_file = NULL,
.cookie_valid = 0
};
pa_client_conf *pa_client_conf_new(void) {
pa_client_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf));
c->daemon_binary = pa_xstrdup(POLYPAUDIO_BINARY);
c->extra_arguments = pa_xstrdup("--log-target=syslog --exit-idle-time=5");
c->cookie_file = pa_xstrdup(PA_NATIVE_COOKIE_FILE);
return c;
}
void pa_client_conf_free(pa_client_conf *c) {
assert(c);
pa_xfree(c->daemon_binary);
pa_xfree(c->extra_arguments);
pa_xfree(c->default_sink);
pa_xfree(c->default_source);
pa_xfree(c->default_server);
pa_xfree(c->cookie_file);
pa_xfree(c);
}
int pa_client_conf_load(pa_client_conf *c, const char *filename) {
FILE *f = NULL;
char *fn = NULL;
int r = -1;
/* Prepare the configuration parse table */
pa_config_item table[] = {
{ "daemon-binary", pa_config_parse_string, NULL },
{ "extra-arguments", pa_config_parse_string, NULL },
{ "default-sink", pa_config_parse_string, NULL },
{ "default-source", pa_config_parse_string, NULL },
{ "default-server", pa_config_parse_string, NULL },
{ "autospawn", pa_config_parse_bool, NULL },
{ "cookie-file", pa_config_parse_string, NULL },
{ NULL, NULL, NULL },
};
table[0].data = &c->daemon_binary;
table[1].data = &c->extra_arguments;
table[2].data = &c->default_sink;
table[3].data = &c->default_source;
table[4].data = &c->default_server;
table[5].data = &c->autospawn;
table[6].data = &c->cookie_file;
f = filename ?
fopen((fn = pa_xstrdup(filename)), "r") :
pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn);
if (!f && errno != EINTR) {
pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s\n", filename, strerror(errno));
goto finish;
}
r = f ? pa_config_parse(fn, f, table, NULL) : 0;
if (!r)
r = pa_client_conf_load_cookie(c);
finish:
pa_xfree(fn);
if (f)
fclose(f);
return r;
}
int pa_client_conf_env(pa_client_conf *c) {
char *e;
if ((e = getenv(ENV_DEFAULT_SINK))) {
pa_xfree(c->default_sink);
c->default_sink = pa_xstrdup(e);
}
if ((e = getenv(ENV_DEFAULT_SOURCE))) {
pa_xfree(c->default_source);
c->default_source = pa_xstrdup(e);
}
if ((e = getenv(ENV_DEFAULT_SERVER))) {
pa_xfree(c->default_server);
c->default_server = pa_xstrdup(e);
}
if ((e = getenv(ENV_DAEMON_BINARY))) {
pa_xfree(c->daemon_binary);
c->daemon_binary = pa_xstrdup(e);
}
if ((e = getenv(ENV_COOKIE_FILE))) {
pa_xfree(c->cookie_file);
c->cookie_file = pa_xstrdup(e);
return pa_client_conf_load_cookie(c);
}
return 0;
}
int pa_client_conf_load_cookie(pa_client_conf* c) {
assert(c);
c->cookie_valid = 0;
if (!c->cookie_file)
return -1;
if (pa_authkey_load_auto(c->cookie_file, c->cookie, sizeof(c->cookie)) < 0)
return -1;
c->cookie_valid = 1;
return 0;
}

52
src/polyp/client-conf.h Normal file
View file

@ -0,0 +1,52 @@
#ifndef fooclientconfhfoo
#define fooclientconfhfoo
/* $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 "../polypcore/native-common.h"
/* A structure containing configuration data for polypaudio clients. */
typedef struct pa_client_conf {
char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file;
int autospawn;
uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
int cookie_valid; /* non-zero, when cookie is valid */
} pa_client_conf;
/* Create a new configuration data object and reset it to defaults */
pa_client_conf *pa_client_conf_new(void);
void pa_client_conf_free(pa_client_conf *c);
/* Load the configuration data from the speicified file, overwriting
* the current settings in *c. When the filename is NULL, the
* default client configuration file name is used. */
int pa_client_conf_load(pa_client_conf *c, const char *filename);
/* Load the configuration data from the environment of the current
process, overwriting the current settings in *c. */
int pa_client_conf_env(pa_client_conf *c);
/* Load cookie data from c->cookie_file into c->cookie */
int pa_client_conf_load_cookie(pa_client_conf* c);
#endif

538
src/polyp/glib-mainloop.c Normal file
View file

@ -0,0 +1,538 @@
/* $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 <polyp/glib-mainloop.h>
#include <polypcore/idxset.h>
#include <polypcore/xmalloc.h>
#include "glib.h"
#include <polypcore/util.h>
struct pa_io_event {
pa_glib_mainloop *mainloop;
int dead;
GIOChannel *io_channel;
GSource *source;
GIOCondition io_condition;
int fd;
void (*callback) (pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata);
void *userdata;
void (*destroy_callback) (pa_mainloop_api *m, pa_io_event *e, void *userdata);
pa_io_event *next, *prev;
};
struct pa_time_event {
pa_glib_mainloop *mainloop;
int dead;
GSource *source;
struct timeval timeval;
void (*callback) (pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata);
void *userdata;
void (*destroy_callback) (pa_mainloop_api *m, pa_time_event*e, void *userdata);
pa_time_event *next, *prev;
};
struct pa_defer_event {
pa_glib_mainloop *mainloop;
int dead;
GSource *source;
void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata);
void *userdata;
void (*destroy_callback) (pa_mainloop_api *m, pa_defer_event*e, void *userdata);
pa_defer_event *next, *prev;
};
struct pa_glib_mainloop {
GMainContext *glib_main_context;
pa_mainloop_api api;
GSource *cleanup_source;
pa_io_event *io_events, *dead_io_events;
pa_time_event *time_events, *dead_time_events;
pa_defer_event *defer_events, *dead_defer_events;
};
static void schedule_free_dead_events(pa_glib_mainloop *g);
static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f);
static pa_io_event* glib_io_new(pa_mainloop_api*m, int fd, pa_io_event_flags_t f, void (*callback) (pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata), void *userdata) {
pa_io_event *e;
pa_glib_mainloop *g;
assert(m && m->userdata && fd >= 0 && callback);
g = m->userdata;
e = pa_xmalloc(sizeof(pa_io_event));
e->mainloop = m->userdata;
e->dead = 0;
e->fd = fd;
e->callback = callback;
e->userdata = userdata;
e->destroy_callback = NULL;
e->io_channel = g_io_channel_unix_new(e->fd);
assert(e->io_channel);
e->source = NULL;
e->io_condition = 0;
glib_io_enable(e, f);
e->next = g->io_events;
if (e->next) e->next->prev = e;
g->io_events = e;
e->prev = NULL;
return e;
}
/* The callback GLIB calls whenever an IO condition is met */
static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) {
pa_io_event *e = data;
pa_io_event_flags_t f;
assert(source && e && e->io_channel == source);
f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) |
(condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) |
(condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) |
(condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0);
e->callback(&e->mainloop->api, e, e->fd, f, e->userdata);
return TRUE;
}
static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) {
GIOCondition c;
assert(e && !e->dead);
c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0);
if (c == e->io_condition)
return;
if (e->source) {
g_source_destroy(e->source);
g_source_unref(e->source);
}
e->source = g_io_create_watch(e->io_channel, c | G_IO_ERR | G_IO_HUP);
assert(e->source);
g_source_set_callback(e->source, (GSourceFunc) io_cb, e, NULL);
g_source_attach(e->source, e->mainloop->glib_main_context);
g_source_set_priority(e->source, G_PRIORITY_DEFAULT);
e->io_condition = c;
}
static void glib_io_free(pa_io_event*e) {
assert(e && !e->dead);
if (e->source) {
g_source_destroy(e->source);
g_source_unref(e->source);
e->source = NULL;
}
if (e->prev)
e->prev->next = e->next;
else
e->mainloop->io_events = e->next;
if (e->next)
e->next->prev = e->prev;
if ((e->next = e->mainloop->dead_io_events))
e->next->prev = e;
e->mainloop->dead_io_events = e;
e->prev = NULL;
e->dead = 1;
schedule_free_dead_events(e->mainloop);
}
static void glib_io_set_destroy(pa_io_event*e, void (*callback)(pa_mainloop_api*m, pa_io_event *e, void *userdata)) {
assert(e);
e->destroy_callback = callback;
}
/* Time sources */
static void glib_time_restart(pa_time_event*e, const struct timeval *tv);
static pa_time_event* glib_time_new(pa_mainloop_api*m, const struct timeval *tv, void (*callback) (pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) {
pa_glib_mainloop *g;
pa_time_event *e;
assert(m && m->userdata && tv && callback);
g = m->userdata;
e = pa_xmalloc(sizeof(pa_time_event));
e->mainloop = g;
e->dead = 0;
e->callback = callback;
e->userdata = userdata;
e->destroy_callback = NULL;
e->source = NULL;
glib_time_restart(e, tv);
e->next = g->time_events;
if (e->next) e->next->prev = e;
g->time_events = e;
e->prev = NULL;
return e;
}
static guint msec_diff(const struct timeval *a, const struct timeval *b) {
guint r;
assert(a && b);
if (a->tv_sec < b->tv_sec)
return 0;
if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec)
return 0;
r = (a->tv_sec-b->tv_sec)*1000;
if (a->tv_usec >= b->tv_usec)
r += (a->tv_usec - b->tv_usec) / 1000;
else
r -= (b->tv_usec - a->tv_usec) / 1000;
return r;
}
static gboolean time_cb(gpointer data) {
pa_time_event* e = data;
assert(e && e->mainloop && e->source);
g_source_unref(e->source);
e->source = NULL;
e->callback(&e->mainloop->api, e, &e->timeval, e->userdata);
return FALSE;
}
static void glib_time_restart(pa_time_event*e, const struct timeval *tv) {
struct timeval now;
assert(e && e->mainloop && !e->dead);
pa_gettimeofday(&now);
if (e->source) {
g_source_destroy(e->source);
g_source_unref(e->source);
}
if (tv) {
e->timeval = *tv;
e->source = g_timeout_source_new(msec_diff(tv, &now));
assert(e->source);
g_source_set_callback(e->source, time_cb, e, NULL);
g_source_set_priority(e->source, G_PRIORITY_DEFAULT);
g_source_attach(e->source, e->mainloop->glib_main_context);
} else
e->source = NULL;
}
static void glib_time_free(pa_time_event *e) {
assert(e && e->mainloop && !e->dead);
if (e->source) {
g_source_destroy(e->source);
g_source_unref(e->source);
e->source = NULL;
}
if (e->prev)
e->prev->next = e->next;
else
e->mainloop->time_events = e->next;
if (e->next)
e->next->prev = e->prev;
if ((e->next = e->mainloop->dead_time_events))
e->next->prev = e;
e->mainloop->dead_time_events = e;
e->prev = NULL;
e->dead = 1;
schedule_free_dead_events(e->mainloop);
}
static void glib_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*m, pa_time_event*e, void *userdata)) {
assert(e);
e->destroy_callback = callback;
}
/* Deferred sources */
static void glib_defer_enable(pa_defer_event *e, int b);
static pa_defer_event* glib_defer_new(pa_mainloop_api*m, void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata), void *userdata) {
pa_defer_event *e;
pa_glib_mainloop *g;
assert(m && m->userdata && callback);
g = m->userdata;
e = pa_xmalloc(sizeof(pa_defer_event));
e->mainloop = g;
e->dead = 0;
e->callback = callback;
e->userdata = userdata;
e->destroy_callback = NULL;
e->source = NULL;
glib_defer_enable(e, 1);
e->next = g->defer_events;
if (e->next) e->next->prev = e;
g->defer_events = e;
e->prev = NULL;
return e;
}
static gboolean idle_cb(gpointer data) {
pa_defer_event* e = data;
assert(e && e->mainloop && e->source);
e->callback(&e->mainloop->api, e, e->userdata);
return TRUE;
}
static void glib_defer_enable(pa_defer_event *e, int b) {
assert(e && e->mainloop);
if (e->source && !b) {
g_source_destroy(e->source);
g_source_unref(e->source);
e->source = NULL;
} else if (!e->source && b) {
e->source = g_idle_source_new();
assert(e->source);
g_source_set_callback(e->source, idle_cb, e, NULL);
g_source_attach(e->source, e->mainloop->glib_main_context);
g_source_set_priority(e->source, G_PRIORITY_HIGH);
}
}
static void glib_defer_free(pa_defer_event *e) {
assert(e && e->mainloop && !e->dead);
if (e->source) {
g_source_destroy(e->source);
g_source_unref(e->source);
e->source = NULL;
}
if (e->prev)
e->prev->next = e->next;
else
e->mainloop->defer_events = e->next;
if (e->next)
e->next->prev = e->prev;
if ((e->next = e->mainloop->dead_defer_events))
e->next->prev = e;
e->mainloop->dead_defer_events = e;
e->prev = NULL;
e->dead = 1;
schedule_free_dead_events(e->mainloop);
}
static void glib_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api *m, pa_defer_event *e, void *userdata)) {
assert(e);
e->destroy_callback = callback;
}
/* quit() */
static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) {
pa_glib_mainloop *g;
assert(a && a->userdata);
g = a->userdata;
/* NOOP */
}
static const pa_mainloop_api vtable = {
.userdata = NULL,
.io_new = glib_io_new,
.io_enable = glib_io_enable,
.io_free = glib_io_free,
.io_set_destroy= glib_io_set_destroy,
.time_new = glib_time_new,
.time_restart = glib_time_restart,
.time_free = glib_time_free,
.time_set_destroy = glib_time_set_destroy,
.defer_new = glib_defer_new,
.defer_enable = glib_defer_enable,
.defer_free = glib_defer_free,
.defer_set_destroy = glib_defer_set_destroy,
.quit = glib_quit,
};
pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) {
pa_glib_mainloop *g;
g = pa_xmalloc(sizeof(pa_glib_mainloop));
if (c) {
g->glib_main_context = c;
g_main_context_ref(c);
} else
g->glib_main_context = g_main_context_default();
g->api = vtable;
g->api.userdata = g;
g->io_events = g->dead_io_events = NULL;
g->time_events = g->dead_time_events = NULL;
g->defer_events = g->dead_defer_events = NULL;
g->cleanup_source = NULL;
return g;
}
static void free_io_events(pa_io_event *e) {
while (e) {
pa_io_event *r = e;
e = r->next;
if (r->source) {
g_source_destroy(r->source);
g_source_unref(r->source);
}
if (r->io_channel)
g_io_channel_unref(r->io_channel);
if (r->destroy_callback)
r->destroy_callback(&r->mainloop->api, r, r->userdata);
pa_xfree(r);
}
}
static void free_time_events(pa_time_event *e) {
while (e) {
pa_time_event *r = e;
e = r->next;
if (r->source) {
g_source_destroy(r->source);
g_source_unref(r->source);
}
if (r->destroy_callback)
r->destroy_callback(&r->mainloop->api, r, r->userdata);
pa_xfree(r);
}
}
static void free_defer_events(pa_defer_event *e) {
while (e) {
pa_defer_event *r = e;
e = r->next;
if (r->source) {
g_source_destroy(r->source);
g_source_unref(r->source);
}
if (r->destroy_callback)
r->destroy_callback(&r->mainloop->api, r, r->userdata);
pa_xfree(r);
}
}
void pa_glib_mainloop_free(pa_glib_mainloop* g) {
assert(g);
free_io_events(g->io_events);
free_io_events(g->dead_io_events);
free_defer_events(g->defer_events);
free_defer_events(g->dead_defer_events);
free_time_events(g->time_events);
free_time_events(g->dead_time_events);
if (g->cleanup_source) {
g_source_destroy(g->cleanup_source);
g_source_unref(g->cleanup_source);
}
g_main_context_unref(g->glib_main_context);
pa_xfree(g);
}
pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) {
assert(g);
return &g->api;
}
static gboolean free_dead_events(gpointer p) {
pa_glib_mainloop *g = p;
assert(g);
free_io_events(g->dead_io_events);
free_defer_events(g->dead_defer_events);
free_time_events(g->dead_time_events);
g->dead_io_events = NULL;
g->dead_defer_events = NULL;
g->dead_time_events = NULL;
g_source_destroy(g->cleanup_source);
g_source_unref(g->cleanup_source);
g->cleanup_source = NULL;
return FALSE;
}
static void schedule_free_dead_events(pa_glib_mainloop *g) {
assert(g && g->glib_main_context);
if (g->cleanup_source)
return;
g->cleanup_source = g_idle_source_new();
assert(g->cleanup_source);
g_source_set_callback(g->cleanup_source, free_dead_events, g, NULL);
g_source_attach(g->cleanup_source, g->glib_main_context);
}

55
src/polyp/glib-mainloop.h Normal file
View file

@ -0,0 +1,55 @@
#ifndef fooglibmainloophfoo
#define fooglibmainloophfoo
/* $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 <glib.h>
#include <polyp/mainloop-api.h>
#include <polyp/cdecl.h>
/** \file
* GLIB main loop support */
PA_C_DECL_BEGIN
/** \pa_glib_mainloop
* An opaque GLIB main loop object */
typedef struct pa_glib_mainloop pa_glib_mainloop;
/** Create a new GLIB main loop object for the specified GLIB main loop context. If c is NULL the default context is used. */
#if GLIB_MAJOR_VERSION >= 2
pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c);
#else
pa_glib_mainloop *pa_glib_mainloop_new(void);
#endif
/** Free the GLIB main loop object */
void pa_glib_mainloop_free(pa_glib_mainloop* g);
/** Return the abstract main loop API vtable for the GLIB main loop object */
pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g);
PA_C_DECL_END
#endif

500
src/polyp/glib12-mainloop.c Normal file
View file

@ -0,0 +1,500 @@
/* $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 <polyp/glib-mainloop.h>
#include <polypcore/idxset.h>
#include <polypcore/xmalloc.h>
#include <polypcore/util.h>
/* A mainloop implementation based on GLIB 1.2 */
struct pa_io_event {
pa_glib_mainloop *mainloop;
int dead;
GIOChannel *io_channel;
guint source;
GIOCondition io_condition;
int fd;
void (*callback) (pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata);
void *userdata;
void (*destroy_callback) (pa_mainloop_api *m, pa_io_event*e, void *userdata);
pa_io_event *next, *prev;
};
struct pa_time_event {
pa_glib_mainloop *mainloop;
int dead;
guint source;
struct timeval timeval;
void (*callback) (pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata);
void *userdata;
void (*destroy_callback) (pa_mainloop_api *m, pa_time_event*e, void *userdata);
pa_time_event *next, *prev;
};
struct pa_defer_event {
pa_glib_mainloop *mainloop;
int dead;
guint source;
void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata);
void *userdata;
void (*destroy_callback) (pa_mainloop_api *m, pa_defer_event*e, void *userdata);
pa_defer_event *next, *prev;
};
struct pa_glib_mainloop {
pa_mainloop_api api;
guint cleanup_source;
pa_io_event *io_events, *dead_io_events;
pa_time_event *time_events, *dead_time_events;
pa_defer_event *defer_events, *dead_defer_events;
};
static void schedule_free_dead_events(pa_glib_mainloop *g);
static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f);
static pa_io_event* glib_io_new(pa_mainloop_api*m, int fd, pa_io_event_flags_t f, void (*callback) (pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata), void *userdata) {
pa_io_event *e;
pa_glib_mainloop *g;
assert(m && m->userdata && fd >= 0 && callback);
g = m->userdata;
e = pa_xmalloc(sizeof(pa_io_event));
e->mainloop = m->userdata;
e->dead = 0;
e->fd = fd;
e->callback = callback;
e->userdata = userdata;
e->destroy_callback = NULL;
e->io_channel = g_io_channel_unix_new(e->fd);
assert(e->io_channel);
e->source = (guint) -1;
e->io_condition = 0;
glib_io_enable(e, f);
e->next = g->io_events;
if (e->next) e->next->prev = e;
g->io_events = e;
e->prev = NULL;
return e;
}
static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) {
pa_io_event *e = data;
pa_io_event_flags_t f;
assert(source && e && e->io_channel == source);
f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) |
(condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) |
(condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) |
(condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0);
e->callback(&e->mainloop->api, e, e->fd, f, e->userdata);
return TRUE;
}
static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) {
GIOCondition c;
assert(e && !e->dead);
c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0);
if (c == e->io_condition)
return;
if (e->source != (guint) -1)
g_source_remove(e->source);
e->source = g_io_add_watch_full(e->io_channel, G_PRIORITY_DEFAULT, c | G_IO_ERR | G_IO_HUP, io_cb, e, NULL);
assert(e->source != (guint) -1);
e->io_condition = c;
}
static void glib_io_free(pa_io_event*e) {
assert(e && !e->dead);
if (e->source != (guint) -1) {
g_source_remove(e->source);
e->source = (guint) -1;
}
if (e->prev)
e->prev->next = e->next;
else
e->mainloop->io_events = e->next;
if (e->next)
e->next->prev = e->prev;
if ((e->next = e->mainloop->dead_io_events))
e->next->prev = e;
e->mainloop->dead_io_events = e;
e->prev = NULL;
e->dead = 1;
schedule_free_dead_events(e->mainloop);
}
static void glib_io_set_destroy(pa_io_event*e, void (*callback)(pa_mainloop_api*m, pa_io_event *e, void *userdata)) {
assert(e);
e->destroy_callback = callback;
}
/* Time sources */
static void glib_time_restart(pa_time_event*e, const struct timeval *tv);
static pa_time_event* glib_time_new(pa_mainloop_api*m, const struct timeval *tv, void (*callback) (pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) {
pa_glib_mainloop *g;
pa_time_event *e;
assert(m && m->userdata && tv && callback);
g = m->userdata;
e = pa_xmalloc(sizeof(pa_time_event));
e->mainloop = g;
e->dead = 0;
e->callback = callback;
e->userdata = userdata;
e->destroy_callback = NULL;
e->source = (guint) -1;
glib_time_restart(e, tv);
e->next = g->time_events;
if (e->next) e->next->prev = e;
g->time_events = e;
e->prev = NULL;
return e;
}
static guint msec_diff(const struct timeval *a, const struct timeval *b) {
guint r;
assert(a && b);
if (a->tv_sec < b->tv_sec)
return 0;
if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec)
return 0;
r = (a->tv_sec-b->tv_sec)*1000;
if (a->tv_usec >= b->tv_usec)
r += (a->tv_usec - b->tv_usec) / 1000;
else
r -= (b->tv_usec - a->tv_usec) / 1000;
return r;
}
static gboolean time_cb(gpointer data) {
pa_time_event* e = data;
assert(e && e->mainloop && e->source != (guint) -1);
g_source_remove(e->source);
e->source = (guint) -1;
e->callback(&e->mainloop->api, e, &e->timeval, e->userdata);
return FALSE;
}
static void glib_time_restart(pa_time_event*e, const struct timeval *tv) {
struct timeval now;
assert(e && e->mainloop && !e->dead);
pa_gettimeofday(&now);
if (e->source != (guint) -1)
g_source_remove(e->source);
if (tv) {
e->timeval = *tv;
e->source = g_timeout_add_full(G_PRIORITY_DEFAULT, msec_diff(tv, &now), time_cb, e, NULL);
assert(e->source != (guint) -1);
} else
e->source = (guint) -1;
}
static void glib_time_free(pa_time_event *e) {
assert(e && e->mainloop && !e->dead);
if (e->source != (guint) -1) {
g_source_remove(e->source);
e->source = (guint) -1;
}
if (e->prev)
e->prev->next = e->next;
else
e->mainloop->time_events = e->next;
if (e->next)
e->next->prev = e->prev;
if ((e->next = e->mainloop->dead_time_events))
e->next->prev = e;
e->mainloop->dead_time_events = e;
e->prev = NULL;
e->dead = 1;
schedule_free_dead_events(e->mainloop);
}
static void glib_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*m, pa_time_event*e, void *userdata)) {
assert(e);
e->destroy_callback = callback;
}
/* Deferred sources */
static void glib_defer_enable(pa_defer_event *e, int b);
static pa_defer_event* glib_defer_new(pa_mainloop_api*m, void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata), void *userdata) {
pa_defer_event *e;
pa_glib_mainloop *g;
assert(m && m->userdata && callback);
g = m->userdata;
e = pa_xmalloc(sizeof(pa_defer_event));
e->mainloop = g;
e->dead = 0;
e->callback = callback;
e->userdata = userdata;
e->destroy_callback = NULL;
e->source = (guint) -1;
glib_defer_enable(e, 1);
e->next = g->defer_events;
if (e->next) e->next->prev = e;
g->defer_events = e;
e->prev = NULL;
return e;
}
static gboolean idle_cb(gpointer data) {
pa_defer_event* e = data;
assert(e && e->mainloop && e->source != (guint) -1);
e->callback(&e->mainloop->api, e, e->userdata);
return TRUE;
}
static void glib_defer_enable(pa_defer_event *e, int b) {
assert(e && e->mainloop);
if (e->source != (guint) -1 && !b) {
g_source_remove(e->source);
e->source = (guint) -1;
} else if (e->source == (guint) -1 && b) {
e->source = g_idle_add_full(G_PRIORITY_HIGH, idle_cb, e, NULL);
assert(e->source != (guint) -1);
}
}
static void glib_defer_free(pa_defer_event *e) {
assert(e && e->mainloop && !e->dead);
if (e->source != (guint) -1) {
g_source_remove(e->source);
e->source = (guint) -1;
}
if (e->prev)
e->prev->next = e->next;
else
e->mainloop->defer_events = e->next;
if (e->next)
e->next->prev = e->prev;
if ((e->next = e->mainloop->dead_defer_events))
e->next->prev = e;
e->mainloop->dead_defer_events = e;
e->prev = NULL;
e->dead = 1;
schedule_free_dead_events(e->mainloop);
}
static void glib_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api *m, pa_defer_event *e, void *userdata)) {
assert(e);
e->destroy_callback = callback;
}
/* quit() */
static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) {
pa_glib_mainloop *g;
assert(a && a->userdata);
g = a->userdata;
/* NOOP */
}
static const pa_mainloop_api vtable = {
.userdata = NULL,
.io_new = glib_io_new,
.io_enable = glib_io_enable,
.io_free = glib_io_free,
.io_set_destroy= glib_io_set_destroy,
.time_new = glib_time_new,
.time_restart = glib_time_restart,
.time_free = glib_time_free,
.time_set_destroy = glib_time_set_destroy,
.defer_new = glib_defer_new,
.defer_enable = glib_defer_enable,
.defer_free = glib_defer_free,
.defer_set_destroy = glib_defer_set_destroy,
.quit = glib_quit,
};
pa_glib_mainloop *pa_glib_mainloop_new(void) {
pa_glib_mainloop *g;
g = pa_xmalloc(sizeof(pa_glib_mainloop));
g->api = vtable;
g->api.userdata = g;
g->io_events = g->dead_io_events = NULL;
g->time_events = g->dead_time_events = NULL;
g->defer_events = g->dead_defer_events = NULL;
g->cleanup_source = (guint) -1;
return g;
}
static void free_io_events(pa_io_event *e) {
while (e) {
pa_io_event *r = e;
e = r->next;
if (r->source != (guint) -1)
g_source_remove(r->source);
if (r->io_channel)
g_io_channel_unref(r->io_channel);
if (r->destroy_callback)
r->destroy_callback(&r->mainloop->api, r, r->userdata);
pa_xfree(r);
}
}
static void free_time_events(pa_time_event *e) {
while (e) {
pa_time_event *r = e;
e = r->next;
if (r->source != (guint) -1)
g_source_remove(r->source);
if (r->destroy_callback)
r->destroy_callback(&r->mainloop->api, r, r->userdata);
pa_xfree(r);
}
}
static void free_defer_events(pa_defer_event *e) {
while (e) {
pa_defer_event *r = e;
e = r->next;
if (r->source != (guint) -1)
g_source_remove(r->source);
if (r->destroy_callback)
r->destroy_callback(&r->mainloop->api, r, r->userdata);
pa_xfree(r);
}
}
void pa_glib_mainloop_free(pa_glib_mainloop* g) {
assert(g);
free_io_events(g->io_events);
free_io_events(g->dead_io_events);
free_defer_events(g->defer_events);
free_defer_events(g->dead_defer_events);
free_time_events(g->time_events);
free_time_events(g->dead_time_events);
if (g->cleanup_source != (guint) -1)
g_source_remove(g->cleanup_source);
pa_xfree(g);
}
pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) {
assert(g);
return &g->api;
}
static gboolean free_dead_events(gpointer p) {
pa_glib_mainloop *g = p;
assert(g);
free_io_events(g->dead_io_events);
free_defer_events(g->dead_defer_events);
free_time_events(g->dead_time_events);
g->dead_io_events = NULL;
g->dead_defer_events = NULL;
g->dead_time_events = NULL;
g_source_remove(g->cleanup_source);
g->cleanup_source = (guint) -1;
return FALSE;
}
static void schedule_free_dead_events(pa_glib_mainloop *g) {
assert(g);
if (g->cleanup_source != (guint) -1)
return;
g->cleanup_source = g_idle_add_full(G_PRIORITY_HIGH, free_dead_events, g, NULL);
}

68
src/polyp/mainloop-api.c Normal file
View file

@ -0,0 +1,68 @@
/* $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 "mainloop-api.h"
#include <polypcore/xmalloc.h>
#include <polypcore/gccmacro.h>
struct once_info {
void (*callback)(pa_mainloop_api*m, void *userdata);
void *userdata;
};
static void once_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
struct once_info *i = userdata;
assert(m && i && i->callback);
i->callback(m, i->userdata);
assert(m->defer_free);
m->defer_free(e);
}
static void free_callback(pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event *e, void *userdata) {
struct once_info *i = userdata;
assert(m && i);
pa_xfree(i);
}
void pa_mainloop_api_once(pa_mainloop_api* m, void (*callback)(pa_mainloop_api *m, void *userdata), void *userdata) {
struct once_info *i;
pa_defer_event *e;
assert(m && callback);
i = pa_xnew(struct once_info, 1);
i->callback = callback;
i->userdata = userdata;
assert(m->defer_new);
e = m->defer_new(m, once_callback, i);
assert(e);
m->defer_set_destroy(e, free_callback);
}

120
src/polyp/mainloop-api.h Normal file
View file

@ -0,0 +1,120 @@
#ifndef foomainloopapihfoo
#define foomainloopapihfoo
/* $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/time.h>
#include <time.h>
#include <polyp/cdecl.h>
/** \file
*
* Main loop abstraction layer. Both the polypaudio core and the
* polypaudio client library use a main loop abstraction layer. Due to
* this it is possible to embed polypaudio into other
* applications easily. Two main loop implemenations are
* currently available:
* \li A minimal implementation based on the C library's poll() function (See \ref mainloop.h)
* \li A wrapper around the GLIB main loop. Use this to embed polypaudio into your GLIB/GTK+/GNOME programs (See \ref glib-mainloop.h)
*
* The structure pa_mainloop_api is used as vtable for the main loop abstraction.
*
* This mainloop abstraction layer has no direct support for UNIX signals. Generic, mainloop implementation agnostic support is available throught \ref mainloop-signal.h.
* */
PA_C_DECL_BEGIN
/** A bitmask for IO events */
typedef enum pa_io_event_flags {
PA_IO_EVENT_NULL = 0, /**< No event */
PA_IO_EVENT_INPUT = 1, /**< Input event */
PA_IO_EVENT_OUTPUT = 2, /**< Output event */
PA_IO_EVENT_HANGUP = 4, /**< Hangup event */
PA_IO_EVENT_ERROR = 8 /**< Error event */
} pa_io_event_flags_t;
/** \pa_io_event
* An opaque IO event source object */
typedef struct pa_io_event pa_io_event;
/** \pa_defer_event
* An opaque deferred event source object. Events of this type are triggered once in every main loop iteration */
typedef struct pa_defer_event pa_defer_event;
/** \pa_time_event
* An opaque timer event source object */
typedef struct pa_time_event pa_time_event;
/** An abstract mainloop API vtable */
typedef struct pa_mainloop_api pa_mainloop_api;
struct pa_mainloop_api {
/** A pointer to some private, arbitrary data of the main loop implementation */
void *userdata;
/** Create a new IO event source object */
pa_io_event* (*io_new)(pa_mainloop_api*a, int fd, pa_io_event_flags_t events, void (*callback) (pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata), void *userdata);
/** Enable or disable IO events on this object */
void (*io_enable)(pa_io_event* e, pa_io_event_flags_t events);
/** Free a IO event source object */
void (*io_free)(pa_io_event* e);
/** Set a function that is called when the IO event source is destroyed. Use this to free the userdata argument if required */
void (*io_set_destroy)(pa_io_event *e, void (*callback) (pa_mainloop_api*a, pa_io_event *e, void *userdata));
/** Create a new timer event source object for the specified Unix time */
pa_time_event* (*time_new)(pa_mainloop_api*a, const struct timeval *tv, void (*callback) (pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata), void *userdata);
/** Restart a running or expired timer event source with a new Unix time */
void (*time_restart)(pa_time_event* e, const struct timeval *tv);
/** Free a deferred timer event source object */
void (*time_free)(pa_time_event* e);
/** Set a function that is called when the timer event source is destroyed. Use this to free the userdata argument if required */
void (*time_set_destroy)(pa_time_event *e, void (*callback) (pa_mainloop_api*a, pa_time_event *e, void *userdata));
/** Create a new deferred event source object */
pa_defer_event* (*defer_new)(pa_mainloop_api*a, void (*callback) (pa_mainloop_api*a, pa_defer_event* e, void *userdata), void *userdata);
/** Enable or disable a deferred event source temporarily */
void (*defer_enable)(pa_defer_event* e, int b);
/** Free a deferred event source object */
void (*defer_free)(pa_defer_event* e);
/** Set a function that is called when the deferred event source is destroyed. Use this to free the userdata argument if required */
void (*defer_set_destroy)(pa_defer_event *e, void (*callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata));
/** Exit the main loop and return the specfied retval*/
void (*quit)(pa_mainloop_api*a, int retval);
};
/** Run the specified callback function once from the main loop using an anonymous defer event. */
void pa_mainloop_api_once(pa_mainloop_api*m, void (*callback)(pa_mainloop_api*m, void *userdata), void *userdata);
PA_C_DECL_END
#endif

267
src/polyp/mainloop-signal.c Normal file
View file

@ -0,0 +1,267 @@
/* $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 <signal.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include <polyp/mainloop-signal.h>
#include <polypcore/util.h>
#include <polypcore/xmalloc.h>
#include <polypcore/log.h>
#include <polypcore/gccmacro.h>
struct pa_signal_event {
int sig;
#ifdef HAVE_SIGACTION
struct sigaction saved_sigaction;
#else
void (*saved_handler)(int sig);
#endif
void (*callback) (pa_mainloop_api*a, pa_signal_event *e, int sig, void *userdata);
void *userdata;
void (*destroy_callback) (pa_mainloop_api*a, pa_signal_event*e, void *userdata);
pa_signal_event *previous, *next;
};
static pa_mainloop_api *api = NULL;
static int signal_pipe[2] = { -1, -1 };
static pa_io_event* io_event = NULL;
static pa_defer_event *defer_event = NULL;
static pa_signal_event *signals = NULL;
#ifdef OS_IS_WIN32
static unsigned int waiting_signals = 0;
static CRITICAL_SECTION crit;
#endif
static void signal_handler(int sig) {
#ifndef HAVE_SIGACTION
signal(sig, signal_handler);
#endif
write(signal_pipe[1], &sig, sizeof(sig));
#ifdef OS_IS_WIN32
EnterCriticalSection(&crit);
waiting_signals++;
LeaveCriticalSection(&crit);
#endif
}
static void dispatch(pa_mainloop_api*a, int sig) {
pa_signal_event*s;
for (s = signals; s; s = s->next)
if (s->sig == sig) {
assert(s->callback);
s->callback(a, s, sig, s->userdata);
break;
}
}
static void defer(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event*e, PA_GCC_UNUSED void *userdata) {
ssize_t r;
int sig;
unsigned int sigs;
#ifdef OS_IS_WIN32
EnterCriticalSection(&crit);
sigs = waiting_signals;
waiting_signals = 0;
LeaveCriticalSection(&crit);
#endif
while (sigs) {
if ((r = read(signal_pipe[0], &sig, sizeof(sig))) < 0) {
pa_log(__FILE__": read(): %s\n", strerror(errno));
return;
}
if (r != sizeof(sig)) {
pa_log(__FILE__": short read()\n");
return;
}
dispatch(a, sig);
sigs--;
}
}
static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags_t f, PA_GCC_UNUSED void *userdata) {
ssize_t r;
int sig;
assert(a && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == signal_pipe[0]);
if ((r = read(signal_pipe[0], &sig, sizeof(sig))) < 0) {
if (errno == EAGAIN)
return;
pa_log(__FILE__": read(): %s\n", strerror(errno));
return;
}
if (r != sizeof(sig)) {
pa_log(__FILE__": short read()\n");
return;
}
dispatch(a, sig);
}
int pa_signal_init(pa_mainloop_api *a) {
assert(!api && a && signal_pipe[0] == -1 && signal_pipe[1] == -1 && !io_event && !defer_event);
#ifdef OS_IS_WIN32
if (_pipe(signal_pipe, 200, _O_BINARY) < 0) {
#else
if (pipe(signal_pipe) < 0) {
#endif
pa_log(__FILE__": pipe() failed: %s\n", strerror(errno));
return -1;
}
pa_make_nonblock_fd(signal_pipe[0]);
pa_make_nonblock_fd(signal_pipe[1]);
pa_fd_set_cloexec(signal_pipe[0], 1);
pa_fd_set_cloexec(signal_pipe[1], 1);
api = a;
#ifndef OS_IS_WIN32
io_event = api->io_new(api, signal_pipe[0], PA_IO_EVENT_INPUT, callback, NULL);
assert(io_event);
#else
defer_event = api->defer_new(api, defer, NULL);
assert(defer_event);
InitializeCriticalSection(&crit);
#endif
return 0;
}
void pa_signal_done(void) {
assert(api && signal_pipe[0] >= 0 && signal_pipe[1] >= 0 && (io_event || defer_event));
while (signals)
pa_signal_free(signals);
#ifndef OS_IS_WIN32
api->io_free(io_event);
io_event = NULL;
#else
api->defer_free(defer_event);
defer_event = NULL;
DeleteCriticalSection(&crit);
#endif
close(signal_pipe[0]);
close(signal_pipe[1]);
signal_pipe[0] = signal_pipe[1] = -1;
api = NULL;
}
pa_signal_event* pa_signal_new(int sig, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata) {
pa_signal_event *e = NULL;
#ifdef HAVE_SIGACTION
struct sigaction sa;
#endif
assert(sig > 0 && _callback);
for (e = signals; e; e = e->next)
if (e->sig == sig)
goto fail;
e = pa_xmalloc(sizeof(pa_signal_event));
e->sig = sig;
e->callback = _callback;
e->userdata = userdata;
e->destroy_callback = NULL;
#ifdef HAVE_SIGACTION
memset(&sa, 0, sizeof(sa));
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(sig, &sa, &e->saved_sigaction) < 0)
#else
if ((e->saved_handler = signal(sig, signal_handler)) == SIG_ERR)
#endif
goto fail;
e->previous = NULL;
e->next = signals;
signals = e;
return e;
fail:
if (e)
pa_xfree(e);
return NULL;
}
void pa_signal_free(pa_signal_event *e) {
assert(e);
if (e->next)
e->next->previous = e->previous;
if (e->previous)
e->previous->next = e->next;
else
signals = e->next;
#ifdef HAVE_SIGACTION
sigaction(e->sig, &e->saved_sigaction, NULL);
#else
signal(e->sig, e->saved_handler);
#endif
if (e->destroy_callback)
e->destroy_callback(api, e, e->userdata);
pa_xfree(e);
}
void pa_signal_set_destroy(pa_signal_event *e, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata)) {
assert(e);
e->destroy_callback = _callback;
}

View file

@ -0,0 +1,60 @@
#ifndef foomainloopsignalhfoo
#define foomainloopsignalhfoo
/* $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/mainloop-api.h>
#include <polyp/cdecl.h>
PA_C_DECL_BEGIN
/** \file
* UNIX signal support for main loops. In contrast to other
* main loop event sources such as timer and IO events, UNIX signal
* support requires modification of the global process
* environment. Due to this the generic main loop abstraction layer as
* defined in \ref mainloop-api.h doesn't have direct support for UNIX
* signals. However, you may hook signal support into an abstract main loop via the routines defined herein.
*/
/** Initialize the UNIX signal subsystem and bind it to the specified main loop */
int pa_signal_init(pa_mainloop_api *api);
/** Cleanup the signal subsystem */
void pa_signal_done(void);
/** \pa_signal_event
* An opaque UNIX signal event source object */
typedef struct pa_signal_event pa_signal_event;
/** Create a new UNIX signal event source object */
pa_signal_event* pa_signal_new(int sig, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata);
/** Free a UNIX signal event source object */
void pa_signal_free(pa_signal_event *e);
/** Set a function that is called when the signal event source is destroyed. Use this to free the userdata argument if required */
void pa_signal_set_destroy(pa_signal_event *e, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata));
PA_C_DECL_END
#endif

812
src/polyp/mainloop.c Normal file
View file

@ -0,0 +1,812 @@
/* $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 <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <errno.h>
#ifdef HAVE_SYS_POLL_H
#include <sys/poll.h>
#else
#include "poll.h"
#endif
#include <polypcore/winsock.h>
#include "mainloop.h"
#include <polypcore/util.h>
#include <polypcore/idxset.h>
#include <polypcore/xmalloc.h>
#include <polypcore/log.h>
struct pa_io_event {
pa_mainloop *mainloop;
int dead;
int fd;
pa_io_event_flags_t events;
void (*callback) (pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata);
struct pollfd *pollfd;
void *userdata;
void (*destroy_callback) (pa_mainloop_api*a, pa_io_event *e, void *userdata);
};
struct pa_time_event {
pa_mainloop *mainloop;
int dead;
int enabled;
struct timeval timeval;
void (*callback)(pa_mainloop_api*a, pa_time_event *e, const struct timeval*tv, void *userdata);
void *userdata;
void (*destroy_callback) (pa_mainloop_api*a, pa_time_event *e, void *userdata);
};
struct pa_defer_event {
pa_mainloop *mainloop;
int dead;
int enabled;
void (*callback)(pa_mainloop_api*a, pa_defer_event*e, void *userdata);
void *userdata;
void (*destroy_callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata);
};
struct pa_mainloop {
pa_idxset *io_events, *time_events, *defer_events;
int io_events_scan_dead, defer_events_scan_dead, time_events_scan_dead;
struct pollfd *pollfds;
unsigned max_pollfds, n_pollfds;
int rebuild_pollfds;
int prepared_timeout;
int quit, retval;
pa_mainloop_api api;
int deferred_pending;
int wakeup_pipe[2];
enum {
STATE_PASSIVE,
STATE_PREPARED,
STATE_POLLING,
STATE_POLLED,
STATE_QUIT
} state;
};
/* IO events */
static pa_io_event* mainloop_io_new(
pa_mainloop_api*a,
int fd,
pa_io_event_flags_t events,
void (*callback) (pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata),
void *userdata) {
pa_mainloop *m;
pa_io_event *e;
assert(a && a->userdata && fd >= 0 && callback);
m = a->userdata;
assert(a == &m->api);
pa_mainloop_wakeup(m);
e = pa_xmalloc(sizeof(pa_io_event));
e->mainloop = m;
e->dead = 0;
e->fd = fd;
e->events = events;
e->callback = callback;
e->userdata = userdata;
e->destroy_callback = NULL;
e->pollfd = NULL;
#ifdef OS_IS_WIN32
{
fd_set xset;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO (&xset);
FD_SET (fd, &xset);
if ((select((SELECT_TYPE_ARG1) fd, NULL, NULL, SELECT_TYPE_ARG234 &xset,
SELECT_TYPE_ARG5 &tv) == -1) &&
(WSAGetLastError() == WSAENOTSOCK)) {
pa_log_warn(__FILE__": WARNING: cannot monitor non-socket file descriptors.\n");
e->dead = 1;
}
}
#endif
pa_idxset_put(m->io_events, e, NULL);
m->rebuild_pollfds = 1;
return e;
}
static void mainloop_io_enable(pa_io_event *e, pa_io_event_flags_t events) {
assert(e && e->mainloop);
pa_mainloop_wakeup(e->mainloop);
e->events = events;
e->mainloop->rebuild_pollfds = 1;
}
static void mainloop_io_free(pa_io_event *e) {
assert(e && e->mainloop);
pa_mainloop_wakeup(e->mainloop);
e->dead = e->mainloop->io_events_scan_dead = e->mainloop->rebuild_pollfds = 1;
}
static void mainloop_io_set_destroy(pa_io_event *e, void (*callback)(pa_mainloop_api*a, pa_io_event *e, void *userdata)) {
assert(e);
e->destroy_callback = callback;
}
/* Defer events */
static pa_defer_event* mainloop_defer_new(pa_mainloop_api*a, void (*callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata), void *userdata) {
pa_mainloop *m;
pa_defer_event *e;
assert(a && a->userdata && callback);
m = a->userdata;
assert(a == &m->api);
e = pa_xmalloc(sizeof(pa_defer_event));
e->mainloop = m;
e->dead = 0;
e->enabled = 1;
e->callback = callback;
e->userdata = userdata;
e->destroy_callback = NULL;
pa_idxset_put(m->defer_events, e, NULL);
m->deferred_pending++;
return e;
}
static void mainloop_defer_enable(pa_defer_event *e, int b) {
assert(e);
if (e->enabled && !b) {
assert(e->mainloop->deferred_pending > 0);
e->mainloop->deferred_pending--;
} else if (!e->enabled && b)
e->mainloop->deferred_pending++;
e->enabled = b;
}
static void mainloop_defer_free(pa_defer_event *e) {
assert(e);
e->dead = e->mainloop->defer_events_scan_dead = 1;
if (e->enabled) {
e->enabled = 0;
assert(e->mainloop->deferred_pending > 0);
e->mainloop->deferred_pending--;
}
}
static void mainloop_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api*a, pa_defer_event *e, void *userdata)) {
assert(e);
e->destroy_callback = callback;
}
/* Time events */
static pa_time_event* mainloop_time_new(pa_mainloop_api*a, const struct timeval *tv, void (*callback) (pa_mainloop_api*a, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) {
pa_mainloop *m;
pa_time_event *e;
assert(a && a->userdata && callback);
m = a->userdata;
assert(a == &m->api);
pa_mainloop_wakeup(m);
e = pa_xmalloc(sizeof(pa_time_event));
e->mainloop = m;
e->dead = 0;
e->enabled = !!tv;
if (tv)
e->timeval = *tv;
e->callback = callback;
e->userdata = userdata;
e->destroy_callback = NULL;
pa_idxset_put(m->time_events, e, NULL);
return e;
}
static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) {
assert(e);
pa_mainloop_wakeup(e->mainloop);
if (tv) {
e->enabled = 1;
e->timeval = *tv;
} else
e->enabled = 0;
}
static void mainloop_time_free(pa_time_event *e) {
assert(e);
pa_mainloop_wakeup(e->mainloop);
e->dead = e->mainloop->time_events_scan_dead = 1;
}
static void mainloop_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*a, pa_time_event *e, void *userdata)) {
assert(e);
e->destroy_callback = callback;
}
/* quit() */
static void mainloop_quit(pa_mainloop_api*a, int retval) {
pa_mainloop *m;
assert(a && a->userdata);
m = a->userdata;
assert(a == &m->api);
pa_mainloop_wakeup(m);
m->quit = 1;
m->retval = retval;
}
static const pa_mainloop_api vtable = {
.userdata = NULL,
.io_new= mainloop_io_new,
.io_enable= mainloop_io_enable,
.io_free= mainloop_io_free,
.io_set_destroy= mainloop_io_set_destroy,
.time_new = mainloop_time_new,
.time_restart = mainloop_time_restart,
.time_free = mainloop_time_free,
.time_set_destroy = mainloop_time_set_destroy,
.defer_new = mainloop_defer_new,
.defer_enable = mainloop_defer_enable,
.defer_free = mainloop_defer_free,
.defer_set_destroy = mainloop_defer_set_destroy,
.quit = mainloop_quit,
};
pa_mainloop *pa_mainloop_new(void) {
pa_mainloop *m;
m = pa_xmalloc(sizeof(pa_mainloop));
#ifndef OS_ISWIN32
if (pipe(m->wakeup_pipe) < 0) {
pa_xfree(m);
return NULL;
}
pa_make_nonblock_fd(m->wakeup_pipe[0]);
pa_make_nonblock_fd(m->wakeup_pipe[1]);
#else
m->wakeup_pipe[0] = -1;
m->wakeup_pipe[1] = -1;
#endif
m->io_events = pa_idxset_new(NULL, NULL);
m->defer_events = pa_idxset_new(NULL, NULL);
m->time_events = pa_idxset_new(NULL, NULL);
assert(m->io_events && m->defer_events && m->time_events);
m->io_events_scan_dead = m->defer_events_scan_dead = m->time_events_scan_dead = 0;
m->pollfds = NULL;
m->max_pollfds = m->n_pollfds = m->rebuild_pollfds = 0;
m->quit = m->retval = 0;
m->api = vtable;
m->api.userdata = m;
m->deferred_pending = 0;
m->state = STATE_PASSIVE;
return m;
}
static int io_foreach(void *p, uint32_t PA_GCC_UNUSED idx, int *del, void*userdata) {
pa_io_event *e = p;
int *all = userdata;
assert(e && del && all);
if (!*all && !e->dead)
return 0;
if (e->destroy_callback)
e->destroy_callback(&e->mainloop->api, e, e->userdata);
pa_xfree(e);
*del = 1;
return 0;
}
static int time_foreach(void *p, uint32_t PA_GCC_UNUSED idx, int *del, void*userdata) {
pa_time_event *e = p;
int *all = userdata;
assert(e && del && all);
if (!*all && !e->dead)
return 0;
if (e->destroy_callback)
e->destroy_callback(&e->mainloop->api, e, e->userdata);
pa_xfree(e);
*del = 1;
return 0;
}
static int defer_foreach(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void*userdata) {
pa_defer_event *e = p;
int *all = userdata;
assert(e && del && all);
if (!*all && !e->dead)
return 0;
if (e->destroy_callback)
e->destroy_callback(&e->mainloop->api, e, e->userdata);
pa_xfree(e);
*del = 1;
return 0;
}
void pa_mainloop_free(pa_mainloop* m) {
int all = 1;
assert(m && (m->state != STATE_POLLING));
pa_idxset_foreach(m->io_events, io_foreach, &all);
pa_idxset_foreach(m->time_events, time_foreach, &all);
pa_idxset_foreach(m->defer_events, defer_foreach, &all);
pa_idxset_free(m->io_events, NULL, NULL);
pa_idxset_free(m->time_events, NULL, NULL);
pa_idxset_free(m->defer_events, NULL, NULL);
pa_xfree(m->pollfds);
if (m->wakeup_pipe[0] >= 0)
close(m->wakeup_pipe[0]);
if (m->wakeup_pipe[1] >= 0)
close(m->wakeup_pipe[1]);
pa_xfree(m);
}
static void scan_dead(pa_mainloop *m) {
int all = 0;
assert(m);
if (m->io_events_scan_dead)
pa_idxset_foreach(m->io_events, io_foreach, &all);
if (m->time_events_scan_dead)
pa_idxset_foreach(m->time_events, time_foreach, &all);
if (m->defer_events_scan_dead)
pa_idxset_foreach(m->defer_events, defer_foreach, &all);
m->io_events_scan_dead = m->time_events_scan_dead = m->defer_events_scan_dead = 0;
}
static void rebuild_pollfds(pa_mainloop *m) {
pa_io_event*e;
struct pollfd *p;
uint32_t idx = PA_IDXSET_INVALID;
unsigned l;
l = pa_idxset_size(m->io_events) + 1;
if (m->max_pollfds < l) {
m->pollfds = pa_xrealloc(m->pollfds, sizeof(struct pollfd)*l);
m->max_pollfds = l;
}
m->n_pollfds = 0;
p = m->pollfds;
if (m->wakeup_pipe[0] >= 0) {
m->pollfds[0].fd = m->wakeup_pipe[0];
m->pollfds[0].events = POLLIN;
m->pollfds[0].revents = 0;
p++;
m->n_pollfds++;
}
for (e = pa_idxset_first(m->io_events, &idx); e; e = pa_idxset_next(m->io_events, &idx)) {
if (e->dead) {
e->pollfd = NULL;
continue;
}
e->pollfd = p;
p->fd = e->fd;
p->events =
((e->events & PA_IO_EVENT_INPUT) ? POLLIN : 0) |
((e->events & PA_IO_EVENT_OUTPUT) ? POLLOUT : 0) |
POLLHUP |
POLLERR;
p->revents = 0;
p++;
m->n_pollfds++;
}
m->rebuild_pollfds = 0;
}
static int dispatch_pollfds(pa_mainloop *m) {
uint32_t idx = PA_IDXSET_INVALID;
pa_io_event *e;
int r = 0;
for (e = pa_idxset_first(m->io_events, &idx); e && !m->quit; e = pa_idxset_next(m->io_events, &idx)) {
if (e->dead || !e->pollfd || !e->pollfd->revents)
continue;
assert(e->pollfd->fd == e->fd && e->callback);
e->callback(&m->api, e, e->fd,
(e->pollfd->revents & POLLHUP ? PA_IO_EVENT_HANGUP : 0) |
(e->pollfd->revents & POLLIN ? PA_IO_EVENT_INPUT : 0) |
(e->pollfd->revents & POLLOUT ? PA_IO_EVENT_OUTPUT : 0) |
(e->pollfd->revents & POLLERR ? PA_IO_EVENT_ERROR : 0),
e->userdata);
e->pollfd->revents = 0;
r++;
}
return r;
}
static int dispatch_defer(pa_mainloop *m) {
uint32_t idx;
pa_defer_event *e;
int r = 0;
if (!m->deferred_pending)
return 0;
for (e = pa_idxset_first(m->defer_events, &idx); e && !m->quit; e = pa_idxset_next(m->defer_events, &idx)) {
if (e->dead || !e->enabled)
continue;
assert(e->callback);
e->callback(&m->api, e, e->userdata);
r++;
}
return r;
}
static int calc_next_timeout(pa_mainloop *m) {
uint32_t idx;
pa_time_event *e;
struct timeval now;
int t = -1;
int got_time = 0;
if (pa_idxset_isempty(m->time_events))
return -1;
for (e = pa_idxset_first(m->time_events, &idx); e; e = pa_idxset_next(m->time_events, &idx)) {
int tmp;
if (e->dead || !e->enabled)
continue;
/* Let's save a system call */
if (!got_time) {
pa_gettimeofday(&now);
got_time = 1;
}
if (e->timeval.tv_sec < now.tv_sec || (e->timeval.tv_sec == now.tv_sec && e->timeval.tv_usec <= now.tv_usec))
return 0;
tmp = (e->timeval.tv_sec - now.tv_sec)*1000;
if (e->timeval.tv_usec > now.tv_usec)
tmp += (e->timeval.tv_usec - now.tv_usec)/1000;
else
tmp -= (now.tv_usec - e->timeval.tv_usec)/1000;
if (tmp == 0)
return 0;
else if (t == -1 || tmp < t)
t = tmp;
}
return t;
}
static int dispatch_timeout(pa_mainloop *m) {
uint32_t idx;
pa_time_event *e;
struct timeval now;
int got_time = 0;
int r = 0;
assert(m);
if (pa_idxset_isempty(m->time_events))
return 0;
for (e = pa_idxset_first(m->time_events, &idx); e && !m->quit; e = pa_idxset_next(m->time_events, &idx)) {
if (e->dead || !e->enabled)
continue;
/* Let's save a system call */
if (!got_time) {
pa_gettimeofday(&now);
got_time = 1;
}
if (e->timeval.tv_sec < now.tv_sec || (e->timeval.tv_sec == now.tv_sec && e->timeval.tv_usec <= now.tv_usec)) {
assert(e->callback);
e->enabled = 0;
e->callback(&m->api, e, &e->timeval, e->userdata);
r++;
}
}
return r;
}
void pa_mainloop_wakeup(pa_mainloop *m) {
char c = 'W';
assert(m);
if (m->wakeup_pipe[1] >= 0)
write(m->wakeup_pipe[1], &c, sizeof(c));
}
static void clear_wakeup(pa_mainloop *m) {
char c[10];
assert(m);
if (m->wakeup_pipe[0] < 0)
return;
while (read(m->wakeup_pipe[0], &c, sizeof(c)) == sizeof(c));
}
int pa_mainloop_prepare(pa_mainloop *m, int timeout) {
int dispatched = 0;
assert(m && (m->state == STATE_PASSIVE));
clear_wakeup(m);
scan_dead(m);
if (m->quit)
goto quit;
dispatched += dispatch_defer(m);
if (m->quit)
goto quit;
if (m->rebuild_pollfds)
rebuild_pollfds(m);
m->prepared_timeout = calc_next_timeout(m);
if ((timeout >= 0) && (m->prepared_timeout > timeout))
m->prepared_timeout = timeout;
m->state = STATE_PREPARED;
return dispatched;
quit:
m->state = STATE_QUIT;
return -2;
}
int pa_mainloop_poll(pa_mainloop *m) {
int r;
assert(m && (m->state == STATE_PREPARED));
m->state = STATE_POLLING;
r = poll(m->pollfds, m->n_pollfds, m->prepared_timeout);
if ((r < 0) && (errno == EINTR))
r = 0;
if (r < 0)
m->state = STATE_PASSIVE;
else
m->state = STATE_POLLED;
return r;
}
int pa_mainloop_dispatch(pa_mainloop *m) {
int dispatched = 0;
assert(m && (m->state == STATE_POLLED));
dispatched += dispatch_timeout(m);
if (m->quit)
goto quit;
dispatched += dispatch_pollfds(m);
if (m->quit)
goto quit;
m->state = STATE_PASSIVE;
return dispatched;
quit:
m->state = STATE_QUIT;
return -2;
}
int pa_mainloop_get_retval(pa_mainloop *m) {
assert(m);
return m->retval;
}
int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) {
int r, dispatched = 0;
assert(m);
r = pa_mainloop_prepare(m, block ? -1 : 0);
if (r < 0) {
if ((r == -2) && retval)
*retval = pa_mainloop_get_retval(m);
return r;
}
dispatched += r;
r = pa_mainloop_poll(m);
if (r < 0) {
pa_log(__FILE__": poll(): %s\n", strerror(errno));
return r;
}
r = pa_mainloop_dispatch(m);
if (r < 0) {
if ((r == -2) && retval)
*retval = pa_mainloop_get_retval(m);
return r;
}
dispatched += r;
return dispatched;
}
int pa_mainloop_run(pa_mainloop *m, int *retval) {
int r;
while ((r = pa_mainloop_iterate(m, 1, retval)) >= 0);
if (r == -2)
return 1;
else if (r < 0)
return -1;
else
return 0;
}
void pa_mainloop_quit(pa_mainloop *m, int r) {
assert(m);
pa_mainloop_wakeup(m);
m->quit = r;
}
pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m) {
assert(m);
return &m->api;
}
int pa_mainloop_deferred_pending(pa_mainloop *m) {
assert(m);
return m->deferred_pending > 0;
}
#if 0
void pa_mainloop_dump(pa_mainloop *m) {
assert(m);
pa_log(__FILE__": Dumping mainloop sources START\n");
{
uint32_t idx = PA_IDXSET_INVALID;
pa_io_event *e;
for (e = pa_idxset_first(m->io_events, &idx); e; e = pa_idxset_next(m->io_events, &idx)) {
if (e->dead)
continue;
pa_log(__FILE__": kind=io fd=%i events=%i callback=%p userdata=%p\n", e->fd, (int) e->events, (void*) e->callback, (void*) e->userdata);
}
}
{
uint32_t idx = PA_IDXSET_INVALID;
pa_defer_event *e;
for (e = pa_idxset_first(m->defer_events, &idx); e; e = pa_idxset_next(m->defer_events, &idx)) {
if (e->dead)
continue;
pa_log(__FILE__": kind=defer enabled=%i callback=%p userdata=%p\n", e->enabled, (void*) e->callback, (void*) e->userdata);
}
}
{
uint32_t idx = PA_IDXSET_INVALID;
pa_time_event *e;
for (e = pa_idxset_first(m->time_events, &idx); e; e = pa_idxset_next(m->time_events, &idx)) {
if (e->dead)
continue;
pa_log(__FILE__": kind=time enabled=%i time=%lu.%lu callback=%p userdata=%p\n", e->enabled, (unsigned long) e->timeval.tv_sec, (unsigned long) e->timeval.tv_usec, (void*) e->callback, (void*) e->userdata);
}
}
pa_log(__FILE__": Dumping mainloop sources STOP\n");
}
#endif

90
src/polyp/mainloop.h Normal file
View file

@ -0,0 +1,90 @@
#ifndef foomainloophfoo
#define foomainloophfoo
/* $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/mainloop-api.h>
#include <polyp/cdecl.h>
PA_C_DECL_BEGIN
/** \file
*
* A minimal main loop implementation based on the C library's poll()
* function. Using the routines defined herein you may create a simple
* main loop supporting the generic main loop abstraction layer as
* defined in \ref mainloop-api.h. This implementation is thread safe
* as long as you access the main loop object from a single thread only.*/
/** \pa_mainloop
* An opaque main loop object
*/
typedef struct pa_mainloop pa_mainloop;
/** Allocate a new main loop object */
pa_mainloop *pa_mainloop_new(void);
/** Free a main loop object */
void pa_mainloop_free(pa_mainloop* m);
/** Prepare for a single iteration of the main loop. Returns a negative value
on error or exit request. timeout specifies a maximum timeout for the subsequent
poll, or -1 for blocking behaviour. Defer events are also dispatched when this
function is called. On success returns the number of source dispatched in this
iteration.*/
int pa_mainloop_prepare(pa_mainloop *m, int timeout);
/** Execute the previously prepared poll. Returns a negative value on error.*/
int pa_mainloop_poll(pa_mainloop *m);
/** Dispatch timeout and io events from the previously executed poll. Returns
a negative value on error. On success returns the number of source dispatched. */
int pa_mainloop_dispatch(pa_mainloop *m);
/** Return the return value as specified with the main loop's quit() routine. */
int pa_mainloop_get_retval(pa_mainloop *m);
/** Run a single iteration of the main loop. This is a convenience function
for pa_mainloop_prepare(), pa_mainloop_poll() and pa_mainloop_dispatch().
Returns a negative value on error or exit request. If block is nonzero,
block for events if none are queued. Optionally return the return value as
specified with the main loop's quit() routine in the integer variable retval points
to. On success returns the number of source dispatched in this iteration. */
int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval);
/** Run unlimited iterations of the main loop object until the main loop's quit() routine is called. */
int pa_mainloop_run(pa_mainloop *m, int *retval);
/** Return the abstract main loop abstraction layer vtable for this main loop. This calls pa_mainloop_iterate() iteratively.*/
pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m);
/** Return non-zero when there are any deferred events pending. \since 0.5 */
int pa_mainloop_deferred_pending(pa_mainloop *m);
/** Shutdown the main loop */
void pa_mainloop_quit(pa_mainloop *m, int r);
/** Interrupt a running poll (for threaded systems) */
void pa_mainloop_wakeup(pa_mainloop *m);
PA_C_DECL_END
#endif

View file

@ -0,0 +1,312 @@
/* $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.h>
#include "polyplib-browser.h"
#include <polypcore/xmalloc.h>
#include <polypcore/log.h>
#include <polypcore/util.h>
#define SERVICE_NAME_SINK "_polypaudio-sink._tcp."
#define SERVICE_NAME_SOURCE "_polypaudio-source._tcp."
#define SERVICE_NAME_SERVER "_polypaudio-server._tcp."
pa_browser {
int ref;
pa_mainloop_api *mainloop;
void (*callback)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void *userdata);
void *callback_userdata;
sw_discovery discovery;
pa_io_event *io_event;
};
static void io_callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags events, void *userdata) {
pa_browser *b = userdata;
assert(a && b && b->mainloop == a);
if (events != PA_IO_EVENT_INPUT || sw_discovery_read_socket(b->discovery) != SW_OKAY) {
pa_log(__FILE__": connection to HOWL daemon failed.\n");
b->mainloop->io_free(b->io_event);
b->io_event = NULL;
return;
}
}
static sw_result resolve_reply(
sw_discovery discovery,
sw_discovery_oid oid,
sw_uint32 interface_index,
sw_const_string name,
sw_const_string type,
sw_const_string domain,
sw_ipv4_address address,
sw_port port,
sw_octets text_record,
sw_ulong text_record_len,
sw_opaque extra) {
pa_browser *b = extra;
pa_browse_info i;
char ip[256], a[256];
pa_browse_opcode opcode;
int device_found = 0;
uint32_t cookie;
pa_typeid_t typeid;
pa_sample_spec ss;
int ss_valid = 0;
sw_text_record_iterator iterator;
int free_iterator = 0;
char *c = NULL;
assert(b);
sw_discovery_cancel(discovery, oid);
memset(&i, 0, sizeof(i));
i.name = name;
if (!b->callback)
goto fail;
if (!strcmp(type, SERVICE_NAME_SINK))
opcode = PA_BROWSE_NEW_SINK;
else if (!strcmp(type, SERVICE_NAME_SOURCE))
opcode = PA_BROWSE_NEW_SOURCE;
else if (!strcmp(type, SERVICE_NAME_SERVER))
opcode = PA_BROWSE_NEW_SERVER;
else
goto fail;
snprintf(a, sizeof(a), "tcp:%s:%u", sw_ipv4_address_name(address, ip, sizeof(ip)), port);
i.server = a;
if (text_record && text_record_len) {
char key[SW_TEXT_RECORD_MAX_LEN];
uint8_t val[SW_TEXT_RECORD_MAX_LEN];
uint32_t val_len;
if (sw_text_record_iterator_init(&iterator, text_record, text_record_len) != SW_OKAY) {
pa_log("sw_text_record_string_iterator_init() failed.\n");
goto fail;
}
free_iterator = 1;
while (sw_text_record_iterator_next(iterator, key, val, &val_len) == SW_OKAY) {
c = pa_xstrndup((char*) val, val_len);
if (!strcmp(key, "device")) {
device_found = 1;
pa_xfree((char*) i.device);
i.device = c;
c = NULL;
} else if (!strcmp(key, "server-version")) {
pa_xfree((char*) i.server_version);
i.server_version = c;
c = NULL;
} else if (!strcmp(key, "user-name")) {
pa_xfree((char*) i.user_name);
i.user_name = c;
c = NULL;
} else if (!strcmp(key, "fqdn")) {
size_t l;
pa_xfree((char*) i.fqdn);
i.fqdn = c;
c = NULL;
l = strlen(a);
assert(l+1 <= sizeof(a));
strncat(a, " ", sizeof(a)-l-1);
strncat(a, i.fqdn, sizeof(a)-l-2);
} else if (!strcmp(key, "cookie")) {
if (pa_atou(c, &cookie) < 0)
goto fail;
i.cookie = &cookie;
} else if (!strcmp(key, "description")) {
pa_xfree((char*) i.description);
i.description = c;
c = NULL;
} else if (!strcmp(key, "typeid")) {
if (pa_atou(c, &typeid) < 0)
goto fail;
i.typeid = &typeid;
} else if (!strcmp(key, "channels")) {
uint32_t ch;
if (pa_atou(c, &ch) < 0 || ch <= 0 || ch > 255)
goto fail;
ss.channels = (uint8_t) ch;
ss_valid |= 1;
} else if (!strcmp(key, "rate")) {
if (pa_atou(c, &ss.rate) < 0)
goto fail;
ss_valid |= 2;
} else if (!strcmp(key, "format")) {
if ((ss.format = pa_parse_sample_format(c)) == PA_SAMPLE_INVALID)
goto fail;
ss_valid |= 4;
}
pa_xfree(c);
c = NULL;
}
}
/* No device txt record was sent for a sink or source service */
if (opcode != PA_BROWSE_NEW_SERVER && !device_found)
goto fail;
if (ss_valid == 7)
i.sample_spec = &ss;
b->callback(b, opcode, &i, b->callback_userdata);
fail:
pa_xfree((void*) i.device);
pa_xfree((void*) i.fqdn);
pa_xfree((void*) i.server_version);
pa_xfree((void*) i.user_name);
pa_xfree((void*) i.description);
pa_xfree(c);
if (free_iterator)
sw_text_record_iterator_fina(iterator);
return SW_OKAY;
}
static sw_result browse_reply(
sw_discovery discovery,
sw_discovery_oid id,
sw_discovery_browse_status status,
sw_uint32 interface_index,
sw_const_string name,
sw_const_string type,
sw_const_string domain,
sw_opaque extra) {
pa_browser *b = extra;
assert(b);
switch (status) {
case SW_DISCOVERY_BROWSE_ADD_SERVICE: {
sw_discovery_oid oid;
if (sw_discovery_resolve(b->discovery, 0, name, type, domain, resolve_reply, b, &oid) != SW_OKAY)
pa_log("sw_discovery_resolve() failed\n");
break;
}
case SW_DISCOVERY_BROWSE_REMOVE_SERVICE:
if (b->callback) {
pa_browse_info i;
memset(&i, 0, sizeof(i));
i.name = name;
b->callback(b, PA_BROWSE_REMOVE, &i, b->callback_userdata);
}
break;
default:
;
}
return SW_OKAY;
}
pa_browser *pa_browser_new(pa_mainloop_api *mainloop) {
pa_browser *b;
sw_discovery_oid oid;
b = pa_xmalloc(sizeof(pa_browser));
b->mainloop = mainloop;
b->ref = 1;
b->callback = NULL;
b->callback_userdata = NULL;
if (sw_discovery_init(&b->discovery) != SW_OKAY) {
pa_log("sw_discovery_init() failed.\n");
pa_xfree(b);
return NULL;
}
if (sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SERVER, NULL, browse_reply, b, &oid) != SW_OKAY ||
sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SINK, NULL, browse_reply, b, &oid) != SW_OKAY ||
sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SOURCE, NULL, browse_reply, b, &oid) != SW_OKAY) {
pa_log("sw_discovery_browse() failed.\n");
sw_discovery_fina(b->discovery);
pa_xfree(b);
return NULL;
}
b->io_event = mainloop->io_new(mainloop, sw_discovery_socket(b->discovery), PA_IO_EVENT_INPUT, io_callback, b);
return b;
}
static void browser_free(pa_browser *b) {
assert(b && b->mainloop);
if (b->io_event)
b->mainloop->io_free(b->io_event);
sw_discovery_fina(b->discovery);
pa_xfree(b);
}
pa_browser *pa_browser_ref(pa_browser *b) {
assert(b && b->ref >= 1);
b->ref++;
return b;
}
void pa_browser_unref(pa_browser *b) {
assert(b && b->ref >= 1);
if ((-- (b->ref)) <= 0)
browser_free(b);
}
void pa_browser_set_callback(pa_browser *b, void (*cb)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void* userdata), void *userdata) {
assert(b);
b->callback = cb;
b->callback_userdata = userdata;
}

View file

@ -0,0 +1,65 @@
#ifndef foopolyplibbrowserhfoo
#define foopolyplibbrowserhfoo
/* $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/mainloop-api.h>
#include <polyp/sample.h>
#include <polyp/cdecl.h>
#include <polyp/typeid.h>
PA_C_DECL_BEGIN
pa_browser;
pa_browse_opcode {
PA_BROWSE_NEW_SERVER,
PA_BROWSE_NEW_SINK,
PA_BROWSE_NEW_SOURCE,
PA_BROWSE_REMOVE
};
pa_browser *pa_browser_new(pa_mainloop_api *mainloop);
pa_browser *pa_browser_ref(pa_browser *z);
void pa_browser_unref(pa_browser *z);
pa_browse_info {
/* Unique service name */
const char *name; /* always available */
/* Server info */
const char *server; /* always available */
const char *server_version, *user_name, *fqdn; /* optional */
const uint32_t *cookie; /* optional */
/* Device info */
const char *device; /* always available when this information is of a sink/source */
const char *description; /* optional */
const pa_typeid_t *typeid; /* optional */
const pa_sample_spec *sample_spec; /* optional */
};
void pa_browser_set_callback(pa_browser *z, void (*cb)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void *userdata), void *userdata);
PA_C_DECL_END
#endif

View file

@ -0,0 +1,871 @@
/* $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 <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
#include <limits.h>
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#include <polypcore/winsock.h>
#include "polyplib-internal.h"
#include "polyplib-context.h"
#include "polyplib-version.h"
#include <polypcore/native-common.h>
#include <polypcore/pdispatch.h>
#include <polypcore/pstream.h>
#include <polypcore/dynarray.h>
#include <polypcore/socket-client.h>
#include <polypcore/pstream-util.h>
#include <polypcore/util.h>
#include <polypcore/xmalloc.h>
#include <polypcore/log.h>
#include <polyp/client-conf.h>
#include <polypcore/socket-util.h>
#ifdef HAVE_X11
#include "client-conf-x11.h"
#endif
#define AUTOSPAWN_LOCK "autospawn.lock"
static const pa_pdispatch_callback command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_REQUEST] = pa_command_request,
[PA_COMMAND_PLAYBACK_STREAM_KILLED] = pa_command_stream_killed,
[PA_COMMAND_RECORD_STREAM_KILLED] = pa_command_stream_killed,
[PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event
};
static void unlock_autospawn_lock_file(pa_context *c) {
assert(c);
if (c->autospawn_lock_fd >= 0) {
char lf[PATH_MAX];
pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
pa_unlock_lockfile(lf, c->autospawn_lock_fd);
c->autospawn_lock_fd = -1;
}
}
pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
pa_context *c;
assert(mainloop && name);
c = pa_xmalloc(sizeof(pa_context));
c->ref = 1;
c->name = pa_xstrdup(name);
c->mainloop = mainloop;
c->client = NULL;
c->pstream = NULL;
c->pdispatch = NULL;
c->playback_streams = pa_dynarray_new();
c->record_streams = pa_dynarray_new();
assert(c->playback_streams && c->record_streams);
PA_LLIST_HEAD_INIT(pa_stream, c->streams);
PA_LLIST_HEAD_INIT(pa_operation, c->operations);
c->error = PA_ERROR_OK;
c->state = PA_CONTEXT_UNCONNECTED;
c->ctag = 0;
c->state_callback = NULL;
c->state_userdata = NULL;
c->subscribe_callback = NULL;
c->subscribe_userdata = NULL;
c->memblock_stat = pa_memblock_stat_new();
c->local = -1;
c->server_list = NULL;
c->server = NULL;
c->autospawn_lock_fd = -1;
memset(&c->spawn_api, 0, sizeof(c->spawn_api));
c->do_autospawn = 0;
#ifdef SIGPIPE
pa_check_signal_is_blocked(SIGPIPE);
#endif
c->conf = pa_client_conf_new();
pa_client_conf_load(c->conf, NULL);
#ifdef HAVE_X11
pa_client_conf_from_x11(c->conf, NULL);
#endif
pa_client_conf_env(c->conf);
return c;
}
static void context_free(pa_context *c) {
assert(c);
unlock_autospawn_lock_file(c);
while (c->operations)
pa_operation_cancel(c->operations);
while (c->streams)
pa_stream_set_state(c->streams, PA_STREAM_TERMINATED);
if (c->client)
pa_socket_client_unref(c->client);
if (c->pdispatch)
pa_pdispatch_unref(c->pdispatch);
if (c->pstream) {
pa_pstream_close(c->pstream);
pa_pstream_unref(c->pstream);
}
if (c->record_streams)
pa_dynarray_free(c->record_streams, NULL, NULL);
if (c->playback_streams)
pa_dynarray_free(c->playback_streams, NULL, NULL);
pa_memblock_stat_unref(c->memblock_stat);
if (c->conf)
pa_client_conf_free(c->conf);
pa_strlist_free(c->server_list);
pa_xfree(c->name);
pa_xfree(c->server);
pa_xfree(c);
}
pa_context* pa_context_ref(pa_context *c) {
assert(c && c->ref >= 1);
c->ref++;
return c;
}
void pa_context_unref(pa_context *c) {
assert(c && c->ref >= 1);
if ((--(c->ref)) == 0)
context_free(c);
}
void pa_context_set_state(pa_context *c, pa_context_state_t st) {
assert(c);
if (c->state == st)
return;
pa_context_ref(c);
if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) {
pa_stream *s;
s = c->streams ? pa_stream_ref(c->streams) : NULL;
while (s) {
pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
pa_stream_set_state(s, st == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
pa_stream_unref(s);
s = n;
}
if (c->pdispatch)
pa_pdispatch_unref(c->pdispatch);
c->pdispatch = NULL;
if (c->pstream) {
pa_pstream_close(c->pstream);
pa_pstream_unref(c->pstream);
}
c->pstream = NULL;
if (c->client)
pa_socket_client_unref(c->client);
c->client = NULL;
}
c->state = st;
if (c->state_callback)
c->state_callback(c, c->state_userdata);
pa_context_unref(c);
}
void pa_context_fail(pa_context *c, int error) {
assert(c);
c->error = error;
pa_context_set_state(c, PA_CONTEXT_FAILED);
}
static void pstream_die_callback(pa_pstream *p, void *userdata) {
pa_context *c = userdata;
assert(p && c);
pa_context_fail(c, PA_ERROR_CONNECTIONTERMINATED);
}
static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *userdata) {
pa_context *c = userdata;
assert(p && packet && c);
pa_context_ref(c);
if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) {
pa_log(__FILE__": invalid packet.\n");
pa_context_fail(c, PA_ERROR_PROTOCOL);
}
pa_context_unref(c);
}
static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, PA_GCC_UNUSED uint32_t delta, const pa_memchunk *chunk, void *userdata) {
pa_context *c = userdata;
pa_stream *s;
assert(p && chunk && c && chunk->memblock && chunk->memblock->data);
pa_context_ref(c);
if ((s = pa_dynarray_get(c->record_streams, channel))) {
pa_mcalign_push(s->mcalign, chunk);
for (;;) {
pa_memchunk t;
if (pa_mcalign_pop(s->mcalign, &t) < 0)
break;
if (s->read_callback) {
s->read_callback(s, (uint8_t*) t.memblock->data + t.index, t.length, s->read_userdata);
s->counter += chunk->length;
}
pa_memblock_unref(t.memblock);
}
}
pa_context_unref(c);
}
int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) {
assert(c);
if (command == PA_COMMAND_ERROR) {
assert(t);
if (pa_tagstruct_getu32(t, &c->error) < 0) {
pa_context_fail(c, PA_ERROR_PROTOCOL);
return -1;
}
} else if (command == PA_COMMAND_TIMEOUT)
c->error = PA_ERROR_TIMEOUT;
else {
pa_context_fail(c, PA_ERROR_PROTOCOL);
return -1;
}
return 0;
}
static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_context *c = userdata;
assert(pd && c && (c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME));
pa_context_ref(c);
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(c, command, t) < 0)
pa_context_fail(c, PA_ERROR_PROTOCOL);
pa_context_fail(c, c->error);
goto finish;
}
switch(c->state) {
case PA_CONTEXT_AUTHORIZING: {
pa_tagstruct *reply;
reply = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME);
pa_tagstruct_putu32(reply, tag = c->ctag++);
pa_tagstruct_puts(reply, c->name);
pa_pstream_send_tagstruct(c->pstream, reply);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c);
pa_context_set_state(c, PA_CONTEXT_SETTING_NAME);
break;
}
case PA_CONTEXT_SETTING_NAME :
pa_context_set_state(c, PA_CONTEXT_READY);
break;
default:
assert(0);
}
finish:
pa_context_unref(c);
}
static void setup_context(pa_context *c, pa_iochannel *io) {
pa_tagstruct *t;
uint32_t tag;
assert(c && io);
pa_context_ref(c);
assert(!c->pstream);
c->pstream = pa_pstream_new(c->mainloop, io, c->memblock_stat);
assert(c->pstream);
pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c);
pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c);
pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c);
assert(!c->pdispatch);
c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX);
assert(c->pdispatch);
if (!c->conf->cookie_valid) {
pa_context_fail(c, PA_ERROR_AUTHKEY);
goto finish;
}
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_AUTH);
pa_tagstruct_putu32(t, tag = c->ctag++);
pa_tagstruct_put_arbitrary(t, c->conf->cookie, sizeof(c->conf->cookie));
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c);
pa_context_set_state(c, PA_CONTEXT_AUTHORIZING);
finish:
pa_context_unref(c);
}
static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata);
#ifndef OS_IS_WIN32
static int context_connect_spawn(pa_context *c) {
pid_t pid;
int status, r;
int fds[2] = { -1, -1} ;
pa_iochannel *io;
pa_context_ref(c);
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
pa_log(__FILE__": socketpair() failed: %s\n", strerror(errno));
pa_context_fail(c, PA_ERROR_INTERNAL);
goto fail;
}
pa_fd_set_cloexec(fds[0], 1);
pa_socket_low_delay(fds[0]);
pa_socket_low_delay(fds[1]);
if (c->spawn_api.prefork)
c->spawn_api.prefork();
if ((pid = fork()) < 0) {
pa_log(__FILE__": fork() failed: %s\n", strerror(errno));
pa_context_fail(c, PA_ERROR_INTERNAL);
if (c->spawn_api.postfork)
c->spawn_api.postfork();
goto fail;
} else if (!pid) {
/* Child */
char t[128];
const char *state = NULL;
#define MAX_ARGS 64
const char * argv[MAX_ARGS+1];
int n;
/* Not required, since fds[0] has CLOEXEC enabled anyway */
close(fds[0]);
if (c->spawn_api.atfork)
c->spawn_api.atfork();
/* Setup argv */
n = 0;
argv[n++] = c->conf->daemon_binary;
argv[n++] = "--daemonize=yes";
snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]);
argv[n++] = strdup(t);
while (n < MAX_ARGS) {
char *a;
if (!(a = pa_split_spaces(c->conf->extra_arguments, &state)))
break;
argv[n++] = a;
}
argv[n++] = NULL;
execv(argv[0], (char * const *) argv);
_exit(1);
#undef MAX_ARGS
}
/* Parent */
r = waitpid(pid, &status, 0);
if (c->spawn_api.postfork)
c->spawn_api.postfork();
if (r < 0) {
pa_log(__FILE__": waitpid() failed: %s\n", strerror(errno));
pa_context_fail(c, PA_ERROR_INTERNAL);
goto fail;
} else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED);
goto fail;
}
close(fds[1]);
c->local = 1;
io = pa_iochannel_new(c->mainloop, fds[0], fds[0]);
setup_context(c, io);
unlock_autospawn_lock_file(c);
pa_context_unref(c);
return 0;
fail:
if (fds[0] != -1)
close(fds[0]);
if (fds[1] != -1)
close(fds[1]);
unlock_autospawn_lock_file(c);
pa_context_unref(c);
return -1;
}
#endif /* OS_IS_WIN32 */
static int try_next_connection(pa_context *c) {
char *u = NULL;
int r = -1;
assert(c && !c->client);
for (;;) {
if (u)
pa_xfree(u);
u = NULL;
c->server_list = pa_strlist_pop(c->server_list, &u);
if (!u) {
#ifndef OS_IS_WIN32
if (c->do_autospawn) {
r = context_connect_spawn(c);
goto finish;
}
#endif
pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED);
goto finish;
}
pa_log_debug(__FILE__": Trying to connect to %s...\n", u);
pa_xfree(c->server);
c->server = pa_xstrdup(u);
if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT)))
continue;
c->local = pa_socket_client_is_local(c->client);
pa_socket_client_set_callback(c->client, on_connection, c);
break;
}
r = 0;
finish:
if (u)
pa_xfree(u);
return r;
}
static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) {
pa_context *c = userdata;
assert(client && c && c->state == PA_CONTEXT_CONNECTING);
pa_context_ref(c);
pa_socket_client_unref(client);
c->client = NULL;
if (!io) {
/* Try the item in the list */
if (errno == ECONNREFUSED || errno == ETIMEDOUT || errno == EHOSTUNREACH) {
try_next_connection(c);
goto finish;
}
pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED);
goto finish;
}
unlock_autospawn_lock_file(c);
setup_context(c, io);
finish:
pa_context_unref(c);
}
int pa_context_connect(pa_context *c, const char *server, int spawn, const pa_spawn_api *api) {
int r = -1;
assert(c && c->ref >= 1 && c->state == PA_CONTEXT_UNCONNECTED);
if (!server)
server = c->conf->default_server;
pa_context_ref(c);
assert(!c->server_list);
if (server) {
if (!(c->server_list = pa_strlist_parse(server))) {
pa_context_fail(c, PA_ERROR_INVALIDSERVER);
goto finish;
}
} else {
char *d;
char ufn[PATH_MAX];
/* Prepend in reverse order */
if ((d = getenv("DISPLAY"))) {
char *e;
d = pa_xstrdup(d);
if ((e = strchr(d, ':')))
*e = 0;
if (*d)
c->server_list = pa_strlist_prepend(c->server_list, d);
pa_xfree(d);
}
c->server_list = pa_strlist_prepend(c->server_list, "tcp6:localhost");
c->server_list = pa_strlist_prepend(c->server_list, "localhost");
c->server_list = pa_strlist_prepend(c->server_list, pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET, ufn, sizeof(ufn)));
/* Wrap the connection attempts in a single transaction for sane autospawn locking */
if (spawn && c->conf->autospawn) {
char lf[PATH_MAX];
pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
pa_make_secure_parent_dir(lf);
assert(c->autospawn_lock_fd <= 0);
c->autospawn_lock_fd = pa_lock_lockfile(lf);
if (api)
c->spawn_api = *api;
c->do_autospawn = 1;
}
}
pa_context_set_state(c, PA_CONTEXT_CONNECTING);
r = try_next_connection(c);
finish:
pa_context_unref(c);
return r;
}
void pa_context_disconnect(pa_context *c) {
assert(c);
pa_context_set_state(c, PA_CONTEXT_TERMINATED);
}
pa_context_state_t pa_context_get_state(pa_context *c) {
assert(c && c->ref >= 1);
return c->state;
}
int pa_context_errno(pa_context *c) {
assert(c && c->ref >= 1);
return c->error;
}
void pa_context_set_state_callback(pa_context *c, void (*cb)(pa_context *c, void *userdata), void *userdata) {
assert(c && c->ref >= 1);
c->state_callback = cb;
c->state_userdata = userdata;
}
int pa_context_is_pending(pa_context *c) {
assert(c && c->ref >= 1);
/* pa_log("pstream: %i\n", pa_pstream_is_pending(c->pstream)); */
/* pa_log("pdispatch: %i\n", pa_pdispatch_is_pending(c->pdispatch)); */
return (c->pstream && pa_pstream_is_pending(c->pstream)) ||
(c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) ||
c->client;
}
static void set_dispatch_callbacks(pa_operation *o);
static void pdispatch_drain_callback(PA_GCC_UNUSED pa_pdispatch*pd, void *userdata) {
set_dispatch_callbacks(userdata);
}
static void pstream_drain_callback(PA_GCC_UNUSED pa_pstream *s, void *userdata) {
set_dispatch_callbacks(userdata);
}
static void set_dispatch_callbacks(pa_operation *o) {
int done = 1;
assert(o && o->context && o->context->ref >= 1 && o->ref >= 1 && o->context->state == PA_CONTEXT_READY);
pa_pstream_set_drain_callback(o->context->pstream, NULL, NULL);
pa_pdispatch_set_drain_callback(o->context->pdispatch, NULL, NULL);
if (pa_pdispatch_is_pending(o->context->pdispatch)) {
pa_pdispatch_set_drain_callback(o->context->pdispatch, pdispatch_drain_callback, o);
done = 0;
}
if (pa_pstream_is_pending(o->context->pstream)) {
pa_pstream_set_drain_callback(o->context->pstream, pstream_drain_callback, o);
done = 0;
}
if (!done)
pa_operation_ref(o);
else {
if (o->callback) {
void (*cb)(pa_context *c, void *userdata);
cb = (void (*)(pa_context*, void*)) o->callback;
cb(o->context, o->userdata);
}
pa_operation_done(o);
}
pa_operation_unref(o);
}
pa_operation* pa_context_drain(pa_context *c, void (*cb) (pa_context*c, void *userdata), void *userdata) {
pa_operation *o;
assert(c && c->ref >= 1);
if (c->state != PA_CONTEXT_READY)
return NULL;
if (!pa_context_is_pending(c))
return NULL;
o = pa_operation_new(c, NULL);
assert(o);
o->callback = (pa_operation_callback) cb;
o->userdata = userdata;
set_dispatch_callbacks(pa_operation_ref(o));
return o;
}
void pa_context_exit_daemon(pa_context *c) {
pa_tagstruct *t;
assert(c && c->ref >= 1);
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_EXIT);
pa_tagstruct_putu32(t, c->ctag++);
pa_pstream_send_tagstruct(c->pstream, t);
}
void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
int success = 1;
assert(pd && o && o->context && o->ref >= 1);
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
goto finish;
success = 0;
} else if (!pa_tagstruct_eof(t)) {
pa_context_fail(o->context, PA_ERROR_PROTOCOL);
goto finish;
}
if (o->callback) {
void (*cb)(pa_context *c, int _success, void *_userdata) = (void (*)(pa_context *c, int _success, void *_userdata)) o->callback;
cb(o->context, success, o->userdata);
}
finish:
pa_operation_done(o);
pa_operation_unref(o);
}
pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata) {
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
assert(c && cb);
o = pa_operation_new(c, NULL);
o->callback = cb;
o->userdata = userdata;
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, command);
pa_tagstruct_putu32(t, tag = c->ctag++);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_callback, o);
return pa_operation_ref(o);
}
pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) {
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
assert(c && cb);
o = pa_operation_new(c, NULL);
o->callback = (pa_operation_callback) cb;
o->userdata = userdata;
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_SET_DEFAULT_SINK);
pa_tagstruct_putu32(t, tag = c->ctag++);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
return pa_operation_ref(o);
}
pa_operation* pa_context_set_default_source(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) {
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
assert(c && cb);
o = pa_operation_new(c, NULL);
o->callback = (pa_operation_callback) cb;
o->userdata = userdata;
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_SET_DEFAULT_SOURCE);
pa_tagstruct_putu32(t, tag = c->ctag++);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
return pa_operation_ref(o);
}
int pa_context_is_local(pa_context *c) {
assert(c);
return c->local;
}
pa_operation* pa_context_set_name(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) {
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
assert(c && name && cb);
o = pa_operation_new(c, NULL);
o->callback = (pa_operation_callback) cb;
o->userdata = userdata;
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_SET_CLIENT_NAME);
pa_tagstruct_putu32(t, tag = c->ctag++);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
return pa_operation_ref(o);
}
const char* pa_get_library_version(void) {
return PACKAGE_VERSION;
}
const char* pa_context_get_server(pa_context *c) {
if (!c->server)
return NULL;
if (*c->server == '{') {
char *e = strchr(c->server+1, '}');
return e ? e+1 : c->server;
}
return c->server;
}

View file

@ -0,0 +1,117 @@
#ifndef foopolyplibcontexthfoo
#define foopolyplibcontexthfoo
/* $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 <polyp/polyplib-def.h>
#include <polyp/mainloop-api.h>
#include <polyp/cdecl.h>
#include <polyp/polyplib-operation.h>
/** \file
* Connection contexts for asynchrononous communication with a
* server. A pa_context object wraps a connection to a polypaudio
* server using its native protocol. A context may be used to issue
* commands on the server or to create playback or recording
* streams. Multiple playback streams may be piped through a single
* connection context. Operations on the contect involving
* communication with the server are executed asynchronously: i.e. the
* client function do not implicitely wait for completion of the
* operation on the server. Instead the caller specifies a call back
* function that is called when the operation is completed. Currently
* running operations may be canceled using pa_operation_cancel(). */
/** \example pacat.c
* A playback and recording tool using the asynchronous API */
/** \example paplay.c
* A sound file playback tool using the asynchronous API, based on libsndfile */
PA_C_DECL_BEGIN
/** \pa_context
* An opaque connection context to a daemon */
typedef struct pa_context pa_context;
/** Instantiate a new connection context with an abstract mainloop API
* and an application name */
pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name);
/** Decrease the reference counter of the context by one */
void pa_context_unref(pa_context *c);
/** Increase the reference counter of the context by one */
pa_context* pa_context_ref(pa_context *c);
typedef void (*pa_context_state_callback)(pa_context *c, void *userdata);
/** Set a callback function that is called whenever the context status changes */
void pa_context_set_state_callback(pa_context *c, pa_context_state_callback callback, void *userdata);
/** Return the error number of the last failed operation */
int pa_context_errno(pa_context *c);
/** Return non-zero if some data is pending to be written to the connection */
int pa_context_is_pending(pa_context *c);
/** Return the current context status */
pa_context_state_t pa_context_get_state(pa_context *c);
/** Connect the context to the specified server. If server is NULL,
connect to the default server. This routine may but will not always
return synchronously on error. Use pa_context_set_state_callback() to
be notified when the connection is established. If spawn is non-zero
and no specific server is specified or accessible a new daemon is
spawned. If api is non-NULL, the functions specified in the structure
are used when forking a new child process. */
int pa_context_connect(pa_context *c, const char *server, int spawn, const pa_spawn_api *api);
/** Terminate the context connection immediately */
void pa_context_disconnect(pa_context *c);
/** Drain the context. If there is nothing to drain, the function returns NULL */
pa_operation* pa_context_drain(pa_context *c, void (*cb) (pa_context*c, void *userdata), void *userdata);
/** Tell the daemon to exit. No operation object is returned as the
* connection is terminated when the daemon quits, thus this operation
* would never complete. */
void pa_context_exit_daemon(pa_context *c);
/** Set the name of the default sink. \since 0.4 */
pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata);
/** Set the name of the default source. \since 0.4 */
pa_operation* pa_context_set_default_source(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata);
/** Returns 1 when the connection is to a local daemon. Returns negative when no connection has been made yet. \since 0.5 */
int pa_context_is_local(pa_context *c);
/** Set a different application name for context on the server. \since 0.5 */
pa_operation* pa_context_set_name(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata);
/** Return the server name this context is connected to. \since 0.7 */
const char* pa_context_get_server(pa_context *c);
PA_C_DECL_END
#endif

213
src/polyp/polyplib-def.h Normal file
View file

@ -0,0 +1,213 @@
#ifndef foopolyplibdefhfoo
#define foopolyplibdefhfoo
/* $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 <sys/time.h>
#include <time.h>
#include <polyp/cdecl.h>
#include <polyp/sample.h>
/** \file
* Global definitions */
PA_C_DECL_BEGIN
/** The state of a connection context */
typedef enum pa_context_state {
PA_CONTEXT_UNCONNECTED, /**< The context hasn't been connected yet */
PA_CONTEXT_CONNECTING, /**< A connection is being established */
PA_CONTEXT_AUTHORIZING, /**< The client is authorizing itself to the daemon */
PA_CONTEXT_SETTING_NAME, /**< The client is passing its application name to the daemon */
PA_CONTEXT_READY, /**< The connection is established, the context is ready to execute operations */
PA_CONTEXT_FAILED, /**< The connection failed or was disconnected */
PA_CONTEXT_TERMINATED /**< The connection was terminated cleanly */
} pa_context_state_t;
/** The state of a stream */
typedef enum pa_stream_state {
PA_STREAM_DISCONNECTED, /**< The stream is not yet connected to any sink or source */
PA_STREAM_CREATING, /**< The stream is being created */
PA_STREAM_READY, /**< The stream is established, you may pass audio data to it now */
PA_STREAM_FAILED, /**< An error occured that made the stream invalid */
PA_STREAM_TERMINATED /**< The stream has been terminated cleanly */
} pa_stream_state_t;
/** The state of an operation */
typedef enum pa_operation_state {
PA_OPERATION_RUNNING, /**< The operation is still running */
PA_OPERATION_DONE, /**< The operation has been completed */
PA_OPERATION_CANCELED /**< The operation has been canceled */
} pa_operation_state_t;
/** An invalid index */
#define PA_INVALID_INDEX ((uint32_t) -1)
/** The direction of a pa_stream object */
typedef enum pa_stream_direction {
PA_STREAM_NODIRECTION, /**< Invalid direction */
PA_STREAM_PLAYBACK, /**< Playback stream */
PA_STREAM_RECORD, /**< Record stream */
PA_STREAM_UPLOAD /**< Sample upload stream */
} pa_stream_direction_t;
/** Some special flags for stream connections. \since 0.6 */
typedef enum pa_stream_flags {
PA_STREAM_START_CORKED = 1, /**< Create the stream corked, requiring an explicit pa_stream_cork() call to uncork it. */
PA_STREAM_INTERPOLATE_LATENCY = 2 /**< Interpolate the latency for
* this stream. When enabled,
* you can use
* pa_stream_interpolated_xxx()
* for synchronization. Using
* these functions instead of
* pa_stream_get_latency() has
* the advantage of not
* requiring a whole roundtrip
* for responses. Consider using
* this option when frequently
* requesting latency
* information. This is
* especially useful on long latency
* network connections. */
} pa_stream_flags_t;
/** Playback and record buffer metrics */
typedef struct pa_buffer_attr {
uint32_t maxlength; /**< Maximum length of the buffer */
uint32_t tlength; /**< Playback only: target length of the buffer. The server tries to assure that at least tlength bytes are always available in the buffer */
uint32_t prebuf; /**< Playback only: pre-buffering. The server does not start with playback before at least prebug bytes are available in the buffer */
uint32_t minreq; /**< Playback only: minimum request. The server does not request less than minreq bytes from the client, instead waints until the buffer is free enough to request more bytes at once */
uint32_t fragsize; /**< Recording only: fragment size. The server sends data in blocks of fragsize bytes size. Large values deminish interactivity with other operations on the connection context but decrease control overhead. */
} pa_buffer_attr;
/** Error values as used by pa_context_errno(). Use pa_strerror() to convert these values to human readable strings */
enum {
PA_ERROR_OK, /**< No error */
PA_ERROR_ACCESS, /**< Access failure */
PA_ERROR_COMMAND, /**< Unknown command */
PA_ERROR_INVALID, /**< Invalid argument */
PA_ERROR_EXIST, /**< Entity exists */
PA_ERROR_NOENTITY, /**< No such entity */
PA_ERROR_CONNECTIONREFUSED, /**< Connection refused */
PA_ERROR_PROTOCOL, /**< Protocol error */
PA_ERROR_TIMEOUT, /**< Timeout */
PA_ERROR_AUTHKEY, /**< No authorization key */
PA_ERROR_INTERNAL, /**< Internal error */
PA_ERROR_CONNECTIONTERMINATED, /**< Connection terminated */
PA_ERROR_KILLED, /**< Entity killed */
PA_ERROR_INVALIDSERVER, /**< Invalid server */
PA_ERROR_INITFAILED, /**< Module initialization failed */
PA_ERROR_MAX /**< Not really an error but the first invalid error code */
};
/** Subscription event mask, as used by pa_context_subscribe() */
typedef enum pa_subscription_mask {
PA_SUBSCRIPTION_MASK_NULL = 0, /**< No events */
PA_SUBSCRIPTION_MASK_SINK = 1, /**< Sink events */
PA_SUBSCRIPTION_MASK_SOURCE = 2, /**< Source events */
PA_SUBSCRIPTION_MASK_SINK_INPUT = 4, /**< Sink input events */
PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT = 8, /**< Source output events */
PA_SUBSCRIPTION_MASK_MODULE = 16, /**< Module events */
PA_SUBSCRIPTION_MASK_CLIENT = 32, /**< Client events */
PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 64, /**< Sample cache events */
PA_SUBSCRIPTION_MASK_SERVER = 128, /**< Other global server changes. \since 0.4 */
PA_SUBSCRIPTION_MASK_AUTOLOAD = 256 /**< Autoload table events. \since 0.5 */
} pa_subscription_mask_t;
/** Subscription event types, as used by pa_context_subscribe() */
typedef enum pa_subscription_event_type {
PA_SUBSCRIPTION_EVENT_SINK = 0, /**< Event type: Sink */
PA_SUBSCRIPTION_EVENT_SOURCE = 1, /**< Event type: Source */
PA_SUBSCRIPTION_EVENT_SINK_INPUT = 2, /**< Event type: Sink input */
PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT = 3, /**< Event type: Source output */
PA_SUBSCRIPTION_EVENT_MODULE = 4, /**< Event type: Module */
PA_SUBSCRIPTION_EVENT_CLIENT = 5, /**< Event type: Client */
PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 6, /**< Event type: Sample cache item */
PA_SUBSCRIPTION_EVENT_SERVER = 7, /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. \since 0.4 */
PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8, /**< Event type: Autoload table changes. \since 0.5 */
PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 15, /**< A mask to extract the event type from an event value */
PA_SUBSCRIPTION_EVENT_NEW = 0, /**< A new object was created */
PA_SUBSCRIPTION_EVENT_CHANGE = 16, /**< A property of the object was modified */
PA_SUBSCRIPTION_EVENT_REMOVE = 32, /**< An object was removed */
PA_SUBSCRIPTION_EVENT_TYPE_MASK = 16+32 /**< A mask to extract the event operation from an event value */
} pa_subscription_event_type_t;
/** Return one if an event type t matches an event mask bitfield */
#define pa_subscription_match_flags(m, t) (!!((m) & (1 << ((t) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK))))
/** A structure for latency info. See pa_stream_get_latency(). The
* total output latency a sample that is written with
* pa_stream_write() takes to be played may be estimated by
* sink_usec+buffer_usec+transport_usec. The output buffer to which
* buffer_usec relates may be manipulated freely (with
* pa_stream_write()'s delta argument, pa_stream_flush() and friends),
* the buffers sink_usec/source_usec relates to is a first-in
* first-out buffer which cannot be flushed or manipulated in any
* way. The total input latency a sample that is recorded takes to be
* delivered to the application is:
* source_usec+buffer_usec+transport_usec-sink_usec. (Take care of
* sign issues!) When connected to a monitor source sink_usec contains
* the latency of the owning sink.*/
typedef struct pa_latency_info {
pa_usec_t buffer_usec; /**< Time in usecs the current buffer takes to play. For both playback and record streams. */
pa_usec_t sink_usec; /**< Time in usecs a sample takes to be played on the sink. For playback streams and record streams connected to a monitor source. */
pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. \since 0.5*/
pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. \since 0.5 */
int playing; /**< Non-zero when the stream is currently playing. Only for playback streams. */
uint32_t queue_length; /**< Queue size in bytes. For both playback and record streams. */
int synchronized_clocks; /**< Non-zero if the local and the
* remote machine have synchronized
* clocks. If synchronized clocks are
* detected transport_usec becomes much
* more reliable. However, the code that
* detects synchronized clocks is very
* limited und unreliable itself. \since
* 0.5 */
struct timeval timestamp; /**< The time when this latency info was current */
uint64_t counter; /**< The byte counter current when the latency info was requested. \since 0.6 */
} pa_latency_info;
/** A structure for the spawn api. This may be used to integrate auto
* spawned daemons into your application. For more information see
* pa_context_connect(). When spawning a new child process the
* waitpid() is used on the child's PID. The spawn routine will not
* block or ignore SIGCHLD signals, since this cannot be done in a
* thread compatible way. You might have to do this in
* prefork/postfork. \since 0.4 */
typedef struct pa_spawn_api {
void (*prefork)(void); /**< Is called just before the fork in the parent process. May be NULL. */
void (*postfork)(void); /**< Is called immediately after the fork in the parent process. May be NULL.*/
void (*atfork)(void); /**< Is called immediately after the
* fork in the child process. May be
* NULL. It is not safe to close all
* file descriptors in this function
* unconditionally, since a UNIX socket
* (created using socketpair()) is
* passed to the new process. */
} pa_spawn_api;
PA_C_DECL_END
#endif

View file

@ -0,0 +1,54 @@
/* $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 <stdlib.h>
#include "polyplib-error.h"
#include <polypcore/native-common.h>
static const char* const errortab[PA_ERROR_MAX] = {
[PA_ERROR_OK] = "OK",
[PA_ERROR_ACCESS] = "Access denied",
[PA_ERROR_COMMAND] = "Unknown command",
[PA_ERROR_INVALID] = "Invalid argument",
[PA_ERROR_EXIST] = "Entity exists",
[PA_ERROR_NOENTITY] = "No such entity",
[PA_ERROR_CONNECTIONREFUSED] = "Connection refused",
[PA_ERROR_PROTOCOL] = "Protocol error",
[PA_ERROR_TIMEOUT] = "Timeout",
[PA_ERROR_AUTHKEY] = "No authorization key",
[PA_ERROR_INTERNAL] = "Internal error",
[PA_ERROR_CONNECTIONTERMINATED] = "Connection terminated",
[PA_ERROR_KILLED] = "Entity killed",
[PA_ERROR_INVALIDSERVER] = "Invalid server",
};
const char*pa_strerror(uint32_t error) {
if (error >= PA_ERROR_MAX)
return NULL;
return errortab[error];
}

View file

@ -0,0 +1,38 @@
#ifndef foopolypliberrorhfoo
#define foopolypliberrorhfoo
/* $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/cdecl.h>
/** \file
* Error management */
PA_C_DECL_BEGIN
/** Return a human readable error message for the specified numeric error code */
const char* pa_strerror(uint32_t error);
PA_C_DECL_END
#endif

View file

@ -0,0 +1,154 @@
#ifndef foopolyplibinternalhfoo
#define foopolyplibinternalhfoo
/* $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/mainloop-api.h>
#include <polypcore/socket-client.h>
#include <polypcore/pstream.h>
#include <polypcore/pdispatch.h>
#include <polypcore/dynarray.h>
#include "polyplib-context.h"
#include "polyplib-stream.h"
#include "polyplib-operation.h"
#include <polypcore/llist.h>
#include <polypcore/native-common.h>
#include <polyp/client-conf.h>
#include <polypcore/strlist.h>
#include <polypcore/mcalign.h>
#define DEFAULT_TIMEOUT (10)
struct pa_context {
int ref;
char *name;
pa_mainloop_api* mainloop;
pa_socket_client *client;
pa_pstream *pstream;
pa_pdispatch *pdispatch;
pa_dynarray *record_streams, *playback_streams;
PA_LLIST_HEAD(pa_stream, streams);
PA_LLIST_HEAD(pa_operation, operations);
uint32_t ctag;
uint32_t error;
pa_context_state_t state;
void (*state_callback)(pa_context*c, void *userdata);
void *state_userdata;
void (*subscribe_callback)(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata);
void *subscribe_userdata;
pa_memblock_stat *memblock_stat;
int local;
int do_autospawn;
int autospawn_lock_fd;
pa_spawn_api spawn_api;
pa_strlist *server_list;
char *server;
pa_client_conf *conf;
};
struct pa_stream {
int ref;
pa_context *context;
pa_mainloop_api *mainloop;
PA_LLIST_FIELDS(pa_stream);
char *name;
pa_buffer_attr buffer_attr;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
uint32_t channel;
int channel_valid;
uint32_t device_index;
pa_stream_direction_t direction;
uint32_t requested_bytes;
uint64_t counter;
pa_usec_t previous_time;
pa_usec_t previous_ipol_time;
pa_stream_state_t state;
pa_mcalign *mcalign;
int interpolate;
int corked;
uint32_t ipol_usec;
struct timeval ipol_timestamp;
pa_time_event *ipol_event;
int ipol_requested;
void (*state_callback)(pa_stream*c, void *userdata);
void *state_userdata;
void (*read_callback)(pa_stream *p, const void*data, size_t length, void *userdata);
void *read_userdata;
void (*write_callback)(pa_stream *p, size_t length, void *userdata);
void *write_userdata;
};
typedef void (*pa_operation_callback)(void);
struct pa_operation {
int ref;
pa_context *context;
pa_stream *stream;
PA_LLIST_FIELDS(pa_operation);
pa_operation_state_t state;
void *userdata;
pa_operation_callback callback;
};
void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
pa_operation *pa_operation_new(pa_context *c, pa_stream *s);
void pa_operation_done(pa_operation *o);
void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_context_fail(pa_context *c, int error);
void pa_context_set_state(pa_context *c, pa_context_state_t st);
int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t);
pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata);
void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);
void pa_stream_trash_ipol(pa_stream *s);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,279 @@
#ifndef foopolyplibintrospecthfoo
#define foopolyplibintrospecthfoo
/* $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/polyplib-operation.h>
#include <polyp/polyplib-context.h>
#include <polyp/cdecl.h>
#include <polyp/channelmap.h>
#include <polyp/volume.h>
/** \file
*
* Routines for daemon introspection. When enumerating all entitites
* of a certain kind, use the pa_context_xxx_list() functions. The
* specified callback function is called once for each entry. The
* enumeration is finished by a call to the callback function with
* is_last=1 and i=NULL. Strings referenced in pa_xxx_info structures
* and the structures themselves point to internal memory that may not
* be modified. That memory is only valid during the call to the
* callback function. A deep copy is required if you need this data
* outside the callback functions. An error is signalled by a call to * the callback function with i=NULL and is_last=0.
*
* When using the routines that ask fo a single entry only, a callback
* with the same signature is used. However, no finishing call to the
* routine is issued. */
PA_C_DECL_BEGIN
/** Stores information about sinks */
typedef struct pa_sink_info {
const char *name; /**< Name of the sink */
uint32_t index; /**< Index of the sink */
const char *description; /**< Description of this sink */
pa_sample_spec sample_spec; /**< Sample spec of this sink */
pa_channel_map channel_map; /**< Channel map \since 0.9 */
uint32_t owner_module; /**< Index of the owning module of this sink, or PA_INVALID_INDEX */
pa_cvolume volume; /**< Volume of the sink */
uint32_t monitor_source; /**< Index of the monitor source connected to this sink */
const char *monitor_source_name; /**< The name of the monitor source */
pa_usec_t latency; /**< Length of filled playback buffer of this sink */
const char *driver; /**< Driver name. \since 0.9 */
} pa_sink_info;
/** Get information about a sink by its name */
pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata);
/** Get information about a sink by its index */
pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t id, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata);
/** Get the complete sink list */
pa_operation* pa_context_get_sink_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata);
/** Stores information about sources */
typedef struct pa_source_info {
const char *name ; /**< Name of the source */
uint32_t index; /**< Index of the source */
const char *description; /**< Description of this source */
pa_sample_spec sample_spec; /**< Sample spec of this source */
pa_channel_map channel_map; /**< Channel map \since 0.9 */
uint32_t owner_module; /**< Owning module index, or PA_INVALID_INDEX */
uint32_t monitor_of_sink; /**< If this is a monitor source the index of the owning sink, otherwise PA_INVALID_INDEX */
const char *monitor_of_sink_name; /**< Name of the owning sink, or PA_INVALID_INDEX */
pa_usec_t latency; /**< Length of filled record buffer of this source. \since 0.5 */
const char *driver; /**< Driver name \since 0.9 */
} pa_source_info;
/** Get information about a source by its name */
pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata);
/** Get information about a source by its index */
pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t id, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata);
/** Get the complete source list */
pa_operation* pa_context_get_source_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata);
/** Server information */
typedef struct pa_server_info {
const char *user_name; /**< User name of the daemon process */
const char *host_name; /**< Host name the daemon is running on */
const char *server_version; /**< Version string of the daemon */
const char *server_name; /**< Server package name (usually "polypaudio") */
pa_sample_spec sample_spec; /**< Default sample specification */
const char *default_sink_name; /**< Name of default sink. \since 0.4 */
const char *default_source_name; /**< Name of default sink. \since 0.4*/
uint32_t cookie; /**< A random cookie for identifying this instance of polypaudio. \since 0.8 */
} pa_server_info;
/** Get some information about the server */
pa_operation* pa_context_get_server_info(pa_context *c, void (*cb)(pa_context *c, const pa_server_info*i, void *userdata), void *userdata);
/** Stores information about modules */
typedef struct pa_module_info {
uint32_t index; /**< Index of the module */
const char*name, /**< Name of the module */
*argument; /**< Argument string of the module */
uint32_t n_used; /**< Usage counter or PA_INVALID_INDEX */
int auto_unload; /**< Non-zero if this is an autoloaded module */
} pa_module_info;
/** Get some information about a module by its index */
pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata);
/** Get the complete list of currently loaded modules */
pa_operation* pa_context_get_module_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata);
/** Stores information about clients */
typedef struct pa_client_info {
uint32_t index; /**< Index of this client */
const char *name; /**< Name of this client */
uint32_t owner_module; /**< Index of the owning module, or PA_INVALID_INDEX */
const char *driver; /**< Driver name \since 0.9 */
} pa_client_info;
/** Get information about a client by its index */
pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata);
/** Get the complete client list */
pa_operation* pa_context_get_client_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata);
/** Stores information about sink inputs */
typedef struct pa_sink_input_info {
uint32_t index; /**< Index of the sink input */
const char *name; /**< Name of the sink input */
uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */
uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */
uint32_t sink; /**< Index of the connected sink */
pa_sample_spec sample_spec; /**< The sample specification of the sink input */
pa_channel_map channel_map; /**< Channel map */
pa_cvolume volume; /**< The volume of this sink input */
pa_usec_t buffer_usec; /**< Latency due to buffering in sink input, see pa_latency_info for details */
pa_usec_t sink_usec; /**< Latency of the sink device, see pa_latency_info for details */
const char *resample_method; /**< Thre resampling method used by this sink input. \since 0.7 */
const char *driver; /**< Driver name \since 0.9 */
} pa_sink_input_info;
/** Get some information about a sink input by its index */
pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata);
/** Get the complete sink input list */
pa_operation* pa_context_get_sink_input_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata);
/** Stores information about source outputs */
typedef struct pa_source_output_info {
uint32_t index; /**< Index of the sink input */
const char *name; /**< Name of the sink input */
uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */
uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */
uint32_t source; /**< Index of the connected source */
pa_sample_spec sample_spec; /**< The sample specification of the source output */
pa_channel_map channel_map; /**< Channel map */
pa_usec_t buffer_usec; /**< Latency due to buffering in the source output, see pa_latency_info for details. \since 0.5 */
pa_usec_t source_usec; /**< Latency of the source device, see pa_latency_info for details. \since 0.5 */
const char *resample_method; /**< Thre resampling method used by this source output. \since 0.7 */
const char *driver; /**< Driver name \since 0.9 */
} pa_source_output_info;
/** Get information about a source output by its index */
pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata);
/** Get the complete list of source outputs */
pa_operation* pa_context_get_source_output_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata);
/** Set the volume of a sink device specified by its index */
pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
/** Set the volume of a sink device specified by its name */
pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
/** Set the volume of a sink input stream */
pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
/** Memory block statistics */
typedef struct pa_stat_info {
uint32_t memblock_total; /**< Currently allocated memory blocks */
uint32_t memblock_total_size; /**< Currentl total size of allocated memory blocks */
uint32_t memblock_allocated; /**< Allocated memory blocks during the whole lifetime of the daemon */
uint32_t memblock_allocated_size; /**< Total size of all memory blocks allocated during the whole lifetime of the daemon */
uint32_t scache_size; /**< Total size of all sample cache entries. \since 0.4 */
} pa_stat_info;
/** Get daemon memory block statistics */
pa_operation* pa_context_stat(pa_context *c, void (*cb)(pa_context *c, const pa_stat_info *i, void *userdata), void *userdata);
/** Stores information about sample cache entries */
typedef struct pa_sample_info {
uint32_t index; /**< Index of this entry */
const char *name; /**< Name of this entry */
pa_cvolume volume; /**< Default volume of this entry */
pa_sample_spec sample_spec; /**< Sample specification of the sample */
pa_channel_map channel_map; /**< The channel map */
pa_usec_t duration; /**< Duration of this entry */
uint32_t bytes; /**< Length of this sample in bytes. \since 0.4 */
int lazy; /**< Non-zero when this is a lazy cache entry. \since 0.5 */
const char *filename; /**< In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. \since 0.5 */
} pa_sample_info;
/** Get information about a sample by its name */
pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata);
/** Get information about a sample by its index */
pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata);
/** Get the complete list of samples stored in the daemon. */
pa_operation* pa_context_get_sample_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata);
/** Kill a client. \since 0.5 */
pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
/** Kill a sink input. \since 0.5 */
pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
/** Kill a source output. \since 0.5 */
pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
/** Load a module. \since 0.5 */
pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, void (*cb)(pa_context *c, uint32_t idx, void *userdata), void *userdata);
/** Unload a module. \since 0.5 */
pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
/** Type of an autoload entry. \since 0.5 */
typedef enum pa_autoload_type {
PA_AUTOLOAD_SINK = 0,
PA_AUTOLOAD_SOURCE = 1
} pa_autoload_type_t;
/** Stores information about autoload entries. \since 0.5 */
typedef struct pa_autoload_info {
uint32_t index; /**< Index of this autoload entry */
const char *name; /**< Name of the sink or source */
pa_autoload_type_t type; /**< Type of the autoload entry */
const char *module; /**< Module name to load */
const char *argument; /**< Argument string for module */
} pa_autoload_info;
/** Get info about a specific autoload entry. \since 0.6 */
pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata);
/** Get info about a specific autoload entry. \since 0.6 */
pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata);
/** Get the complete list of autoload entries. \since 0.5 */
pa_operation* pa_context_get_autoload_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata);
/** Add a new autoload entry. \since 0.5 */
pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, void (*cb)(pa_context *c, int idx, void *userdata), void* userdata);
/** Remove an autoload entry. \since 0.6 */
pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, void (*cb)(pa_context *c, int success, void *userdata), void* userdata);
/** Remove an autoload entry. \since 0.6 */
pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void* userdata);
PA_C_DECL_END
#endif

View file

@ -0,0 +1,103 @@
/* $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 <polypcore/xmalloc.h>
#include "polyplib-internal.h"
#include "polyplib-operation.h"
pa_operation *pa_operation_new(pa_context *c, pa_stream *s) {
pa_operation *o;
assert(c);
o = pa_xmalloc(sizeof(pa_operation));
o->ref = 1;
o->context = pa_context_ref(c);
o->stream = s ? pa_stream_ref(s) : NULL;
o->state = PA_OPERATION_RUNNING;
o->userdata = NULL;
o->callback = NULL;
PA_LLIST_PREPEND(pa_operation, o->context->operations, o);
return pa_operation_ref(o);
}
pa_operation *pa_operation_ref(pa_operation *o) {
assert(o && o->ref >= 1);
o->ref++;
return o;
}
void pa_operation_unref(pa_operation *o) {
assert(o && o->ref >= 1);
if ((--(o->ref)) == 0) {
assert(!o->context);
assert(!o->stream);
free(o);
}
}
static void operation_set_state(pa_operation *o, pa_operation_state_t st) {
assert(o && o->ref >= 1);
if (st == o->state)
return;
if (!o->context)
return;
o->state = st;
if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) {
PA_LLIST_REMOVE(pa_operation, o->context->operations, o);
pa_context_unref(o->context);
if (o->stream)
pa_stream_unref(o->stream);
o->context = NULL;
o->stream = NULL;
o->callback = NULL;
o->userdata = NULL;
pa_operation_unref(o);
}
}
void pa_operation_cancel(pa_operation *o) {
assert(o && o->ref >= 1);
operation_set_state(o, PA_OPERATION_CANCELED);
}
void pa_operation_done(pa_operation *o) {
assert(o && o->ref >= 1);
operation_set_state(o, PA_OPERATION_DONE);
}
pa_operation_state_t pa_operation_get_state(pa_operation *o) {
assert(o && o->ref >= 1);
return o->state;
}

View file

@ -0,0 +1,51 @@
#ifndef foopolypliboperationhfoo
#define foopolypliboperationhfoo
/* $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/cdecl.h>
#include <polyp/polyplib-def.h>
/** \file
* Asynchronous operations */
PA_C_DECL_BEGIN
/** \pa_operation
* An asynchronous operation object */
typedef struct pa_operation pa_operation;
/** Increase the reference count by one */
pa_operation *pa_operation_ref(pa_operation *o);
/** Decrease the reference count by one */
void pa_operation_unref(pa_operation *o);
/** Cancel the operation. Beware! This will not necessarily cancel the execution of the operation on the server side. */
void pa_operation_cancel(pa_operation *o);
/** Return the current status of the operation */
pa_operation_state_t pa_operation_get_state(pa_operation *o);
PA_C_DECL_END
#endif

127
src/polyp/polyplib-scache.c Normal file
View file

@ -0,0 +1,127 @@
/* $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 "polyplib-scache.h"
#include "polyplib-internal.h"
#include <polypcore/pstream-util.h>
void pa_stream_connect_upload(pa_stream *s, size_t length) {
pa_tagstruct *t;
uint32_t tag;
assert(s && length);
pa_stream_ref(s);
s->state = PA_STREAM_CREATING;
s->direction = PA_STREAM_UPLOAD;
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_CREATE_UPLOAD_STREAM);
pa_tagstruct_putu32(t, tag = s->context->ctag++);
pa_tagstruct_puts(t, s->name);
pa_tagstruct_put_sample_spec(t, &s->sample_spec);
pa_tagstruct_putu32(t, length);
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s);
pa_stream_unref(s);
}
void pa_stream_finish_upload(pa_stream *s) {
pa_tagstruct *t;
uint32_t tag;
assert(s);
if (!s->channel_valid || !s->context->state == PA_CONTEXT_READY)
return;
pa_stream_ref(s);
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_FINISH_UPLOAD_STREAM);
pa_tagstruct_putu32(t, tag = s->context->ctag++);
pa_tagstruct_putu32(t, s->channel);
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s);
pa_stream_unref(s);
}
pa_operation * pa_context_play_sample(pa_context *c, const char *name, const char *dev, uint32_t volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
assert(c && name && *name && (!dev || *dev));
o = pa_operation_new(c, NULL);
o->callback = (pa_operation_callback) cb;
o->userdata = userdata;
if (!dev)
dev = c->conf->default_sink;
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_PLAY_SAMPLE);
pa_tagstruct_putu32(t, tag = c->ctag++);
pa_tagstruct_putu32(t, (uint32_t) -1);
pa_tagstruct_puts(t, dev);
pa_tagstruct_putu32(t, volume);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
return pa_operation_ref(o);
}
pa_operation* pa_context_remove_sample(pa_context *c, const char *name, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
assert(c && name);
o = pa_operation_new(c, NULL);
o->callback = (pa_operation_callback) cb;
o->userdata = userdata;
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_SAMPLE);
pa_tagstruct_putu32(t, tag = c->ctag++);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
return pa_operation_ref(o);
}

View file

@ -0,0 +1,50 @@
#ifndef foopolyplibscachehfoo
#define foopolyplibscachehfoo
/* $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 <polyp/polyplib-context.h>
#include <polyp/polyplib-stream.h>
#include <polyp/cdecl.h>
/** \file
* All sample cache related routines */
PA_C_DECL_BEGIN
/** Make this stream a sample upload stream */
void pa_stream_connect_upload(pa_stream *s, size_t length);
/** Finish the sample upload, the stream name will become the sample name. You cancel a sample upload by issuing pa_stream_disconnect() */
void pa_stream_finish_upload(pa_stream *s);
/** Play a sample from the sample cache to the specified device. If the latter is NULL use the default sink. Returns an operation object */
pa_operation* pa_context_play_sample(pa_context *c, const char *name, const char *dev, uint32_t volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
/** Remove a sample from the sample cache. Returns an operation object which may be used to cancel the operation while it is running */
pa_operation* pa_context_remove_sample(pa_context *c, const char *name, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
PA_C_DECL_END
#endif

393
src/polyp/polyplib-simple.c Normal file
View file

@ -0,0 +1,393 @@
/* $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 "polyplib-simple.h"
#include "polyplib.h"
#include "mainloop.h"
#include <polypcore/native-common.h>
#include <polypcore/xmalloc.h>
#include <polypcore/log.h>
struct pa_simple {
pa_mainloop *mainloop;
pa_context *context;
pa_stream *stream;
pa_stream_direction_t direction;
int dead;
void *read_data;
size_t read_index, read_length;
pa_usec_t latency;
};
static void read_callback(pa_stream *s, const void*data, size_t length, void *userdata);
static int check_error(pa_simple *p, int *rerror) {
pa_context_state_t cst;
pa_stream_state_t sst;
assert(p);
if ((cst = pa_context_get_state(p->context)) == PA_CONTEXT_FAILED)
goto fail;
assert(cst != PA_CONTEXT_TERMINATED);
if (p->stream) {
if ((sst = pa_stream_get_state(p->stream)) == PA_STREAM_FAILED)
goto fail;
assert(sst != PA_STREAM_TERMINATED);
}
return 0;
fail:
if (rerror)
*rerror = pa_context_errno(p->context);
p->dead = 1;
return -1;
}
static int iterate(pa_simple *p, int block, int *rerror) {
assert(p && p->context && p->mainloop);
if (check_error(p, rerror) < 0)
return -1;
if (!block && !pa_context_is_pending(p->context))
return 0;
do {
if (pa_mainloop_iterate(p->mainloop, 1, NULL) < 0) {
if (rerror)
*rerror = PA_ERROR_INTERNAL;
return -1;
}
if (check_error(p, rerror) < 0)
return -1;
} while (pa_context_is_pending(p->context));
while (pa_mainloop_deferred_pending(p->mainloop)) {
if (pa_mainloop_iterate(p->mainloop, 0, NULL) < 0) {
if (rerror)
*rerror = PA_ERROR_INTERNAL;
return -1;
}
if (check_error(p, rerror) < 0)
return -1;
}
return 0;
}
pa_simple* pa_simple_new(
const char *server,
const char *name,
pa_stream_direction_t dir,
const char *dev,
const char *stream_name,
const pa_sample_spec *ss,
const pa_buffer_attr *attr,
int *rerror) {
pa_simple *p;
int error = PA_ERROR_INTERNAL;
assert(ss && (dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD));
p = pa_xmalloc(sizeof(pa_simple));
p->context = NULL;
p->stream = NULL;
p->mainloop = pa_mainloop_new();
assert(p->mainloop);
p->dead = 0;
p->direction = dir;
p->read_data = NULL;
p->read_index = p->read_length = 0;
p->latency = 0;
if (!(p->context = pa_context_new(pa_mainloop_get_api(p->mainloop), name)))
goto fail;
pa_context_connect(p->context, server, 1, NULL);
/* Wait until the context is ready */
while (pa_context_get_state(p->context) != PA_CONTEXT_READY) {
if (iterate(p, 1, &error) < 0)
goto fail;
}
if (!(p->stream = pa_stream_new(p->context, stream_name, ss, NULL)))
goto fail;
if (dir == PA_STREAM_PLAYBACK)
pa_stream_connect_playback(p->stream, dev, attr, 0, NULL);
else
pa_stream_connect_record(p->stream, dev, attr, 0);
/* Wait until the stream is ready */
while (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
if (iterate(p, 1, &error) < 0)
goto fail;
}
pa_stream_set_read_callback(p->stream, read_callback, p);
return p;
fail:
if (rerror)
*rerror = error;
pa_simple_free(p);
return NULL;
}
void pa_simple_free(pa_simple *s) {
assert(s);
pa_xfree(s->read_data);
if (s->stream)
pa_stream_unref(s->stream);
if (s->context)
pa_context_unref(s->context);
if (s->mainloop)
pa_mainloop_free(s->mainloop);
pa_xfree(s);
}
int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) {
assert(p && data && p->direction == PA_STREAM_PLAYBACK);
if (p->dead) {
if (rerror)
*rerror = pa_context_errno(p->context);
return -1;
}
while (length > 0) {
size_t l;
while (!(l = pa_stream_writable_size(p->stream)))
if (iterate(p, 1, rerror) < 0)
return -1;
if (l > length)
l = length;
pa_stream_write(p->stream, data, l, NULL, 0);
data = (const uint8_t*) data + l;
length -= l;
}
/* Make sure that no data is pending for write */
if (iterate(p, 0, rerror) < 0)
return -1;
return 0;
}
static void read_callback(pa_stream *s, const void*data, size_t length, void *userdata) {
pa_simple *p = userdata;
assert(s && data && length && p);
if (p->read_data) {
pa_log(__FILE__": Buffer overflow, dropping incoming memory blocks.\n");
pa_xfree(p->read_data);
}
p->read_data = pa_xmemdup(data, p->read_length = length);
p->read_index = 0;
}
int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) {
assert(p && data && p->direction == PA_STREAM_RECORD);
if (p->dead) {
if (rerror)
*rerror = pa_context_errno(p->context);
return -1;
}
while (length > 0) {
if (p->read_data) {
size_t l = length;
if (p->read_length <= l)
l = p->read_length;
memcpy(data, (uint8_t*) p->read_data+p->read_index, l);
data = (uint8_t*) data + l;
length -= l;
p->read_index += l;
p->read_length -= l;
if (!p->read_length) {
pa_xfree(p->read_data);
p->read_data = NULL;
p->read_index = 0;
}
if (!length)
return 0;
assert(!p->read_data);
}
if (iterate(p, 1, rerror) < 0)
return -1;
}
return 0;
}
static void drain_or_flush_complete(pa_stream *s, int success, void *userdata) {
pa_simple *p = userdata;
assert(s && p);
if (!success)
p->dead = 1;
}
int pa_simple_drain(pa_simple *p, int *rerror) {
pa_operation *o;
assert(p && p->direction == PA_STREAM_PLAYBACK);
if (p->dead) {
if (rerror)
*rerror = pa_context_errno(p->context);
return -1;
}
o = pa_stream_drain(p->stream, drain_or_flush_complete, p);
while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
if (iterate(p, 1, rerror) < 0) {
pa_operation_cancel(o);
pa_operation_unref(o);
return -1;
}
}
pa_operation_unref(o);
if (p->dead && rerror)
*rerror = pa_context_errno(p->context);
return p->dead ? -1 : 0;
}
static void latency_complete(pa_stream *s, const pa_latency_info *l, void *userdata) {
pa_simple *p = userdata;
assert(s && p);
if (!l)
p->dead = 1;
else {
int negative = 0;
p->latency = pa_stream_get_latency(s, l, &negative);
if (negative)
p->latency = 0;
}
}
pa_usec_t pa_simple_get_playback_latency(pa_simple *p, int *rerror) {
pa_operation *o;
assert(p && p->direction == PA_STREAM_PLAYBACK);
if (p->dead) {
if (rerror)
*rerror = pa_context_errno(p->context);
return (pa_usec_t) -1;
}
p->latency = 0;
o = pa_stream_get_latency_info(p->stream, latency_complete, p);
while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
if (iterate(p, 1, rerror) < 0) {
pa_operation_cancel(o);
pa_operation_unref(o);
return -1;
}
}
pa_operation_unref(o);
if (p->dead && rerror)
*rerror = pa_context_errno(p->context);
return p->dead ? (pa_usec_t) -1 : p->latency;
}
int pa_simple_flush(pa_simple *p, int *rerror) {
pa_operation *o;
assert(p && p->direction == PA_STREAM_PLAYBACK);
if (p->dead) {
if (rerror)
*rerror = pa_context_errno(p->context);
return -1;
}
o = pa_stream_flush(p->stream, drain_or_flush_complete, p);
while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
if (iterate(p, 1, rerror) < 0) {
pa_operation_cancel(o);
pa_operation_unref(o);
return -1;
}
}
pa_operation_unref(o);
if (p->dead && rerror)
*rerror = pa_context_errno(p->context);
return p->dead ? -1 : 0;
}

View file

@ -0,0 +1,80 @@
#ifndef foopolyplibsimplehfoo
#define foopolyplibsimplehfoo
/* $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 "sample.h"
#include "polyplib-def.h"
#include <polyp/cdecl.h>
/** \file
* A simple but limited synchronous playback and recording
* API. This is synchronouse, simplified wrapper around the standard
* asynchronous API. */
/** \example pacat-simple.c
* A simple playback tool using the simple API */
/** \example parec-simple.c
* A simple recording tool using the simple API */
PA_C_DECL_BEGIN
/** \pa_simple
* An opaque simple connection object */
typedef struct pa_simple pa_simple;
/** Create a new connection to the server */
pa_simple* pa_simple_new(
const char *server, /**< Server name, or NULL for default */
const char *name, /**< A descriptive name for this client (application name, ...) */
pa_stream_direction_t dir, /**< Open this stream for recording or playback? */
const char *dev, /**< Sink (resp. source) name, or NULL for default */
const char *stream_name, /**< A descriptive name for this client (application name, song title, ...) */
const pa_sample_spec *ss, /**< The sample type to use */
const pa_buffer_attr *attr, /**< Buffering attributes, or NULL for default */
int *error /**< A pointer where the error code is stored when the routine returns NULL. It is OK to pass NULL here. */
);
/** Close and free the connection to the server. The connection objects becomes invalid when this is called. */
void pa_simple_free(pa_simple *s);
/** Write some data to the server */
int pa_simple_write(pa_simple *s, const void*data, size_t length, int *error);
/** Wait until all data already written is played by the daemon */
int pa_simple_drain(pa_simple *s, int *error);
/** Read some data from the server */
int pa_simple_read(pa_simple *s, void*data, size_t length, int *error);
/** Return the playback latency. \since 0.5 */
pa_usec_t pa_simple_get_playback_latency(pa_simple *s, int *error);
/** Flush the playback buffer. \since 0.5 */
int pa_simple_flush(pa_simple *s, int *error);
PA_C_DECL_END
#endif

807
src/polyp/polyplib-stream.c Normal file
View file

@ -0,0 +1,807 @@
/* $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 <string.h>
#include "polyplib-internal.h"
#include <polypcore/xmalloc.h>
#include <polypcore/pstream-util.h>
#include <polypcore/util.h>
#include <polypcore/log.h>
#define LATENCY_IPOL_INTERVAL_USEC (10000L)
pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) {
pa_stream *s;
assert(c);
assert(ss);
if (!pa_sample_spec_valid(ss))
return NULL;
if (map && !pa_channel_map_valid(map))
return NULL;
s = pa_xnew(pa_stream, 1);
s->ref = 1;
s->context = c;
s->mainloop = c->mainloop;
s->read_callback = NULL;
s->read_userdata = NULL;
s->write_callback = NULL;
s->write_userdata = NULL;
s->state_callback = NULL;
s->state_userdata = NULL;
s->direction = PA_STREAM_NODIRECTION;
s->name = pa_xstrdup(name);
s->sample_spec = *ss;
if (map)
s->channel_map = *map;
else
pa_channel_map_init_auto(&s->channel_map, ss->channels);
s->channel = 0;
s->channel_valid = 0;
s->device_index = PA_INVALID_INDEX;
s->requested_bytes = 0;
s->state = PA_STREAM_DISCONNECTED;
memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
s->mcalign = pa_mcalign_new(pa_frame_size(ss), c->memblock_stat);
s->counter = 0;
s->previous_time = 0;
s->previous_ipol_time = 0;
s->corked = 0;
s->interpolate = 0;
s->ipol_usec = 0;
memset(&s->ipol_timestamp, 0, sizeof(s->ipol_timestamp));
s->ipol_event = NULL;
s->ipol_requested = 0;
PA_LLIST_PREPEND(pa_stream, c->streams, s);
return pa_stream_ref(s);
}
static void stream_free(pa_stream *s) {
assert(s);
if (s->ipol_event) {
assert(s->mainloop);
s->mainloop->time_free(s->ipol_event);
}
pa_mcalign_free(s->mcalign);
pa_xfree(s->name);
pa_xfree(s);
}
void pa_stream_unref(pa_stream *s) {
assert(s && s->ref >= 1);
if (--(s->ref) == 0)
stream_free(s);
}
pa_stream* pa_stream_ref(pa_stream *s) {
assert(s && s->ref >= 1);
s->ref++;
return s;
}
pa_stream_state_t pa_stream_get_state(pa_stream *s) {
assert(s && s->ref >= 1);
return s->state;
}
pa_context* pa_stream_get_context(pa_stream *s) {
assert(s && s->ref >= 1);
return s->context;
}
uint32_t pa_stream_get_index(pa_stream *s) {
assert(s && s->ref >= 1);
return s->device_index;
}
void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) {
assert(s && s->ref >= 1);
if (s->state == st)
return;
pa_stream_ref(s);
s->state = st;
if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED) && s->context) {
if (s->channel_valid)
pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
PA_LLIST_REMOVE(pa_stream, s->context->streams, s);
pa_stream_unref(s);
}
if (s->state_callback)
s->state_callback(s, s->state_userdata);
pa_stream_unref(s);
}
void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_context *c = userdata;
pa_stream *s;
uint32_t channel;
assert(pd && (command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED) && t && c);
pa_context_ref(c);
if (pa_tagstruct_getu32(t, &channel) < 0 ||
!pa_tagstruct_eof(t)) {
pa_context_fail(c, PA_ERROR_PROTOCOL);
goto finish;
}
if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel)))
goto finish;
c->error = PA_ERROR_KILLED;
pa_stream_set_state(s, PA_STREAM_FAILED);
finish:
pa_context_unref(c);
}
void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_stream *s;
pa_context *c = userdata;
uint32_t bytes, channel;
assert(pd && command == PA_COMMAND_REQUEST && t && c);
pa_context_ref(c);
if (pa_tagstruct_getu32(t, &channel) < 0 ||
pa_tagstruct_getu32(t, &bytes) < 0 ||
!pa_tagstruct_eof(t)) {
pa_context_fail(c, PA_ERROR_PROTOCOL);
goto finish;
}
if (!(s = pa_dynarray_get(c->playback_streams, channel)))
goto finish;
if (s->state != PA_STREAM_READY)
goto finish;
pa_stream_ref(s);
s->requested_bytes += bytes;
if (s->requested_bytes && s->write_callback)
s->write_callback(s, s->requested_bytes, s->write_userdata);
pa_stream_unref(s);
finish:
pa_context_unref(c);
}
static void ipol_callback(pa_mainloop_api *m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
struct timeval tv2;
pa_stream *s = userdata;
pa_stream_ref(s);
/* pa_log("requesting new ipol data\n"); */
if (s->state == PA_STREAM_READY && !s->ipol_requested) {
pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
s->ipol_requested = 1;
}
pa_gettimeofday(&tv2);
pa_timeval_add(&tv2, LATENCY_IPOL_INTERVAL_USEC);
m->time_restart(e, &tv2);
pa_stream_unref(s);
}
void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_stream *s = userdata;
assert(pd && s && s->state == PA_STREAM_CREATING);
pa_stream_ref(s);
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(s->context, command, t) < 0)
goto finish;
pa_stream_set_state(s, PA_STREAM_FAILED);
goto finish;
}
if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->device_index) < 0) ||
((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0) ||
!pa_tagstruct_eof(t)) {
pa_context_fail(s->context, PA_ERROR_PROTOCOL);
goto finish;
}
s->channel_valid = 1;
pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s);
pa_stream_set_state(s, PA_STREAM_READY);
if (s->interpolate) {
struct timeval tv;
pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
pa_gettimeofday(&tv);
tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */
assert(!s->ipol_event);
s->ipol_event = s->mainloop->time_new(s->mainloop, &tv, &ipol_callback, s);
}
if (s->requested_bytes && s->ref > 1 && s->write_callback)
s->write_callback(s, s->requested_bytes, s->write_userdata);
finish:
pa_stream_unref(s);
}
static void create_stream(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags, const pa_cvolume *volume) {
pa_tagstruct *t;
uint32_t tag;
assert(s && s->ref >= 1 && s->state == PA_STREAM_DISCONNECTED);
pa_stream_ref(s);
s->interpolate = !!(flags & PA_STREAM_INTERPOLATE_LATENCY);
pa_stream_trash_ipol(s);
if (attr)
s->buffer_attr = *attr;
else {
/* half a second */
s->buffer_attr.tlength = pa_bytes_per_second(&s->sample_spec)/2;
s->buffer_attr.maxlength = (s->buffer_attr.tlength*3)/2;
s->buffer_attr.minreq = s->buffer_attr.tlength/100;
s->buffer_attr.prebuf = s->buffer_attr.tlength - s->buffer_attr.minreq;
s->buffer_attr.fragsize = s->buffer_attr.tlength/100;
}
pa_stream_set_state(s, PA_STREAM_CREATING);
t = pa_tagstruct_new(NULL, 0);
assert(t);
if (!dev) {
if (s->direction == PA_STREAM_PLAYBACK)
dev = s->context->conf->default_sink;
else
dev = s->context->conf->default_source;
}
pa_tagstruct_put(t,
PA_TAG_U32, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM,
PA_TAG_U32, tag = s->context->ctag++,
PA_TAG_STRING, s->name,
PA_TAG_SAMPLE_SPEC, &s->sample_spec,
PA_TAG_CHANNEL_MAP, &s->channel_map,
PA_TAG_U32, PA_INVALID_INDEX,
PA_TAG_STRING, dev,
PA_TAG_U32, s->buffer_attr.maxlength,
PA_TAG_BOOLEAN, !!(flags & PA_STREAM_START_CORKED),
PA_TAG_INVALID);
if (s->direction == PA_STREAM_PLAYBACK) {
pa_cvolume cv;
pa_tagstruct_put(t,
PA_TAG_U32, s->buffer_attr.tlength,
PA_TAG_U32, s->buffer_attr.prebuf,
PA_TAG_U32, s->buffer_attr.minreq,
PA_TAG_INVALID);
if (!volume) {
pa_cvolume_reset(&cv, s->sample_spec.channels);
volume = &cv;
}
pa_tagstruct_put_cvolume(t, volume);
} else
pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s);
pa_stream_unref(s);
}
void pa_stream_connect_playback(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags, pa_cvolume *volume) {
assert(s && s->context->state == PA_CONTEXT_READY && s->ref >= 1);
s->direction = PA_STREAM_PLAYBACK;
create_stream(s, dev, attr, flags, volume);
}
void pa_stream_connect_record(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags) {
assert(s && s->context->state == PA_CONTEXT_READY && s->ref >= 1);
s->direction = PA_STREAM_RECORD;
create_stream(s, dev, attr, flags, 0);
}
void pa_stream_write(pa_stream *s, const void *data, size_t length, void (*free_cb)(void *p), size_t delta) {
pa_memchunk chunk;
assert(s && s->context && data && length && s->state == PA_STREAM_READY && s->ref >= 1);
if (free_cb) {
chunk.memblock = pa_memblock_new_user((void*) data, length, free_cb, 1, s->context->memblock_stat);
assert(chunk.memblock && chunk.memblock->data);
} else {
chunk.memblock = pa_memblock_new(length, s->context->memblock_stat);
assert(chunk.memblock && chunk.memblock->data);
memcpy(chunk.memblock->data, data, length);
}
chunk.index = 0;
chunk.length = length;
pa_pstream_send_memblock(s->context->pstream, s->channel, delta, &chunk);
pa_memblock_unref(chunk.memblock);
if (length < s->requested_bytes)
s->requested_bytes -= length;
else
s->requested_bytes = 0;
s->counter += length;
}
size_t pa_stream_writable_size(pa_stream *s) {
assert(s && s->ref >= 1);
return s->state == PA_STREAM_READY ? s->requested_bytes : 0;
}
pa_operation * pa_stream_drain(pa_stream *s, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
assert(s && s->ref >= 1 && s->state == PA_STREAM_READY);
o = pa_operation_new(s->context, s);
assert(o);
o->callback = (pa_operation_callback) cb;
o->userdata = userdata;
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_DRAIN_PLAYBACK_STREAM);
pa_tagstruct_putu32(t, tag = s->context->ctag++);
pa_tagstruct_putu32(t, s->channel);
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o);
return pa_operation_ref(o);
}
static void stream_get_latency_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
pa_latency_info i, *p = NULL;
struct timeval local, remote, now;
assert(pd && o && o->stream && o->context);
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
goto finish;
} else if (pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
pa_tagstruct_get_usec(t, &i.sink_usec) < 0 ||
pa_tagstruct_get_usec(t, &i.source_usec) < 0 ||
pa_tagstruct_get_boolean(t, &i.playing) < 0 ||
pa_tagstruct_getu32(t, &i.queue_length) < 0 ||
pa_tagstruct_get_timeval(t, &local) < 0 ||
pa_tagstruct_get_timeval(t, &remote) < 0 ||
pa_tagstruct_getu64(t, &i.counter) < 0 ||
!pa_tagstruct_eof(t)) {
pa_context_fail(o->context, PA_ERROR_PROTOCOL);
goto finish;
} else {
pa_gettimeofday(&now);
if (pa_timeval_cmp(&local, &remote) <= 0 && pa_timeval_cmp(&remote, &now) <= 0) {
/* local and remote seem to have synchronized clocks */
if (o->stream->direction == PA_STREAM_PLAYBACK)
i.transport_usec = pa_timeval_diff(&remote, &local);
else
i.transport_usec = pa_timeval_diff(&now, &remote);
i.synchronized_clocks = 1;
i.timestamp = remote;
} else {
/* clocks are not synchronized, let's estimate latency then */
i.transport_usec = pa_timeval_diff(&now, &local)/2;
i.synchronized_clocks = 0;
i.timestamp = local;
pa_timeval_add(&i.timestamp, i.transport_usec);
}
if (o->stream->interpolate) {
/* pa_log("new interpol data\n"); */
o->stream->ipol_timestamp = i.timestamp;
o->stream->ipol_usec = pa_stream_get_time(o->stream, &i);
o->stream->ipol_requested = 0;
}
p = &i;
}
if (o->callback) {
void (*cb)(pa_stream *s, const pa_latency_info *_i, void *_userdata) = (void (*)(pa_stream *s, const pa_latency_info *_i, void *_userdata)) o->callback;
cb(o->stream, p, o->userdata);
}
finish:
pa_operation_done(o);
pa_operation_unref(o);
}
pa_operation* pa_stream_get_latency_info(pa_stream *s, void (*cb)(pa_stream *p, const pa_latency_info*i, void *userdata), void *userdata) {
uint32_t tag;
pa_operation *o;
pa_tagstruct *t;
struct timeval now;
assert(s && s->direction != PA_STREAM_UPLOAD);
o = pa_operation_new(s->context, s);
assert(o);
o->callback = (pa_operation_callback) cb;
o->userdata = userdata;
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY);
pa_tagstruct_putu32(t, tag = s->context->ctag++);
pa_tagstruct_putu32(t, s->channel);
pa_gettimeofday(&now);
pa_tagstruct_put_timeval(t, &now);
pa_tagstruct_putu64(t, s->counter);
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_info_callback, o);
return pa_operation_ref(o);
}
void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_stream *s = userdata;
assert(pd && s && s->ref >= 1);
pa_stream_ref(s);
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(s->context, command, t) < 0)
goto finish;
pa_stream_set_state(s, PA_STREAM_FAILED);
goto finish;
} else if (!pa_tagstruct_eof(t)) {
pa_context_fail(s->context, PA_ERROR_PROTOCOL);
goto finish;
}
pa_stream_set_state(s, PA_STREAM_TERMINATED);
finish:
pa_stream_unref(s);
}
void pa_stream_disconnect(pa_stream *s) {
pa_tagstruct *t;
uint32_t tag;
assert(s && s->ref >= 1);
if (!s->channel_valid || !s->context->state == PA_CONTEXT_READY)
return;
pa_stream_ref(s);
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_DELETE_PLAYBACK_STREAM :
(s->direction == PA_STREAM_RECORD ? PA_COMMAND_DELETE_RECORD_STREAM : PA_COMMAND_DELETE_UPLOAD_STREAM));
pa_tagstruct_putu32(t, tag = s->context->ctag++);
pa_tagstruct_putu32(t, s->channel);
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s);
pa_stream_unref(s);
}
void pa_stream_set_read_callback(pa_stream *s, void (*cb)(pa_stream *p, const void*data, size_t length, void *userdata), void *userdata) {
assert(s && s->ref >= 1);
s->read_callback = cb;
s->read_userdata = userdata;
}
void pa_stream_set_write_callback(pa_stream *s, void (*cb)(pa_stream *p, size_t length, void *userdata), void *userdata) {
assert(s && s->ref >= 1);
s->write_callback = cb;
s->write_userdata = userdata;
}
void pa_stream_set_state_callback(pa_stream *s, void (*cb)(pa_stream *s, void *userdata), void *userdata) {
assert(s && s->ref >= 1);
s->state_callback = cb;
s->state_userdata = userdata;
}
void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
int success = 1;
assert(pd && o && o->context && o->ref >= 1);
if (command != PA_COMMAND_REPLY) {
if (pa_context_handle_error(o->context, command, t) < 0)
goto finish;
success = 0;
} else if (!pa_tagstruct_eof(t)) {
pa_context_fail(o->context, PA_ERROR_PROTOCOL);
goto finish;
}
if (o->callback) {
void (*cb)(pa_stream *s, int _success, void *_userdata) = (void (*)(pa_stream *s, int _success, void *_userdata)) o->callback;
cb(o->stream, success, o->userdata);
}
finish:
pa_operation_done(o);
pa_operation_unref(o);
}
pa_operation* pa_stream_cork(pa_stream *s, int b, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
assert(s && s->ref >= 1 && s->state == PA_STREAM_READY);
if (s->interpolate) {
if (!s->corked && b)
/* Pausing */
s->ipol_usec = pa_stream_get_interpolated_time(s);
else if (s->corked && !b)
/* Unpausing */
pa_gettimeofday(&s->ipol_timestamp);
}
s->corked = b;
o = pa_operation_new(s->context, s);
assert(o);
o->callback = (pa_operation_callback) cb;
o->userdata = userdata;
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CORK_PLAYBACK_STREAM : PA_COMMAND_CORK_RECORD_STREAM);
pa_tagstruct_putu32(t, tag = s->context->ctag++);
pa_tagstruct_putu32(t, s->channel);
pa_tagstruct_put_boolean(t, !!b);
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o);
pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
return pa_operation_ref(o);
}
static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) {
pa_tagstruct *t;
pa_operation *o;
uint32_t tag;
assert(s && s->ref >= 1 && s->state == PA_STREAM_READY);
o = pa_operation_new(s->context, s);
o->callback = (pa_operation_callback) cb;
o->userdata = userdata;
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, command);
pa_tagstruct_putu32(t, tag = s->context->ctag++);
pa_tagstruct_putu32(t, s->channel);
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o);
return pa_operation_ref(o);
}
pa_operation* pa_stream_flush(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) {
pa_operation *o;
o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata);
pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
return o;
}
pa_operation* pa_stream_prebuf(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) {
pa_operation *o;
o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata);
pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
return o;
}
pa_operation* pa_stream_trigger(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) {
pa_operation *o;
o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata);
pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
return o;
}
pa_operation* pa_stream_set_name(pa_stream *s, const char *name, void(*cb)(pa_stream*c, int success, void *userdata), void *userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
assert(s && s->ref >= 1 && s->state == PA_STREAM_READY && name && s->direction != PA_STREAM_UPLOAD);
o = pa_operation_new(s->context, s);
assert(o);
o->callback = (pa_operation_callback) cb;
o->userdata = userdata;
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME);
pa_tagstruct_putu32(t, tag = s->context->ctag++);
pa_tagstruct_putu32(t, s->channel);
pa_tagstruct_puts(t, name);
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o);
return pa_operation_ref(o);
}
uint64_t pa_stream_get_counter(pa_stream *s) {
assert(s);
return s->counter;
}
pa_usec_t pa_stream_get_time(pa_stream *s, const pa_latency_info *i) {
pa_usec_t usec;
assert(s);
usec = pa_bytes_to_usec(i->counter, &s->sample_spec);
if (i) {
if (s->direction == PA_STREAM_PLAYBACK) {
pa_usec_t latency = i->transport_usec + i->buffer_usec + i->sink_usec;
if (usec < latency)
usec = 0;
else
usec -= latency;
} else if (s->direction == PA_STREAM_RECORD) {
usec += i->source_usec + i->buffer_usec + i->transport_usec;
if (usec > i->sink_usec)
usec -= i->sink_usec;
else
usec = 0;
}
}
if (usec < s->previous_time)
usec = s->previous_time;
s->previous_time = usec;
return usec;
}
static pa_usec_t time_counter_diff(pa_stream *s, pa_usec_t t, pa_usec_t c, int *negative) {
assert(s);
if (negative)
*negative = 0;
if (c < t) {
if (s->direction == PA_STREAM_RECORD) {
if (negative)
*negative = 1;
return t-c;
} else
return 0;
} else
return c-t;
}
pa_usec_t pa_stream_get_latency(pa_stream *s, const pa_latency_info *i, int *negative) {
pa_usec_t t, c;
assert(s && i);
t = pa_stream_get_time(s, i);
c = pa_bytes_to_usec(s->counter, &s->sample_spec);
return time_counter_diff(s, t, c, negative);
}
const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) {
assert(s);
return &s->sample_spec;
}
void pa_stream_trash_ipol(pa_stream *s) {
assert(s);
if (!s->interpolate)
return;
memset(&s->ipol_timestamp, 0, sizeof(s->ipol_timestamp));
s->ipol_usec = 0;
}
pa_usec_t pa_stream_get_interpolated_time(pa_stream *s) {
pa_usec_t usec;
assert(s && s->interpolate);
if (s->corked)
usec = s->ipol_usec;
else {
if (s->ipol_timestamp.tv_sec == 0)
usec = 0;
else
usec = s->ipol_usec + pa_timeval_age(&s->ipol_timestamp);
}
if (usec < s->previous_ipol_time)
usec = s->previous_ipol_time;
s->previous_ipol_time = usec;
return usec;
}
pa_usec_t pa_stream_get_interpolated_latency(pa_stream *s, int *negative) {
pa_usec_t t, c;
assert(s && s->interpolate);
t = pa_stream_get_interpolated_time(s);
c = pa_bytes_to_usec(s->counter, &s->sample_spec);
return time_counter_diff(s, t, c, negative);
}

181
src/polyp/polyplib-stream.h Normal file
View file

@ -0,0 +1,181 @@
#ifndef foopolyplibstreamhfoo
#define foopolyplibstreamhfoo
/* $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 <polyp/sample.h>
#include <polyp/channelmap.h>
#include <polyp/volume.h>
#include <polyp/polyplib-def.h>
#include <polyp/cdecl.h>
#include <polyp/polyplib-operation.h>
/** \file
* Audio streams for input, output and sample upload */
PA_C_DECL_BEGIN
/** \pa_stream
* An opaque stream for playback or recording */
typedef struct pa_stream pa_stream;
/** Create a new, unconnected stream with the specified name and sample type */
pa_stream* pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map);
/** Decrease the reference counter by one */
void pa_stream_unref(pa_stream *s);
/** Increase the reference counter by one */
pa_stream *pa_stream_ref(pa_stream *s);
/** Return the current state of the stream */
pa_stream_state_t pa_stream_get_state(pa_stream *p);
/** Return the context this stream is attached to */
pa_context* pa_stream_get_context(pa_stream *p);
/** Return the device (sink input or source output) index this stream is connected to */
uint32_t pa_stream_get_index(pa_stream *s);
/** Connect the stream to a sink */
void pa_stream_connect_playback(
pa_stream *s,
const char *dev,
const pa_buffer_attr *attr,
pa_stream_flags_t flags,
pa_cvolume *volume);
/** Connect the stream to a source */
void pa_stream_connect_record(
pa_stream *s,
const char *dev,
const pa_buffer_attr *attr,
pa_stream_flags_t flags);
/** Disconnect a stream from a source/sink */
void pa_stream_disconnect(pa_stream *s);
/** Write some data to the server (for playback sinks), if free_cb is
* non-NULL this routine is called when all data has been written out
* and an internal reference to the specified data is kept, the data
* is not copied. If NULL, the data is copied into an internal
* buffer. */
void pa_stream_write(pa_stream *p /**< The stream to use */,
const void *data /**< The data to write */,
size_t length /**< The length of the data to write */,
void (*free_cb)(void *p) /**< A cleanup routine for the data or NULL to request an internal copy */,
size_t delta /**< Drop this many
bytes in the playback
buffer before writing
this data. Use
(size_t) -1 for
clearing the whole
playback
buffer. Normally you
will specify 0 here,
i.e. append to the
playback buffer. If
the value given here
is greater than the
buffered data length
the buffer is cleared
and the data is
written to the
buffer's start. This
value is ignored on
upload streams. */);
/** Return the amount of bytes that may be written using pa_stream_write() */
size_t pa_stream_writable_size(pa_stream *p);
/** Drain a playback stream */
pa_operation* pa_stream_drain(pa_stream *s, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata);
/** Get the playback latency of a stream */
pa_operation* pa_stream_get_latency_info(pa_stream *p, void (*cb)(pa_stream *p, const pa_latency_info *i, void *userdata), void *userdata);
/** Set the callback function that is called whenever the state of the stream changes */
void pa_stream_set_state_callback(pa_stream *s, void (*cb)(pa_stream *s, void *userdata), void *userdata);
/** Set the callback function that is called when new data may be
* written to the stream. */
void pa_stream_set_write_callback(pa_stream *p, void (*cb)(pa_stream *p, size_t length, void *userdata), void *userdata);
/** Set the callback function that is called when new data is available from the stream */
void pa_stream_set_read_callback(pa_stream *p, void (*cb)(pa_stream *p, const void*data, size_t length, void *userdata), void *userdata);
/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. \since 0.3 */
pa_operation* pa_stream_cork(pa_stream *s, int b, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata);
/** Flush the playback buffer of this stream. Most of the time you're
* better off using the parameter delta of pa_stream_write() instead of this
* function. Available on both playback and recording streams. \since 0.3 */
pa_operation* pa_stream_flush(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata);
/** Reenable prebuffering. Available for playback streams only. \since 0.6 */
pa_operation* pa_stream_prebuf(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata);
/** Request immediate start of playback on this stream. This disables
* prebuffering as specified in the pa_buffer_attr structure. Available for playback streams only. \since
* 0.3 */
pa_operation* pa_stream_trigger(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata);
/** Rename the stream. \since 0.5 */
pa_operation* pa_stream_set_name(pa_stream *s, const char *name, void(*cb)(pa_stream*c, int success, void *userdata), void *userdata);
/** Return the total number of bytes written to/read from the
* stream. This counter is not reset on pa_stream_flush(), you may do
* this yourself using pa_stream_reset_counter(). \since 0.6 */
uint64_t pa_stream_get_counter(pa_stream *s);
/** Return the current playback/recording time. This is based on the
* counter accessible with pa_stream_get_counter(). This function
* requires a pa_latency_info structure as argument, which should be
* acquired using pa_stream_get_latency(). \since 0.6 */
pa_usec_t pa_stream_get_time(pa_stream *s, const pa_latency_info *i);
/** Return the total stream latency. Thus function requires a
* pa_latency_info structure as argument, which should be aquired
* using pa_stream_get_latency(). In case the stream is a monitoring
* stream the result can be negative, i.e. the captured samples are
* not yet played. In this case *negative is set to 1. \since 0.6 */
pa_usec_t pa_stream_get_latency(pa_stream *s, const pa_latency_info *i, int *negative);
/** Return the interpolated playback/recording time. Requires the
* PA_STREAM_INTERPOLATE_LATENCY bit set when creating the stream. In
* contrast to pa_stream_get_latency() this function doesn't require
* a whole roundtrip for response. \since 0.6 */
pa_usec_t pa_stream_get_interpolated_time(pa_stream *s);
/** Return the interpolated playback/recording latency. Requires the
* PA_STREAM_INTERPOLATE_LATENCY bit set when creating the
* stream. \since 0.6 */
pa_usec_t pa_stream_get_interpolated_latency(pa_stream *s, int *negative);
/** Return a pointer to the streams sample specification. \since 0.6 */
const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s);
PA_C_DECL_END
#endif

View file

@ -0,0 +1,81 @@
/* $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 <stdio.h>
#include "polyplib-subscribe.h"
#include "polyplib-internal.h"
#include <polypcore/pstream-util.h>
#include <polypcore/gccmacro.h>
void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_context *c = userdata;
pa_subscription_event_type_t e;
uint32_t index;
assert(pd && command == PA_COMMAND_SUBSCRIBE_EVENT && t && c);
pa_context_ref(c);
if (pa_tagstruct_getu32(t, &e) < 0 ||
pa_tagstruct_getu32(t, &index) < 0 ||
!pa_tagstruct_eof(t)) {
pa_context_fail(c, PA_ERROR_PROTOCOL);
goto finish;
}
if (c->subscribe_callback)
c->subscribe_callback(c, e, index, c->subscribe_userdata);
finish:
pa_context_unref(c);
}
pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) {
pa_operation *o;
pa_tagstruct *t;
uint32_t tag;
assert(c);
o = pa_operation_new(c, NULL);
o->callback = (pa_operation_callback) cb;
o->userdata = userdata;
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE);
pa_tagstruct_putu32(t, tag = c->ctag++);
pa_tagstruct_putu32(t, m);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
return pa_operation_ref(o);
}
void pa_context_set_subscribe_callback(pa_context *c, void (*cb)(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata) {
assert(c);
c->subscribe_callback = cb;
c->subscribe_userdata = userdata;
}

View file

@ -0,0 +1,47 @@
#ifndef foopolyplibsubscribehfoo
#define foopolyplibsubscribehfoo
/* $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/polyplib-def.h>
#include <polyp/polyplib-context.h>
#include <polyp/cdecl.h>
/** \file
* Daemon introspection event subscription subsystem. Use this
* to be notified whenever the internal layout of daemon changes:
* i.e. entities such as sinks or sources are create, removed or
* modified. */
PA_C_DECL_BEGIN
/** Enable event notification */
pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
/** Set the context specific call back function that is called whenever the state of the daemon changes */
void pa_context_set_subscribe_callback(pa_context *c, void (*cb)(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata);
PA_C_DECL_END
#endif

View file

@ -0,0 +1,47 @@
#ifndef foopolyplibversionhfoo /*-*-C-*-*/
#define foopolyplibversionhfoo
/* $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.
***/
/* WARNING: Make sure to edit the real source file polyplib-version.h.in! */
/** \file
* Define header version */
PA_C_DECL_BEGIN
/** Return the version of the header files. Keep in mind that this is
a macro and not a function, so it is impossible to get the pointer of
it. */
#define pa_get_headers_version() ("@PACKAGE_VERSION@")
/** Return the version of the library the current application is linked to. */
const char* pa_get_library_version(void);
/** The current API version. Version 6 relates to polypaudio
* 0.6. Prior versions (i.e. Polypaudio 0.5.1 and older) have
* PA_API_VERSION undefined. */
#define PA_API_VERSION @PA_API_VERSION@
PA_C_DECL_END
#endif

86
src/polyp/polyplib.h Normal file
View file

@ -0,0 +1,86 @@
#ifndef foopolyplibhfoo
#define foopolyplibhfoo
/* $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/mainloop-api.h>
#include <polyp/sample.h>
#include <polyp/polyplib-def.h>
#include <polyp/polyplib-context.h>
#include <polyp/polyplib-stream.h>
#include <polyp/polyplib-introspect.h>
#include <polyp/polyplib-subscribe.h>
#include <polyp/polyplib-scache.h>
#include <polyp/polyplib-version.h>
/** \file
* Include all polyplib header file at once. The following files are included: \ref mainloop-api.h, \ref sample.h,
* \ref polyplib-def.h, \ref polyplib-context.h, \ref polyplib-stream.h,
* \ref polyplib-introspect.h, \ref polyplib-subscribe.h and \ref polyplib-scache.h \ref polyplib-version.h
* at once */
/** \mainpage
*
* \section intro_sec Introduction
*
* This document describes the client API for the polypaudio sound
* server. The API comes in two flavours:
*
* \li The complete but somewhat complicated to use asynchronous API
* \li And the simplified, easy to use, but limited synchronous API
*
* The polypaudio client libraries are thread safe as long as all
* objects created by any library function are accessed from the thread
* that created them only.
*
* \section simple_sec Simple API
*
* Use this if you develop your program in synchronous style and just
* need a way to play or record data on the sound server. See
* \ref polyplib-simple.h for more details.
*
* \section async_api Asynchronous API
*
* Use this if you develop your programs in asynchronous, main loop
* based style or want to use advanced features of the polypaudio
* API. A good starting point is \ref polyplib-context.h
*
* The asynchronous API relies on an abstract main loop API that is
* described in \ref mainloop-api.h. Two distinct implementations are
* available:
*
* \li \ref mainloop.h: a minimal but fast implementation based on poll()
* \li \ref glib-mainloop.h: a wrapper around GLIB's main loop
*
* UNIX signals may be hooked to a main loop using the functions from
* \ref mainloop-signal.h
*
* \section pkgconfig pkg-config
*
* The polypaudio libraries provide pkg-config snippets for the different modules. To use the
* asynchronous API use "polyplib" as pkg-config file. GLIB main loop
* support is available as "polyplib-glib-mainloop". The simple
* synchronous API is available as "polyplib-simple".
*/
#endif

149
src/polyp/sample.c Normal file
View file

@ -0,0 +1,149 @@
/* $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 <math.h>
#include <string.h>
#include "sample.h"
size_t pa_sample_size(const pa_sample_spec *spec) {
assert(spec);
switch (spec->format) {
case PA_SAMPLE_U8:
case PA_SAMPLE_ULAW:
case PA_SAMPLE_ALAW:
return 1;
case PA_SAMPLE_S16LE:
case PA_SAMPLE_S16BE:
return 2;
case PA_SAMPLE_FLOAT32LE:
case PA_SAMPLE_FLOAT32BE:
return 4;
default:
assert(0);
}
}
size_t pa_frame_size(const pa_sample_spec *spec) {
assert(spec);
return pa_sample_size(spec) * spec->channels;
}
size_t pa_bytes_per_second(const pa_sample_spec *spec) {
assert(spec);
return spec->rate*pa_frame_size(spec);
}
pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) {
assert(spec);
return (pa_usec_t) (((double) length/pa_frame_size(spec)*1000000)/spec->rate);
}
int pa_sample_spec_valid(const pa_sample_spec *spec) {
assert(spec);
if (spec->rate <= 0 ||
spec->channels <= 0 ||
spec->channels > PA_CHANNELS_MAX ||
spec->format >= PA_SAMPLE_MAX ||
spec->format < 0)
return 0;
return 1;
}
int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) {
assert(a && b);
return (a->format == b->format) && (a->rate == b->rate) && (a->channels == b->channels);
}
const char *pa_sample_format_to_string(pa_sample_format_t f) {
static const char* const table[]= {
[PA_SAMPLE_U8] = "u8",
[PA_SAMPLE_ALAW] = "aLaw",
[PA_SAMPLE_ULAW] = "uLaw",
[PA_SAMPLE_S16LE] = "s16le",
[PA_SAMPLE_S16BE] = "s16be",
[PA_SAMPLE_FLOAT32LE] = "float32le",
[PA_SAMPLE_FLOAT32BE] = "float32be",
};
if (f >= PA_SAMPLE_MAX)
return NULL;
return table[f];
}
char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) {
assert(s && l && spec);
if (!pa_sample_spec_valid(spec))
snprintf(s, l, "Invalid");
else
snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate);
return s;
}
void pa_bytes_snprint(char *s, size_t l, unsigned v) {
if (v >= ((unsigned) 1024)*1024*1024)
snprintf(s, l, "%0.1f GB", ((double) v)/1024/1024/1024);
else if (v >= ((unsigned) 1024)*1024)
snprintf(s, l, "%0.1f MB", ((double) v)/1024/1024);
else if (v >= (unsigned) 1024)
snprintf(s, l, "%0.1f KB", ((double) v)/1024);
else
snprintf(s, l, "%u B", (unsigned) v);
}
pa_sample_format_t pa_parse_sample_format(const char *format) {
if (strcasecmp(format, "s16le") == 0)
return PA_SAMPLE_S16LE;
else if (strcasecmp(format, "s16be") == 0)
return PA_SAMPLE_S16BE;
else if (strcasecmp(format, "s16ne") == 0 || strcasecmp(format, "s16") == 0 || strcasecmp(format, "16") == 0)
return PA_SAMPLE_S16NE;
else if (strcasecmp(format, "u8") == 0 || strcasecmp(format, "8") == 0)
return PA_SAMPLE_U8;
else if (strcasecmp(format, "float32") == 0 || strcasecmp(format, "float32ne") == 0)
return PA_SAMPLE_FLOAT32;
else if (strcasecmp(format, "float32le") == 0)
return PA_SAMPLE_FLOAT32LE;
else if (strcasecmp(format, "float32be") == 0)
return PA_SAMPLE_FLOAT32BE;
else if (strcasecmp(format, "ulaw") == 0)
return PA_SAMPLE_ULAW;
else if (strcasecmp(format, "alaw") == 0)
return PA_SAMPLE_ALAW;
return -1;
}

120
src/polyp/sample.h Normal file
View file

@ -0,0 +1,120 @@
#ifndef foosamplehfoo
#define foosamplehfoo
/* $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 <sys/types.h>
#include <math.h>
#include <polyp/cdecl.h>
/** \file
* Constants and routines for sample type handling */
PA_C_DECL_BEGIN
/* Maximum allowed channels */
#define PA_CHANNELS_MAX 16
/** Sample format */
typedef enum pa_sample_format {
PA_SAMPLE_U8, /**< Unsigned 8 Bit PCM */
PA_SAMPLE_ALAW, /**< 8 Bit a-Law */
PA_SAMPLE_ULAW, /**< 8 Bit mu-Law */
PA_SAMPLE_S16LE, /**< Signed 16 Bit PCM, little endian (PC) */
PA_SAMPLE_S16BE, /**< Signed 16 Bit PCM, big endian */
PA_SAMPLE_FLOAT32LE, /**< 32 Bit IEEE floating point, little endian, range -1..1 */
PA_SAMPLE_FLOAT32BE, /**< 32 Bit IEEE floating point, big endian, range -1..1 */
PA_SAMPLE_MAX, /**< Upper limit of valid sample types */
PA_SAMPLE_INVALID = -1 /**< An invalid value */
} pa_sample_format_t;
#ifdef WORDS_BIGENDIAN
/** Signed 16 Bit PCM, native endian */
#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE
/** 32 Bit IEEE floating point, native endian */
#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE
/** Signed 16 Bit PCM reverse endian */
#define PA_SAMPLE_S16RE PA_SAMPLE_S16LE
/** 32 Bit IEEE floating point, reverse endian */
#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32LE
#else
/** Signed 16 Bit PCM, native endian */
#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
/** 32 Bit IEEE floating point, native endian */
#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE
/** Signed 16 Bit PCM reverse endian */
#define PA_SAMPLE_S16RE PA_SAMPLE_S16BE
/** 32 Bit IEEE floating point, reverse endian */
#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32BE
#endif
/** A Shortcut for PA_SAMPLE_FLOAT32NE */
#define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32NE
/** A sample format and attribute specification */
typedef struct pa_sample_spec {
pa_sample_format_t format; /**< The sample format */
uint32_t rate; /**< The sample rate. (e.g. 44100) */
uint8_t channels; /**< Audio channels. (1 for mono, 2 for stereo, ...) */
} pa_sample_spec;
/** Type for usec specifications (unsigned). May be either 32 or 64 bit, depending on the architecture */
typedef uint64_t pa_usec_t;
/** Return the amount of bytes playback of a second of audio with the specified sample type takes */
size_t pa_bytes_per_second(const pa_sample_spec *spec);
/** Return the size of a frame with the specific sample type */
size_t pa_frame_size(const pa_sample_spec *spec);
/** Return the size of a sample with the specific sample type */
size_t pa_sample_size(const pa_sample_spec *spec);
/** Calculate the time the specified bytes take to play with the specified sample type */
pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec);
/** Return non-zero when the sample type specification is valid */
int pa_sample_spec_valid(const pa_sample_spec *spec);
/** Return non-zero when the two sample type specifications match */
int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b);
/* Return a descriptive string for the specified sample format. \since 0.8 */
const char *pa_sample_format_to_string(pa_sample_format_t f);
/** Parse a sample format text. Inverse of pa_sample_format_to_string() */
pa_sample_format_t pa_parse_sample_format(const char *format);
/** Maximum required string length for pa_sample_spec_snprint() */
#define PA_SAMPLE_SPEC_SNPRINT_MAX 32
/** Pretty print a sample type specification to a string */
char* pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec);
/** Pretty print a byte size value. (i.e. "2.5 MB") */
void pa_bytes_snprint(char *s, size_t l, unsigned v);
PA_C_DECL_END
#endif

176
src/polyp/volume.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 <stdio.h>
#include <string.h>
#include "volume.h"
int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) {
int i;
assert(a);
assert(b);
if (a->channels != b->channels)
return 0;
for (i = 0; i < a->channels; i++)
if (a->values[i] != b->values[i])
return 0;
return 1;
}
pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) {
int i;
assert(a);
assert(channels > 0);
assert(channels <= PA_CHANNELS_MAX);
a->channels = channels;
for (i = 0; i < a->channels; i++)
a->values[i] = v;
return a;
}
pa_volume_t pa_cvolume_avg(const pa_cvolume *a) {
uint64_t sum = 0;
int i;
assert(a);
for (i = 0; i < a->channels; i++)
sum += a->values[i];
sum /= a->channels;
return (pa_volume_t) sum;
}
pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) {
return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a)* pa_sw_volume_to_linear(b));
}
#define USER_DECIBEL_RANGE 30
pa_volume_t pa_sw_volume_from_dB(double dB) {
if (dB <= -USER_DECIBEL_RANGE)
return PA_VOLUME_MUTED;
return (pa_volume_t) ((dB/USER_DECIBEL_RANGE+1)*PA_VOLUME_NORM);
}
double pa_sw_volume_to_dB(pa_volume_t v) {
if (v == PA_VOLUME_MUTED)
return PA_DECIBEL_MININFTY;
return ((double) v/PA_VOLUME_NORM-1)*USER_DECIBEL_RANGE;
}
pa_volume_t pa_sw_volume_from_linear(double v) {
if (v <= 0)
return PA_VOLUME_MUTED;
if (v > .999 && v < 1.001)
return PA_VOLUME_NORM;
return pa_sw_volume_from_dB(20*log10(v));
}
double pa_sw_volume_to_linear(pa_volume_t v) {
if (v == PA_VOLUME_MUTED)
return 0;
return pow(10, pa_sw_volume_to_dB(v)/20);
}
char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) {
unsigned channel;
int first = 1;
char *e;
assert(s);
assert(l > 0);
assert(c);
*(e = s) = 0;
for (channel = 0; channel < c->channels && l > 1; channel++) {
l -= snprintf(e, l, "%s%u: %3u%%",
first ? "" : " ",
channel,
(c->values[channel]*100)/PA_VOLUME_NORM);
e = strchr(e, 0);
first = 0;
}
return s;
}
/** Return non-zero if the volume of all channels is equal to the specified value */
int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) {
unsigned c;
assert(a);
for (c = 0; c < a->channels; c++)
if (a->values[c] != v)
return 0;
return 1;
}
pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) {
unsigned i;
assert(dest);
assert(a);
assert(b);
for (i = 0; i < a->channels || i < b->channels || i < PA_CHANNELS_MAX; i++) {
dest->values[i] = pa_sw_volume_multiply(
i < a->channels ? a->values[i] : PA_VOLUME_NORM,
i < b->channels ? b->values[i] : PA_VOLUME_NORM);
}
dest->channels = i;
return dest;
}
int pa_cvolume_valid(const pa_cvolume *v) {
assert(v);
if (v->channels <= 0 || v->channels > PA_CHANNELS_MAX)
return 0;
return 1;
}

107
src/polyp/volume.h Normal file
View file

@ -0,0 +1,107 @@
#ifndef foovolumehfoo
#define foovolumehfoo
/* $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/cdecl.h>
#include <polyp/sample.h>
/** \file
* Constants and routines for volume handling */
PA_C_DECL_BEGIN
/** Volume specification:
* PA_VOLUME_MUTED: silence;
* < PA_VOLUME_NORM: decreased volume;
* PA_VOLUME_NORM: normal volume;
* > PA_VOLUME_NORM: increased volume */
typedef uint32_t pa_volume_t;
/** Normal volume (100%) */
#define PA_VOLUME_NORM (0x10000)
/** Muted volume (0%) */
#define PA_VOLUME_MUTED (0)
/** A structure encapsulating a per-channel volume */
typedef struct pa_cvolume {
uint8_t channels;
pa_volume_t values[PA_CHANNELS_MAX];
} pa_cvolume;
/** Return non-zero when *a == *b */
int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b);
/** Set the volume of all channels to PA_VOLUME_NORM */
#define pa_cvolume_reset(a, n) pa_cvolume_set((a), (n), PA_VOLUME_NORM)
/** Set the volume of all channels to PA_VOLUME_MUTED */
#define pa_cvolume_mute(a, n) pa_cvolume_set((a), (n), PA_VOLUME_MUTED)
/** Set the volume of all channels to the specified parameter */
pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v);
/** Pretty print a volume structure */
#define PA_CVOLUME_SNPRINT_MAX 64
char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c);
/** Return the average volume of all channels */
pa_volume_t pa_cvolume_avg(const pa_cvolume *a);
/** Return TRUE when the passed cvolume structure is valid, FALSE otherwise */
int pa_cvolume_valid(const pa_cvolume *v);
/** Return non-zero if the volume of all channels is equal to the specified value */
int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v);
#define pa_cvolume_is_muted(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_MUTED)
#define pa_cvolume_is_norm(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_NORM)
/** Multiply two volumes specifications, return the result. This uses PA_VOLUME_NORM as neutral element of multiplication. */
pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b);
pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b);
/** Convert a decibel value to a volume. \since 0.4 */
pa_volume_t pa_sw_volume_from_dB(double f);
/** Convert a volume to a decibel value. \since 0.4 */
double pa_sw_volume_to_dB(pa_volume_t v);
/** Convert a linear factor to a volume. \since 0.8 */
pa_volume_t pa_sw_volume_from_linear(double v);
/** Convert a volume to a linear factor. \since 0.8 */
double pa_sw_volume_to_linear(pa_volume_t v);
#ifdef INFINITY
#define PA_DECIBEL_MININFTY (-INFINITY)
#else
/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). \since 0.4 */
#define PA_DECIBEL_MININFTY (-200)
#endif
PA_C_DECL_END
#endif