]> code.delx.au - pulseaudio/commitdiff
sample cache work
authorLennart Poettering <lennart@poettering.net>
Tue, 3 Aug 2004 19:26:56 +0000 (19:26 +0000)
committerLennart Poettering <lennart@poettering.net>
Tue, 3 Aug 2004 19:26:56 +0000 (19:26 +0000)
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@102 fefdeb5f-60dc-0310-8127-8f9354f1896f

27 files changed:
configure.ac
doc/todo
polyp/Makefile.am
polyp/cli-command.c
polyp/clitext.c
polyp/module-alsa-sink.c
polyp/module-alsa-source.c
polyp/module-oss-mmap.c
polyp/module-oss.c
polyp/module-x11-bell.c
polyp/native-common.h
polyp/pactl.c
polyp/pdispatch.c
polyp/polyplib-def.h
polyp/polyplib.c
polyp/polyplib.h
polyp/protocol-esound.c
polyp/protocol-native.c
polyp/protocol-simple.c
polyp/pstream.c
polyp/resampler.c
polyp/sample-util.c
polyp/sample.c
polyp/sample.h
polyp/scache.c
polyp/sink.c
polyp/source.c

index 3028228a4066e226a604d42c1b63a54205a33028..9e5352a5edf135d9807788f6103b7664d6f9bb22 100644 (file)
@@ -46,6 +46,10 @@ PKG_CHECK_MODULES(LIBSAMPLERATE, [ samplerate >= 0.1.0 ])
 AC_SUBST(LIBSAMPLERATE_CFLAGS)
 AC_SUBST(LIBSAMPLERATE_LIBS)
 
+PKG_CHECK_MODULES(LIBSNDFILE, [ sndfile >= 1.0.0 ])
+AC_SUBST(LIBSNDFILE_CFLAGS)
+AC_SUBST(LIBSNDFILE_LIBS)
+
 PKG_CHECK_MODULES(ASOUNDLIB, [ alsa >= 1.0.0 ])
 AC_SUBST(ASOUNDLIB_CFLAGS)
 AC_SUBST(ASOUNDLIB_LIBS)
index 73401e7a4514ba1aace2953c37783efc1f6a9634..3f04bfca3db0b87c78ef25a9868aa43ce8a62d1f 100644 (file)
--- a/doc/todo
+++ b/doc/todo
@@ -2,16 +2,14 @@
 
 *** 0.2 ***
 
-- enable searchdir
-
-- scache remove()
-- scache in native protocol
-- scache/debug.h copyright
-
 - future cancellation
 - autoloading/autounloading
-- doxygen
 - make mcalign merge chunks
+
+- doxygen
+
+- scache.[ch]/module-x11-bell.c/debug.h copyright
+- enable searchdir
 - autoscan
 - rename clitext.[ch] to cli-text.[ch]
 
index abe1d0747225e788aec88e566f9366d847f1d6a9..48b984f80ff2d9e24554e8738faeb68c1a25b322 100644 (file)
@@ -107,11 +107,13 @@ polypaudio_SOURCES = idxset.c idxset.h \
                clitext.c clitext.h \
                tokenizer.c tokenizer.h \
                dynarray.c dynarray.h \
-               scache.c scache.h
+               scache.c scache.h \
+               sound-file.c sound-file.h \
+               play-memchunk.c play-memchunk.h
 
-polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS)
+polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS)
 polypaudio_INCLUDES = $(INCLTDL)
-polypaudio_LDADD = $(AM_LDADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS)
+polypaudio_LDADD = $(AM_LDADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS)
 polypaudio_LDFLAGS=-export-dynamic
 
 libprotocol_simple_la_SOURCES = protocol-simple.c protocol-simple.h
@@ -270,7 +272,8 @@ libpolyp_la_SOURCES = polyplib.c polyplib.h \
                memchunk.c memchunk.h \
                authkey.c authkey.h \
                socket-util.c socket-util.h \
-               native-common.h
+               native-common.h \
+               sample.c sample.h
 libpolyp_la_CFLAGS = $(AM_CFLAGS)
 
 libpolyp_mainloop_la_SOURCES = mainloop-api.h mainloop-api.c \
@@ -290,8 +293,8 @@ pacat_LDADD = $(AM_LDADD) libpolyp.la libpolyp-error.la libpolyp-mainloop.la
 pacat_CFLAGS = $(AM_CFLAGS) 
 
 pactl_SOURCES = pactl.c
-pactl_LDADD = $(AM_LDADD) libpolyp.la libpolyp-error.la libpolyp-mainloop.la
-pactl_CFLAGS = $(AM_CFLAGS) 
+pactl_LDADD = $(AM_LDADD) libpolyp.la libpolyp-error.la libpolyp-mainloop.la $(LIBSNDFILE_LIBS)
+pactl_CFLAGS = $(AM_CFLAGS) $(LIBSDNFILE_CFLAGS)
 
 pacat_simple_SOURCES = pacat-simple.c
 pacat_simple_LDADD = $(AM_LDADD) libpolyp-simple.la libpolyp-error.la
index 03bd125b3e1f1e192da9812aea21b3f7c5e627dc..826789ce288e0f05b98a1ed96f8af03a59b01976 100644 (file)
@@ -42,6 +42,8 @@
 #include "clitext.h"
 #include "scache.h"
 #include "sample-util.h"
+#include "sound-file.h"
+#include "play-memchunk.h"
 
 struct command {
     const char *name;
@@ -72,6 +74,8 @@ static int pa_cli_command_kill_source_output(struct pa_core *c, struct pa_tokeni
 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 int pa_cli_command_scache_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_play_file(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 },
@@ -98,6 +102,8 @@ static const struct command commands[] = {
     { "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},
+    { "scache_load",             pa_cli_command_scache_load,        "Load a sound file into the sample cache (args: filename,name)", 3},
+    { "play_file",               pa_cli_command_play_file,          "Play a sound file (args: filename, sink|index)", 3},
     { NULL, NULL, NULL, 0 }
 };
 
@@ -488,6 +494,55 @@ static int pa_cli_command_scache_remove(struct pa_core *c, struct pa_tokenizer *
     return 0;
 }
 
+static int pa_cli_command_scache_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+    const char *fname, *n;
+    struct pa_memchunk chunk;
+    struct pa_sample_spec ss;
+    assert(c && t && buf && fail && verbose);
+
+    if (!(fname = pa_tokenizer_get(t, 1)) || !(n = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a file name and a sample name.\n");
+        return -1;
+    }
+
+    if (pa_sound_file_load(fname, &ss, &chunk) < 0) {
+        pa_strbuf_puts(buf, "Failed to load sound file.\n");
+        return -1;
+    }
+
+    pa_scache_add_item(c, n, &ss, &chunk, NULL);
+    pa_memblock_unref(chunk.memblock);
+    return 0;
+}
+
+static int pa_cli_command_play_file(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+    const char *fname, *sink_name;
+    struct pa_memchunk chunk;
+    struct pa_sample_spec ss;
+    struct pa_sink *sink;
+    int ret;
+    assert(c && t && buf && fail && verbose);
+
+    if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a file 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_sound_file_load(fname, &ss, &chunk) < 0) {
+        pa_strbuf_puts(buf, "Failed to load sound file.\n");
+        return -1;
+    }
+
+    ret = pa_play_memchunk(sink, fname, &ss, &chunk, PA_VOLUME_NORM);
+    pa_memblock_unref(chunk.memblock);
+    return ret;
+}
+
 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 d0c3f9a740748bfa30ff59379ccf4d7c2b57e13c..fbce22237a14a1042a090676a1fd35bea99b26bb 100644 (file)
@@ -225,12 +225,13 @@ char *pa_scache_list_to_string(struct pa_core *c) {
             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",
+                s, "    name: <%s>\n\tindex: <%i>\n\tsample_spec: <%s>\n\tlength: <%u>\n\tduration: <%0.1fs>\n\tvolume: <0x%04x>\n",
                 e->name,
                 e->index,
                 ss,
                 e->memchunk.length,
-                l);
+                l,
+                e->volume);
         }
     }
 
