diff options
Diffstat (limited to 'polyp')
| -rw-r--r-- | polyp/Makefile.am | 3 | ||||
| -rw-r--r-- | polyp/cli-command.c | 59 | ||||
| -rw-r--r-- | polyp/clitext.c | 35 | ||||
| -rw-r--r-- | polyp/clitext.h | 1 | ||||
| -rw-r--r-- | polyp/core.c | 4 | ||||
| -rw-r--r-- | polyp/core.h | 4 | ||||
| -rw-r--r-- | polyp/debug.h | 8 | ||||
| -rw-r--r-- | polyp/hashmap.c | 14 | ||||
| -rw-r--r-- | polyp/hashmap.h | 5 | ||||
| -rw-r--r-- | polyp/main.c | 2 | ||||
| -rw-r--r-- | polyp/modargs.c | 4 | ||||
| -rw-r--r-- | polyp/module-alsa-sink.c | 2 | ||||
| -rw-r--r-- | polyp/protocol-esound.c | 194 | ||||
| -rw-r--r-- | polyp/scache.c | 181 | ||||
| -rw-r--r-- | polyp/scache.h | 24 | 
15 files changed, 520 insertions, 20 deletions
| diff --git a/polyp/Makefile.am b/polyp/Makefile.am index 94edf616..d1615585 100644 --- a/polyp/Makefile.am +++ b/polyp/Makefile.am @@ -105,7 +105,8 @@ polypaudio_SOURCES = idxset.c idxset.h \  		cli-command.c cli-command.h \  		clitext.c clitext.h \  		tokenizer.c tokenizer.h \ -		dynarray.c dynarray.h +		dynarray.c dynarray.h \ +		scache.c scache.h  polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS)  polypaudio_INCLUDES = $(INCLTDL) diff --git a/polyp/cli-command.c b/polyp/cli-command.c index f3a2f8a0..03bd125b 100644 --- a/polyp/cli-command.c +++ b/polyp/cli-command.c @@ -40,6 +40,8 @@  #include "strbuf.h"  #include "namereg.h"  #include "clitext.h" +#include "scache.h" +#include "sample-util.h"  struct command {      const char *name; @@ -67,6 +69,9 @@ static int pa_cli_command_source_default(struct pa_core *c, struct pa_tokenizer  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 int pa_cli_command_scache_play(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_scache_remove(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_scache_list(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 }, @@ -90,6 +95,9 @@ static const struct command commands[] = {      { "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}, +    { "scache_list",             pa_cli_command_scache_list,        "List all entries in the sample cache", 2}, +    { "scache_play",             pa_cli_command_scache_play,        "Play a sample from the sample cache (args: name, sink|index)", 3}, +    { "scache_remove",           pa_cli_command_scache_remove,      "Remove a sample from the sample cache (args: name)", 2},      { NULL, NULL, NULL, 0 }  }; @@ -199,6 +207,7 @@ static int pa_cli_command_info(struct pa_core *c, struct pa_tokenizer *t, struct      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); +    pa_cli_command_scache_list(c, t, buf, fail, verbose);      return 0;  } @@ -429,6 +438,56 @@ static int pa_cli_command_kill_source_output(struct pa_core *c, struct pa_tokeni      return 0;  } +static int pa_cli_command_scache_list(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { +    char *s; +    assert(c && t); +    s = pa_scache_list_to_string(c); +    assert(s); +    pa_strbuf_puts(buf, s); +    free(s); +    return 0; +} + +static int pa_cli_command_scache_play(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { +    const char *n, *sink_name; +    struct pa_sink *sink; +    assert(c && t && buf && fail && verbose); + +    if (!(n = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) { +        pa_strbuf_puts(buf, "You need to specify a sample name and a sink name.\n"); +        return -1; +    } + +    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK))) { +        pa_strbuf_puts(buf, "No sink by that name.\n"); +        return -1; +    } + +    if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM) < 0) { +        pa_strbuf_puts(buf, "Failed to play sample.\n"); +        return -1; +    } + +    return 0; +} + +static int pa_cli_command_scache_remove(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { +    const char *n; +    assert(c && t && buf && fail && verbose); + +    if (!(n = pa_tokenizer_get(t, 1))) { +        pa_strbuf_puts(buf, "You need to specify a sample name.\n"); +        return -1; +    } + +    if (pa_scache_remove_item(c, n) < 0) { +        pa_strbuf_puts(buf, "Failed to remove sample.\n"); +        return -1; +    } + +    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; diff --git a/polyp/clitext.c b/polyp/clitext.c index c1b9953b..d0c3f9a7 100644 --- a/polyp/clitext.c +++ b/polyp/clitext.c @@ -24,6 +24,7 @@  #endif  #include <assert.h> +#include <string.h>  #include "clitext.h"  #include "module.h" @@ -34,6 +35,7 @@  #include "source-output.h"  #include "strbuf.h"  #include "sample-util.h" +#include "scache.h"  char *pa_module_list_to_string(struct pa_core *c) {      struct pa_strbuf *s; @@ -201,3 +203,36 @@ char *pa_sink_input_list_to_string(struct pa_core *c) {      return pa_strbuf_tostring_free(s);  } + +char *pa_scache_list_to_string(struct pa_core *c) { +    struct pa_scache_entry *e; +    void *state = NULL; +    struct pa_strbuf *s; +    assert(c); + +    s = pa_strbuf_new(); +    assert(s); + +    pa_strbuf_printf(s, "%u cache entries available.\n", c->scache_hashmap ? pa_hashmap_ncontents(c->scache_hashmap) : 0); + +    if (c->scache_hashmap) { + +        while ((e = pa_hashmap_iterate(c->scache_hashmap, &state))) { +            double l; +            char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH]; +            pa_sample_snprint(ss, sizeof(ss), &e->sample_spec); +             +            l = (double) e->memchunk.length / pa_bytes_per_second(&e->sample_spec); +             +            pa_strbuf_printf( +                s, "    name: <%s>\n\tindex: <%i>\n\tsample_spec: <%s>\n\tlength: <%u>\n\tduration: <%0.1fs>\n", +                e->name, +                e->index, +                ss, +                e->memchunk.length, +                l); +        } +    } + +    return pa_strbuf_tostring_free(s); +} diff --git a/polyp/clitext.h b/polyp/clitext.h index b1718cb5..4e5252fe 100644 --- a/polyp/clitext.h +++ b/polyp/clitext.h @@ -30,6 +30,7 @@ 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); +char *pa_scache_list_to_string(struct pa_core *c);  #endif diff --git a/polyp/core.c b/polyp/core.c index dc9525a8..1c69f914 100644 --- a/polyp/core.c +++ b/polyp/core.c @@ -33,6 +33,7 @@  #include "source.h"  #include "namereg.h"  #include "util.h" +#include "scache.h"  struct pa_core* pa_core_new(struct pa_mainloop_api *m) {      struct pa_core* c; @@ -50,6 +51,8 @@ struct pa_core* pa_core_new(struct pa_mainloop_api *m) {      c->modules = NULL;      c->namereg = NULL; +    c->scache_idxset = NULL; +    c->scache_hashmap = NULL;      c->default_sample_spec.format = PA_SAMPLE_S16NE;      c->default_sample_spec.rate = 44100; @@ -82,6 +85,7 @@ void pa_core_free(struct pa_core *c) {      pa_idxset_free(c->sink_inputs, NULL, NULL);      pa_namereg_free(c); +    pa_scache_free(c);      free(c);      }; diff --git a/polyp/core.h b/polyp/core.h index 99d7d76a..03b8671a 100644 --- a/polyp/core.h +++ b/polyp/core.h @@ -30,9 +30,9 @@  struct pa_core {      struct pa_mainloop_api *mainloop; -    struct pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules; +    struct pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache_idxset; -    struct pa_hashmap *namereg; +    struct pa_hashmap *namereg, *scache_hashmap;      uint32_t default_source_index, default_sink_index; diff --git a/polyp/debug.h b/polyp/debug.h new file mode 100644 index 00000000..fb2b889e --- /dev/null +++ b/polyp/debug.h @@ -0,0 +1,8 @@ +#ifndef foodebughfoo +#define foodebughfoo + +/* A nice trick for debuggers, working on x86 only */ + +#define DEBUG_TRAP __asm__("int $3") + +#endif diff --git a/polyp/hashmap.c b/polyp/hashmap.c index 2c7c92b5..51e3879b 100644 --- a/polyp/hashmap.c +++ b/polyp/hashmap.c @@ -168,3 +168,17 @@ int pa_hashmap_remove(struct pa_hashmap *h, const void *key) {  unsigned pa_hashmap_ncontents(struct pa_hashmap *h) {      return h->n_entries;  } + +void *pa_hashmap_iterate(struct pa_hashmap *h, void **state) { +    assert(h && state); + +    if (!*state) { +        *state = h->first_entry; +    } else +        *state = ((struct hashmap_entry*) *state)->next; + +    if (!*state) +        return NULL; +     +    return ((struct hashmap_entry*) *state)->value; +} diff --git a/polyp/hashmap.h b/polyp/hashmap.h index b24e74a5..3b79d7ae 100644 --- a/polyp/hashmap.h +++ b/polyp/hashmap.h @@ -34,4 +34,9 @@ int pa_hashmap_remove(struct pa_hashmap *h, const void *key);  unsigned pa_hashmap_ncontents(struct pa_hashmap *h); +/* Maybe used to iterate through the hashmap. Initial state should +   point to a NULL pointer. The hashmap may not be modified during +   iteration */ +void *pa_hashmap_iterate(struct pa_hashmap *h, void **state); +  #endif diff --git a/polyp/main.c b/polyp/main.c index bcc1fada..0c6104a4 100644 --- a/polyp/main.c +++ b/polyp/main.c @@ -126,7 +126,7 @@ int main(int argc, char *argv[]) {      r = lt_dlinit();      assert(r == 0);  #ifdef DLSEARCHDIR -    lt_dladdsearchdir(DLSEARCHDIR); +/*    lt_dladdsearchdir(DLSEARCHDIR);*/  #endif      mainloop = pa_mainloop_new(); diff --git a/polyp/modargs.c b/polyp/modargs.c index ea104761..3841a9ec 100644 --- a/polyp/modargs.c +++ b/polyp/modargs.c @@ -36,6 +36,8 @@  #include "sink.h"  #include "source.h" +#include "debug.h" +  struct pa_modargs;  struct entry { @@ -213,6 +215,8 @@ int pa_modargs_get_sample_spec(struct pa_modargs *ma, struct pa_sample_spec *rss      struct pa_sample_spec ss;      assert(ma && rss); +/*    DEBUG_TRAP;*/ +          ss = *rss;      if ((pa_modargs_get_value_u32(ma, "rate", &ss.rate)) < 0)          return -1; diff --git a/polyp/module-alsa-sink.c b/polyp/module-alsa-sink.c index 8a3388af..c250d1cf 100644 --- a/polyp/module-alsa-sink.c +++ b/polyp/module-alsa-sink.c @@ -154,7 +154,7 @@ int pa_module_init(struct pa_core *c, struct pa_module*m) {      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; diff --git a/polyp/protocol-esound.c b/polyp/protocol-esound.c index 8a7c4bcb..91e6b7d6 100644 --- a/polyp/protocol-esound.c +++ b/polyp/protocol-esound.c @@ -39,8 +39,10 @@  #include "source-output.h"  #include "source.h"  #include "sample.h" - +#include "scache.h" +#include "sample-util.h"  #include "authkey.h" +#include "debug.h"  #define DEFAULT_COOKIE_FILE ".esd_auth" @@ -49,6 +51,10 @@  #define RECORD_BUFFER_SECONDS (5)  #define RECORD_BUFFER_FRAGMENTS (100) +#define MAX_CACHE_SAMPLE_SIZE (1024000) + +#define SCACHE_PREFIX "esound~" +  /* This is heavily based on esound's code */  struct connection { @@ -71,6 +77,11 @@ struct connection {          struct pa_memblock *current_memblock;          size_t memblock_index, fragment_size;      } playback; + +     +    struct pa_memchunk scache_memchunk; +    char *scache_name; +    struct pa_sample_spec scache_sample_spec;  };  struct pa_protocol_esound { @@ -105,6 +116,9 @@ static int esd_proto_get_latency(struct connection *c, esd_proto_t request, cons  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); +static int esd_proto_sample_cache(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_sample_get_id(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] = { @@ -116,9 +130,9 @@ static struct proto_handler proto_map[ESD_PROTO_MAX] = {      { 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" }, +    { ESD_NAME_MAX + 3 * sizeof(int), esd_proto_sample_cache, "sample cache" }, +    { sizeof(int),                    esd_proto_sample_free_or_play, "sample free" }, +    { sizeof(int),                    esd_proto_sample_free_or_play, "sample play" },      { sizeof(int),                    NULL, "sample loop" },      { sizeof(int),                    NULL, "sample stop" },      { -1,                             NULL, "TODO: sample kill" }, @@ -126,7 +140,7 @@ static struct proto_handler proto_map[ESD_PROTO_MAX] = {      { ESD_KEY_LEN + sizeof(int),      NULL, "standby" },      { ESD_KEY_LEN + sizeof(int),      NULL, "resume" }, -    { ESD_NAME_MAX,                   NULL, "sample getid" }, +    { ESD_NAME_MAX,                   esd_proto_sample_get_id, "sample getid" },      { ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream filter" },      { sizeof(int),                    esd_proto_server_info, "server info" }, @@ -170,6 +184,10 @@ static void connection_free(struct connection *c) {      if (c->fixed_source)          c->protocol->core->mainloop->cancel_fixed(c->protocol->core->mainloop, c->fixed_source); + +    if (c->scache_memchunk.memblock) +        pa_memblock_unref(c->scache_memchunk.memblock); +    free(c->scache_name);      free(c);  } @@ -216,6 +234,22 @@ static void* connection_write(struct connection *c, size_t length) {      return c->write_data+i;  } +static void format_esd2native(int format, struct pa_sample_spec *ss) { +    assert(ss); + +    ss->channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1; +    ss->format = ((format & ESD_MASK_BITS) == ESD_BITS16) ? PA_SAMPLE_S16NE : PA_SAMPLE_U8; +} + +static int format_native2esd(struct pa_sample_spec *ss) { +    int format = 0; +     +    format = (ss->format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16; +    format |= (ss->channels >= 2) ? ESD_STEREO : ESD_MONO; + +    return format; +} +  /*** esound commands ***/  static int esd_proto_connect(struct connection *c, esd_proto_t request, const void *data, size_t length) { @@ -260,8 +294,7 @@ static int esd_proto_stream_play(struct connection *c, esd_proto_t request, cons      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; +    format_esd2native(format, &ss);      if (!pa_sample_spec_valid(&ss))          return -1; @@ -313,8 +346,7 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co      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; +    format_esd2native(format, &ss);      if (!pa_sample_spec_valid(&ss))          return -1; @@ -390,8 +422,7 @@ static int esd_proto_server_info(struct connection *c, esd_proto_t request, cons      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; +        format = format_native2esd(&sink->sample_spec);      }      response = connection_write(c, sizeof(int)*3); @@ -428,8 +459,7 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v          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; +            format = format_native2esd(&conn->sink_input->sample_spec);          }          /* id */ @@ -488,6 +518,103 @@ static int esd_proto_stream_pan(struct connection *c, esd_proto_t request, const      return 0;  } +static int esd_proto_sample_cache(struct connection *c, esd_proto_t request, const void *data, size_t length) { +    struct pa_sample_spec ss; +    int format, rate; +    size_t sc_length; +    uint32_t index; +    int *ok; +    char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1]; +    assert(c && data && length == (ESD_NAME_MAX+3*sizeof(int))); + +    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; +    format_esd2native(format, &ss); + +    sc_length = (size_t) maybe_swap_endian_32(c->swap_byte_order, (*((int*)data + 2))); + +    if (sc_length >= MAX_CACHE_SAMPLE_SIZE) +        return -1; + +    strcpy(name, SCACHE_PREFIX); +    strncpy(name+sizeof(SCACHE_PREFIX)-1, data+3*sizeof(int), ESD_NAME_MAX); +    name[sizeof(name)-1] = 0; +     +    assert(!c->scache_memchunk.memblock); +    c->scache_memchunk.memblock = pa_memblock_new(sc_length); +    c->scache_memchunk.index = 0; +    c->scache_memchunk.length = sc_length; +    c->scache_sample_spec = ss; +    assert(!c->scache_name); +    c->scache_name = strdup(name); +    assert(c->scache_name); + +    c->state = ESD_CACHING_SAMPLE; + +    pa_scache_add_item(c->protocol->core, c->scache_name, NULL, NULL, &index); + +    ok = connection_write(c, sizeof(int)); +    assert(ok); +     +    *ok = index+1; +     +    return 0; +} + +static int esd_proto_sample_get_id(struct connection *c, esd_proto_t request, const void *data, size_t length) { +    int *ok; +    uint32_t index; +    char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1]; +    assert(c && data && length == ESD_NAME_MAX); + +    ok = connection_write(c, sizeof(int)); +    assert(ok); + +    *ok = -1; + +    strcpy(name, SCACHE_PREFIX); +    strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX); +    name[sizeof(name)-1] = 0; + +    if ((index = pa_scache_get_id_by_name(c->protocol->core, name)) != PA_IDXSET_INVALID) +        *ok = (int) index +1; + +    return 0; +} + +static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length) { +    int *ok; +    const char *name; +    uint32_t index; +    assert(c && data && length == sizeof(int)); + +    index = (uint32_t) maybe_swap_endian_32(c->swap_byte_order, *(int*)data)-1; + +    ok = connection_write(c, sizeof(int)); +    assert(ok); + +    *ok = 0; +     +    if ((name = pa_scache_get_name_by_id(c->protocol->core, index))) { +        if (request == ESD_PROTO_SAMPLE_PLAY) { +            struct pa_sink *sink; +         +            if ((sink = get_output_sink(c->protocol))) +                if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM) >= 0) +                    *ok = (int) index+1; +        } else { +            assert(request == ESD_PROTO_SAMPLE_FREE); + +            if (pa_scache_remove_item(c->protocol->core, name) >= 0) +                *ok = (int) index+1; +        } +    } +     +    return 0; +} +  /*** client callbacks ***/  static void client_kill_cb(struct pa_client *c) { @@ -566,6 +693,40 @@ static int do_read(struct connection *c) {              if (handler->proc(c, c->request, c->read_data, l) < 0)                  return -1;          } +    } else if (c->state == ESD_CACHING_SAMPLE) { +        ssize_t r; + +        assert(c->scache_memchunk.memblock && c->scache_name && c->scache_memchunk.index < c->scache_memchunk.length); +         +        if ((r = pa_iochannel_read(c->io, c->scache_memchunk.memblock->data+c->scache_memchunk.index, c->scache_memchunk.length-c->scache_memchunk.index)) <= 0) { +            fprintf(stderr, __FILE__": read() failed: %s\n", r == 0 ? "EOF" : strerror(errno)); +            return -1; +        } + +        c->scache_memchunk.index += r; +        assert(c->scache_memchunk.index <= c->scache_memchunk.length); +         +        if (c->scache_memchunk.index == c->scache_memchunk.length) { +            uint32_t index; +            int *ok; +             +            c->scache_memchunk.index = 0; +            pa_scache_add_item(c->protocol->core, c->scache_name, &c->scache_sample_spec, &c->scache_memchunk, &index); + +            pa_memblock_unref(c->scache_memchunk.memblock); +            c->scache_memchunk.memblock = NULL; +            c->scache_memchunk.index = c->scache_memchunk.length = 0; + +            free(c->scache_name); +            c->scache_name = NULL; + +            c->state = ESD_NEXT_REQUEST; + +            ok = connection_write(c, sizeof(int)); +            assert(ok); +            *ok = index+1; +        } +              } else if (c->state == ESD_STREAMING_DATA && c->sink_input) {          struct pa_memchunk chunk;          ssize_t r; @@ -608,7 +769,6 @@ static int do_read(struct connection *c) {          pa_memblockq_push_align(c->input_memblockq, &chunk, 0);          assert(c->sink_input);          pa_sink_notify(c->sink_input->sink); -      }      return 0; @@ -789,10 +949,14 @@ static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, vo      c->playback.memblock_index = 0;      c->playback.fragment_size = 0; +    c->scache_memchunk.length = c->scache_memchunk.index = 0; +    c->scache_memchunk.memblock = NULL; +    c->scache_name = NULL; +          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);  } diff --git a/polyp/scache.c b/polyp/scache.c new file mode 100644 index 00000000..d2a84827 --- /dev/null +++ b/polyp/scache.c @@ -0,0 +1,181 @@ +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "scache.h" +#include "sink-input.h" + +static void free_entry(struct pa_scache_entry *e) { +    assert(e); +    free(e->name); +    if (e->memchunk.memblock) +        pa_memblock_unref(e->memchunk.memblock); +    free(e); +} + +void pa_scache_add_item(struct pa_core *c, const char *name, struct pa_sample_spec *ss, struct pa_memchunk *chunk, uint32_t *index) { +    struct pa_scache_entry *e; +    int put; +    assert(c && name); + +    if (c->scache_hashmap && (e = pa_hashmap_get(c->scache_hashmap, name))) { +        put = 0; +        if (e->memchunk.memblock) +            pa_memblock_unref(e->memchunk.memblock); +    } else { +        put = 1; +        e = malloc(sizeof(struct pa_scache_entry)); +        assert(e); +        e->name = strdup(name); +        assert(e->name); +    } + +    if (ss) +        e->sample_spec = *ss; +    else +        memset(&e->sample_spec, 0, sizeof(struct pa_sample_spec)); + +    if (chunk) { +        e->memchunk = *chunk; +        pa_memblock_ref(e->memchunk.memblock); +    } else { +        e->memchunk.memblock = NULL; +        e->memchunk.index = e->memchunk.length = 0; +    } + +    if (put) { +        if (!c->scache_hashmap) { +            c->scache_hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); +            assert(c->scache_hashmap); +        } +         +        if (!c->scache_idxset) { +            c->scache_idxset = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); +            assert(c->scache_idxset); +        } +         +        pa_idxset_put(c->scache_idxset, e, &e->index); +        pa_hashmap_put(c->scache_hashmap, e->name, e); +    } +         +    if (index) +        *index = e->index; +} + +int pa_scache_remove_item(struct pa_core *c, const char *name) { +    struct pa_scache_entry *e; +    assert(c && name); + +    if (!c->scache_hashmap || !(e = pa_hashmap_get(c->scache_hashmap, name))) +        return -1; + +    pa_hashmap_remove(c->scache_hashmap, name); +    pa_idxset_remove_by_index(c->scache_idxset, e->index); +    free_entry(e); +    return 0; +} + +static void free_cb(void *p, void *userdata) { +    struct pa_scache_entry *e = p; +    assert(e); +    free_entry(e); +} + +void pa_scache_free(struct pa_core *c) { +    assert(c); + +    if (c->scache_hashmap) { +        pa_hashmap_free(c->scache_hashmap, free_cb, NULL); +        c->scache_hashmap = NULL; +    } + +    if (c->scache_idxset) { +        pa_idxset_free(c->scache_idxset, NULL, NULL); +        c->scache_idxset = NULL; +    } +} + +static void sink_input_kill(struct pa_sink_input *i) { +    struct pa_memchunk *c; +    assert(i && i->userdata); +    c = i->userdata; + +    pa_memblock_unref(c->memblock); +    free(c); +    pa_sink_input_free(i); +} + +static int sink_input_peek(struct pa_sink_input *i, struct pa_memchunk *chunk) { +    struct pa_memchunk *c; +    assert(i && chunk && i->userdata); +    c = i->userdata; + +    assert(c->length && c->memblock && c->memblock->length); +    *chunk = *c; +    pa_memblock_ref(c->memblock); +     +    return 0; +} + +static void sink_input_drop(struct pa_sink_input *i, size_t length) { +    struct pa_memchunk *c; +    assert(i && length && i->userdata); +    c = i->userdata; + +    assert(length <= c->length); + +    c->length -= length; +    c->index += length; + +    if (c->length <= 0) +        sink_input_kill(i); +} + +int pa_scache_play_item(struct pa_core *c, const char *name, struct pa_sink *sink, uint32_t volume) { +    struct pa_sink_input *si; +    struct pa_scache_entry *e; +    struct pa_memchunk *chunk; +    assert(c && name && sink); + +    if (!c->scache_hashmap || !(e = pa_hashmap_get(c->scache_hashmap, name))) +        return -1; + +    if (!e->memchunk.memblock) +        return -1; +     +    if (!(si = pa_sink_input_new(sink, name, &e->sample_spec))) +        return -1; + +    si->volume = volume; + +    si->peek = sink_input_peek; +    si->drop = sink_input_drop; +    si->kill = sink_input_kill; +    si->userdata = chunk = malloc(sizeof(struct pa_memchunk)); +    assert(chunk); +    *chunk = e->memchunk; +    pa_memblock_ref(chunk->memblock); + +    return 0; +} + +const char * pa_scache_get_name_by_id(struct pa_core *c, uint32_t id) { +    struct pa_scache_entry *e; +    assert(c && id != PA_IDXSET_INVALID); + +    if (!c->scache_idxset || !(e = pa_idxset_get_by_index(c->scache_idxset, id))) +        return NULL; + +    return e->name; +     +} + +uint32_t pa_scache_get_id_by_name(struct pa_core *c, const char *name) { +    struct pa_scache_entry *e; +    assert(c && name); + +    if (!c->scache_hashmap || !(e = pa_hashmap_get(c->scache_hashmap, name))) +        return PA_IDXSET_INVALID; + +    return e->index; +} diff --git a/polyp/scache.h b/polyp/scache.h new file mode 100644 index 00000000..73759b81 --- /dev/null +++ b/polyp/scache.h @@ -0,0 +1,24 @@ +#ifndef fooscachehfoo +#define fooscachehfoo + +#include "core.h" +#include "memchunk.h" +#include "sink.h" + +struct pa_scache_entry { +    uint32_t index; +    char *name; +    struct pa_sample_spec sample_spec; +    struct pa_memchunk memchunk; +}; + +void pa_scache_add_item(struct pa_core *c, const char *name, struct pa_sample_spec *ss, struct pa_memchunk *chunk, uint32_t *index); + +int pa_scache_remove_item(struct pa_core *c, const char *name); +int pa_scache_play_item(struct pa_core *c, const char *name, struct pa_sink *sink, uint32_t volume); +void pa_scache_free(struct pa_core *c); + +const char *pa_scache_get_name_by_id(struct pa_core *c, uint32_t id); +uint32_t pa_scache_get_id_by_name(struct pa_core *c, const char *name); + +#endif | 
