mirror of
				https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
				synced 2025-11-03 09:01:50 -05:00 
			
		
		
		
	got mmap oss output working
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@20 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
		
							parent
							
								
									a8f788111c
								
							
						
					
					
						commit
						4b86ff0e6c
					
				
					 17 changed files with 607 additions and 112 deletions
				
			
		| 
						 | 
					@ -22,7 +22,7 @@ bin_PROGRAMS = polypaudio
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pkglib_LTLIBRARIES=libprotocol-simple.la module-simple-protocol-tcp.la \
 | 
					pkglib_LTLIBRARIES=libprotocol-simple.la module-simple-protocol-tcp.la \
 | 
				
			||||||
		libsocket-server.la module-pipe-sink.la libpstream.la libiochannel.la \
 | 
							libsocket-server.la module-pipe-sink.la libpstream.la libiochannel.la \
 | 
				
			||||||
		libpacket.la module-oss.la
 | 
							libpacket.la module-oss.la module-oss-mmap.la liboss.la
 | 
				
			||||||
 | 
					
 | 
				
			||||||
polypaudio_SOURCES = idxset.c queue.c strbuf.c mainloop.c  \
 | 
					polypaudio_SOURCES = idxset.c queue.c strbuf.c mainloop.c  \
 | 
				
			||||||
		memblock.c sample.c memblockq.c client.c \
 | 
							memblock.c sample.c memblockq.c client.c \
 | 
				
			||||||
| 
						 | 
					@ -50,6 +50,9 @@ libiochannel_la_LDFLAGS = -avoid-version
 | 
				
			||||||
libpacket_la_SOURCES = packet.c
 | 
					libpacket_la_SOURCES = packet.c
 | 
				
			||||||
libpacket_la_LDFLAGS = -avoid-version
 | 
					libpacket_la_LDFLAGS = -avoid-version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					liboss_la_SOURCES = oss.c
 | 
				
			||||||
 | 
					liboss_la_LDFLAGS = -avoid-version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module_simple_protocol_tcp_la_SOURCES = module-simple-protocol-tcp.c
 | 
					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_LDFLAGS = -module -avoid-version
 | 
				
			||||||
module_simple_protocol_tcp_la_LIBADD = libprotocol-simple.la libiochannel.la
 | 
					module_simple_protocol_tcp_la_LIBADD = libprotocol-simple.la libiochannel.la
 | 
				
			||||||
| 
						 | 
					@ -60,4 +63,8 @@ module_pipe_sink_la_LIBADD = libiochannel.la
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module_oss_la_SOURCES = module-oss.c
 | 
					module_oss_la_SOURCES = module-oss.c
 | 
				
			||||||
module_oss_la_LDFLAGS = -module -avoid-version
 | 
					module_oss_la_LDFLAGS = -module -avoid-version
 | 
				
			||||||