index c250d1cf256febb1ba6b2c69817114f804f693ad..c19582272db1f7df686fa767bcf44518d36f04a9 100644 (file)
@@ -142,7 +142,7 @@ static uint32_t sink_get_latency_cb(struct pa_sink *s) {
     if (frames < 0)
         frames = 0;
     
-    return pa_samples_usec(frames * u->frame_size, &s->sample_spec);
+    return pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec);
 }
 
 int pa_module_init(struct pa_core *c, struct pa_module*m) {
@@ -165,7 +165,7 @@ int pa_module_init(struct pa_core *c, struct pa_module*m) {
         fprintf(stderr, __FILE__": failed to parse sample specification\n");
         goto fail;
     }
-    frame_size = pa_sample_size(&ss);
+    frame_size = pa_frame_size(&ss);
     
     periods = 12;
     fragsize = 1024;
index 287a0350209dd2dc737b810739ca5ac41fb67370..a453944e9232e36bb9d322046172a35109981ffe 100644 (file)
@@ -149,7 +149,7 @@ int pa_module_init(struct pa_core *c, struct pa_module*m) {
         fprintf(stderr, __FILE__": failed to parse sample specification\n");
         goto fail;
     }
-    frame_size = pa_sample_size(&ss);
+    frame_size = pa_frame_size(&ss);
     
     periods = 12;
     fragsize = 1024;
index 800eaf252c0502e0327180a2678bce4c0a08abe4..30ff3ce6b9a8ba2ce91da639bf6519b6ee85c7dc 100644 (file)
@@ -198,7 +198,7 @@ static uint32_t sink_get_latency_cb(struct pa_sink *s) {
     assert(s && u);
 
     do_write(u);
-    return pa_samples_usec(u->out_fill, &s->sample_spec);
+    return pa_bytes_to_usec(u->out_fill, &s->sample_spec);
 }
 
 int pa_module_init(struct pa_core *c, struct pa_module*m) {
index d727534aca7d881e0fffbbb6244709b8b0271e6a..51585ca9def69562b11173f108277240fa1b3daf 100644 (file)
@@ -153,7 +153,7 @@ static uint32_t sink_get_latency_cb(struct pa_sink *s) {
         return 0;
     }
 
-    return pa_samples_usec(arg, &s->sample_spec);
+    return pa_bytes_to_usec(arg, &s->sample_spec);
 }
 
 int pa_module_init(struct pa_core *c, struct pa_module*m) {
@@ -258,7 +258,7 @@ int pa_module_init(struct pa_core *c, struct pa_module*m) {
 
     u->memchunk.memblock = NULL;
     u->memchunk.length = 0;
-    u->sample_size = pa_sample_size(&ss);
+    u->sample_size = pa_frame_size(&ss);
 
     u->out_fragment_size = out_frag_size;
     u->in_fragment_size = in_frag_size;
index 2966313472ccdbac2e4bbeb5d0848f4ea63b0087..4da3c880e9ad67c85bd70e88512f7d7c52d25f35 100644 (file)
@@ -110,7 +110,7 @@ int pa_module_init(struct pa_core *c, struct pa_module*m) {
     u->core = c;
     u->display = NULL;
     u->x11_sources = NULL;
-    u->scache_item = strdup(pa_modargs_get_value(ma, "sample", "bell"));
+    u->scache_item = strdup(pa_modargs_get_value(ma, "sample", "x11-bell"));
     assert(u->scache_item);
         
     if (pa_modargs_get_sink_index(ma, c, &u->sink_index) < 0) {
index 2acbae8e8a0abdaf1a3c1aaeaf9720c67a052e87..dc730e4b0f8531650723b4543d2d417e2ff8101c 100644 (file)
@@ -41,6 +41,24 @@ enum {
     PA_COMMAND_RECORD_STREAM_KILLED,
     PA_COMMAND_STAT,
     PA_COMMAND_GET_PLAYBACK_LATENCY,
+    
+    PA_COMMAND_CREATE_UPLOAD_STREAM,
+    PA_COMMAND_DELETE_UPLOAD_STREAM,
+    PA_COMMAND_FINISH_UPLOAD_STREAM,
+    PA_COMMAND_PLAY_SAMPLE,
+    PA_COMMAND_REMOVE_SAMPLE,
+    
+    PA_COMMAND_GET_SINK,
+    PA_COMMAND_GET_SOURCE,
+    PA_COMMAND_GET_MODULE,
+    PA_COMMAND_GET_CLIENT,
+    PA_COMMAND_GET_SINK_INPUT,
+    PA_COMMAND_GET_SOURCE_OUTPUT,
+    PA_COMMAND_GET_SAMPLE,
+
+    PA_COMMAND_SUBSCRIBE,
+    PA_COMMAND_SUBSCRIBE_EVENT,
+    
     PA_COMMAND_MAX
 };
 
index 61060c469e568d2e9fcf95d19587136d9079e128..28b187b044cef55675b34a9a4244de70fdf18818 100644 (file)
 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <limits.h>
+
+#include <sndfile.h>
 
 #include <polyp/polyplib.h>
 #include <polyp/polyplib-error.h>
 #include <polyp/mainloop.h>
 #include <polyp/mainloop-signal.h>
+#include <polyp/sample.h>
+
+#define BUFSIZE 1024
 
 static struct pa_context *context = NULL;
 static struct pa_mainloop_api *mainloop_api = NULL;
 
+static char **process_argv = NULL;
+
+static SNDFILE *sndfile = NULL;
+static struct pa_stream *sample_stream = NULL;
+static struct pa_sample_spec sample_spec;
+static size_t sample_length = 0;
+
+static char *sample_name = NULL;
+
 static enum {
     NONE,
     EXIT,
-    STAT
+    STAT,
+    UPLOAD_SAMPLE,
+    PLAY_SAMPLE,
+    REMOVE_SAMPLE
 } action = NONE;
 
 static void quit(int ret) {
@@ -78,6 +96,79 @@ static void stat_callback(struct pa_context *c, uint32_t blocks, uint32_t total,
     drain();
 }
 
+static void play_sample_callback(struct pa_context *c, int success, void *userdata) {
+    if (!success) {
+        fprintf(stderr, "Failed to play sample: %s\n", pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    drain();
+}
+
+static void remove_sample_callback(struct pa_context *c, int success, void *userdata) {
+    if (!success) {
+        fprintf(stderr, "Failed to remove sample: %s\n", pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    drain();
+}
+
+static void stream_die_callback(struct pa_stream *s, void *userdata) {
+    assert(s);
+    fprintf(stderr, "Stream deleted, exiting.\n");
+    quit(1);
+}
+
+static void finish_sample_callback(struct pa_stream *s, int success, void *userdata) {
+    assert(s);
+
+    if (!success) {
+        fprintf(stderr, "Failed to upload sample: %s\n", pa_strerror(pa_context_errno(context)));
+        quit(1);
+        return;
+    }
+
+    drain();
+}
+
+static void stream_write_callback(struct pa_stream *s, size_t length, void *userdata) {
+    sf_count_t l;
+    float *d;
+    assert(s && length && sndfile);
+
+    d = malloc(length);
+    assert(d);
+
+    assert(sample_length >= length);
+    l = length/pa_frame_size(&sample_spec);
+
+    if ((sf_readf_float(sndfile, d, l)) != l) {
+        free(d);
+        fprintf(stderr, "Premature end of file\n");
+        quit(1);
+    }
+    
+    pa_stream_write(s, d, length);
+    free(d);
+
+    sample_length -= length;
+
+    if (sample_length  <= 0) {
+        pa_stream_set_write_callback(sample_stream, NULL, NULL);
+        pa_stream_finish_sample(sample_stream, finish_sample_callback, NULL);
+    }
+}
+
+static void upload_callback(struct pa_stream *s, int success, void *userdata) {
+    if (!success) {
+        fprintf(stderr, "Failed to upload sample: %s\n", pa_strerror(pa_context_errno(context)));
+        quit(1);
+    }
+}
+
 static void context_complete_callback(struct pa_context *c, int success, void *userdata) {
     assert(c);
 
@@ -90,7 +181,19 @@ static void context_complete_callback(struct pa_context *c, int success, void *u
 
     if (action == STAT)
         pa_context_stat(c, stat_callback, NULL);
-    else {
+    else if (action == PLAY_SAMPLE)
+        pa_context_play_sample(c, process_argv[2], NULL, 0x100, play_sample_callback, NULL);
+    else if (action == REMOVE_SAMPLE)
+        pa_context_remove_sample(c, process_argv[2], remove_sample_callback, NULL);
+    else if (action == UPLOAD_SAMPLE) {
+        if (!(sample_stream = pa_context_upload_sample(c, sample_name, &sample_spec, sample_length, upload_callback, NULL))) {
+            fprintf(stderr, "Failed to upload sample: %s\n", pa_strerror(pa_context_errno(c)));
+            goto fail;
+        }
+        
+        pa_stream_set_die_callback(sample_stream, stream_die_callback, NULL);
+        pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
+    } else {
         assert(action == EXIT);
         pa_context_exit(c);
         drain();
@@ -105,11 +208,12 @@ fail:
 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;
+    char tmp[PATH_MAX];
+    
     int ret = 1, r;
 
     if (argc >= 2) {
@@ -117,13 +221,60 @@ int main(int argc, char *argv[]) {
             action = STAT;
         else if (!strcmp(argv[1], "exit"))
             action = EXIT;
+        else if (!strcmp(argv[1], "scache_upload")) {
+            struct SF_INFO sfinfo;
+            action = UPLOAD_SAMPLE;
+
+            if (argc < 3) {
+                fprintf(stderr, "Please specify a sample file to load\n");
+                goto quit;
+            }
+
+            if (argc >= 4)
+                sample_name = argv[3];
+            else {
+                char *f = strrchr(argv[2], '/');
+                if (f)
+                    f++;
+                else
+                    f = argv[2];
+
+                strncpy(sample_name = tmp, f, strcspn(f, "."));
+            }
+            
+            memset(&sfinfo, 0, sizeof(sfinfo));
+            if (!(sndfile = sf_open(argv[2], SFM_READ, &sfinfo))) {
+                fprintf(stderr, "Failed to open sound file.\n");
+                goto quit;
+            }
+            
+            sample_spec.format =  PA_SAMPLE_FLOAT32;
+            sample_spec.rate = sfinfo.samplerate;
+            sample_spec.channels = sfinfo.channels;
+
+            sample_length = sfinfo.frames*pa_frame_size(&sample_spec);
+        } else if (!strcmp(argv[1], "scache_play")) {
+            action = PLAY_SAMPLE;
+            if (argc < 3) {
+                fprintf(stderr, "You have to specify a sample name to play\n");
+                goto quit;
+            }
+        } else if (!strcmp(argv[1], "scache_remove")) {
+            action = REMOVE_SAMPLE;
+            if (argc < 3) {
+                fprintf(stderr, "You have to specify a sample name to remove\n");
+                goto quit;
+            }
+        }
     }
 
     if (action == NONE) {
-        fprintf(stderr, "No valid action specified. Use one of: stat, exit\n");
+        fprintf(stderr, "No valid action specified. Use one of: stat, exit, scache_upload, scache_play, scache_remove\n");
         goto quit;
     }
 
+    process_argv = argv;
+    
     if (!(m = pa_mainloop_new())) {
         fprintf(stderr, "pa_mainloop_new() failed.\n");
         goto quit;
@@ -162,5 +313,8 @@ quit:
         pa_mainloop_free(m);
     }
     
+    if (sndfile)
+        sf_close(sndfile);
+    
     return ret;
 }
index a28458a4091299f873235d2addd6d1d2e01c33e2..2ab98b52a483e7e271f2b22a6d8457c46fc3f672 100644 (file)
@@ -30,6 +30,8 @@
 #include "pdispatch.h"
 #include "native-common.h"
 
+/*#define DEBUG_OPCODES*/
+
 #ifdef DEBUG_OPCODES
 
 static const char *command_names[PA_COMMAND_MAX] = {
@@ -51,6 +53,11 @@ static const char *command_names[PA_COMMAND_MAX] = {
     [PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED",
     [PA_COMMAND_STAT] = "STAT",
     [PA_COMMAND_GET_PLAYBACK_LATENCY] = "PLAYBACK_LATENCY",
+    [PA_COMMAND_CREATE_UPLOAD_STREAM] = "CREATE_UPLOAD_STREAM",
+    [PA_COMMAND_DELETE_UPLOAD_STREAM] = "DELETE_UPLOAD_STREAM",
+    [PA_COMMAND_FINISH_UPLOAD_STREAM] = "FINISH_UPLOAD_STREAM",
+    [PA_COMMAND_PLAY_SAMPLE] = "PLAY_SAMPLE",
+    [PA_COMMAND_REMOVE_SAMPLE] = "REMOVE_SAMPLE",
 };
 
 #endif
@@ -108,12 +115,13 @@ struct pa_pdispatch* pa_pdispatch_new(struct pa_mainloop_api *mainloop, const st
     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;
index e43f4b3597a3ae670f7f828f427a510c1605b9ae..ec2d528b04ce8995c2d72b337605203a009b4f24 100644 (file)
@@ -26,7 +26,8 @@
 
 enum pa_stream_direction {
     PA_STREAM_PLAYBACK,
-    PA_STREAM_RECORD
+    PA_STREAM_RECORD,
+    PA_STREAM_UPLOAD
 };
 
 struct pa_buffer_attr {
index ea5003b4008bdf876798f978c1a7f0b0e629a2a7..b1052a8d383fa782fc10c8fddda44b12e1ae140d 100644 (file)
@@ -81,6 +81,12 @@ struct pa_context {
 
     void (*stat_callback)(struct pa_context*c, uint32_t count, uint32_t total, void *userdata);
     void *stat_userdata;
+
+    void (*play_sample_callback)(struct pa_context*c, int success, void *userdata);
+    void *play_sample_userdata;
+    
+    void (*remove_sample_callback)(struct pa_context*c, int success, void *userdata);
+    void *remove_sample_userdata;
     
     uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
 };
@@ -92,12 +98,12 @@ struct pa_stream {
     char *name;
     struct pa_buffer_attr buffer_attr;
     struct pa_sample_spec sample_spec;
-    uint32_t device_index;
     uint32_t channel;
     int channel_valid;
+    uint32_t device_index;
     enum pa_stream_direction direction;
     
-    enum { STREAM_LOOKING_UP, STREAM_CREATING, STREAM_READY, STREAM_DEAD} state;
+    enum { 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);
@@ -117,6 +123,9 @@ struct pa_stream {
 
     void (*get_latency_callback)(struct pa_stream*c, uint32_t latency, void *userdata);
     void *get_latency_userdata;
+
+    void (*finish_sample_callback)(struct pa_stream*c, int success, void *userdata);
+    void *finish_sample_userdata;
 };
 
 static void command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
@@ -167,6 +176,12 @@ struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *
     c->stat_callback = NULL;
     c->stat_userdata = NULL;
 
+    c->play_sample_callback = NULL;
+    c->play_sample_userdata = NULL;
+
+    c->remove_sample_callback = NULL;
+    c->remove_sample_userdata = NULL;
+
     pa_check_for_sigpipe();
     return c;
 }
@@ -494,7 +509,7 @@ static void command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t
         return;
     
     s->requested_bytes += bytes;
-    
+
     if (s->requested_bytes && s->write_callback)
         s->write_callback(s, s->requested_bytes, s->write_userdata);
 }
@@ -517,7 +532,7 @@ static void create_stream_callback(struct pa_pdispatch *pd, uint32_t command, ui
     }
 
     if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
-        pa_tagstruct_getu32(t, &s->device_index) < 0 ||
+        ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->device_index) < 0) ||
         !pa_tagstruct_eof(t)) {
         s->context->error = PA_ERROR_PROTOCOL;
         context_dead(s->context);
@@ -525,14 +540,14 @@ static void create_stream_callback(struct pa_pdispatch *pd, uint32_t command, ui
     }
 
     s->channel_valid = 1;
-    pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams :  s->context->record_streams, s->channel, s);
+    pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_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) {
+static void create_stream(struct pa_stream *s, const char *dev) {
     struct pa_tagstruct *t;
     uint32_t tag;
     assert(s);
@@ -546,7 +561,8 @@ static void create_stream(struct pa_stream *s, uint32_t tdev_index) {
     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, (uint32_t) -1);
+    pa_tagstruct_puts(t, dev ? dev : "");
     pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
     if (s->direction == PA_STREAM_PLAYBACK) {
         pa_tagstruct_putu32(t, s->buffer_attr.tlength);
@@ -559,49 +575,42 @@ static void create_stream(struct pa_stream *s, uint32_t tdev_index) {
     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);
+static struct pa_stream *internal_stream_new(struct pa_context *c) {
+    struct pa_stream *s;
 
-    if (command != PA_COMMAND_REPLY) {
-        if (handle_error(s->context, command, t) < 0) {
-            context_dead(s->context);
-            return;
-        }
+    s = malloc(sizeof(struct pa_stream));
+    assert(s);
+    s->context = c;
 
-        stream_dead(s);
-        if (s->create_complete_callback)
-            s->create_complete_callback(s, 0, s->create_complete_userdata);
-        return;
-    }
+    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 = NULL;
+    s->create_complete_userdata = NULL;
+    s->get_latency_callback = NULL;
+    s->get_latency_userdata = NULL;
+    s->finish_sample_callback = NULL;
+    s->finish_sample_userdata = NULL;
 
-    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);
-}
+    s->name = NULL;
+    s->state = STREAM_CREATING;
+    s->requested_bytes = 0;
+    s->channel = 0;
+    s->channel_valid = 0;
+    s->device_index = (uint32_t) -1;
 
-static void lookup_device(struct pa_stream *s, const char *tdev) {
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(s);
+    memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
     
-    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);
+    s->next = c->first_stream;
+    if (s->next)
+        s->next->previous = s;
+    s->previous = NULL;
+    c->first_stream = s;
 
-    pa_pstream_send_tagstruct(s->context->pstream, t);
-    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, lookup_device_callback, s);
+    return s;
 }
 
 struct pa_stream* pa_stream_new(
@@ -616,29 +625,15 @@ struct pa_stream* pa_stream_new(
     
     struct pa_stream *s;
 
-    assert(c && name && ss && c->state == CONTEXT_READY);
-    
-    s = malloc(sizeof(struct pa_stream));
+    assert(c && name && ss && c->state == CONTEXT_READY && (dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD));
+
+    s = internal_stream_new(c);
     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->create_complete_userdata = userdata;
     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)
@@ -651,16 +646,7 @@ struct pa_stream* pa_stream_new(
         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);
+    create_stream(s, dev);
     
     return s;
 }
@@ -677,7 +663,8 @@ void pa_stream_free(struct pa_stream *s) {
         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->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_DELETE_PLAYBACK_STREAM :
+                            (s->direction == PA_STREAM_RECORD ? PA_COMMAND_DELETE_RECORD_STREAM : PA_COMMAND_DELETE_UPLOAD_STREAM));
         pa_tagstruct_putu32(t, s->context->ctag++);
         pa_tagstruct_putu32(t, s->channel);
         pa_pstream_send_tagstruct(s->context->pstream, t);
@@ -697,7 +684,6 @@ void pa_stream_free(struct pa_stream *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;
 }
@@ -971,3 +957,163 @@ void pa_stream_get_latency(struct pa_stream *p, void (*cb)(struct pa_stream *p,
     pa_pstream_send_tagstruct(p->context->pstream, t);
     pa_pdispatch_register_reply(p->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, p);
 }
+
+struct pa_stream* pa_context_upload_sample(struct pa_context *c, const char *name, const struct pa_sample_spec *ss, size_t length, void (*cb) (struct pa_stream*s, int success, void *userdata), void *userdata) {
+    struct pa_stream *s;
+    struct pa_tagstruct *t;
+    uint32_t tag;
+    
+    s = internal_stream_new(c);
+    assert(s);
+
+    s->create_complete_callback = cb;
+    s->create_complete_userdata = userdata;
+    s->name = strdup(name);
+    s->state = STREAM_CREATING;
+    s->direction = PA_STREAM_UPLOAD;
+    s->sample_spec = *ss;
+
+    t = pa_tagstruct_new(NULL, 0);
+    assert(t);
+    pa_tagstruct_putu32(t, PA_COMMAND_CREATE_UPLOAD_STREAM);
+    pa_tagstruct_putu32(t, tag = c->ctag++);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_put_sample_spec(t, ss);
+    pa_tagstruct_putu32(t, length);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, s);
+
+    return s;
+}
+
+static void stream_finish_sample_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;
+        }
+
+        if (s->finish_sample_callback)
+            s->finish_sample_callback(s, 0, s->finish_sample_userdata);
+        return;
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        s->context->error = PA_ERROR_PROTOCOL;
+        context_dead(s->context);
+        return;
+    }
+
+    if (s->finish_sample_callback)
+        s->finish_sample_callback(s, 1, s->finish_sample_userdata);
+}
+
+void pa_stream_finish_sample(struct pa_stream *p, void (*cb)(struct pa_stream*s, int success, void *userdata), void *userdata) {
+    struct pa_tagstruct *t;
+    uint32_t tag;
+    assert(p);
+
+    p->finish_sample_callback = cb;
+    p->finish_sample_userdata = userdata;
+    
+    t = pa_tagstruct_new(NULL, 0);
+    assert(t);
+    pa_tagstruct_putu32(t, PA_COMMAND_FINISH_UPLOAD_STREAM);
+    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_finish_sample_callback, p);
+}
+
+static void context_play_sample_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);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (handle_error(c, command, t) < 0) {
+            context_dead(c);
+            return;
+        }
+
+        if (c->play_sample_callback)
+            c->play_sample_callback(c, 0, c->play_sample_userdata);
+        return;
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        c->error = PA_ERROR_PROTOCOL;
+        context_dead(c);
+        return;
+    }
+
+    if (c->play_sample_callback)
+        c->play_sample_callback(c, 1, c->play_sample_userdata);
+}
+
+void pa_context_play_sample(struct pa_context *c, const char *name, const char *dev, uint32_t volume, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata) {
+    struct pa_tagstruct *t;
+    uint32_t tag;
+    assert(c && name && *name && (!dev || *dev));
+
+    if (!volume)
+        return;
+    
+    c->play_sample_callback = cb;
+    c->play_sample_userdata = userdata;
+
+    t = pa_tagstruct_new(NULL, 0);
+    assert(t);
+    pa_tagstruct_putu32(t, PA_COMMAND_PLAY_SAMPLE);
+    pa_tagstruct_putu32(t, tag = c->ctag++);
+    pa_tagstruct_putu32(t, (uint32_t) -1);
+    pa_tagstruct_puts(t, dev ? dev : "");
+    pa_tagstruct_putu32(t, volume);
+    pa_tagstruct_puts(t, name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_play_sample_callback, c);
+}
+
+static void context_remove_sample_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);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (handle_error(c, command, t) < 0) {
+            context_dead(c);
+            return;
+        }
+
+        if (c->remove_sample_callback)
+            c->remove_sample_callback(c, 0, c->remove_sample_userdata);
+        return;
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        c->error = PA_ERROR_PROTOCOL;
+        context_dead(c);
+        return;
+    }
+
+    if (c->remove_sample_callback)
+        c->remove_sample_callback(c, 1, c->remove_sample_userdata);
+}
+
+void pa_context_remove_sample(struct pa_context *c, const char *name, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata) {
+    struct pa_tagstruct *t;
+    uint32_t tag;
+    assert(c && name);
+
+    c->remove_sample_callback = cb;
+    c->remove_sample_userdata = userdata;
+    
+    t = pa_tagstruct_new(NULL, 0);
+    assert(t);
+    pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_SAMPLE);
+    pa_tagstruct_putu32(t, tag = c->ctag++);
+    pa_tagstruct_puts(t, name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_remove_sample_callback, c);
+}
index 440b9658e2aaf071a0380bd2d081635fb6cc00ca..391cb0c868219495424092efddaecb909f1d0849 100644 (file)
@@ -91,4 +91,10 @@ void pa_stream_get_latency(struct pa_stream *p, void (*cb)(struct pa_stream *p,
 
 struct pa_context* pa_stream_get_context(struct pa_stream *p);
 
+struct pa_stream* pa_context_upload_sample(struct pa_context *c, const char *name, const struct pa_sample_spec *ss, size_t length, void (*cb) (struct pa_stream*s, int success, void *userdata), void *userdata);
+void pa_stream_finish_sample(struct pa_stream *p, void (*cb)(struct pa_stream*s, int success, void *userdata), void *userdata);
+
+void pa_context_play_sample(struct pa_context *c, const char *name, const char *dev, uint32_t volume, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata);
+void pa_context_remove_sample(struct pa_context *c, const char *name, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata);
+
 #endif
index 5db0442f9a7b4a4ff464bce3a230ffd407887e31..d80445dee874f29bc31ed905672a3275e042f35c 100644 (file)
@@ -312,7 +312,7 @@ static int esd_proto_stream_play(struct connection *c, esd_proto_t request, cons
     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);
+    c->input_memblockq = pa_memblockq_new(l, 0, pa_frame_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;
@@ -376,7 +376,7 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co
     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);
+    c->output_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&ss), 0, 0);
     assert(c->output_memblockq);
     pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2);
     
@@ -936,7 +936,7 @@ static void sink_input_kill_cb(struct pa_sink_input *i) {
 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);
+    return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
 }
 
 /*** source_output callbacks ***/
