]> code.delx.au - pulseaudio/commitdiff
sample cache work
authorLennart Poettering <lennart@poettering.net>
Tue, 20 Jul 2004 01:07:06 +0000 (01:07 +0000)
committerLennart Poettering <lennart@poettering.net>
Tue, 20 Jul 2004 01:07:06 +0000 (01:07 +0000)
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@98 fefdeb5f-60dc-0310-8127-8f9354f1896f

17 files changed:
bootstrap.sh
doc/todo
polyp/Makefile.am
polyp/cli-command.c
polyp/clitext.c
polyp/clitext.h
polyp/core.c
polyp/core.h
polyp/debug.h [new file with mode: 0644]
polyp/hashmap.c
polyp/hashmap.h
polyp/main.c
polyp/modargs.c
polyp/module-alsa-sink.c
polyp/protocol-esound.c
polyp/scache.c [new file with mode: 0644]
polyp/scache.h [new file with mode: 0644]

index c9880d85e7071014a808283a22727bbe55618f54..3592ce75cedb29b8beaa0ff5dc968383723d08a8 100755 (executable)
 # along with polypaudio; if not, write to the Free Software Foundation,
 # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 
+run_versioned() {
+    local P
+    type -p "$1-$2" &> /dev/null && P="$1-$2" || local P="$1"
+
+    shift 2
+    "$P" "$@"
+}
+
 if [ "x$1" = "xam" ] ; then
     set -ex
-    automake -a -c --foreign
+    run_versioned automake 1.7 -a -c --foreign
     ./config.status
 else 
     set -ex
@@ -27,10 +35,10 @@ else
     rm -rf autom4te.cache
     rm -f config.cache
 
-    aclocal
+    run_versioned aclocal 1.7
     libtoolize -c --force
     autoheader
-    automake -a -c
+    run_versioned automake 1.7 -a -c --foreign
     autoconf -Wall
 
     CFLAGS="-g -O0" ./configure --sysconfdir=/etc "$@"
index 5142c0138bbb0f811950a3fe8d0a833aa0cc6587..76b0d312fb4be81f1e3a081f45fcbda7d7a41e20 100644 (file)
--- a/doc/todo
+++ b/doc/todo
@@ -1,11 +1,18 @@
 *** $Id$ ***
 
 *** 0.2 ***
+
+- scache memory leak
+- scache remove()
+- scache in native protocol
+- scache/debug.h copyright
+
 - future cancellation
-- clip cache
 - autoloading/autounloading
 - doxygen
 - make mcalign merge chunks
+- autoscan
+- rename clitext.[ch] to cli-text.[ch]
 
 *** 0.3 ***
 - client-ui
index 94edf6165175a6931c70fc59e91427d3d7eb8064..d16155857e8bdf3effec053e1325a6656c11659c 100644 (file)
@@ -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)
index f3a2f8a0d916182deed78b6e8a04174afac8f7fd..03bd125b3e1f1e192da9812aea21b3f7c5e627dc 100644 (file)
@@ -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;
     
index c1b9953b2cad2e80a3b781d9e9fdd864ea37e89b..d0c3f9a740748bfa30ff59379ccf4d7c2b57e13c 100644 (file)
@@ -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);
+}
index b1718cb5a06d34b5f3057a92ad51a8722916291c..4e5252fe35c7a7bbd1635a1e898e96131c2b3504 100644 (file)
@@ -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
 
index dc9525a8f62f18871b9dcf587b4e08f03269ab18..1c69f9147c4b6b06731294ab4d938c86b2e1e56f 100644 (file)
@@ -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);    
 };
index 99d7d76a785a9c0140ea9412b0d47a20d8d1b5f5..03b8671ab16856bda6d9a318e03815fc7d20ce18 100644 (file)
@@ -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 (file)
index 0000000..fb2b889
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef foodebughfoo
+#define foodebughfoo
+
+/* A nice trick for debuggers, working on x86 only */
+
+#define DEBUG_TRAP __asm__("int $3")
+
+#endif
index 2c7c92b5deb607ea58d31e21c43ac53b82ec7c66..51e3879b5b1e82624547e39098b1ffd2da8ec73d 100644 (file)
@@ -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;
+}
index b24e74a52766b683ec9c3a554ac7015dd5fd8665..3b79d7ae87bd8c3c46005649c28596db9578c68e 100644 (file)
@@ -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
index bcc1fadae52bea000cb4379dd9bfc452139d5a39..0c6104a44b69a8bc084e2cbc1a166a7c2b42e3e8 100644 (file)
@@ -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();
index ea104761a10577eeb870698b78bb3e69e51868d6..3841a9ec25f9ff7137961b7039f8e62daa31424d 100644 (file)
@@ -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;
index 8a3388af97391e433ef2dd0bb7e409d5e3602f63..c250d1cf256febb1ba6b2c69817114f804f693ad 100644 (file)
@@ -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;
index 8a7c4bcbfc2e8f25592f01d33dc819d77f219384..91e6b7d68053db96aa39982c76f6c433a8be9e2c 100644 (file)
 #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"
 
 #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 (file)
index 0000000..d2a8482
--- /dev/null
@@ -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 (file)
index 0000000..73759b8
--- /dev/null
@@ -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