rename src to polyp

git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@90 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
Lennart Poettering 2004-07-17 14:12:30 +00:00
parent 563201e128
commit 41f6aea8fd
125 changed files with 0 additions and 0 deletions

363
polyp/Makefile.am Normal file
View file

@ -0,0 +1,363 @@
# $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 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 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.
AM_CFLAGS=-ansi -D_GNU_SOURCE
EXTRA_DIST = polypaudio.run depmod.py
bin_PROGRAMS = polypaudio pacat pactl
noinst_PROGRAMS = pacat-simple parec-simple
pkginclude_HEADERS=polyplib.h \
polyplib-def.h \
polyplib-simple.h \
polyplib-error.h \
mainloop-api.h \
mainloop.h \
mainloop-signal.h \
sample.h
pkglib_LTLIBRARIES=libiochannel.la \
libsocket-server.la \
libsocket-client.la \
libpstream.la \
libpacket.la \
liboss-util.la \
libalsa-util.la \
libioline.la \
libcli.la \
libprotocol-cli.la \
libtagstruct.la \
libpstream-util.la \
libpdispatch.la \
libauthkey.la \
libsocket-util.la \
libprotocol-simple.la \
libprotocol-esound.la \
libprotocol-native.la \
module-cli.la \
module-cli-protocol-tcp.la \
module-cli-protocol-unix.la \
module-pipe-sink.la \
module-alsa-sink.la \
module-alsa-source.la \
module-oss.la \
module-oss-mmap.la \
module-simple-protocol-tcp.la \
module-simple-protocol-unix.la \
module-esound-protocol-tcp.la \
module-esound-protocol-unix.la \
module-native-protocol-tcp.la \
module-native-protocol-unix.la
lib_LTLIBRARIES=libpolyp.la \
libpolyp-simple.la \
libpolyp-error.la \
libpolyp-mainloop.la
polypaudio_SOURCES = idxset.c idxset.h \
queue.c queue.h \
strbuf.c strbuf.h \
main.c \
mainloop.c mainloop.h \
memblock.c memblock.h \
sample.c sample.h \
sample-util.c sample-util.h \
memblockq.c memblockq.h \
client.c client.h \
core.c core.h \
source-output.c source-output.h \
sink-input.c sink-input.h \
source.c source.h \
sink.c sink.h \
module.c module.h \
mainloop-signal.c mainloop-signal.h \
mainloop-api.c mainloop-api.h \
util.c util.h \
hashmap.c hashmap.h \
namereg.c namereg.h \
sconv.c sconv.h \
resampler.c resampler.h \
endianmacros.h \
memchunk.c memchunk.h \
sconv-s16le.c sconv-s16le.h \
sconv-s16be.c sconv-s16be.h \
sioman.c sioman.h \
modargs.c modargs.h \
cmdline.c cmdline.h \
cli-command.c cli-command.h \
clitext.c clitext.h \
tokenizer.c tokenizer.h \
dynarray.c dynarray.h
polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS)
polypaudio_INCLUDES = $(INCLTDL)
polypaudio_LDADD = $(LIBLTDL) $(LIBSAMPLERATE_LIBS)
polypaudio_LDFLAGS=-export-dynamic
libprotocol_simple_la_SOURCES = protocol-simple.c protocol-simple.h
libprotocol_simple_la_LDFLAGS = -avoid-version
libprotocol_simple_la_LIBADD = libsocket-server.la libiochannel.la
libsocket_server_la_SOURCES = socket-server.c socket-server.h
libsocket_server_la_LDFLAGS = -avoid-version
libsocket_server_la_LIBADD = libiochannel.la libsocket-util.la
libsocket_client_la_SOURCES = socket-client.c socket-client.h
libsocket_client_la_LDFLAGS = -avoid-version
libsocket_client_la_LIBADD = libiochannel.la libsocket-util.la
libpstream_la_SOURCES = pstream.c pstream.h
libpstream_la_LDFLAGS = -avoid-version
libpstream_la_LIBADD = libpacket.la libiochannel.la
libpstream_util_la_SOURCES = pstream-util.c pstream-util.h
libpstream_util_la_LDFLAGS = -avoid-version
libpstream_util_la_LIBADD = libpacket.la libpstream.la libtagstruct.la
libpdispatch_la_SOURCES = pdispatch.c pdispatch.h
libpdispatch_la_LDFLAGS = -avoid-version
libpdispatch_la_LIBADD = libtagstruct.la
libiochannel_la_SOURCES = iochannel.c iochannel.h
libiochannel_la_LDFLAGS = -avoid-version
libiochannel_la_LIBADD = libsocket-util.la
libpacket_la_SOURCES = packet.c packet.h
libpacket_la_LDFLAGS = -avoid-version
liboss_util_la_SOURCES = oss-util.c oss-util.h
liboss_util_la_LDFLAGS = -avoid-version
libalsa_util_la_SOURCES = alsa-util.c alsa-util.h
libalsa_util_la_LDFLAGS = -avoid-version
libalsa_util_la_LIBADD = $(ASOUNDLIB_LIBS)
libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
libioline_la_SOURCES = ioline.c ioline.h
libioline_la_LDFLAGS = -avoid-version
libioline_la_LIBADD = libiochannel.la
libcli_la_SOURCES = cli.c cli.h
libcli_la_LDFLAGS = -avoid-version
libcli_la_LIBADD = libiochannel.la libioline.la
libprotocol_cli_la_SOURCES = protocol-cli.c protocol-cli.h
libprotocol_cli_la_LDFLAGS = -avoid-version
libprotocol_cli_la_LIBADD = libsocket-server.la libiochannel.la libcli.la
libprotocol_native_la_SOURCES = protocol-native.c protocol-native.h native-common.h
libprotocol_native_la_LDFLAGS = -avoid-version
libprotocol_native_la_LIBADD = libsocket-server.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la
libtagstruct_la_SOURCES = tagstruct.c tagstruct.h
libtagstruct_la_LDFLAGS = -avoid-version
libprotocol_esound_la_SOURCES = protocol-esound.c protocol-esound.h esound.h
libprotocol_esound_la_LDFLAGS = -avoid-version
libprotocol_esound_la_LIBADD = libsocket-server.la libiochannel.la libauthkey.la
libauthkey_la_SOURCES = authkey.c authkey.h
libauthkey_la_LDFLAGS = -avoid-version
libsocket_util_la_SOURCES = socket-util.c socket-util.h
libsocket_util_la_LDFLAGS = -avoid-version
module_simple_protocol_tcp_la_SOURCES = module-protocol-stub.c
module_simple_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)
module_simple_protocol_tcp_la_LDFLAGS = -module -avoid-version
module_simple_protocol_tcp_la_LIBADD = libprotocol-simple.la libsocket-server.la
module_simple_protocol_unix_la_SOURCES = module-protocol-stub.c
module_simple_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)
module_simple_protocol_unix_la_LDFLAGS = -module -avoid-version
module_simple_protocol_unix_la_LIBADD = libprotocol-simple.la libsocket-server.la libsocket-util.la
module_cli_protocol_tcp_la_SOURCES = module-protocol-stub.c
module_cli_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)
module_cli_protocol_tcp_la_LDFLAGS = -module -avoid-version
module_cli_protocol_tcp_la_LIBADD = libprotocol-cli.la libsocket-server.la
module_cli_protocol_unix_la_SOURCES = module-protocol-stub.c
module_cli_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)
module_cli_protocol_unix_la_LDFLAGS = -module -avoid-version
module_cli_protocol_unix_la_LIBADD = libprotocol-cli.la libsocket-server.la libsocket-util.la
module_native_protocol_tcp_la_SOURCES = module-protocol-stub.c
module_native_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)
module_native_protocol_tcp_la_LDFLAGS = -module -avoid-version
module_native_protocol_tcp_la_LIBADD = libprotocol-native.la libsocket-server.la
module_native_protocol_unix_la_SOURCES = module-protocol-stub.c
module_native_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)
module_native_protocol_unix_la_LDFLAGS = -module -avoid-version
module_native_protocol_unix_la_LIBADD = libprotocol-native.la libsocket-server.la libsocket-util.la
module_esound_protocol_tcp_la_SOURCES = module-protocol-stub.c
module_esound_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)
module_esound_protocol_tcp_la_LDFLAGS = -module -avoid-version
module_esound_protocol_tcp_la_LIBADD = libprotocol-esound.la libsocket-server.la
module_esound_protocol_unix_la_SOURCES = module-protocol-stub.c
module_esound_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)
module_esound_protocol_unix_la_LDFLAGS = -module -avoid-version
module_esound_protocol_unix_la_LIBADD = libprotocol-esound.la libsocket-server.la libsocket-util.la
module_pipe_sink_la_SOURCES = module-pipe-sink.c
module_pipe_sink_la_LDFLAGS = -module -avoid-version
module_pipe_sink_la_LIBADD = libiochannel.la
module_alsa_sink_la_SOURCES = module-alsa-sink.c
module_alsa_sink_la_LDFLAGS = -module -avoid-version
module_alsa_sink_la_LIBADD = $(ASOUNDLIB_LIBS) libalsa-util.la
module_alsa_sink_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
module_alsa_source_la_SOURCES = module-alsa-source.c
module_alsa_source_la_LDFLAGS = -module -avoid-version
module_alsa_source_la_LIBADD = $(ASOUNDLIB_LIBS) libalsa-util.la
module_alsa_source_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
module_oss_la_SOURCES = module-oss.c
module_oss_la_LDFLAGS = -module -avoid-version
module_oss_la_LIBADD = libiochannel.la liboss-util.la
module_oss_mmap_la_SOURCES = module-oss-mmap.c
module_oss_mmap_la_LDFLAGS = -module -avoid-version
module_oss_mmap_la_LIBADD = liboss-util.la
module_cli_la_SOURCES = module-cli.c
module_cli_la_LDFLAGS = -module -avoid-version
module_cli_la_LIBADD = libcli.la libiochannel.la
libpolyp_la_SOURCES = polyplib.c polyplib.h \
polyplib-def.h \
tagstruct.c tagstruct.h \
iochannel.c iochannel.h \
pstream.c pstream.h \
pstream-util.c pstream-util.h \
pdispatch.c pdispatch.h \
mainloop-api.c mainloop-api.h \
idxset.c idxset.h \
util.c util.h \
memblock.c memblock.h \
socket-client.c socket-client.h \
packet.c packet.h \
queue.c queue.h \
dynarray.c dynarray.h \
memchunk.c memchunk.h \
authkey.c authkey.h \
socket-util.c socket-util.h \
native-common.h
libpolyp_la_CFLAGS = $(AM_CFLAGS)
libpolyp_mainloop_la_SOURCES = mainloop-api.h mainloop-api.c \
mainloop.c mainloop.h \
mainloop-signal.c mainloop-signal.h
libpolyp_mainloop_la_CFLAGS = $(AM_CFLAGS)
libpolyp_error_la_SOURCES = polyplib-error.c polyplib-error.h
libpolyp_error_la_CFLAGS = $(AM_CFLAGS)
libpolyp_simple_la_SOURCES = polyplib-simple.c polyplib-simple.h
libpolyp_simple_la_CFLAGS = $(AM_CFLAGS)
libpolyp_simple_la_LIBADD = libpolyp.la libpolyp-mainloop.la
pacat_SOURCES = pacat.c
pacat_LDADD = libpolyp.la libpolyp-error.la libpolyp-mainloop.la
pacat_CFLAGS = $(AM_CFLAGS)
pactl_SOURCES = pactl.c
pactl_LDADD = libpolyp.la libpolyp-error.la libpolyp-mainloop.la
pactl_CFLAGS = $(AM_CFLAGS)
pacat_simple_SOURCES = pacat-simple.c
pacat_simple_LDADD = libpolyp-simple.la libpolyp-error.la
pacat_simple_CFLAGS = $(AM_CFLAGS)
parec_simple_SOURCES = parec-simple.c
parec_simple_LDADD = libpolyp-simple.la libpolyp-error.la
parec_simple_CFLAGS = $(AM_CFLAGS)
if BUILD_LIBPOLYPCORE
pkginclude_HEADERS+=cli-command.h\
client.h \
core.h \
dynarray.h \
endianmacros.h \
hashmap.h \
idxset.h \
iochannel.h \
memblock.h \
memblockq.h \
memchunk.h \
modargs.h \
module.h \
namereg.h \
queue.h \
resampler.h \
sample-util.h \
sink.h \
sink-input.h \
sioman.h \
socket-server.h \
socket-client.h \
socket-util.h \
source.h \
source-output.h \
strbuf.h \
tokenizer.h \
tagstruct.h \
util.h
lib_LTLIBRARIES+= libpolypcore.la
libpolypcore_la_SOURCES = idxset.c idxset.h \
queue.c queue.h \
strbuf.c strbuf.h \
mainloop.c mainloop.h \
memblock.c memblock.h \
sample.c sample.h \
sample-util.c sample-util.h \
memblockq.c memblockq.h \
client.c client.h \
core.c core.h \
source-output.c source-output.h \
sink-input.c sink-input.h \
source.c source.h \
sink.c sink.h \
module.c module.h \
mainloop-signal.c mainloop-signal.h \
mainloop-api.c mainloop-api.h \
util.c util.h \
hashmap.c hashmap.h \
namereg.c namereg.h \
sconv.c sconv.h \
resampler.c resampler.h \
endianmacros.h \
memchunk.c memchunk.h \
sconv-s16le.c sconv-s16le.h \
sconv-s16be.c sconv-s16be.h \
sioman.c sioman.h \
modargs.c modargs.h \
cli-command.c cli-command.h \
clitext.c clitext.h \
tokenizer.c tokenizer.h \
dynarray.c dynarray.h
endif

98
polyp/alsa-util.c Normal file
View file

@ -0,0 +1,98 @@
/* $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 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 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 <asoundlib.h>
#include "alsa-util.h"
#include "sample.h"
int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, struct pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *buffer_size) {
int ret = 0;
snd_pcm_hw_params_t *hwparams = NULL;
static const snd_pcm_format_t format_trans[] = {
[PA_SAMPLE_U8] = SND_PCM_FORMAT_U8,
[PA_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW,
[PA_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW,
[PA_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE,
[PA_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE,
[PA_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE,
[PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
};
if (snd_pcm_hw_params_malloc(&hwparams) < 0 ||
snd_pcm_hw_params_any(pcm_handle, hwparams) < 0 ||
snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 ||
snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[ss->format]) < 0 ||
snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &ss->rate, NULL) < 0 ||
snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss->channels) < 0 ||
snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, periods, NULL) < 0 ||
snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, buffer_size) < 0 ||
snd_pcm_hw_params(pcm_handle, hwparams) < 0) {
ret = -1;
}
if (hwparams)
snd_pcm_hw_params_free(hwparams);
return ret;
}
int pa_create_io_sources(snd_pcm_t *pcm_handle, struct pa_mainloop_api* m, void ***io_sources, unsigned *n_io_sources, void (*cb)(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata) {
unsigned i;
struct pollfd *pfds, *ppfd;
void **ios;
assert(pcm_handle && m && io_sources && n_io_sources && cb);
*n_io_sources = snd_pcm_poll_descriptors_count(pcm_handle);
pfds = malloc(sizeof(struct pollfd) * *n_io_sources);
assert(pfds);
if (snd_pcm_poll_descriptors(pcm_handle, pfds, *n_io_sources) < 0) {
free(pfds);
return -1;
}
*io_sources = malloc(sizeof(void*) * *n_io_sources);
assert(io_sources);
for (i = 0, ios = *io_sources, ppfd = pfds; i < *n_io_sources; i++, ios++, ppfd++) {
*ios = m->source_io(m, ppfd->fd,
((ppfd->events & POLLIN) ? PA_MAINLOOP_API_IO_EVENT_INPUT : 0) |
((ppfd->events & POLLOUT) ? PA_MAINLOOP_API_IO_EVENT_OUTPUT : 0), cb, userdata);
assert(*ios);
}
free(pfds);
return 0;
}
void pa_free_io_sources(struct pa_mainloop_api* m, void **io_sources, unsigned n_io_sources) {
unsigned i;
void **ios;
assert(m && io_sources);
for (ios = io_sources, i = 0; i < n_io_sources; i++, ios++)
m->cancel_io(m, *ios);
free(io_sources);
}

35
polyp/alsa-util.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef fooalsautilhfoo
#define fooalsautilhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <asoundlib.h>
#include "sample.h"
#include "mainloop-api.h"
int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, struct pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *buffer_size);
int pa_create_io_sources(snd_pcm_t *pcm_handle, struct pa_mainloop_api *m, void ***io_sources, unsigned *n_io_sources, void (*cb)(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata);
void pa_free_io_sources(struct pa_mainloop_api* m, void **io_sources, unsigned n_io_sources);
#endif

151
polyp/authkey.c Normal file
View file

@ -0,0 +1,151 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <inttypes.h>
#include <stdlib.h>
#include <time.h>
#include <limits.h>
#include "authkey.h"
#include "util.h"
#define RANDOM_DEVICE "/dev/urandom"
static int load(const char *fn, void *data, size_t length) {
int fd = -1, ret = -1;
ssize_t r;
assert(fn && data && length);
if ((fd = open(fn, O_RDONLY)) < 0)
goto finish;
if ((r = pa_loop_read(fd, data, length)) < 0 || (size_t) r != length) {
ret = -2;
goto finish;
}
ret = 0;
finish:
if (fd >= 0)
close(fd);
return ret;
}
static int generate(const char *fn, void *data, size_t length) {
int fd = -1, random_fd = -1, ret = -1;
ssize_t r;
assert(fn && data && length);
if ((fd = open(fn, O_WRONLY|O_EXCL|O_CREAT, S_IRUSR | S_IWUSR)) < 0)
goto finish;
if ((random_fd = open(RANDOM_DEVICE, O_RDONLY)) >= 0) {
if ((r = pa_loop_read(random_fd, data, length)) < 0 || (size_t) r != length) {
ret = -2;
goto finish;
}
} else {
uint8_t *p;
size_t l;
fprintf(stderr, "WARNING: Failed to open entropy device '"RANDOM_DEVICE"': %s, falling back to unsecure pseudo RNG.\n", strerror(errno));
srandom(time(NULL));
for (p = data, l = length; l > 0; p++, l--)
*p = (uint8_t) random();
}
if ((r = pa_loop_write(fd, data, length)) < 0 || (size_t) r != length) {
ret = -2;
goto finish;
}
ret = 0;
finish:
if (fd >= 0) {
if (ret != 0)
unlink(fn);
close(fd);
}
if (random_fd >= 0)
close(random_fd);
return ret;
}
int pa_authkey_load(const char *path, void *data, size_t length) {
int ret, i;
assert(path && data && length);
for (i = 0; i < 10; i++) {
if ((ret = load(path, data, length)) < 0)
if (ret == -1 && errno == ENOENT)
if ((ret = generate(path, data, length)) < 0)
if (ret == -1 && errno == EEXIST)
continue;
break;
}
if (ret < 0)
fprintf(stderr, "Failed to load authorization key '%s': %s\n", path, (ret == -1) ? strerror(errno) : "file corrupt");
return ret;
}
int pa_authkey_load_from_home(const char *fn, void *data, size_t length) {
char *home;
char path[PATH_MAX];
assert(fn && data && length);
if (!(home = getenv("HOME")))
return -2;
snprintf(path, sizeof(path), "%s/%s", home, fn);
return pa_authkey_load(path, data, length);
}
int pa_authkey_load_auto(const char *fn, void *data, size_t length) {
assert(fn && data && length);
if (*fn == '/')
return pa_authkey_load(fn, data, length);
else
return pa_authkey_load_from_home(fn, data, length);
}

31
polyp/authkey.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef fooauthkeyhfoo
#define fooauthkeyhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <sys/types.h>
int pa_authkey_load(const char *path, void *data, size_t len);
int pa_authkey_load_from_home(const char *fn, void *data, size_t length);
int pa_authkey_load_auto(const char *fn, void *data, size_t length);
#endif

557
polyp/cli-command.c Normal file
View file

@ -0,0 +1,557 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include "cli-command.h"
#include "module.h"
#include "sink.h"
#include "source.h"
#include "client.h"
#include "sink-input.h"
#include "source-output.h"
#include "tokenizer.h"
#include "strbuf.h"
#include "namereg.h"
#include "clitext.h"
struct command {
const char *name;
int (*proc) (struct pa_core *c, struct pa_tokenizer*t, struct pa_strbuf *buf, int *fail, int *verbose);
const char *help;
unsigned args;
};
static int pa_cli_command_exit(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_help(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_modules(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_clients(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_sinks(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_sources(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_sink_inputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_source_outputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_stat(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_info(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_unload(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_sink_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_sink_input_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_sink_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_source_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_kill_client(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_kill_sink_input(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static int pa_cli_command_kill_source_output(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
static const struct command commands[] = {
{ "exit", pa_cli_command_exit, "Terminate the daemon", 1 },
{ "help", pa_cli_command_help, "Show this help", 1 },
{ "modules", pa_cli_command_modules, "List loaded modules", 1 },
{ "sinks", pa_cli_command_sinks, "List loaded sinks", 1 },
{ "sources", pa_cli_command_sources, "List loaded sources", 1 },
{ "clients", pa_cli_command_clients, "List loaded clients", 1 },
{ "sink_inputs", pa_cli_command_sink_inputs, "List sink inputs", 1 },
{ "source_outputs", pa_cli_command_source_outputs, "List source outputs", 1 },
{ "stat", pa_cli_command_stat, "Show memory block statistics", 1 },
{ "info", pa_cli_command_info, "Show comprehensive status", 1 },
{ "ls", pa_cli_command_info, NULL, 1 },
{ "list", pa_cli_command_info, NULL, 1 },
{ "load", pa_cli_command_load, "Load a module (args: name, arguments)", 3},
{ "unload", pa_cli_command_unload, "Unload a module (args: index)", 2},
{ "sink_volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3},
{ "sink_input_volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index|name, volume)", 3},
{ "sink_default", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2},
{ "source_default", pa_cli_command_source_default, "Set the default source (args: index|name)", 2},
{ "kill_client", pa_cli_command_kill_client, "Kill a client (args: index)", 2},
{ "kill_sink_input", pa_cli_command_kill_sink_input, "Kill a sink input (args: index)", 2},
{ "kill_source_output", pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2},
{ NULL, NULL, NULL, 0 }
};
static const char whitespace[] = " \t\n\r";
static const char linebreak[] = "\n\r";
static uint32_t parse_index(const char *n) {
long index;
char *x;
index = strtol(n, &x, 0);
if (!x || *x != 0 || index < 0)
return (uint32_t) PA_IDXSET_INVALID;
return (uint32_t) index;
}
static int pa_cli_command_exit(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
assert(c && c->mainloop && t);
c->mainloop->quit(c->mainloop, 0);
return 0;
}
static int pa_cli_command_help(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
const struct command*command;
assert(c && t && buf);
pa_strbuf_puts(buf, "Available commands:\n");
for (command = commands; command->name; command++)
if (command->help)
pa_strbuf_printf(buf, " %-20s %s\n", command->name, command->help);
return 0;
}
static int pa_cli_command_modules(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
char *s;
assert(c && t);
s = pa_module_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
free(s);
return 0;
}
static int pa_cli_command_clients(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
char *s;
assert(c && t);
s = pa_client_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
free(s);
return 0;
}
static int pa_cli_command_sinks(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
char *s;
assert(c && t);
s = pa_sink_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
free(s);
return 0;
}
static int pa_cli_command_sources(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
char *s;
assert(c && t);
s = pa_source_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
free(s);
return 0;
}
static int pa_cli_command_sink_inputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
char *s;
assert(c && t);
s = pa_sink_input_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
free(s);
return 0;
}
static int pa_cli_command_source_outputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
char *s;
assert(c && t);
s = pa_source_output_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
free(s);
return 0;
}
static int pa_cli_command_stat(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
assert(c && t);
pa_strbuf_printf(buf, "Memory blocks allocated: %u, total size: %u bytes.\n", pa_memblock_get_count(), pa_memblock_get_total());
return 0;
}
static int pa_cli_command_info(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
assert(c && t);
pa_cli_command_stat(c, t, buf, fail, verbose);
pa_cli_command_modules(c, t, buf, fail, verbose);
pa_cli_command_sinks(c, t, buf, fail, verbose);
pa_cli_command_sources(c, t, buf, fail, verbose);
pa_cli_command_clients(c, t, buf, fail, verbose);
pa_cli_command_sink_inputs(c, t, buf, fail, verbose);
pa_cli_command_source_outputs(c, t, buf, fail, verbose);
return 0;
}
static int pa_cli_command_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
struct pa_module *m;
const char *name;
char txt[256];
assert(c && t);
if (!(name = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify the module name and optionally arguments.\n");
return -1;
}
if (!(m = pa_module_load(c, name, pa_tokenizer_get(t, 2)))) {
pa_strbuf_puts(buf, "Module load failed.\n");
return -1;
}
if (*verbose) {
snprintf(txt, sizeof(txt), "Module successfully loaded, index: %u.\n", m->index);
pa_strbuf_puts(buf, txt);
}
return 0;
}
static int pa_cli_command_unload(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
struct pa_module *m;
uint32_t index;
const char *i;
char *e;
assert(c && t);
if (!(i = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify the module index.\n");
return -1;
}
index = (uint32_t) strtoul(i, &e, 10);
if (*e || !(m = pa_idxset_get_by_index(c->modules, index))) {
pa_strbuf_puts(buf, "Invalid module index.\n");
return -1;
}
pa_module_unload_request(c, m);
return 0;
}
static int pa_cli_command_sink_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
const char *n, *v;
char *x = NULL;
struct pa_sink *sink;
long volume;
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
return -1;
}
if (!(v = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
return -1;
}
volume = strtol(v, &x, 0);
if (!x || *x != 0 || volume < 0) {
pa_strbuf_puts(buf, "Failed to parse volume.\n");
return -1;
}
if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
pa_strbuf_puts(buf, "No sink found by this name or index.\n");
return -1;
}
sink->volume = (uint32_t) volume;
return 0;
}
static int pa_cli_command_sink_input_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
const char *n, *v;
struct pa_sink_input *si;
long volume;
uint32_t index;
char *x;
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
return -1;
}
if ((index = parse_index(n)) == PA_IDXSET_INVALID) {
pa_strbuf_puts(buf, "Failed to parse index.\n");
return -1;
}
if (!(v = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
return -1;
}
x = NULL;
volume = strtol(v, &x, 0);
if (!x || *x != 0 || volume < 0) {
pa_strbuf_puts(buf, "Failed to parse volume.\n");
return -1;
}
if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) index))) {
pa_strbuf_puts(buf, "No sink input found with this index.\n");
return -1;
}
si->volume = (uint32_t) volume;
return 0;
}
static int pa_cli_command_sink_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
const char *n;
struct pa_sink *sink;
assert(c && t);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
return -1;
}
if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
pa_strbuf_puts(buf, "No sink found by this name or index.\n");
return -1;
}
c->default_sink_index = sink->index;
return 0;
}
static int pa_cli_command_source_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
const char *n;
struct pa_source *source;
assert(c && t);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
return -1;
}
if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
pa_strbuf_puts(buf, "No source found by this name or index.\n");
return -1;
}
c->default_source_index = source->index;
return 0;
}
static int pa_cli_command_kill_client(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
const char *n;
struct pa_client *client;
uint32_t index;
assert(c && t);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a client by its index.\n");
return -1;
}
if ((index = parse_index(n)) == PA_IDXSET_INVALID) {
pa_strbuf_puts(buf, "Failed to parse index.\n");
return -1;
}
if (!(client = pa_idxset_get_by_index(c->clients, index))) {
pa_strbuf_puts(buf, "No client found by this index.\n");
return -1;
}
pa_client_kill(client);
return 0;
}
static int pa_cli_command_kill_sink_input(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
const char *n;
struct pa_sink_input *sink_input;
uint32_t index;
assert(c && t);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
return -1;
}
if ((index = parse_index(n)) == PA_IDXSET_INVALID) {
pa_strbuf_puts(buf, "Failed to parse index.\n");
return -1;
}
if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, index))) {
pa_strbuf_puts(buf, "No sink input found by this index.\n");
return -1;
}
pa_sink_input_kill(sink_input);
return 0;
}
static int pa_cli_command_kill_source_output(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
const char *n;
struct pa_source_output *source_output;
uint32_t index;
assert(c && t);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
return -1;
}
if ((index = parse_index(n)) == PA_IDXSET_INVALID) {
pa_strbuf_puts(buf, "Failed to parse index.\n");
return -1;
}
if (!(source_output = pa_idxset_get_by_index(c->source_outputs, index))) {
pa_strbuf_puts(buf, "No source output found by this index.\n");
return -1;
}
pa_source_output_kill(source_output);
return 0;
}
int pa_cli_command_execute_line(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail, int *verbose) {
const char *cs;
cs = s+strspn(s, whitespace);
if (*cs == '#' || !*cs)
return 0;
else if (*cs == '.') {
static const char fail_meta[] = ".fail";
static const char nofail_meta[] = ".nofail";
static const char verbose_meta[] = ".verbose";
static const char noverbose_meta[] = ".noverbose";
if (!strcmp(cs, verbose_meta))
*verbose = 1;
else if (!strcmp(cs, noverbose_meta))
*verbose = 0;
else if (!strcmp(cs, fail_meta))
*fail = 1;
else if (!strcmp(cs, nofail_meta))
*fail = 0;
else {
size_t l;
static const char include_meta[] = ".include";
l = strcspn(cs, whitespace);
if (l == sizeof(include_meta)-1 && !strncmp(cs, include_meta, l)) {
const char *filename = cs+l+strspn(cs+l, whitespace);
if (pa_cli_command_execute_file(c, filename, buf, fail, verbose) < 0)
if (*fail) return -1;
} else {
pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs);
if (*fail) return -1;
}
}
} else {
const struct command*command;
int unknown = 1;
size_t l;
l = strcspn(cs, whitespace);
for (command = commands; command->name; command++)
if (strlen(command->name) == l && !strncmp(cs, command->name, l)) {
int ret;
struct pa_tokenizer *t = pa_tokenizer_new(cs, command->args);
assert(t);
ret = command->proc(c, t, buf, fail, verbose);
pa_tokenizer_free(t);
unknown = 0;
if (ret < 0 && *fail)
return -1;
break;
}
if (unknown) {
pa_strbuf_printf(buf, "Unknown command: %s\n", cs);
if (*fail)
return -1;
}
}
return 0;
}
int pa_cli_command_execute_file(struct pa_core *c, const char *fn, struct pa_strbuf *buf, int *fail, int *verbose) {
char line[256];
FILE *f = NULL;
int ret = -1;
assert(c && fn && buf);
if (!(f = fopen(fn, "r"))) {
pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, strerror(errno));
if (!*fail)
ret = 0;
goto fail;
}
if (*verbose)
pa_strbuf_printf(buf, "Executing file: '%s'\n", fn);
while (fgets(line, sizeof(line), f)) {
char *e = line + strcspn(line, linebreak);
*e = 0;
if (pa_cli_command_execute_line(c, line, buf, fail, verbose) < 0 && *fail)
goto fail;
}
if (*verbose)
pa_strbuf_printf(buf, "Executed file: '%s'\n", fn);
ret = 0;
fail:
if (f)
fclose(f);
return ret;
}
int pa_cli_command_execute(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail, int *verbose) {
const char *p;
assert(c && s && buf && fail && verbose);
p = s;
while (*p) {
size_t l = strcspn(p, linebreak);
char *line = strndup(p, l);
assert(line);
if (pa_cli_command_execute_line(c, line, buf, fail, verbose) < 0&& *fail) {
free(line);
return -1;
}
free(line);
p += l;
p += strspn(p, linebreak);
}
return 0;
}

32
polyp/cli-command.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef fooclicommandhfoo
#define fooclicommandhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "strbuf.h"
#include "core.h"
int pa_cli_command_execute_line(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail, int *verbose);
int pa_cli_command_execute_file(struct pa_core *c, const char *fn, struct pa_strbuf *buf, int *fail, int *verbose);
int pa_cli_command_execute(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail, int *verbose);
#endif

148
polyp/cli.c Normal file
View file

@ -0,0 +1,148 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include "ioline.h"
#include "cli.h"
#include "module.h"
#include "sink.h"
#include "source.h"
#include "client.h"
#include "sink-input.h"
#include "source-output.h"
#include "tokenizer.h"
#include "strbuf.h"
#include "namereg.h"
#include "clitext.h"
#include "cli-command.h"
struct pa_cli {
struct pa_core *core;
struct pa_ioline *line;
void (*eof_callback)(struct pa_cli *c, void *userdata);
void *userdata;
struct pa_client *client;
int fail, verbose, kill_requested, defer_kill;
};
static void line_callback(struct pa_ioline *line, const char *s, void *userdata);
static const char prompt[] = ">>> ";
static void client_kill(struct pa_client *c);
struct pa_cli* pa_cli_new(struct pa_core *core, struct pa_iochannel *io, struct pa_module *m) {
char cname[256];
struct pa_cli *c;
assert(io);
c = malloc(sizeof(struct pa_cli));
assert(c);
c->core = core;
c->line = pa_ioline_new(io);
assert(c->line);
c->userdata = NULL;
c->eof_callback = NULL;
pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
c->client = pa_client_new(core, "CLI", cname);
assert(c->client);
c->client->kill = client_kill;
c->client->userdata = c;
c->client->owner = m;
pa_ioline_set_callback(c->line, line_callback, c);
pa_ioline_puts(c->line, "Welcome to polypaudio! Use \"help\" for usage information.\n");
pa_ioline_puts(c->line, prompt);
c->fail = c->kill_requested = c->defer_kill = 0;
c->verbose = 1;
return c;
}
void pa_cli_free(struct pa_cli *c) {
assert(c);
pa_ioline_free(c->line);
pa_client_free(c->client);
free(c);
}
static void client_kill(struct pa_client *client) {
struct pa_cli *c;
assert(client && client->userdata);
c = client->userdata;
fprintf(stderr, "CLI client killed.\n");
if (c->defer_kill)
c->kill_requested = 1;
else {
if (c->eof_callback)
c->eof_callback(c, c->userdata);
}
}
static void line_callback(struct pa_ioline *line, const char *s, void *userdata) {
struct pa_strbuf *buf;
struct pa_cli *c = userdata;
char *p;
assert(line && c);
if (!s) {
fprintf(stderr, "CLI got EOF from user.\n");
if (c->eof_callback)
c->eof_callback(c, c->userdata);
return;
}
buf = pa_strbuf_new();
assert(buf);
c->defer_kill++;
pa_cli_command_execute_line(c->core, s, buf, &c->fail, &c->verbose);
c->defer_kill--;
pa_ioline_puts(line, p = pa_strbuf_tostring_free(buf));
free(p);
if (c->kill_requested) {
if (c->eof_callback)
c->eof_callback(c, c->userdata);
} else
pa_ioline_puts(line, prompt);
}
void pa_cli_set_eof_callback(struct pa_cli *c, void (*cb)(struct pa_cli*c, void *userdata), void *userdata) {
assert(c && cb);
c->eof_callback = cb;
c->userdata = userdata;
}

36
polyp/cli.h Normal file
View file

@ -0,0 +1,36 @@
#ifndef fooclihfoo
#define fooclihfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "iochannel.h"
#include "core.h"
#include "module.h"
struct pa_cli;
struct pa_cli* pa_cli_new(struct pa_core *core, struct pa_iochannel *io, struct pa_module *m);
void pa_cli_free(struct pa_cli *cli);
void pa_cli_set_eof_callback(struct pa_cli *cli, void (*cb)(struct pa_cli*c, void *userdata), void *userdata);
#endif

79
polyp/client.c Normal file
View file

@ -0,0 +1,79 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "client.h"
struct pa_client *pa_client_new(struct pa_core *core, const char *protocol_name, char *name) {
struct pa_client *c;
int r;
assert(core);
c = malloc(sizeof(struct pa_client));
assert(c);
c->name = name ? strdup(name) : NULL;
c->owner = NULL;
c->core = core;
c->protocol_name = protocol_name;
c->kill = NULL;
c->userdata = NULL;
r = pa_idxset_put(core->clients, c, &c->index);
assert(c->index != PA_IDXSET_INVALID && r >= 0);
fprintf(stderr, "client: created %u \"%s\"\n", c->index, c->name);
return c;
}
void pa_client_free(struct pa_client *c) {
assert(c && c->core);
pa_idxset_remove_by_data(c->core->clients, c, NULL);
fprintf(stderr, "client: freed %u \"%s\"\n", c->index, c->name);
free(c->name);
free(c);
}
void pa_client_kill(struct pa_client *c) {
assert(c);
if (!c->kill) {
fprintf(stderr, "kill() operation not implemented for client %u\n", c->index);
return;
}
c->kill(c);
}
void pa_client_rename(struct pa_client *c, const char *name) {
assert(c);
free(c->name);
c->name = name ? strdup(name) : NULL;
}

51
polyp/client.h Normal file
View file

@ -0,0 +1,51 @@
#ifndef fooclienthfoo
#define fooclienthfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "core.h"
#include "module.h"
struct pa_client {
uint32_t index;
struct pa_module *owner;
char *name;
struct pa_core *core;
const char *protocol_name;
void (*kill)(struct pa_client *c);
void *userdata;
};
struct pa_client *pa_client_new(struct pa_core *c, const char *protocol_name, char *name);
/* This function should be called only by the code that created the client */
void pa_client_free(struct pa_client *c);
/* Code that didn't create the client should call this function to
* request destruction of the client */
void pa_client_kill(struct pa_client *c);
void pa_client_rename(struct pa_client *c, const char *name);
#endif

203
polyp/clitext.c Normal file
View file

@ -0,0 +1,203 @@
/* $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 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 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 "clitext.h"
#include "module.h"
#include "client.h"
#include "sink.h"
#include "source.h"
#include "sink-input.h"
#include "source-output.h"
#include "strbuf.h"
#include "sample-util.h"
char *pa_module_list_to_string(struct pa_core *c) {
struct pa_strbuf *s;
struct pa_module *m;
uint32_t index = PA_IDXSET_INVALID;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_ncontents(c->modules));
for (m = pa_idxset_first(c->modules, &index); m; m = pa_idxset_next(c->modules, &index))
pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\targument: <%s>\n", m->index, m->name, m->argument);
return pa_strbuf_tostring_free(s);
}
char *pa_client_list_to_string(struct pa_core *c) {
struct pa_strbuf *s;
struct pa_client *client;
uint32_t index = PA_IDXSET_INVALID;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u client(s).\n", pa_idxset_ncontents(c->clients));
for (client = pa_idxset_first(c->clients, &index); client; client = pa_idxset_next(c->clients, &index)) {
pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\tprotocol_name: <%s>\n", client->index, client->name, client->protocol_name);
if (client->owner)
pa_strbuf_printf(s, "\towner module: <%u>\n", client->owner->index);
}
return pa_strbuf_tostring_free(s);
}
char *pa_sink_list_to_string(struct pa_core *c) {
struct pa_strbuf *s;
struct pa_sink *sink, *default_sink;
uint32_t index = PA_IDXSET_INVALID;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_ncontents(c->sinks));
default_sink = pa_sink_get_default(c);
for (sink = pa_idxset_first(c->sinks, &index); sink; sink = pa_idxset_next(c->sinks, &index)) {
char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH];
pa_sample_snprint(ss, sizeof(ss), &sink->sample_spec);
assert(sink->monitor_source);
pa_strbuf_printf(
s,
" %c index: %u\n\tname: <%s>\n\tvolume: <0x%04x>\n\tlatency: <%u usec>\n\tmonitor_source: <%u>\n\tsample_spec: <%s>\n",
sink == default_sink ? '*' : ' ',
sink->index, sink->name,
(unsigned) sink->volume,
pa_sink_get_latency(sink),
sink->monitor_source->index,
ss);
if (sink->owner)
pa_strbuf_printf(s, "\towner module: <%u>\n", sink->owner->index);
if (sink->description)
pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description);
}
return pa_strbuf_tostring_free(s);
}
char *pa_source_list_to_string(struct pa_core *c) {
struct pa_strbuf *s;
struct pa_source *source, *default_source;
uint32_t index = PA_IDXSET_INVALID;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_ncontents(c->sources));
default_source = pa_source_get_default(c);
for (source = pa_idxset_first(c->sources, &index); source; source = pa_idxset_next(c->sources, &index)) {
char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH];
pa_sample_snprint(ss, sizeof(ss), &source->sample_spec);
pa_strbuf_printf(s, " %c index: %u\n\tname: <%s>\n\tsample_spec: <%s>\n", source == default_source ? '*' : ' ', source->index, source->name, ss);
if (source->monitor_of)
pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index);
if (source->owner)
pa_strbuf_printf(s, "\towner module: <%u>\n", source->owner->index);
if (source->description)
pa_strbuf_printf(s, "\tdescription: <%s>\n", source->description);
}
return pa_strbuf_tostring_free(s);
}
char *pa_source_output_list_to_string(struct pa_core *c) {
struct pa_strbuf *s;
struct pa_source_output *o;
uint32_t index = PA_IDXSET_INVALID;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_ncontents(c->source_outputs));
for (o = pa_idxset_first(c->source_outputs, &index); o; o = pa_idxset_next(c->source_outputs, &index)) {
char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH];
pa_sample_snprint(ss, sizeof(ss), &o->sample_spec);
assert(o->source);
pa_strbuf_printf(
s, " index: %u\n\tname: <%s>\n\tsource: <%u>\n\tsample_spec: <%s>\n",
o->index,
o->name,
o->source->index,
ss);
if (o->owner)
pa_strbuf_printf(s, "\towner module: <%u>\n", o->owner->index);
if (o->client)
pa_strbuf_printf(s, "\tclient: <%u>\n", o->client->index);
}
return pa_strbuf_tostring_free(s);
}
char *pa_sink_input_list_to_string(struct pa_core *c) {
struct pa_strbuf *s;
struct pa_sink_input *i;
uint32_t index = PA_IDXSET_INVALID;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_ncontents(c->sink_inputs));
for (i = pa_idxset_first(c->sink_inputs, &index); i; i = pa_idxset_next(c->sink_inputs, &index)) {
char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH];
pa_sample_snprint(ss, sizeof(ss), &i->sample_spec);
assert(i->sink);
pa_strbuf_printf(
s, " index: %u\n\tname: <%s>\n\tsink: <%u>\n\tvolume: <0x%04x>\n\tlatency: <%u usec>\n\tsample_spec: <%s>\n",
i->index,
i->name,
i->sink->index,
(unsigned) i->volume,
pa_sink_input_get_latency(i),
ss);
if (i->owner)
pa_strbuf_printf(s, "\towner module: <%u>\n", i->owner->index);
if (i->client)
pa_strbuf_printf(s, "\tclient: <%u>\n", i->client->index);
}
return pa_strbuf_tostring_free(s);
}

35
polyp/clitext.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef fooclitexthfoo
#define fooclitexthfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "core.h"
char *pa_sink_input_list_to_string(struct pa_core *c);
char *pa_source_output_list_to_string(struct pa_core *c);
char *pa_sink_list_to_string(struct pa_core *core);
char *pa_source_list_to_string(struct pa_core *c);
char *pa_client_list_to_string(struct pa_core *c);
char *pa_module_list_to_string(struct pa_core *c);
#endif

111
polyp/cmdline.c Normal file
View file

@ -0,0 +1,111 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include "cmdline.h"
#include "util.h"
#include "strbuf.h"
void pa_cmdline_help(const char *argv0) {
const char *e;
if ((e = strrchr(argv0, '/')))
e++;
else
e = argv0;
printf("%s [options]\n"
" -L MODULE Load the specified plugin module with the specified argument\n"
" -F FILE Run the specified script\n"
" -C Open a command line on the running TTY\n"
" -D Daemonize after loading the modules\n"
" -f Dont quit when the startup fails\n"
" -v Verbose startup\n"
" -h Show this help\n", e);
}
struct pa_cmdline* pa_cmdline_parse(int argc, char * const argv []) {
char c;
struct pa_cmdline *cmdline = NULL;
struct pa_strbuf *buf = NULL;
assert(argc && argv);
cmdline = malloc(sizeof(struct pa_cmdline));
assert(cmdline);
cmdline->daemonize = cmdline->help = cmdline->verbose = 0;
cmdline->fail = 1;
buf = pa_strbuf_new();
assert(buf);
while ((c = getopt(argc, argv, "L:F:CDhfv")) != -1) {
switch (c) {
case 'L':
pa_strbuf_printf(buf, "load %s\n", optarg);
break;
case 'F':
pa_strbuf_printf(buf, ".include %s\n", optarg);
break;
case 'C':
pa_strbuf_puts(buf, "load module-cli\n");
break;
case 'D':
cmdline->daemonize = 1;
break;
case 'h':
cmdline->help = 1;
break;
case 'f':
cmdline->fail = 0;
break;
case 'v':
cmdline->verbose = 0;
break;
default:
goto fail;
}
}
cmdline->cli_commands = pa_strbuf_tostring_free(buf);
return cmdline;
fail:
if (cmdline)
pa_cmdline_free(cmdline);
if (buf)
pa_strbuf_free(buf);
return NULL;
}
void pa_cmdline_free(struct pa_cmdline *cmd) {
assert(cmd);
free(cmd->cli_commands);
free(cmd);
}

36
polyp/cmdline.h Normal file
View file

@ -0,0 +1,36 @@
#ifndef foocmdlinehfoo
#define foocmdlinehfoo
/* $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 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 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.
***/
struct pa_cmdline {
int daemonize, help, fail, verbose;
char *cli_commands;
};
struct pa_cmdline* pa_cmdline_parse(int argc, char * const argv []);
void pa_cmdline_free(struct pa_cmdline *cmd);
void pa_cmdline_help(const char *argv0);
#endif

88
polyp/core.c Normal file
View file

@ -0,0 +1,88 @@
/* $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 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 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 "core.h"
#include "module.h"
#include "sink.h"
#include "source.h"
#include "namereg.h"
#include "util.h"
struct pa_core* pa_core_new(struct pa_mainloop_api *m) {
struct pa_core* c;
c = malloc(sizeof(struct pa_core));
assert(c);
c->mainloop = m;
c->clients = pa_idxset_new(NULL, NULL);
c->sinks = pa_idxset_new(NULL, NULL);
c->sources = pa_idxset_new(NULL, NULL);
c->source_outputs = pa_idxset_new(NULL, NULL);
c->sink_inputs = pa_idxset_new(NULL, NULL);
c->default_source_index = c->default_sink_index = PA_IDXSET_INVALID;
c->modules = NULL;
c->namereg = NULL;
c->default_sample_spec.format = PA_SAMPLE_S16NE;
c->default_sample_spec.rate = 44100;
c->default_sample_spec.channels = 2;
pa_check_for_sigpipe();
return c;
};
void pa_core_free(struct pa_core *c) {
assert(c);
pa_module_unload_all(c);
assert(!c->modules);
assert(pa_idxset_isempty(c->clients));
pa_idxset_free(c->clients, NULL, NULL);
assert(pa_idxset_isempty(c->sinks));
pa_idxset_free(c->sinks, NULL, NULL);
assert(pa_idxset_isempty(c->sources));
pa_idxset_free(c->sources, NULL, NULL);
assert(pa_idxset_isempty(c->source_outputs));
pa_idxset_free(c->source_outputs, NULL, NULL);
assert(pa_idxset_isempty(c->sink_inputs));
pa_idxset_free(c->sink_inputs, NULL, NULL);
pa_namereg_free(c);
free(c);
};

45
polyp/core.h Normal file
View file

@ -0,0 +1,45 @@
#ifndef foocorehfoo
#define foocorehfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 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 "idxset.h"
#include "hashmap.h"
#include "mainloop-api.h"
#include "sample.h"
struct pa_core {
struct pa_mainloop_api *mainloop;
struct pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules;
struct pa_hashmap *namereg;
uint32_t default_source_index, default_sink_index;
struct pa_sample_spec default_sample_spec;
};
struct pa_core* pa_core_new(struct pa_mainloop_api *m);
void pa_core_free(struct pa_core*c);
#endif

73
polyp/depmod.py Executable file
View file

@ -0,0 +1,73 @@
#!/usr/bin/python
# $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 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 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.
import sys, os, string
exported_symbols = {}
imported_symbols = {}
for fn in sys.argv[1:]:
f = os.popen("nm '%s'" % fn, "r")
imported_symbols[fn] = []
for line in f:
sym_address = line[:7].strip()
sym_type = line[9].strip()
sym_name = line[11:].strip()
if sym_name in ('_fini', '_init'):
continue
if sym_type in ('T', 'B', 'R', 'D' 'G', 'S', 'D'):
if exported_symbols.has_key(sym_name):
sys.stderr.write("CONFLICT: %s defined in both '%s' and '%s'.\n" % (sym_name, fn, exported_symbols[sym_name]))
else:
exported_symbols[sym_name] = fn
elif sym_type in ('U',):
if sym_name[:3] == 'pa_':
imported_symbols[fn].append(sym_name)
f.close()
dependencies = {}
unresolved_symbols = {}
for fn in imported_symbols:
dependencies[fn] = []
for sym in imported_symbols[fn]:
if exported_symbols.has_key(sym):
if exported_symbols[sym] not in dependencies[fn]:
dependencies[fn].append(exported_symbols[sym])
else:
if unresolved_symbols.has_key(sym):
unresolved_symbols[sym].append(fn)
else:
unresolved_symbols[sym] = [fn]
for sym, files in unresolved_symbols.iteritems():
print "WARNING: Unresolved symbol '%s' in %s" % (sym, `files`)
k = dependencies.keys()
k.sort()
for fn in k:
dependencies[fn].sort()
print "%s: %s" % (fn, string.join(dependencies[fn], " "))

98
polyp/dynarray.c Normal file
View file

@ -0,0 +1,98 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include "dynarray.h"
struct pa_dynarray {
void **data;
unsigned n_allocated, n_entries;
};
struct pa_dynarray* pa_dynarray_new(void) {
struct pa_dynarray *a;
a = malloc(sizeof(struct pa_dynarray));
assert(a);
a->data = NULL;
a->n_entries = 0;
a->n_allocated = 0;
return a;
}
void pa_dynarray_free(struct pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata) {
unsigned i;
assert(a);
if (func)
for (i = 0; i < a->n_entries; i++)
if (a->data[i])
func(a->data[i], userdata);
free(a->data);
free(a);
}
void pa_dynarray_put(struct pa_dynarray*a, unsigned i, void *p) {
assert(a);
if (i >= a->n_allocated) {
unsigned n;
if (!p)
return;
n = i+100;
a->data = realloc(a->data, sizeof(void*)*n);
memset(a->data+a->n_allocated, 0, sizeof(void*)*(n-a->n_allocated));
a->n_allocated = n;
}
a->data[i] = p;
if (i >= a->n_entries)
a->n_entries = i+1;
}
unsigned pa_dynarray_append(struct pa_dynarray*a, void *p) {
unsigned i = a->n_entries;
pa_dynarray_put(a, i, p);
return i;
}
void *pa_dynarray_get(struct pa_dynarray*a, unsigned i) {
assert(a);
if (i >= a->n_allocated)
return NULL;
assert(a->data);
return a->data[i];
}
unsigned pa_dynarray_ncontents(struct pa_dynarray*a) {
assert(a);
return a->n_entries;
}

37
polyp/dynarray.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef foodynarrayhfoo
#define foodynarrayhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 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.
***/
struct pa_dynarray;
struct pa_dynarray* pa_dynarray_new(void);
void pa_dynarray_free(struct pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata);
void pa_dynarray_put(struct pa_dynarray*a, unsigned i, void *p);
unsigned pa_dynarray_append(struct pa_dynarray*a, void *p);
void *pa_dynarray_get(struct pa_dynarray*a, unsigned i);
unsigned pa_dynarray_ncontents(struct pa_dynarray*a);
#endif

62
polyp/endianmacros.h Normal file
View file

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

213
polyp/esound.h Normal file
View file

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

170
polyp/hashmap.c Normal file
View file

@ -0,0 +1,170 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "hashmap.h"
#include "idxset.h"
struct hashmap_entry {
struct hashmap_entry *next, *previous, *bucket_next, *bucket_previous;
unsigned hash;
const void *key;
void *value;
};
struct pa_hashmap {
unsigned size;
struct hashmap_entry **data;
struct hashmap_entry *first_entry;
unsigned n_entries;
unsigned (*hash_func) (const void *p);
int (*compare_func) (const void*a, const void*b);
};
struct pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) {
struct pa_hashmap *h;
h = malloc(sizeof(struct pa_hashmap));
assert(h);
h->data = malloc(sizeof(struct hashmap_entry*)*(h->size = 1023));
assert(h->data);
memset(h->data, 0, sizeof(struct hashmap_entry*)*(h->size = 1023));
h->first_entry = NULL;
h->n_entries = 0;
h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
return h;
}
static void remove(struct pa_hashmap *h, struct hashmap_entry *e) {
assert(e);
if (e->next)
e->next->previous = e->previous;
if (e->previous)
e->previous->next = e->next;
else
h->first_entry = e->next;
if (e->bucket_next)
e->bucket_next->bucket_previous = e->bucket_previous;
if (e->bucket_previous)
e->bucket_previous->bucket_next = e->bucket_next;
else
h->data[e->hash] = e->bucket_next;
free(e);
h->n_entries--;
}
void pa_hashmap_free(struct pa_hashmap*h, void (*free_func)(void *p, void *userdata), void *userdata) {
assert(h);
while (h->first_entry) {
if (free_func)
free_func(h->first_entry->value, userdata);
remove(h, h->first_entry);
}
free(h->data);
free(h);
}
static struct hashmap_entry *get(struct pa_hashmap *h, unsigned hash, const void *key) {
struct hashmap_entry *e;
for (e = h->data[hash]; e; e = e->bucket_next)
if (h->compare_func(e->key, key) == 0)
return e;
return NULL;
}
int pa_hashmap_put(struct pa_hashmap *h, const void *key, void *value) {
struct hashmap_entry *e;
unsigned hash;
assert(h && key);
hash = h->hash_func(key) % h->size;
if ((e = get(h, hash, key)))
return -1;
e = malloc(sizeof(struct hashmap_entry));
assert(e);
e->hash = hash;
e->key = key;
e->value = value;
e->previous = NULL;
e->next = h->first_entry;
if (h->first_entry)
h->first_entry->previous = e;
h->first_entry = e;
e->bucket_previous = NULL;
e->bucket_next = h->data[hash];
if (h->data[hash])
h->data[hash]->bucket_previous = e;
h->data[hash] = e;
h->n_entries ++;
return 0;
}
void* pa_hashmap_get(struct pa_hashmap *h, const void *key) {
unsigned hash;
struct hashmap_entry *e;
assert(h && key);
hash = h->hash_func(key) % h->size;
if (!(e = get(h, hash, key)))
return NULL;
return e->value;
}
int pa_hashmap_remove(struct pa_hashmap *h, const void *key) {
struct hashmap_entry *e;
unsigned hash;
assert(h && key);
hash = h->hash_func(key) % h->size;
if (!(e = get(h, hash, key)))
return 1;
remove(h, e);
return 0;
}
unsigned pa_hashmap_ncontents(struct pa_hashmap *h) {
return h->n_entries;
}

37
polyp/hashmap.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef foohashmaphfoo
#define foohashmaphfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 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.
***/
struct pa_hashmap;
struct pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b));
void pa_hashmap_free(struct pa_hashmap*, void (*free_func)(void *p, void *userdata), void *userdata);
int pa_hashmap_put(struct pa_hashmap *h, const void *key, void *value);
void* pa_hashmap_get(struct pa_hashmap *h, const void *key);
int pa_hashmap_remove(struct pa_hashmap *h, const void *key);
unsigned pa_hashmap_ncontents(struct pa_hashmap *h);
#endif

397
polyp/idxset.c Normal file
View file

@ -0,0 +1,397 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "idxset.h"
struct idxset_entry {
void *data;
uint32_t index;
unsigned hash_value;
struct idxset_entry *hash_prev, *hash_next;
struct idxset_entry* iterate_prev, *iterate_next;
};
struct pa_idxset {
unsigned (*hash_func) (const void *p);
int (*compare_func)(const void *a, const void *b);
unsigned hash_table_size, n_entries;
struct idxset_entry **hash_table, **array, *iterate_list_head, *iterate_list_tail;
uint32_t index, start_index, array_size;
};
unsigned pa_idxset_string_hash_func(const void *p) {
unsigned hash = 0;
const char *c;
for (c = p; *c; c++)
hash = 31 * hash + *c;
return hash;
}
int pa_idxset_string_compare_func(const void *a, const void *b) {
return strcmp(a, b);
}
unsigned pa_idxset_trivial_hash_func(const void *p) {
return (unsigned) p;
}
int pa_idxset_trivial_compare_func(const void *a, const void *b) {
return a != b;
}
struct pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) {
struct pa_idxset *s;
s = malloc(sizeof(struct pa_idxset));
assert(s);
s->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
s->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
s->hash_table_size = 1023;
s->hash_table = malloc(sizeof(struct idxset_entry*)*s->hash_table_size);
assert(s->hash_table);
memset(s->hash_table, 0, sizeof(struct idxset_entry*)*s->hash_table_size);
s->array = NULL;
s->array_size = 0;
s->index = 0;
s->start_index = 0;
s->n_entries = 0;
s->iterate_list_head = s->iterate_list_tail = NULL;
return s;
}
void pa_idxset_free(struct pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata) {
assert(s);
if (free_func) {
while (s->iterate_list_head) {
struct idxset_entry *e = s->iterate_list_head;
s->iterate_list_head = s->iterate_list_head->iterate_next;
if (free_func)
free_func(e->data, userdata);
free(e);
}
}
free(s->hash_table);
free(s->array);
free(s);
}
static struct idxset_entry* hash_scan(struct pa_idxset *s, struct idxset_entry* e, void *p) {
assert(p);
assert(s->compare_func);
for (; e; e = e->hash_next)
if (s->compare_func(e->data, p) == 0)
return e;
return NULL;
}
static void extend_array(struct pa_idxset *s, uint32_t index) {
uint32_t i, j, l;
struct idxset_entry** n;
assert(index >= s->start_index);
if (index < s->start_index + s->array_size)
return;
for (i = 0; i < s->array_size; i++)
if (s->array[i])
break;
l = index - s->start_index - i + 100;
n = malloc(sizeof(struct hash_table_entry*)*l);
assert(n);
memset(n, 0, sizeof(struct hash_table_entry*)*l);
for (j = 0; j < s->array_size-i; j++)
n[j] = s->array[i+j];
free(s->array);
s->array = n;
s->array_size = l;
s->start_index += i;
}
static struct idxset_entry** array_index(struct pa_idxset*s, uint32_t index) {
if (index >= s->start_index + s->array_size)
return NULL;
if (index < s->start_index)
return NULL;
return s->array + (index - s->start_index);
}
int pa_idxset_put(struct pa_idxset*s, void *p, uint32_t *index) {
unsigned h;
struct idxset_entry *e, **a;
assert(s && p);
assert(s->hash_func);
h = s->hash_func(p) % s->hash_table_size;
assert(s->hash_table);
if ((e = hash_scan(s, s->hash_table[h], p))) {
if (index)
*index = e->index;
return -1;
}
e = malloc(sizeof(struct idxset_entry));
assert(e);
e->data = p;
e->index = s->index++;
e->hash_value = h;
/* Insert into hash table */
e->hash_next = s->hash_table[h];
e->hash_prev = NULL;
if (s->hash_table[h])
s->hash_table[h]->hash_prev = e;
s->hash_table[h] = e;
/* Insert into array */
extend_array(s, e->index);
a = array_index(s, e->index);
assert(a && !*a);
*a = e;
/* Insert into linked list */
e->iterate_next = NULL;
e->iterate_prev = s->iterate_list_tail;
if (s->iterate_list_tail) {
assert(s->iterate_list_head);
s->iterate_list_tail->iterate_next = e;
} else {
assert(!s->iterate_list_head);
s->iterate_list_head = e;
}
s->iterate_list_tail = e;
s->n_entries++;
assert(s->n_entries >= 1);
if (index)
*index = e->index;
return 0;
}
void* pa_idxset_get_by_index(struct pa_idxset*s, uint32_t index) {
struct idxset_entry **a;
assert(s);
if (!(a = array_index(s, index)))
return NULL;
if (!*a)
return NULL;
return (*a)->data;
}
void* pa_idxset_get_by_data(struct pa_idxset*s, void *p, uint32_t *index) {
unsigned h;
struct idxset_entry *e;
assert(s && p);
assert(s->hash_func);
h = s->hash_func(p) % s->hash_table_size;
assert(s->hash_table);
if (!(e = hash_scan(s, s->hash_table[h], p)))
return NULL;
if (index)
*index = e->index;
return e->data;
}
static void remove_entry(struct pa_idxset *s, struct idxset_entry *e) {
struct idxset_entry **a;
assert(s && e);
/* Remove from array */
a = array_index(s, e->index);
assert(a && *a && *a == e);
*a = NULL;
/* Remove from linked list */
if (e->iterate_next)
e->iterate_next->iterate_prev = e->iterate_prev;
else
s->iterate_list_tail = e->iterate_prev;
if (e->iterate_prev)
e->iterate_prev->iterate_next = e->iterate_next;
else
s->iterate_list_head = e->iterate_next;
/* Remove from hash table */
if (e->hash_next)
e->hash_next->hash_prev = e->hash_prev;
if (e->hash_prev)
e->hash_prev->hash_next = e->hash_next;
else
s->hash_table[e->hash_value] = e->hash_next;
free(e);
assert(s->n_entries >= 1);
s->n_entries--;
}
void* pa_idxset_remove_by_index(struct pa_idxset*s, uint32_t index) {
struct idxset_entry **a;
void *data;
assert(s);
if (!(a = array_index(s, index)))
return NULL;
data = (*a)->data;
remove_entry(s, *a);
return data;
}
void* pa_idxset_remove_by_data(struct pa_idxset*s, void *data, uint32_t *index) {
struct idxset_entry *e;
unsigned h;
assert(s->hash_func);
h = s->hash_func(data) % s->hash_table_size;
assert(s->hash_table);
if (!(e = hash_scan(s, s->hash_table[h], data)))
return NULL;
data = e->data;
if (index)
*index = e->index;
remove_entry(s, e);
return data;
}
void* pa_idxset_rrobin(struct pa_idxset *s, uint32_t *index) {
struct idxset_entry **a, *e = NULL;
assert(s && index);
if ((a = array_index(s, *index)) && *a)
e = (*a)->iterate_next;
if (!e)
e = s->iterate_list_head;
if (!e)
return NULL;
*index = e->index;
return e->data;
}
void* pa_idxset_first(struct pa_idxset *s, uint32_t *index) {
assert(s);
if (!s->iterate_list_head)
return NULL;
if (index)
*index = s->iterate_list_head->index;
return s->iterate_list_head->data;
}
void *pa_idxset_next(struct pa_idxset *s, uint32_t *index) {
struct idxset_entry **a, *e = NULL;
assert(s && index);
if ((a = array_index(s, *index)) && *a)
e = (*a)->iterate_next;
if (e) {
*index = e->index;
return e->data;
} else {
*index = PA_IDXSET_INVALID;
return NULL;
}
}
int pa_idxset_foreach(struct pa_idxset*s, int (*func)(void *p, uint32_t index, int *del, void*userdata), void *userdata) {
struct idxset_entry *e;
assert(s && func);
e = s->iterate_list_head;
while (e) {
int del = 0, r;
struct idxset_entry *n = e->iterate_next;
r = func(e->data, e->index, &del, userdata);
if (del)
remove_entry(s, e);
if (r < 0)
return r;
e = n;
}
return 0;
}
unsigned pa_idxset_ncontents(struct pa_idxset*s) {
assert(s);
return s->n_entries;
}
int pa_idxset_isempty(struct pa_idxset *s) {
assert(s);
return s->n_entries == 0;
}

63
polyp/idxset.h Normal file
View file

@ -0,0 +1,63 @@
#ifndef fooidxsethfoo
#define fooidxsethfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 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>
#define PA_IDXSET_INVALID ((uint32_t) -1)
unsigned pa_idxset_trivial_hash_func(const void *p);
int pa_idxset_trivial_compare_func(const void *a, const void *b);
unsigned pa_idxset_string_hash_func(const void *p);
int pa_idxset_string_compare_func(const void *a, const void *b);
struct pa_idxset;
struct pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b));
void pa_idxset_free(struct pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata);
int pa_idxset_put(struct pa_idxset*s, void *p, uint32_t *index);
void* pa_idxset_get_by_index(struct pa_idxset*s, uint32_t index);
void* pa_idxset_get_by_data(struct pa_idxset*s, void *p, uint32_t *index);
void* pa_idxset_remove_by_index(struct pa_idxset*s, uint32_t index);
void* pa_idxset_remove_by_data(struct pa_idxset*s, void *p, uint32_t *index);
/* This may be used to iterate through all entries. When called with
an invalid index value it returns the first entry, otherwise the
next following. The function is best called with *index =
PA_IDXSET_VALID first. */
void* pa_idxset_rrobin(struct pa_idxset *s, uint32_t *index);
/* Return the oldest entry in the idxset */
void* pa_idxset_first(struct pa_idxset *s, uint32_t *index);
void *pa_idxset_next(struct pa_idxset *s, uint32_t *index);
int pa_idxset_foreach(struct pa_idxset*s, int (*func)(void *p, uint32_t index, int *del, void*userdata), void *userdata);
unsigned pa_idxset_ncontents(struct pa_idxset*s);
int pa_idxset_isempty(struct pa_idxset *s);
#endif

222
polyp/iochannel.c Normal file
View file

@ -0,0 +1,222 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include "iochannel.h"
#include "util.h"
#include "socket-util.h"
struct pa_iochannel {
int ifd, ofd;
struct pa_mainloop_api* mainloop;
void (*callback)(struct pa_iochannel*io, void *userdata);
void*userdata;
int readable;
int writable;
int hungup;
int no_close;
void* input_source, *output_source;
};
static void enable_mainloop_sources(struct pa_iochannel *io) {
assert(io);
if (io->input_source == io->output_source) {
enum pa_mainloop_api_io_events e = PA_MAINLOOP_API_IO_EVENT_NULL;
assert(io->input_source);
if (!io->readable)
e |= PA_MAINLOOP_API_IO_EVENT_INPUT;
if (!io->writable)
e |= PA_MAINLOOP_API_IO_EVENT_OUTPUT;
io->mainloop->enable_io(io->mainloop, io->input_source, e);
} else {
if (io->input_source)
io->mainloop->enable_io(io->mainloop, io->input_source, io->readable ? PA_MAINLOOP_API_IO_EVENT_NULL : PA_MAINLOOP_API_IO_EVENT_INPUT);
if (io->output_source)
io->mainloop->enable_io(io->mainloop, io->output_source, io->writable ? PA_MAINLOOP_API_IO_EVENT_NULL : PA_MAINLOOP_API_IO_EVENT_OUTPUT);
}
}
static void callback(struct pa_mainloop_api* m, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
struct pa_iochannel *io = userdata;
int changed = 0;
assert(m && fd >= 0 && events && userdata);
if ((events & PA_MAINLOOP_API_IO_EVENT_HUP) && !io->hungup) {
io->hungup = 1;
changed = 1;
}
if ((events & PA_MAINLOOP_API_IO_EVENT_INPUT) && !io->readable) {
io->readable = 1;
changed = 1;
assert(id == io->input_source);
}
if ((events & PA_MAINLOOP_API_IO_EVENT_OUTPUT) && !io->writable) {
io->writable = 1;
changed = 1;
assert(id == io->output_source);
}
if (changed) {
enable_mainloop_sources(io);
if (io->callback)
io->callback(io, io->userdata);
}
}
struct pa_iochannel* pa_iochannel_new(struct pa_mainloop_api*m, int ifd, int ofd) {
struct pa_iochannel *io;
assert(m && (ifd >= 0 || ofd >= 0));
io = malloc(sizeof(struct pa_iochannel));
io->ifd = ifd;
io->ofd = ofd;
io->mainloop = m;
io->userdata = NULL;
io->callback = NULL;
io->readable = 0;
io->writable = 0;
io->hungup = 0;
io->no_close = 0;
if (ifd == ofd) {
assert(ifd >= 0);
pa_make_nonblock_fd(io->ifd);
io->input_source = io->output_source = m->source_io(m, ifd, PA_MAINLOOP_API_IO_EVENT_BOTH, callback, io);
} else {
if (ifd >= 0) {
pa_make_nonblock_fd(io->ifd);
io->input_source = m->source_io(m, ifd, PA_MAINLOOP_API_IO_EVENT_INPUT, callback, io);
} else
io->input_source = NULL;
if (ofd >= 0) {
pa_make_nonblock_fd(io->ofd);
io->output_source = m->source_io(m, ofd, PA_MAINLOOP_API_IO_EVENT_OUTPUT, callback, io);
} else
io->output_source = NULL;
}
return io;
}
void pa_iochannel_free(struct pa_iochannel*io) {
assert(io);
if (!io->no_close) {
if (io->ifd >= 0)
close(io->ifd);
if (io->ofd >= 0 && io->ofd != io->ifd)
close(io->ofd);
}
if (io->input_source)
io->mainloop->cancel_io(io->mainloop, io->input_source);
if (io->output_source && (io->output_source != io->input_source))
io->mainloop->cancel_io(io->mainloop, io->output_source);
free(io);
}
int pa_iochannel_is_readable(struct pa_iochannel*io) {
assert(io);
return io->readable;
}
int pa_iochannel_is_writable(struct pa_iochannel*io) {
assert(io);
return io->writable;
}
int pa_iochannel_is_hungup(struct pa_iochannel*io) {
assert(io);
return io->hungup;
}
ssize_t pa_iochannel_write(struct pa_iochannel*io, const void*data, size_t l) {
ssize_t r;
assert(io && data && l && io->ofd >= 0);
if ((r = write(io->ofd, data, l)) >= 0) {
io->writable = 0;
enable_mainloop_sources(io);
}
return r;
}
ssize_t pa_iochannel_read(struct pa_iochannel*io, void*data, size_t l) {
ssize_t r;
assert(io && data && io->ifd >= 0);
if ((r = read(io->ifd, data, l)) >= 0) {
io->readable = 0;
enable_mainloop_sources(io);
}
return r;
}
void pa_iochannel_set_callback(struct pa_iochannel*io, void (*callback)(struct pa_iochannel*io, void *userdata), void *userdata) {
assert(io);
io->callback = callback;
io->userdata = userdata;
}
void pa_iochannel_set_noclose(struct pa_iochannel*io, int b) {
assert(io);
io->no_close = b;
}
void pa_iochannel_socket_peer_to_string(struct pa_iochannel*io, char*s, size_t l) {
assert(io && s && l);
pa_socket_peer_to_string(io->ifd, s, l);
}
int pa_iochannel_socket_set_rcvbuf(struct pa_iochannel *io, size_t l) {
assert(io);
return pa_socket_set_rcvbuf(io->ifd, l);
}
int pa_iochannel_socket_set_sndbuf(struct pa_iochannel *io, size_t l) {
assert(io);
return pa_socket_set_sndbuf(io->ofd, l);
}

50
polyp/iochannel.h Normal file
View file

@ -0,0 +1,50 @@
#ifndef fooiochannelhfoo
#define fooiochannelhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 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 "mainloop-api.h"
/* It is safe to destroy the calling iochannel object from the callback */
struct pa_iochannel;
struct pa_iochannel* pa_iochannel_new(struct pa_mainloop_api*m, int ifd, int ofd);
void pa_iochannel_free(struct pa_iochannel*io);
ssize_t pa_iochannel_write(struct pa_iochannel*io, const void*data, size_t l);
ssize_t pa_iochannel_read(struct pa_iochannel*io, void*data, size_t l);
int pa_iochannel_is_readable(struct pa_iochannel*io);
int pa_iochannel_is_writable(struct pa_iochannel*io);
int pa_iochannel_is_hungup(struct pa_iochannel*io);
void pa_iochannel_set_noclose(struct pa_iochannel*io, int b);
void pa_iochannel_set_callback(struct pa_iochannel*io, void (*callback)(struct pa_iochannel*io, void *userdata), void *userdata);
void pa_iochannel_socket_peer_to_string(struct pa_iochannel*io, char*s, size_t l);
int pa_iochannel_socket_set_rcvbuf(struct pa_iochannel*io, size_t l);
int pa_iochannel_socket_set_sndbuf(struct pa_iochannel*io, size_t l);
#endif

220
polyp/ioline.c Normal file
View file

@ -0,0 +1,220 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "ioline.h"
#define BUFFER_LIMIT (64*1024)
#define READ_SIZE (1024)
struct pa_ioline {
struct pa_iochannel *io;
int dead;
char *wbuf;
size_t wbuf_length, wbuf_index, wbuf_valid_length;
char *rbuf;
size_t rbuf_length, rbuf_index, rbuf_valid_length;
void (*callback)(struct pa_ioline*io, const char *s, void *userdata);
void *userdata;
};
static void io_callback(struct pa_iochannel*io, void *userdata);
static int do_write(struct pa_ioline *l);
struct pa_ioline* pa_ioline_new(struct pa_iochannel *io) {
struct pa_ioline *l;
assert(io);
l = malloc(sizeof(struct pa_ioline));
assert(l);
l->io = io;
l->dead = 0;
l->wbuf = NULL;
l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0;
l->rbuf = NULL;
l->rbuf_length = l->rbuf_index = l->rbuf_valid_length = 0;
l->callback = NULL;
l->userdata = NULL;
pa_iochannel_set_callback(io, io_callback, l);
return l;
}
void pa_ioline_free(struct pa_ioline *l) {
assert(l);
pa_iochannel_free(l->io);
free(l->wbuf);
free(l->rbuf);
free(l);
}
void pa_ioline_puts(struct pa_ioline *l, const char *c) {
size_t len;
assert(l && c);
len = strlen(c);
if (len > BUFFER_LIMIT - l->wbuf_valid_length)
len = BUFFER_LIMIT - l->wbuf_valid_length;
if (!len)
return;
if (len > l->wbuf_length - l->wbuf_valid_length) {
size_t n = l->wbuf_valid_length+len;
char *new = malloc(n);
if (l->wbuf) {
memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
free(l->wbuf);
}
l->wbuf = new;
l->wbuf_length = n;
l->wbuf_index = 0;
} else if (len > l->wbuf_length - l->wbuf_valid_length - l->wbuf_index) {
memmove(l->wbuf, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
l->wbuf_index = 0;
}
memcpy(l->wbuf+l->wbuf_index+l->wbuf_valid_length, c, len);
l->wbuf_valid_length += len;
do_write(l);
}
void pa_ioline_set_callback(struct pa_ioline*l, void (*callback)(struct pa_ioline*io, const char *s, void *userdata), void *userdata) {
assert(l && callback);
l->callback = callback;
l->userdata = userdata;
}
static int do_read(struct pa_ioline *l) {
ssize_t r;
size_t m, len;
char *e;
assert(l);
if (!pa_iochannel_is_readable(l->io))
return 0;
len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
if (len < READ_SIZE) {
size_t n = l->rbuf_valid_length+READ_SIZE;
if (n >= BUFFER_LIMIT)
n = BUFFER_LIMIT;
if (l->rbuf_length >= n) {
if (l->rbuf_valid_length)
memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
} else {
char *new = malloc(n);
if (l->rbuf_valid_length)
memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
free(l->rbuf);
l->rbuf = new;
l->rbuf_length = n;
}
l->rbuf_index = 0;
}
len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
if ((r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0)
return -1;
e = memchr(l->rbuf+l->rbuf_index+l->rbuf_valid_length, '\n', r);
l->rbuf_valid_length += r;
if (!e &&l->rbuf_valid_length >= BUFFER_LIMIT)
e = l->rbuf+BUFFER_LIMIT-1;
if (e) {
char *p;
*e = 0;
p = l->rbuf+l->rbuf_index;
m = strlen(p);
l->rbuf_index += m+1;
l->rbuf_valid_length -= m+1;
if (l->rbuf_valid_length == 0)
l->rbuf_index = 0;
if (l->callback)
l->callback(l, p, l->userdata);
}
return 0;
}
static int do_write(struct pa_ioline *l) {
ssize_t r;
assert(l);
if (!l->wbuf_valid_length || !pa_iochannel_is_writable(l->io))
return 0;
if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0)
return -1;
l->wbuf_valid_length -= r;
if (l->wbuf_valid_length == 0)
l->wbuf_index = 0;
return 0;
}
static void io_callback(struct pa_iochannel*io, void *userdata) {
struct pa_ioline *l = userdata;
assert(io && l);
if (!l->dead && do_write(l) < 0)
goto fail;
if (!l->dead && do_read(l) < 0)
goto fail;
return;
fail:
l->dead = 1;
if (l->callback)
l->callback(l, NULL, l->userdata);
}

35
polyp/ioline.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef fooiolinehfoo
#define fooiolinehfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "iochannel.h"
struct pa_ioline;
struct pa_ioline* pa_ioline_new(struct pa_iochannel *io);
void pa_ioline_free(struct pa_ioline *l);
void pa_ioline_puts(struct pa_ioline *s, const char *c);
void pa_ioline_set_callback(struct pa_ioline*io, void (*callback)(struct pa_ioline*io, const char *s, void *userdata), void *userdata);
#endif

182
polyp/main.c Normal file
View file

@ -0,0 +1,182 @@
/* $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 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 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 <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <stddef.h>
#include <assert.h>
#include <ltdl.h>
#include <memblock.h>
#include "core.h"
#include "mainloop.h"
#include "module.h"
#include "mainloop-signal.h"
#include "cmdline.h"
#include "cli-command.h"
#include "util.h"
#include "sioman.h"
static struct pa_mainloop *mainloop;
static void exit_signal_callback(void *id, int sig, void *userdata) {
struct pa_mainloop_api* m = pa_mainloop_get_api(mainloop);
m->quit(m, 1);
fprintf(stderr, __FILE__": got signal.\n");
}
static void aux_signal_callback(void *id, int sig, void *userdata) {
struct pa_core *c = userdata;
assert(c);
pa_module_load(c, sig == SIGUSR1 ? "module-cli" : "module-cli-protocol-unix", NULL);
}
static void close_pipe(int p[2]) {
if (p[0] != -1)
close(p[0]);
if (p[1] != -1)
close(p[1]);
p[0] = p[1] = -1;
}
int main(int argc, char *argv[]) {
struct pa_core *c;
struct pa_cmdline *cmdline = NULL;
struct pa_strbuf *buf = NULL;
char *s;
int r, retval = 1;
int daemon_pipe[2] = { -1, -1 };
if (!(cmdline = pa_cmdline_parse(argc, argv))) {
fprintf(stderr, __FILE__": failed to parse command line.\n");
goto finish;
}
if (cmdline->help) {
pa_cmdline_help(argv[0]);
retval = 0;
goto finish;
}
if (cmdline->daemonize) {
pid_t child;
if (pa_stdio_acquire() < 0) {
fprintf(stderr, __FILE__": failed to acquire stdio.\n");
goto finish;
}
if (pipe(daemon_pipe) < 0) {
fprintf(stderr, __FILE__": failed to create pipe.\n");
goto finish;
}
if ((child = fork()) < 0) {
fprintf(stderr, __FILE__": fork() failed: %s\n", strerror(errno));
goto finish;
}
if (child != 0) {
/* Father */
close(daemon_pipe[1]);
daemon_pipe[1] = -1;
if (pa_loop_read(daemon_pipe[0], &retval, sizeof(retval)) != sizeof(retval)) {
fprintf(stderr, __FILE__": read() failed: %s\n", strerror(errno));
retval = 1;
}
goto finish;
}
close(daemon_pipe[0]);
daemon_pipe[0] = -1;
setsid();
setpgrp();
}
r = lt_dlinit();
assert(r == 0);
mainloop = pa_mainloop_new();
assert(mainloop);
r = pa_signal_init(pa_mainloop_get_api(mainloop));
assert(r == 0);
pa_signal_register(SIGINT, exit_signal_callback, NULL);
signal(SIGPIPE, SIG_IGN);
c = pa_core_new(pa_mainloop_get_api(mainloop));
assert(c);
pa_signal_register(SIGUSR1, aux_signal_callback, c);
pa_signal_register(SIGUSR2, aux_signal_callback, c);
buf = pa_strbuf_new();
assert(buf);
r = pa_cli_command_execute(c, cmdline->cli_commands, buf, &cmdline->fail, &cmdline->verbose);
fprintf(stderr, s = pa_strbuf_tostring_free(buf));
free(s);
if (r < 0 && cmdline->fail) {
fprintf(stderr, __FILE__": failed to initialize daemon.\n");
if (cmdline->daemonize)
pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
} else if (!c->modules || pa_idxset_ncontents(c->modules) == 0) {
fprintf(stderr, __FILE__": daemon startup without any loaded modules, refusing to work.\n");
if (cmdline->daemonize)
pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
} else {
retval = 0;
if (cmdline->daemonize)
pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
fprintf(stderr, __FILE__": mainloop entry.\n");
if (pa_mainloop_run(mainloop, &retval) < 0)
retval = 1;
fprintf(stderr, __FILE__": mainloop exit.\n");
}
pa_core_free(c);
pa_signal_done();
pa_mainloop_free(mainloop);
lt_dlexit();
finish:
if (cmdline)
pa_cmdline_free(cmdline);
close_pipe(daemon_pipe);
return retval;
}

60
polyp/mainloop-api.c Normal file
View file

@ -0,0 +1,60 @@
/* $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 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 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"
struct once_info {
void (*callback)(void *userdata);
void *userdata;
};
static void once_callback(struct pa_mainloop_api *api, void *id, void *userdata) {
struct once_info *i = userdata;
assert(api && i && i->callback);
i->callback(i->userdata);
assert(api->cancel_fixed);
api->cancel_fixed(api, id);
free(i);
}
void pa_mainloop_api_once(struct pa_mainloop_api* api, void (*callback)(void *userdata), void *userdata) {
struct once_info *i;
void *id;
assert(api && callback);
i = malloc(sizeof(struct once_info));
assert(i);
i->callback = callback;
i->userdata = userdata;
assert(api->source_fixed);
id = api->source_fixed(api, once_callback, i);
assert(id);
/* Note: if the mainloop is destroyed before once_callback() was called, some memory is leaked. */
}

65
polyp/mainloop-api.h Normal file
View file

@ -0,0 +1,65 @@
#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 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 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 <time.h>
#include <sys/time.h>
enum pa_mainloop_api_io_events {
PA_MAINLOOP_API_IO_EVENT_NULL = 0,
PA_MAINLOOP_API_IO_EVENT_INPUT = 1,
PA_MAINLOOP_API_IO_EVENT_OUTPUT = 2,
PA_MAINLOOP_API_IO_EVENT_BOTH = 3,
PA_MAINLOOP_API_IO_EVENT_HUP = 4
};
struct pa_mainloop_api {
void *userdata;
/* IO sources */
void* (*source_io)(struct pa_mainloop_api*a, int fd, enum pa_mainloop_api_io_events events, void (*callback) (struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata);
void (*enable_io)(struct pa_mainloop_api*a, void* id, enum pa_mainloop_api_io_events events);
void (*cancel_io)(struct pa_mainloop_api*a, void* id);
/* Fixed sources */
void* (*source_fixed)(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, void *id, void *userdata), void *userdata);
void (*enable_fixed)(struct pa_mainloop_api*a, void* id, int b);
void (*cancel_fixed)(struct pa_mainloop_api*a, void* id);
/* Idle sources */
void* (*source_idle)(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, void *id, void *userdata), void *userdata);
void (*enable_idle)(struct pa_mainloop_api*a, void* id, int b);
void (*cancel_idle)(struct pa_mainloop_api*a, void* id);
/* Time sources */
void* (*source_time)(struct pa_mainloop_api*a, const struct timeval *tv, void (*callback) (struct pa_mainloop_api*a, void *id, const struct timeval *tv, void *userdata), void *userdata);
void (*enable_time)(struct pa_mainloop_api*a, void *id, const struct timeval *tv);
void (*cancel_time)(struct pa_mainloop_api*a, void* id);
/* Exit mainloop */
void (*quit)(struct pa_mainloop_api*a, int retval);
};
void pa_mainloop_api_once(struct pa_mainloop_api*m, void (*callback)(void *userdata), void *userdata);
#endif

163
polyp/mainloop-signal.c Normal file
View file

@ -0,0 +1,163 @@
/* $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 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 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 "mainloop-signal.h"
#include "util.h"
struct signal_info {
int sig;
struct sigaction saved_sigaction;
void (*callback) (void *id, int signal, void *userdata);
void *userdata;
struct signal_info *previous, *next;
};
static struct pa_mainloop_api *api = NULL;
static int signal_pipe[2] = { -1, -1 };
static void* mainloop_source = NULL;
static struct signal_info *signals = NULL;
static void signal_handler(int sig) {
write(signal_pipe[1], &sig, sizeof(sig));
}
static void callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
assert(a && id && events == PA_MAINLOOP_API_IO_EVENT_INPUT && id == mainloop_source && fd == signal_pipe[0]);
for (;;) {
ssize_t r;
int sig;
struct signal_info*s;
if ((r = read(signal_pipe[0], &sig, sizeof(sig))) < 0) {
if (errno == EAGAIN)
return;
fprintf(stderr, "signal.c: read(): %s\n", strerror(errno));
return;
}
if (r != sizeof(sig)) {
fprintf(stderr, "signal.c: short read()\n");
return;
}
for (s = signals; s; s = s->next)
if (s->sig == sig) {
assert(s->callback);
s->callback(s, sig, s->userdata);
break;
}
}
}
int pa_signal_init(struct pa_mainloop_api *a) {
assert(a);
if (pipe(signal_pipe) < 0) {
fprintf(stderr, "pipe() failed: %s\n", strerror(errno));
return -1;
}
pa_make_nonblock_fd(signal_pipe[0]);
pa_make_nonblock_fd(signal_pipe[1]);
api = a;
mainloop_source = api->source_io(api, signal_pipe[0], PA_MAINLOOP_API_IO_EVENT_INPUT, callback, NULL);
assert(mainloop_source);
return 0;
}
void pa_signal_done(void) {
assert(api && signal_pipe[0] >= 0 && signal_pipe[1] >= 0 && mainloop_source);
api->cancel_io(api, mainloop_source);
mainloop_source = NULL;
close(signal_pipe[0]);
close(signal_pipe[1]);
signal_pipe[0] = signal_pipe[1] = -1;
while (signals)
pa_signal_unregister(signals);
api = NULL;
}
void* pa_signal_register(int sig, void (*callback) (void *id, int signal, void *userdata), void *userdata) {
struct signal_info *s = NULL;
struct sigaction sa;
assert(sig > 0 && callback);
for (s = signals; s; s = s->next)
if (s->sig == sig)
goto fail;
s = malloc(sizeof(struct signal_info));
assert(s);
s->sig = sig;
s->callback = callback;
s->userdata = userdata;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(sig, &sa, &s->saved_sigaction) < 0)
goto fail;
s->previous = NULL;
s->next = signals;
signals = s;
return s;
fail:
if (s)
free(s);
return NULL;
}
void pa_signal_unregister(void *id) {
struct signal_info *s = id;
assert(s);
if (s->next)
s->next->previous = s->previous;
if (s->previous)
s->previous->next = s->next;
else
signals = s->next;
sigaction(s->sig, &s->saved_sigaction, NULL);
free(s);
}

33
polyp/mainloop-signal.h Normal file
View file

@ -0,0 +1,33 @@
#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 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 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 "mainloop-api.h"
int pa_signal_init(struct pa_mainloop_api *api);
void pa_signal_done(void);
void* pa_signal_register(int signal, void (*callback) (void *id, int signal, void *userdata), void *userdata);
void pa_signal_unregister(void *id);
#endif

553
polyp/mainloop.c Normal file
View file

@ -0,0 +1,553 @@
/* $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 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 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 <sys/poll.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <errno.h>
#include "mainloop.h"
#include "util.h"
#include "idxset.h"
struct mainloop_source_header {
struct pa_mainloop *mainloop;
int dead;
};
struct mainloop_source_io {
struct mainloop_source_header header;
int fd;
enum pa_mainloop_api_io_events events;
void (*callback) (struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata);
void *userdata;
struct pollfd *pollfd;
};
struct mainloop_source_fixed_or_idle {
struct mainloop_source_header header;
int enabled;
void (*callback)(struct pa_mainloop_api*a, void *id, void *userdata);
void *userdata;
};
struct mainloop_source_time {
struct mainloop_source_header header;
int enabled;
struct timeval timeval;
void (*callback)(struct pa_mainloop_api*a, void *id, const struct timeval*tv, void *userdata);
void *userdata;
};
struct pa_mainloop {
struct pa_idxset *io_sources, *fixed_sources, *idle_sources, *time_sources;
int io_sources_scan_dead, fixed_sources_scan_dead, idle_sources_scan_dead, time_sources_scan_dead;
struct pollfd *pollfds;
unsigned max_pollfds, n_pollfds;
int rebuild_pollfds;
int quit, running, retval;
struct pa_mainloop_api api;
};
static void setup_api(struct pa_mainloop *m);
struct pa_mainloop *pa_mainloop_new(void) {
struct pa_mainloop *m;
m = malloc(sizeof(struct pa_mainloop));
assert(m);
m->io_sources = pa_idxset_new(NULL, NULL);
m->fixed_sources = pa_idxset_new(NULL, NULL);
m->idle_sources = pa_idxset_new(NULL, NULL);
m->time_sources = pa_idxset_new(NULL, NULL);
assert(m->io_sources && m->fixed_sources && m->idle_sources && m->time_sources);
m->io_sources_scan_dead = m->fixed_sources_scan_dead = m->idle_sources_scan_dead = m->time_sources_scan_dead = 0;
m->pollfds = NULL;
m->max_pollfds = m->n_pollfds = m->rebuild_pollfds = 0;
m->quit = m->running = m->retval = 0;
setup_api(m);
return m;
}
static int foreach(void *p, uint32_t index, int *del, void*userdata) {
struct mainloop_source_header *h = p;
int *all = userdata;
assert(p && del && all);
if (*all || h->dead) {
free(h);
*del = 1;
}
return 0;
};
void pa_mainloop_free(struct pa_mainloop* m) {
int all = 1;
assert(m);
pa_idxset_foreach(m->io_sources, foreach, &all);
pa_idxset_foreach(m->fixed_sources, foreach, &all);
pa_idxset_foreach(m->idle_sources, foreach, &all);
pa_idxset_foreach(m->time_sources, foreach, &all);
pa_idxset_free(m->io_sources, NULL, NULL);
pa_idxset_free(m->fixed_sources, NULL, NULL);
pa_idxset_free(m->idle_sources, NULL, NULL);
pa_idxset_free(m->time_sources, NULL, NULL);
free(m->pollfds);
free(m);
}
static void scan_dead(struct pa_mainloop *m) {
int all = 0;
assert(m);
if (m->io_sources_scan_dead)
pa_idxset_foreach(m->io_sources, foreach, &all);
if (m->fixed_sources_scan_dead)
pa_idxset_foreach(m->fixed_sources, foreach, &all);
if (m->idle_sources_scan_dead)
pa_idxset_foreach(m->idle_sources, foreach, &all);
if (m->time_sources_scan_dead)
pa_idxset_foreach(m->time_sources, foreach, &all);
}
static void rebuild_pollfds(struct pa_mainloop *m) {
struct mainloop_source_io*s;
struct pollfd *p;
uint32_t index = PA_IDXSET_INVALID;
unsigned l;
l = pa_idxset_ncontents(m->io_sources);
if (m->max_pollfds < l) {
m->pollfds = realloc(m->pollfds, sizeof(struct pollfd)*l);
m->max_pollfds = l;
}
m->n_pollfds = 0;
p = m->pollfds;
for (s = pa_idxset_first(m->io_sources, &index); s; s = pa_idxset_next(m->io_sources, &index)) {
if (s->header.dead) {
s->pollfd = NULL;
continue;
}
s->pollfd = p;
p->fd = s->fd;
p->events = ((s->events & PA_MAINLOOP_API_IO_EVENT_INPUT) ? POLLIN : 0) | ((s->events & PA_MAINLOOP_API_IO_EVENT_OUTPUT) ? POLLOUT : 0);
p->revents = 0;
p++;
m->n_pollfds++;
}
}
static void dispatch_pollfds(struct pa_mainloop *m) {
uint32_t index = PA_IDXSET_INVALID;
struct mainloop_source_io *s;
for (s = pa_idxset_first(m->io_sources, &index); s; s = pa_idxset_next(m->io_sources, &index)) {
if (s->header.dead || !s->pollfd || !s->pollfd->revents)
continue;
assert(s->pollfd->fd == s->fd && s->callback);
s->callback(&m->api, s, s->fd,
((s->pollfd->revents & POLLHUP) ? PA_MAINLOOP_API_IO_EVENT_HUP : 0) |
((s->pollfd->revents & POLLIN) ? PA_MAINLOOP_API_IO_EVENT_INPUT : 0) |
((s->pollfd->revents & POLLOUT) ? PA_MAINLOOP_API_IO_EVENT_OUTPUT : 0), s->userdata);
s->pollfd->revents = 0;
}
}
static void run_fixed_or_idle(struct pa_mainloop *m, struct pa_idxset *i) {
uint32_t index = PA_IDXSET_INVALID;
struct mainloop_source_fixed_or_idle *s;
for (s = pa_idxset_first(i, &index); s; s = pa_idxset_next(i, &index)) {
if (s->header.dead || !s->enabled)
continue;
assert(s->callback);
s->callback(&m->api, s, s->userdata);
}
}
static int calc_next_timeout(struct pa_mainloop *m) {
uint32_t index = PA_IDXSET_INVALID;
struct mainloop_source_time *s;
struct timeval now;
int t = -1;
if (pa_idxset_isempty(m->time_sources))
return -1;
gettimeofday(&now, NULL);
for (s = pa_idxset_first(m->time_sources, &index); s; s = pa_idxset_next(m->time_sources, &index)) {
int tmp;
if (s->header.dead || !s->enabled)
continue;
if (s->timeval.tv_sec < now.tv_sec || (s->timeval.tv_sec == now.tv_sec && s->timeval.tv_usec <= now.tv_usec))
return 0;
tmp = (s->timeval.tv_sec - now.tv_sec)*1000;
if (s->timeval.tv_usec > now.tv_usec)
tmp += (s->timeval.tv_usec - now.tv_usec)/1000;
else
tmp -= (now.tv_usec - s->timeval.tv_usec)/1000;
if (tmp == 0)
return 0;
else if (t == -1 || tmp < t)
t = tmp;
}
return t;
}
static void dispatch_timeout(struct pa_mainloop *m) {
uint32_t index = PA_IDXSET_INVALID;
struct mainloop_source_time *s;
struct timeval now;
assert(m);
if (pa_idxset_isempty(m->time_sources))
return;
gettimeofday(&now, NULL);
for (s = pa_idxset_first(m->time_sources, &index); s; s = pa_idxset_next(m->time_sources, &index)) {
if (s->header.dead || !s->enabled)
continue;
if (s->timeval.tv_sec < now.tv_sec || (s->timeval.tv_sec == now.tv_sec && s->timeval.tv_usec <= now.tv_usec)) {
assert(s->callback);
s->enabled = 0;
s->callback(&m->api, s, &s->timeval, s->userdata);
}
}
}
static int any_idle_sources(struct pa_mainloop *m) {
struct mainloop_source_fixed_or_idle *s;
uint32_t index;
assert(m);
for (s = pa_idxset_first(m->idle_sources, &index); s; s = pa_idxset_next(m->idle_sources, &index))
if (!s->header.dead && s->enabled)
return 1;
return 0;
}
int pa_mainloop_iterate(struct pa_mainloop *m, int block, int *retval) {
int r, idle;
assert(m && !m->running);
if(m->quit) {
if (retval)
*retval = m->retval;
return 1;
}
m->running = 1;
scan_dead(m);
run_fixed_or_idle(m, m->fixed_sources);
if (m->rebuild_pollfds) {
rebuild_pollfds(m);
m->rebuild_pollfds = 0;
}
idle = any_idle_sources(m);
do {
int t;
if (!block || idle)
t = 0;
else
t = calc_next_timeout(m);
r = poll(m->pollfds, m->n_pollfds, t);
} while (r < 0 && errno == EINTR);
dispatch_timeout(m);
if (r > 0)
dispatch_pollfds(m);
else if (r == 0 && idle)
run_fixed_or_idle(m, m->idle_sources);
else if (r < 0)
fprintf(stderr, "select(): %s\n", strerror(errno));
m->running = 0;
return r < 0 ? -1 : 0;
}
int pa_mainloop_run(struct pa_mainloop *m, int *retval) {
int r;
while ((r = pa_mainloop_iterate(m, 1, retval)) == 0);
return r;
}
void pa_mainloop_quit(struct pa_mainloop *m, int r) {
assert(m);
m->quit = r;
}
/* IO sources */
static void* mainloop_source_io(struct pa_mainloop_api*a, int fd, enum pa_mainloop_api_io_events events, void (*callback) (struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata) {
struct pa_mainloop *m;
struct mainloop_source_io *s;
assert(a && a->userdata && fd >= 0 && callback);
m = a->userdata;
assert(a == &m->api);
s = malloc(sizeof(struct mainloop_source_io));
assert(s);
s->header.mainloop = m;
s->header.dead = 0;
s->fd = fd;
s->events = events;
s->callback = callback;
s->userdata = userdata;
s->pollfd = NULL;
pa_idxset_put(m->io_sources, s, NULL);
m->rebuild_pollfds = 1;
return s;
}
static void mainloop_enable_io(struct pa_mainloop_api*a, void* id, enum pa_mainloop_api_io_events events) {
struct pa_mainloop *m;
struct mainloop_source_io *s = id;
assert(a && a->userdata && s && !s->header.dead);
m = a->userdata;
assert(a == &m->api && s->header.mainloop == m);
s->events = events;
if (s->pollfd)
s->pollfd->events = ((s->events & PA_MAINLOOP_API_IO_EVENT_INPUT) ? POLLIN : 0) | ((s->events & PA_MAINLOOP_API_IO_EVENT_OUTPUT) ? POLLOUT : 0);
}
static void mainloop_cancel_io(struct pa_mainloop_api*a, void* id) {
struct pa_mainloop *m;
struct mainloop_source_io *s = id;
assert(a && a->userdata && s && !s->header.dead);
m = a->userdata;
assert(a == &m->api && s->header.mainloop == m);
s->header.dead = 1;
m->io_sources_scan_dead = 1;
m->rebuild_pollfds = 1;
}
/* Fixed sources */
static void* mainloop_source_fixed(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, void *id, void *userdata), void *userdata) {
struct pa_mainloop *m;
struct mainloop_source_fixed_or_idle *s;
assert(a && a->userdata && callback);
m = a->userdata;
assert(a == &m->api);
s = malloc(sizeof(struct mainloop_source_fixed_or_idle));
assert(s);
s->header.mainloop = m;
s->header.dead = 0;
s->enabled = 1;
s->callback = callback;
s->userdata = userdata;
pa_idxset_put(m->fixed_sources, s, NULL);
return s;
}
static void mainloop_enable_fixed(struct pa_mainloop_api*a, void* id, int b) {
struct pa_mainloop *m;
struct mainloop_source_fixed_or_idle *s = id;
assert(a && a->userdata && s && !s->header.dead);
m = a->userdata;
assert(a == &m->api);
s->enabled = b;
}
static void mainloop_cancel_fixed(struct pa_mainloop_api*a, void* id) {
struct pa_mainloop *m;
struct mainloop_source_fixed_or_idle *s = id;
assert(a && a->userdata && s && !s->header.dead);
m = a->userdata;
assert(a == &m->api);
s->header.dead = 1;
m->fixed_sources_scan_dead = 1;
}
/* Idle sources */
static void* mainloop_source_idle(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, void *id, void *userdata), void *userdata) {
struct pa_mainloop *m;
struct mainloop_source_fixed_or_idle *s;
assert(a && a->userdata && callback);
m = a->userdata;
assert(a == &m->api);
s = malloc(sizeof(struct mainloop_source_fixed_or_idle));
assert(s);
s->header.mainloop = m;
s->header.dead = 0;
s->enabled = 1;
s->callback = callback;
s->userdata = userdata;
pa_idxset_put(m->idle_sources, s, NULL);
return s;
}
static void mainloop_cancel_idle(struct pa_mainloop_api*a, void* id) {
struct pa_mainloop *m;
struct mainloop_source_fixed_or_idle *s = id;
assert(a && a->userdata && s && !s->header.dead);
m = a->userdata;
assert(a == &m->api);
s->header.dead = 1;
m->idle_sources_scan_dead = 1;
}
/* Time sources */
static void* mainloop_source_time(struct pa_mainloop_api*a, const struct timeval *tv, void (*callback) (struct pa_mainloop_api*a, void *id, const struct timeval *tv, void *userdata), void *userdata) {
struct pa_mainloop *m;
struct mainloop_source_time *s;
assert(a && a->userdata && callback);
m = a->userdata;
assert(a == &m->api);
s = malloc(sizeof(struct mainloop_source_time));
assert(s);
s->header.mainloop = m;
s->header.dead = 0;
s->enabled = !!tv;
if (tv)
s->timeval = *tv;
s->callback = callback;
s->userdata = userdata;
pa_idxset_put(m->time_sources, s, NULL);
return s;
}
static void mainloop_enable_time(struct pa_mainloop_api*a, void *id, const struct timeval *tv) {
struct pa_mainloop *m;
struct mainloop_source_time *s = id;
assert(a && a->userdata && s && !s->header.dead);
m = a->userdata;
assert(a == &m->api);
if (tv) {
s->enabled = 1;
s->timeval = *tv;
} else
s->enabled = 0;
}
static void mainloop_cancel_time(struct pa_mainloop_api*a, void* id) {
struct pa_mainloop *m;
struct mainloop_source_time *s = id;
assert(a && a->userdata && s && !s->header.dead);
m = a->userdata;
assert(a == &m->api);
s->header.dead = 1;
m->time_sources_scan_dead = 1;
}
static void mainloop_quit(struct pa_mainloop_api*a, int retval) {
struct pa_mainloop *m;
assert(a && a->userdata);
m = a->userdata;
assert(a == &m->api);
m->quit = 1;
m->retval = retval;
}
static void setup_api(struct pa_mainloop *m) {
assert(m);
m->api.userdata = m;
m->api.source_io = mainloop_source_io;
m->api.enable_io = mainloop_enable_io;
m->api.cancel_io = mainloop_cancel_io;
m->api.source_fixed = mainloop_source_fixed;
m->api.enable_fixed = mainloop_enable_fixed;
m->api.cancel_fixed = mainloop_cancel_fixed;
m->api.source_idle = mainloop_source_idle;
m->api.enable_idle = mainloop_enable_fixed; /* (!) */
m->api.cancel_idle = mainloop_cancel_idle;
m->api.source_time = mainloop_source_time;
m->api.enable_time = mainloop_enable_time;
m->api.cancel_time = mainloop_cancel_time;
m->api.quit = mainloop_quit;
}
struct pa_mainloop_api* pa_mainloop_get_api(struct pa_mainloop*m) {
assert(m);
return &m->api;
}

37
polyp/mainloop.h Normal file
View file

@ -0,0 +1,37 @@
#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 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 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 "mainloop-api.h"
struct pa_mainloop;
struct pa_mainloop *pa_mainloop_new(void);
void pa_mainloop_free(struct pa_mainloop* m);
int pa_mainloop_iterate(struct pa_mainloop *m, int block, int *retval);
int pa_mainloop_run(struct pa_mainloop *m, int *retval);
struct pa_mainloop_api* pa_mainloop_get_api(struct pa_mainloop*m);
#endif

113
polyp/memblock.c Normal file
View file

@ -0,0 +1,113 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "memblock.h"
static unsigned memblock_count = 0, memblock_total = 0;
struct pa_memblock *pa_memblock_new(size_t length) {
struct pa_memblock *b = malloc(sizeof(struct pa_memblock)+length);
b->type = PA_MEMBLOCK_APPENDED;
b->ref = 1;
b->length = length;
b->data = b+1;
memblock_count++;
memblock_total += length;
return b;
}
struct pa_memblock *pa_memblock_new_fixed(void *d, size_t length) {
struct pa_memblock *b = malloc(sizeof(struct pa_memblock));
b->type = PA_MEMBLOCK_FIXED;
b->ref = 1;
b->length = length;
b->data = d;
memblock_count++;
memblock_total += length;
return b;
}
struct pa_memblock *pa_memblock_new_dynamic(void *d, size_t length) {
struct pa_memblock *b = malloc(sizeof(struct pa_memblock));
b->type = PA_MEMBLOCK_DYNAMIC;
b->ref = 1;
b->length = length;
b->data = d;
memblock_count++;
memblock_total += length;
return b;
}
struct pa_memblock* pa_memblock_ref(struct pa_memblock*b) {
assert(b && b->ref >= 1);
b->ref++;
return b;
}
void pa_memblock_unref(struct pa_memblock*b) {
assert(b && b->ref >= 1);
b->ref--;
if (b->ref == 0) {
if (b->type == PA_MEMBLOCK_DYNAMIC)
free(b->data);
memblock_count--;
memblock_total -= b->length;
free(b);
}
}
void pa_memblock_unref_fixed(struct pa_memblock *b) {
void *d;
assert(b && b->ref >= 1);
if (b->ref == 1) {
pa_memblock_unref(b);
return;
} else {
d = malloc(b->length);
assert(d);
memcpy(d, b->data, b->length);
b->data = d;
b->type = PA_MEMBLOCK_DYNAMIC;
b->ref--;
}
}
unsigned pa_memblock_get_count(void) {
return memblock_count;
}
unsigned pa_memblock_get_total(void) {
return memblock_total;
}

49
polyp/memblock.h Normal file
View file

@ -0,0 +1,49 @@
#ifndef foomemblockhfoo
#define foomemblockhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <sys/types.h>
#include <inttypes.h>
enum pa_memblock_type { PA_MEMBLOCK_FIXED, PA_MEMBLOCK_APPENDED, PA_MEMBLOCK_DYNAMIC };
struct pa_memblock {
enum pa_memblock_type type;
unsigned ref;
size_t length;
void *data;
};
struct pa_memblock *pa_memblock_new(size_t length);
struct pa_memblock *pa_memblock_new_fixed(void *data, size_t length);
struct pa_memblock *pa_memblock_new_dynamic(void *data, size_t length);
void pa_memblock_unref(struct pa_memblock*b);
struct pa_memblock* pa_memblock_ref(struct pa_memblock*b);
void pa_memblock_unref_fixed(struct pa_memblock*b);
unsigned pa_memblock_get_count(void);
unsigned pa_memblock_get_total(void);
#endif

326
polyp/memblockq.c Normal file
View file

@ -0,0 +1,326 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "memblockq.h"
struct memblock_list {
struct memblock_list *next;
struct pa_memchunk chunk;
struct timeval stamp;
};
struct pa_memblockq {
struct memblock_list *blocks, *blocks_tail;
unsigned n_blocks;
size_t current_length, maxlength, tlength, base, prebuf, minreq;
int measure_delay;
uint32_t delay;
struct pa_mcalign *mcalign;
};
struct pa_memblockq* pa_memblockq_new(size_t maxlength, size_t tlength, size_t base, size_t prebuf, size_t minreq) {
struct pa_memblockq* bq;
assert(maxlength && base && maxlength);
bq = malloc(sizeof(struct pa_memblockq));
assert(bq);
bq->blocks = bq->blocks_tail = 0;
bq->n_blocks = 0;
bq->current_length = 0;
fprintf(stderr, "memblockq requested: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", maxlength, tlength, base, prebuf, minreq);
bq->base = base;
bq->maxlength = ((maxlength+base-1)/base)*base;
assert(bq->maxlength >= base);
bq->tlength = ((tlength+base-1)/base)*base;
if (bq->tlength == 0 || bq->tlength >= bq->maxlength)
bq->tlength = bq->maxlength;
bq->prebuf = (prebuf == (size_t) -1) ? bq->maxlength/2 : prebuf;
bq->prebuf = (bq->prebuf/base)*base;
if (bq->prebuf > bq->maxlength)
bq->prebuf = bq->maxlength;
bq->minreq = (minreq/base)*base;
if (bq->minreq == 0)
bq->minreq = 1;
fprintf(stderr, "memblockq sanitized: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", bq->maxlength, bq->tlength, bq->base, bq->prebuf, bq->minreq);
bq->measure_delay = 0;
bq->delay = 0;
bq->mcalign = NULL;
return bq;
}
void pa_memblockq_free(struct pa_memblockq* bq) {
struct memblock_list *l;
assert(bq);
if (bq->mcalign)
pa_mcalign_free(bq->mcalign);
while ((l = bq->blocks)) {
bq->blocks = l->next;
pa_memblock_unref(l->chunk.memblock);
free(l);
}
free(bq);
}
void pa_memblockq_push(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta) {
struct memblock_list *q;
assert(bq && chunk && chunk->memblock && chunk->length && (chunk->length % bq->base) == 0);
if (bq->blocks_tail && bq->blocks_tail->chunk.memblock == chunk->memblock) {
/* Try to merge memory chunks */
if (bq->blocks_tail->chunk.index+bq->blocks_tail->chunk.length == chunk->index) {
bq->blocks_tail->chunk.length += chunk->length;
bq->current_length += chunk->length;
/* fprintf(stderr, __FILE__": merge succeeded: %u\n", chunk->length);*/
return;
}
}
q = malloc(sizeof(struct memblock_list));
assert(q);
if (bq->measure_delay)
gettimeofday(&q->stamp, NULL);
else
timerclear(&q->stamp);
q->chunk = *chunk;
pa_memblock_ref(q->chunk.memblock);
assert(q->chunk.index+q->chunk.length <= q->chunk.memblock->length);
q->next = NULL;
if (bq->blocks_tail)
bq->blocks_tail->next = q;
else
bq->blocks = q;
bq->blocks_tail = q;
bq->n_blocks++;
bq->current_length += chunk->length;
pa_memblockq_shorten(bq, bq->maxlength);
}
int pa_memblockq_peek(struct pa_memblockq* bq, struct pa_memchunk *chunk) {
assert(bq && chunk);
if (!bq->blocks || bq->current_length < bq->prebuf)
return -1;
bq->prebuf = 0;
*chunk = bq->blocks->chunk;
pa_memblock_ref(chunk->memblock);
/* if (chunk->memblock->ref != 2) */
/* fprintf(stderr, "block %p with ref %u peeked.\n", chunk->memblock, chunk->memblock->ref); */
return 0;
}
/*
int memblockq_pop(struct memblockq* bq, struct pa_memchunk *chunk) {
struct memblock_list *q;
assert(bq && chunk);
if (!bq->blocks || bq->current_length < bq->prebuf)
return -1;
bq->prebuf = 0;
q = bq->blocks;
bq->blocks = bq->blocks->next;
*chunk = q->chunk;
bq->n_blocks--;
bq->current_length -= chunk->length;
free(q);
return 0;
}
*/
static uint32_t age(struct timeval *tv) {
assert(tv);
struct timeval now;
uint32_t r;
if (tv->tv_sec == 0)
return 0;
gettimeofday(&now, NULL);
r = (now.tv_sec-tv->tv_sec) * 1000000;
if (now.tv_usec >= tv->tv_usec)
r += now.tv_usec - tv->tv_usec;
else
r -= tv->tv_usec - now.tv_usec;
return r;
}
void pa_memblockq_drop(struct pa_memblockq *bq, size_t length) {
assert(bq && length && (length % bq->base) == 0);
while (length > 0) {
size_t l = length;
assert(bq->blocks && bq->current_length >= length);
if (l > bq->blocks->chunk.length)
l = bq->blocks->chunk.length;
if (bq->measure_delay)
bq->delay = age(&bq->blocks->stamp);
bq->blocks->chunk.index += l;
bq->blocks->chunk.length -= l;
bq->current_length -= l;
if (bq->blocks->chunk.length == 0) {
struct memblock_list *q;
q = bq->blocks;
bq->blocks = bq->blocks->next;
if (bq->blocks == NULL)
bq->blocks_tail = NULL;
pa_memblock_unref(q->chunk.memblock);
free(q);
bq->n_blocks--;
}
length -= l;
}
}
void pa_memblockq_shorten(struct pa_memblockq *bq, size_t length) {
size_t l;
assert(bq);
if (bq->current_length <= length)
return;
fprintf(stderr, "Warning! pa_memblockq_shorten()\n");
l = bq->current_length - length;
l /= bq->base;
l *= bq->base;
pa_memblockq_drop(bq, l);
}
void pa_memblockq_empty(struct pa_memblockq *bq) {
assert(bq);
pa_memblockq_shorten(bq, 0);
}
int pa_memblockq_is_readable(struct pa_memblockq *bq) {
assert(bq);
return bq->current_length && (bq->current_length >= bq->prebuf);
}
int pa_memblockq_is_writable(struct pa_memblockq *bq, size_t length) {
assert(bq);
return bq->current_length + length <= bq->tlength;
}
uint32_t pa_memblockq_get_delay(struct pa_memblockq *bq) {
assert(bq);
return bq->delay;
}
uint32_t pa_memblockq_get_length(struct pa_memblockq *bq) {
assert(bq);
return bq->current_length;
}
uint32_t pa_memblockq_missing(struct pa_memblockq *bq) {
size_t l;
assert(bq);
if (bq->current_length >= bq->tlength)
return 0;
l = bq->tlength - bq->current_length;
assert(l);
return (l >= bq->minreq) ? l : 0;
}
void pa_memblockq_push_align(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta) {
struct pa_memchunk rchunk;
assert(bq && chunk && bq->base);
if (bq->base == 1) {
pa_memblockq_push(bq, chunk, delta);
return;
}
if (!bq->mcalign) {
bq->mcalign = pa_mcalign_new(bq->base);
assert(bq->mcalign);
}
pa_mcalign_push(bq->mcalign, chunk);
while (pa_mcalign_pop(bq->mcalign, &rchunk) >= 0) {
pa_memblockq_push(bq, &rchunk, delta);
pa_memblock_unref(rchunk.memblock);
delta = 0;
}
}
uint32_t pa_memblockq_get_minreq(struct pa_memblockq *bq) {
assert(bq);
return bq->minreq;
}

82
polyp/memblockq.h Normal file
View file

@ -0,0 +1,82 @@
#ifndef foomemblockqhfoo
#define foomemblockqhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <sys/types.h>
#include "memblock.h"
#include "memchunk.h"
struct pa_memblockq;
/* Parameters:
- maxlength: maximum length of queue. If more data is pushed into the queue, data from the front is dropped
- length: the target length of the queue.
- base: a base value for all metrics. Only multiples of this value are popped from the queue
- prebuf: before passing the first byte out, make sure that enough bytes are in the queue
- minreq: pa_memblockq_missing() will only return values greater than this value
*/
struct pa_memblockq* pa_memblockq_new(size_t maxlength,
size_t tlength,
size_t base,
size_t prebuf,
size_t minreq);
void pa_memblockq_free(struct pa_memblockq*bq);
/* Push a new memory chunk into the queue. Optionally specify a value for future cancellation. This is currently not implemented, however! */
void pa_memblockq_push(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta);
/* Same as pa_memblockq_push(), however chunks are filtered through a mcalign object, and thus aligned to multiples of base */
void pa_memblockq_push_align(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta);
/* Return a copy of the next memory chunk in the queue. It is not removed from the queue */
int pa_memblockq_peek(struct pa_memblockq* bq, struct pa_memchunk *chunk);
/* Drop the specified bytes from the queue */
void pa_memblockq_drop(struct pa_memblockq *bq, size_t length);
/* Shorten the pa_memblockq to the specified length by dropping data at the end of the queue */
void pa_memblockq_shorten(struct pa_memblockq *bq, size_t length);
/* Empty the pa_memblockq */
void pa_memblockq_empty(struct pa_memblockq *bq);
/* Test if the pa_memblockq is currently readable, that is, more data than base */
int pa_memblockq_is_readable(struct pa_memblockq *bq);
/* Test if the pa_memblockq is currently writable for the specified amount of bytes */
int pa_memblockq_is_writable(struct pa_memblockq *bq, size_t length);
/* The time memory chunks stay in the queue until they are removed completely in usecs */
uint32_t pa_memblockq_get_delay(struct pa_memblockq *bq);
/* Return the length of the queue in bytes */
uint32_t pa_memblockq_get_length(struct pa_memblockq *bq);
/* Return how many bytes are missing in queue to the specified fill amount */
uint32_t pa_memblockq_missing(struct pa_memblockq *bq);
uint32_t pa_memblockq_get_minreq(struct pa_memblockq *bq);
#endif

149
polyp/memchunk.c Normal file
View file

@ -0,0 +1,149 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "memchunk.h"
void pa_memchunk_make_writable(struct pa_memchunk *c) {
struct pa_memblock *n;
assert(c && c->memblock && c->memblock->ref >= 1);
if (c->memblock->ref == 1)
return;
n = pa_memblock_new(c->length);
assert(n);
memcpy(n->data, c->memblock->data+c->index, c->length);
pa_memblock_unref(c->memblock);
c->memblock = n;
c->index = 0;
}
struct pa_mcalign {
size_t base;
struct pa_memchunk chunk;
uint8_t *buffer;
size_t buffer_fill;
};
struct pa_mcalign *pa_mcalign_new(size_t base) {
struct pa_mcalign *m;
assert(base);
m = malloc(sizeof(struct pa_mcalign));
assert(m);
m->base = base;
m->chunk.memblock = NULL;
m->chunk.length = m->chunk.index = 0;
m->buffer = NULL;
m->buffer_fill = 0;
return m;
}
void pa_mcalign_free(struct pa_mcalign *m) {
assert(m);
free(m->buffer);
if (m->chunk.memblock)
pa_memblock_unref(m->chunk.memblock);
free(m);
}
void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c) {
assert(m && c && !m->chunk.memblock && c->memblock && c->length);
m->chunk = *c;
pa_memblock_ref(m->chunk.memblock);
}
int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c) {
assert(m && c && m->base > m->buffer_fill);
int ret;
if (!m->chunk.memblock)
return -1;
if (m->buffer_fill) {
size_t l = m->base - m->buffer_fill;
if (l > m->chunk.length)
l = m->chunk.length;
assert(m->buffer && l);
memcpy(m->buffer + m->buffer_fill, m->chunk.memblock->data + m->chunk.index, l);
m->buffer_fill += l;
m->chunk.index += l;
m->chunk.length -= l;
if (m->chunk.length == 0) {
m->chunk.length = m->chunk.index = 0;
pa_memblock_unref(m->chunk.memblock);
m->chunk.memblock = NULL;
}
assert(m->buffer_fill <= m->base);
if (m->buffer_fill == m->base) {
c->memblock = pa_memblock_new_dynamic(m->buffer, m->base);
assert(c->memblock);
c->index = 0;
c->length = m->base;
m->buffer = NULL;
m->buffer_fill = 0;
return 0;
}
return -1;
}
m->buffer_fill = m->chunk.length % m->base;
if (m->buffer_fill) {
assert(!m->buffer);
m->buffer = malloc(m->base);
assert(m->buffer);
m->chunk.length -= m->buffer_fill;
memcpy(m->buffer, m->chunk.memblock->data + m->chunk.index + m->chunk.length, m->buffer_fill);
}
if (m->chunk.length) {
*c = m->chunk;
pa_memblock_ref(c->memblock);
ret = 0;
} else
ret = -1;
m->chunk.length = m->chunk.index = 0;
pa_memblock_unref(m->chunk.memblock);
m->chunk.memblock = NULL;
return ret;
}

41
polyp/memchunk.h Normal file
View file

@ -0,0 +1,41 @@
#ifndef foomemchunkhfoo
#define foomemchunkhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include "memblock.h"
struct pa_memchunk {
struct pa_memblock *memblock;
size_t index, length;
};
void pa_memchunk_make_writable(struct pa_memchunk *c);
struct pa_mcalign;
struct pa_mcalign *pa_mcalign_new(size_t base);
void pa_mcalign_free(struct pa_mcalign *m);
void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c);
int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c);
#endif

288
polyp/modargs.c Normal file
View file

@ -0,0 +1,288 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <ctype.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "hashmap.h"
#include "modargs.h"
#include "idxset.h"
#include "sample-util.h"
#include "namereg.h"
#include "sink.h"
#include "source.h"
struct pa_modargs;
struct entry {
char *key, *value;
};
static int add_key_value(struct pa_hashmap *map, char *key, char *value, const char* const* valid_keys) {
struct entry *e;
assert(map && key && value);
if (valid_keys) {
const char*const* v;
for (v = valid_keys; *v; v++)
if (strcmp(*v, key) == 0)
break;
if (!*v) {
free(key);
free(value);
return -1;
}
}
e = malloc(sizeof(struct entry));
assert(e);
e->key = key;
e->value = value;
pa_hashmap_put(map, key, e);
return 0;
}
struct pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
struct pa_hashmap *map = NULL;
map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
assert(map);
if (args) {
enum { WHITESPACE, KEY, VALUE_START, VALUE_SIMPLE, VALUE_DOUBLE_QUOTES, VALUE_TICKS } state;
const char *p, *key, *value;
size_t key_len, value_len;
key = value = NULL;
state = WHITESPACE;
for (p = args; *p; p++) {
switch (state) {
case WHITESPACE:
if (*p == '=')
goto fail;
else if (!isspace(*p)) {
key = p;
state = KEY;
key_len = 1;
}
break;
case KEY:
if (*p == '=')
state = VALUE_START;
else
key_len++;
break;
case VALUE_START:
if (*p == '\'') {
state = VALUE_TICKS;
value = p+1;
value_len = 0;
} else if (*p == '"') {
state = VALUE_DOUBLE_QUOTES;
value = p+1;
value_len = 0;
} else if (isspace(*p)) {
if (add_key_value(map, strndup(key, key_len), strdup(""), valid_keys) < 0)
goto fail;
state = WHITESPACE;
} else {
state = VALUE_SIMPLE;
value = p;
value_len = 1;
}
break;
case VALUE_SIMPLE:
if (isspace(*p)) {
if (add_key_value(map, strndup(key, key_len), strndup(value, value_len), valid_keys) < 0)
goto fail;
state = WHITESPACE;
} else
value_len++;
break;
case VALUE_DOUBLE_QUOTES:
if (*p == '"') {
if (add_key_value(map, strndup(key, key_len), strndup(value, value_len), valid_keys) < 0)
goto fail;
state = WHITESPACE;
} else
value_len++;
break;
case VALUE_TICKS:
if (*p == '\'') {
if (add_key_value(map, strndup(key, key_len), strndup(value, value_len), valid_keys) < 0)
goto fail;
state = WHITESPACE;
} else
value_len++;
break;
}
}
if (state == VALUE_START) {
if (add_key_value(map, strndup(key, key_len), strdup(""), valid_keys) < 0)
goto fail;
} else if (state == VALUE_SIMPLE) {
if (add_key_value(map, strndup(key, key_len), strdup(value), valid_keys) < 0)
goto fail;
} else if (state != WHITESPACE)
goto fail;
}
return (struct pa_modargs*) map;
fail:
if (map)
pa_modargs_free((struct pa_modargs*) map);
return NULL;
}
static void free_func(void *p, void*userdata) {
struct entry *e = p;
assert(e);
free(e->key);
free(e->value);
free(e);
}
void pa_modargs_free(struct pa_modargs*ma) {
struct pa_hashmap *map = (struct pa_hashmap*) ma;
pa_hashmap_free(map, free_func, NULL);
}
const char *pa_modargs_get_value(struct pa_modargs *ma, const char *key, const char *def) {
struct pa_hashmap *map = (struct pa_hashmap*) ma;
struct entry*e;
if (!(e = pa_hashmap_get(map, key)))
return def;
return e->value;
}
int pa_modargs_get_value_u32(struct pa_modargs *ma, const char *key, uint32_t *value) {
const char *v;
char *e;
unsigned long l;
assert(ma && key && value);
if (!(v = pa_modargs_get_value(ma, key, NULL)))
return 0;
if (!*v)
return -1;
l = strtoul(v, &e, 0);
if (*e)
return -1;
*value = (uint32_t) l;
return 0;
}
int pa_modargs_get_sample_spec(struct pa_modargs *ma, struct pa_sample_spec *rss) {
const char *format;
uint32_t channels;
struct pa_sample_spec ss;
assert(ma && rss);
ss = *rss;
if ((pa_modargs_get_value_u32(ma, "rate", &ss.rate)) < 0)
return -1;
channels = ss.channels;
if ((pa_modargs_get_value_u32(ma, "channels", &channels)) < 0)
return -1;
ss.channels = (uint8_t) channels;
if ((format = pa_modargs_get_value(ma, "format", NULL))) {
if (strcmp(format, "s16le") == 0)
ss.format = PA_SAMPLE_S16LE;
else if (strcmp(format, "s16be") == 0)
ss.format = PA_SAMPLE_S16BE;
else if (strcmp(format, "s16ne") == 0 || strcmp(format, "s16") == 0 || strcmp(format, "16") == 0)
ss.format = PA_SAMPLE_S16NE;
else if (strcmp(format, "u8") == 0 || strcmp(format, "8") == 0)
ss.format = PA_SAMPLE_U8;
else if (strcmp(format, "float32") == 0 || strcmp(format, "float32ne") == 0)
ss.format = PA_SAMPLE_FLOAT32;
else if (strcmp(format, "float32le") == 0)
ss.format = PA_SAMPLE_FLOAT32LE;
else if (strcmp(format, "float32be") == 0)
ss.format = PA_SAMPLE_FLOAT32BE;
else if (strcmp(format, "ulaw") == 0)
ss.format = PA_SAMPLE_ULAW;
else if (strcmp(format, "alaw") == 0)
ss.format = PA_SAMPLE_ALAW;
else
return -1;
}
if (!pa_sample_spec_valid(&ss))
return -1;
*rss = ss;
return 0;
}
int pa_modargs_get_source_index(struct pa_modargs *ma, struct pa_core *c, uint32_t *index) {
const char *t;
assert(ma && index);
if (!(t = pa_modargs_get_value(ma, "source", NULL)))
*index = PA_IDXSET_INVALID;
else {
struct pa_source *source;
if (!(source = pa_namereg_get(c, t, PA_NAMEREG_SOURCE)))
return -1;
*index = source->index;
}
return 0;
}
int pa_modargs_get_sink_index(struct pa_modargs *ma, struct pa_core *c, uint32_t *index) {
const char *t;
assert(ma && index);
if (!(t = pa_modargs_get_value(ma, "sink", NULL)))
*index = PA_IDXSET_INVALID;
else {
struct pa_sink *sink;
if (!(sink = pa_namereg_get(c, t, PA_NAMEREG_SINK)))
return -1;
*index = sink->index;
}
return 0;
}

42
polyp/modargs.h Normal file
View file

@ -0,0 +1,42 @@
#ifndef foomodargshfoo
#define foomodargshfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 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 "sample.h"
#include "core.h"
struct pa_modargs;
struct pa_modargs *pa_modargs_new(const char *args, const char* const* keys);
void pa_modargs_free(struct pa_modargs*ma);
const char *pa_modargs_get_value(struct pa_modargs *ma, const char *key, const char *def);
int pa_modargs_get_value_u32(struct pa_modargs *ma, const char *key, uint32_t *value);
int pa_modargs_get_sample_spec(struct pa_modargs *ma, struct pa_sample_spec *ss);
int pa_modargs_get_source_index(struct pa_modargs *ma, struct pa_core *c, uint32_t *index);
int pa_modargs_get_sink_index(struct pa_modargs *ma, struct pa_core *c, uint32_t *index);
#endif

259
polyp/module-alsa-sink.c Normal file
View file

@ -0,0 +1,259 @@
/* $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 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 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 <sys/poll.h>
#include <asoundlib.h>
#include "module.h"
#include "core.h"
#include "memchunk.h"
#include "sink.h"
#include "modargs.h"
#include "util.h"
#include "sample-util.h"
#include "alsa-util.h"
struct userdata {
snd_pcm_t *pcm_handle;
struct pa_sink *sink;
void **io_sources;
unsigned n_io_sources;
size_t frame_size, fragment_size;
struct pa_memchunk memchunk, silence;
};
static const char* const valid_modargs[] = {
"device",
"sink_name",
"format",
"channels",
"rate",
"fragments",
"fragment_size",
NULL
};
#define DEFAULT_SINK_NAME "alsa_output"
#define DEFAULT_DEVICE "plughw:0,0"
static void xrun_recovery(struct userdata *u) {
assert(u);
fprintf(stderr, "*** ALSA-XRUN (playback) ***\n");
if (snd_pcm_prepare(u->pcm_handle) < 0)
fprintf(stderr, "snd_pcm_prepare() failed\n");
}
static void do_write(struct userdata *u) {
assert(u);
for (;;) {
struct pa_memchunk *memchunk = NULL;
snd_pcm_sframes_t frames;
if (u->memchunk.memblock)
memchunk = &u->memchunk;
else {
if (pa_sink_render(u->sink, u->fragment_size, &u->memchunk) < 0)
memchunk = &u->silence;
else
memchunk = &u->memchunk;
}
assert(memchunk->memblock && memchunk->memblock->data && memchunk->length && memchunk->memblock->length && (memchunk->length % u->frame_size) == 0);
if ((frames = snd_pcm_writei(u->pcm_handle, memchunk->memblock->data + memchunk->index, memchunk->length / u->frame_size)) < 0) {
if (frames == -EAGAIN)
return;
if (frames == -EPIPE) {
xrun_recovery(u);
continue;
}
fprintf(stderr, "snd_pcm_writei() failed\n");
return;
}
if (memchunk == &u->memchunk) {
size_t l = frames * u->frame_size;
memchunk->index += l;
memchunk->length -= l;
if (memchunk->length == 0) {
pa_memblock_unref(memchunk->memblock);
memchunk->memblock = NULL;
memchunk->index = memchunk->length = 0;
}
}
break;
}
}
static void io_callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
struct userdata *u = userdata;
assert(u && a && id);
if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN)
xrun_recovery(u);
do_write(u);
}
static uint32_t sink_get_latency_cb(struct pa_sink *s) {
struct userdata *u = s->userdata;
snd_pcm_sframes_t frames;
assert(s && u && u->sink);
if (snd_pcm_delay(u->pcm_handle, &frames) < 0) {
fprintf(stderr, __FILE__": failed to get delay\n");
s->get_latency = NULL;
return 0;
}
if (frames < 0)
frames = 0;
return pa_samples_usec(frames * u->frame_size, &s->sample_spec);
}
int pa_module_init(struct pa_core *c, struct pa_module*m) {
struct pa_modargs *ma = NULL;
int ret = -1;
struct userdata *u = NULL;
const char *dev;
struct pa_sample_spec ss;
unsigned periods, fragsize;
snd_pcm_uframes_t buffer_size;
size_t frame_size;
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
fprintf(stderr, __FILE__": failed to parse module arguments\n");
goto fail;
}
ss = c->default_sample_spec;
if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
fprintf(stderr, __FILE__": failed to parse sample specification\n");
goto fail;
}
frame_size = pa_sample_size(&ss);
periods = 12;
fragsize = 1024;
if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) {
fprintf(stderr, __FILE__": failed to parse buffer metrics\n");
goto fail;
}
buffer_size = fragsize/frame_size*periods;
u = malloc(sizeof(struct userdata));
assert(u);
memset(u, 0, sizeof(struct userdata));
m->userdata = u;
if (snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) {
fprintf(stderr, __FILE__": Error opening PCM device %s\n", dev);
goto fail;
}
if (pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &buffer_size) < 0) {
fprintf(stderr, __FILE__": Failed to set hardware parameters\n");
goto fail;
}
u->sink = pa_sink_new(c, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss);
assert(u->sink);
u->sink->get_latency = sink_get_latency_cb;
u->sink->userdata = u;
pa_sink_set_owner(u->sink, m);
u->sink->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev);
if (pa_create_io_sources(u->pcm_handle, c->mainloop, &u->io_sources, &u->n_io_sources, io_callback, u) < 0) {
fprintf(stderr, __FILE__": failed to obtain file descriptors\n");
goto fail;
}
u->frame_size = frame_size;
u->fragment_size = buffer_size*u->frame_size/periods;
fprintf(stderr, __FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size);
u->silence.memblock = pa_memblock_new(u->silence.length = u->fragment_size);
assert(u->silence.memblock);
pa_silence_memblock(u->silence.memblock, &ss);
u->silence.index = 0;
u->memchunk.memblock = NULL;
u->memchunk.index = u->memchunk.length = 0;
ret = 0;
finish:
if (ma)
pa_modargs_free(ma);
return ret;
fail:
if (u)
pa_module_done(c, m);
goto finish;
}
void pa_module_done(struct pa_core *c, struct pa_module*m) {
struct userdata *u;
assert(c && m);
if ((u = m->userdata)) {
if (u->sink)
pa_sink_free(u->sink);
if (u->io_sources)
pa_free_io_sources(c->mainloop, u->io_sources, u->n_io_sources);
if (u->pcm_handle) {
snd_pcm_drop(u->pcm_handle);
snd_pcm_close(u->pcm_handle);
}
if (u->memchunk.memblock)
pa_memblock_unref(u->memchunk.memblock);
if (u->silence.memblock)
pa_memblock_unref(u->silence.memblock);
free(u);
}
}

237
polyp/module-alsa-source.c Normal file
View file

@ -0,0 +1,237 @@
/* $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 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 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 <sys/poll.h>
#include <asoundlib.h>
#include "module.h"
#include "core.h"
#include "memchunk.h"
#include "sink.h"
#include "modargs.h"
#include "util.h"
#include "sample-util.h"
#include "alsa-util.h"
struct userdata {
snd_pcm_t *pcm_handle;
struct pa_source *source;
void **io_sources;
unsigned n_io_sources;
size_t frame_size, fragment_size;
struct pa_memchunk memchunk;
};
static const char* const valid_modargs[] = {
"device",
"source_name",
"format",
"channels",
"rate",
"fragments",
"fragment_size",
NULL
};
#define DEFAULT_SOURCE_NAME "alsa_input"
#define DEFAULT_DEVICE "hw:0,0"
static void xrun_recovery(struct userdata *u) {
assert(u);
fprintf(stderr, "*** ALSA-XRUN (capture) ***\n");
if (snd_pcm_prepare(u->pcm_handle) < 0)
fprintf(stderr, "snd_pcm_prepare() failed\n");
}
static void do_read(struct userdata *u) {
assert(u);
for (;;) {
struct pa_memchunk post_memchunk;
snd_pcm_sframes_t frames;
size_t l;
if (!u->memchunk.memblock) {
u->memchunk.memblock = pa_memblock_new(u->memchunk.length = u->fragment_size);
u->memchunk.index = 0;
}
assert(u->memchunk.memblock && u->memchunk.memblock->data && u->memchunk.length && u->memchunk.memblock->length && (u->memchunk.length % u->frame_size) == 0);
if ((frames = snd_pcm_readi(u->pcm_handle, u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length / u->frame_size)) < 0) {
if (frames == -EAGAIN)
return;
if (frames == -EPIPE) {
xrun_recovery(u);
continue;
}
fprintf(stderr, "snd_pcm_readi() failed: %s\n", strerror(-frames));
return;
}
l = frames * u->frame_size;
post_memchunk = u->memchunk;
post_memchunk.length = l;
pa_source_post(u->source, &post_memchunk);
u->memchunk.index += l;
u->memchunk.length -= l;
if (u->memchunk.length == 0) {
pa_memblock_unref(u->memchunk.memblock);
u->memchunk.memblock = NULL;
u->memchunk.index = u->memchunk.length = 0;
}
break;
}
}
static void io_callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
struct userdata *u = userdata;
assert(u && a && id);
if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN)
xrun_recovery(u);
do_read(u);
}
int pa_module_init(struct pa_core *c, struct pa_module*m) {
struct pa_modargs *ma = NULL;
int ret = -1;
struct userdata *u = NULL;
const char *dev;
struct pa_sample_spec ss;
unsigned periods, fragsize;
snd_pcm_uframes_t buffer_size;
size_t frame_size;
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
fprintf(stderr, __FILE__": failed to parse module arguments\n");
goto fail;
}
ss = c->default_sample_spec;
if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
fprintf(stderr, __FILE__": failed to parse sample specification\n");
goto fail;
}
frame_size = pa_sample_size(&ss);
periods = 12;
fragsize = 1024;
if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) {
fprintf(stderr, __FILE__": failed to parse buffer metrics\n");
goto fail;
}
buffer_size = fragsize/frame_size*periods;
u = malloc(sizeof(struct userdata));
assert(u);
memset(u, 0, sizeof(struct userdata));
m->userdata = u;
if (snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) < 0) {
fprintf(stderr, __FILE__": Error opening PCM device %s\n", dev);
goto fail;
}
if (pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &buffer_size) < 0) {
fprintf(stderr, __FILE__": Failed to set hardware parameters\n");
goto fail;
}
u->source = pa_source_new(c, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss);
assert(u->source);
u->source->userdata = u;
pa_source_set_owner(u->source, m);
u->source->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev);
if (pa_create_io_sources(u->pcm_handle, c->mainloop, &u->io_sources, &u->n_io_sources, io_callback, u) < 0) {
fprintf(stderr, __FILE__": failed to obtain file descriptors\n");
goto fail;
}
u->frame_size = frame_size;
u->fragment_size = buffer_size*u->frame_size/periods;
fprintf(stderr, __FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size);
u->memchunk.memblock = NULL;
u->memchunk.index = u->memchunk.length = 0;
snd_pcm_start(u->pcm_handle);
ret = 0;
finish:
if (ma)
pa_modargs_free(ma);
return ret;
fail:
if (u)
pa_module_done(c, m);
goto finish;
}
void pa_module_done(struct pa_core *c, struct pa_module*m) {
struct userdata *u;
assert(c && m);
if ((u = m->userdata)) {
if (u->source)
pa_source_free(u->source);
if (u->io_sources)
pa_free_io_sources(c->mainloop, u->io_sources, u->n_io_sources);
if (u->pcm_handle) {
snd_pcm_drop(u->pcm_handle);
snd_pcm_close(u->pcm_handle);
}
if (u->memchunk.memblock)
pa_memblock_unref(u->memchunk.memblock);
free(u);
}
}

73
polyp/module-cli.c Normal file
View file

@ -0,0 +1,73 @@
/* $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 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 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 <unistd.h>
#include "module.h"
#include "iochannel.h"
#include "cli.h"
#include "sioman.h"
static void eof_cb(struct pa_cli*c, void *userdata) {
struct pa_module *m = userdata;
assert(c && m);
pa_module_unload_request(m->core, m);
}
int pa_module_init(struct pa_core *c, struct pa_module*m) {
struct pa_iochannel *io;
assert(c && m);
if (m->argument) {
fprintf(stderr, __FILE__": module doesn't accept arguments.\n");
return -1;
}
if (pa_stdio_acquire() < 0) {
fprintf(stderr, __FILE__": STDIN/STDUSE already in use.\n");
return -1;
}
io = pa_iochannel_new(c->mainloop, STDIN_FILENO, STDOUT_FILENO);
assert(io);
pa_iochannel_set_noclose(io, 1);
m->userdata = pa_cli_new(c, io, m);
assert(m->userdata);
pa_cli_set_eof_callback(m->userdata, eof_cb, m);
return 0;
}
void pa_module_done(struct pa_core *c, struct pa_module*m) {
assert(c && m);
pa_cli_free(m->userdata);
pa_stdio_release();
}

404
polyp/module-oss-mmap.c Normal file
View file

@ -0,0 +1,404 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <sys/mman.h>
#include "iochannel.h"
#include "sink.h"
#include "source.h"
#include "module.h"
#include "oss-util.h"
#include "sample-util.h"
#include "util.h"
#include "modargs.h"
struct userdata {
struct pa_sink *sink;
struct pa_source *source;
struct pa_core *core;
struct pa_sample_spec sample_spec;
size_t in_fragment_size, out_fragment_size, in_fragments, out_fragments, out_fill;
int fd;
void *in_mmap, *out_mmap;
size_t in_mmap_length, out_mmap_length;
void *mainloop_source;
struct pa_memblock **in_memblocks, **out_memblocks;
unsigned out_current, in_current;
};
static const char* const valid_modargs[] = {
"sink_name",
"source_name",
"device",
"record",
"playback",
"fragments",
"fragment_size",
"format",
"rate",
"channels",
NULL
};
#define DEFAULT_SINK_NAME "oss_output"
#define DEFAULT_SOURCE_NAME "oss_input"
#define DEFAULT_DEVICE "/dev/dsp"
static void out_fill_memblocks(struct userdata *u, unsigned n) {
assert(u && u->out_memblocks);
while (n > 0) {
struct pa_memchunk chunk;
if (u->out_memblocks[u->out_current])
pa_memblock_unref_fixed(u->out_memblocks[u->out_current]);
chunk.memblock = u->out_memblocks[u->out_current] = pa_memblock_new_fixed(u->out_mmap+u->out_fragment_size*u->out_current, u->out_fragment_size);
assert(chunk.memblock);
chunk.length = chunk.memblock->length;
chunk.index = 0;
pa_sink_render_into_full(u->sink, &chunk);
u->out_current++;
while (u->out_current >= u->out_fragments)
u->out_current -= u->out_fragments;
n--;
}
}
static void do_write(struct userdata *u) {
struct count_info info;
assert(u && u->sink);
if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
fprintf(stderr, "SNDCTL_DSP_GETOPTR: %s\n", strerror(errno));
return;
}
u->out_fill = (u->out_fragment_size * u->out_fragments) - (info.ptr % u->out_fragment_size);
if (!info.blocks)
return;
out_fill_memblocks(u, info.blocks);
}
static void in_post_memblocks(struct userdata *u, unsigned n) {
assert(u && u->in_memblocks);
while (n > 0) {
struct pa_memchunk chunk;
if (!u->in_memblocks[u->in_current]) {
chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed(u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size);
chunk.length = chunk.memblock->length;
chunk.index = 0;
pa_source_post(u->source, &chunk);
}
u->in_current++;
while (u->in_current >= u->in_fragments)
u->in_current -= u->in_fragments;
n--;
}
}
static void in_clear_memblocks(struct userdata*u, unsigned n) {
unsigned i = u->in_current;
assert(u && u->in_memblocks);
if (n > u->in_fragments)
n = u->in_fragments;
while (n > 0) {
if (u->in_memblocks[i]) {
pa_memblock_unref_fixed(u->in_memblocks[i]);
u->in_memblocks[i] = NULL;
}
i++;
while (i >= u->in_fragments)
i -= u->in_fragments;
n--;
}
}
static void do_read(struct userdata *u) {
struct count_info info;
assert(u && u->source);
if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
fprintf(stderr, "SNDCTL_DSP_GETIPTR: %s\n", strerror(errno));
return;
}
if (!info.blocks)
return;
in_post_memblocks(u, info.blocks);
in_clear_memblocks(u, u->in_fragments/2);
};
static void io_callback(struct pa_mainloop_api *m, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
struct userdata *u = userdata;
assert (u && u->core->mainloop == m && u->mainloop_source == id);
if (events & PA_MAINLOOP_API_IO_EVENT_INPUT)
do_read(u);
if (events & PA_MAINLOOP_API_IO_EVENT_OUTPUT)
do_write(u);
}
static uint32_t sink_get_latency_cb(struct pa_sink *s) {
struct userdata *u = s->userdata;
assert(s && u);
do_write(u);
return pa_samples_usec(u->out_fill, &s->sample_spec);
}
int pa_module_init(struct pa_core *c, struct pa_module*m) {
struct audio_buf_info info;
struct userdata *u = NULL;
const char *p;
int nfrags, frag_size;
int mode, caps;
int enable_bits = 0, zero = 0;
int playback = 1, record = 1;
struct pa_modargs *ma = NULL;
assert(c && m);
m->userdata = u = malloc(sizeof(struct userdata));
assert(u);
memset(u, 0, sizeof(struct userdata));
u->fd = -1;
u->core = c;
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
fprintf(stderr, __FILE__": failed to parse module arguments.\n");
goto fail;
}
if (pa_modargs_get_value_u32(ma, "record", &record) < 0 || pa_modargs_get_value_u32(ma, "playback", &playback) < 0) {
fprintf(stderr, __FILE__": record= and playback= expect numeric arguments.\n");
goto fail;
}
mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
if (mode == 0) {
fprintf(stderr, __FILE__": neither playback nor record enabled for device.\n");
goto fail;
}
nfrags = 12;
frag_size = 1024;
if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || nfrags < 2 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 || frag_size < 1) {
fprintf(stderr, __FILE__": failed to parse fragments arguments\n");
goto fail;
}
u->sample_spec = c->default_sample_spec;
if (pa_modargs_get_sample_spec(ma, &u->sample_spec) < 0) {
fprintf(stderr, __FILE__": failed to parse sample specification\n");
goto fail;
}
if ((u->fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0)
goto fail;
if (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_REALTIME) || !(caps & DSP_CAP_TRIGGER)) {
fprintf(stderr, "OSS device not mmap capable.\n");
goto fail;
}
fprintf(stderr, "module-oss: device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
if (pa_oss_set_fragments(u->fd, nfrags, frag_size) < 0)
goto fail;
if (pa_oss_auto_format(u->fd, &u->sample_spec) < 0)
goto fail;
if (mode != O_WRONLY) {
if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
fprintf(stderr, "SNDCTL_DSP_GETISPACE: %s\n", strerror(errno));
goto fail;
}
fprintf(stderr, "module-oss-mmap: input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
u->in_mmap_length = (u->in_fragment_size = info.fragsize) * (u->in_fragments = info.fragstotal);
if ((u->in_mmap = mmap(NULL, u->in_mmap_length, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
if (mode == O_RDWR) {
fprintf(stderr, "module-oss-mmap: mmap failed for input. Changing to O_WRONLY mode.\n");
mode = O_WRONLY;
} else {
fprintf(stderr, "modeule-oss-mmap: mmap(): %s\n", strerror(errno));
goto fail;
}
} else {
u->source = pa_source_new(c, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &u->sample_spec);
assert(u->source);
u->source->userdata = u;
pa_source_set_owner(u->source, m);
u->source->description = pa_sprintf_malloc("Open Sound System PCM/mmap() on '%s'", p);
u->in_memblocks = malloc(sizeof(struct pa_memblock *)*u->in_fragments);
memset(u->in_memblocks, 0, sizeof(struct pa_memblock *)*u->in_fragments);
enable_bits |= PCM_ENABLE_INPUT;
}
}
if (mode != O_RDONLY) {
if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
fprintf(stderr, "SNDCTL_DSP_GETOSPACE: %s\n", strerror(errno));
goto fail;
}
fprintf(stderr, "module-oss: output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
u->out_mmap_length = (u->out_fragment_size = info.fragsize) * (u->out_fragments = info.fragstotal);
if ((u->out_mmap = mmap(NULL, u->out_mmap_length, PROT_WRITE, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
if (mode == O_RDWR) {
fprintf(stderr, "module-oss-mmap: mmap filed for output. Changing to O_RDONLY mode.\n");
mode = O_RDONLY;
} else {
fprintf(stderr, "module-oss-mmap: mmap(): %s\n", strerror(errno));
goto fail;
}
} else {
pa_silence_memory(u->out_mmap, u->out_mmap_length, &u->sample_spec);
u->sink = pa_sink_new(c, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &u->sample_spec);
assert(u->sink);
u->sink->get_latency = sink_get_latency_cb;
u->sink->userdata = u;
pa_sink_set_owner(u->sink, m);
u->sink->description = pa_sprintf_malloc("Open Sound System PCM/mmap() on '%s'", p);
u->out_memblocks = malloc(sizeof(struct memblock *)*u->out_fragments);
memset(u->out_memblocks, 0, sizeof(struct pa_memblock *)*u->out_fragments);
enable_bits |= PCM_ENABLE_OUTPUT;
}
}
zero = 0;
if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero) < 0) {
fprintf(stderr, "SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno));
goto fail;
}
if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0) {
fprintf(stderr, "SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno));
goto fail;
}
assert(u->source || u->sink);
u->mainloop_source = c->mainloop->source_io(c->mainloop, u->fd, (u->source ? PA_MAINLOOP_API_IO_EVENT_INPUT : 0) | (u->sink ? PA_MAINLOOP_API_IO_EVENT_OUTPUT : 0), io_callback, u);
assert(u->mainloop_source);
pa_modargs_free(ma);
return 0;
fail:
pa_module_done(c, m);
if (ma)
pa_modargs_free(ma);
return -1;
}
void pa_module_done(struct pa_core *c, struct pa_module*m) {
struct userdata *u;
assert(c && m);
u = m->userdata;
assert(u);
if (u->out_memblocks) {
unsigned i;
for (i = 0; i < u->out_fragments; i++)
if (u->out_memblocks[i])
pa_memblock_unref_fixed(u->out_memblocks[i]);
free(u->out_memblocks);
}
if (u->in_memblocks) {
unsigned i;
for (i = 0; i < u->in_fragments; i++)
if (u->in_memblocks[i])
pa_memblock_unref_fixed(u->in_memblocks[i]);
free(u->in_memblocks);
}
if (u->in_mmap && u->in_mmap != MAP_FAILED)
munmap(u->in_mmap, u->in_mmap_length);
if (u->out_mmap && u->out_mmap != MAP_FAILED)
munmap(u->out_mmap, u->out_mmap_length);
if (u->sink)
pa_sink_free(u->sink);
if (u->source)
pa_source_free(u->source);
if (u->mainloop_source)
u->core->mainloop->cancel_io(u->core->mainloop, u->mainloop_source);
if (u->fd >= 0)
close(u->fd);
free(u);
}

304
polyp/module-oss.c Normal file
View file

@ -0,0 +1,304 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include "iochannel.h"
#include "sink.h"
#include "source.h"
#include "module.h"
#include "oss-util.h"
#include "sample-util.h"
#include "util.h"
#include "modargs.h"
struct userdata {
struct pa_sink *sink;
struct pa_source *source;
struct pa_iochannel *io;
struct pa_core *core;
struct pa_memchunk memchunk, silence;
uint32_t in_fragment_size, out_fragment_size, sample_size;
int fd;
};
static const char* const valid_modargs[] = {
"sink_name",
"source_name",
"device",
"record",
"playback",
"fragments",
"fragment_size",
"format",
"rate",
"channels",
NULL
};
#define DEFAULT_SINK_NAME "oss_output"
#define DEFAULT_SOURCE_NAME "oss_input"
#define DEFAULT_DEVICE "/dev/dsp"
static void do_write(struct userdata *u) {
struct pa_memchunk *memchunk;
ssize_t r;
assert(u);
if (!u->sink || !pa_iochannel_is_writable(u->io))
return;
if (!u->memchunk.length) {
if (pa_sink_render(u->sink, u->out_fragment_size, &u->memchunk) < 0)
memchunk = &u->silence;
else
memchunk = &u->memchunk;
}
assert(memchunk->memblock && memchunk->length);
if ((r = pa_iochannel_write(u->io, memchunk->memblock->data + memchunk->index, memchunk->length)) < 0) {
fprintf(stderr, "write() failed: %s\n", strerror(errno));
return;
}
if (memchunk == &u->silence)
assert(r % u->sample_size == 0);
else {
u->memchunk.index += r;
u->memchunk.length -= r;
if (u->memchunk.length <= 0) {
pa_memblock_unref(u->memchunk.memblock);
u->memchunk.memblock = NULL;
}
}
}
static void do_read(struct userdata *u) {
struct pa_memchunk memchunk;
ssize_t r;
assert(u);
if (!u->source || !pa_iochannel_is_readable(u->io))
return;
memchunk.memblock = pa_memblock_new(u->in_fragment_size);
assert(memchunk.memblock);
if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) {
pa_memblock_unref(memchunk.memblock);
if (errno != EAGAIN)
fprintf(stderr, "read() failed: %s\n", strerror(errno));
return;
}
assert(r <= (ssize_t) memchunk.memblock->length);
memchunk.length = memchunk.memblock->length = r;
memchunk.index = 0;
pa_source_post(u->source, &memchunk);
pa_memblock_unref(memchunk.memblock);
};
static void io_callback(struct pa_iochannel *io, void*userdata) {
struct userdata *u = userdata;
assert(u);
do_write(u);
do_read(u);
}
static uint32_t sink_get_latency_cb(struct pa_sink *s) {
int arg;
struct userdata *u = s->userdata;
assert(s && u && u->sink);
if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) {
fprintf(stderr, "module-oss: device doesn't support SNDCTL_DSP_GETODELAY.\n");
s->get_latency = NULL;
return 0;
}
return pa_samples_usec(arg, &s->sample_spec);
}
int pa_module_init(struct pa_core *c, struct pa_module*m) {
struct audio_buf_info info;
struct userdata *u = NULL;
const char *p;
int fd = -1;
int nfrags, frag_size, in_frag_size, out_frag_size;
int mode;
uint32_t record = 1, playback = 1;
struct pa_sample_spec ss;
struct pa_modargs *ma = NULL;
assert(c && m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
fprintf(stderr, __FILE__": failed to parse module arguments.\n");
goto fail;
}
if (pa_modargs_get_value_u32(ma, "record", &record) < 0 || pa_modargs_get_value_u32(ma, "playback", &playback) < 0) {
fprintf(stderr, __FILE__": record= and playback= expect numeric argument.\n");
goto fail;
}
mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
if (mode == 0) {
fprintf(stderr, __FILE__": neither playback nor record enabled for device.\n");
goto fail;
}
nfrags = 12;
frag_size = 1024;
if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || nfrags < 2 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 || frag_size < 1) {
fprintf(stderr, __FILE__": failed to parse fragments arguments\n");
goto fail;
}
ss = c->default_sample_spec;
if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
fprintf(stderr, __FILE__": failed to parse sample specification\n");
goto fail;
}
if ((fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, NULL)) < 0)
goto fail;
fprintf(stderr, "module-oss: device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
if (pa_oss_set_fragments(fd, nfrags, frag_size) < 0)
goto fail;
if (pa_oss_auto_format(fd, &ss) < 0)
goto fail;
if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) {
fprintf(stderr, "SNDCTL_DSP_GETBLKSIZE: %s\n", strerror(errno));
goto fail;
}
assert(frag_size);
in_frag_size = out_frag_size = frag_size;
if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {
fprintf(stderr, "module-oss: input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
in_frag_size = info.fragsize;
}
if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
fprintf(stderr, "module-oss: output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
out_frag_size = info.fragsize;
}
u = malloc(sizeof(struct userdata));
assert(u);
u->core = c;
if (mode != O_WRONLY) {
u->source = pa_source_new(c, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss);
assert(u->source);
u->source->userdata = u;
pa_source_set_owner(u->source, m);
u->source->description = pa_sprintf_malloc("Open Sound System PCM on '%s'", p);
} else
u->source = NULL;
if (mode != O_RDONLY) {
u->sink = pa_sink_new(c, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss);
assert(u->sink);
u->sink->get_latency = sink_get_latency_cb;
u->sink->userdata = u;
pa_sink_set_owner(u->sink, m);
u->sink->description = pa_sprintf_malloc("Open Sound System PCM on '%s'", p);
} else
u->sink = NULL;
assert(u->source || u->sink);
u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0);
assert(u->io);
pa_iochannel_set_callback(u->io, io_callback, u);
u->fd = fd;
u->memchunk.memblock = NULL;
u->memchunk.length = 0;
u->sample_size = pa_sample_size(&ss);
u->out_fragment_size = out_frag_size;
u->in_fragment_size = in_frag_size;
u->silence.memblock = pa_memblock_new(u->silence.length = u->out_fragment_size);
assert(u->silence.memblock);
pa_silence_memblock(u->silence.memblock, &ss);
u->silence.index = 0;
m->userdata = u;
pa_modargs_free(ma);
return 0;
fail:
if (fd >= 0)
close(fd);
if (ma)
pa_modargs_free(ma);
return -1;
}
void pa_module_done(struct pa_core *c, struct pa_module*m) {
struct userdata *u;
assert(c && m);
u = m->userdata;
assert(u);
if (u->memchunk.memblock)
pa_memblock_unref(u->memchunk.memblock);
if (u->silence.memblock)
pa_memblock_unref(u->silence.memblock);
if (u->sink)
pa_sink_free(u->sink);
if (u->source)
pa_source_free(u->source);
pa_iochannel_free(u->io);
free(u);
}

218
polyp/module-pipe-sink.c Normal file
View file

@ -0,0 +1,218 @@
/* $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 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 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 <sys/stat.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include "iochannel.h"
#include "sink.h"
#include "module.h"
#include "util.h"
#include "modargs.h"
#define DEFAULT_FIFO_NAME "/tmp/musicfifo"
#define DEFAULT_SINK_NAME "fifo_output"
struct userdata {
struct pa_core *core;
char *filename;
struct pa_sink *sink;
struct pa_iochannel *io;
void *mainloop_source;
struct pa_memchunk memchunk;
};
static const char* const valid_modargs[] = {
"file",
"rate",
"channels",
"format",
"sink_name",
NULL
};
static void do_write(struct userdata *u) {
ssize_t r;
assert(u);
u->core->mainloop->enable_fixed(u->core->mainloop, u->mainloop_source, 0);
if (!pa_iochannel_is_writable(u->io))
return;
if (!u->memchunk.length)
if (pa_sink_render(u->sink, PIPE_BUF, &u->memchunk) < 0)
return;
assert(u->memchunk.memblock && u->memchunk.length);
if ((r = pa_iochannel_write(u->io, u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) {
fprintf(stderr, "write() failed: %s\n", strerror(errno));
return;
}
u->memchunk.index += r;
u->memchunk.length -= r;
if (u->memchunk.length <= 0) {
pa_memblock_unref(u->memchunk.memblock);
u->memchunk.memblock = NULL;
}
}
static void notify_cb(struct pa_sink*s) {
struct userdata *u = s->userdata;
assert(s && u);
if (pa_iochannel_is_writable(u->io))
u->core->mainloop->enable_fixed(u->core->mainloop, u->mainloop_source, 1);
}
static void fixed_callback(struct pa_mainloop_api *m, void *id, void *userdata) {
struct userdata *u = userdata;
assert(u);
do_write(u);
}
static void io_callback(struct pa_iochannel *io, void*userdata) {
struct userdata *u = userdata;
assert(u);
do_write(u);
}
int pa_module_init(struct pa_core *c, struct pa_module*m) {
struct userdata *u = NULL;
struct stat st;
const char *p;
int fd = -1;
struct pa_sample_spec ss;
struct pa_modargs *ma = NULL;
assert(c && m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
fprintf(stderr, __FILE__": failed to parse module arguments\n");
goto fail;
}
ss = c->default_sample_spec;
if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
fprintf(stderr, __FILE__": invalid sample format specification\n");
goto fail;
}
mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777);
if ((fd = open(p, O_RDWR)) < 0) {
fprintf(stderr, __FILE__": open('%s'): %s\n", p, strerror(errno));
goto fail;
}
if (fstat(fd, &st) < 0) {
fprintf(stderr, __FILE__": fstat('%s'): %s\n", p, strerror(errno));
goto fail;
}
if (!S_ISFIFO(st.st_mode)) {
fprintf(stderr, __FILE__": '%s' is not a FIFO.\n", p);
goto fail;
}
u = malloc(sizeof(struct userdata));
assert(u);
memset(u, 0, sizeof(struct userdata));
u->filename = strdup(p);
assert(u->filename);
u->core = c;
if (!(u->sink = pa_sink_new(c, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss))) {
fprintf(stderr, __FILE__": failed to create sink.\n");
goto fail;
}
u->sink->notify = notify_cb;
u->sink->userdata = u;
pa_sink_set_owner(u->sink, m);
u->sink->description = pa_sprintf_malloc("Unix FIFO sink '%s'", p);
assert(u->sink->description);
u->io = pa_iochannel_new(c->mainloop, -1, fd);
assert(u->io);
pa_iochannel_set_callback(u->io, io_callback, u);
u->memchunk.memblock = NULL;
u->memchunk.length = 0;
u->mainloop_source = c->mainloop->source_fixed(c->mainloop, fixed_callback, u);
assert(u->mainloop_source);
c->mainloop->enable_fixed(c->mainloop, u->mainloop_source, 0);
m->userdata = u;
pa_modargs_free(ma);
return 0;
fail:
if (ma)
pa_modargs_free(ma);
if (fd >= 0)
close(fd);
pa_module_done(c, m);
return -1;
}
void pa_module_done(struct pa_core *c, struct pa_module*m) {
struct userdata *u;
assert(c && m);
if (!(u = m->userdata))
return;
if (u->memchunk.memblock)
pa_memblock_unref(u->memchunk.memblock);
pa_sink_free(u->sink);
pa_iochannel_free(u->io);
u->core->mainloop->cancel_fixed(u->core->mainloop, u->mainloop_source);
assert(u->filename);
unlink(u->filename);
free(u->filename);
free(u);
}

View file

@ -0,0 +1,165 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "module.h"
#include "socket-server.h"
#include "socket-util.h"
#include "util.h"
#include "modargs.h"
#ifdef USE_PROTOCOL_SIMPLE
#include "protocol-simple.h"
#define protocol_new pa_protocol_simple_new
#define protocol_free pa_protocol_simple_free
#define IPV4_PORT 4711
#define UNIX_SOCKET "/tmp/polypaudio/simple"
#define MODULE_ARGUMENTS "rate", "format", "channels", "sink", "source", "playback", "record",
#else
#ifdef USE_PROTOCOL_CLI
#include "protocol-cli.h"
#define protocol_new pa_protocol_cli_new
#define protocol_free pa_protocol_cli_free
#define IPV4_PORT 4712
#define UNIX_SOCKET "/tmp/polypaudio/cli"
#define MODULE_ARGUMENTS
#else
#ifdef USE_PROTOCOL_NATIVE
#include "protocol-native.h"
#define protocol_new pa_protocol_native_new
#define protocol_free pa_protocol_native_free
#define IPV4_PORT 4713
#define UNIX_SOCKET "/tmp/polypaudio/native"
#define MODULE_ARGUMENTS "public", "cookie",
#else
#ifdef USE_PROTOCOL_ESOUND
#include "protocol-esound.h"
#include "esound.h"
#define protocol_new pa_protocol_esound_new
#define protocol_free pa_protocol_esound_free
#define IPV4_PORT ESD_DEFAULT_PORT
#define UNIX_SOCKET ESD_UNIX_SOCKET_NAME
#define MODULE_ARGUMENTS "sink", "source", "public", "cookie",
#else
#error "Broken build system"
#endif
#endif
#endif
#endif
static const char* const valid_modargs[] = {
MODULE_ARGUMENTS
#ifdef USE_TCP_SOCKETS
"port",
"loopback",
#else
"socket",
#endif
NULL
};
static struct pa_socket_server *create_socket_server(struct pa_core *c, struct pa_modargs *ma) {
struct pa_socket_server *s;
#ifdef USE_TCP_SOCKETS
uint32_t loopback = 1, port = IPV4_PORT;
if (pa_modargs_get_value_u32(ma, "loopback", &loopback) < 0) {
fprintf(stderr, "loopback= expects a numerical argument.\n");
return NULL;
}
if (pa_modargs_get_value_u32(ma, "port", &port) < 0) {
fprintf(stderr, "port= expects a numerical argument.\n");
return NULL;
}
if (!(s = pa_socket_server_new_ipv4(c->mainloop, loopback ? INADDR_LOOPBACK : INADDR_ANY, port)))
return NULL;
#else
int r;
const char *p;
p = pa_modargs_get_value(ma, "socket", UNIX_SOCKET);
assert(p);
if (pa_unix_socket_make_secure_dir(p) < 0) {
fprintf(stderr, "Failed to create secure socket directory.\n");
return NULL;
}
if ((r = pa_unix_socket_remove_stale(p)) < 0) {
fprintf(stderr, "Failed to remove stale UNIX socket '%s': %s\n", p, strerror(errno));
return NULL;
}
if (r)
fprintf(stderr, "Removed stale UNIX socket '%s'.", p);
if (!(s = pa_socket_server_new_unix(c->mainloop, p)))
return NULL;
#endif
return s;
}
int pa_module_init(struct pa_core *c, struct pa_module*m) {
struct pa_socket_server *s;
struct pa_modargs *ma = NULL;
int ret = -1;
assert(c && m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
fprintf(stderr, "Failed to parse module arguments\n");
goto finish;
}
if (!(s = create_socket_server(c, ma)))
goto finish;
if (!(m->userdata = protocol_new(c, s, m, ma))) {
pa_socket_server_free(s);
goto finish;
}
ret = 0;
finish:
if (ma)
pa_modargs_free(ma);
return ret;
}
void pa_module_done(struct pa_core *c, struct pa_module*m) {
assert(c && m);
protocol_free(m->userdata);
}

161
polyp/module.c Normal file
View file

@ -0,0 +1,161 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include "module.h"
struct pa_module* pa_module_load(struct pa_core *c, const char *name, const char *argument) {
struct pa_module *m = NULL;
int r;
assert(c && name);
m = malloc(sizeof(struct pa_module));
assert(m);
m->name = strdup(name);
m->argument = argument ? strdup(argument) : NULL;
if (!(m->dl = lt_dlopenext(name)))
goto fail;
if (!(m->init = lt_dlsym(m->dl, "pa_module_init")))
goto fail;
if (!(m->done = lt_dlsym(m->dl, "pa_module_done")))
goto fail;
m->userdata = NULL;
m->core = c;
assert(m->init);
if (m->init(c, m) < 0)
goto fail;
if (!c->modules)
c->modules = pa_idxset_new(NULL, NULL);
assert(c->modules);
r = pa_idxset_put(c->modules, m, &m->index);
assert(r >= 0 && m->index != PA_IDXSET_INVALID);
fprintf(stderr, "module: loaded %u \"%s\" with argument \"%s\".\n", m->index, m->name, m->argument);
return m;
fail:
if (m) {
free(m->argument);
free(m->name);
if (m->dl)
lt_dlclose(m->dl);
free(m);
}
return NULL;
}
static void pa_module_free(struct pa_module *m) {
assert(m && m->done && m->core);
m->done(m->core, m);
lt_dlclose(m->dl);
fprintf(stderr, "module: unloaded %u \"%s\".\n", m->index, m->name);
free(m->name);
free(m->argument);
free(m);
}
void pa_module_unload(struct pa_core *c, struct pa_module *m) {
assert(c && m);
assert(c->modules);
if (!(m = pa_idxset_remove_by_data(c->modules, m, NULL)))
return;
pa_module_free(m);
}
void pa_module_unload_by_index(struct pa_core *c, uint32_t index) {
struct pa_module *m;
assert(c && index != PA_IDXSET_INVALID);
assert(c->modules);
if (!(m = pa_idxset_remove_by_index(c->modules, index)))
return;
pa_module_free(m);
}
static void free_callback(void *p, void *userdata) {
struct pa_module *m = p;
assert(m);
pa_module_free(m);
}
void pa_module_unload_all(struct pa_core *c) {
assert(c);
if (!c->modules)
return;
pa_idxset_free(c->modules, free_callback, NULL);
c->modules = NULL;
}
struct once_info {
struct pa_core *core;
uint32_t index;
};
static void module_unload_once_callback(void *userdata) {
struct once_info *i = userdata;
assert(i);
pa_module_unload_by_index(i->core, i->index);
free(i);
}
void pa_module_unload_request(struct pa_core *c, struct pa_module *m) {
struct once_info *i;
assert(c && m);
i = malloc(sizeof(struct once_info));
assert(i);
i->core = c;
i->index = m->index;
pa_mainloop_api_once(c->mainloop, module_unload_once_callback, i);
}

55
polyp/module.h Normal file
View file

@ -0,0 +1,55 @@
#ifndef foomodulehfoo
#define foomodulehfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
#include <ltdl.h>
#include "core.h"
struct pa_module {
struct pa_core *core;
char *name, *argument;
uint32_t index;
lt_dlhandle dl;
int (*init)(struct pa_core *c, struct pa_module*m);
void (*done)(struct pa_core *c, struct pa_module*m);
void *userdata;
};
struct pa_module* pa_module_load(struct pa_core *c, const char *name, const char*argument);
void pa_module_unload(struct pa_core *c, struct pa_module *m);
void pa_module_unload_by_index(struct pa_core *c, uint32_t index);
void pa_module_unload_all(struct pa_core *c);
void pa_module_unload_request(struct pa_core *c, struct pa_module *m);
/* These to following prototypes are for module entrypoints and not implemented by the core */
int pa_module_init(struct pa_core *c, struct pa_module*m);
void pa_module_done(struct pa_core *c, struct pa_module*m);
#endif

136
polyp/namereg.c Normal file
View file

@ -0,0 +1,136 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <string.h>
#include <malloc.h>
#include <stdio.h>
#include "namereg.h"
struct namereg_entry {
enum pa_namereg_type type;
char *name;
void *data;
};
void pa_namereg_free(struct pa_core *c) {
assert(c);
if (!c->namereg)
return;
assert(pa_hashmap_ncontents(c->namereg) == 0);
pa_hashmap_free(c->namereg, NULL, NULL);
}
const char *pa_namereg_register(struct pa_core *c, const char *name, enum pa_namereg_type type, void *data, int fail) {
struct namereg_entry *e;
char *n = NULL;
int r;
assert(c && name && data);
if (!c->namereg) {
c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
assert(c->namereg);
}
if ((e = pa_hashmap_get(c->namereg, name)) && fail)
return NULL;
if (!e)
n = strdup(name);
else {
unsigned i;
size_t l = strlen(name);
n = malloc(l+3);
assert(n);
for (i = 1; i <= 99; i++) {
snprintf(n, l+2, "%s%u", name, i);
if (!(e = pa_hashmap_get(c->namereg, n)))
break;
}
if (e) {
free(n);
return NULL;
}
}
assert(n);
e = malloc(sizeof(struct namereg_entry));
assert(e);
e->type = type;
e->name = n;
e->data = data;
r = pa_hashmap_put(c->namereg, e->name, e);
assert (r >= 0);
return e->name;
}
void pa_namereg_unregister(struct pa_core *c, const char *name) {
struct namereg_entry *e;
int r;
assert(c && name);
e = pa_hashmap_get(c->namereg, name);
assert(e);
r = pa_hashmap_remove(c->namereg, name);
assert(r >= 0);
free(e->name);
free(e);
}
void* pa_namereg_get(struct pa_core *c, const char *name, enum pa_namereg_type type) {
struct namereg_entry *e;
uint32_t index;
char *x = NULL;
void *d = NULL;
assert(c && name);
if ((e = pa_hashmap_get(c->namereg, name)))
if (e->type == e->type)
return e->data;
index = (uint32_t) strtol(name, &x, 0);
if (!x || *x != 0)
return NULL;
if (type == PA_NAMEREG_SINK)
d = pa_idxset_get_by_index(c->sinks, index);
else if (type == PA_NAMEREG_SOURCE)
d = pa_idxset_get_by_index(c->sources, index);
return d;
}

38
polyp/namereg.h Normal file
View file

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

68
polyp/native-common.h Normal file
View file

@ -0,0 +1,68 @@
#ifndef foonativecommonhfoo
#define foonativecommonhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 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.
***/
enum {
PA_COMMAND_ERROR,
PA_COMMAND_TIMEOUT, /* pseudo command */
PA_COMMAND_REPLY,
PA_COMMAND_CREATE_PLAYBACK_STREAM,
PA_COMMAND_DELETE_PLAYBACK_STREAM,
PA_COMMAND_CREATE_RECORD_STREAM,
PA_COMMAND_DELETE_RECORD_STREAM,
PA_COMMAND_EXIT,
PA_COMMAND_REQUEST,
PA_COMMAND_AUTH,
PA_COMMAND_SET_NAME,
PA_COMMAND_LOOKUP_SINK,
PA_COMMAND_LOOKUP_SOURCE,
PA_COMMAND_DRAIN_PLAYBACK_STREAM,
PA_COMMAND_PLAYBACK_STREAM_KILLED,
PA_COMMAND_RECORD_STREAM_KILLED,
PA_COMMAND_STAT,
PA_COMMAND_GET_PLAYBACK_LATENCY,
PA_COMMAND_MAX
};
enum {
PA_ERROR_OK,
PA_ERROR_ACCESS,
PA_ERROR_COMMAND,
PA_ERROR_INVALID,
PA_ERROR_EXIST,
PA_ERROR_NOENTITY,
PA_ERROR_CONNECTIONREFUSED,
PA_ERROR_PROTOCOL,
PA_ERROR_TIMEOUT,
PA_ERROR_AUTHKEY,
PA_ERROR_INTERNAL,
PA_ERROR_CONNECTIONTERMINATED,
PA_ERROR_KILLED,
PA_ERROR_INVALIDSERVER,
PA_ERROR_MAX
};
#define PA_NATIVE_COOKIE_LENGTH 256
#define PA_NATIVE_COOKIE_FILE ".polypaudio-cookie"
#endif

163
polyp/oss-util.c Normal file
View file

@ -0,0 +1,163 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "oss-util.h"
int pa_oss_open(const char *device, int *mode, int* pcaps) {
int fd = -1;
assert(device && mode && (*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY));
if (*mode == O_RDWR) {
if ((fd = open(device, O_RDWR|O_NDELAY)) >= 0) {
int dcaps, *tcaps;
ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
tcaps = pcaps ? pcaps : &dcaps;
if (ioctl(fd, SNDCTL_DSP_GETCAPS, tcaps) < 0) {
fprintf(stderr, __FILE__": SNDCTL_DSP_GETCAPS: %s\n", strerror(errno));
goto fail;
}
if (*tcaps & DSP_CAP_DUPLEX)
return fd;
close(fd);
}
if ((fd = open(device, (*mode = O_WRONLY)|O_NDELAY)) < 0) {
if ((fd = open(device, (*mode = O_RDONLY)|O_NDELAY)) < 0) {
fprintf(stderr, __FILE__": open('%s'): %s\n", device, strerror(errno));
goto fail;
}
}
} else {
if ((fd = open(device, *mode|O_NDELAY)) < 0) {
fprintf(stderr, __FILE__": open('%s'): %s\n", device, strerror(errno));
goto fail;
}
}
if (pcaps) {
if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {
fprintf(stderr, "SNDCTL_DSP_GETCAPS: %s\n", strerror(errno));
goto fail;
}
}
return fd;
fail:
if (fd >= 0)
close(fd);
return fd;
}
int pa_oss_auto_format(int fd, struct pa_sample_spec *ss) {
int format, channels, speed, reqformat;
static const int format_trans[] = {
[PA_SAMPLE_U8] = AFMT_U8,
[PA_SAMPLE_ALAW] = AFMT_A_LAW,
[PA_SAMPLE_ULAW] = AFMT_MU_LAW,
[PA_SAMPLE_S16LE] = AFMT_S16_LE,
[PA_SAMPLE_S16BE] = AFMT_S16_BE,
[PA_SAMPLE_FLOAT32LE] = AFMT_QUERY, /* not supported */
[PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */
};
assert(fd >= 0 && ss);
reqformat = format = format_trans[ss->format];
if (reqformat == AFMT_QUERY || ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != reqformat) {
format = AFMT_S16_NE;
if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_S16_NE) {
int f = AFMT_S16_NE == AFMT_S16_LE ? AFMT_S16_BE : AFMT_S16_LE;
format = f;
if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != f) {
format = AFMT_U8;
if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_U8) {
fprintf(stderr, "SNDCTL_DSP_SETFMT: %s\n", format != AFMT_U8 ? "No supported sample format" : strerror(errno));
return -1;
} else
ss->format = PA_SAMPLE_U8;
} else
ss->format = f == AFMT_S16_LE ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE;
} else
ss->format = PA_SAMPLE_S16NE;
}
channels = ss->channels;
if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) {
fprintf(stderr, "SNDCTL_DSP_CHANNELS: %s\n", strerror(errno));
return -1;
}
assert(channels);
ss->channels = channels;
speed = ss->rate;
if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) {
fprintf(stderr, "SNDCTL_DSP_SPEED: %s\n", strerror(errno));
return -1;
}
assert(speed);
ss->rate = speed;
return 0;
}
static int log2(int v) {
int k = 0;
for (;;) {
v >>= 1;
if (!v) break;
k++;
}
return k;
}
int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {
int arg;
arg = ((int) nfrags << 16) | log2(frag_size);
if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) {
fprintf(stderr, "SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno));
return -1;
}
return 0;
}

32
polyp/oss-util.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef fooossutilhfoo
#define fooossutilhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 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 "sample.h"
int pa_oss_open(const char *device, int *mode, int* pcaps);
int pa_oss_auto_format(int fd, struct pa_sample_spec *ss);
int pa_oss_set_fragments(int fd, int frags, int frag_size);
#endif

83
polyp/pacat-simple.c Normal file
View file

@ -0,0 +1,83 @@
/* $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 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 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 <unistd.h>
#include <string.h>
#include <errno.h>
#include "polyplib-simple.h"
#include "polyplib-error.h"
#define BUFSIZE 1024
int main(int argc, char*argv[]) {
static const struct pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = 44100,
.channels = 2
};
struct pa_simple *s = NULL;
int ret = 1;
int error;
if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, &error))) {
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
goto finish;
}
for (;;) {
uint8_t buf[BUFSIZE];
ssize_t r;
if ((r = read(STDIN_FILENO, buf, sizeof(buf))) <= 0) {
if (r == 0) /* eof */
break;
fprintf(stderr, __FILE__": read() failed: %s\n", strerror(errno));
goto finish;
}
if (pa_simple_write(s, buf, r, &error) < 0) {
fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error));
goto finish;
}
}
/* Make sure that every single sample way played */
if (pa_simple_drain(s, &error) < 0) {
fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error));
goto finish;
}
ret = 0;
finish:
if (s)
pa_simple_free(s);
return ret;
}

336
polyp/pacat.c Normal file
View file

@ -0,0 +1,336 @@
/* $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 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 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 <signal.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "polyplib.h"
#include "polyplib-error.h"
#include "mainloop.h"
#include "mainloop-signal.h"
static enum { RECORD, PLAYBACK } mode = PLAYBACK;
static struct pa_context *context = NULL;
static struct pa_stream *stream = NULL;
static struct pa_mainloop_api *mainloop_api = NULL;
static void *buffer = NULL;
static size_t buffer_length = 0, buffer_index = 0;
static void* stdio_source = NULL;
static void quit(int ret) {
assert(mainloop_api);
mainloop_api->quit(mainloop_api, ret);
}
static void context_die_callback(struct pa_context *c, void *userdata) {
assert(c);
fprintf(stderr, "Connection to server shut down, exiting.\n");
quit(1);
}
static void stream_die_callback(struct pa_stream *s, void *userdata) {
assert(s);
fprintf(stderr, "Stream deleted, exiting.\n");
quit(1);
}
static void do_stream_write(size_t length) {
size_t l;
assert(length);
if (!buffer || !buffer_length)
return;
l = length;
if (l > buffer_length)
l = buffer_length;
pa_stream_write(stream, buffer+buffer_index, l);
buffer_length -= l;
buffer_index += l;
if (!buffer_length) {
free(buffer);
buffer = NULL;
buffer_index = buffer_length = 0;
}
}
static void stream_write_callback(struct pa_stream *s, size_t length, void *userdata) {
assert(s && length);
if (stdio_source)
mainloop_api->enable_io(mainloop_api, stdio_source, PA_MAINLOOP_API_IO_EVENT_INPUT);
if (!buffer)
return;
do_stream_write(length);
}
static void stream_read_callback(struct pa_stream *s, const void*data, size_t length, void *userdata) {
assert(s && data && length);
if (stdio_source)
mainloop_api->enable_io(mainloop_api, stdio_source, PA_MAINLOOP_API_IO_EVENT_OUTPUT);
if (buffer) {
fprintf(stderr, "Buffer overrrun, dropping incoming data\n");
return;
}
buffer = malloc(buffer_length = length);
assert(buffer);
memcpy(buffer, data, length);
buffer_index = 0;
}
static void stream_complete_callback(struct pa_stream*s, int success, void *userdata) {
assert(s);
if (!success) {
fprintf(stderr, "Stream creation failed: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
quit(1);
return;
}
fprintf(stderr, "Stream created.\n");
}
static void context_complete_callback(struct pa_context *c, int success, void *userdata) {
static const struct pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = 44100,
.channels = 2
};
assert(c && !stream);
if (!success) {
fprintf(stderr, "Connection failed: %s\n", pa_strerror(pa_context_errno(c)));
goto fail;
}
fprintf(stderr, "Connection established.\n");
if (!(stream = pa_stream_new(c, mode == PLAYBACK ? PA_STREAM_PLAYBACK : PA_STREAM_RECORD, NULL, "pacat", &ss, NULL, stream_complete_callback, NULL))) {
fprintf(stderr, "pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(c)));
goto fail;
}
pa_stream_set_die_callback(stream, stream_die_callback, NULL);
pa_stream_set_write_callback(stream, stream_write_callback, NULL);
pa_stream_set_read_callback(stream, stream_read_callback, NULL);
return;
fail:
quit(1);
}
static void context_drain_complete(struct pa_context*c, void *userdata) {
quit(0);
}
static void stream_drain_complete(struct pa_stream*s, void *userdata) {
fprintf(stderr, "Playback stream drained.\n");
pa_stream_free(stream);
stream = NULL;
if (pa_context_drain(context, context_drain_complete, NULL) < 0)
quit(0);
else
fprintf(stderr, "Draining connection to server.\n");
}
static void stdin_callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
size_t l, w = 0;
ssize_t r;
assert(a == mainloop_api && id && stdio_source == id);
if (buffer) {
mainloop_api->enable_io(mainloop_api, stdio_source, PA_MAINLOOP_API_IO_EVENT_NULL);
return;
}
if (!stream || !pa_stream_is_ready(stream) || !(l = w = pa_stream_writable_size(stream)))
l = 4096;
buffer = malloc(l);
assert(buffer);
if ((r = read(fd, buffer, l)) <= 0) {
if (r == 0) {
fprintf(stderr, "Got EOF.\n");
pa_stream_drain(stream, stream_drain_complete, NULL);
} else {
fprintf(stderr, "read() failed: %s\n", strerror(errno));
quit(1);
}
mainloop_api->cancel_io(mainloop_api, stdio_source);
stdio_source = NULL;
return;
}
buffer_length = r;
buffer_index = 0;
if (w)
do_stream_write(w);
}
static void stdout_callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
ssize_t r;
assert(a == mainloop_api && id && stdio_source == id);
if (!buffer) {
mainloop_api->enable_io(mainloop_api, stdio_source, PA_MAINLOOP_API_IO_EVENT_NULL);
return;
}
assert(buffer_length);
if ((r = write(fd, buffer+buffer_index, buffer_length)) <= 0) {
fprintf(stderr, "write() failed: %s\n", strerror(errno));
quit(1);
mainloop_api->cancel_io(mainloop_api, stdio_source);
stdio_source = NULL;
return;
}
buffer_length -= r;
buffer_index += r;
if (!buffer_length) {
free(buffer);
buffer = NULL;
buffer_length = buffer_index = 0;
}
}
static void exit_signal_callback(void *id, int sig, void *userdata) {
fprintf(stderr, "Got SIGINT, exiting.\n");
quit(0);
}
static void stream_get_latency_callback(struct pa_stream *s, uint32_t latency, void *userdata) {
assert(s);
if (latency == (uint32_t) -1) {
fprintf(stderr, "Failed to get latency: %s\n", strerror(errno));
quit(1);
return;
}
fprintf(stderr, "Current latency is %u usecs.\n", latency);
}
static void sigusr1_signal_callback(void *id, int sig, void *userdata) {
if (mode == PLAYBACK) {
fprintf(stderr, "Got SIGUSR1, requesting latency.\n");
pa_stream_get_latency(stream, stream_get_latency_callback, NULL);
}
}
int main(int argc, char *argv[]) {
struct pa_mainloop* m = NULL;
int ret = 1, r;
char *bn;
if (!(bn = strrchr(argv[0], '/')))
bn = argv[0];
if (strstr(bn, "rec") || strstr(bn, "mon"))
mode = RECORD;
else if (strstr(bn, "cat") || strstr(bn, "play"))
mode = PLAYBACK;
fprintf(stderr, "Opening a %s stream.\n", mode == RECORD ? "recording" : "playback");
if (!(m = pa_mainloop_new())) {
fprintf(stderr, "pa_mainloop_new() failed.\n");
goto quit;
}
mainloop_api = pa_mainloop_get_api(m);
r = pa_signal_init(mainloop_api);
assert(r == 0);
pa_signal_register(SIGINT, exit_signal_callback, NULL);
pa_signal_register(SIGUSR1, sigusr1_signal_callback, NULL);
signal(SIGPIPE, SIG_IGN);
if (!(stdio_source = mainloop_api->source_io(mainloop_api,
mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
mode == PLAYBACK ? PA_MAINLOOP_API_IO_EVENT_INPUT : PA_MAINLOOP_API_IO_EVENT_OUTPUT,
mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) {
fprintf(stderr, "source_io() failed.\n");
goto quit;
}
if (!(context = pa_context_new(mainloop_api, argv[0]))) {
fprintf(stderr, "pa_context_new() failed.\n");
goto quit;
}
if (pa_context_connect(context, NULL, context_complete_callback, NULL) < 0) {
fprintf(stderr, "pa_context_connext() failed.\n");
goto quit;
}
pa_context_set_die_callback(context, context_die_callback, NULL);
if (pa_mainloop_run(m, &ret) < 0) {
fprintf(stderr, "pa_mainloop_run() failed.\n");
goto quit;
}
quit:
if (stream)
pa_stream_free(stream);
if (context)
pa_context_free(context);
if (m) {
pa_signal_done();
pa_mainloop_free(m);
}
if (buffer)
free(buffer);
return ret;
}

72
polyp/packet.c Normal file
View file

@ -0,0 +1,72 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include "packet.h"
struct pa_packet* pa_packet_new(size_t length) {
struct pa_packet *p;
assert(length);
p = malloc(sizeof(struct pa_packet)+length);
assert(p);
p->ref = 1;
p->length = length;
p->data = (uint8_t*) (p+1);
p->type = PA_PACKET_APPENDED;
return p;
}
struct pa_packet* pa_packet_new_dynamic(uint8_t* data, size_t length) {
struct pa_packet *p;
assert(data && length);
p = malloc(sizeof(struct pa_packet));
assert(p);
p->ref = 1;
p->length = length;
p->data = data;
p->type = PA_PACKET_DYNAMIC;
return p;
}
struct pa_packet* pa_packet_ref(struct pa_packet *p) {
assert(p && p->ref >= 1);
p->ref++;
return p;
}
void pa_packet_unref(struct pa_packet *p) {
assert(p && p->ref >= 1);
p->ref--;
if (p->ref == 0) {
if (p->type == PA_PACKET_DYNAMIC)
free(p->data);
free(p);
}
}

41
polyp/packet.h Normal file
View file

@ -0,0 +1,41 @@
#ifndef foopackethfoo
#define foopackethfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 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 <stdint.h>
struct pa_packet {
enum { PA_PACKET_APPENDED, PA_PACKET_DYNAMIC } type;
unsigned ref;
size_t length;
uint8_t *data;
};
struct pa_packet* pa_packet_new(size_t length);
struct pa_packet* pa_packet_new_dynamic(uint8_t* data, size_t length);
struct pa_packet* pa_packet_ref(struct pa_packet *p);
void pa_packet_unref(struct pa_packet *p);
#endif

166
polyp/pactl.c Normal file
View file

@ -0,0 +1,166 @@
/* $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 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 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 <signal.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "polyplib.h"
#include "polyplib-error.h"
#include "mainloop.h"
#include "mainloop-signal.h"
static struct pa_context *context = NULL;
static struct pa_mainloop_api *mainloop_api = NULL;
static enum {
NONE,
EXIT,
STAT
} action = NONE;
static void quit(int ret) {
assert(mainloop_api);
mainloop_api->quit(mainloop_api, ret);
}
static void context_die_callback(struct pa_context *c, void *userdata) {
assert(c);
fprintf(stderr, "Connection to server shut down, exiting.\n");
quit(1);
}
static void context_drain_complete(struct pa_context *c, void *userdata) {
assert(c);
fprintf(stderr, "Connection to server shut down, exiting.\n");
quit(0);
}
static void drain(void) {
if (pa_context_drain(context, context_drain_complete, NULL) < 0)
quit(0);
}
static void stat_callback(struct pa_context *c, uint32_t blocks, uint32_t total, void *userdata) {
if (blocks == (uint32_t) -1) {
fprintf(stderr, "Failed to get statistics: %s\n", pa_strerror(pa_context_errno(c)));
quit(1);
return;
}
fprintf(stderr, "Currently in use: %u blocks containing %u bytes total.\n", blocks, total);
drain();
}
static void context_complete_callback(struct pa_context *c, int success, void *userdata) {
assert(c);
if (!success) {
fprintf(stderr, "Connection failed: %s\n", pa_strerror(pa_context_errno(c)));
goto fail;
}
fprintf(stderr, "Connection established.\n");
if (action == STAT)
pa_context_stat(c, stat_callback, NULL);
else {
assert(action == EXIT);
pa_context_exit(c);
drain();
}
return;
fail:
quit(1);
}
static void exit_signal_callback(void *id, int sig, void *userdata) {
fprintf(stderr, "Got SIGINT, exiting.\n");
quit(0);
}
int main(int argc, char *argv[]) {
struct pa_mainloop* m = NULL;
int ret = 1, r;
if (argc >= 2) {
if (!strcmp(argv[1], "stat"))
action = STAT;
else if (!strcmp(argv[1], "exit"))
action = EXIT;
}
if (action == NONE) {
fprintf(stderr, "No valid action specified. Use one of: stat, exit\n");
goto quit;
}
if (!(m = pa_mainloop_new())) {
fprintf(stderr, "pa_mainloop_new() failed.\n");
goto quit;
}
mainloop_api = pa_mainloop_get_api(m);
r = pa_signal_init(mainloop_api);
assert(r == 0);
pa_signal_register(SIGINT, exit_signal_callback, NULL);
signal(SIGPIPE, SIG_IGN);
if (!(context = pa_context_new(mainloop_api, argv[0]))) {
fprintf(stderr, "pa_context_new() failed.\n");
goto quit;
}
if (pa_context_connect(context, NULL, context_complete_callback, NULL) < 0) {
fprintf(stderr, "pa_context_connext() failed.\n");
goto quit;
}
pa_context_set_die_callback(context, context_die_callback, NULL);
if (pa_mainloop_run(m, &ret) < 0) {
fprintf(stderr, "pa_mainloop_run() failed.\n");
goto quit;
}
quit:
if (context)
pa_context_free(context);
if (m) {
pa_signal_done();
pa_mainloop_free(m);
}
return ret;
}

94
polyp/parec-simple.c Normal file
View file

@ -0,0 +1,94 @@
/* $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 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 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 <unistd.h>
#include <string.h>
#include <errno.h>
#include "polyplib-simple.h"
#include "polyplib-error.h"
#define BUFSIZE 1024
static ssize_t loop_write(int fd, const void*data, size_t size) {
ssize_t ret = 0;
while (size > 0) {
ssize_t r;
if ((r = write(fd, data, size)) < 0)
return r;
if (r == 0)
break;
ret += r;
data += r;
size -= r;
}
return ret;
}
int main(int argc, char*argv[]) {
static const struct pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = 44100,
.channels = 2
};
struct pa_simple *s = NULL;
int ret = 1;
int error;
if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, NULL, "record", &ss, NULL, &error))) {
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
goto finish;
}
for (;;) {
uint8_t buf[BUFSIZE];
ssize_t r;
if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) {
fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error));
goto finish;
}
if ((r = loop_write(STDOUT_FILENO, buf, sizeof(buf))) <= 0) {
fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno));
goto finish;
}
}
ret = 0;
finish:
if (s)
pa_simple_free(s);
return ret;
}

247
polyp/pdispatch.c Normal file
View file

@ -0,0 +1,247 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "pdispatch.h"
#include "native-common.h"
#ifdef DEBUG_OPCODES
static const char *command_names[PA_COMMAND_MAX] = {
[PA_COMMAND_ERROR] = "ERROR",
[PA_COMMAND_TIMEOUT] = "TIMEOUT",
[PA_COMMAND_REPLY] = "REPLY",
[PA_COMMAND_CREATE_PLAYBACK_STREAM] = "CREATE_PLAYBACK_STREAM",
[PA_COMMAND_DELETE_PLAYBACK_STREAM] = "DELETE_PLAYBACK_STREAM",
[PA_COMMAND_CREATE_RECORD_STREAM] = "CREATE_RECORD_STREAM",
[PA_COMMAND_DELETE_RECORD_STREAM] = "DELETE_RECORD_STREAM",
[PA_COMMAND_AUTH] = "AUTH",
[PA_COMMAND_REQUEST] = "REQUEST",
[PA_COMMAND_EXIT] = "EXIT",
[PA_COMMAND_SET_NAME] = "SET_NAME",
[PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK",
[PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE",
[PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM",
[PA_COMMAND_PLAYBACK_STREAM_KILLED] = "PLAYBACK_STREAM_KILLED",
[PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED",
[PA_COMMAND_STAT] = "STAT",
[PA_COMMAND_GET_PLAYBACK_LATENCY] = "PLAYBACK_LATENCY",
};
#endif
struct reply_info {
struct pa_pdispatch *pdispatch;
struct reply_info *next, *previous;
void (*callback)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
void *userdata;
uint32_t tag;
void *mainloop_timeout;
int callback_is_running;
};
struct pa_pdispatch {
struct pa_mainloop_api *mainloop;
const struct pa_pdispatch_command *command_table;
unsigned n_commands;
struct reply_info *replies;
void (*drain_callback)(struct pa_pdispatch *pd, void *userdata);
void *drain_userdata;
int in_use, shall_free;
};
static void reply_info_free(struct reply_info *r) {
assert(r && r->pdispatch && r->pdispatch->mainloop);
if (r->pdispatch)
r->pdispatch->mainloop->cancel_time(r->pdispatch->mainloop, r->mainloop_timeout);
if (r->previous)
r->previous->next = r->next;
else
r->pdispatch->replies = r->next;
if (r->next)
r->next->previous = r->previous;
free(r);
}
struct pa_pdispatch* pa_pdispatch_new(struct pa_mainloop_api *mainloop, const struct pa_pdispatch_command*table, unsigned entries) {
struct pa_pdispatch *pd;
assert(mainloop);
assert((entries && table) || (!entries && !table));
pd = malloc(sizeof(struct pa_pdispatch));
assert(pd);
pd->mainloop = mainloop;
pd->command_table = table;
pd->n_commands = entries;
pd->replies = NULL;
pd->drain_callback = NULL;
pd->drain_userdata = NULL;
pd->in_use = pd->shall_free = 0;
return pd;
}
void pa_pdispatch_free(struct pa_pdispatch *pd) {
assert(pd);
if (pd->in_use) {
pd->shall_free = 1;
return;
}
while (pd->replies)
reply_info_free(pd->replies);
free(pd);
}
int pa_pdispatch_run(struct pa_pdispatch *pd, struct pa_packet*packet, void *userdata) {
uint32_t tag, command;
struct pa_tagstruct *ts = NULL;
int ret = -1;
assert(pd && packet && packet->data && !pd->in_use);
if (packet->length <= 8)
goto finish;
ts = pa_tagstruct_new(packet->data, packet->length);
assert(ts);
if (pa_tagstruct_getu32(ts, &command) < 0 ||
pa_tagstruct_getu32(ts, &tag) < 0)
goto finish;
#ifdef DEBUG_OPCODES
fprintf(stderr, __FILE__": Recieved opcode <%s>\n", command_names[command]);
#endif
if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
struct reply_info *r;
for (r = pd->replies; r; r = r->next)
if (r->tag == tag)
break;
if (r) {
pd->in_use = r->callback_is_running = 1;
assert(r->callback);
r->callback(r->pdispatch, command, tag, ts, r->userdata);
pd->in_use = r->callback_is_running = 0;
reply_info_free(r);
if (pd->shall_free)
pa_pdispatch_free(pd);
else {
if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
pd->drain_callback(pd, pd->drain_userdata);
}
}
} else if (pd->command_table && command < pd->n_commands) {
const struct pa_pdispatch_command *c = pd->command_table+command;
if (c->proc)
c->proc(pd, command, tag, ts, userdata);
} else
goto finish;
ret = 0;
finish:
if (ts)
pa_tagstruct_free(ts);
return ret;
}
static void timeout_callback(struct pa_mainloop_api*m, void *id, const struct timeval *tv, void *userdata) {
struct reply_info*r = userdata;
assert (r && r->mainloop_timeout == id && r->pdispatch && r->pdispatch->mainloop == m && r->callback);
r->callback(r->pdispatch, PA_COMMAND_TIMEOUT, r->tag, NULL, r->userdata);
reply_info_free(r);
if (r->pdispatch->drain_callback && !pa_pdispatch_is_pending(r->pdispatch))
r->pdispatch->drain_callback(r->pdispatch, r->pdispatch->drain_userdata);
}
void pa_pdispatch_register_reply(struct pa_pdispatch *pd, uint32_t tag, int timeout, void (*cb)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata), void *userdata) {
struct reply_info *r;
struct timeval tv;
assert(pd && cb);
r = malloc(sizeof(struct reply_info));
assert(r);
r->pdispatch = pd;
r->callback = cb;
r->userdata = userdata;
r->tag = tag;
r->callback_is_running = 0;
gettimeofday(&tv, NULL);
tv.tv_sec += timeout;
r->mainloop_timeout = pd->mainloop->source_time(pd->mainloop, &tv, timeout_callback, r);
assert(r->mainloop_timeout);
r->previous = NULL;
r->next = pd->replies;
if (r->next)
r->next->previous = r;
pd->replies = r;
}
int pa_pdispatch_is_pending(struct pa_pdispatch *pd) {
assert(pd);
return !!pd->replies;
}
void pa_pdispatch_set_drain_callback(struct pa_pdispatch *pd, void (*cb)(struct pa_pdispatch *pd, void *userdata), void *userdata) {
assert(pd);
assert(!cb || pa_pdispatch_is_pending(pd));
pd->drain_callback = cb;
pd->drain_userdata = userdata;
}
void pa_pdispatch_unregister_reply(struct pa_pdispatch *pd, void *userdata) {
struct reply_info *r, *n;
assert(pd);
for (r = pd->replies; r; r = n) {
n = r->next;
if (!r->callback_is_running && r->userdata == userdata) /* when this item's callback is currently running it is destroyed anyway in the very near future */
reply_info_free(r);
}
}

52
polyp/pdispatch.h Normal file
View file

@ -0,0 +1,52 @@
#ifndef foopdispatchhfoo
#define foopdispatchhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
#include "tagstruct.h"
#include "packet.h"
#include "mainloop-api.h"
struct pa_pdispatch;
/* It is safe to destroy the calling pdispatch object from all callbacks */
struct pa_pdispatch_command {
void (*proc)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
};
struct pa_pdispatch* pa_pdispatch_new(struct pa_mainloop_api *m, const struct pa_pdispatch_command*table, unsigned entries);
void pa_pdispatch_free(struct pa_pdispatch *pd);
int pa_pdispatch_run(struct pa_pdispatch *pd, struct pa_packet*p, void *userdata);
void pa_pdispatch_register_reply(struct pa_pdispatch *pd, uint32_t tag, int timeout, void (*cb)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata), void *userdata);
int pa_pdispatch_is_pending(struct pa_pdispatch *pd);
void pa_pdispatch_set_drain_callback(struct pa_pdispatch *pd, void (*cb)(struct pa_pdispatch *pd, void *userdata), void *userdata);
/* Remove all reply slots with the give userdata parameter */
void pa_pdispatch_unregister_reply(struct pa_pdispatch *pd, void *userdata);
#endif

41
polyp/polypaudio.pa Executable file
View file

@ -0,0 +1,41 @@
#!./polypaudio -F
#
# This file is part of polypaudio.
#
# polypaudio is free software; you can redistribute it and/or modify it
# under the terms of the GNU 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 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.
# Load audio drivers
load module-alsa-sink
load module-alsa-source device=plughw:1,0
#load module-oss device="/dev/dsp"
#load module-oss-mmap device="/dev/dsp"
# Load several protocols
load module-esound-protocol-tcp
load module-simple-protocol-tcp
load module-native-protocol-unix
load module-cli-protocol-unix
# Load the CLI module
load module-cli
.nofail
# Make some devices default
sink_default alsa_output
source_default alsa_input

40
polyp/polyplib-def.h Normal file
View file

@ -0,0 +1,40 @@
#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 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 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>
enum pa_stream_direction {
PA_STREAM_PLAYBACK,
PA_STREAM_RECORD
};
struct pa_buffer_attr {
uint32_t maxlength;
uint32_t tlength;
uint32_t prebuf;
uint32_t minreq;
uint32_t fragsize;
};
#endif

53
polyp/polyplib-error.c Normal file
View file

@ -0,0 +1,53 @@
/* $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 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 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 "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 corrupt",
[PA_ERROR_TIMEOUT] = "Timeout",
[PA_ERROR_AUTHKEY] = "Not authorization key",
[PA_ERROR_INTERNAL] = "Internal error",
[PA_ERROR_CONNECTIONTERMINATED] = "Connection terminated",
[PA_ERROR_KILLED] = "Entity killed",
};
const char*pa_strerror(uint32_t error) {
if (error >= PA_ERROR_MAX)
return NULL;
return errortab[error];
}

29
polyp/polyplib-error.h Normal file
View file

@ -0,0 +1,29 @@
#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 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 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>
const char* pa_strerror(uint32_t error);
#endif

259
polyp/polyplib-simple.c Normal file
View file

@ -0,0 +1,259 @@
/* $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 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 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 "native-common.h"
/*#include "polyp-error.h"*/
struct pa_simple {
struct pa_mainloop *mainloop;
struct pa_context *context;
struct pa_stream *stream;
enum pa_stream_direction direction;
int dead, drained;
void *read_data;
size_t read_index, read_length;
};
static void read_callback(struct pa_stream *s, const void*data, size_t length, void *userdata);
static int check_error(struct pa_simple *p, int *perror) {
assert(p);
if (pa_context_is_dead(p->context) || (p->stream && pa_stream_is_dead(p->stream))) {
if (perror)
*perror = pa_context_errno(p->context);
return -1;
}
return 0;
}
static int iterate(struct pa_simple *p, int block, int *perror) {
assert(p && p->context && p->mainloop);
if (check_error(p, perror) < 0)
return -1;
if (!block && !pa_context_is_pending(p->context))
return 0;
do {
if (pa_mainloop_iterate(p->mainloop, 1, NULL) < 0) {
if (perror)
*perror = PA_ERROR_INTERNAL;
return -1;
}
if (check_error(p, perror) < 0)
return -1;
} while (pa_context_is_pending(p->context));
return 0;
}
struct pa_simple* pa_simple_new(
const char *server,
const char *name,
enum pa_stream_direction dir,
const char *dev,
const char *stream_name,
const struct pa_sample_spec *ss,
const struct pa_buffer_attr *attr,
int *perror) {
struct pa_simple *p;
int error = PA_ERROR_INTERNAL;
assert(ss);
p = malloc(sizeof(struct pa_simple));
assert(p);
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;
if (!(p->context = pa_context_new(pa_mainloop_get_api(p->mainloop), name)))
goto fail;
if (pa_context_connect(p->context, server, NULL, NULL) < 0) {
error = pa_context_errno(p->context);
goto fail;
}
/* Wait until the context is ready */
while (!pa_context_is_ready(p->context)) {
if (iterate(p, 1, &error) < 0)
goto fail;
}
if (!(p->stream = pa_stream_new(p->context, dir, dev, stream_name, ss, attr, NULL, NULL)))
goto fail;
/* Wait until the stream is ready */
while (!pa_stream_is_ready(p->stream)) {
if (iterate(p, 1, &error) < 0)
goto fail;
}
pa_stream_set_read_callback(p->stream, read_callback, p);
return p;
fail:
if (perror)
*perror = error;
pa_simple_free(p);
return NULL;
}
void pa_simple_free(struct pa_simple *s) {
assert(s);
free(s->read_data);
if (s->stream)
pa_stream_free(s->stream);
if (s->context)
pa_context_free(s->context);
if (s->mainloop)
pa_mainloop_free(s->mainloop);
free(s);
}
int pa_simple_write(struct pa_simple *p, const void*data, size_t length, int *perror) {
assert(p && data && p->direction == PA_STREAM_PLAYBACK);
while (length > 0) {
size_t l;
while (!(l = pa_stream_writable_size(p->stream)))
if (iterate(p, 1, perror) < 0)
return -1;
if (l > length)
l = length;
pa_stream_write(p->stream, data, l);
data += l;
length -= l;
}
/* Make sure that no data is pending for write */
if (iterate(p, 0, perror) < 0)
return -1;
return 0;
}
static void read_callback(struct pa_stream *s, const void*data, size_t length, void *userdata) {
struct pa_simple *p = userdata;
assert(s && data && length && p);
if (p->read_data) {
fprintf(stderr, __FILE__": Buffer overflow, dropping incoming memory blocks.\n");
free(p->read_data);
}
p->read_data = malloc(p->read_length = length);
assert(p->read_data);
memcpy(p->read_data, data, length);
p->read_index = 0;
}
int pa_simple_read(struct pa_simple *p, void*data, size_t length, int *perror) {
assert(p && data && p->direction == PA_STREAM_RECORD);
while (length > 0) {
if (p->read_data) {
size_t l = length;
if (p->read_length <= l)
l = p->read_length;
memcpy(data, p->read_data+p->read_index, l);
data += l;
length -= l;
p->read_index += l;
p->read_length -= l;
if (!p->read_length) {
free(p->read_data);
p->read_data = NULL;
p->read_index = 0;
}
if (!length)
return 0;
assert(!p->read_data);
}
if (iterate(p, 1, perror) < 0)
return -1;
}
return 0;
}
static void drain_complete(struct pa_stream *s, void *userdata) {
struct pa_simple *p = userdata;
assert(s && p);
p->drained = 1;
}
int pa_simple_drain(struct pa_simple *p, int *perror) {
assert(p && p->direction == PA_STREAM_PLAYBACK);
p->drained = 0;
pa_stream_drain(p->stream, drain_complete, p);
while (!p->drained) {
if (iterate(p, 1, perror) < 0) {
pa_stream_drain(p->stream, NULL, NULL);
return -1;
}
}
return 0;
}

49
polyp/polyplib-simple.h Normal file
View file

@ -0,0 +1,49 @@
#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 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 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"
struct pa_simple;
struct pa_simple* pa_simple_new(
const char *server,
const char *name,
enum pa_stream_direction dir,
const char *dev,
const char *stream_name,
const struct pa_sample_spec *ss,
const struct pa_buffer_attr *attr,
int *error);
void pa_simple_free(struct pa_simple *s);
int pa_simple_write(struct pa_simple *s, const void*data, size_t length, int *error);
int pa_simple_drain(struct pa_simple *s, int *error);
int pa_simple_read(struct pa_simple *s, void*data, size_t length, int *error);
#endif

973
polyp/polyplib.c Normal file
View file

@ -0,0 +1,973 @@
/* $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 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 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 <sys/socket.h>
#include <netdb.h>
#include "polyplib.h"
#include "native-common.h"
#include "pdispatch.h"
#include "pstream.h"
#include "dynarray.h"
#include "socket-client.h"
#include "pstream-util.h"
#include "authkey.h"
#include "util.h"
#define DEFAULT_MAXLENGTH 204800
#define DEFAULT_TLENGTH 10240
#define DEFAULT_PREBUF 4096
#define DEFAULT_MINREQ 1024
#define DEFAULT_FRAGSIZE 1024
#define DEFAULT_TIMEOUT (5*60)
#define DEFAULT_SERVER "/tmp/polypaudio/native"
#define DEFAULT_PORT "4713"
struct pa_context {
char *name;
struct pa_mainloop_api* mainloop;
struct pa_socket_client *client;
struct pa_pstream *pstream;
struct pa_pdispatch *pdispatch;
struct pa_dynarray *record_streams, *playback_streams;
struct pa_stream *first_stream;
uint32_t ctag;
uint32_t error;
enum {
CONTEXT_UNCONNECTED,
CONTEXT_CONNECTING,
CONTEXT_AUTHORIZING,
CONTEXT_SETTING_NAME,
CONTEXT_READY,
CONTEXT_DEAD
} state;
void (*connect_complete_callback)(struct pa_context*c, int success, void *userdata);
void *connect_complete_userdata;
void (*drain_complete_callback)(struct pa_context*c, void *userdata);
void *drain_complete_userdata;
void (*die_callback)(struct pa_context*c, void *userdata);
void *die_userdata;
void (*stat_callback)(struct pa_context*c, uint32_t count, uint32_t total, void *userdata);
void *stat_userdata;
uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
};
struct pa_stream {
struct pa_context *context;
struct pa_stream *next, *previous;
char *name;
struct pa_buffer_attr buffer_attr;
struct pa_sample_spec sample_spec;
uint32_t device_index;
uint32_t channel;
int channel_valid;
enum pa_stream_direction direction;
enum { STREAM_LOOKING_UP, STREAM_CREATING, STREAM_READY, STREAM_DEAD} state;
uint32_t requested_bytes;
void (*read_callback)(struct pa_stream *p, const void*data, size_t length, void *userdata);
void *read_userdata;
void (*write_callback)(struct pa_stream *p, size_t length, void *userdata);
void *write_userdata;
void (*create_complete_callback)(struct pa_stream *s, int success, void *userdata);
void *create_complete_userdata;
void (*drain_complete_callback)(struct pa_stream *s, void *userdata);
void *drain_complete_userdata;
void (*die_callback)(struct pa_stream*c, void *userdata);
void *die_userdata;
void (*get_latency_callback)(struct pa_stream*c, uint32_t latency, void *userdata);
void *get_latency_userdata;
};
static void command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
static void command_playback_stream_killed(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_ERROR] = { NULL },
[PA_COMMAND_REPLY] = { NULL },
[PA_COMMAND_CREATE_PLAYBACK_STREAM] = { NULL },
[PA_COMMAND_DELETE_PLAYBACK_STREAM] = { NULL },
[PA_COMMAND_CREATE_RECORD_STREAM] = { NULL },
[PA_COMMAND_DELETE_RECORD_STREAM] = { NULL },
[PA_COMMAND_EXIT] = { NULL },
[PA_COMMAND_REQUEST] = { command_request },
[PA_COMMAND_PLAYBACK_STREAM_KILLED] = { command_playback_stream_killed },
[PA_COMMAND_RECORD_STREAM_KILLED] = { command_playback_stream_killed },
};
struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *name) {
struct pa_context *c;
assert(mainloop && name);
c = malloc(sizeof(struct pa_context));
assert(c);
c->name = strdup(name);
c->mainloop = mainloop;
c->client = NULL;
c->pstream = NULL;
c->pdispatch = NULL;
c->playback_streams = pa_dynarray_new();
assert(c->playback_streams);
c->record_streams = pa_dynarray_new();
assert(c->record_streams);
c->first_stream = NULL;
c->error = PA_ERROR_OK;
c->state = CONTEXT_UNCONNECTED;
c->ctag = 0;
c->connect_complete_callback = NULL;
c->connect_complete_userdata = NULL;
c->drain_complete_callback = NULL;
c->drain_complete_userdata = NULL;
c->die_callback = NULL;
c->die_userdata = NULL;
c->stat_callback = NULL;
c->stat_userdata = NULL;
pa_check_for_sigpipe();
return c;
}
void pa_context_free(struct pa_context *c) {
assert(c);
while (c->first_stream)
pa_stream_free(c->first_stream);
if (c->client)
pa_socket_client_free(c->client);
if (c->pdispatch)
pa_pdispatch_free(c->pdispatch);
if (c->pstream)
pa_pstream_free(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);
free(c->name);
free(c);
}
static void stream_dead(struct pa_stream *s) {
assert(s);
if (s->state == STREAM_DEAD)
return;
if (s->state == STREAM_READY) {
s->state = STREAM_DEAD;
if (s->die_callback)
s->die_callback(s, s->die_userdata);
} else
s->state = STREAM_DEAD;
}
static void context_dead(struct pa_context *c) {
struct pa_stream *s;
assert(c);
if (c->state == CONTEXT_DEAD)
return;
if (c->pdispatch)
pa_pdispatch_free(c->pdispatch);
c->pdispatch = NULL;
if (c->pstream)
pa_pstream_free(c->pstream);
c->pstream = NULL;
if (c->client)
pa_socket_client_free(c->client);
c->client = NULL;
for (s = c->first_stream; s; s = s->next)
stream_dead(s);
if (c->state == CONTEXT_READY) {
c->state = CONTEXT_DEAD;
if (c->die_callback)
c->die_callback(c, c->die_userdata);
} else
c->state = CONTEXT_DEAD;
}
static void pstream_die_callback(struct pa_pstream *p, void *userdata) {
struct pa_context *c = userdata;
assert(p && c);
c->error = PA_ERROR_CONNECTIONTERMINATED;
context_dead(c);
}
static void pstream_packet_callback(struct pa_pstream *p, struct pa_packet *packet, void *userdata) {
struct pa_context *c = userdata;
assert(p && packet && c);
if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) {
fprintf(stderr, "polyp.c: invalid packet.\n");
c->error = PA_ERROR_PROTOCOL;
context_dead(c);
}
}
static void pstream_memblock_callback(struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata) {
struct pa_context *c = userdata;
struct pa_stream *s;
assert(p && chunk && c && chunk->memblock && chunk->memblock->data);
if (!(s = pa_dynarray_get(c->record_streams, channel)))
return;
if (s->read_callback)
s->read_callback(s, chunk->memblock->data + chunk->index, chunk->length, s->read_userdata);
}
static int handle_error(struct pa_context *c, uint32_t command, struct pa_tagstruct *t) {
assert(c && t);
if (command == PA_COMMAND_ERROR) {
if (pa_tagstruct_getu32(t, &c->error) < 0) {
c->error = PA_ERROR_PROTOCOL;
return -1;
}
return 0;
}
c->error = (command == PA_COMMAND_TIMEOUT) ? PA_ERROR_TIMEOUT : PA_ERROR_INTERNAL;
return -1;
}
static void setup_complete_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct pa_context *c = userdata;
assert(pd && c && (c->state == CONTEXT_AUTHORIZING || c->state == CONTEXT_SETTING_NAME));
if (command != PA_COMMAND_REPLY) {
handle_error(c, command, t);
context_dead(c);
if (c->connect_complete_callback)
c->connect_complete_callback(c, 0, c->connect_complete_userdata);
return;
}
if (c->state == CONTEXT_AUTHORIZING) {
struct pa_tagstruct *t;
c->state = CONTEXT_SETTING_NAME;
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_SET_NAME);
pa_tagstruct_putu32(t, tag = c->ctag++);
pa_tagstruct_puts(t, c->name);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c);
} else {
assert(c->state == CONTEXT_SETTING_NAME);
c->state = CONTEXT_READY;
if (c->connect_complete_callback)
c->connect_complete_callback(c, 1, c->connect_complete_userdata);
}
return;
}
static void on_connection(struct pa_socket_client *client, struct pa_iochannel*io, void *userdata) {
struct pa_context *c = userdata;
struct pa_tagstruct *t;
uint32_t tag;
assert(client && c && c->state == CONTEXT_CONNECTING);
pa_socket_client_free(client);
c->client = NULL;
if (!io) {
c->error = PA_ERROR_CONNECTIONREFUSED;
context_dead(c);
if (c->connect_complete_callback)
c->connect_complete_callback(c, 0, c->connect_complete_userdata);
return;
}
c->pstream = pa_pstream_new(c->mainloop, io);
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);
c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX);
assert(c->pdispatch);
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->auth_cookie, sizeof(c->auth_cookie));
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c);
c->state = CONTEXT_AUTHORIZING;
}
static struct sockaddr *resolve_server(const char *server, size_t *len) {
struct sockaddr *sa;
struct addrinfo hints, *result = NULL;
char *port;
assert(server && len);
if ((port = strrchr(server, ':')))
port++;
if (!port)
port = DEFAULT_PORT;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
if (getaddrinfo(server, port, &hints, &result) != 0)
return NULL;
assert(result);
sa = malloc(*len = result->ai_addrlen);
assert(sa);
memcpy(sa, result->ai_addr, *len);
freeaddrinfo(result);
return sa;
}
int pa_context_connect(struct pa_context *c, const char *server, void (*complete) (struct pa_context*c, int success, void *userdata), void *userdata) {
assert(c && c->state == CONTEXT_UNCONNECTED);
if (pa_authkey_load_from_home(PA_NATIVE_COOKIE_FILE, c->auth_cookie, sizeof(c->auth_cookie)) < 0) {
c->error = PA_ERROR_AUTHKEY;
return -1;
}
if (!server)
if (!(server = getenv("POLYP_SERVER")))
server = DEFAULT_SERVER;
assert(!c->client);
if (*server == '/') {
if (!(c->client = pa_socket_client_new_unix(c->mainloop, server))) {
c->error = PA_ERROR_CONNECTIONREFUSED;
return -1;
}
} else {
struct sockaddr* sa;
size_t sa_len;
if (!(sa = resolve_server(server, &sa_len))) {
c->error = PA_ERROR_INVALIDSERVER;
return -1;
}
c->client = pa_socket_client_new_sockaddr(c->mainloop, sa, sa_len);
free(sa);
if (!c->client) {
c->error = PA_ERROR_CONNECTIONREFUSED;
return -1;
}
}
c->connect_complete_callback = complete;
c->connect_complete_userdata = userdata;
pa_socket_client_set_callback(c->client, on_connection, c);
c->state = CONTEXT_CONNECTING;
return 0;
}
int pa_context_is_dead(struct pa_context *c) {
assert(c);
return c->state == CONTEXT_DEAD;
}
int pa_context_is_ready(struct pa_context *c) {
assert(c);
return c->state == CONTEXT_READY;
}
int pa_context_errno(struct pa_context *c) {
assert(c);
return c->error;
}
void pa_context_set_die_callback(struct pa_context *c, void (*cb)(struct pa_context *c, void *userdata), void *userdata) {
assert(c);
c->die_callback = cb;
c->die_userdata = userdata;
}
static void command_playback_stream_killed(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct pa_context *c = userdata;
struct pa_stream *s;
uint32_t channel;
assert(pd && (command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED) && t && c);
if (pa_tagstruct_getu32(t, &channel) < 0 ||
!pa_tagstruct_eof(t)) {
c->error = PA_ERROR_PROTOCOL;
context_dead(c);
return;
}
if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel)))
return;
c->error = PA_ERROR_KILLED;
stream_dead(s);
}
static void command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct pa_stream *s;
struct pa_context *c = userdata;
uint32_t bytes, channel;
assert(pd && command == PA_COMMAND_REQUEST && t && c);
if (pa_tagstruct_getu32(t, &channel) < 0 ||
pa_tagstruct_getu32(t, &bytes) < 0 ||
!pa_tagstruct_eof(t)) {
c->error = PA_ERROR_PROTOCOL;
context_dead(c);
return;
}
if (!(s = pa_dynarray_get(c->playback_streams, channel)))
return;
if (s->state != STREAM_READY)
return;
s->requested_bytes += bytes;
if (s->requested_bytes && s->write_callback)
s->write_callback(s, s->requested_bytes, s->write_userdata);
}
static void create_stream_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct pa_stream *s = userdata;
assert(pd && s && s->state == STREAM_CREATING);
if (command != PA_COMMAND_REPLY) {
if (handle_error(s->context, command, t) < 0) {
context_dead(s->context);
return;
}
stream_dead(s);
if (s->create_complete_callback)
s->create_complete_callback(s, 0, s->create_complete_userdata);
return;
}
if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
pa_tagstruct_getu32(t, &s->device_index) < 0 ||
!pa_tagstruct_eof(t)) {
s->context->error = PA_ERROR_PROTOCOL;
context_dead(s->context);
return;
}
s->channel_valid = 1;
pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, s);
s->state = STREAM_READY;
if (s->create_complete_callback)
s->create_complete_callback(s, 1, s->create_complete_userdata);
}
static void create_stream(struct pa_stream *s, uint32_t tdev_index) {
struct pa_tagstruct *t;
uint32_t tag;
assert(s);
s->state = STREAM_CREATING;
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_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, tdev_index);
pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
if (s->direction == PA_STREAM_PLAYBACK) {
pa_tagstruct_putu32(t, s->buffer_attr.tlength);
pa_tagstruct_putu32(t, s->buffer_attr.prebuf);
pa_tagstruct_putu32(t, s->buffer_attr.minreq);
} 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, create_stream_callback, s);
}
static void lookup_device_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct pa_stream *s = userdata;
uint32_t tdev;
assert(pd && s && s->state == STREAM_LOOKING_UP);
if (command != PA_COMMAND_REPLY) {
if (handle_error(s->context, command, t) < 0) {
context_dead(s->context);
return;
}
stream_dead(s);
if (s->create_complete_callback)
s->create_complete_callback(s, 0, s->create_complete_userdata);
return;
}
if (pa_tagstruct_getu32(t, &tdev) < 0 ||
!pa_tagstruct_eof(t)) {
s->context->error = PA_ERROR_PROTOCOL;
context_dead(s->context);
return;
}
create_stream(s, tdev);
}
static void lookup_device(struct pa_stream *s, const char *tdev) {
struct pa_tagstruct *t;
uint32_t tag;
assert(s);
s->state = STREAM_LOOKING_UP;
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_LOOKUP_SINK : PA_COMMAND_LOOKUP_SOURCE);
pa_tagstruct_putu32(t, tag = s->context->ctag++);
pa_tagstruct_puts(t, tdev);
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, lookup_device_callback, s);
}
struct pa_stream* pa_stream_new(
struct pa_context *c,
enum pa_stream_direction dir,
const char *dev,
const char *name,
const struct pa_sample_spec *ss,
const struct pa_buffer_attr *attr,
void (*complete) (struct pa_stream*s, int success, void *userdata),
void *userdata) {
struct pa_stream *s;
assert(c && name && ss && c->state == CONTEXT_READY);
s = malloc(sizeof(struct pa_stream));
assert(s);
s->context = c;
s->read_callback = NULL;
s->read_userdata = NULL;
s->write_callback = NULL;
s->write_userdata = NULL;
s->die_callback = NULL;
s->die_userdata = NULL;
s->create_complete_callback = complete;
s->create_complete_userdata = NULL;
s->get_latency_callback = NULL;
s->get_latency_userdata = NULL;
s->name = strdup(name);
s->state = STREAM_CREATING;
s->requested_bytes = 0;
s->channel = 0;
s->channel_valid = 0;
s->device_index = (uint32_t) -1;
s->direction = dir;
s->sample_spec = *ss;
if (attr)
s->buffer_attr = *attr;
else {
s->buffer_attr.maxlength = DEFAULT_MAXLENGTH;
s->buffer_attr.tlength = DEFAULT_TLENGTH;
s->buffer_attr.prebuf = DEFAULT_PREBUF;
s->buffer_attr.minreq = DEFAULT_MINREQ;
s->buffer_attr.fragsize = DEFAULT_FRAGSIZE;
}
s->next = c->first_stream;
if (s->next)
s->next->previous = s;
s->previous = NULL;
c->first_stream = s;
if (dev)
lookup_device(s, dev);
else
create_stream(s, (uint32_t) -1);
return s;
}
void pa_stream_free(struct pa_stream *s) {
assert(s && s->context);
if (s->context->pdispatch)
pa_pdispatch_unregister_reply(s->context->pdispatch, s);
free(s->name);
if (s->channel_valid && s->context->state == CONTEXT_READY) {
struct pa_tagstruct *t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_DELETE_PLAYBACK_STREAM);
pa_tagstruct_putu32(t, s->context->ctag++);
pa_tagstruct_putu32(t, s->channel);
pa_pstream_send_tagstruct(s->context->pstream, t);
}
if (s->channel_valid)
pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
if (s->next)
s->next->previous = s->previous;
if (s->previous)
s->previous->next = s->next;
else
s->context->first_stream = s->next;
free(s);
}
void pa_stream_set_write_callback(struct pa_stream *s, void (*cb)(struct pa_stream *p, size_t length, void *userdata), void *userdata) {
assert(s && cb);
s->write_callback = cb;
s->write_userdata = userdata;
}
void pa_stream_write(struct pa_stream *s, const void *data, size_t length) {
struct pa_memchunk chunk;
assert(s && s->context && data && length && s->state == STREAM_READY);
chunk.memblock = pa_memblock_new(length);
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, 0, &chunk);
pa_memblock_unref(chunk.memblock);
/*fprintf(stderr, "Sent %u bytes\n", length);*/
if (length < s->requested_bytes)
s->requested_bytes -= length;
else
s->requested_bytes = 0;
}
size_t pa_stream_writable_size(struct pa_stream *s) {
assert(s && s->state == STREAM_READY);
return s->requested_bytes;
}
void pa_stream_set_read_callback(struct pa_stream *s, void (*cb)(struct pa_stream *p, const void*data, size_t length, void *userdata), void *userdata) {
assert(s && cb);
s->read_callback = cb;
s->read_userdata = userdata;
}
int pa_stream_is_dead(struct pa_stream *s) {
return s->state == STREAM_DEAD;
}
int pa_stream_is_ready(struct pa_stream*s) {
return s->state == STREAM_READY;
}
void pa_stream_set_die_callback(struct pa_stream *s, void (*cb)(struct pa_stream *s, void *userdata), void *userdata) {
assert(s);
s->die_callback = cb;
s->die_userdata = userdata;
}
int pa_context_is_pending(struct pa_context *c) {
assert(c);
if (c->state != CONTEXT_READY)
return 0;
return pa_pstream_is_pending(c->pstream) || pa_pdispatch_is_pending(c->pdispatch);
}
struct pa_context* pa_stream_get_context(struct pa_stream *p) {
assert(p);
return p->context;
}
static void set_dispatch_callbacks(struct pa_context *c);
static void pdispatch_drain_callback(struct pa_pdispatch*pd, void *userdata) {
set_dispatch_callbacks(userdata);
}
static void pstream_drain_callback(struct pa_pstream *s, void *userdata) {
set_dispatch_callbacks(userdata);
}
static void set_dispatch_callbacks(struct pa_context *c) {
assert(c && c->state == CONTEXT_READY);
pa_pstream_set_drain_callback(c->pstream, NULL, NULL);
pa_pdispatch_set_drain_callback(c->pdispatch, NULL, NULL);
if (pa_pdispatch_is_pending(c->pdispatch)) {
pa_pdispatch_set_drain_callback(c->pdispatch, pdispatch_drain_callback, c);
return;
}
if (pa_pstream_is_pending(c->pstream)) {
pa_pstream_set_drain_callback(c->pstream, pstream_drain_callback, c);
return;
}
assert(c->drain_complete_callback);
c->drain_complete_callback(c, c->drain_complete_userdata);
}
int pa_context_drain(
struct pa_context *c,
void (*complete) (struct pa_context*c, void *userdata),
void *userdata) {
assert(c && c->state == CONTEXT_READY);
if (complete == NULL) {
c->drain_complete_callback = NULL;
pa_pstream_set_drain_callback(c->pstream, NULL, NULL);
pa_pdispatch_set_drain_callback(c->pdispatch, NULL, NULL);
return 0;
}
if (!pa_context_is_pending(c))
return -1;
c->drain_complete_callback = complete;
c->drain_complete_userdata = userdata;
set_dispatch_callbacks(c);
return 0;
}
static void stream_drain_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct pa_stream *s = userdata;
assert(pd && s);
if (command != PA_COMMAND_REPLY) {
if (handle_error(s->context, command, t) < 0) {
context_dead(s->context);
return;
}
stream_dead(s);
return;
}
if (s->state != STREAM_READY)
return;
if (!pa_tagstruct_eof(t)) {
s->context->error = PA_ERROR_PROTOCOL;
context_dead(s->context);
return;
}
if (s->drain_complete_callback) {
void (*temp) (struct pa_stream*s, void *userdata) = s->drain_complete_callback;
s->drain_complete_callback = NULL;
temp(s, s->drain_complete_userdata);
}
}
void pa_stream_drain(struct pa_stream *s, void (*complete) (struct pa_stream*s, void *userdata), void *userdata) {
struct pa_tagstruct *t;
uint32_t tag;
assert(s && s->state == STREAM_READY);
if (!complete) {
s->drain_complete_callback = NULL;
return;
}
s->drain_complete_callback = complete;
s->drain_complete_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, stream_drain_callback, s);
}
void pa_context_exit(struct pa_context *c) {
struct pa_tagstruct *t;
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);
}
static void context_stat_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct pa_context *c = userdata;
uint32_t total, count;
assert(pd && c);
if (command != PA_COMMAND_REPLY) {
if (handle_error(c, command, t) < 0) {
context_dead(c);
return;
}
if (c->stat_callback)
c->stat_callback(c, (uint32_t) -1, (uint32_t) -1, c->stat_userdata);
return;
}
if (pa_tagstruct_getu32(t, &count) < 0 ||
pa_tagstruct_getu32(t, &total) < 0 ||
!pa_tagstruct_eof(t)) {
c->error = PA_ERROR_PROTOCOL;
context_dead(c);
return;
}
if (c->stat_callback)
c->stat_callback(c, count, total, c->stat_userdata);
}
void pa_context_stat(struct pa_context *c, void (*cb)(struct pa_context *c, uint32_t count, uint32_t total, void *userdata), void *userdata) {
uint32_t tag;
struct pa_tagstruct *t;
c->stat_callback = cb;
c->stat_userdata = userdata;
if (cb == NULL)
return;
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_STAT);
pa_tagstruct_putu32(t, tag = c->ctag++);
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_stat_callback, c);
}
static void stream_get_latency_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct pa_stream *s = userdata;
uint32_t latency;
assert(pd && s);
if (command != PA_COMMAND_REPLY) {
if (handle_error(s->context, command, t) < 0) {
context_dead(s->context);
return;
}
if (s->get_latency_callback)
s->get_latency_callback(s, (uint32_t) -1, s->get_latency_userdata);
return;
}
if (pa_tagstruct_getu32(t, &latency) < 0 ||
!pa_tagstruct_eof(t)) {
s->context->error = PA_ERROR_PROTOCOL;
context_dead(s->context);
return;
}
if (s->get_latency_callback)
s->get_latency_callback(s, latency, s->get_latency_userdata);
}
void pa_stream_get_latency(struct pa_stream *p, void (*cb)(struct pa_stream *p, uint32_t latency, void *userdata), void *userdata) {
uint32_t tag;
struct pa_tagstruct *t;
p->get_latency_callback = cb;
p->get_latency_userdata = userdata;
if (cb == NULL)
return;
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_GET_PLAYBACK_LATENCY);
pa_tagstruct_putu32(t, tag = p->context->ctag++);
pa_tagstruct_putu32(t, p->channel);
pa_pstream_send_tagstruct(p->context->pstream, t);
pa_pdispatch_register_reply(p->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, p);
}

94
polyp/polyplib.h Normal file
View file

@ -0,0 +1,94 @@
#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 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 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 "mainloop-api.h"
struct pa_context;
struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *name);
int pa_context_connect(
struct pa_context *c,
const char *server,
void (*complete) (struct pa_context*c, int success, void *userdata),
void *userdata);
int pa_context_drain(
struct pa_context *c,
void (*complete) (struct pa_context*c, void *userdata),
void *userdata);
void pa_context_free(struct pa_context *c);
void pa_context_set_die_callback(struct pa_context *c, void (*cb)(struct pa_context *c, void *userdata), void *userdata);
int pa_context_is_dead(struct pa_context *c);
int pa_context_is_ready(struct pa_context *c);
int pa_context_errno(struct pa_context *c);
int pa_context_is_pending(struct pa_context *c);
void pa_context_exit(struct pa_context *c);
void pa_context_stat(struct pa_context *c, void (*cb)(struct pa_context *c, uint32_t count, uint32_t total, void *userdata), void *userdata);
struct pa_stream;
struct pa_stream* pa_stream_new(
struct pa_context *c,
enum pa_stream_direction dir,
const char *dev,
const char *name,
const struct pa_sample_spec *ss,
const struct pa_buffer_attr *attr,
void (*complete) (struct pa_stream*s, int success, void *userdata),
void *userdata);
void pa_stream_free(struct pa_stream *p);
void pa_stream_drain(
struct pa_stream *s,
void (*complete) (struct pa_stream*s, void *userdata),
void *userdata);
void pa_stream_set_die_callback(struct pa_stream *s, void (*cb)(struct pa_stream *s, void *userdata), void *userdata);
void pa_stream_set_write_callback(struct pa_stream *p, void (*cb)(struct pa_stream *p, size_t length, void *userdata), void *userdata);
void pa_stream_write(struct pa_stream *p, const void *data, size_t length);
size_t pa_stream_writable_size(struct pa_stream *p);
void pa_stream_set_read_callback(struct pa_stream *p, void (*cb)(struct pa_stream *p, const void*data, size_t length, void *userdata), void *userdata);
int pa_stream_is_dead(struct pa_stream *p);
int pa_stream_is_ready(struct pa_stream*p);
void pa_stream_get_latency(struct pa_stream *p, void (*cb)(struct pa_stream *p, uint32_t latency, void *userdata), void *userdata);
struct pa_context* pa_stream_get_context(struct pa_stream *p);
#endif

85
polyp/protocol-cli.c Normal file
View file

@ -0,0 +1,85 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include "protocol-cli.h"
#include "cli.h"
struct pa_protocol_cli {
struct pa_module *module;
struct pa_core *core;
struct pa_socket_server*server;
struct pa_idxset *connections;
};
static void cli_eof_cb(struct pa_cli*c, void*userdata) {
struct pa_protocol_cli *p = userdata;
assert(p);
pa_idxset_remove_by_data(p->connections, c, NULL);
pa_cli_free(c);
}
static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) {
struct pa_protocol_cli *p = userdata;
struct pa_cli *c;
assert(s && io && p);
c = pa_cli_new(p->core, io, p->module);
assert(c);
pa_cli_set_eof_callback(c, cli_eof_cb, p);
pa_idxset_put(p->connections, c, NULL);
}
struct pa_protocol_cli* pa_protocol_cli_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) {
struct pa_protocol_cli* p;
assert(core && server);
p = malloc(sizeof(struct pa_protocol_cli));
assert(p);
p->module = m;
p->core = core;
p->server = server;
p->connections = pa_idxset_new(NULL, NULL);
pa_socket_server_set_callback(p->server, on_connection, p);
return p;
}
static void free_connection(void *p, void *userdata) {
assert(p);
pa_cli_free(p);
}
void pa_protocol_cli_free(struct pa_protocol_cli *p) {
assert(p);
pa_idxset_free(p->connections, free_connection, NULL);
pa_socket_server_free(p->server);
free(p);
}

35
polyp/protocol-cli.h Normal file
View file

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

847
polyp/protocol-esound.c Normal file
View file

@ -0,0 +1,847 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <limits.h>
#include "protocol-esound.h"
#include "esound.h"
#include "memblock.h"
#include "client.h"
#include "sink-input.h"
#include "sink.h"
#include "source-output.h"
#include "source.h"
#include "sample.h"
#include "authkey.h"
#define DEFAULT_COOKIE_FILE ".esd_auth"
#define PLAYBACK_BUFFER_SECONDS (.5)
#define PLAYBACK_BUFFER_FRAGMENTS (10)
#define RECORD_BUFFER_SECONDS (5)
#define RECORD_BUFFER_FRAGMENTS (100)
/* This is heavily based on esound's code */
struct connection {
uint32_t index;
struct pa_protocol_esound *protocol;
struct pa_iochannel *io;
struct pa_client *client;
int authorized, swap_byte_order;
void *write_data;
size_t write_data_alloc, write_data_index, write_data_length;
void *read_data;
size_t read_data_alloc, read_data_length;
esd_proto_t request;
esd_client_state_t state;
struct pa_sink_input *sink_input;
struct pa_source_output *source_output;
struct pa_memblockq *input_memblockq, *output_memblockq;
void *fixed_source;
struct {
struct pa_memblock *current_memblock;
size_t memblock_index, fragment_size;
} playback;
};
struct pa_protocol_esound {
int public;
struct pa_module *module;
struct pa_core *core;
struct pa_socket_server *server;
struct pa_idxset *connections;
uint32_t sink_index, source_index;
unsigned n_player;
uint8_t esd_key[ESD_KEY_LEN];
};
typedef struct proto_handler {
size_t data_length;
int (*proc)(struct connection *c, esd_proto_t request, const void *data, size_t length);
const char *description;
} esd_proto_handler_info_t;
static void sink_input_drop_cb(struct pa_sink_input *i, size_t length);
static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk);
static void sink_input_kill_cb(struct pa_sink_input *i);
static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i);
static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk);
static void source_output_kill_cb(struct pa_source_output *o);
static int esd_proto_connect(struct connection *c, esd_proto_t request, const void *data, size_t length);
static int esd_proto_stream_play(struct connection *c, esd_proto_t request, const void *data, size_t length);
static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length);
static int esd_proto_get_latency(struct connection *c, esd_proto_t request, const void *data, size_t length);
static int esd_proto_server_info(struct connection *c, esd_proto_t request, const void *data, size_t length);
static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length);
static int esd_proto_stream_pan(struct connection *c, esd_proto_t request, const void *data, size_t length);
/* the big map of protocol handler info */
static struct proto_handler proto_map[ESD_PROTO_MAX] = {
{ ESD_KEY_LEN + sizeof(int), esd_proto_connect, "connect" },
{ ESD_KEY_LEN + sizeof(int), NULL, "lock" },
{ ESD_KEY_LEN + sizeof(int), NULL, "unlock" },
{ ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_play, "stream play" },
{ ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream rec" },
{ ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream mon" },
{ ESD_NAME_MAX + 3 * sizeof(int), NULL, "sample cache" },
{ sizeof(int), NULL, "sample free" },
{ sizeof(int), NULL, "sample play" },
{ sizeof(int), NULL, "sample loop" },
{ sizeof(int), NULL, "sample stop" },
{ -1, NULL, "TODO: sample kill" },
{ ESD_KEY_LEN + sizeof(int), NULL, "standby" },
{ ESD_KEY_LEN + sizeof(int), NULL, "resume" },
{ ESD_NAME_MAX, NULL, "sample getid" },
{ ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream filter" },
{ sizeof(int), esd_proto_server_info, "server info" },
{ sizeof(int), esd_proto_all_info, "all info" },
{ -1, NULL, "TODO: subscribe" },
{ -1, NULL, "TODO: unsubscribe" },
{ 3 * sizeof(int), esd_proto_stream_pan, "stream pan"},
{ 3 * sizeof(int), NULL, "sample pan" },
{ sizeof(int), NULL, "standby mode" },
{ 0, esd_proto_get_latency, "get latency" }
};
static void connection_free(struct connection *c) {
assert(c);
pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
if (c->state == ESD_STREAMING_DATA)
c->protocol->n_player--;
pa_client_free(c->client);
if (c->sink_input)
pa_sink_input_free(c->sink_input);
if (c->source_output)
pa_source_output_free(c->source_output);
if (c->input_memblockq)
pa_memblockq_free(c->input_memblockq);
if (c->output_memblockq)
pa_memblockq_free(c->output_memblockq);
if (c->playback.current_memblock)
pa_memblock_unref(c->playback.current_memblock);
free(c->read_data);
free(c->write_data);
pa_iochannel_free(c->io);
if (c->fixed_source)
c->protocol->core->mainloop->cancel_fixed(c->protocol->core->mainloop, c->fixed_source);
free(c);
}
static struct pa_sink* get_output_sink(struct pa_protocol_esound *p) {
struct pa_sink *s;
assert(p);
if (!(s = pa_idxset_get_by_index(p->core->sinks, p->sink_index)))
s = pa_sink_get_default(p->core);
p->sink_index = s ? s->index : PA_IDXSET_INVALID;
return s;
}
static struct pa_source* get_input_source(struct pa_protocol_esound *p) {
struct pa_source *s;
assert(p);
if (!(s = pa_idxset_get_by_index(p->core->sources, p->sink_index)))
s = pa_source_get_default(p->core);
p->source_index = s ? s->index : PA_IDXSET_INVALID;
return s;
}
static void* connection_write(struct connection *c, size_t length) {
size_t t, i;
assert(c);
assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed);
c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1);
t = c->write_data_length+length;
if (c->write_data_alloc < t)
c->write_data = realloc(c->write_data, c->write_data_alloc = t);
assert(c->write_data);
i = c->write_data_length;
c->write_data_length += length;
return c->write_data+i;
}
/*** esound commands ***/
static int esd_proto_connect(struct connection *c, esd_proto_t request, const void *data, size_t length) {
uint32_t ekey;
int *ok;
assert(length == (ESD_KEY_LEN + sizeof(uint32_t)));
if (!c->authorized) {
if (memcmp(data, c->protocol->esd_key, ESD_KEY_LEN) != 0) {
fprintf(stderr, __FILE__": kicked client with invalid authorization key.\n");
return -1;
}
c->authorized = 1;
}
ekey = *(uint32_t*)(data+ESD_KEY_LEN);
if (ekey == ESD_ENDIAN_KEY)
c->swap_byte_order = 0;
else if (ekey == ESD_SWAP_ENDIAN_KEY)
c->swap_byte_order = 1;
else {
fprintf(stderr, __FILE__": client sent invalid endian key\n");
return -1;
}
ok = connection_write(c, sizeof(int));
assert(ok);
*ok = 1;
return 0;
}
static int esd_proto_stream_play(struct connection *c, esd_proto_t request, const void *data, size_t length) {
char name[ESD_NAME_MAX];
int format, rate;
struct pa_sink *sink;
struct pa_sample_spec ss;
size_t l;
assert(c && length == (sizeof(int)*2+ESD_NAME_MAX));
format = maybe_swap_endian_32(c->swap_byte_order, *(int*)data);
rate = maybe_swap_endian_32(c->swap_byte_order, *((int*)data + 1));
ss.rate = rate;
ss.channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1;
ss.format = ((format & ESD_MASK_BITS) == ESD_BITS16) ? PA_SAMPLE_S16NE : PA_SAMPLE_U8;
if (!pa_sample_spec_valid(&ss))
return -1;
if (!(sink = get_output_sink(c->protocol)))
return -1;
strncpy(name, data + sizeof(int)*2, sizeof(name));
name[sizeof(name)-1] = 0;
pa_client_rename(c->client, name);
assert(!c->input_memblockq);
l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS);
c->input_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&ss), l/2, l/PLAYBACK_BUFFER_FRAGMENTS);
assert(c->input_memblockq);
pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2);
c->playback.fragment_size = l/10;
assert(!c->sink_input);
c->sink_input = pa_sink_input_new(sink, name, &ss);
assert(c->sink_input);
c->sink_input->owner = c->protocol->module;
c->sink_input->client = c->client;
c->sink_input->peek = sink_input_peek_cb;
c->sink_input->drop = sink_input_drop_cb;
c->sink_input->kill = sink_input_kill_cb;
c->sink_input->get_latency = sink_input_get_latency_cb;
c->sink_input->userdata = c;
c->state = ESD_STREAMING_DATA;
c->protocol->n_player++;
return 0;
}
static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length) {
char name[ESD_NAME_MAX];
int format, rate;
struct pa_source *source;
struct pa_sample_spec ss;
size_t l;
assert(c && length == (sizeof(int)*2+ESD_NAME_MAX));
format = maybe_swap_endian_32(c->swap_byte_order, *(int*)data);
rate = maybe_swap_endian_32(c->swap_byte_order, *((int*)data + 1));
ss.rate = rate;
ss.channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1;
ss.format = ((format & ESD_MASK_BITS) == ESD_BITS16) ? PA_SAMPLE_S16NE : PA_SAMPLE_U8;
if (!pa_sample_spec_valid(&ss))
return -1;
if (request == ESD_PROTO_STREAM_MON) {
struct pa_sink* sink;
if (!(sink = get_output_sink(c->protocol)))
return -1;
if (!(source = sink->monitor_source))
return -1;
} else {
assert(request == ESD_PROTO_STREAM_REC);
if (!(source = get_input_source(c->protocol)))
return -1;
}
strncpy(name, data + sizeof(int)*2, sizeof(name));
name[sizeof(name)-1] = 0;
pa_client_rename(c->client, name);
assert(!c->output_memblockq);
l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
c->output_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&ss), 0, 0);
assert(c->output_memblockq);
pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2);
assert(!c->source_output);
c->source_output = pa_source_output_new(source, name, &ss);
assert(c->source_output);
c->source_output->owner = c->protocol->module;
c->source_output->client = c->client;
c->source_output->push = source_output_push_cb;
c->source_output->kill = source_output_kill_cb;
c->source_output->userdata = c;
c->state = ESD_STREAMING_DATA;
c->protocol->n_player++;
return 0;
}
static int esd_proto_get_latency(struct connection *c, esd_proto_t request, const void *data, size_t length) {
struct pa_sink *sink;
int latency, *lag;
assert(c && !data && length == 0);
if (!(sink = get_output_sink(c->protocol)))
latency = 0;
else {
float usec = pa_sink_get_latency(sink);
usec += PLAYBACK_BUFFER_SECONDS*1000000; /* A better estimation would be a good idea! */
latency = (int) ((usec*44100)/1000000);
}
lag = connection_write(c, sizeof(int));
assert(lag);
*lag = c->swap_byte_order ? swap_endian_32(latency) : latency;
return 0;
}
static int esd_proto_server_info(struct connection *c, esd_proto_t request, const void *data, size_t length) {
int rate = 44100, format = ESD_STEREO|ESD_BITS16;
int *response;
struct pa_sink *sink;
assert(c && data && length == sizeof(int));
if ((sink = get_output_sink(c->protocol))) {
rate = sink->sample_spec.rate;
format = (sink->sample_spec.format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16;
format |= (sink->sample_spec.channels >= 2) ? ESD_STEREO : ESD_MONO;
}
response = connection_write(c, sizeof(int)*3);
assert(response);
*(response++) = 0;
*(response++) = maybe_swap_endian_32(c->swap_byte_order, rate);
*(response++) = maybe_swap_endian_32(c->swap_byte_order, format);
return 0;
}
static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length) {
void *response;
size_t t, k, s;
struct connection *conn;
size_t index = PA_IDXSET_INVALID;
assert(c && data && length == sizeof(int));
if (esd_proto_server_info(c, request, data, length) < 0)
return -1;
k = sizeof(int)*5+ESD_NAME_MAX;
s = sizeof(int)*6+ESD_NAME_MAX;
response = connection_write(c, (t = s+k*(c->protocol->n_player+1)));
assert(k);
for (conn = pa_idxset_first(c->protocol->connections, &index); conn; conn = pa_idxset_next(c->protocol->connections, &index)) {
int format = ESD_BITS16 | ESD_STEREO, rate = 44100, volume = 0xFF;
if (conn->state != ESD_STREAMING_DATA)
continue;
assert(t >= s+k+k);
if (conn->sink_input) {
rate = conn->sink_input->sample_spec.rate;
volume = (conn->sink_input->volume*0xFF)/0x100;
format = (conn->sink_input->sample_spec.format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16;
format |= (conn->sink_input->sample_spec.channels >= 2) ? ESD_STEREO : ESD_MONO;
}
/* id */
*((int*) response) = maybe_swap_endian_32(c->swap_byte_order, (int) conn->index);
response += sizeof(int);
/* name */
assert(conn->client);
strncpy(response, conn->client->name, ESD_NAME_MAX);
response += ESD_NAME_MAX;
/* rate */
*((int*) response) = maybe_swap_endian_32(c->swap_byte_order, rate);
response += sizeof(int);
/* left */
*((int*) response) = maybe_swap_endian_32(c->swap_byte_order, volume);
response += sizeof(int);
/*right*/
*((int*) response) = maybe_swap_endian_32(c->swap_byte_order, volume);
response += sizeof(int);
/*format*/
*((int*) response) = maybe_swap_endian_32(c->swap_byte_order, format);
response += sizeof(int);
t-= k;
}
assert(t == s+k);
memset(response, 0, t);
return 0;
}
static int esd_proto_stream_pan(struct connection *c, esd_proto_t request, const void *data, size_t length) {
int *ok;
uint32_t index, volume;
struct connection *conn;
assert(c && data && length == sizeof(int)*3);
index = (uint32_t) maybe_swap_endian_32(c->swap_byte_order, *(int*)data);
volume = (uint32_t) maybe_swap_endian_32(c->swap_byte_order, *((int*)data + 1));
volume = (volume*0x100)/0xFF;
ok = connection_write(c, sizeof(int));
assert(ok);
if ((conn = pa_idxset_get_by_index(c->protocol->connections, index))) {
assert(conn->sink_input);
conn->sink_input->volume = volume;
*ok = 1;
} else
*ok = 0;
return 0;
}
/*** client callbacks ***/
static void client_kill_cb(struct pa_client *c) {
assert(c && c->userdata);
connection_free(c->userdata);
}
/*** pa_iochannel callbacks ***/
static int do_read(struct connection *c) {
assert(c && c->io);
if (c->state == ESD_NEXT_REQUEST) {
ssize_t r;
assert(c->read_data_length < sizeof(c->request));
if ((r = pa_iochannel_read(c->io, ((void*) &c->request) + c->read_data_length, sizeof(c->request) - c->read_data_length)) <= 0) {
fprintf(stderr, "protocol-esound.c: read() failed: %s\n", r == 0 ? "EOF" : strerror(errno));
return -1;
}
if ((c->read_data_length+= r) >= sizeof(c->request)) {
struct proto_handler *handler;
if (c->swap_byte_order)
c->request = swap_endian_32(c->request);
if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) {
fprintf(stderr, "protocol-esound.c: recieved invalid request.\n");
return -1;
}
handler = proto_map+c->request;
if (!handler->proc) {
fprintf(stderr, "protocol-sound.c: recieved unimplemented request.\n");
return -1;
}
if (handler->data_length == 0) {
c->read_data_length = 0;
if (handler->proc(c, c->request, NULL, 0) < 0)
return -1;
} else {
if (c->read_data_alloc < handler->data_length)
c->read_data = realloc(c->read_data, c->read_data_alloc = handler->data_length);
assert(c->read_data);
c->state = ESD_NEEDS_REQDATA;
c->read_data_length = 0;
}
}
} else if (c->state == ESD_NEEDS_REQDATA) {
ssize_t r;
struct proto_handler *handler = proto_map+c->request;
assert(handler->proc);
assert(c->read_data && c->read_data_length < handler->data_length);
if ((r = pa_iochannel_read(c->io, c->read_data + c->read_data_length, handler->data_length - c->read_data_length)) <= 0) {
fprintf(stderr, "protocol-esound.c: read() failed: %s\n", r == 0 ? "EOF" : strerror(errno));
return -1;
}
if ((c->read_data_length+= r) >= handler->data_length) {
size_t l = c->read_data_length;
assert(handler->proc);
c->state = ESD_NEXT_REQUEST;
c->read_data_length = 0;
if (handler->proc(c, c->request, c->read_data, l) < 0)
return -1;
}
} else if (c->state == ESD_STREAMING_DATA && c->sink_input) {
struct pa_memchunk chunk;
ssize_t r;
size_t l;
assert(c->input_memblockq);
if (!(l = pa_memblockq_missing(c->input_memblockq)))
return 0;
if (l > c->playback.fragment_size)
l = c->playback.fragment_size;
if (c->playback.current_memblock)
if (c->playback.current_memblock->length - c->playback.memblock_index < l) {
pa_memblock_unref(c->playback.current_memblock);
c->playback.current_memblock = NULL;
c->playback.memblock_index = 0;
}
if (!c->playback.current_memblock) {
c->playback.current_memblock = pa_memblock_new(c->playback.fragment_size*2);
assert(c->playback.current_memblock && c->playback.current_memblock->length >= l);
c->playback.memblock_index = 0;
}
if ((r = pa_iochannel_read(c->io, c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) {
fprintf(stderr, __FILE__": read() failed: %s\n", r == 0 ? "EOF" : strerror(errno));
return -1;
}
chunk.memblock = c->playback.current_memblock;
chunk.index = c->playback.memblock_index;
chunk.length = r;
assert(chunk.memblock);
c->playback.memblock_index += r;
assert(c->input_memblockq);
pa_memblockq_push_align(c->input_memblockq, &chunk, 0);
assert(c->sink_input);
pa_sink_notify(c->sink_input->sink);
}
return 0;
}
static int do_write(struct connection *c) {
assert(c && c->io);
if (c->write_data_length) {
ssize_t r;
assert(c->write_data_index < c->write_data_length);
if ((r = pa_iochannel_write(c->io, c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) {
fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno));
return -1;
}
if ((c->write_data_index +=r) >= c->write_data_length)
c->write_data_length = c->write_data_index = 0;
} else if (c->state == ESD_STREAMING_DATA && c->source_output) {
struct pa_memchunk chunk;
ssize_t r;
assert(c->output_memblockq);
if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
return 0;
assert(chunk.memblock && chunk.length);
if ((r = pa_iochannel_write(c->io, chunk.memblock->data+chunk.index, chunk.length)) < 0) {
pa_memblock_unref(chunk.memblock);
fprintf(stderr, __FILE__": write(): %s\n", strerror(errno));
return -1;
}
pa_memblockq_drop(c->output_memblockq, r);
pa_memblock_unref(chunk.memblock);
}
return 0;
}
static void do_work(struct connection *c) {
assert(c);
assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed);
c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 0);
if (pa_iochannel_is_hungup(c->io))
goto fail;
if (pa_iochannel_is_writable(c->io))
if (do_write(c) < 0)
goto fail;
if (pa_iochannel_is_readable(c->io))
if (do_read(c) < 0)
goto fail;
return;
fail:
connection_free(c);
}
static void io_callback(struct pa_iochannel*io, void *userdata) {
struct connection *c = userdata;
assert(io && c && c->io == io);
do_work(c);
}
/*** fixed callback ***/
static void fixed_callback(struct pa_mainloop_api*a, void *id, void *userdata) {
struct connection *c = userdata;
assert(a && c && c->fixed_source == id);
do_work(c);
}
/*** sink_input callbacks ***/
static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk) {
struct connection*c;
assert(i && i->userdata && chunk);
c = i->userdata;
if (pa_memblockq_peek(c->input_memblockq, chunk) < 0)
return -1;
return 0;
}
static void sink_input_drop_cb(struct pa_sink_input *i, size_t length) {
struct connection*c = i->userdata;
assert(i && c && length);
pa_memblockq_drop(c->input_memblockq, length);
/* do something */
assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed);
c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1);
}
static void sink_input_kill_cb(struct pa_sink_input *i) {
assert(i && i->userdata);
connection_free((struct connection *) i->userdata);
}
static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i) {
struct connection*c = i->userdata;
assert(i && c);
return pa_samples_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
}
/*** source_output callbacks ***/
static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk) {
struct connection *c = o->userdata;
assert(o && c && chunk);
pa_memblockq_push(c->output_memblockq, chunk, 0);
/* do something */
assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed);
c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1);
}
static void source_output_kill_cb(struct pa_source_output *o) {
assert(o && o->userdata);
connection_free((struct connection *) o->userdata);
}
/*** socket server callback ***/
static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) {
struct connection *c;
char cname[256];
assert(s && io && userdata);
c = malloc(sizeof(struct connection));
assert(c);
c->protocol = userdata;
c->io = io;
pa_iochannel_set_callback(c->io, io_callback, c);
pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
assert(c->protocol->core);
c->client = pa_client_new(c->protocol->core, "ESOUND", cname);
assert(c->client);
c->client->owner = c->protocol->module;
c->client->kill = client_kill_cb;
c->client->userdata = c;
c->authorized = c->protocol->public;
c->swap_byte_order = 0;
c->read_data_length = 0;
c->read_data = malloc(c->read_data_alloc = proto_map[ESD_PROTO_CONNECT].data_length);
assert(c->read_data);
c->write_data_length = c->write_data_index = c->write_data_alloc = 0;
c->write_data = NULL;
c->state = ESD_NEEDS_REQDATA;
c->request = ESD_PROTO_CONNECT;
c->sink_input = NULL;
c->input_memblockq = NULL;
c->source_output = NULL;
c->output_memblockq = NULL;
c->playback.current_memblock = NULL;
c->playback.memblock_index = 0;
c->playback.fragment_size = 0;
c->fixed_source = c->protocol->core->mainloop->source_fixed(c->protocol->core->mainloop, fixed_callback, c);
assert(c->fixed_source);
c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 0);
pa_idxset_put(c->protocol->connections, c, &c->index);
}
/*** entry points ***/
struct pa_protocol_esound* pa_protocol_esound_new(struct pa_core*core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) {
uint32_t source_index, sink_index;
struct pa_protocol_esound *p;
assert(core && server && ma);
if (pa_modargs_get_source_index(ma, core, &source_index) < 0) {
fprintf(stderr, __FILE__": source does not exist.\n");
return NULL;
}
if (pa_modargs_get_sink_index(ma, core, &sink_index) < 0) {
fprintf(stderr, __FILE__": sink does not exist.\n");
return NULL;
}
p = malloc(sizeof(struct pa_protocol_esound));
assert(p);
if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", DEFAULT_COOKIE_FILE), p->esd_key, sizeof(p->esd_key)) < 0) {
free(p);
return NULL;
}
p->module = m;
p->public = 0;
p->server = server;
pa_socket_server_set_callback(p->server, on_connection, p);
p->core = core;
p->connections = pa_idxset_new(NULL, NULL);
assert(p->connections);
p->sink_index = sink_index;
p->source_index = source_index;
p->n_player = 0;
return p;
}
void pa_protocol_esound_free(struct pa_protocol_esound *p) {
struct connection *c;
assert(p);
while ((c = pa_idxset_first(p->connections, NULL)))
connection_free(c);
pa_idxset_free(p->connections, NULL, NULL);
pa_socket_server_free(p->server);
free(p);
}

35
polyp/protocol-esound.h Normal file
View file

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

863
polyp/protocol-native.c Normal file
View file

@ -0,0 +1,863 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "protocol-native.h"
#include "native-common.h"
#include "packet.h"
#include "client.h"
#include "source-output.h"
#include "sink-input.h"
#include "pstream.h"
#include "tagstruct.h"
#include "pdispatch.h"
#include "pstream-util.h"
#include "authkey.h"
#include "namereg.h"
struct connection;
struct pa_protocol_native;
struct record_stream {
struct connection *connection;
uint32_t index;
struct pa_source_output *source_output;
struct pa_memblockq *memblockq;
size_t fragment_size;
};
struct playback_stream {
struct connection *connection;
uint32_t index;
struct pa_sink_input *sink_input;
struct pa_memblockq *memblockq;
size_t requested_bytes;
int drain_request;
uint32_t drain_tag;
};
struct connection {
int authorized;
struct pa_protocol_native *protocol;
struct pa_client *client;
struct pa_pstream *pstream;
struct pa_pdispatch *pdispatch;
struct pa_idxset *record_streams, *playback_streams;
uint32_t rrobin_index;
};
struct pa_protocol_native {
struct pa_module *module;
int public;
struct pa_core *core;
struct pa_socket_server *server;
struct pa_idxset *connections;
uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
};
static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk);
static void sink_input_drop_cb(struct pa_sink_input *i, size_t length);
static void sink_input_kill_cb(struct pa_sink_input *i);
static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i);
static void request_bytes(struct playback_stream*s);
static void source_output_kill_cb(struct pa_source_output *o);
static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk);
static void command_exit(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
static void command_delete_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
static void command_drain_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
static void command_create_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
static void command_delete_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
static void command_auth(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
static void command_set_name(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
static void command_lookup(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
static void command_stat(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_ERROR] = { NULL },
[PA_COMMAND_TIMEOUT] = { NULL },
[PA_COMMAND_REPLY] = { NULL },
[PA_COMMAND_CREATE_PLAYBACK_STREAM] = { command_create_playback_stream },
[PA_COMMAND_DELETE_PLAYBACK_STREAM] = { command_delete_playback_stream },
[PA_COMMAND_DRAIN_PLAYBACK_STREAM] = { command_drain_playback_stream },
[PA_COMMAND_CREATE_RECORD_STREAM] = { command_create_record_stream },
[PA_COMMAND_DELETE_RECORD_STREAM] = { command_delete_record_stream },
[PA_COMMAND_AUTH] = { command_auth },
[PA_COMMAND_REQUEST] = { NULL },
[PA_COMMAND_EXIT] = { command_exit },
[PA_COMMAND_SET_NAME] = { command_set_name },
[PA_COMMAND_LOOKUP_SINK] = { command_lookup },
[PA_COMMAND_LOOKUP_SOURCE] = { command_lookup },
[PA_COMMAND_STAT] = { command_stat },
[PA_COMMAND_GET_PLAYBACK_LATENCY] = { command_get_playback_latency },
};
/* structure management */
static struct record_stream* record_stream_new(struct connection *c, struct pa_source *source, struct pa_sample_spec *ss, const char *name, size_t maxlength, size_t fragment_size) {
struct record_stream *s;
struct pa_source_output *source_output;
size_t base;
assert(c && source && ss && name && maxlength);
if (!(source_output = pa_source_output_new(source, name, ss)))
return NULL;
s = malloc(sizeof(struct record_stream));
assert(s);
s->connection = c;
s->source_output = source_output;
s->source_output->push = source_output_push_cb;
s->source_output->kill = source_output_kill_cb;
s->source_output->userdata = s;
s->source_output->owner = c->protocol->module;
s->source_output->client = c->client;
s->memblockq = pa_memblockq_new(maxlength, 0, base = pa_sample_size(ss), 0, 0);
assert(s->memblockq);
s->fragment_size = (fragment_size/base)*base;
if (!s->fragment_size)
s->fragment_size = base;
pa_idxset_put(c->record_streams, s, &s->index);
return s;
}
static void record_stream_free(struct record_stream* r) {
assert(r && r->connection);
pa_idxset_remove_by_data(r->connection->record_streams, r, NULL);
pa_source_output_free(r->source_output);
pa_memblockq_free(r->memblockq);
free(r);
}
static struct playback_stream* playback_stream_new(struct connection *c, struct pa_sink *sink, struct pa_sample_spec *ss, const char *name,
size_t maxlength,
size_t tlength,
size_t prebuf,
size_t minreq) {
struct playback_stream *s;
struct pa_sink_input *sink_input;
assert(c && sink && ss && name && maxlength);
if (!(sink_input = pa_sink_input_new(sink, name, ss)))
return NULL;
s = malloc(sizeof(struct playback_stream));
assert (s);
s->connection = c;
s->sink_input = sink_input;
s->sink_input->peek = sink_input_peek_cb;
s->sink_input->drop = sink_input_drop_cb;
s->sink_input->kill = sink_input_kill_cb;
s->sink_input->get_latency = sink_input_get_latency_cb;
s->sink_input->userdata = s;
s->sink_input->owner = c->protocol->module;
s->sink_input->client = c->client;
s->memblockq = pa_memblockq_new(maxlength, tlength, pa_sample_size(ss), prebuf, minreq);
assert(s->memblockq);
s->requested_bytes = 0;
s->drain_request = 0;
pa_idxset_put(c->playback_streams, s, &s->index);
return s;
}
static void playback_stream_free(struct playback_stream* p) {
assert(p && p->connection);
if (p->drain_request)
pa_pstream_send_error(p->connection->pstream, p->drain_tag, PA_ERROR_NOENTITY);
pa_idxset_remove_by_data(p->connection->playback_streams, p, NULL);
pa_sink_input_free(p->sink_input);
pa_memblockq_free(p->memblockq);
free(p);
}
static void connection_free(struct connection *c) {
struct record_stream *r;
struct playback_stream *p;
assert(c && c->protocol);
pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
while ((r = pa_idxset_first(c->record_streams, NULL)))
record_stream_free(r);
pa_idxset_free(c->record_streams, NULL, NULL);
while ((p = pa_idxset_first(c->playback_streams, NULL)))
playback_stream_free(p);
pa_idxset_free(c->playback_streams, NULL, NULL);
pa_pdispatch_free(c->pdispatch);
pa_pstream_free(c->pstream);
pa_client_free(c->client);
free(c);
}
static void request_bytes(struct playback_stream *s) {
struct pa_tagstruct *t;
size_t l;
assert(s);
if (!(l = pa_memblockq_missing(s->memblockq)))
return;
if (l <= s->requested_bytes)
return;
l -= s->requested_bytes;
if (l < pa_memblockq_get_minreq(s->memblockq))
return;
s->requested_bytes += l;
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_REQUEST);
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
pa_tagstruct_putu32(t, s->index);
pa_tagstruct_putu32(t, l);
pa_pstream_send_tagstruct(s->connection->pstream, t);
/*fprintf(stderr, "Requesting %u bytes\n", l);*/
}
static void send_memblock(struct connection *c) {
uint32_t start;
struct record_stream *r;
start = PA_IDXSET_INVALID;
for (;;) {
struct pa_memchunk chunk;
if (!(r = pa_idxset_rrobin(c->record_streams, &c->rrobin_index)))
return;
if (start == PA_IDXSET_INVALID)
start = c->rrobin_index;
else if (start == c->rrobin_index)
return;
if (pa_memblockq_peek(r->memblockq, &chunk) >= 0) {
if (chunk.length > r->fragment_size)
chunk.length = r->fragment_size;
pa_pstream_send_memblock(c->pstream, r->index, 0, &chunk);
pa_memblockq_drop(r->memblockq, chunk.length);
pa_memblock_unref(chunk.memblock);
return;
}
}
}
static void send_playback_stream_killed(struct playback_stream *p) {
struct pa_tagstruct *t;
assert(p);
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_KILLED);
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
pa_tagstruct_putu32(t, p->index);
pa_pstream_send_tagstruct(p->connection->pstream, t);
}
static void send_record_stream_killed(struct record_stream *r) {
struct pa_tagstruct *t;
assert(r);
t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_KILLED);
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
pa_tagstruct_putu32(t, r->index);
pa_pstream_send_tagstruct(r->connection->pstream, t);
}
/*** sinkinput callbacks ***/
static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk) {
struct playback_stream *s;
assert(i && i->userdata && chunk);
s = i->userdata;
if (pa_memblockq_peek(s->memblockq, chunk) < 0)
return -1;
return 0;
}
static void sink_input_drop_cb(struct pa_sink_input *i, size_t length) {
struct playback_stream *s;
assert(i && i->userdata && length);
s = i->userdata;
pa_memblockq_drop(s->memblockq, length);
request_bytes(s);
if (s->drain_request && !pa_memblockq_is_readable(s->memblockq)) {
pa_pstream_send_simple_ack(s->connection->pstream, s->drain_tag);
s->drain_request = 0;
}
}
static void sink_input_kill_cb(struct pa_sink_input *i) {
assert(i && i->userdata);
send_playback_stream_killed((struct playback_stream *) i->userdata);
playback_stream_free((struct playback_stream *) i->userdata);
}
static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i) {
struct playback_stream *s;
assert(i && i->userdata);
s = i->userdata;
return pa_samples_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec);
}
/*** source_output callbacks ***/
static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk) {
struct record_stream *s;
assert(o && o->userdata && chunk);
s = o->userdata;
pa_memblockq_push(s->memblockq, chunk, 0);
if (!pa_pstream_is_pending(s->connection->pstream))
send_memblock(s->connection);
}
static void source_output_kill_cb(struct pa_source_output *o) {
assert(o && o->userdata);
send_record_stream_killed((struct record_stream *) o->userdata);
record_stream_free((struct record_stream *) o->userdata);
}
/*** pdispatch callbacks ***/
static void protocol_error(struct connection *c) {
fprintf(stderr, __FILE__": protocol error, kicking client\n");
connection_free(c);
}
static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct connection *c = userdata;
struct playback_stream *s;
size_t maxlength, tlength, prebuf, minreq;
uint32_t sink_index;
const char *name;
struct pa_sample_spec ss;
struct pa_tagstruct *reply;
struct pa_sink *sink;
assert(c && t && c->protocol && c->protocol->core);
if (pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
pa_tagstruct_getu32(t, &sink_index) < 0 ||
pa_tagstruct_getu32(t, &maxlength) < 0 ||
pa_tagstruct_getu32(t, &tlength) < 0 ||
pa_tagstruct_getu32(t, &prebuf) < 0 ||
pa_tagstruct_getu32(t, &minreq) < 0 ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
if (!c->authorized) {
pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
return;
}
if (sink_index == (uint32_t) -1)
sink = pa_sink_get_default(c->protocol->core);
else
sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
if (!sink) {
pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
return;
}
if (!(s = playback_stream_new(c, sink, &ss, name, maxlength, tlength, prebuf, minreq))) {
pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID);
return;
}
reply = pa_tagstruct_new(NULL, 0);
assert(reply);
pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
pa_tagstruct_putu32(reply, tag);
pa_tagstruct_putu32(reply, s->index);
assert(s->sink_input);
pa_tagstruct_putu32(reply, s->sink_input->index);
pa_pstream_send_tagstruct(c->pstream, reply);
request_bytes(s);
}
static void command_delete_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct connection *c = userdata;
uint32_t channel;
struct playback_stream *s;
assert(c && t);
if (pa_tagstruct_getu32(t, &channel) < 0 ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
if (!c->authorized) {
pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
return;
}
if (!(s = pa_idxset_get_by_index(c->playback_streams, channel))) {
pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST);
return;
}
playback_stream_free(s);
pa_pstream_send_simple_ack(c->pstream, tag);
}
static void command_create_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct connection *c = userdata;
struct record_stream *s;
size_t maxlength, fragment_size;
uint32_t source_index;
const char *name;
struct pa_sample_spec ss;
struct pa_tagstruct *reply;
struct pa_source *source;
assert(c && t && c->protocol && c->protocol->core);
if (pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
pa_tagstruct_getu32(t, &source_index) < 0 ||
pa_tagstruct_getu32(t, &maxlength) < 0 ||
pa_tagstruct_getu32(t, &fragment_size) < 0 ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
if (!c->authorized) {
pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
return;
}
if (source_index == (uint32_t) -1)
source = pa_source_get_default(c->protocol->core);
else
source = pa_idxset_get_by_index(c->protocol->core->sources, source_index);
if (!source) {
pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
return;
}
if (!(s = record_stream_new(c, source, &ss, name, maxlength, fragment_size))) {
pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID);
return;
}
reply = pa_tagstruct_new(NULL, 0);
assert(reply);
pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
pa_tagstruct_putu32(reply, tag);
pa_tagstruct_putu32(reply, s->index);
assert(s->source_output);
pa_tagstruct_putu32(reply, s->source_output->index);
pa_pstream_send_tagstruct(c->pstream, reply);
}
static void command_delete_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct connection *c = userdata;
uint32_t channel;
struct record_stream *s;
assert(c && t);
if (pa_tagstruct_getu32(t, &channel) < 0 ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
if (!c->authorized) {
pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
return;
}
if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) {
pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST);
return;
}
record_stream_free(s);
pa_pstream_send_simple_ack(c->pstream, tag);
}
static void command_exit(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct connection *c = userdata;
assert(c && t);
if (!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
if (!c->authorized) {
pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
return;
}
assert(c->protocol && c->protocol->core && c->protocol->core->mainloop);
c->protocol->core->mainloop->quit(c->protocol->core->mainloop, 0);
pa_pstream_send_simple_ack(c->pstream, tag); /* nonsense */
return;
}
static void command_auth(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct connection *c = userdata;
const void*cookie;
assert(c && t);
if (pa_tagstruct_get_arbitrary(t, &cookie, PA_NATIVE_COOKIE_LENGTH) < 0 ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
if (memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) != 0) {
fprintf(stderr, "protocol-native.c: Denied access to client with invalid authorization key.\n");
pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
return;
}
c->authorized = 1;
pa_pstream_send_simple_ack(c->pstream, tag);
return;
}
static void command_set_name(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct connection *c = userdata;
const char *name;
assert(c && t);
if (pa_tagstruct_gets(t, &name) < 0 ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
pa_client_rename(c->client, name);
pa_pstream_send_simple_ack(c->pstream, tag);
return;
}
static void command_lookup(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct connection *c = userdata;
const char *name;
uint32_t index = PA_IDXSET_INVALID;
assert(c && t);
if (pa_tagstruct_gets(t, &name) < 0 ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
if (!c->authorized) {
pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
return;
}
if (command == PA_COMMAND_LOOKUP_SINK) {
struct pa_sink *sink;
if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK)))
index = sink->index;
} else {
struct pa_source *source;
assert(command == PA_COMMAND_LOOKUP_SOURCE);
if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE)))
index = source->index;
}
if (index == PA_IDXSET_INVALID)
pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
else {
struct pa_tagstruct *reply;
reply = pa_tagstruct_new(NULL, 0);
assert(reply);
pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
pa_tagstruct_putu32(reply, tag);
pa_tagstruct_putu32(reply, index);
pa_pstream_send_tagstruct(c->pstream, reply);
}
}
static void command_drain_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct connection *c = userdata;
uint32_t index;
struct playback_stream *s;
assert(c && t);
if (pa_tagstruct_getu32(t, &index) < 0 ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
if (!c->authorized) {
pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
return;
}
if (!(s = pa_idxset_get_by_index(c->playback_streams, index))) {
pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
return;
}
s->drain_request = 0;
if (!pa_memblockq_is_readable(s->memblockq))
pa_pstream_send_simple_ack(c->pstream, tag);
else {
s->drain_request = 1;
s->drain_tag = tag;
}
}
static void command_stat(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct connection *c = userdata;
assert(c && t);
struct pa_tagstruct *reply;
if (!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
if (!c->authorized) {
pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
return;
}
reply = pa_tagstruct_new(NULL, 0);
assert(reply);
pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
pa_tagstruct_putu32(reply, tag);
pa_tagstruct_putu32(reply, pa_memblock_get_count());
pa_tagstruct_putu32(reply, pa_memblock_get_total());
pa_pstream_send_tagstruct(c->pstream, reply);
}
static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct connection *c = userdata;
assert(c && t);
struct pa_tagstruct *reply;
struct playback_stream *s;
uint32_t index, latency;
if (pa_tagstruct_getu32(t, &index) < 0 ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
if (!c->authorized) {
pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
return;
}
if (!(s = pa_idxset_get_by_index(c->playback_streams, index))) {
pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
return;
}
latency = pa_sink_input_get_latency(s->sink_input);
reply = pa_tagstruct_new(NULL, 0);
assert(reply);
pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
pa_tagstruct_putu32(reply, tag);
pa_tagstruct_putu32(reply, latency);
pa_pstream_send_tagstruct(c->pstream, reply);
}
/*** pstream callbacks ***/
static void pstream_packet_callback(struct pa_pstream *p, struct pa_packet *packet, void *userdata) {
struct connection *c = userdata;
assert(p && packet && packet->data && c);
if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) {
fprintf(stderr, "protocol-native: invalid packet.\n");
connection_free(c);
}
}
static void pstream_memblock_callback(struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata) {
struct connection *c = userdata;
struct playback_stream *stream;
assert(p && chunk && userdata);
if (!(stream = pa_idxset_get_by_index(c->playback_streams, channel))) {
fprintf(stderr, "protocol-native: client sent block for invalid stream.\n");
connection_free(c);
return;
}
if (chunk->length >= stream->requested_bytes)
stream->requested_bytes = 0;
else
stream->requested_bytes -= chunk->length;
pa_memblockq_push_align(stream->memblockq, chunk, delta);
assert(stream->sink_input);
pa_sink_notify(stream->sink_input->sink);
/*fprintf(stderr, "Recieved %u bytes.\n", chunk->length);*/
}
static void pstream_die_callback(struct pa_pstream *p, void *userdata) {
struct connection *c = userdata;
assert(p && c);
connection_free(c);
fprintf(stderr, "protocol-native: connection died.\n");
}
static void pstream_drain_callback(struct pa_pstream *p, void *userdata) {
struct connection *c = userdata;
assert(p && c);
send_memblock(c);
}
/*** client callbacks ***/
static void client_kill_cb(struct pa_client *c) {
assert(c && c->userdata);
connection_free(c->userdata);
}
/*** socket server callbacks ***/
static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) {
struct pa_protocol_native *p = userdata;
struct connection *c;
assert(s && io && p);
c = malloc(sizeof(struct connection));
assert(c);
c->authorized = p->public;
c->protocol = p;
assert(p->core);
c->client = pa_client_new(p->core, "NATIVE", "Client");
assert(c->client);
c->client->kill = client_kill_cb;
c->client->userdata = c;
c->client->owner = p->module;
c->pstream = pa_pstream_new(p->core->mainloop, io);
assert(c->pstream);
pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c);
pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c);
pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c);
pa_pstream_set_drain_callback(c->pstream, pstream_drain_callback, c);
c->pdispatch = pa_pdispatch_new(p->core->mainloop, command_table, PA_COMMAND_MAX);
assert(c->pdispatch);
c->record_streams = pa_idxset_new(NULL, NULL);
c->playback_streams = pa_idxset_new(NULL, NULL);
assert(c->record_streams && c->playback_streams);
c->rrobin_index = PA_IDXSET_INVALID;
pa_idxset_put(p->connections, c, NULL);
}
/*** module entry points ***/
struct pa_protocol_native* pa_protocol_native_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) {
struct pa_protocol_native *p;
uint32_t public;
assert(core && server && ma);
if (pa_modargs_get_value_u32(ma, "public", &public) < 0) {
fprintf(stderr, __FILE__": public= expects numeric argument.\n");
return NULL;
}
p = malloc(sizeof(struct pa_protocol_native));
assert(p);
if (pa_authkey_load_from_home(pa_modargs_get_value(ma, "cookie", PA_NATIVE_COOKIE_FILE), p->auth_cookie, sizeof(p->auth_cookie)) < 0) {
free(p);
return NULL;
}
p->module = m;
p->public = public;
p->server = server;
p->core = core;
p->connections = pa_idxset_new(NULL, NULL);
assert(p->connections);
pa_socket_server_set_callback(p->server, on_connection, p);
return p;
}
void pa_protocol_native_free(struct pa_protocol_native *p) {
struct connection *c;
assert(p);
while ((c = pa_idxset_first(p->connections, NULL)))
connection_free(c);
pa_idxset_free(p->connections, NULL, NULL);
pa_socket_server_free(p->server);
free(p);
}

35
polyp/protocol-native.h Normal file
View file

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

444
polyp/protocol-simple.c Normal file
View file

@ -0,0 +1,444 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "sink-input.h"
#include "source-output.h"
#include "protocol-simple.h"
#include "client.h"
#include "sample-util.h"
#include "namereg.h"
struct connection {
struct pa_protocol_simple *protocol;
struct pa_iochannel *io;
struct pa_sink_input *sink_input;
struct pa_source_output *source_output;
struct pa_client *client;
struct pa_memblockq *input_memblockq, *output_memblockq;
void *fixed_source;
struct {
struct pa_memblock *current_memblock;
size_t memblock_index, fragment_size;
} playback;
};
struct pa_protocol_simple {
struct pa_module *module;
struct pa_core *core;
struct pa_socket_server*server;
struct pa_idxset *connections;
enum {
RECORD = 1,
PLAYBACK = 2,
DUPLEX = 3
} mode;
struct pa_sample_spec sample_spec;
uint32_t sink_index, source_index;
};
#define PLAYBACK_BUFFER_SECONDS (.5)
#define PLAYBACK_BUFFER_FRAGMENTS (10)
#define RECORD_BUFFER_SECONDS (5)
#define RECORD_BUFFER_FRAGMENTS (100)
static void connection_free(struct connection *c) {
assert(c);
pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
if (c->playback.current_memblock)
pa_memblock_unref(c->playback.current_memblock);
if (c->sink_input)
pa_sink_input_free(c->sink_input);
if (c->source_output)
pa_source_output_free(c->source_output);
if (c->client)
pa_client_free(c->client);
if (c->io)
pa_iochannel_free(c->io);
if (c->input_memblockq)
pa_memblockq_free(c->input_memblockq);
if (c->output_memblockq)
pa_memblockq_free(c->output_memblockq);
if (c->fixed_source)
c->protocol->core->mainloop->cancel_fixed(c->protocol->core->mainloop, c->fixed_source);
free(c);
}
static int do_read(struct connection *c) {
struct pa_memchunk chunk;
ssize_t r;
size_t l;
if (!c->sink_input || !(l = pa_memblockq_missing(c->input_memblockq)))
return 0;
if (l > c->playback.fragment_size)
l = c->playback.fragment_size;
if (c->playback.current_memblock)
if (c->playback.current_memblock->length - c->playback.memblock_index < l) {
pa_memblock_unref(c->playback.current_memblock);
c->playback.current_memblock = NULL;
c->playback.memblock_index = 0;
}
if (!c->playback.current_memblock) {
c->playback.current_memblock = pa_memblock_new(c->playback.fragment_size*2);
assert(c->playback.current_memblock && c->playback.current_memblock->length >= l);
c->playback.memblock_index = 0;
}
if ((r = pa_iochannel_read(c->io, c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) {
fprintf(stderr, __FILE__": read() failed: %s\n", r == 0 ? "EOF" : strerror(errno));
return -1;
}
chunk.memblock = c->playback.current_memblock;
chunk.index = c->playback.memblock_index;
chunk.length = r;
assert(chunk.memblock);
c->playback.memblock_index += r;
assert(c->input_memblockq);
pa_memblockq_push_align(c->input_memblockq, &chunk, 0);
assert(c->sink_input);
pa_sink_notify(c->sink_input->sink);
return 0;
}
static int do_write(struct connection *c) {
struct pa_memchunk chunk;
ssize_t r;
if (!c->source_output)
return 0;
assert(c->output_memblockq);
if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
return 0;
assert(chunk.memblock && chunk.length);
if ((r = pa_iochannel_write(c->io, chunk.memblock->data+chunk.index, chunk.length)) < 0) {
pa_memblock_unref(chunk.memblock);
fprintf(stderr, "write(): %s\n", strerror(errno));
return -1;
}
pa_memblockq_drop(c->output_memblockq, r);
pa_memblock_unref(chunk.memblock);
return 0;
}
static void do_work(struct connection *c) {
assert(c);
assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed);
c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 0);
if (pa_iochannel_is_hungup(c->io))
goto fail;
if (pa_iochannel_is_writable(c->io))
if (do_write(c) < 0)
goto fail;
if (pa_iochannel_is_readable(c->io))
if (do_read(c) < 0)
goto fail;
return;
fail:
connection_free(c);
}
/*** sink_input callbacks ***/
static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk) {
struct connection*c;
assert(i && i->userdata && chunk);
c = i->userdata;
if (pa_memblockq_peek(c->input_memblockq, chunk) < 0)
return -1;
return 0;
}
static void sink_input_drop_cb(struct pa_sink_input *i, size_t length) {
struct connection*c = i->userdata;
assert(i && c && length);
pa_memblockq_drop(c->input_memblockq, length);
/* do something */
assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed);
c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1);
}
static void sink_input_kill_cb(struct pa_sink_input *i) {
assert(i && i->userdata);
connection_free((struct connection *) i->userdata);
}
static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i) {
struct connection*c = i->userdata;
assert(i && c);
return pa_samples_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
}
/*** source_output callbacks ***/
static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk) {
struct connection *c = o->userdata;
assert(o && c && chunk);
pa_memblockq_push(c->output_memblockq, chunk, 0);
/* do something */
assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed);
c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1);
}
static void source_output_kill_cb(struct pa_source_output *o) {
assert(o && o->userdata);
connection_free((struct connection *) o->userdata);
}
/*** client callbacks ***/
static void client_kill_cb(struct pa_client *c) {
assert(c && c->userdata);
connection_free((struct connection *) c->userdata);
}
/*** pa_iochannel callbacks ***/
static void io_callback(struct pa_iochannel*io, void *userdata) {
struct connection *c = userdata;
assert(io && c && c->io == io);
do_work(c);
}
/*** fixed callback ***/
static void fixed_callback(struct pa_mainloop_api*a, void *id, void *userdata) {
struct connection *c = userdata;
assert(a && c && c->fixed_source == id);
do_work(c);
}
/*** socket_server callbacks ***/
static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) {
struct pa_protocol_simple *p = userdata;
struct connection *c = NULL;
char cname[256];
assert(s && io && p);
c = malloc(sizeof(struct connection));
assert(c);
c->io = io;
c->sink_input = NULL;
c->source_output = NULL;
c->fixed_source = NULL;
c->input_memblockq = c->output_memblockq = NULL;
c->protocol = p;
c->playback.current_memblock = NULL;
c->playback.memblock_index = 0;
c->playback.fragment_size = 0;
pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
c->client = pa_client_new(p->core, "SIMPLE", cname);
assert(c->client);
c->client->owner = p->module;
c->client->kill = client_kill_cb;
c->client->userdata = c;
if (p->mode & PLAYBACK) {
struct pa_sink *sink;
size_t l;
if (!(sink = pa_idxset_get_by_index(p->core->sinks, p->sink_index)))
if (!(sink = pa_sink_get_default(p->core))) {
fprintf(stderr, "Failed to get sink.\n");
goto fail;
}
c->sink_input = pa_sink_input_new(sink, c->client->name, &p->sample_spec);
if (!c->sink_input) {
fprintf(stderr, "Failed to create sink input.\n");
goto fail;
}
c->sink_input->owner = p->module;
c->sink_input->client = c->client;
c->sink_input->peek = sink_input_peek_cb;
c->sink_input->drop = sink_input_drop_cb;
c->sink_input->kill = sink_input_kill_cb;
c->sink_input->get_latency = sink_input_get_latency_cb;
c->sink_input->userdata = c;
l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS);
c->input_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&p->sample_spec), l/2, l/PLAYBACK_BUFFER_FRAGMENTS);
assert(c->input_memblockq);
pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5);
c->playback.fragment_size = l/10;
}
if (p->mode & RECORD) {
struct pa_source *source;
size_t l;
if (!(source = pa_idxset_get_by_index(p->core->sources, p->source_index)))
if (!(source = pa_source_get_default(p->core))) {
fprintf(stderr, "Failed to get source.\n");
goto fail;
}
c->source_output = pa_source_output_new(source, c->client->name, &p->sample_spec);
if (!c->source_output) {
fprintf(stderr, "Failed to create source output.\n");
goto fail;
}
c->source_output->owner = p->module;
c->source_output->client = c->client;
c->source_output->push = source_output_push_cb;
c->source_output->kill = source_output_kill_cb;
c->source_output->userdata = c;
l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS);
c->output_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&p->sample_spec), 0, 0);
pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2);
}
pa_iochannel_set_callback(c->io, io_callback, c);
pa_idxset_put(p->connections, c, NULL);
c->fixed_source = p->core->mainloop->source_fixed(p->core->mainloop, fixed_callback, c);
assert(c->fixed_source);
p->core->mainloop->enable_fixed(p->core->mainloop, c->fixed_source, 0);
return;
fail:
if (c)
connection_free(c);
}
struct pa_protocol_simple* pa_protocol_simple_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) {
struct pa_protocol_simple* p = NULL;
uint32_t enable;
assert(core && server && ma);
p = malloc(sizeof(struct pa_protocol_simple));
assert(p);
memset(p, 0, sizeof(struct pa_protocol_simple));
p->module = m;
p->core = core;
p->server = server;
p->connections = pa_idxset_new(NULL, NULL);
p->sample_spec = core->default_sample_spec;
if (pa_modargs_get_sample_spec(ma, &p->sample_spec) < 0) {
fprintf(stderr, "Failed to parse sample type specification.\n");
goto fail;
}
if (pa_modargs_get_source_index(ma, core, &p->source_index) < 0) {
fprintf(stderr, __FILE__": source does not exist.\n");
goto fail;
}
if (pa_modargs_get_sink_index(ma, core, &p->sink_index) < 0) {
fprintf(stderr, __FILE__": sink does not exist.\n");
goto fail;
}
enable = 0;
if (pa_modargs_get_value_u32(ma, "record", &enable) < 0) {
fprintf(stderr, __FILE__": record= expects a numeric argument.\n");
goto fail;
}
p->mode = enable ? RECORD : 0;
enable = 1;
if (pa_modargs_get_value_u32(ma, "playback", &enable) < 0) {
fprintf(stderr, __FILE__": playback= expects a numeric argument.\n");
goto fail;
}
p->mode |= enable ? PLAYBACK : 0;
if ((p->mode & (RECORD|PLAYBACK)) == 0) {
fprintf(stderr, __FILE__": neither playback nor recording enabled for protocol.\n");
goto fail;
}
pa_socket_server_set_callback(p->server, on_connection, p);
return p;
fail:
if (p)
pa_protocol_simple_free(p);
return NULL;
}
void pa_protocol_simple_free(struct pa_protocol_simple *p) {
struct connection *c;
assert(p);
if (p->connections) {
while((c = pa_idxset_first(p->connections, NULL)))
connection_free(c);
pa_idxset_free(p->connections, NULL, NULL);
}
if (p->server)
pa_socket_server_free(p->server);
free(p);
}

35
polyp/protocol-simple.h Normal file
View file

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

60
polyp/pstream-util.c Normal file
View file

@ -0,0 +1,60 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include "native-common.h"
#include "pstream-util.h"
void pa_pstream_send_tagstruct(struct pa_pstream *p, struct pa_tagstruct *t) {
size_t length;
uint8_t *data;
struct pa_packet *packet;
assert(p && t);
data = pa_tagstruct_free_data(t, &length);
assert(data && length);
packet = pa_packet_new_dynamic(data, length);
assert(packet);
pa_pstream_send_packet(p, packet);
pa_packet_unref(packet);
}
void pa_pstream_send_error(struct pa_pstream *p, uint32_t tag, uint32_t error) {
struct pa_tagstruct *t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_ERROR);
pa_tagstruct_putu32(t, tag);
pa_tagstruct_putu32(t, error);
pa_pstream_send_tagstruct(p, t);
}
void pa_pstream_send_simple_ack(struct pa_pstream *p, uint32_t tag) {
struct pa_tagstruct *t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_REPLY);
pa_tagstruct_putu32(t, tag);
pa_pstream_send_tagstruct(p, t);
}

35
polyp/pstream-util.h Normal file
View file

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

457
polyp/pstream.c Normal file
View file

@ -0,0 +1,457 @@
/* $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 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 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 <netinet/in.h>
#include "pstream.h"
#include "queue.h"
enum pa_pstream_descriptor_index {
PA_PSTREAM_DESCRIPTOR_LENGTH,
PA_PSTREAM_DESCRIPTOR_CHANNEL,
PA_PSTREAM_DESCRIPTOR_DELTA,
PA_PSTREAM_DESCRIPTOR_MAX
};
typedef uint32_t pa_pstream_descriptor[PA_PSTREAM_DESCRIPTOR_MAX];
#define PA_PSTREAM_DESCRIPTOR_SIZE (PA_PSTREAM_DESCRIPTOR_MAX*sizeof(uint32_t))
#define FRAME_SIZE_MAX (1024*64)
struct item_info {
enum { PA_PSTREAM_ITEM_PACKET, PA_PSTREAM_ITEM_MEMBLOCK } type;
/* memblock info */
struct pa_memchunk chunk;
uint32_t channel;
int32_t delta;
/* packet info */
struct pa_packet *packet;
};
struct pa_pstream {
struct pa_mainloop_api *mainloop;
struct mainloop_source *mainloop_source;
struct pa_iochannel *io;
struct pa_queue *send_queue;
int in_use, shall_free;
int dead;
void (*die_callback) (struct pa_pstream *p, void *userdata);
void *die_callback_userdata;
struct {
struct item_info* current;
pa_pstream_descriptor descriptor;
void *data;
size_t index;
} write;
struct {
struct pa_memblock *memblock;
struct pa_packet *packet;
pa_pstream_descriptor descriptor;
void *data;
size_t index;
} read;
void (*recieve_packet_callback) (struct pa_pstream *p, struct pa_packet *packet, void *userdata);
void *recieve_packet_callback_userdata;
void (*recieve_memblock_callback) (struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata);
void *recieve_memblock_callback_userdata;
void (*drain_callback)(struct pa_pstream *p, void *userdata);
void *drain_userdata;
};
static void do_write(struct pa_pstream *p);
static void do_read(struct pa_pstream *p);
static void do_something(struct pa_pstream *p) {
assert(p && !p->shall_free);
p->mainloop->enable_fixed(p->mainloop, p->mainloop_source, 0);
if (p->dead)
return;
if (pa_iochannel_is_hungup(p->io)) {
p->dead = 1;
if (p->die_callback)
p->die_callback(p, p->die_callback_userdata);
return;
}
if (pa_iochannel_is_writable(p->io)) {
p->in_use = 1;
do_write(p);
p->in_use = 0;
if (p->shall_free) {
pa_pstream_free(p);
return;
}
}
if (pa_iochannel_is_readable(p->io)) {
p->in_use = 1;
do_read(p);
p->in_use = 0;
if (p->shall_free) {
pa_pstream_free(p);
return;
}
}
}
static void io_callback(struct pa_iochannel*io, void *userdata) {
struct pa_pstream *p = userdata;
assert(p && p->io == io);
do_something(p);
}
static void fixed_callback(struct pa_mainloop_api *m, void *id, void*userdata) {
struct pa_pstream *p = userdata;
assert(p && p->mainloop_source == id && p->mainloop == m);
do_something(p);
}
struct pa_pstream *pa_pstream_new(struct pa_mainloop_api *m, struct pa_iochannel *io) {
struct pa_pstream *p;
assert(io);
p = malloc(sizeof(struct pa_pstream));
assert(p);
p->io = io;
pa_iochannel_set_callback(io, io_callback, p);
p->dead = 0;
p->die_callback = NULL;
p->die_callback_userdata = NULL;
p->mainloop = m;
p->mainloop_source = m->source_fixed(m, fixed_callback, p);
m->enable_fixed(m, p->mainloop_source, 0);
p->send_queue = pa_queue_new();
assert(p->send_queue);
p->write.current = NULL;
p->write.index = 0;
p->read.memblock = NULL;
p->read.packet = NULL;
p->read.index = 0;
p->recieve_packet_callback = NULL;
p->recieve_packet_callback_userdata = NULL;
p->recieve_memblock_callback = NULL;
p->recieve_memblock_callback_userdata = NULL;
p->drain_callback = NULL;
p->drain_userdata = NULL;
p->in_use = p->shall_free = 0;
return p;
}
static void item_free(void *item, void *p) {
struct item_info *i = item;
assert(i);
if (i->type == PA_PSTREAM_ITEM_MEMBLOCK) {
assert(i->chunk.memblock);
pa_memblock_unref(i->chunk.memblock);
} else {
assert(i->type == PA_PSTREAM_ITEM_PACKET);
assert(i->packet);
pa_packet_unref(i->packet);
}
free(i);
}
void pa_pstream_free(struct pa_pstream *p) {
assert(p);
if (p->in_use) {
/* If this pstream object is used by someone else on the call stack, we have to postpone the freeing */
p->dead = p->shall_free = 1;
return;
}
pa_iochannel_free(p->io);
pa_queue_free(p->send_queue, item_free, NULL);
if (p->write.current)
item_free(p->write.current, NULL);
if (p->read.memblock)
pa_memblock_unref(p->read.memblock);
if (p->read.packet)
pa_packet_unref(p->read.packet);
p->mainloop->cancel_fixed(p->mainloop, p->mainloop_source);
free(p);
}
void pa_pstream_send_packet(struct pa_pstream*p, struct pa_packet *packet) {
struct item_info *i;
assert(p && packet);
i = malloc(sizeof(struct item_info));
assert(i);
i->type = PA_PSTREAM_ITEM_PACKET;
i->packet = pa_packet_ref(packet);
pa_queue_push(p->send_queue, i);
p->mainloop->enable_fixed(p->mainloop, p->mainloop_source, 1);
}
void pa_pstream_send_memblock(struct pa_pstream*p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk) {
struct item_info *i;
assert(p && channel != (uint32_t) -1 && chunk);
i = malloc(sizeof(struct item_info));
assert(i);
i->type = PA_PSTREAM_ITEM_MEMBLOCK;
i->chunk = *chunk;
i->channel = channel;
i->delta = delta;
pa_memblock_ref(i->chunk.memblock);
pa_queue_push(p->send_queue, i);
p->mainloop->enable_fixed(p->mainloop, p->mainloop_source, 1);
}
void pa_pstream_set_recieve_packet_callback(struct pa_pstream *p, void (*callback) (struct pa_pstream *p, struct pa_packet *packet, void *userdata), void *userdata) {
assert(p && callback);
p->recieve_packet_callback = callback;
p->recieve_packet_callback_userdata = userdata;
}
void pa_pstream_set_recieve_memblock_callback(struct pa_pstream *p, void (*callback) (struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata), void *userdata) {
assert(p && callback);
p->recieve_memblock_callback = callback;
p->recieve_memblock_callback_userdata = userdata;
}
static void prepare_next_write_item(struct pa_pstream *p) {
assert(p);
if (!(p->write.current = pa_queue_pop(p->send_queue)))
return;
p->write.index = 0;
if (p->write.current->type == PA_PSTREAM_ITEM_PACKET) {
assert(p->write.current->packet);
p->write.data = p->write.current->packet->data;
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->packet->length);
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl((uint32_t) -1);
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA] = 0;
} else {
assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK && p->write.current->chunk.memblock);
p->write.data = p->write.current->chunk.memblock->data + p->write.current->chunk.index;
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->chunk.length);
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl(p->write.current->channel);
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA] = htonl(p->write.current->delta);
}
}
static void do_write(struct pa_pstream *p) {
void *d;
size_t l;
ssize_t r;
assert(p);
if (!p->write.current)
prepare_next_write_item(p);
if (!p->write.current)
return;
assert(p->write.data);
if (p->write.index < PA_PSTREAM_DESCRIPTOR_SIZE) {
d = (void*) p->write.descriptor + p->write.index;
l = PA_PSTREAM_DESCRIPTOR_SIZE - p->write.index;
} else {
d = (void*) p->write.data + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE;
l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE);
}
if ((r = pa_iochannel_write(p->io, d, l)) < 0)
goto die;
p->write.index += r;
if (p->write.index >= PA_PSTREAM_DESCRIPTOR_SIZE+ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])) {
assert(p->write.current);
item_free(p->write.current, (void *) 1);
p->write.current = NULL;
if (p->drain_callback && !pa_pstream_is_pending(p))
p->drain_callback(p, p->drain_userdata);
}
return;
die:
p->dead = 1;
if (p->die_callback)
p->die_callback(p, p->die_callback_userdata);
}
static void do_read(struct pa_pstream *p) {
void *d;
size_t l;
ssize_t r;
assert(p);
if (p->read.index < PA_PSTREAM_DESCRIPTOR_SIZE) {
d = (void*) p->read.descriptor + p->read.index;
l = PA_PSTREAM_DESCRIPTOR_SIZE - p->read.index;
} else {
assert(p->read.data);
d = (void*) p->read.data + p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE;
l = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE);
}
if ((r = pa_iochannel_read(p->io, d, l)) <= 0)
goto die;
p->read.index += r;
if (p->read.index == PA_PSTREAM_DESCRIPTOR_SIZE) {
/* Reading of frame descriptor complete */
/* Frame size too large */
if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) > FRAME_SIZE_MAX)
goto die;
assert(!p->read.packet && !p->read.memblock);
if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]) == (uint32_t) -1) {
/* Frame is a packet frame */
p->read.packet = pa_packet_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]));
assert(p->read.packet);
p->read.data = p->read.packet->data;
} else {
/* Frame is a memblock frame */
p->read.memblock = pa_memblock_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]));
assert(p->read.memblock);
p->read.data = p->read.memblock->data;
}
} else if (p->read.index > PA_PSTREAM_DESCRIPTOR_SIZE) {
/* Frame payload available */
if (p->read.memblock && p->recieve_memblock_callback) { /* Is this memblock data? Than pass it to the user */
size_t l;
l = (p->read.index - r) < PA_PSTREAM_DESCRIPTOR_SIZE ? p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE : (size_t) r;
if (l > 0) {
struct pa_memchunk chunk;
chunk.memblock = p->read.memblock;
chunk.index = p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE - l;
chunk.length = l;
if (p->recieve_memblock_callback)
p->recieve_memblock_callback(
p,
ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]),
(int32_t) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA]),
&chunk,
p->recieve_memblock_callback_userdata);
}
}
/* Frame complete */
if (p->read.index >= ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) + PA_PSTREAM_DESCRIPTOR_SIZE) {
if (p->read.memblock) {
assert(!p->read.packet);
pa_memblock_unref(p->read.memblock);
p->read.memblock = NULL;
} else {
assert(p->read.packet);
if (p->recieve_packet_callback)
p->recieve_packet_callback(p, p->read.packet, p->recieve_packet_callback_userdata);
pa_packet_unref(p->read.packet);
p->read.packet = NULL;
}
p->read.index = 0;
}
}
return;
die:
p->dead = 1;
if (p->die_callback)
p->die_callback(p, p->die_callback_userdata);
}
void pa_pstream_set_die_callback(struct pa_pstream *p, void (*callback)(struct pa_pstream *p, void *userdata), void *userdata) {
assert(p && callback);
p->die_callback = callback;
p->die_callback_userdata = userdata;
}
int pa_pstream_is_pending(struct pa_pstream *p) {
assert(p);
if (p->dead)
return 0;
return p->write.current || !pa_queue_is_empty(p->send_queue);
}
void pa_pstream_set_drain_callback(struct pa_pstream *p, void (*cb)(struct pa_pstream *p, void *userdata), void *userdata) {
assert(p);
p->drain_callback = cb;
p->drain_userdata = userdata;
}

51
polyp/pstream.h Normal file
View file

@ -0,0 +1,51 @@
#ifndef foopstreamhfoo
#define foopstreamhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
#include "packet.h"
#include "memblock.h"
#include "iochannel.h"
#include "mainloop-api.h"
#include "memchunk.h"
/* It is safe to destroy the calling pstream object from all callbacks */
struct pa_pstream;
struct pa_pstream* pa_pstream_new(struct pa_mainloop_api *m, struct pa_iochannel *io);
void pa_pstream_free(struct pa_pstream*p);
void pa_pstream_send_packet(struct pa_pstream*p, struct pa_packet *packet);
void pa_pstream_send_memblock(struct pa_pstream*p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk);
void pa_pstream_set_recieve_packet_callback(struct pa_pstream *p, void (*callback) (struct pa_pstream *p, struct pa_packet *packet, void *userdata), void *userdata);
void pa_pstream_set_recieve_memblock_callback(struct pa_pstream *p, void (*callback) (struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata), void *userdata);
void pa_pstream_set_drain_callback(struct pa_pstream *p, void (*cb)(struct pa_pstream *p, void *userdata), void *userdata);
void pa_pstream_set_die_callback(struct pa_pstream *p, void (*callback)(struct pa_pstream *p, void *userdata), void *userdata);
int pa_pstream_is_pending(struct pa_pstream *p);
#endif

109
polyp/queue.c Normal file
View file

@ -0,0 +1,109 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include "queue.h"
struct queue_entry {
struct queue_entry *next;
void *data;
};
struct pa_queue {
struct queue_entry *front, *back;
unsigned length;
};
struct pa_queue* pa_queue_new(void) {
struct pa_queue *q = malloc(sizeof(struct pa_queue));
assert(q);
q->front = q->back = NULL;
q->length = 0;
return q;
}
void pa_queue_free(struct pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata) {
struct queue_entry *e;
assert(q);
e = q->front;
while (e) {
struct queue_entry *n = e->next;
if (destroy)
destroy(e->data, userdata);
free(e);
e = n;
}
free(q);
}
void pa_queue_push(struct pa_queue *q, void *p) {
struct queue_entry *e;
e = malloc(sizeof(struct queue_entry));
e->data = p;
e->next = NULL;
if (q->back)
q->back->next = e;
else {
assert(!q->front);
q->front = e;
}
q->back = e;
q->length++;
}
void* pa_queue_pop(struct pa_queue *q) {
void *p;
struct queue_entry *e;
assert(q);
if (!(e = q->front))
return NULL;
q->front = e->next;
if (q->back == e)
q->back = NULL;
p = e->data;
free(e);
q->length--;
return p;
}
int pa_queue_is_empty(struct pa_queue *q) {
assert(q);
return q->length == 0;
}

34
polyp/queue.h Normal file
View file

@ -0,0 +1,34 @@
#ifndef fooqueuehfoo
#define fooqueuehfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 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.
***/
struct pa_queue;
struct pa_queue* pa_queue_new(void);
void pa_queue_free(struct pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata);
void pa_queue_push(struct pa_queue *q, void *p);
void* pa_queue_pop(struct pa_queue *q);
int pa_queue_is_empty(struct pa_queue *q);
#endif

180
polyp/resampler.c Normal file
View file

@ -0,0 +1,180 @@
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 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 <samplerate.h>
#include "resampler.h"
#include "sconv.h"
struct pa_resampler {
struct pa_sample_spec i_ss, o_ss;
float* i_buf, *o_buf;
unsigned i_alloc, o_alloc;
size_t i_sz, o_sz;
int channels;
pa_convert_to_float32_func_t to_float32_func;
pa_convert_from_float32_func_t from_float32_func;
SRC_STATE *src_state;
};
struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const struct pa_sample_spec *b) {
struct pa_resampler *r;
int err;
assert(a && b && pa_sample_spec_valid(a) && pa_sample_spec_valid(b));
if (a->channels != b->channels && a->channels != 1 && b->channels != 1)
goto fail;
if (a->format == PA_SAMPLE_ALAW || a->format == PA_SAMPLE_ULAW || b->format == PA_SAMPLE_ALAW || b->format == PA_SAMPLE_ULAW)
goto fail;
r = malloc(sizeof(struct pa_resampler));
assert(r);
r->channels = a->channels;
if (b->channels < r->channels)
r->channels = b->channels;
r->i_buf = r->o_buf = NULL;
r->i_alloc = r->o_alloc = 0;
if (a->rate != b->rate) {
r->src_state = src_new(SRC_SINC_FASTEST, r->channels, &err);
if (err != 0 || !r->src_state)
goto fail;
} else
r->src_state = NULL;
r->i_ss = *a;
r->o_ss = *b;
r->i_sz = pa_sample_size(a);
r->o_sz = pa_sample_size(b);
r->to_float32_func = pa_get_convert_to_float32_function(a->format);
r->from_float32_func = pa_get_convert_from_float32_function(b->format);
assert(r->to_float32_func && r->from_float32_func);
return r;
fail:
if (r)
free(r);
return NULL;
}
void pa_resampler_free(struct pa_resampler *r) {
assert(r);
if (r->src_state)
src_delete(r->src_state);
free(r->i_buf);
free(r->o_buf);
free(r);
}
size_t pa_resampler_request(struct pa_resampler *r, size_t out_length) {
assert(r && (out_length % r->o_sz) == 0);
return (((out_length / r->o_sz)*r->i_ss.rate)/r->o_ss.rate) * r->i_sz;
}
void pa_resampler_run(struct pa_resampler *r, const struct pa_memchunk *in, struct pa_memchunk *out) {
unsigned i_nchannels, o_nchannels, ins, ons, eff_ins, eff_ons;
float *cbuf;
assert(r && in && out && in->length && in->memblock && (in->length % r->i_sz) == 0);
/* How many input samples? */
ins = in->length/r->i_sz;
/* How much space for output samples? */
if (r->src_state)
ons = (ins*r->o_ss.rate/r->i_ss.rate)+1024;
else
ons = ins;
/* How many channels? */
if (r->i_ss.channels == r->o_ss.channels) {
i_nchannels = o_nchannels = 1;
eff_ins = ins*r->i_ss.channels; /* effective samples */
eff_ons = ons*r->o_ss.channels;
} else {
i_nchannels = r->i_ss.channels;
o_nchannels = r->o_ss.channels;
eff_ins = ins;
eff_ons = ons;
}
out->memblock = pa_memblock_new(out->length = (ons*r->o_sz));
out->index = 0;
assert(out->memblock);
if (r->i_alloc < eff_ins)
r->i_buf = realloc(r->i_buf, sizeof(float) * (r->i_alloc = eff_ins));
assert(r->i_buf);
r->to_float32_func(eff_ins, in->memblock->data+in->index, i_nchannels, r->i_buf);
if (r->src_state) {
int ret;
SRC_DATA data;
if (r->o_alloc < eff_ons)
r->o_buf = realloc(r->o_buf, sizeof(float) * (r->o_alloc = eff_ons));
assert(r->o_buf);
data.data_in = r->i_buf;
data.input_frames = ins;
data.data_out = r->o_buf;
data.output_frames = ons;
data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate;
data.end_of_input = 0;
ret = src_process(r->src_state, &data);
assert(ret == 0);
assert((unsigned) data.input_frames_used == ins);
cbuf = r->o_buf;
ons = data.output_frames_gen;
if (r->i_ss.channels == r->o_ss.channels)
eff_ons = ons*r->o_ss.channels;
else
eff_ons = ons;
} else
cbuf = r->i_buf;
r->from_float32_func(eff_ons, cbuf, out->memblock->data+out->index, o_nchannels);
out->length = ons*r->o_sz;
}

37
polyp/resampler.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef fooresamplerhfoo
#define fooresamplerhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 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 "sample.h"
#include "memblock.h"
#include "memchunk.h"
struct pa_resampler;
struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const struct pa_sample_spec *b);
void pa_resampler_free(struct pa_resampler *r);
size_t pa_resampler_request(struct pa_resampler *r, size_t out_length);
void pa_resampler_run(struct pa_resampler *r, const struct pa_memchunk *in, struct pa_memchunk *out);
#endif

144
polyp/sample-util.c Normal file
View file

@ -0,0 +1,144 @@
/* $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 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 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 "sample-util.h"
struct pa_memblock *pa_silence_memblock(struct pa_memblock* b, const struct pa_sample_spec *spec) {
assert(b && b->data && spec);
pa_silence_memory(b->data, b->length, spec);
return b;
}
void pa_silence_memchunk(struct pa_memchunk *c, const struct pa_sample_spec *spec) {
assert(c && c->memblock && c->memblock->data && spec && c->length);
pa_silence_memory(c->memblock->data+c->index, c->length, spec);
}
void pa_silence_memory(void *p, size_t length, const struct pa_sample_spec *spec) {
char c = 0;
assert(p && length && spec);
switch (spec->format) {
case PA_SAMPLE_U8:
c = 127;
break;
case PA_SAMPLE_S16LE:
case PA_SAMPLE_S16BE:
case PA_SAMPLE_FLOAT32:
c = 0;
break;
case PA_SAMPLE_ALAW:
case PA_SAMPLE_ULAW:
c = 80;
break;
default:
assert(0);
}
memset(p, c, length);
}
size_t pa_mix(struct pa_mix_info channels[], unsigned nchannels, void *data, size_t length, const struct pa_sample_spec *spec, uint32_t volume) {
unsigned c, d;
assert(channels && data && length && spec);
assert(spec->format == PA_SAMPLE_S16NE);
for (d = 0;; d += sizeof(int16_t)) {
int32_t sum = 0;
if (d >= length)
return d;
for (c = 0; c < nchannels; c++) {
int32_t v;
uint32_t volume = channels[c].volume;
if (d >= channels[c].chunk.length)
return d;
if (volume == PA_VOLUME_MUTE)
v = 0;
else {
v = *((int16_t*) (channels[c].chunk.memblock->data + channels[c].chunk.index + d));
if (volume != PA_VOLUME_NORM)
v = (int32_t) ((float)v*volume/PA_VOLUME_NORM);
}
sum += v;
}
if (volume == PA_VOLUME_MUTE)
sum = 0;
else if (volume != PA_VOLUME_NORM)
sum = (int32_t) ((float) sum*volume/PA_VOLUME_NORM);
if (sum < -0x8000) sum = -0x8000;
if (sum > 0x7FFF) sum = 0x7FFF;
*((int16_t*) data) = sum;
data += sizeof(int16_t);
}
}
void pa_volume_memchunk(struct pa_memchunk*c, const struct pa_sample_spec *spec, uint32_t volume) {
int16_t *d;
size_t n;
assert(c && spec && (c->length % pa_sample_size(spec) == 0));
assert(spec->format == PA_SAMPLE_S16NE);
if (volume == PA_VOLUME_NORM)
return;
if (volume == PA_VOLUME_MUTE) {
pa_silence_memchunk(c, spec);
return;
}
for (d = (c->memblock->data+c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) {
int32_t t = (int32_t)(*d);
t *= volume;
t /= PA_VOLUME_NORM;
if (t < -0x8000) t = -0x8000;
if (t > 0x7FFF) t = 0x7FFF;
*d = (int16_t) t;
}
}
uint32_t pa_volume_multiply(uint32_t a, uint32_t b) {
uint64_t p = a;
p *= b;
p /= PA_VOLUME_NORM;
return (uint32_t) p;
}

48
polyp/sample-util.h Normal file
View file

@ -0,0 +1,48 @@
#ifndef foosampleutilhfoo
#define foosampleutilhfoo
/* $Id$ */
/***
This file is part of polypaudio.
polypaudio is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 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 "sample.h"
#include "memblock.h"
#include "memchunk.h"
#define PA_VOLUME_NORM (0x100)
#define PA_VOLUME_MUTE (0)
struct pa_memblock *pa_silence_memblock(struct pa_memblock* b, const struct pa_sample_spec *spec);
void pa_silence_memchunk(struct pa_memchunk *c, const struct pa_sample_spec *spec);
void pa_silence_memory(void *p, size_t length, const struct pa_sample_spec *spec);
struct pa_mix_info {
struct pa_memchunk chunk;
uint32_t volume;
void *userdata;
};
size_t pa_mix(struct pa_mix_info channels[], unsigned nchannels, void *data, size_t length, const struct pa_sample_spec *spec, uint32_t volume);
void pa_volume_memchunk(struct pa_memchunk*c, const struct pa_sample_spec *spec, uint32_t volume);
uint32_t pa_volume_multiply(uint32_t a, uint32_t b);
#endif

99
polyp/sample.c Normal file
View file

@ -0,0 +1,99 @@
/* $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 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 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 "sample.h"
size_t pa_sample_size(const struct pa_sample_spec *spec) {
assert(spec);
size_t b = 1;
switch (spec->format) {
case PA_SAMPLE_U8:
case PA_SAMPLE_ULAW:
case PA_SAMPLE_ALAW:
b = 1;
break;
case PA_SAMPLE_S16LE:
case PA_SAMPLE_S16BE:
b = 2;
break;
case PA_SAMPLE_FLOAT32LE:
case PA_SAMPLE_FLOAT32BE:
b = 4;
break;
default:
assert(0);
}
return b * spec->channels;
}
size_t pa_bytes_per_second(const struct pa_sample_spec *spec) {
assert(spec);
return spec->rate*pa_sample_size(spec);
}
uint32_t pa_samples_usec(size_t length, const struct pa_sample_spec *spec) {
assert(spec);
return (uint32_t) (((double) length /pa_sample_size(spec))/spec->rate*1000000);
}
int pa_sample_spec_valid(const struct pa_sample_spec *spec) {
assert(spec);
if (!spec->rate || !spec->channels)
return 0;
if (spec->format >= PA_SAMPLE_MAX)
return 0;
return 1;
}
int pa_sample_spec_equal(const struct pa_sample_spec*a, const struct pa_sample_spec*b) {
assert(a && b);
return (a->format == b->format) && (a->rate == b->rate) && (a->channels == b->channels);
}
void pa_sample_snprint(char *s, size_t l, const struct pa_sample_spec *spec) {
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",
};
assert(pa_sample_spec_valid(spec));
snprintf(s, l, "%s %uch %uHz", table[spec->format], spec->channels, spec->rate);
}

64
polyp/sample.h Normal file
View file

@ -0,0 +1,64 @@
#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 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 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>
enum pa_sample_format {
PA_SAMPLE_U8,
PA_SAMPLE_ALAW,
PA_SAMPLE_ULAW,
PA_SAMPLE_S16LE,
PA_SAMPLE_S16BE,
PA_SAMPLE_FLOAT32LE,
PA_SAMPLE_FLOAT32BE,
PA_SAMPLE_MAX
};
#ifdef WORDS_BIGENDIAN
#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE
#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE
#else
#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE
#endif
#define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32NE
struct pa_sample_spec {
enum pa_sample_format format;
uint32_t rate;
uint8_t channels;
};
size_t pa_bytes_per_second(const struct pa_sample_spec *spec);
size_t pa_sample_size(const struct pa_sample_spec *spec);
uint32_t pa_samples_usec(size_t length, const struct pa_sample_spec *spec);
int pa_sample_spec_valid(const struct pa_sample_spec *spec);
int pa_sample_spec_equal(const struct pa_sample_spec*a, const struct pa_sample_spec*b);
#define PA_SAMPLE_SNPRINT_MAX_LENGTH 32
void pa_sample_snprint(char *s, size_t l, const struct pa_sample_spec *spec);
#endif

34
polyp/sconv-s16be.c Normal file
View file

@ -0,0 +1,34 @@
/* $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 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 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 "sconv-s16be.h"
#define INT16_FROM INT16_FROM_BE
#define INT16_TO INT16_TO_BE
#define pa_sconv_s16le_to_float32 pa_sconv_s16be_to_float32
#define pa_sconv_s16le_from_float32 pa_sconv_s16be_from_float32
#include "sconv-s16le.c"

28
polyp/sconv-s16be.h Normal file
View file

@ -0,0 +1,28 @@
#ifndef foosconv_s16befoo
#define foosconv_s16befoo
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
void pa_sconv_s16be_to_float32(unsigned n, const void *a, unsigned an, float *b);
void pa_sconv_s16be_from_float32(unsigned n, const float *a, void *b, unsigned bn);
#endif

82
polyp/sconv-s16le.c Normal file
View file

@ -0,0 +1,82 @@
/* $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 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 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 <inttypes.h>
#include "endianmacros.h"
#include "sconv.h"
#ifndef INT16_FROM
#define INT16_FROM INT16_FROM_LE
#endif
#ifndef INT16_TO
#define INT16_TO INT16_TO_LE
#endif
void pa_sconv_s16le_to_float32(unsigned n, const void *a, unsigned an, float *b) {
const int16_t *ca = a;
assert(n && a && an && b);
for (; n > 0; n--) {
unsigned i;
float sum = 0;
for (i = 0; i < an; i++) {
int16_t s = *(ca++);
sum += ((float) INT16_FROM(s))/0x7FFF;
}
if (sum > 1)
sum = 1;
if (sum < -1)
sum = -1;
*(b++) = sum;
}
}
void pa_sconv_s16le_from_float32(unsigned n, const float *a, void *b, unsigned bn) {
int16_t *cb = b;
assert(n && a && b && bn);
for (; n > 0; n--) {
unsigned i;
int16_t s;
float v = *(a++);
if (v > 1)
v = 1;
if (v < -1)
v = -1;
s = (int16_t) (v * 0x7FFF);
s = INT16_TO(s);
for (i = 0; i < bn; i++)
*(cb++) = s;
}
}

28
polyp/sconv-s16le.h Normal file
View file

@ -0,0 +1,28 @@
#ifndef foosconv_s16lefoo
#define foosconv_s16lefoo
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
void pa_sconv_s16le_to_float32(unsigned n, const void *a, unsigned an, float *b);
void pa_sconv_s16le_from_float32(unsigned n, const float *a, void *b, unsigned bn);
#endif

137
polyp/sconv.c Normal file
View file

@ -0,0 +1,137 @@
/* $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 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 General Public License
along with polypaudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "endianmacros.h"
#include "sconv.h"
#include "sconv-s16le.h"
#include "sconv-s16be.h"
static void u8_to_float32(unsigned n, const void *a, unsigned an, float *b) {
unsigned i;
const uint8_t *ca = a;
assert(n && a && an && b);
for (; n > 0; n--) {
float sum = 0;
for (i = 0; i < an; i++) {
uint8_t v = *(ca++);
sum += (((float) v)-127)/127;
}
if (sum > 1)
sum = 1;
if (sum < -1)
sum = -1;
*(b++) = sum;
}
}
static void u8_from_float32(unsigned n, const float *a, void *b, unsigned bn) {
unsigned i;
uint8_t *cb = b;
assert(n && a && b && bn);
for (; n > 0; n--) {
float v = *(a++);
uint8_t u;
if (v > 1)
v = 1;
if (v < -1)
v = -1;
u = (uint8_t) (v*127+127);
for (i = 0; i < bn; i++)
*(cb++) = u;
}
}
static void float32_to_float32(unsigned n, const void *a, unsigned an, float *b) {
unsigned i;
const float *ca = a;
assert(n && a && an && b);
for (; n > 0; n--) {
float sum = 0;
for (i = 0; i < an; i++)
sum += *(ca++);
if (sum > 1)
sum = 1;
if (sum < -1)
sum = -1;
*(b++) = sum;
}
}
static void float32_from_float32(unsigned n, const float *a, void *b, unsigned bn) {
unsigned i;
float *cb = b;
assert(n && a && b && bn);
for (; n > 0; n--) {
float v = *(a++);
for (i = 0; i < bn; i++)
*(cb++) = v;
}
}
pa_convert_to_float32_func_t pa_get_convert_to_float32_function(enum pa_sample_format f) {
switch(f) {
case PA_SAMPLE_U8:
return u8_to_float32;
case PA_SAMPLE_S16LE:
return pa_sconv_s16le_to_float32;
case PA_SAMPLE_S16BE:
return pa_sconv_s16be_to_float32;
case PA_SAMPLE_FLOAT32:
return float32_to_float32;
default:
return NULL;
}
}
pa_convert_from_float32_func_t pa_get_convert_from_float32_function(enum pa_sample_format f) {
switch(f) {
case PA_SAMPLE_U8:
return u8_from_float32;
case PA_SAMPLE_S16LE:
return pa_sconv_s16le_from_float32;
case PA_SAMPLE_S16BE:
return pa_sconv_s16be_from_float32;
case PA_SAMPLE_FLOAT32:
return float32_from_float32;
default:
return NULL;
}
}

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