mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-05 13:29:57 -05:00
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:
parent
5b881e6228
commit
e205b25d65
246 changed files with 724 additions and 689 deletions
42
src/polyp/cdecl.h
Normal file
42
src/polyp/cdecl.h
Normal 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
202
src/polyp/channelmap.c
Normal 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
98
src/polyp/channelmap.h
Normal 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
|
||||
91
src/polyp/client-conf-x11.c
Normal file
91
src/polyp/client-conf-x11.c
Normal 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;
|
||||
|
||||
}
|
||||
31
src/polyp/client-conf-x11.h
Normal file
31
src/polyp/client-conf-x11.h
Normal 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
191
src/polyp/client-conf.c
Normal 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
52
src/polyp/client-conf.h
Normal 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
538
src/polyp/glib-mainloop.c
Normal 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
55
src/polyp/glib-mainloop.h
Normal 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
500
src/polyp/glib12-mainloop.c
Normal 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
68
src/polyp/mainloop-api.c
Normal 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
120
src/polyp/mainloop-api.h
Normal 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
267
src/polyp/mainloop-signal.c
Normal 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;
|
||||
}
|
||||
60
src/polyp/mainloop-signal.h
Normal file
60
src/polyp/mainloop-signal.h
Normal 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
812
src/polyp/mainloop.c
Normal 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
90
src/polyp/mainloop.h
Normal 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
|
||||
312
src/polyp/polyplib-browser.c
Normal file
312
src/polyp/polyplib-browser.c
Normal 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;
|
||||
}
|
||||
65
src/polyp/polyplib-browser.h
Normal file
65
src/polyp/polyplib-browser.h
Normal 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
|
||||
871
src/polyp/polyplib-context.c
Normal file
871
src/polyp/polyplib-context.c
Normal 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;
|
||||
}
|
||||
117
src/polyp/polyplib-context.h
Normal file
117
src/polyp/polyplib-context.h
Normal 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
213
src/polyp/polyplib-def.h
Normal 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
|
||||
54
src/polyp/polyplib-error.c
Normal file
54
src/polyp/polyplib-error.c
Normal 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];
|
||||
}
|
||||
38
src/polyp/polyplib-error.h
Normal file
38
src/polyp/polyplib-error.h
Normal 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
|
||||
154
src/polyp/polyplib-internal.h
Normal file
154
src/polyp/polyplib-internal.h
Normal 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
|
||||
1003
src/polyp/polyplib-introspect.c
Normal file
1003
src/polyp/polyplib-introspect.c
Normal file
File diff suppressed because it is too large
Load diff
279
src/polyp/polyplib-introspect.h
Normal file
279
src/polyp/polyplib-introspect.h
Normal 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
|
||||
103
src/polyp/polyplib-operation.c
Normal file
103
src/polyp/polyplib-operation.c
Normal 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;
|
||||
}
|
||||
51
src/polyp/polyplib-operation.h
Normal file
51
src/polyp/polyplib-operation.h
Normal 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
127
src/polyp/polyplib-scache.c
Normal 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);
|
||||
}
|
||||
|
||||
50
src/polyp/polyplib-scache.h
Normal file
50
src/polyp/polyplib-scache.h
Normal 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
393
src/polyp/polyplib-simple.c
Normal 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;
|
||||
}
|
||||
80
src/polyp/polyplib-simple.h
Normal file
80
src/polyp/polyplib-simple.h
Normal 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
807
src/polyp/polyplib-stream.c
Normal 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
181
src/polyp/polyplib-stream.h
Normal 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
|
||||
81
src/polyp/polyplib-subscribe.c
Normal file
81
src/polyp/polyplib-subscribe.c
Normal 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;
|
||||
}
|
||||
47
src/polyp/polyplib-subscribe.h
Normal file
47
src/polyp/polyplib-subscribe.h
Normal 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
|
||||
47
src/polyp/polyplib-version.h.in
Normal file
47
src/polyp/polyplib-version.h.in
Normal 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
86
src/polyp/polyplib.h
Normal 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
149
src/polyp/sample.c
Normal 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
120
src/polyp/sample.h
Normal 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
176
src/polyp/volume.c
Normal 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
107
src/polyp/volume.h
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue