diff options
Diffstat (limited to 'speex~')
-rw-r--r-- | speex~/CHANGES.LOG | 4 | ||||
-rw-r--r-- | speex~/INSTALL | 18 | ||||
-rw-r--r-- | speex~/Makefile | 84 | ||||
-rw-r--r-- | speex~/README | 32 | ||||
-rw-r--r-- | speex~/help-speex~.pd | 68 | ||||
-rw-r--r-- | speex~/speexin~.c | 607 | ||||
-rw-r--r-- | speex~/speexout~.c | 450 |
7 files changed, 1263 insertions, 0 deletions
diff --git a/speex~/CHANGES.LOG b/speex~/CHANGES.LOG new file mode 100644 index 0000000..2bc161f --- /dev/null +++ b/speex~/CHANGES.LOG @@ -0,0 +1,4 @@ +0.2 + check length of frames to avoid crashes +0.1 + first implementation diff --git a/speex~/INSTALL b/speex~/INSTALL new file mode 100644 index 0000000..852c774 --- /dev/null +++ b/speex~/INSTALL @@ -0,0 +1,18 @@ +first, you need to install Speex library,
+i used v0.6.0 available from http://speex.sourceforge.net
+
+untar in /my/pd/dir/externs
+
+cd /my/pd/dir/externs/speex~
+
+make clean
+
+make
+
+make install
+
+open help-speex~.pd
+
+Thanx for getting here.
+
+Yves/
diff --git a/speex~/Makefile b/speex~/Makefile new file mode 100644 index 0000000..754c858 --- /dev/null +++ b/speex~/Makefile @@ -0,0 +1,84 @@ +NAME=speexout~
+NAMEB=speexin~
+
+current: pd_linux
+
+# ----------------------- NT -----------------------
+
+pd_nt: $(NAME).dll $(NAMEB).dll
+
+.SUFFIXES: .dll
+
+PDNTCFLAGS = /W3 /WX /DNT /DPD /nologo
+VC="C:\Program Files\Microsoft Visual Studio\Vc98"
+
+PDNTINCLUDE = /I. /I\tcl\include /I\ftp\pd\src /I$(VC)\include
+
+PDNTLDIR = $(VC)\lib
+PDNTLIB = $(PDNTLDIR)\libc.lib \
+ $(PDNTLDIR)\oldnames.lib \
+ $(PDNTLDIR)\kernel32.lib \
+ \ftp\pd\bin\pd.lib
+
+.c.dll:
+ cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c $*.c
+ link /dll /export:$(CSYM)_setup $*.obj $(PDNTLIB)
+
+# ----------------------- IRIX 5.x -----------------------
+
+pd_irix5: $(NAME).pd_irix5 $(NAMEB).pd_irix5
+
+.SUFFIXES: .pd_irix5
+
+SGICFLAGS5 = -o32 -DPD -DUNIX -DIRIX -O2
+
+SGIINCLUDE = -I../../src
+
+.c.pd_irix5:
+ cc $(SGICFLAGS5) $(SGIINCLUDE) -o $*.o -c $*.c
+ ld -elf -shared -rdata_shared -o $*.pd_irix5 $*.o
+ rm $*.o
+
+# ----------------------- IRIX 6.x -----------------------
+
+pd_irix6: $(NAME).pd_irix6 $(NAMEB).pd_irix6
+
+.SUFFIXES: .pd_irix6
+
+SGICFLAGS6 = -n32 -DPD -DUNIX -DIRIX -DN32 -woff 1080,1064,1185 \
+ -OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \
+ -Ofast=ip32
+
+.c.pd_irix6:
+ cc $(SGICFLAGS6) $(SGIINCLUDE) -o $*.o -c $*.c
+ ld -n32 -IPA -shared -rdata_shared -o $*.pd_irix6 $*.o
+ rm $*.o
+
+# ----------------------- LINUX i386 -----------------------
+
+pd_linux: $(NAME).pd_linux $(NAMEB).pd_linux
+
+.SUFFIXES: .pd_linux
+
+LINUXCFLAGS = -g -I/usr/local/include -DPD -DUNIX -O2 -funroll-loops -fomit-frame-pointer \
+ -Wall -W -Wshadow -Wstrict-prototypes -Werror \
+ -Wno-unused -Wno-parentheses -Wno-switch
+
+LINUXINCLUDE = -I../../src
+
+.c.pd_linux:
+ cc $(LINUXCFLAGS) $(LINUXINCLUDE) -o $*.o -c $*.c
+ ld -export_dynamic -shared -o $*.pd_linux $*.o -L/usr/local/lib -lc -lm -lspeex
+ strip --strip-unneeded $*.pd_linux
+ rm -f $*.o ../$*.pd_linux
+ ln -s speex~/$*.pd_linux ..
+
+
+
+# ----------------------------------------------------------
+
+install:
+ cp help-*.pd ../../doc/5.reference
+
+clean:
+ rm -f *.o *.pd_* so_locations
diff --git a/speex~/README b/speex~/README new file mode 100644 index 0000000..1c290dd --- /dev/null +++ b/speex~/README @@ -0,0 +1,32 @@ +***************************************************************************** +Version 0.01 +copyleft (c) 2001 by Yves Degoyon + +speex~ is a voice quality streamer using Speex library +consisting of two objects : speexin~ and speexout~. + +To install speex~, follow the steps from INSTALL + +This software is published under GPL terms. + +A big thanx to Jean-Marc Valin, author of Speex +who helped me fixing encoding/decoding problems. + +This is software with ABSOLUTELY NO WARRANTY. +Use it at your OWN RISK. It's possible to damage e.g. hardware or your hearing +due to a bug or for other reasons. +We do not warrant that the program is free of infringement of any third-party +patents. + +***************************************************************************** + +speex~ has been compiled for Linux using Speex library v0.6.0 +( http://speex.sourceforge.net ). + +COPYING: you may use this source under GPL terms! + +PLEASE NOTE: The speex codec is patent free unlike GSM codecs. + that's the main reason why it's been choosen. + ( + it allows very low throughputs like 8kbits/s ). + +***************************************************************************** diff --git a/speex~/help-speex~.pd b/speex~/help-speex~.pd new file mode 100644 index 0000000..d1c40d4 --- /dev/null +++ b/speex~/help-speex~.pd @@ -0,0 +1,68 @@ +#N canvas 52 27 918 567 10; +#X msg 36 286 \; pd dsp 1; +#X msg 102 286 \; pd dsp 0; +#X msg 323 51 disconnect; +#X floatatom 221 227 5 0 0; +#X obj 221 199 speexout~; +#X obj 36 265 loadbang; +#X symbolatom 679 245 20 0 0; +#X text 662 261 Incomer's address; +#X text 26 8 speex~ : speexout~ / speexin~; +#X msg 343 161 quality 3; +#X msg 345 327 quality 10; +#X text 432 91 <-- settings for encoding quality; +#X obj 87 145 adc~; +#X obj 615 206 speexin~ 5000 1; +#X text 321 8 Step 1 : connect the emitter to the receiver; +#X text 30 124 Step 2 : speak in your microphone; +#X text 541 487 bugs and comments @ ydegoyon@free.fr [-_-]; +#X text 541 469 part of unauthorized PD ( http://ydegoyon.free.fr ) +; +#X msg 344 301 quality 9; +#X msg 344 276 quality 8; +#X msg 344 254 quality 7; +#X msg 344 231 quality 6; +#X msg 343 208 quality 5; +#X msg 343 185 quality 4; +#X msg 343 139 quality 2; +#X msg 343 116 quality 1; +#X msg 343 92 quality 0; +#X text 461 104 quality: lowest = 0 \, highest = 10 \, default = 5 +; +#X text 497 173 constructor : speexin~ <portno> <graphics>; +#X msg 444 356 bang; +#X obj 445 384 tabwrite~ speex-output; +#N canvas 0 0 450 300 graph2 0; +#X array speex-output 100 float 1; +#A 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0; +#X coords 0 1 99 -1 200 140 1; +#X restore 172 360 graph; +#X text 26 24 speech quality encoder/decoder/streamer; +#X obj 611 316 dac~; +#X msg 323 70 connect localhost 5000; +#X msg 323 29 connect 81.65.246.123 5000; +#X connect 2 0 4 0; +#X connect 4 0 3 0; +#X connect 5 0 0 0; +#X connect 9 0 4 0; +#X connect 10 0 4 0; +#X connect 12 0 4 0; +#X connect 13 0 30 0; +#X connect 13 0 33 0; +#X connect 13 0 33 1; +#X connect 13 1 6 0; +#X connect 18 0 4 0; +#X connect 19 0 4 0; +#X connect 20 0 4 0; +#X connect 21 0 4 0; +#X connect 22 0 4 0; +#X connect 23 0 4 0; +#X connect 24 0 4 0; +#X connect 25 0 4 0; +#X connect 26 0 4 0; +#X connect 29 0 30 0; +#X connect 34 0 4 0; +#X connect 35 0 4 0; diff --git a/speex~/speexin~.c b/speex~/speexin~.c new file mode 100644 index 0000000..161c9de --- /dev/null +++ b/speex~/speexin~.c @@ -0,0 +1,607 @@ +/* ------------------------ speexin~ ------------------------------------------ */ +/* */ +/* Object to receive a speex encoded stream sent by a peer using speexin~. */ +/* Written by Yves Degoyon (ydegoyon@free.fr). */ +/* Tarballs and updates @ http://ydegoyon.free.fr */ +/* */ +/* This program is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU General Public License */ +/* as published by the Free Software Foundation; either version 2 */ +/* of the License, or (at your option) any later version. */ +/* */ +/* See file LICENSE for further informations on licensing terms. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* Uses the Speex voice quality encoding library which can */ +/* be found at http://speex.sourceforge.net. */ +/* */ +/* ---------------------------------------------------------------------------- */ + + +#include <m_pd.h> +#include <g_canvas.h> + +#include <sys/types.h> +#include <string.h> +#include <stdlib.h> +#ifdef UNIX +#include <sys/socket.h> +#include <errno.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <sys/time.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#define SOCKET_ERROR -1 +#else +#include <winsock.h> +#endif + +#include <speex.h> /* speex decoder stuff */ +#include <speex_bits.h> /* speex decoder stuff */ + +#ifdef NT +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4305 ) +#endif + +#define MIN_AUDIO_INPUT 1024 // we must a least have 8 chunks to play a correct sound +#define INPUT_BUFFER_SIZE 32768 +#define OUTPUT_BUFFER_SIZE 32768 /* 32k */ +#define BARHEIGHT 10 + +#define SPEEX_NB_MODE 0 /* audio data must be 8kHz */ +#define SPEEX_WB_MODE 1 /* audio data must be 16kHz */ + +//#define DATADEBUG + +typedef void (*t_fdpollfn)(void *ptr, int fd); +extern void sys_rmpollfn(int fd); +extern void sys_addpollfn(int fd, t_fdpollfn fn, void *ptr); + +/* time-out used for select() call */ +static struct timeval ztout; + +static char *speexin_version = "speexin~: speex voice quality streamer version 0.2, written by ydegoyon@free.fr"; + +extern void sys_sockerror(char *s); + +void speexin_closesocket(int fd) +{ +#ifdef UNIX + if ( close(fd) < 0 ) + { + perror( "close" ); + } + else + { + post( "speexin~ : closed socket : %d", fd ); + } +#endif +#ifdef NT + closesocket(fd); +#endif + sys_rmpollfn(fd); +} + +int setsocketoptions(int sockfd) +{ + int sockopt = 1; + if (setsockopt(sockfd, SOL_TCP, TCP_NODELAY, (const char*) &sockopt, sizeof(int)) < 0) + { + post("speexin~ : setsockopt TCP_NODELAY failed"); + perror( "setsockopt" ); + return -1; + } + else + { + post("speexin~ : TCP_NODELAY set"); + } + +#ifdef UNIX + sockopt = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(int)) < 0) + { + post("speexin~ : setsockopt SO_REUSEADDR failed"); + perror( "setsockopt" ); + return -1; + } + else + { + post("speexin~ : setsockopt SO_REUSEADDR done."); + } +#endif + return 0; +} + + +/* ------------------------ speexin~ ----------------------------- */ + +static t_class *speexin_class; + +typedef struct _speexin +{ + t_object x_obj; + t_int x_socket; + t_outlet *x_connectionip; + t_int x_serversocket; + t_int x_samplerate; + + /* Speex stuff */ + SpeexBits x_bits; /* bits packing structure */ + void *x_decstate; /* decoder state */ + t_int x_framesize; /* frame size */ + t_int x_mode; /* Narrow or Wide Band */ + int x_quality; /* encoding quality ( 0 to 10 ) */ + + t_int x_inpackets; /* number of packets received */ + t_int x_dpacket; /* displayed packet in status bar */ + t_int x_packetsize; /* size of the packets */ + t_int x_pblocks; /* processed blocks */ + t_int x_graphic; /* indicates if we show a graphic bar */ + + void *x_inbuffer; /* accumulation buffer for incoming speex frames */ + t_int x_inwritepos; /* accumulation buffer for incoming speex frames */ + t_int x_encsize; + t_int x_inbuffersize; + + t_float *x_outbuffer; /* buffer to store audio decoded data */ + t_int x_oinp; + t_int x_ooutp; + t_int x_outunread; + t_int x_outbuffersize; + t_float *x_decchunk; + + t_canvas *x_canvas; + + t_int x_stream; /* indicates if a stream is connected ( meaning correct input flow ) */ + t_int x_newstream; /* at first, the stream must provide enough data to start */ + +} t_speexin; + +void speexin_tilde_speex_init(t_speexin *x) +{ + int ret; + int pf=1; + + speex_bits_init(&x->x_bits); + + switch ( x->x_mode ) + { + case SPEEX_NB_MODE : + x->x_decstate = speex_decoder_init(&speex_nb_mode); + break; + + case SPEEX_WB_MODE : + x->x_decstate = speex_decoder_init(&speex_wb_mode); + break; + + default : + error( "speexin~ : severe error : decoding scheme is unknown" ); + break; + } + + speex_decoder_ctl(x->x_decstate, SPEEX_GET_FRAME_SIZE, (void*)&x->x_framesize); + + speex_decoder_ctl(x->x_decstate, SPEEX_SET_PF, &pf); + + post( "speexin~ : frame size : %d", x->x_framesize ); + +} + +static void speexin_decode_input(t_speexin *x) +{ + int i; + int alength = 0; + static char out[8192]; + signed short int *p = (signed short int *) out; + int pbytes; + int ret; + int flength = 0; + + if ( x->x_encsize > 0 ) + { + + while ( x->x_encsize > *(char *)(x->x_inbuffer) ) + { + + flength = *(char *)(x->x_inbuffer ); + + // post( "speexin~ : reading bits from 1 to : %d", flength+1 ); + speex_bits_read_from(&x->x_bits, x->x_inbuffer+1, flength); + +#ifdef DATADEBUG + { + t_int si; + + printf( "speexin~ : decoding : " ); + for ( si=0; si<flength; si++ ) + { + printf( "%d ", *(char *)(x->x_inbuffer+1+si) ); + } + printf( "\n" ); + } +#endif + + { + t_int sp=0, rp=0; + + speex_decode(x->x_decstate, &x->x_bits, x->x_decchunk); + + while( sp < x->x_framesize ) + { + rp=(x->x_oinp+sp)%x->x_outbuffersize; + // if ( rp == x->x_outbuffersize - 1 ) post( "speexin~ : write at the end of audio buffer" ); + // post( "speexin~ : sp=%d : rp=%d", sp, rp ); + x->x_outbuffer[ rp ] = x->x_decchunk[sp++]; + } + x->x_oinp = rp+1; + } + x->x_outunread += x->x_framesize; + memcpy( x->x_inbuffer, x->x_inbuffer+flength+1, x->x_inbuffersize-flength-1 ); + x->x_encsize -= flength+1; + x->x_inwritepos -= flength+1; + + } + + } + + if ( x->x_graphic && glist_isvisible( x->x_canvas ) ) + { + /* update graphical read status */ + if ( x->x_inpackets != x->x_dpacket ) + { + char color[32]; + int minpackets = ( MIN_AUDIO_INPUT/x->x_framesize)-2; // audio loop has eaten some already + + + sys_vgui(".x%x.c delete rectangle %xSTATUS\n", x->x_canvas, x ); + sys_vgui(".x%x.c delete line %xTHRESHOLD\n", x->x_canvas, x ); + if ( x->x_outunread > 0 ) + { + t_int width; + + if ( x->x_inpackets < (MIN_AUDIO_INPUT/x->x_framesize)/2 ) + { + strcpy( color, "red" ); + } + else + { + strcpy( color, "lightgreen" ); + } + width = rtext_width( glist_findrtext( (t_glist*)x->x_canvas, (t_text *)x ) ); + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill %s -tags %xSTATUS\n", + x->x_canvas, x->x_obj.te_xpix, x->x_obj.te_ypix-BARHEIGHT-1, + x->x_obj.te_xpix+(x->x_inpackets*x->x_packetsize*width)/INPUT_BUFFER_SIZE, + x->x_obj.te_ypix - 1, color, x ); + sys_vgui(".x%x.c create line %d %d %d %d -fill red -tags %xTHRESHOLD\n", + x->x_canvas, x->x_obj.te_xpix+(minpackets*x->x_packetsize*width)/INPUT_BUFFER_SIZE, + x->x_obj.te_ypix-BARHEIGHT-1, + x->x_obj.te_xpix+(minpackets*x->x_packetsize*width)/INPUT_BUFFER_SIZE, + x->x_obj.te_ypix-1, x ); + x->x_dpacket = x->x_inpackets; + } + } + + } + +} + +static void speexin_recv(t_speexin *x) +{ + int ret; + + if ( x->x_inwritepos > x->x_inbuffersize - 1024 ) + { + post( "speexin~ : input buffer is full" ); + return; + } + if ( ( ret = recv(x->x_socket, (void*) x->x_inbuffer + x->x_inwritepos, + (size_t)x->x_inbuffersize, + MSG_NOSIGNAL) ) < 0 ) + { + post( "speexin~ : receive error" ); + perror( "recv" ); + return; + } + else + { + // post( "speexin~ : received %d bytes at %d on %d ( up to %d)", + // ret, x->x_inwritepos, x->x_socket, + // x->x_inbuffersize ); + + if ( ret == 0 ) + { + post( "speexin~ : closing connection ( s=%d )", x->x_socket ); + speexin_closesocket(x->x_socket); + x->x_socket = -1; + sys_vgui(".x%x.c delete rectangle %xPBAR\n", x->x_canvas, x ); + sys_vgui(".x%x.c delete line %xTHRESHOLD\n", x->x_canvas, x ); + sys_vgui(".x%x.c delete rectangle %xSTATUS\n", x->x_canvas, x ); + outlet_symbol( x->x_connectionip, gensym("") ); + } + else + { + x->x_inpackets++; + } + + x->x_encsize += ret; + x->x_inwritepos += ret; + + speexin_decode_input(x); + } +} + +static void speexin_acceptconnection(t_speexin *x) +{ + struct sockaddr_in incomer_address; + int sockaddrl = (int) sizeof( struct sockaddr ); + + int fd = accept(x->x_serversocket, (struct sockaddr*)&incomer_address, &sockaddrl ); + post("speexin~: accepted incomer : %d.", fd ); + + if (fd < 0) { + post("speexin~: accept failed"); + return; + } + + if (x->x_socket > 0) { + post("speexin~: the source has changed to %s ( new socket = %d ).", + inet_ntoa( incomer_address.sin_addr ), fd ); + speexin_closesocket(x->x_socket); + } + + x->x_socket = fd; + sys_addpollfn(x->x_socket, (t_fdpollfn)speexin_recv, x); + outlet_symbol( x->x_connectionip, gensym( inet_ntoa( incomer_address.sin_addr) ) ); + + if ( x->x_graphic && glist_isvisible( x->x_canvas ) ) + { + t_int width; + + width = rtext_width( glist_findrtext( (t_glist*)x->x_canvas, (t_text *)x ) ); + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill lightblue -tags %xPBAR\n", + x->x_canvas, x->x_obj.te_xpix, x->x_obj.te_ypix-BARHEIGHT-1, + x->x_obj.te_xpix + width, x->x_obj.te_ypix - 1, x ); + } + x->x_stream = 0; + x->x_newstream = 1; + +} + + +static int speexin_startservice(t_speexin* x, int portno) +{ + struct sockaddr_in server; + int sockfd; + + /* create a socket */ + sockfd = socket(AF_INET, SOCK_STREAM, 0); + + if (sockfd < 0) + { + sys_sockerror("socket"); + return (0); + } + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + + /* assign server port number */ + server.sin_port = htons((u_short)portno); + post("listening to port number %d", portno); + + setsocketoptions(sockfd); + + /* name the socket */ + if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) { + sys_sockerror("bind"); + speexin_closesocket(sockfd); + return (0); + } + + if (listen(sockfd, 5) < 0) { + sys_sockerror("listen"); + speexin_closesocket(sockfd); + } + else + { + x->x_serversocket = sockfd; + sys_addpollfn(x->x_serversocket, (t_fdpollfn)speexin_acceptconnection, x); + } + + return 1; +} + +static void speexin_free(t_speexin *x) +{ + post( "speexin~ : free %x", x ); + if (x->x_serversocket > 0) { + post( "speexin~ : closing server socket" ); + speexin_closesocket(x->x_serversocket); + x->x_serversocket = -1; + } + if (x->x_socket > 0) { + post( "speexin~ : closing socket" ); + speexin_closesocket(x->x_socket); + x->x_socket = -1; + } + if ( x->x_inbuffer ) freebytes( x->x_inbuffer, x->x_inbuffersize ); + if ( x->x_outbuffer ) freebytes( x->x_outbuffer, x->x_outbuffersize*sizeof(t_float) ); + if ( x->x_decchunk ) freebytes(x->x_decchunk, x->x_framesize*sizeof(t_float)); +} + +static t_int *speexin_perform(t_int *w) +{ + t_speexin *x = (t_speexin*) (w[1]); + t_float *out = (t_float *)(w[2]); + t_int n = (int)(w[3]); + t_int bsize = n; + t_int ret; + t_int i = 0; + t_int j = 0; + t_int sp = 0; + t_int sratio; + + // samplerate is supposed to be above 16kHz, thus sratio>1 + if ( x->x_mode == SPEEX_NB_MODE ) + { + sratio = x->x_samplerate / 8000; + } + else + { + sratio = x->x_samplerate / 16000; + } + // post( "speexin~ : ratio : %d", sratio ); + + memset( out, 0x0, n*sizeof(t_float ) ); + + sp = 0; + while( sp < n ) + { + if ( ( ( x->x_outunread > MIN_AUDIO_INPUT ) && x->x_newstream ) || // wait the buffer to load + ( x->x_stream ) // check that the stream provides enough data + ) + { + if ( x->x_newstream ) + { + x->x_newstream = 0; + x->x_stream = 1; + } + /* resampling */ + for ( j=0; j<sratio; j++ ) + { + *(out+sp)=*(x->x_outbuffer+x->x_ooutp)/8000; // input has been scaled + //*(x->x_outbuffer+x->x_ooutp)=0.0; // data read, now zeroed + sp++; + if ( sp >= n ) break; + } + x->x_ooutp = (x->x_ooutp + 1)%x->x_outbuffersize; + // if ( x->x_ooutp == x->x_outbuffersize - 1 ) post( "speexin~ : end of audio buffer" ); + x->x_outunread-=1; + } + else + { + for ( j=0; j<sratio; j++ ) + { + *(out+sp)=0.0; + sp++; + if ( sp >= n ) break; + } + } + } + x->x_pblocks++; + + if ( ( x->x_outunread <= MIN_AUDIO_INPUT/10 ) && ( x->x_stream ) ) + { + // post( "speexin~ : stream lost (too little input)" ); + x->x_stream = 0; + x->x_newstream = 1; // waiting for a new stream + } + + if ( x->x_pblocks == x->x_framesize/bsize ) + { + x->x_inpackets--; + x->x_pblocks = 0; + } + + return (w+4); +} + +static void speexin_dsp(t_speexin *x, t_signal **sp) +{ + dsp_add(speexin_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); +} + + +static void *speexin_new(t_floatarg fportno, t_floatarg fdographics) +{ + t_speexin *x; + int i; + + if ( fportno < 0 || fportno > 65535 ) + { + post( "speexin~ : error : wrong portnumber : %d", (int)fportno ); + return NULL; + } + if ( ((int)fdographics != 0) && ((int)fdographics != 1.) ) + { + post( "speexin~ : error : constructor : speexin~ <portnumber> [graphic flag = 0 | 1 ] ( got = %f)", fdographics ); + return NULL; + } + + x = (t_speexin *)pd_new(speexin_class); + outlet_new(&x->x_obj, &s_signal); + x->x_connectionip = outlet_new(&x->x_obj, &s_symbol); + + x->x_serversocket = -1; + x->x_socket = -1; + x->x_inpackets = 0; + x->x_inwritepos = 0; + x->x_dpacket = -1; + x->x_mode = SPEEX_NB_MODE; + x->x_samplerate = sys_getsr(); + + x->x_canvas = canvas_getcurrent(); + + x->x_inbuffersize = INPUT_BUFFER_SIZE; + x->x_outbuffersize = OUTPUT_BUFFER_SIZE; + x->x_inbuffer = (char*) getbytes( x->x_inbuffersize ); + memset( x->x_inbuffer, 0x0, INPUT_BUFFER_SIZE ); + x->x_outbuffer = (t_float*) getbytes( x->x_outbuffersize*sizeof(t_float) ); + memset( x->x_outbuffer, 0x0, OUTPUT_BUFFER_SIZE*sizeof(t_float) ); + + if ( !x->x_inbuffer || !x->x_outbuffer ) + { + post( "speexin~ : could not allocate buffers." ); + return NULL; + } + + x->x_encsize = 0; + x->x_oinp = 0; + x->x_ooutp = 0; + + ztout.tv_sec = 0; + ztout.tv_usec = 0; + + x->x_graphic = (int)fdographics; + + post( "speexin~ : starting service on port %d", (int)fportno ); + speexin_startservice(x, (int)fportno); + + // init speex decoder + speexin_tilde_speex_init(x); + + x->x_decchunk = (t_float*)getbytes(x->x_framesize*sizeof(t_float)); + if (!x->x_decchunk) /* check allocation... */ + { + error("speexin~ : cannot allocate chunk"); + return NULL; + } + + return (x); +} + + +void speexin_tilde_setup(void) +{ + post( speexin_version ); + speexin_class = class_new(gensym("speexin~"), + (t_newmethod) speexin_new, (t_method) speexin_free, + sizeof(t_speexin), CLASS_NOINLET, A_DEFFLOAT, A_DEFFLOAT, A_NULL); + + class_addmethod(speexin_class, nullfn, gensym("signal"), 0); + class_addmethod(speexin_class, (t_method) speexin_dsp, gensym("dsp"), 0); + class_sethelpsymbol(speexin_class, gensym("help-speex~.pd")); +} diff --git a/speex~/speexout~.c b/speex~/speexout~.c new file mode 100644 index 0000000..793f7ec --- /dev/null +++ b/speex~/speexout~.c @@ -0,0 +1,450 @@ +/* ------------------------ speexout~ ----------------------------------------- */ +/* */ +/* Tilde object to send speex encoded data to a peer using speexin~. */ +/* Written by Yves Degoyon (ydegoyon@free.fr). */ +/* Tarballs and updates @ http://ydegoyon.free.fr */ +/* */ +/* This program is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU General Public License */ +/* as published by the Free Software Foundation; either version 2 */ +/* of the License, or (at your option) any later version. */ +/* */ +/* See file LICENSE for further informations on licensing terms. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* Uses the Speex codec which can */ +/* be found at http://speex.sourceforge.net */ +/* */ +/* "Western values mean nothing to us" */ +/* "She is beyond good and evil" */ +/* Pop Group -- */ +/* ---------------------------------------------------------------------------- */ + + + +#ifdef NT +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4305 ) +#endif + +#include <sys/types.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <malloc.h> +#include <ctype.h> +#ifdef UNIX +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <time.h> +#include <sys/time.h> +#define SOCKET_ERROR -1 +#else +#include <io.h> +#include <windows.h> +#include <winsock.h> +#include <windef.h> +#endif + +#include <speex.h> /* speex codec stuff */ +#include <speex_bits.h> /* speex codec stuff */ + +#include "m_pd.h" /* standard pd stuff */ + +#define IN_BUFFER_SIZE 65536 +#define OUT_BUFFER_SIZE 8192 + +// #define DATADEBUG + +#define SPEEX_NB_MODE 0 /* audio data must be 8kHz */ +#define SPEEX_WB_MODE 1 /* audio data must be 16kHz */ + +#define SPEEX_DEFAULT_QUALITY 5 /* default quality */ + +static char *speexout_version = "speexout~: speex voice quality streamer version 0.2, written by ydegoyon@free.fr"; + +static t_class *speexout_class; + +typedef struct _speexout +{ + t_object x_obj; + int x_samplerate; /* pd sampling rate */ + + /* Speex stuff */ + SpeexBits x_bits; /* bits packing structure */ + void *x_encstate; /* encoder state */ + t_int x_framesize; /* frame size */ + t_int x_mode; /* Narrow or Wide Band */ + int x_quality; /* encoding quality ( 0 to 10 ) */ + + /* buffer stuff */ + unsigned short x_inp; /* in position for buffer */ + unsigned short x_outp; /* out position for buffer */ + t_int x_encsize; /* size of encoded data */ + t_float *x_inbuf; /* data to be coded by Speex */ + char *x_outbuf; /* data returned by Speex -> our speex stream */ + int x_bytesbuffered; /* number of unprocessed bytes in buffer */ + int x_bytesemitted; /* number of encoded bytes emitted */ + int x_start; + t_float *x_encchunk; + + /* connection data */ + int x_fd; /* info about connection status */ + int x_outpackets; /* speex packets sent */ + + t_float x_f; /* float needed for signal input */ + +} t_speexout; + + + /* encode PCM data to speex frames */ +static void speexout_encode(t_speexout *x) +{ + if ( x->x_bytesbuffered > x->x_framesize ) + { + speex_bits_reset(&x->x_bits); + + { + t_int sp=0, rp=0; + + while( sp < x->x_framesize ) + { + rp=(x->x_outp+sp)%IN_BUFFER_SIZE; + // post( "speexout~ : sp=%d : rp=%d", sp, rp ); + x->x_encchunk[ sp++ ] = *(x->x_inbuf+rp); + } + speex_encode(x->x_encstate, x->x_encchunk, &x->x_bits); + } + + x->x_outp = (x->x_outp+x->x_framesize)%IN_BUFFER_SIZE; + x->x_bytesbuffered -= x->x_framesize; + x->x_encsize = speex_bits_write(&x->x_bits, x->x_outbuf+1, OUT_BUFFER_SIZE ); + if ( x->x_encsize < 127 ) + { + *(x->x_outbuf) = (char)x->x_encsize; + } + else + { + post( "speexout~ : encoding error : frame is more than 127 bytes" ); + x->x_encsize = -1; + } + x->x_bytesemitted += x->x_encsize; +#ifdef DATADEBUG + { + t_int si; + + printf( "speexout~ : encoded : " ); + for ( si=0; si<x->x_encsize; si++ ) + { + printf( "%d ", *(x->x_outbuf+si) ); + } + printf( "\n" ); + } +#endif + } + else + { + x->x_encsize = -1; + } +} + + /* stream data to the peer */ +static void speexout_stream(t_speexout *x) +{ + int count = -1, i; + + if ( x->x_encsize > 0 ) + { + count = send(x->x_fd, x->x_outbuf, x->x_encsize+1, MSG_NOSIGNAL); + if(count < 0) + { + error("speexout~: could not send encoded data to the peer (%d)", count); +#ifndef UNIX + closesocket(x->x_fd); +#else + close(x->x_fd); +#endif + x->x_fd = -1; + outlet_float(x->x_obj.ob_outlet, 0); + } + else + { + x->x_outpackets++; + // post( "speexout~ : emitted %d bytes (packets = %d)", count, x->x_outpackets ); + if ( x->x_outpackets%100 == 0 ) + { + // post( "speexout~ : emitted %d bytes (packets = %d)", x->x_bytesemitted, x->x_outpackets ); + } + if(count != x->x_encsize+1) + { + error("speexout~: %d bytes skipped", x->x_encsize - count); + } + } + x->x_encsize = -1; + } +} + + + /* buffer and downsample the data */ +static t_int *speexout_perform(t_int *w) +{ + t_float *in = (t_float *)(w[1]); /* audio inlet */ + t_speexout *x = (t_speexout *)(w[2]); + int n = (int)(w[3]); /* number of samples */ + unsigned short i,wp; + t_float accum = 0.; + int sratio; + + /* samplerate is supposed to be > 16kHz, thus sratio > 1 */ + if ( x->x_mode == SPEEX_NB_MODE ) + { + sratio = x->x_samplerate / 8000; + } + else + { + sratio = x->x_samplerate / 16000; + } + + /* copy the data into the buffer and resample audio data */ + + accum=0; + for(wp = 0; wp < n; wp++) + { + accum += *(in+wp); + if ( wp % sratio == sratio - 1 ) + { + x->x_inbuf[x->x_inp] = ( accum / sratio ) * 8000; // scale the input for speex best efficiency + // post( "x->x_inp : %d", x->x_inp ); + x->x_inp = (x->x_inp+1)%IN_BUFFER_SIZE; + x->x_bytesbuffered ++; + accum = 0; + } + } + + if( ( x->x_fd >= 0 ) && ( x->x_bytesbuffered > x->x_framesize ) ) + { + /* encode and send to the peer */ + speexout_encode(x); /* speex encoding */ + speexout_stream(x); /* stream mp3 to the peer */ + } + else + { + x->x_start = -1; + } + return (w+4); +} + +static void speexout_dsp(t_speexout *x, t_signal **sp) +{ + dsp_add(speexout_perform, 3, sp[0]->s_vec, x, sp[0]->s_n); +} + + /* initialize the speex library */ +static void speexout_tilde_speex_init(t_speexout *x) +{ + + speex_bits_init(&x->x_bits); + + switch ( x->x_mode ) + { + case SPEEX_NB_MODE : + x->x_encstate = speex_encoder_init(&speex_nb_mode); + break; + + case SPEEX_WB_MODE : + x->x_encstate = speex_encoder_init(&speex_wb_mode); + break; + + default : + error( "speexout~ : severe error : encoding scheme is unknown" ); + break; + } + + speex_encoder_ctl(x->x_encstate, SPEEX_GET_FRAME_SIZE, (void*)&x->x_framesize); + post( "speexout~ : frame size : %d", x->x_framesize ); + +} + + /* connect to the peer */ +static void speexout_connect(t_speexout *x, t_symbol *hostname, t_floatarg fportno) +{ + struct sockaddr_in csocket; + struct hostent *hp; + int portno = fportno; /* get port from message box */ + + /* variables used for communication with the peer */ + const char *buf = 0; + unsigned int len; + int sockfd; + +#ifndef UNIX + unsigned int ret; +#else + int ret; +#endif + + if (x->x_fd >= 0) + { + error("speexout~: already connected"); + return; + } + + sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd < 0) + { + error("speexout~: internal error while attempting to open socket"); + return; + } + + /* connect socket using hostname provided in command line */ + csocket.sin_family = AF_INET; + hp = gethostbyname(hostname->s_name); + if (hp == 0) + { + post("speexout~: bad host?"); +#ifndef UNIX + closesocket(sockfd); +#else + close(sockfd); +#endif + return; + } + memcpy((char *)&csocket.sin_addr, (char *)hp->h_addr, hp->h_length); + + /* assign client port number */ + csocket.sin_port = htons((unsigned short)portno); + + /* try to connect. */ + post("speexout~: connecting to port %d", portno); + if (connect(sockfd, (struct sockaddr *) &csocket, sizeof (csocket)) < 0) + { + error("speexout~: connection failed!\n"); +#ifndef UNIX + closesocket(sockfd); +#else + close(sockfd); +#endif + return; + } + + x->x_fd = sockfd; + x->x_outpackets = 0; + outlet_float( x->x_obj.ob_outlet, 1 ); + post( "speexout~ : connected to peer" ); + + +} + + /* close connection to the peer */ +static void speexout_disconnect(t_speexout *x) +{ + + int err = -1; + + if(x->x_fd >= 0) /* close socket */ + { +#ifndef UNIX + closesocket(x->x_fd); +#else + close(x->x_fd); +#endif + x->x_fd = -1; + outlet_float( x->x_obj.ob_outlet, 0 ); + post("speexout~: connection closed"); + } +} + + /* settings for encoding quality */ +static void speexout_quality(t_speexout *x, t_floatarg fquality ) +{ + if ( fquality < 0 || fquality > 10 ) { + post( "speexout~ : wrong quality." ); + return; + } + x->x_quality = fquality; + post("speexout~: setting quality to : %d", x->x_quality); + speex_encoder_ctl(x->x_encstate, SPEEX_SET_QUALITY, &x->x_quality); +} + + /* clean up */ +static void speexout_free(t_speexout *x) +{ + + speex_bits_destroy(&x->x_bits); + + speex_encoder_destroy(x->x_encstate); + + post("speexout~: encoder destroyed"); + + if(x->x_fd >= 0) +#ifndef UNIX + closesocket(x->x_fd); +#else + close(x->x_fd); +#endif + freebytes(x->x_inbuf, IN_BUFFER_SIZE*sizeof(t_float)); + freebytes(x->x_outbuf, OUT_BUFFER_SIZE); + freebytes(x->x_encchunk, x->x_framesize*sizeof(t_float)); +} + +static void *speexout_new(t_symbol *s, int argc, t_atom *argv) +{ + t_speexout *x = (t_speexout *)pd_new(speexout_class); + outlet_new( &x->x_obj, &s_float ); + + x->x_mode = SPEEX_NB_MODE; + x->x_quality = SPEEX_DEFAULT_QUALITY; + x->x_fd = -1; + x->x_outpackets = 0; + x->x_samplerate = sys_getsr(); + x->x_inbuf = getbytes(IN_BUFFER_SIZE*sizeof(t_float)); /* buffer for encoder input */ + x->x_outbuf = getbytes(OUT_BUFFER_SIZE); /* our mp3 stream */ + if ((!x->x_inbuf)||(!x->x_outbuf)) /* check buffers... */ + { + error("speexout~ : cannot allocate buffers"); + return NULL; + } + x->x_bytesbuffered = 0; + x->x_bytesemitted = 0; + x->x_inp = 0; + x->x_outp = 0; + x->x_encsize = 0; + x->x_start = -1; + speexout_tilde_speex_init(x); + + x->x_encchunk = (t_float*)getbytes(x->x_framesize*sizeof(t_float)); + if (!x->x_encchunk) /* check allocation... */ + { + error("speexout~ : cannot allocate chunk"); + return NULL; + } + return(x); +} + +void speexout_tilde_setup(void) +{ + post(speexout_version); + speexout_class = class_new(gensym("speexout~"), (t_newmethod)speexout_new, (t_method)speexout_free, + sizeof(t_speexout), 0, A_GIMME, 0); + CLASS_MAINSIGNALIN(speexout_class, t_speexout, x_f ); + class_sethelpsymbol(speexout_class, gensym("help-speex~.pd")); + class_addmethod(speexout_class, (t_method)speexout_dsp, gensym("dsp"), 0); + class_addmethod(speexout_class, (t_method)speexout_connect, gensym("connect"), A_SYMBOL, A_FLOAT, 0); + class_addmethod(speexout_class, (t_method)speexout_disconnect, gensym("disconnect"), 0); + class_addmethod(speexout_class, (t_method)speexout_quality, gensym("quality"), A_FLOAT, 0); +} + |