From d145fa6f792d6c44da2feec90507adb94e40323e Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Sun, 31 Jul 2011 03:59:16 +0000 Subject: got everything building using the template Makefile svn path=/trunk/externals/unauthorized/; revision=15173 --- speexin~.c | 612 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 612 insertions(+) create mode 100644 speexin~.c (limited to 'speexin~.c') diff --git a/speexin~.c b/speexin~.c new file mode 100644 index 0000000..eff2601 --- /dev/null +++ b/speexin~.c @@ -0,0 +1,612 @@ +/* ------------------------ 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 +#include + +#include +#include +#include +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SOCKET_ERROR -1 +#endif + +#if defined(__APPLE__) || defined(_WIN32) +#define MSG_NOSIGNAL 0 +#define SOL_TCP IPPROTO_TCP +#endif + +#include /* speex decoder stuff */ +#include /* speex decoder stuff */ + +#ifdef _MSC_VER +#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; six_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; jx_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= 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~ [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); +} -- cgit v1.2.1