mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-31 22:25:33 -04:00
more work
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@17 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
parent
98f41f1e70
commit
78f386ad45
19 changed files with 269 additions and 201 deletions
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <stdio.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -20,6 +21,8 @@ struct client *client_new(struct core *core, const char *protocol_name, char *na
|
||||||
r = idxset_put(core->clients, c, &c->index);
|
r = idxset_put(core->clients, c, &c->index);
|
||||||
assert(c->index != IDXSET_INVALID && r >= 0);
|
assert(c->index != IDXSET_INVALID && r >= 0);
|
||||||
|
|
||||||
|
fprintf(stderr, "client: created %u \"%s\"\n", c->index, c->name);
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,6 +30,7 @@ void client_free(struct client *c) {
|
||||||
assert(c && c->core);
|
assert(c && c->core);
|
||||||
|
|
||||||
idxset_remove_by_data(c->core->clients, c, NULL);
|
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->name);
|
||||||
free(c);
|
free(c);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ struct sink* core_get_default_sink(struct core *c) {
|
||||||
if (!(sink = idxset_first(c->sinks, &c->default_sink_index)))
|
if (!(sink = idxset_first(c->sinks, &c->default_sink_index)))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
fprintf(stderr, "Default sink vanished, setting to %u\n", sink->index);
|
fprintf(stderr, "core: default sink vanished, setting to %u.\n", sink->index);
|
||||||
return sink;
|
return sink;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,6 +74,6 @@ struct source* core_get_default_source(struct core *c) {
|
||||||
if (!(source = idxset_first(c->sources, &c->default_source_index)))
|
if (!(source = idxset_first(c->sources, &c->default_source_index)))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
fprintf(stderr, "Default source vanished, setting to %u\n", source->index);
|
fprintf(stderr, "core: default source vanished, setting to %u.\n", source->index);
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
src/main.c
12
src/main.c
|
|
@ -3,6 +3,7 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <ltdl.h>
|
#include <ltdl.h>
|
||||||
|
#include <memblock.h>
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "mainloop.h"
|
#include "mainloop.h"
|
||||||
|
|
@ -10,7 +11,7 @@
|
||||||
|
|
||||||
static void signal_callback(struct mainloop_source *m, int sig, void *userdata) {
|
static void signal_callback(struct mainloop_source *m, int sig, void *userdata) {
|
||||||
mainloop_quit(mainloop_source_get_mainloop(m), -1);
|
mainloop_quit(mainloop_source_get_mainloop(m), -1);
|
||||||
fprintf(stderr, "Got signal.\n");
|
fprintf(stderr, "main: got signal.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|
@ -29,10 +30,17 @@ int main(int argc, char *argv[]) {
|
||||||
mainloop_source_new_signal(m, SIGINT, signal_callback, NULL);
|
mainloop_source_new_signal(m, SIGINT, signal_callback, NULL);
|
||||||
signal(SIGPIPE, SIG_IGN);
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
module_load(c, "module-oss", NULL);
|
module_load(c, "module-oss", "/dev/dsp1");
|
||||||
module_load(c, "module-pipe-sink", NULL);
|
module_load(c, "module-pipe-sink", NULL);
|
||||||
module_load(c, "module-simple-protocol-tcp", NULL);
|
module_load(c, "module-simple-protocol-tcp", NULL);
|
||||||
|
|
||||||
|
|
||||||
|
fprintf(stderr, "main: mainloop entry.\n");
|
||||||
|
while (mainloop_iterate(m, 1) == 0);
|
||||||
|
/* fprintf(stderr, "main: %u blocks\n", n_blocks);*/
|
||||||
|
fprintf(stderr, "main: mainloop exit.\n");
|
||||||
|
|
||||||
|
|
||||||
mainloop_run(m);
|
mainloop_run(m);
|
||||||
|
|
||||||
core_free(c);
|
core_free(c);
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,22 @@
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include "memblock.h"
|
#include "memblock.h"
|
||||||
|
|
||||||
|
unsigned n_blocks = 0;
|
||||||
|
|
||||||
struct memblock *memblock_new(size_t length) {
|
struct memblock *memblock_new(size_t length) {
|
||||||
struct memblock *b = malloc(sizeof(struct memblock)+length);
|
struct memblock *b = malloc(sizeof(struct memblock)+length);
|
||||||
b->type = MEMBLOCK_APPENDED;
|
b->type = MEMBLOCK_APPENDED;
|
||||||
b->ref = 1;
|
b->ref = 1;
|
||||||
b->length = length;
|
b->length = length;
|
||||||
b->data = b+1;
|
b->data = b+1;
|
||||||
|
n_blocks++;
|
||||||
|
timerclear(&b->stamp);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -19,6 +26,8 @@ struct memblock *memblock_new_fixed(void *d, size_t length) {
|
||||||
b->ref = 1;
|
b->ref = 1;
|
||||||
b->length = length;
|
b->length = length;
|
||||||
b->data = d;
|
b->data = d;
|
||||||
|
n_blocks++;
|
||||||
|
timerclear(&b->stamp);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,6 +37,8 @@ struct memblock *memblock_new_dynamic(void *d, size_t length) {
|
||||||
b->ref = 1;
|
b->ref = 1;
|
||||||
b->length = length;
|
b->length = length;
|
||||||
b->data = d;
|
b->data = d;
|
||||||
|
n_blocks++;
|
||||||
|
timerclear(&b->stamp);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,6 +56,7 @@ void memblock_unref(struct memblock*b) {
|
||||||
if (b->type == MEMBLOCK_DYNAMIC)
|
if (b->type == MEMBLOCK_DYNAMIC)
|
||||||
free(b->data);
|
free(b->data);
|
||||||
free(b);
|
free(b);
|
||||||
|
n_blocks--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,3 +77,29 @@ void memblock_unref_fixed(struct memblock *b) {
|
||||||
b->type = MEMBLOCK_DYNAMIC;
|
b->type = MEMBLOCK_DYNAMIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void memblock_stamp(struct memblock*b) {
|
||||||
|
assert(b);
|
||||||
|
gettimeofday(&b->stamp, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t memblock_age(struct memblock*b) {
|
||||||
|
assert(b);
|
||||||
|
struct timeval tv;
|
||||||
|
uint32_t r;
|
||||||
|
|
||||||
|
if (b->stamp.tv_sec == 0)
|
||||||
|
return (suseconds_t) -1;
|
||||||
|
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
|
||||||
|
/*fprintf(stderr, "memblock: (%lu,%lu) -- (%lu,%lu)\r", b->stamp.tv_sec, b->stamp.tv_usec, tv.tv_sec, tv.tv_usec);*/
|
||||||
|
|
||||||
|
r = (tv.tv_sec-b->stamp.tv_sec) * 1000000;
|
||||||
|
|
||||||
|
if (tv.tv_usec >= b->stamp.tv_usec)
|
||||||
|
r += tv.tv_usec - b->stamp.tv_usec;
|
||||||
|
else
|
||||||
|
r -= b->stamp.tv_usec - tv.tv_usec;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#define foomemblockhfoo
|
#define foomemblockhfoo
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
enum memblock_type { MEMBLOCK_FIXED, MEMBLOCK_APPENDED, MEMBLOCK_DYNAMIC };
|
enum memblock_type { MEMBLOCK_FIXED, MEMBLOCK_APPENDED, MEMBLOCK_DYNAMIC };
|
||||||
|
|
||||||
|
|
@ -10,6 +11,7 @@ struct memblock {
|
||||||
unsigned ref;
|
unsigned ref;
|
||||||
size_t length;
|
size_t length;
|
||||||
void *data;
|
void *data;
|
||||||
|
struct timeval stamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct memchunk {
|
struct memchunk {
|
||||||
|
|
@ -26,6 +28,11 @@ struct memblock* memblock_ref(struct memblock*b);
|
||||||
|
|
||||||
void memblock_unref_fixed(struct memblock*b);
|
void memblock_unref_fixed(struct memblock*b);
|
||||||
|
|
||||||
|
void memblock_stamp(struct memblock*b);
|
||||||
|
uint32_t memblock_age(struct memblock*b);
|
||||||
|
|
||||||
#define memblock_assert_exclusive(b) assert((b)->ref == 1)
|
#define memblock_assert_exclusive(b) assert((b)->ref == 1)
|
||||||
|
|
||||||
|
extern unsigned n_blocks;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ struct memblockq {
|
||||||
size_t prebuf;
|
size_t prebuf;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct memblockq* memblockq_new(size_t maxlength, size_t base, size_t prebuf) {
|
struct memblockq* memblockq_new(size_t maxlength, size_t base, size_t prebuf) {
|
||||||
struct memblockq* bq;
|
struct memblockq* bq;
|
||||||
assert(maxlength && base);
|
assert(maxlength && base);
|
||||||
|
|
@ -35,6 +36,7 @@ struct memblockq* memblockq_new(size_t maxlength, size_t base, size_t prebuf) {
|
||||||
bq->prebuf = bq->maxlength;
|
bq->prebuf = bq->maxlength;
|
||||||
|
|
||||||
assert(bq->maxlength >= base);
|
assert(bq->maxlength >= base);
|
||||||
|
|
||||||
return bq;
|
return bq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,4 +22,5 @@ void memblockq_empty(struct memblockq *bq);
|
||||||
int memblockq_is_readable(struct memblockq *bq);
|
int memblockq_is_readable(struct memblockq *bq);
|
||||||
int memblockq_is_writable(struct memblockq *bq, size_t length);
|
int memblockq_is_writable(struct memblockq *bq, size_t length);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ static void do_write(struct userdata *u) {
|
||||||
ssize_t r;
|
ssize_t r;
|
||||||
assert(u);
|
assert(u);
|
||||||
|
|
||||||
if (!iochannel_is_writable(u->io))
|
if (!u->sink || !iochannel_is_writable(u->io))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!u->memchunk.length) {
|
if (!u->memchunk.length) {
|
||||||
|
|
@ -66,7 +66,7 @@ static void do_read(struct userdata *u) {
|
||||||
ssize_t r;
|
ssize_t r;
|
||||||
assert(u);
|
assert(u);
|
||||||
|
|
||||||
if (!iochannel_is_readable(u->io))
|
if (!u->source || !iochannel_is_readable(u->io))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
memchunk.memblock = memblock_new(u->in_fragment_size);
|
memchunk.memblock = memblock_new(u->in_fragment_size);
|
||||||
|
|
@ -103,7 +103,7 @@ int module_init(struct core *c, struct module*m) {
|
||||||
assert(c && m);
|
assert(c && m);
|
||||||
|
|
||||||
p = m->argument ? m->argument : "/dev/dsp";
|
p = m->argument ? m->argument : "/dev/dsp";
|
||||||
if ((fd = open(p, mode = O_RDWR)) >= 0) {
|
if ((fd = open(p, (mode = O_RDWR)|O_NDELAY)) >= 0) {
|
||||||
int caps;
|
int caps;
|
||||||
|
|
||||||
ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
|
ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
|
||||||
|
|
@ -120,15 +120,17 @@ int module_init(struct core *c, struct module*m) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
if ((fd = open(p, mode = O_WRONLY)) < 0) {
|
if ((fd = open(p, (mode = O_WRONLY)|O_NDELAY)) < 0) {
|
||||||
if ((fd = open(p, mode = O_RDONLY)) < 0) {
|
if ((fd = open(p, (mode = O_RDONLY)|O_NDELAY)) < 0) {
|
||||||
fprintf(stderr, "open('%s'): %s\n", p, strerror(errno));
|
fprintf(stderr, "open('%s'): %s\n", p, strerror(errno));
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
frag_size = ((int) 0x7ffff << 4) | 10; /* nfrags = 4; frag_size = 2^10 */
|
fprintf(stderr, "module-oss: device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
|
||||||
|
|
||||||
|
frag_size = ((int) 4 << 16) | 10; /* nfrags = 4; frag_size = 2^10 */
|
||||||
if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag_size) < 0) {
|
if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag_size) < 0) {
|
||||||
fprintf(stderr, "SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno));
|
fprintf(stderr, "SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno));
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -174,12 +176,12 @@ int module_init(struct core *c, struct module*m) {
|
||||||
in_frag_size = out_frag_size = frag_size;
|
in_frag_size = out_frag_size = frag_size;
|
||||||
|
|
||||||
if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {
|
if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {
|
||||||
fprintf(stderr, "INPUT: %u fragments of size %u.\n", info.fragstotal, info.fragsize);
|
fprintf(stderr, "module-oss: input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
|
||||||
in_frag_size = info.fragsize;
|
in_frag_size = info.fragsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
|
if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
|
||||||
fprintf(stderr, "OUTUT: %u fragments of size %u.\n", info.fragstotal, info.fragsize);
|
fprintf(stderr, "module-oss: output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
|
||||||
out_frag_size = info.fragsize;
|
out_frag_size = info.fragsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -240,8 +242,10 @@ void module_done(struct core *c, struct module*m) {
|
||||||
if (u->silence.memblock)
|
if (u->silence.memblock)
|
||||||
memblock_unref(u->silence.memblock);
|
memblock_unref(u->silence.memblock);
|
||||||
|
|
||||||
sink_free(u->sink);
|
if (u->sink)
|
||||||
source_free(u->source);
|
sink_free(u->sink);
|
||||||
|
if (u->source)
|
||||||
|
source_free(u->source);
|
||||||
iochannel_free(u->io);
|
iochannel_free(u->io);
|
||||||
free(u);
|
free(u);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,9 @@ struct module* module_load(struct core *c, const char *name, const char *argumen
|
||||||
assert(c->modules);
|
assert(c->modules);
|
||||||
r = idxset_put(c->modules, m, &m->index);
|
r = idxset_put(c->modules, m, &m->index);
|
||||||
assert(r >= 0 && m->index != IDXSET_INVALID);
|
assert(r >= 0 && m->index != IDXSET_INVALID);
|
||||||
|
|
||||||
|
fprintf(stderr, "module: loaded %u \"%s\" with argument \"%s\".\n", m->index, m->name, m->argument);
|
||||||
|
|
||||||
return m;
|
return m;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
|
@ -61,6 +64,9 @@ static void module_free(struct module *m) {
|
||||||
m->done(m->core, m);
|
m->done(m->core, m);
|
||||||
|
|
||||||
lt_dlclose(m->dl);
|
lt_dlclose(m->dl);
|
||||||
|
|
||||||
|
fprintf(stderr, "module: unloaded %u \"%s\".\n", m->index, m->name);
|
||||||
|
|
||||||
free(m->name);
|
free(m->name);
|
||||||
free(m->argument);
|
free(m->argument);
|
||||||
free(m);
|
free(m);
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,8 @@ static int do_read(struct connection *c) {
|
||||||
chunk.memblock = memblock_new(BUFSIZE);
|
chunk.memblock = memblock_new(BUFSIZE);
|
||||||
assert(chunk.memblock);
|
assert(chunk.memblock);
|
||||||
|
|
||||||
|
memblock_stamp(chunk.memblock);
|
||||||
|
|
||||||
if ((r = iochannel_read(c->io, chunk.memblock->data, BUFSIZE)) <= 0) {
|
if ((r = iochannel_read(c->io, chunk.memblock->data, BUFSIZE)) <= 0) {
|
||||||
fprintf(stderr, "read(): %s\n", r == 0 ? "EOF" : strerror(errno));
|
fprintf(stderr, "read(): %s\n", r == 0 ? "EOF" : strerror(errno));
|
||||||
memblock_unref(chunk.memblock);
|
memblock_unref(chunk.memblock);
|
||||||
|
|
|
||||||
53
src/sample.c
53
src/sample.c
|
|
@ -33,24 +33,6 @@ struct memblock *silence(struct memblock* b, struct sample_spec *spec) {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_clip(struct memchunk *target, struct memchunk *chunk, struct sample_spec *spec) {
|
|
||||||
int16_t *p, *d;
|
|
||||||
size_t i;
|
|
||||||
assert(target && target->memblock && chunk && chunk->memblock && spec);
|
|
||||||
assert(spec->format == SAMPLE_S16NE);
|
|
||||||
assert((target->length & 1) == 0);
|
|
||||||
|
|
||||||
d = target->memblock->data + target->index;
|
|
||||||
p = chunk->memblock->data + chunk->index;
|
|
||||||
|
|
||||||
for (i = 0; i < target->length && i < chunk->length; i++) {
|
|
||||||
int32_t r = (int32_t) *d + (int32_t) *p;
|
|
||||||
if (r < -0x8000) r = 0x8000;
|
|
||||||
if (r > 0x7FFF) r = 0x7FFF;
|
|
||||||
*d = (int16_t) r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sample_size(struct sample_spec *spec) {
|
size_t sample_size(struct sample_spec *spec) {
|
||||||
assert(spec);
|
assert(spec);
|
||||||
size_t b = 1;
|
size_t b = 1;
|
||||||
|
|
@ -78,3 +60,38 @@ size_t bytes_per_second(struct sample_spec *spec) {
|
||||||
return spec->rate*sample_size(spec);
|
return spec->rate*sample_size(spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t mix_chunks(struct mix_info channels[], unsigned nchannels, void *data, size_t length, struct sample_spec *spec) {
|
||||||
|
unsigned c, d;
|
||||||
|
assert(chunks && target && spec);
|
||||||
|
assert(spec->format == 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;
|
||||||
|
uint8_t volume = channels[c].volume;
|
||||||
|
|
||||||
|
if (d >= channels[c].chunk.length)
|
||||||
|
return d;
|
||||||
|
|
||||||
|
if (volume == 0)
|
||||||
|
v = 0;
|
||||||
|
else {
|
||||||
|
v = *((int16_t*) (channels[c].chunk->memblock->data + channels[c].chunk->index + d));
|
||||||
|
|
||||||
|
if (volume != 0xFF)
|
||||||
|
v = v*volume/0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum += v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sum < -0x8000) sum = -0x8000;
|
||||||
|
if (sum > 0x7FFF) sum = 0x7FFF;
|
||||||
|
*(data++) = sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
10
src/sample.h
10
src/sample.h
|
|
@ -27,7 +27,15 @@ struct sample_spec {
|
||||||
extern struct sample_spec default_sample_spec;
|
extern struct sample_spec default_sample_spec;
|
||||||
|
|
||||||
struct memblock *silence(struct memblock* b, struct sample_spec *spec);
|
struct memblock *silence(struct memblock* b, struct sample_spec *spec);
|
||||||
void add_clip(struct memchunk *target, struct memchunk *chunk, struct sample_spec *spec);
|
|
||||||
|
|
||||||
|
struct mix_info {
|
||||||
|
struct memchunk chunk;
|
||||||
|
uint8_t volume;
|
||||||
|
void *userdata;
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t mix_chunks(struct mix_info channels[], unsigned nchannels, void *data, size_t length, struct sample_spec *spec) {
|
||||||
|
|
||||||
size_t bytes_per_second(struct sample_spec *spec);
|
size_t bytes_per_second(struct sample_spec *spec);
|
||||||
size_t sample_size(struct sample_spec *spec);
|
size_t sample_size(struct sample_spec *spec);
|
||||||
|
|
|
||||||
285
src/sink.c
285
src/sink.c
|
|
@ -21,7 +21,7 @@ struct sink* sink_new(struct core *core, const char *name, const struct sample_s
|
||||||
|
|
||||||
s->core = core;
|
s->core = core;
|
||||||
s->sample_spec = *spec;
|
s->sample_spec = *spec;
|
||||||
s->input_streams = idxset_new(NULL, NULL);
|
s->inputs = idxset_new(NULL, NULL);
|
||||||
|
|
||||||
if (name) {
|
if (name) {
|
||||||
n = malloc(strlen(name)+9);
|
n = malloc(strlen(name)+9);
|
||||||
|
|
@ -36,186 +36,32 @@ struct sink* sink_new(struct core *core, const char *name, const struct sample_s
|
||||||
s->notify = NULL;
|
s->notify = NULL;
|
||||||
s->notify_userdata = NULL;
|
s->notify_userdata = NULL;
|
||||||
|
|
||||||
|
fprintf(stderr, "sink: created %u \"%s\".\n", s->index, s->name);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sink_free(struct sink *s) {
|
void sink_free(struct sink *s) {
|
||||||
struct input_stream *i, *j = NULL;
|
struct sink_input *i, *j = NULL;
|
||||||
assert(s);
|
assert(s);
|
||||||
|
|
||||||
while ((i = idxset_first(s->input_streams, NULL))) {
|
while ((i = idxset_first(s->inputs, NULL))) {
|
||||||
assert(i != j);
|
assert(i != j && i->kill);
|
||||||
input_stream_kill(i);
|
i->kill(i);
|
||||||
j = i;
|
j = i;
|
||||||
}
|
}
|
||||||
idxset_free(s->input_streams, NULL, NULL);
|
|
||||||
|
idxset_free(s->inputs, NULL, NULL);
|
||||||
|
|
||||||
idxset_remove_by_data(s->core->sinks, s, NULL);
|
idxset_remove_by_data(s->core->sinks, s, NULL);
|
||||||
source_free(s->monitor_source);
|
source_free(s->monitor_source);
|
||||||
|
|
||||||
|
fprintf(stderr, "sink: freed %u \"%s\"\n", s->index, s->name);
|
||||||
|
|
||||||
free(s->name);
|
free(s->name);
|
||||||
free(s);
|
free(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct pass1_info {
|
|
||||||
size_t maxlength;
|
|
||||||
unsigned count;
|
|
||||||
struct input_stream *last_input_stream;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int get_max_length(void *p, uint32_t index, int *del, void*userdata) {
|
|
||||||
struct memchunk chunk;
|
|
||||||
struct pass1_info *info = userdata;
|
|
||||||
struct input_stream*i = p;
|
|
||||||
assert(info && i);
|
|
||||||
|
|
||||||
if (memblockq_peek(i->memblockq, &chunk) != 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
assert(chunk.length);
|
|
||||||
|
|
||||||
if (info->maxlength > chunk.length)
|
|
||||||
info->maxlength = chunk.length;
|
|
||||||
|
|
||||||
info->count++;
|
|
||||||
info->last_input_stream = i;
|
|
||||||
|
|
||||||
memblock_unref(chunk.memblock);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct pass2_info {
|
|
||||||
struct memchunk *chunk;
|
|
||||||
struct sample_spec *spec;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int do_mix(void *p, uint32_t index, int *del, void*userdata) {
|
|
||||||
struct memchunk chunk;
|
|
||||||
struct pass2_info *info = userdata;
|
|
||||||
struct input_stream*i = p;
|
|
||||||
assert(info && info->chunk && info->chunk->memblock && i && info->spec);
|
|
||||||
|
|
||||||
if (memblockq_peek(i->memblockq, &chunk) != 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
memblock_assert_exclusive(info->chunk->memblock);
|
|
||||||
assert(chunk.length && chunk.length <= info->chunk->memblock->length - info->chunk->index);
|
|
||||||
|
|
||||||
add_clip(info->chunk, &chunk, info->spec);
|
|
||||||
memblock_unref(chunk.memblock);
|
|
||||||
memblockq_drop(i->memblockq, info->chunk->length);
|
|
||||||
|
|
||||||
input_stream_notify(i);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sink_render_into(struct sink*s, struct memblock *target, struct memchunk *result) {
|
|
||||||
struct pass1_info pass1_info;
|
|
||||||
struct pass2_info pass2_info;
|
|
||||||
assert(s && target && result);
|
|
||||||
memblock_assert_exclusive(target);
|
|
||||||
|
|
||||||
/* Calculate how many bytes to mix */
|
|
||||||
pass1_info.maxlength = target->length;
|
|
||||||
pass1_info.count = 0;
|
|
||||||
|
|
||||||
idxset_foreach(s->input_streams, get_max_length, &pass1_info);
|
|
||||||
assert(pass1_info.maxlength);
|
|
||||||
|
|
||||||
/* No data to mix */
|
|
||||||
if (pass1_info.count == 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* A shortcut if only a single input stream is connected */
|
|
||||||
if (pass1_info.count == 1) {
|
|
||||||
struct input_stream *i = pass1_info.last_input_stream;
|
|
||||||
struct memchunk chunk;
|
|
||||||
size_t l;
|
|
||||||
|
|
||||||
assert(i);
|
|
||||||
|
|
||||||
if (memblockq_peek(i->memblockq, &chunk) != 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
l = target->length < chunk.length ? target->length : chunk.length;
|
|
||||||
memcpy(target->data, result->memblock+result->index, l);
|
|
||||||
target->length = l;
|
|
||||||
memblock_unref(chunk.memblock);
|
|
||||||
memblockq_drop(i->memblockq, l);
|
|
||||||
|
|
||||||
input_stream_notify(i);
|
|
||||||
|
|
||||||
result->memblock = target;
|
|
||||||
result->length = l;
|
|
||||||
result->index = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do the real mixing */
|
|
||||||
result->memblock = silence(target, &s->sample_spec);
|
|
||||||
result->index = 0;
|
|
||||||
result->length = pass1_info.maxlength;
|
|
||||||
pass2_info.chunk = result;
|
|
||||||
pass2_info.spec = &s->sample_spec;
|
|
||||||
idxset_foreach(s->input_streams, do_mix, &pass2_info);
|
|
||||||
|
|
||||||
assert(s->monitor_source);
|
|
||||||
source_post(s->monitor_source, result);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sink_render(struct sink*s, size_t length, struct memchunk *result) {
|
|
||||||
struct pass1_info pass1_info;
|
|
||||||
struct pass2_info pass2_info;
|
|
||||||
assert(s && result);
|
|
||||||
|
|
||||||
if (!length)
|
|
||||||
length = (size_t) -1;
|
|
||||||
|
|
||||||
/* Calculate how many bytes to mix */
|
|
||||||
pass1_info.maxlength = length;
|
|
||||||
pass1_info.count = 0;
|
|
||||||
|
|
||||||
idxset_foreach(s->input_streams, get_max_length, &pass1_info);
|
|
||||||
assert(pass1_info.maxlength);
|
|
||||||
|
|
||||||
/* No data to mix */
|
|
||||||
if (pass1_info.count == 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (pass1_info.count == 1) {
|
|
||||||
struct input_stream *i = pass1_info.last_input_stream;
|
|
||||||
size_t l;
|
|
||||||
|
|
||||||
assert(i);
|
|
||||||
|
|
||||||
if (memblockq_peek(i->memblockq, result) != 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
l = length < result->length ? length : result->length;
|
|
||||||
result->length = l;
|
|
||||||
memblockq_drop(i->memblockq, l);
|
|
||||||
input_stream_notify(i);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do the mixing */
|
|
||||||
result->memblock = silence(memblock_new(result->length), &s->sample_spec);
|
|
||||||
result->index = 0;
|
|
||||||
result->length = pass1_info.maxlength;
|
|
||||||
pass2_info.chunk = result;
|
|
||||||
pass2_info.spec = &s->sample_spec;
|
|
||||||
idxset_foreach(s->input_streams, do_mix, &pass2_info);
|
|
||||||
|
|
||||||
assert(s->monitor_source);
|
|
||||||
|
|
||||||
source_post(s->monitor_source, result);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sink_notify(struct sink*s) {
|
void sink_notify(struct sink*s) {
|
||||||
assert(s);
|
assert(s);
|
||||||
|
|
||||||
|
|
@ -230,4 +76,113 @@ void sink_set_notify_callback(struct sink *s, void (*notify_callback)(struct sin
|
||||||
s->notify_userdata = userdata;
|
s->notify_userdata = userdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned fill_mix_info(struct sink *s, struct mix_info *info, unsigned maxinfo) {
|
||||||
|
uint32_t index = IDXSET_ANY;
|
||||||
|
struct sink_input *i;
|
||||||
|
unsigned n;
|
||||||
|
|
||||||
|
assert(s && info);
|
||||||
|
|
||||||
|
while (maxinfo > 0 && i = idxset_rrobin(s->inputs, &index)) {
|
||||||
|
assert(i->peek);
|
||||||
|
if (i->peek(i, &info->chunk, &info->volume) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
assert(info->chunk.memblock && info->chunk.memblock->data && info->chunk.length);
|
||||||
|
info->userdata = i;
|
||||||
|
|
||||||
|
info++;
|
||||||
|
maxinfo--;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void inputs_drop(struct sink *s, struct mix_info *info, unsigned maxinfo, size_t length) {
|
||||||
|
assert(s && info);
|
||||||
|
|
||||||
|
for (; maxinfo > 0; maxinfo--, info++) {
|
||||||
|
struct sink_input *i = info->userdata;
|
||||||
|
assert(i && info->chunk.memblock);
|
||||||
|
|
||||||
|
memblock_unref(info->chunk.memblock);
|
||||||
|
assert(i->drop);
|
||||||
|
i->drop(i, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int sink_render(struct sink*s, size_t length, struct memchunk *result) {
|
||||||
|
struct mix_info info[MAX_MIX_CHANNELS];
|
||||||
|
unsigned n;
|
||||||
|
size_t l;
|
||||||
|
assert(s && length && result);
|
||||||
|
|
||||||
|
n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
|
||||||
|
|
||||||
|
if (n <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (n == 1) {
|
||||||
|
struct sink_info *i = info[0].userdata;
|
||||||
|
assert(i && b);
|
||||||
|
*result = info[0].chunk;
|
||||||
|
memblock_ref(result->memblock);
|
||||||
|
|
||||||
|
if (result->length > length)
|
||||||
|
result->length = length;
|
||||||
|
|
||||||
|
l = result->length;
|
||||||
|
} else {
|
||||||
|
result->memblock = memblock_new(length);
|
||||||
|
assert(result->memblock);
|
||||||
|
|
||||||
|
result->length = l = mix_chunks(info, n, result->memblock->data, length, &s->sample_spec);
|
||||||
|
result->index = 0;
|
||||||
|
|
||||||
|
assert(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs_drop(s, info, n, l);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sink_render_into(struct sink*s, struct memblock *target, struct memchunk *result) {
|
||||||
|
struct mix_info info[MAX_MIX_CHANNELS];
|
||||||
|
unsigned n;
|
||||||
|
size_t l;
|
||||||
|
assert(s && target && target->length && target->data && result);
|
||||||
|
|
||||||
|
n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
|
||||||
|
|
||||||
|
if (n <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (n == 1) {
|
||||||
|
struct sink_info *i = info[0].userdata;
|
||||||
|
assert(i && b);
|
||||||
|
|
||||||
|
l = target->length;
|
||||||
|
if (l > info[0].chunk.length)
|
||||||
|
l = info[0].chunk.length;
|
||||||
|
|
||||||
|
result->memblock = target;
|
||||||
|
memcpy(target->data, info[0].chunk.memblock->data + info[0].chunk.index, l);
|
||||||
|
result->length = target->length = l;
|
||||||
|
result->index = 0;
|
||||||
|
|
||||||
|
if (result->length > length)
|
||||||
|
result->length = length;
|
||||||
|
|
||||||
|
l = result->length;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
result->memblock = target;
|
||||||
|
result->length = l = mix_chunks(info, n, target->data, target->length, &s->sample_spec);
|
||||||
|
result->index = 0;
|
||||||
|
assert(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs_drop(s, info, n, l);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
13
src/sink.h
13
src/sink.h
|
|
@ -10,13 +10,24 @@ struct sink;
|
||||||
#include "idxset.h"
|
#include "idxset.h"
|
||||||
#include "source.h"
|
#include "source.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct sink_input {
|
||||||
|
int (*peek) (struct sink_input *i, struct memchunk *chunk, uint8_t *volume);
|
||||||
|
void (*drop) (struct sink_input *i, size_t length);
|
||||||
|
void (*kill) (struct sink_input *i);
|
||||||
|
|
||||||
|
void *userdata;
|
||||||
|
int index;
|
||||||
|
struct sink *sink;
|
||||||
|
};
|
||||||
|
|
||||||
struct sink {
|
struct sink {
|
||||||
char *name;
|
char *name;
|
||||||
uint32_t index;
|
uint32_t index;
|
||||||
|
|
||||||
struct core *core;
|
struct core *core;
|
||||||
struct sample_spec sample_spec;
|
struct sample_spec sample_spec;
|
||||||
struct idxset *input_streams;
|
struct idxset *inputs;
|
||||||
|
|
||||||
struct source *monitor_source;
|
struct source *monitor_source;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <stdio.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -24,6 +25,8 @@ struct source* source_new(struct core *core, const char *name, const struct samp
|
||||||
s->link_change_callback = NULL;
|
s->link_change_callback = NULL;
|
||||||
s->userdata = NULL;
|
s->userdata = NULL;
|
||||||
|
|
||||||
|
fprintf(stderr, "source: created %u \"%s\"\n", s->index, s->name);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,6 +43,8 @@ void source_free(struct source *s) {
|
||||||
|
|
||||||
idxset_remove_by_data(s->core->sources, s, NULL);
|
idxset_remove_by_data(s->core->sources, s, NULL);
|
||||||
|
|
||||||
|
fprintf(stderr, "source: freed %u \"%s\"\n", s->index, s->name);
|
||||||
|
|
||||||
free(s->name);
|
free(s->name);
|
||||||
free(s);
|
free(s);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue