]> code.delx.au - pulseaudio/commitdiff
oss output works
authorLennart Poettering <lennart@poettering.net>
Tue, 15 Jun 2004 00:29:01 +0000 (00:29 +0000)
committerLennart Poettering <lennart@poettering.net>
Tue, 15 Jun 2004 00:29:01 +0000 (00:29 +0000)
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@15 fefdeb5f-60dc-0310-8127-8f9354f1896f

12 files changed:
src/Makefile.am
src/inputstream.c
src/inputstream.h
src/main.c
src/memblockq.c
src/memblockq.h
src/module-oss.c
src/outputstream.c
src/protocol-simple.c
src/sink.c
src/sink.h
src/todo [new file with mode: 0644]

index d249964bb107820acd34fd771399f48a4e672c2b..f09538462841478f2656010c3ce0ee639095e537 100644 (file)
@@ -20,8 +20,9 @@ AM_CFLAGS=-ansi -D_GNU_SOURCE
 
 bin_PROGRAMS = polypaudio 
 
-pkglib_LTLIBRARIES=libprotocol-simple.la protocol-simple-tcp.la \
-               libsocket-server.la sink-pipe.la libpstream.la libiochannel.la libpacket.la
+pkglib_LTLIBRARIES=libprotocol-simple.la module-simple-protocol-tcp.la \
+               libsocket-server.la module-pipe-sink.la libpstream.la libiochannel.la \
+               libpacket.la module-oss.la
 
 polypaudio_SOURCES = idxset.c queue.c strbuf.c mainloop.c  \
                memblock.c sample.c memblockq.c client.c \
@@ -49,14 +50,14 @@ libiochannel_la_LDFLAGS = -avoid-version
 libpacket_la_SOURCES = packet.c
 libpacket_la_LDFLAGS = -avoid-version
 
-protocol_simple_tcp_la_SOURCES = protocol-simple-tcp.c
-protocol_simple_tcp_la_LDFLAGS = -module -avoid-version
-protocol_simple_tcp_la_LIBADD = libprotocol-simple.la libiochannel.la
+module_simple_protocol_tcp_la_SOURCES = module-simple-protocol-tcp.c
+module_simple_protocol_tcp_la_LDFLAGS = -module -avoid-version
+module_simple_protocol_tcp_la_LIBADD = libprotocol-simple.la libiochannel.la
 
-sink_pipe_la_SOURCES = sink-pipe.c
-sink_pipe_la_LDFLAGS = -module -avoid-version
-sink_pipe_la_LIBADD = libiochannel.la
+module_pipe_sink_la_SOURCES = module-pipe-sink.c
+module_pipe_sink_la_LDFLAGS = -module -avoid-version
+module_pipe_sink_la_LIBADD = libiochannel.la
 
-sink_oss_la_SOURCES = sink-pipe.c
-sink_oss_la_LDFLAGS = -module -avoid-version
-sink_oss_la_LIBADD = libiochannel.la
+module_oss_la_SOURCES = module-oss.c
+module_oss_la_LDFLAGS = -module -avoid-version
+module_oss_la_LIBADD = libiochannel.la
index 8171928867c48f58a610371afb9f6d595887b5b5..7ece3b5c99191f6a69eff28b0a5c8a24ddb62c9f 100644 (file)
@@ -14,10 +14,13 @@ struct input_stream* input_stream_new(struct sink *s, struct sample_spec *spec,
     i->name = name ? strdup(name) : NULL;
     i->sink = s;
     i->spec = *spec;
+
     i->kill = NULL;
     i->kill_userdata = NULL;
+    i->notify = NULL;
+    i->notify_userdata = NULL;
 
-    i->memblockq = memblockq_new(bytes_per_second(spec)*5, sample_size(spec));
+    i->memblockq = memblockq_new(bytes_per_second(spec)*5, sample_size(spec), (size_t) -1);
     assert(i->memblockq);
     
     assert(s->core);
@@ -45,7 +48,7 @@ void input_stream_free(struct input_stream* i) {
 void input_stream_notify_sink(struct input_stream *i) {
     assert(i);
 
-    if (memblockq_is_empty(i->memblockq))
+    if (!memblockq_is_readable(i->memblockq))
         return;
     
     sink_notify(i->sink);
@@ -64,3 +67,16 @@ void input_stream_kill(struct input_stream*i) {
     if (i->kill)
         i->kill(i, i->kill_userdata);
 }
+
+void input_stream_set_notify_callback(struct input_stream *i, void (*notify)(struct input_stream*i, void *userdata), void *userdata) {
+    assert(i && notify);
+
+    i->notify = notify;
+    i->notify_userdata = userdata;
+}
+
+void input_stream_notify(struct input_stream *i) {
+    assert(i);
+    if (i->notify)
+        i->notify(i, i->notify_userdata);
+}
index a258c3d145fad105241312a710b3d89daca21441..544c3318839d3bbed2954a7ae99dbd7ab00db49f 100644 (file)
@@ -18,6 +18,9 @@ struct input_stream {
 
     void (*kill)(struct input_stream* i, void *userdata);
     void *kill_userdata;
+
+    void (*notify)(struct input_stream*i, void *userdata);
+    void *notify_userdata;
 };
 
 struct input_stream* input_stream_new(struct sink *s, struct sample_spec *spec, const char *name);
@@ -31,10 +34,17 @@ void input_stream_notify_sink(struct input_stream *i);
 /* The registrant of the input stream should call this function to set a
  * callback function which is called when destruction of the input stream is
  * requested */
-void input_stream_set_kill_callback(struct input_stream *c, void (*kill)(struct input_stream*i, void *userdata), void *userdata);
+void input_stream_set_kill_callback(struct input_stream *i, void (*kill)(struct input_stream*i, void *userdata), void *userdata);
 
 /* Code that didn't create the input stream should call this function to
  * request destruction of it */
-void input_stream_kill(struct input_stream *c);
+void input_stream_kill(struct input_stream *i);
+
+/* Notify the code that created this input stream that some data has
+ * been removed from the memblockq */
+void input_stream_set_notify_callback(struct input_stream *i, void (*notify)(struct input_stream*i, void *userdata), void *userdata);
+
+void input_stream_notify(struct input_stream *i);
+
 
 #endif
index 436dd30bacc9a6910469c8950081b0877636e985..d54bee0a5437d91003e32c98f0d87780a7aea677 100644 (file)
@@ -29,8 +29,9 @@ int main(int argc, char *argv[]) {
     mainloop_source_new_signal(m, SIGINT, signal_callback, NULL);
     signal(SIGPIPE, SIG_IGN);
 
-    module_load(c, "sink-pipe", NULL);
-    module_load(c, "protocol-simple-tcp", NULL);
+    module_load(c, "module-oss", NULL);
+    module_load(c, "module-pipe-sink", NULL);
+    module_load(c, "module-simple-protocol-tcp", NULL);
     
     mainloop_run(m);
     
index 6437dd5b38a7c25473fd416bdcc70a0a2a9fc2c2..b25adc65496a6e4371b9e4b0e1ec2132ba855eba 100644 (file)
@@ -1,3 +1,4 @@
+#include <stdio.h>
 #include <assert.h>
 #include <stdlib.h>
 
@@ -14,9 +15,10 @@ struct memblockq {
     size_t total_length;
     size_t maxlength;
     size_t base;
+    size_t prebuf;
 };
 
-struct memblockq* memblockq_new(size_t maxlength, size_t base) {
+struct memblockq* memblockq_new(size_t maxlength, size_t base, size_t prebuf) {
     struct memblockq* bq;
     assert(maxlength && base);
     
@@ -27,6 +29,11 @@ struct memblockq* memblockq_new(size_t maxlength, size_t base) {
     bq->total_length = 0;
     bq->base = base;
     bq->maxlength = ((maxlength+base-1)/base)*base;
+    bq->prebuf = prebuf == (size_t) -1 ? bq->maxlength/2 : prebuf;
+    
+    if (bq->prebuf > bq->maxlength)
+        bq->prebuf = bq->maxlength;
+    
     assert(bq->maxlength >= base);
     return bq;
 }
@@ -72,9 +79,11 @@ void memblockq_push(struct memblockq* bq, struct memchunk *chunk, size_t delta)
 int memblockq_peek(struct memblockq* bq, struct memchunk *chunk) {
     assert(bq && chunk);
 
-    if (!bq->blocks)
+    if (!bq->blocks || bq->total_length < bq->prebuf)
         return -1;
 
+    bq->prebuf = 0;
+
     *chunk = bq->blocks->chunk;
     memblock_ref(chunk->memblock);
     return 0;
@@ -85,9 +94,11 @@ int memblockq_pop(struct memblockq* bq, struct memchunk *chunk) {
     
     assert(bq && chunk);
 
-    if (!bq->blocks)
+    if (!bq->blocks || bq->total_length < bq->prebuf)
         return -1;
 
+    bq->prebuf = 0;
+
     q = bq->blocks;
     bq->blocks = bq->blocks->next;
 
@@ -138,6 +149,8 @@ void memblockq_shorten(struct memblockq *bq, size_t length) {
     if (bq->total_length <= length)
         return;
 
+    fprintf(stderr, "Warning! memblockq_shorten()\n");
+    
     l = bq->total_length - length;
     l /= bq->base;
     l *= bq->base;
@@ -151,8 +164,15 @@ void memblockq_empty(struct memblockq *bq) {
     memblockq_shorten(bq, 0);
 }
 
-int memblockq_is_empty(struct memblockq *bq) {
+int memblockq_is_readable(struct memblockq *bq) {
+    assert(bq);
+
+    return bq->total_length >= bq->prebuf;
+}
+
+int memblockq_is_writable(struct memblockq *bq, size_t length) {
     assert(bq);
 
-    return bq->total_length < bq->base;
+    assert(length <= bq->maxlength);
+    return bq->total_length + length <= bq->maxlength;
 }
index 75c5e59eeffd46584d1e1d7703f756a9a80058cc..48050d49df1b944887957327687cafed993e4370 100644 (file)
@@ -7,7 +7,7 @@
 
 struct memblockq;
 
-struct memblockq* memblockq_new(size_t maxlength, size_t base);
+struct memblockq* memblockq_new(size_t maxlength, size_t base, size_t prebuf);
 void memblockq_free(struct memblockq* bq);
 
 void memblockq_push(struct memblockq* bq, struct memchunk *chunk, size_t delta);
@@ -19,6 +19,7 @@ void memblockq_drop(struct memblockq *bq, size_t length);
 void memblockq_shorten(struct memblockq *bq, size_t length);
 void memblockq_empty(struct memblockq *bq);
 
-int memblockq_is_empty(struct memblockq *bq);
+int memblockq_is_readable(struct memblockq *bq);
+int memblockq_is_writable(struct memblockq *bq, size_t length);
 
 #endif
index b2b106b41eb2de751a15242d4b47a994f9e3d41d..7a1482e7f847e5a07c69015deaa42fbd998d1f6f 100644 (file)
@@ -1,4 +1,5 @@
-#include <soundcard.h>
+#include <sys/soundcard.h>
+#include <sys/ioctl.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <stdio.h>
@@ -22,7 +23,7 @@ struct userdata {
 
     struct memchunk memchunk, silence;
 
-    uint32_t fragment_size;
+    uint32_t in_fragment_size, out_fragment_size, sample_size;
 };
 
 static void do_write(struct userdata *u) {
@@ -34,7 +35,7 @@ static void do_write(struct userdata *u) {
         return;
 
     if (!u->memchunk.length) {
-        if (sink_render(u->sink, fragment_size, &u->memchunk) < 0)
+        if (sink_render(u->sink, u->out_fragment_size, &u->memchunk) < 0)
             memchunk = &u->silence;
         else
             memchunk = &u->memchunk;
@@ -68,15 +69,15 @@ static void do_read(struct userdata *u) {
     if (!iochannel_is_readable(u->io))
         return;
 
-    memchunk.memblock = memblock_new(u->fragment_size);
+    memchunk.memblock = memblock_new(u->in_fragment_size);
     assert(memchunk.memblock);
-    if ((r = iochannel_read(u->io, memchunk.memblock->data, memchunk->memblock->length)) < 0) {
+    if ((r = iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) {
         memblock_unref(memchunk.memblock);
         fprintf(stderr, "read() failed: %s\n", strerror(errno));
         return;
     }
 
-    assert(r < memchunk->memblock->length);
+    assert(r <= memchunk.memblock->length);
     memchunk.length = memchunk.memblock->length = r;
     memchunk.index = 0;
 
@@ -92,17 +93,17 @@ static void io_callback(struct iochannel *io, void*userdata) {
 }
 
 int module_init(struct core *c, struct module*m) {
+    struct audio_buf_info info;
     struct userdata *u = NULL;
-    struct stat st;
     char *p;
     int fd = -1;
-    int format, channels, speed, frag_size;
-    int m;
-    const static struct sample_spec ss;
+    int format, channels, speed, frag_size, in_frag_size, out_frag_size;
+    int mode;
+    struct sample_spec ss;
     assert(c && m);
 
     p = m->argument ? m->argument : "/dev/dsp";
-    if ((fd = open(p, m = O_RDWR)) >= 0) {
+    if ((fd = open(p, mode = O_RDWR)) >= 0) {
         int caps;
 
         ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
@@ -119,16 +120,16 @@ int module_init(struct core *c, struct module*m) {
     }
 
     if (fd < 0) {
-        if ((fd = open(p, m = O_WRONLY)) < 0) {
-            if ((fd = open(p, m = O_RDONLY)) < 0) {
+        if ((fd = open(p, mode = O_WRONLY)) < 0) {
+            if ((fd = open(p, mode = O_RDONLY)) < 0) {
                 fprintf(stderr, "open('%s'): %s\n", p, strerror(errno));
                 goto fail;
             }
         }
     }
-
-    frags = 0x7fff0000 | (10 << 16); /* 1024 bytes fragment size */
-    if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frags) < 0) {
+    
+    frag_size = ((int) 0x7ffff << 4) | 10; /* nfrags = 4; frag_size = 2^10 */
+    if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag_size) < 0) {
         fprintf(stderr, "SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno));
         goto fail;
     }
@@ -165,25 +166,35 @@ int module_init(struct core *c, struct module*m) {
     assert(speed);
     ss.rate = speed;
 
-    if (ioctl(fd, SNCTL_DSP_GETBLKSIZE, &frag_size) < 0) {
+    if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) {
         fprintf(stderr, "SNDCTL_DSP_GETBLKSIZE: %s\n", strerror(errno));
         goto fail;
     }
-
     assert(frag_size);
-    
+    in_frag_size = out_frag_size = frag_size;
+
+    if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {
+        fprintf(stderr, "INPUT: %u fragments of size %u.\n", info.fragstotal, info.fragsize);
+        in_frag_size = info.fragsize;
+    }
+
+    if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
+        fprintf(stderr, "OUTUT: %u fragments of size %u.\n", info.fragstotal, info.fragsize);
+        out_frag_size = info.fragsize;
+    }
+
     u = malloc(sizeof(struct userdata));
     assert(u);
 
     u->core = c;
 
-    if (m != O_RDONLY) {
+    if (mode != O_RDONLY) {
         u->sink = sink_new(c, "dsp", &ss);
         assert(u->sink);
     } else
         u->sink = NULL;
 
-    if (m != O_WRONLY) {
+    if (mode != O_WRONLY) {
         u->source = source_new(c, "dsp", &ss);
         assert(u->source);
     } else
@@ -197,11 +208,13 @@ int module_init(struct core *c, struct module*m) {
 
     u->memchunk.memblock = NULL;
     u->memchunk.length = 0;
+    u->sample_size = sample_size(&ss);
 
-    u->fragment_size = frag_size;
-    u->silence.memblock = memblock_new(u->fragment_size);
-    assert(u->silence);
-    u->silence.length = u->fragment_size;
+    u->out_fragment_size = out_frag_size;
+    u->in_fragment_size = in_frag_size;
+    u->silence.memblock = memblock_new(u->silence.length = u->out_fragment_size);
+    assert(u->silence.memblock);
+    silence(u->silence.memblock, &ss);
     u->silence.index = 0;
     
     m->userdata = u;
index c6681d29382ceb03034d6f9c41d08f5738da756f..c3f68a0ee7c880ebe47fac5327ea022185381622 100644 (file)
@@ -17,7 +17,7 @@ struct output_stream* output_stream_new(struct source *s, struct sample_spec *sp
     o->kill = NULL;
     o->kill_userdata = NULL;
 
-    o->memblockq = memblockq_new(bytes_per_second(spec)*5, sample_size(spec));
+    o->memblockq = memblockq_new(bytes_per_second(spec)*5, sample_size(spec), (size_t) -1);
     assert(o->memblockq);
     
     assert(s->core);
index ec121faa13409cc0f0e9965ba7b3b56e37f9aa36..1c462b397bed2639dffdcd1a914b4e7536609abd 100644 (file)
@@ -66,53 +66,73 @@ static void client_kill_cb(struct client *client, void*userdata) {
     destroy_connection(c);
 }
 
-static void io_callback(struct iochannel*io, void *userdata) {
-    struct connection *c = userdata;
-    assert(io && c);
-
-    if (c->istream && iochannel_is_readable(io)) {
-        struct memchunk chunk;
-        ssize_t r;
-
-        chunk.memblock = memblock_new(BUFSIZE);
-        assert(chunk.memblock);
+static int do_read(struct connection *c) {
+    struct memchunk chunk;
+    ssize_t r;
 
-        if ((r = iochannel_read(io, chunk.memblock->data, BUFSIZE)) <= 0) {
-            fprintf(stderr, "read(): %s\n", r == 0 ? "EOF" : strerror(errno));
-            memblock_unref(chunk.memblock);
-            goto fail;
-        }
-        
-        chunk.memblock->length = r;
-        chunk.length = r;
-        chunk.index = 0;
-        
-        memblockq_push(c->istream->memblockq, &chunk, 0);
-        input_stream_notify_sink(c->istream);
+    if (!iochannel_is_readable(c->io))
+        return 0;
+    
+    if (!c->istream || !memblockq_is_writable(c->istream->memblockq, BUFSIZE))
+        return 0;
+    
+    chunk.memblock = memblock_new(BUFSIZE);
+    assert(chunk.memblock);
+    
+    if ((r = iochannel_read(c->io, chunk.memblock->data, BUFSIZE)) <= 0) {
+        fprintf(stderr, "read(): %s\n", r == 0 ? "EOF" : strerror(errno));
         memblock_unref(chunk.memblock);
+        return -1;
     }
+    
+    chunk.memblock->length = r;
+    chunk.length = r;
+    chunk.index = 0;
+    
+    memblockq_push(c->istream->memblockq, &chunk, 0);
+    input_stream_notify_sink(c->istream);
+    memblock_unref(chunk.memblock);
+    return 0;
+}
 
-    if (c->ostream && iochannel_is_writable(io)) {
-        struct memchunk chunk;
-        ssize_t r;
+static int do_write(struct connection *c) {
+    struct memchunk chunk;
+    ssize_t r;
 
-        memblockq_peek(c->ostream->memblockq, &chunk);
-        assert(chunk.memblock && chunk.length);
+    if (!iochannel_is_writable(c->io))
+        return 0;
+    
+    if (!c->ostream)
+        return 0;    
 
-        if ((r = iochannel_write(io, chunk.memblock->data+chunk.index, chunk.length)) < 0) {
-            fprintf(stderr, "write(): %s\n", strerror(errno));
-            memblock_unref(chunk.memblock);
-            goto fail;
-        }
-        
-        memblockq_drop(c->ostream->memblockq, r);
+    memblockq_peek(c->ostream->memblockq, &chunk);
+    assert(chunk.memblock && chunk.length);
+    
+    if ((r = iochannel_write(c->io, chunk.memblock->data+chunk.index, chunk.length)) < 0) {
+        fprintf(stderr, "write(): %s\n", strerror(errno));
         memblock_unref(chunk.memblock);
+        return -1;
     }
+    
+    memblockq_drop(c->ostream->memblockq, r);
+    memblock_unref(chunk.memblock);
+    return 0;
+}
 
-    return;
+static void io_callback(struct iochannel*io, void *userdata) {
+    struct connection *c = userdata;
+    assert(io && c && c->io == io);
+
+    if (do_read(c) < 0 || do_write(c) < 0)
+        destroy_connection(c);
+}
+
+static void istream_notify_cb(struct input_stream *i, void *userdata) {
+    struct connection*c = userdata;
+    assert(i && c && c->istream == i);
     
-fail:
-    destroy_connection(c);
+    if (do_read(c) < 0)
+        destroy_connection(c);
 }
 
 static void on_connection(struct socket_server*s, struct iochannel *io, void *userdata) {
@@ -155,6 +175,7 @@ static void on_connection(struct socket_server*s, struct iochannel *io, void *us
         c->istream = input_stream_new(sink, &DEFAULT_SAMPLE_SPEC, c->client->name);
         assert(c->istream);
         input_stream_set_kill_callback(c->istream, istream_kill_cb, c);
+        input_stream_set_notify_callback(c->istream, istream_notify_cb, c);
     }
 
 
index dfe1bcb9804546b2f70e84911d8edeea7a5cada1..f2d5373d970b0f53946b99ade8b59ef49a6b64de 100644 (file)
@@ -33,8 +33,8 @@ struct sink* sink_new(struct core *core, const char *name, const struct sample_s
     
     s->volume = 0xFF;
 
-    s->notify_callback = NULL;
-    s->userdata = NULL;
+    s->notify = NULL;
+    s->notify_userdata = NULL;
 
     return s;
 }
@@ -103,6 +103,10 @@ static int do_mix(void *p, uint32_t index, int *del, void*userdata) {
     assert(chunk.length && chunk.length <= info->chunk->memblock->length - info->chunk->index);
 
     add_clip(info->chunk, &chunk, info->spec);
+    memblock_unref(chunk.memblock);
+    memblockq_drop(i->memblockq, info->chunk->length);
+
+    input_stream_notify(i);
     return 0;
 }
 
@@ -139,6 +143,8 @@ int sink_render_into(struct sink*s, struct memblock *target, struct memchunk *re
         target->length = l;
         memblock_unref(chunk.memblock);
         memblockq_drop(i->memblockq, l);
+
+        input_stream_notify(i);
         
         result->memblock = target;
         result->length = l;
@@ -191,6 +197,8 @@ int sink_render(struct sink*s, size_t length, struct memchunk *result) {
         l = length < result->length ? length : result->length;
         result->length = l;
         memblockq_drop(i->memblockq, l);
+        input_stream_notify(i);
+        
         return 0;
     }
 
@@ -211,15 +219,15 @@ int sink_render(struct sink*s, size_t length, struct memchunk *result) {
 void sink_notify(struct sink*s) {
     assert(s);
 
-    if (s->notify_callback)
-        s->notify_callback(s, s->userdata);
+    if (s->notify)
+        s->notify(s, s->notify_userdata);
 }
 
 void sink_set_notify_callback(struct sink *s, void (*notify_callback)(struct sink*sink, void *userdata), void *userdata) {
     assert(s && notify_callback);
 
-    s->notify_callback = notify_callback;
-    s->userdata = userdata;
+    s->notify = notify_callback;
+    s->notify_userdata = userdata;
 }
 
 
index a6f98005ac8121c3c0f8426b0f57bc95ba97829d..1678fc75ee61fac2b74200ecabcb1b2d6eb92ee4 100644 (file)
@@ -22,8 +22,8 @@ struct sink {
 
     uint8_t volume;
 
-    void (*notify_callback)(struct sink*sink, void *userdata);
-    void *userdata;
+    void (*notify)(struct sink*sink, void *userdata);
+    void *notify_userdata;
 };
 
 struct sink* sink_new(struct core *core, const char *name, const struct sample_spec *spec);
diff --git a/src/todo b/src/todo
new file mode 100644 (file)
index 0000000..d4cc5f1
--- /dev/null
+++ b/src/todo
@@ -0,0 +1,16 @@
+- mixing
+- resampling
+- native protocol/library
+- oss/mmap
+- esound prodocol
+- config-parser
+-- 0.1
+- future cancellation
+- client-ui
+
+drivers:
+- libao
+- xmms
+- portaudio
+- mplayer
+- python