mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-02 09:01:46 -05:00
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:
parent
563201e128
commit
41f6aea8fd
125 changed files with 0 additions and 0 deletions
363
polyp/Makefile.am
Normal file
363
polyp/Makefile.am
Normal 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
98
polyp/alsa-util.c
Normal 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
35
polyp/alsa-util.h
Normal 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
151
polyp/authkey.c
Normal 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
31
polyp/authkey.h
Normal 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
557
polyp/cli-command.c
Normal 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
32
polyp/cli-command.h
Normal 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
148
polyp/cli.c
Normal 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
36
polyp/cli.h
Normal 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
79
polyp/client.c
Normal 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
51
polyp/client.h
Normal 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
203
polyp/clitext.c
Normal 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
35
polyp/clitext.h
Normal 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
111
polyp/cmdline.c
Normal 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
36
polyp/cmdline.h
Normal 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
88
polyp/core.c
Normal 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
45
polyp/core.h
Normal 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
73
polyp/depmod.py
Executable 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
98
polyp/dynarray.c
Normal 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
37
polyp/dynarray.h
Normal 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
62
polyp/endianmacros.h
Normal 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
213
polyp/esound.h
Normal 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
170
polyp/hashmap.c
Normal 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
37
polyp/hashmap.h
Normal 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
397
polyp/idxset.c
Normal 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
63
polyp/idxset.h
Normal 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
222
polyp/iochannel.c
Normal 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
50
polyp/iochannel.h
Normal 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
220
polyp/ioline.c
Normal 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
35
polyp/ioline.h
Normal 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
182
polyp/main.c
Normal 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
60
polyp/mainloop-api.c
Normal 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
65
polyp/mainloop-api.h
Normal 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
163
polyp/mainloop-signal.c
Normal 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
33
polyp/mainloop-signal.h
Normal 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
553
polyp/mainloop.c
Normal 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
37
polyp/mainloop.h
Normal 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
113
polyp/memblock.c
Normal 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
49
polyp/memblock.h
Normal 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
326
polyp/memblockq.c
Normal 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
82
polyp/memblockq.h
Normal 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
149
polyp/memchunk.c
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of polypaudio.
|
||||
|
||||
polypaudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU 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
41
polyp/memchunk.h
Normal 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
288
polyp/modargs.c
Normal 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
42
polyp/modargs.h
Normal 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
259
polyp/module-alsa-sink.c
Normal 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
237
polyp/module-alsa-source.c
Normal 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
73
polyp/module-cli.c
Normal 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
404
polyp/module-oss-mmap.c
Normal 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
304
polyp/module-oss.c
Normal 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
218
polyp/module-pipe-sink.c
Normal 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);
|
||||
}
|
||||
165
polyp/module-protocol-stub.c
Normal file
165
polyp/module-protocol-stub.c
Normal 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
161
polyp/module.c
Normal 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
55
polyp/module.h
Normal 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
136
polyp/namereg.c
Normal 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
38
polyp/namereg.h
Normal 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
68
polyp/native-common.h
Normal 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
163
polyp/oss-util.c
Normal 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
32
polyp/oss-util.h
Normal 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
83
polyp/pacat-simple.c
Normal 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
336
polyp/pacat.c
Normal 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
72
polyp/packet.c
Normal 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
41
polyp/packet.h
Normal 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
166
polyp/pactl.c
Normal 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
94
polyp/parec-simple.c
Normal 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
247
polyp/pdispatch.c
Normal 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
52
polyp/pdispatch.h
Normal 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
41
polyp/polypaudio.pa
Executable 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
40
polyp/polyplib-def.h
Normal 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
53
polyp/polyplib-error.c
Normal 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
29
polyp/polyplib-error.h
Normal 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
259
polyp/polyplib-simple.c
Normal 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
49
polyp/polyplib-simple.h
Normal 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
973
polyp/polyplib.c
Normal 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
94
polyp/polyplib.h
Normal 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
85
polyp/protocol-cli.c
Normal 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
35
polyp/protocol-cli.h
Normal 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
847
polyp/protocol-esound.c
Normal 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
35
polyp/protocol-esound.h
Normal 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
863
polyp/protocol-native.c
Normal 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
35
polyp/protocol-native.h
Normal 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
444
polyp/protocol-simple.c
Normal 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
35
polyp/protocol-simple.h
Normal 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
60
polyp/pstream-util.c
Normal 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
35
polyp/pstream-util.h
Normal 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
457
polyp/pstream.c
Normal 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
51
polyp/pstream.h
Normal 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
109
polyp/queue.c
Normal 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
34
polyp/queue.h
Normal 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
180
polyp/resampler.c
Normal 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
37
polyp/resampler.h
Normal 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
144
polyp/sample-util.c
Normal 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
48
polyp/sample-util.h
Normal 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
99
polyp/sample.c
Normal 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
64
polyp/sample.h
Normal 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
34
polyp/sconv-s16be.c
Normal 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
28
polyp/sconv-s16be.h
Normal 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
82
polyp/sconv-s16le.c
Normal 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
28
polyp/sconv-s16le.h
Normal 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
137
polyp/sconv.c
Normal 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
Loading…
Add table
Add a link
Reference in a new issue