index 83c910d1d9a77227d0c3fb477f31962b43c0c343..a2332a12785c81629fe0de34a7a49fb3a53ea796 100644 (file)
@@ -40,6 +40,7 @@
 #include "pstream-util.h"
 #include "authkey.h"
 #include "namereg.h"
+#include "scache.h"
 
 struct connection;
 struct pa_protocol_native;
@@ -53,6 +54,7 @@ struct record_stream {
 };
 
 struct playback_stream {
+    int type;
     struct connection *connection;
     uint32_t index;
     struct pa_sink_input *sink_input;
@@ -62,13 +64,32 @@ struct playback_stream {
     uint32_t drain_tag;
 };
 
+struct upload_stream {
+    int type;
+    struct connection *connection;
+    uint32_t index;
+    struct pa_memchunk memchunk;
+    size_t length;
+    char *name;
+    struct pa_sample_spec sample_spec;
+};
+
+struct output_stream {
+    int type;
+};
+
+enum {
+    UPLOAD_STREAM,
+    PLAYBACK_STREAM
+};
+
 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;
+    struct pa_idxset *record_streams, *output_streams;
     uint32_t rrobin_index;
 };
 
@@ -93,25 +114,28 @@ static void source_output_push_cb(struct pa_source_output *o, const struct pa_me
 
 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_delete_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 void command_create_upload_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+static void command_finish_upload_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+static void command_play_sample(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+static void command_remove_sample(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_DELETE_PLAYBACK_STREAM] = { command_delete_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_DELETE_RECORD_STREAM] = { command_delete_stream },
     [PA_COMMAND_AUTH] = { command_auth },
     [PA_COMMAND_REQUEST] = { NULL },
     [PA_COMMAND_EXIT] = { command_exit },
@@ -120,11 +144,51 @@ static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_LOOKUP_SOURCE] = { command_lookup },
     [PA_COMMAND_STAT] = { command_stat },
     [PA_COMMAND_GET_PLAYBACK_LATENCY] = { command_get_playback_latency },
+    [PA_COMMAND_CREATE_UPLOAD_STREAM] = { command_create_upload_stream },
+    [PA_COMMAND_DELETE_UPLOAD_STREAM] = { command_delete_stream },
+    [PA_COMMAND_FINISH_UPLOAD_STREAM] = { command_finish_upload_stream },
+    [PA_COMMAND_PLAY_SAMPLE] = { command_play_sample },
+    [PA_COMMAND_REMOVE_SAMPLE] = { command_remove_sample },
 };
 
 /* 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) {
+static struct upload_stream* upload_stream_new(struct connection *c, const struct pa_sample_spec *ss, const char *name, size_t length) {
+    struct upload_stream *s;
+    assert(c && ss && name && length);
+    
+    s = malloc(sizeof(struct upload_stream));
+    assert (s);
+    s->type = UPLOAD_STREAM;
+    s->connection = c;
+    s->sample_spec = *ss;
+    s->name = strdup(name);
+    assert(s->name);
+
+    s->memchunk.memblock = NULL;
+    s->memchunk.index = 0;
+    s->memchunk.length = 0;
+
+    s->length = length;
+    
+    pa_idxset_put(c->output_streams, s, &s->index);
+    return s;
+}
+
+static void upload_stream_free(struct upload_stream *o) {
+    assert(o && o->connection);
+
+    pa_idxset_remove_by_data(o->connection->output_streams, o, NULL);
+
+    free(o->name);
+    
+    if (o->memchunk.memblock)
+        pa_memblock_unref(o->memchunk.memblock);
+    
+    free(o);
+}
+
+static struct record_stream* record_stream_new(struct connection *c, struct pa_source *source, const 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;
@@ -143,7 +207,7 @@ static struct record_stream* record_stream_new(struct connection *c, struct pa_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);
+    s->memblockq = pa_memblockq_new(maxlength, 0, base = pa_frame_size(ss), 0, 0);
     assert(s->memblockq);
 
     s->fragment_size = (fragment_size/base)*base;
@@ -163,7 +227,7 @@ static void record_stream_free(struct record_stream* r) {
     free(r);
 }
 
-static struct playback_stream* playback_stream_new(struct connection *c, struct pa_sink *sink, struct pa_sample_spec *ss, const char *name,
+static struct playback_stream* playback_stream_new(struct connection *c, struct pa_sink *sink, const struct pa_sample_spec *ss, const char *name,
                                                    size_t maxlength,
                                                    size_t tlength,
                                                    size_t prebuf,
@@ -177,6 +241,7 @@ static struct playback_stream* playback_stream_new(struct connection *c, struct
     
     s = malloc(sizeof(struct playback_stream));
     assert (s);
+    s->type = PLAYBACK_STREAM;
     s->connection = c;
     s->sink_input = sink_input;
     
@@ -188,13 +253,13 @@ static struct playback_stream* playback_stream_new(struct connection *c, struct
     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);
+    s->memblockq = pa_memblockq_new(maxlength, tlength, pa_frame_size(ss), prebuf, minreq);
     assert(s->memblockq);
 
     s->requested_bytes = 0;
     s->drain_request = 0;
     
-    pa_idxset_put(c->playback_streams, s, &s->index);
+    pa_idxset_put(c->output_streams, s, &s->index);
     return s;
 }
 
@@ -204,7 +269,7 @@ static void playback_stream_free(struct playback_stream* p) {
     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_idxset_remove_by_data(p->connection->output_streams, p, NULL);
     pa_sink_input_free(p->sink_input);
     pa_memblockq_free(p->memblockq);
     free(p);
@@ -212,7 +277,7 @@ static void playback_stream_free(struct playback_stream* p) {
 
 static void connection_free(struct connection *c) {
     struct record_stream *r;
-    struct playback_stream *p;
+    struct output_stream *o;
     assert(c && c->protocol);
 
     pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
@@ -220,9 +285,12 @@ static void connection_free(struct connection *c) {
         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);
+    while ((o = pa_idxset_first(c->output_streams, NULL)))
+        if (o->type == PLAYBACK_STREAM)
+            playback_stream_free((struct playback_stream*) o);
+        else
+            upload_stream_free((struct upload_stream*) o);
+    pa_idxset_free(c->output_streams, NULL, NULL);
 
     pa_pdispatch_free(c->pdispatch);
     pa_pstream_free(c->pstream);
@@ -351,7 +419,7 @@ static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i) {
     assert(i && i->userdata);
     s = i->userdata;
 
-    return pa_samples_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec);
+    return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec);
 }
 
 /*** source_output callbacks ***/
@@ -384,7 +452,7 @@ static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t com
     struct playback_stream *s;
     size_t maxlength, tlength, prebuf, minreq;
     uint32_t sink_index;
-    const char *name;
+    const char *name, *sink_name;
     struct pa_sample_spec ss;
     struct pa_tagstruct *reply;
     struct pa_sink *sink;
@@ -393,6 +461,7 @@ static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t com
     if (pa_tagstruct_gets(t, &name) < 0 ||
         pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
         pa_tagstruct_getu32(t, &sink_index) < 0 ||
+        pa_tagstruct_gets(t, &sink_name) < 0 ||
         pa_tagstruct_getu32(t, &maxlength) < 0 ||
         pa_tagstruct_getu32(t, &tlength) < 0 ||
         pa_tagstruct_getu32(t, &prebuf) < 0 ||
@@ -407,10 +476,12 @@ static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t com
         return;
     }
 
-    if (sink_index == (uint32_t) -1)
+    if (!*sink_name || sink_index == (uint32_t) -1)
         sink = pa_sink_get_default(c->protocol->core);
-    else
+    else if (sink_index != (uint32_t) -1)
         sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
+    else
+        sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK);
 
     if (!sink) {
         pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
@@ -433,10 +504,9 @@ static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t com
     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) {
+static void command_delete_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 ||
@@ -449,13 +519,34 @@ static void command_delete_playback_stream(struct pa_pdispatch *pd, uint32_t com
         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);
+    if (command == PA_COMMAND_DELETE_PLAYBACK_STREAM) {
+        struct playback_stream *s;
+        if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != PLAYBACK_STREAM)) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST);
+            return;
+        }
+
+        playback_stream_free(s);
+    } else if (command == PA_COMMAND_DELETE_RECORD_STREAM) {
+        struct record_stream *s;
+        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);
+    } else {
+        struct upload_stream *s;
+        assert(command == PA_COMMAND_DELETE_UPLOAD_STREAM);
+        if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != UPLOAD_STREAM)) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST);
+            return;
+        }
+
+        upload_stream_free(s);
+    }
+            
     pa_pstream_send_simple_ack(c->pstream, tag);
 }
 
@@ -464,7 +555,7 @@ static void command_create_record_stream(struct pa_pdispatch *pd, uint32_t comma
     struct record_stream *s;
     size_t maxlength, fragment_size;
     uint32_t source_index;
-    const char *name;
+    const char *name, *source_name;
     struct pa_sample_spec ss;
     struct pa_tagstruct *reply;
     struct pa_source *source;
@@ -473,6 +564,7 @@ static void command_create_record_stream(struct pa_pdispatch *pd, uint32_t comma
     if (pa_tagstruct_gets(t, &name) < 0 ||
         pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
         pa_tagstruct_getu32(t, &source_index) < 0 ||
+        pa_tagstruct_gets(t, &source_name) < 0 ||
         pa_tagstruct_getu32(t, &maxlength) < 0 ||
         pa_tagstruct_getu32(t, &fragment_size) < 0 ||
         !pa_tagstruct_eof(t)) {
@@ -485,10 +577,12 @@ static void command_create_record_stream(struct pa_pdispatch *pd, uint32_t comma
         return;
     }
 
-    if (source_index == (uint32_t) -1)
+    if (!*source_name || source_index == (uint32_t) -1)
         source = pa_source_get_default(c->protocol->core);
-    else
+    else if (source_index != (uint32_t) -1)
         source = pa_idxset_get_by_index(c->protocol->core->sources, source_index);
+    else
+        source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE);
 
     if (!source) {
         pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
@@ -510,32 +604,6 @@ static void command_create_record_stream(struct pa_pdispatch *pd, uint32_t comma
     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);
@@ -652,7 +720,7 @@ static void command_drain_playback_stream(struct pa_pdispatch *pd, uint32_t comm
         return;
     }
 
-    if (!(s = pa_idxset_get_by_index(c->playback_streams, index))) {
+    if (!(s = pa_idxset_get_by_index(c->output_streams, index)) || s->type != PLAYBACK_STREAM) {
         pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
         return;
     }
@@ -709,7 +777,7 @@ static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t comma
         return;
     }
 
-    if (!(s = pa_idxset_get_by_index(c->playback_streams, index))) {
+    if (!(s = pa_idxset_get_by_index(c->output_streams, index)) || s->type != PLAYBACK_STREAM) {
         pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
         return;
     }
@@ -723,6 +791,147 @@ static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t comma
     pa_pstream_send_tagstruct(c->pstream, reply);
 }
 
+static void command_create_upload_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+    struct connection *c = userdata;
+    struct upload_stream *s;
+    size_t length;
+    const char *name;
+    struct pa_sample_spec ss;
+    struct pa_tagstruct *reply;
+    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, &length) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    if (!c->authorized) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+        return;
+    }
+
+    if ((length % pa_frame_size(&ss)) != 0 || length <= 0 || !*name) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID);
+        return;
+    }
+    
+    if (!(s = upload_stream_new(c, &ss, name, length))) {
+        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);
+    pa_pstream_send_tagstruct(c->pstream, reply);
+    
+    reply = pa_tagstruct_new(NULL, 0);
+    assert(reply);
+    pa_tagstruct_putu32(reply, PA_COMMAND_REQUEST);
+    pa_tagstruct_putu32(reply, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(reply, s->index);
+    pa_tagstruct_putu32(reply, length);
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_finish_upload_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 upload_stream *s;
+    uint32_t index;
+    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->output_streams, channel)) || (s->type != UPLOAD_STREAM)) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST);
+        return;
+    }
+
+    pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->memchunk, &index);
+    pa_pstream_send_simple_ack(c->pstream, tag);
+    upload_stream_free(s);
+}
+
+static void command_play_sample(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+    struct connection *c = userdata;
+    uint32_t sink_index, volume;
+    struct pa_sink *sink;
+    const char *name, *sink_name;
+    assert(c && t);
+
+    if (pa_tagstruct_getu32(t, &sink_index) < 0 ||
+        pa_tagstruct_gets(t, &sink_name) < 0 ||
+        pa_tagstruct_getu32(t, &volume) < 0 ||
+        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 (!*sink_name && sink_index == (uint32_t) -1)
+        sink = pa_sink_get_default(c->protocol->core);
+    else if (sink_index != (uint32_t) -1)
+        sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
+    else
+        sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK);
+
+    if (!sink) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+        return;
+    }
+
+    if (pa_scache_play_item(c->protocol->core, name, sink, volume) < 0) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+        return;
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_remove_sample(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;
+    }
+
+    if (!c->authorized) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+        return;
+    }
+
+    if (pa_scache_remove_item(c->protocol->core, name) < 0) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+        return;
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
 /*** pstream callbacks ***/
 
 static void pstream_packet_callback(struct pa_pstream *p, struct pa_packet *packet, void *userdata) {
@@ -737,25 +946,55 @@ static void pstream_packet_callback(struct pa_pstream *p, struct pa_packet *pack
 
 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;
+    struct output_stream *stream;
     assert(p && chunk && userdata);
 
-    if (!(stream = pa_idxset_get_by_index(c->playback_streams, channel))) {
+    if (!(stream = pa_idxset_get_by_index(c->output_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);*/
+    if (stream->type == PLAYBACK_STREAM) {
+        struct playback_stream *p = (struct playback_stream*) stream;
+        if (chunk->length >= p->requested_bytes)
+            p->requested_bytes = 0;
+        else
+            p->requested_bytes -= chunk->length;
+        
+        pa_memblockq_push_align(p->memblockq, chunk, delta);
+        assert(p->sink_input);
+        pa_sink_notify(p->sink_input->sink);
+        /*fprintf(stderr, "Recieved %u bytes.\n", chunk->length);*/
+    } else {
+        struct upload_stream *u = (struct upload_stream*) stream;
+        size_t l;
+        assert(u->type == UPLOAD_STREAM);
+
+        if (!u->memchunk.memblock) {
+            if (u->length == chunk->length) {
+                u->memchunk = *chunk;
+                pa_memblock_ref(u->memchunk.memblock);
+                u->length = 0;
+                fprintf(stderr, "COPY\n");
+            } else {
+                u->memchunk.memblock = pa_memblock_new(u->length);
+                u->memchunk.index = u->memchunk.length = 0;
+            }
+        }
+        
+        assert(u->memchunk.memblock);
+        
+        l = u->length; 
+        if (l > chunk->length)
+            l = chunk->length;
+
+        if (l > 0) {
+            memcpy(u->memchunk.memblock->data + u->memchunk.index + u->memchunk.length, chunk->memblock->data+chunk->index, l);
+            u->memchunk.length += l;
+            u->length -= l;
+        }
+    }
 }
 
 static void pstream_die_callback(struct pa_pstream *p, void *userdata) {
@@ -811,8 +1050,8 @@ static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, vo
     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->output_streams = pa_idxset_new(NULL, NULL);
+    assert(c->record_streams && c->output_streams);
 
     c->rrobin_index = PA_IDXSET_INVALID;
 
index 3a52e311f312db258e2362952e0c83282af16dc9..037d4f9ae34320fcac9ff46e96cc7dbebbbc85a8 100644 (file)
@@ -221,7 +221,7 @@ static void sink_input_kill_cb(struct pa_sink_input *i) {
 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);
+    return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
 }
 
 /*** source_output callbacks ***/
@@ -319,7 +319,7 @@ static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, vo
         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);
+        c->input_memblockq = pa_memblockq_new(l, 0, pa_frame_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;
@@ -348,7 +348,7 @@ static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, vo
         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);
+        c->output_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&p->sample_spec), 0, 0);
         pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2);
     }
 
index 3076b776fc8a8b1ca90516d835b5a7154ccba702..7d576a16e2fbc2428bef48b929101ccccb64b3aa 100644 (file)
@@ -23,6 +23,7 @@
 #include <config.h>
 #endif
 
+#include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
 #include <netinet/in.h>
@@ -40,7 +41,7 @@ enum pa_pstream_descriptor_index {
 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)
+#define FRAME_SIZE_MAX (1024*500) /* half a megabyte */
 
 struct item_info {
     enum { PA_PSTREAM_ITEM_PACKET, PA_PSTREAM_ITEM_MEMBLOCK } type;
@@ -361,8 +362,10 @@ static void do_read(struct pa_pstream *p) {
         /* Reading of frame descriptor complete */
 
         /* Frame size too large */
-        if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) > FRAME_SIZE_MAX)
+        if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) > FRAME_SIZE_MAX) {
+            fprintf(stderr, "frame size too large\n");
             goto die;
+        }
         
         assert(!p->read.packet && !p->read.memblock);
 
index 320d7119536bca99fed70a2511768b4f99a32793..adf08e80cdf95158e715f67917c294cb5675e845 100644 (file)
@@ -75,8 +75,8 @@ struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const stru
     r->i_ss = *a;
     r->o_ss = *b;
 
-    r->i_sz = pa_sample_size(a);
-    r->o_sz = pa_sample_size(b);
+    r->i_sz = pa_frame_size(a);
+    r->o_sz = pa_frame_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);
index d608ce1bc105533ed0dc2d78377be07de58ed7f7..5b1cd626473bbbf33469f1e802bbf3483816fd47 100644 (file)
@@ -111,7 +111,7 @@ size_t pa_mix(struct pa_mix_info channels[], unsigned nchannels, void *data, siz
 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(c && spec && (c->length % pa_frame_size(spec) == 0));
     assert(spec->format == PA_SAMPLE_S16NE);
 
     if (volume == PA_VOLUME_NORM)
index 8179475dd2ce57deaa24ca23798338f655a8a42d..e4c4bd52637ec512fa606945768919f1f9f19a9c 100644 (file)
@@ -28,7 +28,7 @@
 
 #include "sample.h"
 
-size_t pa_sample_size(const struct pa_sample_spec *spec) {
+size_t pa_frame_size(const struct pa_sample_spec *spec) {
     assert(spec);
     size_t b = 1;
 
@@ -55,14 +55,13 @@ size_t pa_sample_size(const struct pa_sample_spec *spec) {
 
 size_t pa_bytes_per_second(const struct pa_sample_spec *spec) {
     assert(spec);
-    return spec->rate*pa_sample_size(spec);
+    return spec->rate*pa_frame_size(spec);
 }
 
-
-uint32_t pa_samples_usec(size_t length, const struct pa_sample_spec *spec) {
+uint32_t pa_bytes_to_usec(size_t length, const struct pa_sample_spec *spec) {
     assert(spec);
 
-    return (uint32_t) (((double) length /pa_sample_size(spec))/spec->rate*1000000);
+    return (uint32_t) (((double) length /pa_frame_size(spec))/spec->rate*1000000);
 }
 
 int pa_sample_spec_valid(const struct pa_sample_spec *spec) {
index 825441f287059842daa8555650e0bc58a7711d03..01a4efcf2abd60d9f040ffc3dbc275a6ae53dd74 100644 (file)
@@ -52,12 +52,11 @@ struct pa_sample_spec {
 };
 
 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);
+size_t pa_frame_size(const struct pa_sample_spec *spec);
+uint32_t pa_bytes_to_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);
 
index f1f7ec5af037d7100774cf3fea3dd3b4b1898cae..21af0e221a3680efb18aef2381bd1df84602740b 100644 (file)
@@ -6,6 +6,8 @@
 #include "scache.h"
 #include "sink-input.h"
 #include "mainloop.h"
+#include "sample-util.h"
+#include "play-memchunk.h"
 
 static void free_entry(struct pa_scache_entry *e) {
     assert(e);
@@ -100,50 +102,8 @@ void pa_scache_free(struct pa_core *c) {
     }
 }
 
-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 si_kill(void *i) {
-    sink_input_kill(i);
-}
-
-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)
-        pa_mainloop_api_once(i->sink->core->mainloop, si_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)))
@@ -151,20 +111,10 @@ int pa_scache_play_item(struct pa_core *c, const char *name, struct pa_sink *sin
 
     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);
 
+    if (pa_play_memchunk(sink, name, &e->sample_spec, &e->memchunk, pa_volume_multiply(volume, e->volume)) < 0)
+        return -1;
+    
     return 0;
 }
 
index 20fa76a68720864a03b62aa64e19f0975a46029a..c2c873c6dfa9df6de9cd0222deeb5f9f7de63022 100644 (file)
@@ -41,7 +41,7 @@ struct pa_sink* pa_sink_new(struct pa_core *core, const char *name, int fail, co
     char *n = NULL;
     char st[256];
     int r;
-    assert(core && name && spec);
+    assert(core && name && *name && spec);
 
     s = malloc(sizeof(struct pa_sink));
     assert(s);
index ccde0e2f9c3dfda3ed24c1e50bcb3c1f160302c0..13635414515cd7222e84b2aa5d9756176e30cf7b 100644 (file)
@@ -36,7 +36,7 @@ struct pa_source* pa_source_new(struct pa_core *core, const char *name, int fail
     struct pa_source *s;
     char st[256];
     int r;
-    assert(core && spec && name);
+    assert(core && spec && name && *name);
 
     s = malloc(sizeof(struct pa_source));
     assert(s);