module_oss_la_LIBADD = libiochannel.la
 | 
					module_oss_la_LIBADD = libiochannel.la liboss.la
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module_oss_mmap_la_SOURCES = module-oss-mmap.c
 | 
				
			||||||
 | 
					module_oss_mmap_la_LDFLAGS = -module -avoid-version
 | 
				
			||||||
 | 
					module_oss_mmap_la_LIBADD = libiochannel.la liboss.la
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,11 +30,10 @@ int main(int argc, char *argv[]) {
 | 
				
			||||||
    mainloop_source_new_signal(m, SIGINT, signal_callback, NULL);
 | 
					    mainloop_source_new_signal(m, SIGINT, signal_callback, NULL);
 | 
				
			||||||
    signal(SIGPIPE, SIG_IGN);
 | 
					    signal(SIGPIPE, SIG_IGN);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    module_load(c, "module-oss", "/dev/dsp1");
 | 
					    module_load(c, "module-oss-mmap", "/dev/dsp1");
 | 
				
			||||||
    module_load(c, "module-pipe-sink", NULL);
 | 
					    module_load(c, "module-pipe-sink", NULL);
 | 
				
			||||||
    module_load(c, "module-simple-protocol-tcp", NULL);
 | 
					    module_load(c, "module-simple-protocol-tcp", NULL);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					 | 
				
			||||||
    fprintf(stderr, "main: mainloop entry.\n");
 | 
					    fprintf(stderr, "main: mainloop entry.\n");
 | 
				
			||||||
    while (mainloop_iterate(m, 1) == 0);
 | 
					    while (mainloop_iterate(m, 1) == 0);
 | 
				
			||||||
/*        fprintf(stderr, "main: %u blocks\n", n_blocks);*/
 | 
					/*        fprintf(stderr, "main: %u blocks\n", n_blocks);*/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,8 +2,6 @@
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <assert.h>
 | 
					#include <assert.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
#include <sys/time.h>
 | 
					 | 
				
			||||||
#include <time.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "memblock.h"
 | 
					#include "memblock.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,7 +14,6 @@ struct memblock *memblock_new(size_t length) {
 | 
				
			||||||
    b->length = length;
 | 
					    b->length = length;
 | 
				
			||||||
    b->data = b+1;
 | 
					    b->data = b+1;
 | 
				
			||||||
    n_blocks++;
 | 
					    n_blocks++;
 | 
				
			||||||
    timerclear(&b->stamp);
 | 
					 | 
				
			||||||
    return b;
 | 
					    return b;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +24,6 @@ struct memblock *memblock_new_fixed(void *d, size_t length) {
 | 
				
			||||||
    b->length = length;
 | 
					    b->length = length;
 | 
				
			||||||
    b->data = d;
 | 
					    b->data = d;
 | 
				
			||||||
    n_blocks++;
 | 
					    n_blocks++;
 | 
				
			||||||
    timerclear(&b->stamp);
 | 
					 | 
				
			||||||
    return b;
 | 
					    return b;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,7 +34,6 @@ struct memblock *memblock_new_dynamic(void *d, size_t length) {
 | 
				
			||||||
    b->length = length;
 | 
					    b->length = length;
 | 
				
			||||||
    b->data = d;
 | 
					    b->data = d;
 | 
				
			||||||
    n_blocks++;
 | 
					    n_blocks++;
 | 
				
			||||||
    timerclear(&b->stamp);
 | 
					 | 
				
			||||||
    return b;
 | 
					    return b;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -77,29 +72,3 @@ void memblock_unref_fixed(struct memblock *b) {
 | 
				
			||||||
    b->type = MEMBLOCK_DYNAMIC;
 | 
					    b->type = MEMBLOCK_DYNAMIC;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void memblock_stamp(struct memblock*b) {
 | 
					 | 
				
			||||||
    assert(b);
 | 
					 | 
				
			||||||
    gettimeofday(&b->stamp, NULL);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
uint32_t memblock_age(struct memblock*b) {
 | 
					 | 
				
			||||||
    assert(b);
 | 
					 | 
				
			||||||
    struct timeval tv;
 | 
					 | 
				
			||||||
    uint32_t r;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (b->stamp.tv_sec == 0)
 | 
					 | 
				
			||||||
        return (suseconds_t) -1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    gettimeofday(&tv, NULL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /*fprintf(stderr, "memblock: (%lu,%lu) -- (%lu,%lu)\r", b->stamp.tv_sec, b->stamp.tv_usec, tv.tv_sec, tv.tv_usec);*/
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    r = (tv.tv_sec-b->stamp.tv_sec) * 1000000;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (tv.tv_usec >= b->stamp.tv_usec)
 | 
					 | 
				
			||||||
        r += tv.tv_usec - b->stamp.tv_usec;
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
        r -= b->stamp.tv_usec - tv.tv_usec;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return r;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,6 @@ struct memblock {
 | 
				
			||||||
    unsigned ref;
 | 
					    unsigned ref;
 | 
				
			||||||
    size_t length;
 | 
					    size_t length;
 | 
				
			||||||
    void *data;
 | 
					    void *data;
 | 
				
			||||||
    struct timeval stamp;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct memchunk {
 | 
					struct memchunk {
 | 
				
			||||||
| 
						 | 
					@ -28,9 +27,6 @@ struct memblock* memblock_ref(struct memblock*b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void memblock_unref_fixed(struct memblock*b);
 | 
					void memblock_unref_fixed(struct memblock*b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void memblock_stamp(struct memblock*b);
 | 
					 | 
				
			||||||
uint32_t memblock_age(struct memblock*b);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define memblock_assert_exclusive(b) assert((b)->ref == 1)
 | 
					#define memblock_assert_exclusive(b) assert((b)->ref == 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern unsigned n_blocks;
 | 
					extern unsigned n_blocks;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,5 @@
 | 
				
			||||||
 | 
					#include <sys/time.h>
 | 
				
			||||||
 | 
					#include <time.h>
 | 
				
			||||||
#include <stdio.h>
 | 
					#include <stdio.h>
 | 
				
			||||||
#include <assert.h>
 | 
					#include <assert.h>
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
| 
						 | 
					@ -7,18 +9,17 @@
 | 
				
			||||||
struct memblock_list {
 | 
					struct memblock_list {
 | 
				
			||||||
    struct memblock_list *next;
 | 
					    struct memblock_list *next;
 | 
				
			||||||
    struct memchunk chunk;
 | 
					    struct memchunk chunk;
 | 
				
			||||||
 | 
					    struct timeval stamp;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct memblockq {
 | 
					struct memblockq {
 | 
				
			||||||
    struct memblock_list *blocks, *blocks_tail;
 | 
					    struct memblock_list *blocks, *blocks_tail;
 | 
				
			||||||
    unsigned n_blocks;
 | 
					    unsigned n_blocks;
 | 
				
			||||||
    size_t total_length;
 | 
					    size_t total_length, maxlength, base, prebuf;
 | 
				
			||||||
    size_t maxlength;
 | 
					    int measure_latency;
 | 
				
			||||||
    size_t base;
 | 
					    uint32_t latency;
 | 
				
			||||||
    size_t prebuf;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
struct memblockq* memblockq_new(size_t maxlength, size_t base, size_t prebuf) {
 | 
					struct memblockq* memblockq_new(size_t maxlength, size_t base, size_t prebuf) {
 | 
				
			||||||
    struct memblockq* bq;
 | 
					    struct memblockq* bq;
 | 
				
			||||||
    assert(maxlength && base);
 | 
					    assert(maxlength && base);
 | 
				
			||||||
| 
						 | 
					@ -37,6 +38,9 @@ struct memblockq* memblockq_new(size_t maxlength, size_t base, size_t prebuf) {
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    assert(bq->maxlength >= base);
 | 
					    assert(bq->maxlength >= base);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bq->measure_latency = 1;
 | 
				
			||||||
 | 
					    bq->latency = 0;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    return bq;
 | 
					    return bq;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,6 +64,11 @@ void memblockq_push(struct memblockq* bq, struct memchunk *chunk, size_t delta)
 | 
				
			||||||
    q = malloc(sizeof(struct memblock_list));
 | 
					    q = malloc(sizeof(struct memblock_list));
 | 
				
			||||||
    assert(q);
 | 
					    assert(q);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (bq->measure_latency)
 | 
				
			||||||
 | 
					        gettimeofday(&q->stamp, NULL);
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        timerclear(&q->stamp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    q->chunk = *chunk;
 | 
					    q->chunk = *chunk;
 | 
				
			||||||
    memblock_ref(q->chunk.memblock);
 | 
					    memblock_ref(q->chunk.memblock);
 | 
				
			||||||
    assert(q->chunk.index+q->chunk.length <= q->chunk.memblock->length);
 | 
					    assert(q->chunk.index+q->chunk.length <= q->chunk.memblock->length);
 | 
				
			||||||
| 
						 | 
					@ -113,6 +122,26 @@ int memblockq_pop(struct memblockq* bq, struct memchunk *chunk) {
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint32_t age(struct timeval *tv) {
 | 
				
			||||||
 | 
					    assert(tv);
 | 
				
			||||||
 | 
					    struct timeval now;
 | 
				
			||||||
 | 
					    uint32_t r;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (tv->tv_sec == 0)
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    gettimeofday(&now, NULL);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    r = (now.tv_sec-tv->tv_sec) * 1000000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (now.tv_usec >= tv->tv_usec)
 | 
				
			||||||
 | 
					        r += now.tv_usec - tv->tv_usec;
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        r -= tv->tv_usec - now.tv_usec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return r;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void memblockq_drop(struct memblockq *bq, size_t length) {
 | 
					void memblockq_drop(struct memblockq *bq, size_t length) {
 | 
				
			||||||
    assert(bq);
 | 
					    assert(bq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -123,6 +152,9 @@ void memblockq_drop(struct memblockq *bq, size_t length) {
 | 
				
			||||||
        if (l > bq->blocks->chunk.length)
 | 
					        if (l > bq->blocks->chunk.length)
 | 
				
			||||||
            l = bq->blocks->chunk.length;
 | 
					            l = bq->blocks->chunk.length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (bq->measure_latency)
 | 
				
			||||||
 | 
					            bq->latency = age(&bq->blocks->stamp);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        bq->blocks->chunk.index += l;
 | 
					        bq->blocks->chunk.index += l;
 | 
				
			||||||
        bq->blocks->chunk.length -= l;
 | 
					        bq->blocks->chunk.length -= l;
 | 
				
			||||||
        bq->total_length -= l;
 | 
					        bq->total_length -= l;
 | 
				
			||||||
| 
						 | 
					@ -178,3 +210,7 @@ int memblockq_is_writable(struct memblockq *bq, size_t length) {
 | 
				
			||||||
    assert(length <= bq->maxlength);
 | 
					    assert(length <= bq->maxlength);
 | 
				
			||||||
    return bq->total_length + length <= bq->maxlength;
 | 
					    return bq->total_length + length <= bq->maxlength;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint32_t memblockq_get_latency(struct memblockq *bq) {
 | 
				
			||||||
 | 
					    return bq->latency;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,5 +22,6 @@ void memblockq_empty(struct memblockq *bq);
 | 
				
			||||||
int memblockq_is_readable(struct memblockq *bq);
 | 
					int memblockq_is_readable(struct memblockq *bq);
 | 
				
			||||||
int memblockq_is_writable(struct memblockq *bq, size_t length);
 | 
					int memblockq_is_writable(struct memblockq *bq, size_t length);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint32_t memblockq_get_latency(struct memblockq *bq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										369
									
								
								src/module-oss-mmap.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										369
									
								
								src/module-oss-mmap.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,369 @@
 | 
				
			||||||
 | 
					#include <sys/soundcard.h>
 | 
				
			||||||
 | 
					#include <sys/ioctl.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <sys/stat.h>
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <fcntl.h>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					#include <limits.h>
 | 
				
			||||||
 | 
					#include <sys/mman.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "iochannel.h"
 | 
				
			||||||
 | 
					#include "sink.h"
 | 
				
			||||||
 | 
					#include "source.h"
 | 
				
			||||||
 | 
					#include "module.h"
 | 
				
			||||||
 | 
					#include "oss.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct userdata {
 | 
				
			||||||
 | 
					    struct sink *sink;
 | 
				
			||||||
 | 
					    struct source *source;
 | 
				
			||||||
 | 
					    struct core *core;
 | 
				
			||||||
 | 
					    struct sample_spec sample_spec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    size_t in_fragment_size, out_fragment_size, in_fragments, out_fragments, sample_size, out_fill;
 | 
				
			||||||
 | 
					    uint32_t sample_usec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int fd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void *in_mmap, *out_mmap;
 | 
				
			||||||
 | 
					    size_t in_mmap_length, out_mmap_length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct mainloop_source *mainloop_source;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct memblock **in_memblocks, **out_memblocks;
 | 
				
			||||||
 | 
					    unsigned out_current, in_current;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void module_done(struct core *c, struct module*m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void out_clear_memblocks(struct userdata*u, unsigned n) {
 | 
				
			||||||
 | 
					    unsigned i = u->out_current;
 | 
				
			||||||
 | 
					    assert(u && u->out_memblocks);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (n > u->out_fragments)
 | 
				
			||||||
 | 
					        n = u->out_fragments;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    while (n > 0) {
 | 
				
			||||||
 | 
					        if (u->out_memblocks[i]) {
 | 
				
			||||||
 | 
					            memblock_unref_fixed(u->out_memblocks[i]);
 | 
				
			||||||
 | 
					            u->out_memblocks[i] = NULL;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        i++;
 | 
				
			||||||
 | 
					        while (i >= u->out_fragments)
 | 
				
			||||||
 | 
					            i -= u->out_fragments;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        n--;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void out_fill_memblocks(struct userdata *u, unsigned n) {
 | 
				
			||||||
 | 
					    assert(u && u->out_memblocks);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    while (n > 0) {
 | 
				
			||||||
 | 
					        struct memchunk chunk;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (!u->out_memblocks[u->out_current]) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            u->out_memblocks[u->out_current] = chunk.memblock = memblock_new_fixed(u->out_mmap+u->out_fragment_size*u->out_current,  u->out_fragment_size);
 | 
				
			||||||
 | 
					            chunk.length = chunk.memblock->length;
 | 
				
			||||||
 | 
					            chunk.index = 0;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            sink_render_into_full(u->sink, &chunk);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        u->out_current++;
 | 
				
			||||||
 | 
					        while (u->out_current >= u->out_fragments)
 | 
				
			||||||
 | 
					            u->out_current -= u->out_fragments;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        n--;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void do_write(struct userdata *u) {
 | 
				
			||||||
 | 
					    struct count_info info;
 | 
				
			||||||
 | 
					    assert(u && u->sink);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
 | 
				
			||||||
 | 
					        fprintf(stderr, "SNDCTL_DSP_GETOPTR: %s\n", strerror(errno));
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    u->out_fill = (u->out_fragment_size * u->out_fragments) - (info.ptr % u->out_fragment_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!info.blocks)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    out_clear_memblocks(u, info.blocks);
 | 
				
			||||||
 | 
					    out_fill_memblocks(u, info.blocks);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void in_post_memblocks(struct userdata *u, unsigned n) {
 | 
				
			||||||
 | 
					    assert(u && u->in_memblocks);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    while (n > 0) {
 | 
				
			||||||
 | 
					        struct memchunk chunk;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (!u->in_memblocks[u->in_current]) {
 | 
				
			||||||
 | 
					            u->in_memblocks[u->in_current] = chunk.memblock = memblock_new_fixed(u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size);
 | 
				
			||||||
 | 
					            chunk.length = chunk.memblock->length;
 | 
				
			||||||
 | 
					            chunk.index = 0;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            source_post(u->source, &chunk);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        u->in_current++;
 | 
				
			||||||
 | 
					        while (u->in_current >= u->in_fragments)
 | 
				
			||||||
 | 
					            u->in_current -= u->in_fragments;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        n--;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void in_clear_memblocks(struct userdata*u, unsigned n) {
 | 
				
			||||||
 | 
					    unsigned i = u->in_current;
 | 
				
			||||||
 | 
					    assert(u && u->in_memblocks);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (n > u->in_fragments)
 | 
				
			||||||
 | 
					        n = u->in_fragments;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    while (n > 0) {
 | 
				
			||||||
 | 
					        if (u->in_memblocks[i]) {
 | 
				
			||||||
 | 
					            memblock_unref_fixed(u->in_memblocks[i]);
 | 
				
			||||||
 | 
					            u->in_memblocks[i] = NULL;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        i++;
 | 
				
			||||||
 | 
					        while (i >= u->in_fragments)
 | 
				
			||||||
 | 
					            i -= u->in_fragments;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        n--;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void do_read(struct userdata *u) {
 | 
				
			||||||
 | 
					    struct count_info info;
 | 
				
			||||||
 | 
					    assert(u && u->source);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
 | 
				
			||||||
 | 
					        fprintf(stderr, "SNDCTL_DSP_GETIPTR: %s\n", strerror(errno));
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!info.blocks)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    in_post_memblocks(u, info.blocks);
 | 
				
			||||||
 | 
					    in_clear_memblocks(u, u->in_fragments/2);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void io_callback(struct mainloop_source*s, int fd, enum mainloop_io_event event, void *userdata) {
 | 
				
			||||||
 | 
					    struct userdata *u = userdata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (event & MAINLOOP_IO_EVENT_IN)
 | 
				
			||||||
 | 
					        do_read(u);
 | 
				
			||||||
 | 
					    if (event & MAINLOOP_IO_EVENT_OUT)
 | 
				
			||||||
 | 
					        do_write(u);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint32_t sink_get_latency_cb(struct sink *s) {
 | 
				
			||||||
 | 
					    struct userdata *u = s->userdata;
 | 
				
			||||||
 | 
					    assert(s && u);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    do_write(u);
 | 
				
			||||||
 | 
					    return u->out_fill/u->sample_size*u->sample_usec;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int module_init(struct core *c, struct module*m) {
 | 
				
			||||||
 | 
					    struct audio_buf_info info;
 | 
				
			||||||
 | 
					    struct userdata *u = NULL;
 | 
				
			||||||
 | 
					    char *p;
 | 
				
			||||||
 | 
					    int frag_size;
 | 
				
			||||||
 | 
					    int mode, caps, caps_read = 0;
 | 
				
			||||||
 | 
					    int enable_bits = 0, zero = 0;
 | 
				
			||||||
 | 
					    assert(c && m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m->userdata = u = malloc(sizeof(struct userdata));
 | 
				
			||||||
 | 
					    assert(u);
 | 
				
			||||||
 | 
					    memset(u, 0, sizeof(struct userdata));
 | 
				
			||||||
 | 
					    u->fd = -1;
 | 
				
			||||||
 | 
					    u->core = c;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    p = m->argument ? m->argument : "/dev/dsp";
 | 
				
			||||||
 | 
					    if ((u->fd = open(p, (mode = O_RDWR)|O_NDELAY)) >= 0) {
 | 
				
			||||||
 | 
					        ioctl(u->fd, SNDCTL_DSP_SETDUPLEX, 0);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (ioctl(u->fd, SNDCTL_DSP_GETCAPS, &caps) < 0) {
 | 
				
			||||||
 | 
					            fprintf(stderr, "SNDCTL_DSP_GETCAPS: %s\n", strerror(errno));
 | 
				
			||||||
 | 
					            goto fail;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!(caps & DSP_CAP_DUPLEX)) {
 | 
				
			||||||
 | 
					            close(u->fd);
 | 
				
			||||||
 | 
					            u->fd = -1;
 | 
				
			||||||
 | 
					        } else
 | 
				
			||||||
 | 
					            caps_read = 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (u->fd < 0) {
 | 
				
			||||||
 | 
					        if ((u->fd = open(p, (mode = O_WRONLY)|O_NDELAY)) < 0) {
 | 
				
			||||||
 | 
					            if ((u->fd = open(p, (mode = O_RDONLY)|O_NDELAY)) < 0) {
 | 
				
			||||||
 | 
					                fprintf(stderr, "open('%s'): %s\n", p, strerror(errno));
 | 
				
			||||||
 | 
					                goto fail;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!caps_read) {
 | 
				
			||||||
 | 
					        if (ioctl(u->fd, SNDCTL_DSP_GETCAPS, &caps) < 0) {
 | 
				
			||||||
 | 
					            fprintf(stderr, "SNDCTL_DSP_GETCAPS: %s\n", strerror(errno));
 | 
				
			||||||
 | 
					            goto fail;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    if (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_REALTIME) || !(caps & DSP_CAP_TRIGGER)) {
 | 
				
			||||||
 | 
					        fprintf(stderr, "OSS device not mmap capable.\n");
 | 
				
			||||||
 | 
					        goto fail;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fprintf(stderr, "module-oss: device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    frag_size = ((int) 12 << 16) | 10; /* nfrags = 12; frag_size = 2^10 */
 | 
				
			||||||
 | 
					    if (ioctl(u->fd, SNDCTL_DSP_SETFRAGMENT, &frag_size) < 0) {
 | 
				
			||||||
 | 
					        fprintf(stderr, "SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno));
 | 
				
			||||||
 | 
					        goto fail;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (oss_auto_format(u->fd, &u->sample_spec) < 0)
 | 
				
			||||||
 | 
					        goto fail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (mode != O_WRONLY) {
 | 
				
			||||||
 | 
					        if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
 | 
				
			||||||
 | 
					            fprintf(stderr, "SNDCTL_DSP_GETISPACE: %s\n", strerror(errno));
 | 
				
			||||||
 | 
					            goto fail;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fprintf(stderr, "module-oss-mmap: input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
 | 
				
			||||||
 | 
					        u->in_mmap_length = (u->in_fragment_size = info.fragsize) * (u->in_fragments = info.fragstotal);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ((u->in_mmap = mmap(NULL, u->in_mmap_length, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
 | 
				
			||||||
 | 
					            if (mode == O_RDWR) {
 | 
				
			||||||
 | 
					                fprintf(stderr, "module-oss-mmap: mmap failed for input. Changing to O_WRONLY mode.\n");
 | 
				
			||||||
 | 
					                mode = O_WRONLY;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                fprintf(stderr, "modeule-oss-mmap: mmap(): %s\n", strerror(errno));
 | 
				
			||||||
 | 
					                goto fail;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					            u->source = source_new(c, "dsp", &u->sample_spec);
 | 
				
			||||||
 | 
					            assert(u->source);
 | 
				
			||||||
 | 
					            u->source->userdata = u;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            u->in_memblocks = malloc(sizeof(struct memblock *)*u->in_fragments);
 | 
				
			||||||
 | 
					            memset(u->in_memblocks, 0, sizeof(struct memblock *)*u->in_fragments);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            enable_bits |= PCM_ENABLE_INPUT;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (m != O_RDONLY) {
 | 
				
			||||||
 | 
					        if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
 | 
				
			||||||
 | 
					            fprintf(stderr, "SNDCTL_DSP_GETOSPACE: %s\n", strerror(errno));
 | 
				
			||||||
 | 
					            goto fail;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        fprintf(stderr, "module-oss: output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
 | 
				
			||||||
 | 
					        u->out_mmap_length = (u->out_fragment_size = info.fragsize) * (u->out_fragments = info.fragstotal);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ((u->out_mmap = mmap(NULL, u->out_mmap_length, PROT_WRITE, MAP_SHARED, u->fd, 0))  == MAP_FAILED) {
 | 
				
			||||||
 | 
					            if (mode == O_RDWR) {
 | 
				
			||||||
 | 
					                fprintf(stderr, "module-oss-mmap: mmap filed for output. Changing to O_RDONLY mode.\n");
 | 
				
			||||||
 | 
					                mode = O_RDONLY;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                fprintf(stderr, "modeule-oss-mmap: mmap(): %s\n", strerror(errno));
 | 
				
			||||||
 | 
					                goto fail;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            silence_memory(u->out_mmap, u->out_mmap_length, &u->sample_spec);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            u->sink = sink_new(c, "dsp", &u->sample_spec);
 | 
				
			||||||
 | 
					            assert(u->sink);
 | 
				
			||||||
 | 
					            u->sink->get_latency = sink_get_latency_cb;
 | 
				
			||||||
 | 
					            u->sink->userdata = u;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            u->out_memblocks = malloc(sizeof(struct memblock *)*u->out_fragments);
 | 
				
			||||||
 | 
					            memset(u->out_memblocks, 0, sizeof(struct memblock *)*u->out_fragments);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            enable_bits |= PCM_ENABLE_OUTPUT;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    zero = 0;
 | 
				
			||||||
 | 
					    if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero) < 0) {
 | 
				
			||||||
 | 
					        fprintf(stderr, "SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno));
 | 
				
			||||||
 | 
					        goto fail;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0) {
 | 
				
			||||||
 | 
					        fprintf(stderr, "SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno));
 | 
				
			||||||
 | 
					        goto fail;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    assert(u->source || u->sink);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    u->sample_size = sample_size(&u->sample_spec);
 | 
				
			||||||
 | 
					    u->sample_usec = 1000000/u->sample_spec.rate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    u->mainloop_source = mainloop_source_new_io(c->mainloop, u->fd, (u->source ? MAINLOOP_IO_EVENT_IN : 0) | (u->sink ? MAINLOOP_IO_EVENT_OUT : 0), io_callback, u);
 | 
				
			||||||
 | 
					    assert(u->mainloop_source);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fail:
 | 
				
			||||||
 | 
					    module_done(c, m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void module_done(struct core *c, struct module*m) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					    assert(c && m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    u = m->userdata;
 | 
				
			||||||
 | 
					    assert(u);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (u->out_memblocks) {
 | 
				
			||||||
 | 
					        out_clear_memblocks(u, u->out_fragments);
 | 
				
			||||||
 | 
					        free(u->out_memblocks);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (u->in_memblocks) {
 | 
				
			||||||
 | 
					        in_clear_memblocks(u, u->in_fragments);
 | 
				
			||||||
 | 
					        free(u->in_memblocks);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (u->in_mmap && u->in_mmap != MAP_FAILED)
 | 
				
			||||||
 | 
					        munmap(u->in_mmap, u->in_mmap_length);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (u->out_mmap && u->out_mmap != MAP_FAILED)
 | 
				
			||||||
 | 
					        munmap(u->out_mmap, u->out_mmap_length);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (u->sink)
 | 
				
			||||||
 | 
					        sink_free(u->sink);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (u->source)
 | 
				
			||||||
 | 
					        source_free(u->source);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (u->mainloop_source)
 | 
				
			||||||
 | 
					        mainloop_source_free(u->mainloop_source);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (u->fd >= 0)
 | 
				
			||||||
 | 
					        close(u->fd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    free(u);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,8 @@
 | 
				
			||||||
#include "sink.h"
 | 
					#include "sink.h"
 | 
				
			||||||
#include "source.h"
 | 
					#include "source.h"
 | 
				
			||||||
#include "module.h"
 | 
					#include "module.h"
 | 
				
			||||||
 | 
					#include "oss.h"
 | 
				
			||||||
 | 
					#include "sample.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct userdata {
 | 
					struct userdata {
 | 
				
			||||||
    struct sink *sink;
 | 
					    struct sink *sink;
 | 
				
			||||||
| 
						 | 
					@ -23,7 +25,9 @@ struct userdata {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct memchunk memchunk, silence;
 | 
					    struct memchunk memchunk, silence;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    uint32_t in_fragment_size, out_fragment_size, sample_size;
 | 
					    uint32_t in_fragment_size, out_fragment_size, sample_size, sample_usec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int fd;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void do_write(struct userdata *u) {
 | 
					static void do_write(struct userdata *u) {
 | 
				
			||||||
| 
						 | 
					@ -92,12 +96,26 @@ static void io_callback(struct iochannel *io, void*userdata) {
 | 
				
			||||||
    do_read(u);
 | 
					    do_read(u);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint32_t sink_get_latency_cb(struct sink *s) {
 | 
				
			||||||
 | 
					    int arg;
 | 
				
			||||||
 | 
					    struct userdata *u = s->userdata;
 | 
				
			||||||
 | 
					    assert(s && u);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) {
 | 
				
			||||||
 | 
					        fprintf(stderr, "module-oss: device doesn't support SNDCTL_DSP_GETODELAY.\n");
 | 
				
			||||||
 | 
					        s->get_latency = NULL;
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return arg/u->sample_size*u->sample_usec;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int module_init(struct core *c, struct module*m) {
 | 
					int module_init(struct core *c, struct module*m) {
 | 
				
			||||||
    struct audio_buf_info info;
 | 
					    struct audio_buf_info info;
 | 
				
			||||||
    struct userdata *u = NULL;
 | 
					    struct userdata *u = NULL;
 | 
				
			||||||
    char *p;
 | 
					    char *p;
 | 
				
			||||||
    int fd = -1;
 | 
					    int fd = -1;
 | 
				
			||||||
    int format, channels, speed, frag_size, in_frag_size, out_frag_size;
 | 
					    int frag_size, in_frag_size, out_frag_size;
 | 
				
			||||||
    int mode;
 | 
					    int mode;
 | 
				
			||||||
    struct sample_spec ss;
 | 
					    struct sample_spec ss;
 | 
				
			||||||
    assert(c && m);
 | 
					    assert(c && m);
 | 
				
			||||||
| 
						 | 
					@ -136,37 +154,8 @@ int module_init(struct core *c, struct module*m) {
 | 
				
			||||||
        goto fail;
 | 
					        goto fail;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    format = AFMT_S16_NE;
 | 
					    if (oss_auto_format(fd, &ss) < 0)
 | 
				
			||||||
    if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_S16_NE) {
 | 
					 | 
				
			||||||
        int f = AFMT_S16_NE == AFMT_S16_LE ? AFMT_S16_BE : AFMT_S16_LE;
 | 
					 | 
				
			||||||
        format = f;
 | 
					 | 
				
			||||||
        if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != f) {
 | 
					 | 
				
			||||||
            format = AFMT_U8;
 | 
					 | 
				
			||||||
            if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_U8) {
 | 
					 | 
				
			||||||
                fprintf(stderr, "SNDCTL_DSP_SETFMT: %s\n", format != AFMT_U8 ? "No supported sample format" : strerror(errno));
 | 
					 | 
				
			||||||
        goto fail;
 | 
					        goto fail;
 | 
				
			||||||
            } else
 | 
					 | 
				
			||||||
                ss.format = SAMPLE_U8;
 | 
					 | 
				
			||||||
        } else
 | 
					 | 
				
			||||||
            ss.format = f == AFMT_S16_LE ? SAMPLE_S16LE : SAMPLE_S16BE;
 | 
					 | 
				
			||||||
    } else
 | 
					 | 
				
			||||||
        ss.format = SAMPLE_S16NE;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
    channels = 2;
 | 
					 | 
				
			||||||
    if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) {
 | 
					 | 
				
			||||||
        fprintf(stderr, "SNDCTL_DSP_CHANNELS: %s\n", strerror(errno));
 | 
					 | 
				
			||||||
        goto fail;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    assert(channels);
 | 
					 | 
				
			||||||
    ss.channels = channels;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    speed = 44100;
 | 
					 | 
				
			||||||
    if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) {
 | 
					 | 
				
			||||||
        fprintf(stderr, "SNDCTL_DSP_SPEED: %s\n", strerror(errno));
 | 
					 | 
				
			||||||
        goto fail;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    assert(speed);
 | 
					 | 
				
			||||||
    ss.rate = speed;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) {
 | 
					    if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) {
 | 
				
			||||||
        fprintf(stderr, "SNDCTL_DSP_GETBLKSIZE: %s\n", strerror(errno));
 | 
					        fprintf(stderr, "SNDCTL_DSP_GETBLKSIZE: %s\n", strerror(errno));
 | 
				
			||||||
| 
						 | 
					@ -193,12 +182,15 @@ int module_init(struct core *c, struct module*m) {
 | 
				
			||||||
    if (mode != O_RDONLY) {
 | 
					    if (mode != O_RDONLY) {
 | 
				
			||||||
        u->sink = sink_new(c, "dsp", &ss);
 | 
					        u->sink = sink_new(c, "dsp", &ss);
 | 
				
			||||||
        assert(u->sink);
 | 
					        assert(u->sink);
 | 
				
			||||||
 | 
					        u->sink->get_latency = sink_get_latency_cb;
 | 
				
			||||||
 | 
					        u->sink->userdata = u;
 | 
				
			||||||
    } else
 | 
					    } else
 | 
				
			||||||
        u->sink = NULL;
 | 
					        u->sink = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (mode != O_WRONLY) {
 | 
					    if (mode != O_WRONLY) {
 | 
				
			||||||
        u->source = source_new(c, "dsp", &ss);
 | 
					        u->source = source_new(c, "dsp", &ss);
 | 
				
			||||||
        assert(u->source);
 | 
					        assert(u->source);
 | 
				
			||||||
 | 
					        u->source->userdata = u;
 | 
				
			||||||
    } else
 | 
					    } else
 | 
				
			||||||
        u->source = NULL;
 | 
					        u->source = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -207,16 +199,18 @@ int module_init(struct core *c, struct module*m) {
 | 
				
			||||||
    u->io = iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0);
 | 
					    u->io = iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0);
 | 
				
			||||||
    assert(u->io);
 | 
					    assert(u->io);
 | 
				
			||||||
    iochannel_set_callback(u->io, io_callback, u);
 | 
					    iochannel_set_callback(u->io, io_callback, u);
 | 
				
			||||||
 | 
					    u->fd = fd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u->memchunk.memblock = NULL;
 | 
					    u->memchunk.memblock = NULL;
 | 
				
			||||||
    u->memchunk.length = 0;
 | 
					    u->memchunk.length = 0;
 | 
				
			||||||
    u->sample_size = sample_size(&ss);
 | 
					    u->sample_size = sample_size(&ss);
 | 
				
			||||||
 | 
					    u->sample_usec = 1000000/ss.rate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u->out_fragment_size = out_frag_size;
 | 
					    u->out_fragment_size = out_frag_size;
 | 
				
			||||||
    u->in_fragment_size = in_frag_size;
 | 
					    u->in_fragment_size = in_frag_size;
 | 
				
			||||||
    u->silence.memblock = memblock_new(u->silence.length = u->out_fragment_size);
 | 
					    u->silence.memblock = memblock_new(u->silence.length = u->out_fragment_size);
 | 
				
			||||||
    assert(u->silence.memblock);
 | 
					    assert(u->silence.memblock);
 | 
				
			||||||
    silence(u->silence.memblock, &ss);
 | 
					    silence_memblock(u->silence.memblock, &ss);
 | 
				
			||||||
    u->silence.index = 0;
 | 
					    u->silence.index = 0;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    m->userdata = u;
 | 
					    m->userdata = u;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										48
									
								
								src/oss.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/oss.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,48 @@
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					#include <sys/soundcard.h>
 | 
				
			||||||
 | 
					#include <sys/ioctl.h>
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "oss.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int oss_auto_format(int fd, struct sample_spec *ss) {
 | 
				
			||||||
 | 
					    int format, channels, speed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert(fd >= 0 && ss);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    format = AFMT_S16_NE;
 | 
				
			||||||
 | 
					    if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_S16_NE) {
 | 
				
			||||||
 | 
					        int f = AFMT_S16_NE == AFMT_S16_LE ? AFMT_S16_BE : AFMT_S16_LE;
 | 
				
			||||||
 | 
					        format = f;
 | 
				
			||||||
 | 
					        if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != f) {
 | 
				
			||||||
 | 
					            format = AFMT_U8;
 | 
				
			||||||
 | 
					            if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_U8) {
 | 
				
			||||||
 | 
					                fprintf(stderr, "SNDCTL_DSP_SETFMT: %s\n", format != AFMT_U8 ? "No supported sample format" : strerror(errno));
 | 
				
			||||||
 | 
					                return -1;
 | 
				
			||||||
 | 
					            } else
 | 
				
			||||||
 | 
					                ss->format = SAMPLE_U8;
 | 
				
			||||||
 | 
					        } else
 | 
				
			||||||
 | 
					            ss->format = f == AFMT_S16_LE ? SAMPLE_S16LE : SAMPLE_S16BE;
 | 
				
			||||||
 | 
					    } else
 | 
				
			||||||
 | 
					        ss->format = SAMPLE_S16NE;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    channels = 2;
 | 
				
			||||||
 | 
					    if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) {
 | 
				
			||||||
 | 
					        fprintf(stderr, "SNDCTL_DSP_CHANNELS: %s\n", strerror(errno));
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    assert(channels);
 | 
				
			||||||
 | 
					    ss->channels = channels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    speed = 44100;
 | 
				
			||||||
 | 
					    if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) {
 | 
				
			||||||
 | 
					        fprintf(stderr, "SNDCTL_DSP_SPEED: %s\n", strerror(errno));
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    assert(speed);
 | 
				
			||||||
 | 
					    ss->rate = speed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										8
									
								
								src/oss.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/oss.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					#ifndef fooosshfoo
 | 
				
			||||||
 | 
					#define fooosshfoo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "sample.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int oss_auto_format(int fd, struct sample_spec *ss);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -56,6 +56,7 @@ static void destroy_connection(struct connection *c) {
 | 
				
			||||||
static int do_read(struct connection *c) {
 | 
					static int do_read(struct connection *c) {
 | 
				
			||||||
    struct memchunk chunk;
 | 
					    struct memchunk chunk;
 | 
				
			||||||
    ssize_t r;
 | 
					    ssize_t r;
 | 
				
			||||||
 | 
					    uint32_t u1, u2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!iochannel_is_readable(c->io))
 | 
					    if (!iochannel_is_readable(c->io))
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
| 
						 | 
					@ -66,8 +67,6 @@ static int do_read(struct connection *c) {
 | 
				
			||||||
    chunk.memblock = memblock_new(BUFSIZE);
 | 
					    chunk.memblock = memblock_new(BUFSIZE);
 | 
				
			||||||
    assert(chunk.memblock);
 | 
					    assert(chunk.memblock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    memblock_stamp(chunk.memblock);
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    if ((r = iochannel_read(c->io, chunk.memblock->data, BUFSIZE)) <= 0) {
 | 
					    if ((r = iochannel_read(c->io, chunk.memblock->data, BUFSIZE)) <= 0) {
 | 
				
			||||||
        fprintf(stderr, "read(): %s\n", r == 0 ? "EOF" : strerror(errno));
 | 
					        fprintf(stderr, "read(): %s\n", r == 0 ? "EOF" : strerror(errno));
 | 
				
			||||||
        memblock_unref(chunk.memblock);
 | 
					        memblock_unref(chunk.memblock);
 | 
				
			||||||
| 
						 | 
					@ -82,6 +81,13 @@ static int do_read(struct connection *c) {
 | 
				
			||||||
    memblockq_push(c->input_memblockq, &chunk, 0);
 | 
					    memblockq_push(c->input_memblockq, &chunk, 0);
 | 
				
			||||||
    memblock_unref(chunk.memblock);
 | 
					    memblock_unref(chunk.memblock);
 | 
				
			||||||
    sink_notify(c->sink_input->sink);
 | 
					    sink_notify(c->sink_input->sink);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    u1 = memblockq_get_latency(c->input_memblockq);
 | 
				
			||||||
 | 
					    u2 = sink_get_latency(c->sink_input->sink);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fprintf(stderr, "latency: %u+%u=%u\r", u1, u2, u1+u2);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -96,7 +102,9 @@ static int do_write(struct connection *c) {
 | 
				
			||||||
        return 0;    
 | 
					        return 0;    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert(c->output_memblockq);
 | 
					    assert(c->output_memblockq);
 | 
				
			||||||
    memblockq_peek(c->output_memblockq, &chunk);
 | 
					    if (memblockq_peek(c->output_memblockq, &chunk) < 0)
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
    assert(chunk.memblock && chunk.length);
 | 
					    assert(chunk.memblock && chunk.length);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if ((r = iochannel_write(c->io, chunk.memblock->data+chunk.index, chunk.length)) < 0) {
 | 
					    if ((r = iochannel_write(c->io, chunk.memblock->data+chunk.index, chunk.length)) < 0) {
 | 
				
			||||||
| 
						 | 
					@ -145,6 +153,9 @@ static void source_output_push_cb(struct source_output *o, struct memchunk *chun
 | 
				
			||||||
    assert(o && c && chunk);
 | 
					    assert(o && c && chunk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    memblockq_push(c->output_memblockq, chunk, 0);
 | 
					    memblockq_push(c->output_memblockq, chunk, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (do_write(c) < 0)
 | 
				
			||||||
 | 
					        destroy_connection(c);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void source_output_kill_cb(struct source_output *o) {
 | 
					static void source_output_kill_cb(struct source_output *o) {
 | 
				
			||||||
| 
						 | 
					@ -205,7 +216,7 @@ static void on_connection(struct socket_server*s, struct iochannel *io, void *us
 | 
				
			||||||
        c->source_output->kill = source_output_kill_cb;
 | 
					        c->source_output->kill = source_output_kill_cb;
 | 
				
			||||||
        c->source_output->userdata = c;
 | 
					        c->source_output->userdata = c;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        l = 5*bytes_per_second(&DEFAULT_SAMPLE_SPEC);
 | 
					        l = 5*bytes_per_second(&DEFAULT_SAMPLE_SPEC); /* 5s */
 | 
				
			||||||
        c->output_memblockq = memblockq_new(l, sample_size(&DEFAULT_SAMPLE_SPEC), l/2);
 | 
					        c->output_memblockq = memblockq_new(l, sample_size(&DEFAULT_SAMPLE_SPEC), l/2);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -225,7 +236,7 @@ static void on_connection(struct socket_server*s, struct iochannel *io, void *us
 | 
				
			||||||
        c->sink_input->kill = sink_input_kill_cb;
 | 
					        c->sink_input->kill = sink_input_kill_cb;
 | 
				
			||||||
        c->sink_input->userdata = c;
 | 
					        c->sink_input->userdata = c;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        l = 5*bytes_per_second(&DEFAULT_SAMPLE_SPEC);
 | 
					        l = bytes_per_second(&DEFAULT_SAMPLE_SPEC)/2; /* half a second */
 | 
				
			||||||
        c->input_memblockq = memblockq_new(l, sample_size(&DEFAULT_SAMPLE_SPEC), l/2);
 | 
					        c->input_memblockq = memblockq_new(l, sample_size(&DEFAULT_SAMPLE_SPEC), l/2);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										21
									
								
								src/sample.c
									
										
									
									
									
								
							
							
						
						
									
										21
									
								
								src/sample.c
									
										
									
									
									
								
							| 
						 | 
					@ -9,10 +9,22 @@ struct sample_spec default_sample_spec = {
 | 
				
			||||||
    .channels = 2
 | 
					    .channels = 2
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct memblock *silence(struct memblock* b, struct sample_spec *spec) {
 | 
					struct memblock *silence_memblock(struct memblock* b, struct sample_spec *spec) {
 | 
				
			||||||
    char c = 0;
 | 
					    assert(b && b->data && spec);
 | 
				
			||||||
    assert(b && spec);
 | 
					 | 
				
			||||||
    memblock_assert_exclusive(b);
 | 
					    memblock_assert_exclusive(b);
 | 
				
			||||||
 | 
					    silence_memory(b->data, b->length, spec);
 | 
				
			||||||
 | 
					    return b;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void silence_memchunk(struct memchunk *c, struct sample_spec *spec) {
 | 
				
			||||||
 | 
					    assert(c && c->memblock && c->memblock->data && spec && c->length);
 | 
				
			||||||
 | 
					    memblock_assert_exclusive(c->memblock);
 | 
				
			||||||
 | 
					    silence_memory(c->memblock->data+c->index, c->length, spec);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void silence_memory(void *p, size_t length, struct sample_spec *spec) {
 | 
				
			||||||
 | 
					    char c = 0;
 | 
				
			||||||
 | 
					    assert(p && length && spec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (spec->format) {
 | 
					    switch (spec->format) {
 | 
				
			||||||
        case SAMPLE_U8:
 | 
					        case SAMPLE_U8:
 | 
				
			||||||
| 
						 | 
					@ -29,8 +41,7 @@ struct memblock *silence(struct memblock* b, struct sample_spec *spec) {
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
    memset(b->data, c, b->length);
 | 
					    memset(p, c, length);
 | 
				
			||||||
    return b;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
size_t sample_size(struct sample_spec *spec) {
 | 
					size_t sample_size(struct sample_spec *spec) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,8 +26,9 @@ struct sample_spec {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern struct sample_spec default_sample_spec;
 | 
					extern struct sample_spec default_sample_spec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct memblock *silence(struct memblock* b, struct sample_spec *spec);
 | 
					struct memblock *silence_memblock(struct memblock* b, struct sample_spec *spec);
 | 
				
			||||||
 | 
					void silence_memchunk(struct memchunk *c, struct sample_spec *spec);
 | 
				
			||||||
 | 
					void silence_memory(void *p, size_t length, struct sample_spec *spec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct mix_info {
 | 
					struct mix_info {
 | 
				
			||||||
    struct memchunk chunk;
 | 
					    struct memchunk chunk;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										63
									
								
								src/sink.c
									
										
									
									
									
								
							
							
						
						
									
										63
									
								
								src/sink.c
									
										
									
									
									
								
							| 
						 | 
					@ -33,6 +33,7 @@ struct sink* sink_new(struct core *core, const char *name, const struct sample_s
 | 
				
			||||||
    s->volume = 0xFF;
 | 
					    s->volume = 0xFF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    s->notify = NULL;
 | 
					    s->notify = NULL;
 | 
				
			||||||
 | 
					    s->get_latency = NULL;
 | 
				
			||||||
    s->userdata = NULL;
 | 
					    s->userdata = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    r = idxset_put(core->sinks, s, &s->index);
 | 
					    r = idxset_put(core->sinks, s, &s->index);
 | 
				
			||||||
| 
						 | 
					@ -138,14 +139,18 @@ int sink_render(struct sink*s, size_t length, struct memchunk *result) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    inputs_drop(s, info, n, l);
 | 
					    inputs_drop(s, info, n, l);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert(s->monitor_source);
 | 
				
			||||||
 | 
					    source_post(s->monitor_source, result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int sink_render_into(struct sink*s, struct memblock *target, struct memchunk *result) {
 | 
					int sink_render_into(struct sink*s, struct memchunk *target) {
 | 
				
			||||||
    struct mix_info info[MAX_MIX_CHANNELS];
 | 
					    struct mix_info info[MAX_MIX_CHANNELS];
 | 
				
			||||||
    unsigned n;
 | 
					    unsigned n;
 | 
				
			||||||
    size_t l;
 | 
					    size_t l;
 | 
				
			||||||
    assert(s && target && target->length && target->data && result);
 | 
					    assert(s && target && target->length && target->memblock && target->memblock->data);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
 | 
					    n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -160,18 +165,52 @@ int sink_render_into(struct sink*s, struct memblock *target, struct memchunk *re
 | 
				
			||||||
        if (l > info[0].chunk.length)
 | 
					        if (l > info[0].chunk.length)
 | 
				
			||||||
            l = info[0].chunk.length;
 | 
					            l = info[0].chunk.length;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        result->memblock = target;
 | 
					        memcpy(target->memblock->data+target->index, info[0].chunk.memblock->data + info[0].chunk.index, l);
 | 
				
			||||||
        memcpy(target->data, info[0].chunk.memblock->data + info[0].chunk.index, l);
 | 
					        target->length = l;
 | 
				
			||||||
        result->length = target->length = l;
 | 
					    } else
 | 
				
			||||||
        result->index = 0;
 | 
					        target->length = l = mix_chunks(info, n, target->memblock->data+target->index, target->length, &s->sample_spec, s->volume);
 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
        result->memblock = target;
 | 
					 | 
				
			||||||
        result->length = l = mix_chunks(info, n, target->data, target->length, &s->sample_spec, s->volume);
 | 
					 | 
				
			||||||
        result->index = 0;
 | 
					 | 
				
			||||||
    assert(l);
 | 
					    assert(l);
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    inputs_drop(s, info, n, l);
 | 
					    inputs_drop(s, info, n, l);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert(s->monitor_source);
 | 
				
			||||||
 | 
					    source_post(s->monitor_source, target);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void sink_render_into_full(struct sink *s, struct memchunk *target) {
 | 
				
			||||||
 | 
					    struct memchunk chunk;
 | 
				
			||||||
 | 
					    size_t l, d;
 | 
				
			||||||
 | 
					    assert(s && target && target->memblock && target->length && target->memblock->data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    l = target->length;
 | 
				
			||||||
 | 
					    d = 0;
 | 
				
			||||||
 | 
					    while (l > 0) {
 | 
				
			||||||
 | 
					        chunk = *target;
 | 
				
			||||||
 | 
					        chunk.index += d;
 | 
				
			||||||
 | 
					        chunk.length -= d;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (sink_render_into(s, &chunk) < 0)
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        d += chunk.length;
 | 
				
			||||||
 | 
					        l -= chunk.length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (l > 0) {
 | 
				
			||||||
 | 
					        chunk = *target;
 | 
				
			||||||
 | 
					        chunk.index += d;
 | 
				
			||||||
 | 
					        chunk.length -= d;
 | 
				
			||||||
 | 
					        silence_memchunk(&chunk, &s->sample_spec);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint32_t sink_get_latency(struct sink *s) {
 | 
				
			||||||
 | 
					    assert(s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!s->get_latency)
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return s->get_latency(s);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,6 +23,7 @@ struct sink {
 | 
				
			||||||
    uint8_t volume;
 | 
					    uint8_t volume;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void (*notify)(struct sink*sink);
 | 
					    void (*notify)(struct sink*sink);
 | 
				
			||||||
 | 
					    uint32_t (*get_latency)(struct sink *s);
 | 
				
			||||||
    void *userdata;
 | 
					    void *userdata;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,7 +31,10 @@ struct sink* sink_new(struct core *core, const char *name, const struct sample_s
 | 
				
			||||||
void sink_free(struct sink* s);
 | 
					void sink_free(struct sink* s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int sink_render(struct sink*s, size_t length, struct memchunk *result);
 | 
					int sink_render(struct sink*s, size_t length, struct memchunk *result);
 | 
				
			||||||
int sink_render_into(struct sink*s, struct memblock *target, struct memchunk *result);
 | 
					int sink_render_into(struct sink*s, struct memchunk *target);
 | 
				
			||||||
 | 
					void sink_render_into_full(struct sink *s, struct memchunk *target);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint32_t sink_get_latency(struct sink *s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void sink_notify(struct sink*s);
 | 
					void sink_notify(struct sink*s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,7 +59,7 @@ void source_notify(struct source*s) {
 | 
				
			||||||
static int do_post(void *p, uint32_t index, int *del, void*userdata) {
 | 
					static int do_post(void *p, uint32_t index, int *del, void*userdata) {
 | 
				
			||||||
    struct memchunk *chunk = userdata;
 | 
					    struct memchunk *chunk = userdata;
 | 
				
			||||||
    struct source_output *o = p;
 | 
					    struct source_output *o = p;
 | 
				
			||||||
    assert(o && o->push && index && del && chunk);
 | 
					    assert(o && o->push && del && chunk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    o->push(o, chunk);
 | 
					    o->push(o, chunk);
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										14
									
								
								src/todo
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								src/todo
									
										
									
									
									
								
							| 
						 | 
					@ -1,12 +1,11 @@
 | 
				
			||||||
- mixing
 | 
					- simple control protocol
 | 
				
			||||||
- latenzoptimierung
 | 
					 | 
				
			||||||
- optimierung von rebuild_pollfds()
 | 
					 | 
				
			||||||
- resampling
 | 
					 | 
				
			||||||
- native protocol/library
 | 
					- native protocol/library
 | 
				
			||||||
- oss/mmap
 | 
					- resampling
 | 
				
			||||||
- esound prodocol
 | 
					- esound protocol
 | 
				
			||||||
- config-parser
 | 
					- config-parser
 | 
				
			||||||
 | 
					- record-testing
 | 
				
			||||||
-- 0.1
 | 
					-- 0.1
 | 
				
			||||||
 | 
					- optimierung von rebuild_pollfds()
 | 
				
			||||||
- future cancellation
 | 
					- future cancellation
 | 
				
			||||||
- client-ui
 | 
					- client-ui
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,3 +15,6 @@ drivers:
 | 
				
			||||||
- portaudio
 | 
					- portaudio
 | 
				
			||||||
- mplayer
 | 
					- mplayer
 | 
				
			||||||
- python
 | 
					- python
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					modules:
 | 
				
			||||||
 | 
					- alsa?
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue