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 --- mp3fileout~.c | 558 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 558 insertions(+) create mode 100644 mp3fileout~.c (limited to 'mp3fileout~.c') diff --git a/mp3fileout~.c b/mp3fileout~.c new file mode 100644 index 0000000..a24b448 --- /dev/null +++ b/mp3fileout~.c @@ -0,0 +1,558 @@ +/* ------------------------ mp3fileout~ --------------------------------------- */ +/* */ +/* Tilde object to send an mp3 file to a peer using mp3streamin~ */ +/* 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 LAME MPEG 1 Layer 3 encoding library (lame_enc.dll) which can */ +/* be found at http://www.cdex.n3.net. */ +/* */ +/* "See mass murder on a scale you've never seen" */ +/* "And all the one who tried hard to succeed" */ +/* You know who, don't you ??? */ +/* ---------------------------------------------------------------------------- */ + + +#include +#include +#include + +#include +#include +#ifdef _WIN32 +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SOCKET_ERROR -1 +#endif /* _WIN32 */ + +#if defined(__APPLE__) || defined(_WIN32) +#define MSG_NOSIGNAL 0 +#endif + + +#include "mpg123.h" /* mpg123 decoding library from lame 3.92 */ +#include "mpglib.h" /* mpglib decoding library from lame 3.92 */ +#include "interface.h" /* mpglib decoding library from lame 3.92 */ + +#define INPUT_BUFFER_SIZE 32768 +#define OUTPUT_BUFFER_SIZE 32768 +#define MAX_FRAME_SIZE 1152 + +/* useful debugging functions from mpglib */ +extern int decode_header( struct frame* fr, unsigned long newhead ); +extern void print_header_compact( struct frame* fr ); +extern int head_check( unsigned long head, int check_layer ); + +/* time-out used for select() call */ +static struct timeval ztout; + +static char *mp3fileout_version = "mp3fileout~: mp3 file streamer version 0.2, written by ydegoyon@free.fr"; + +extern void sys_sockerror(char *s); + +void mp3fileout_closesocket(int fd) +{ +#ifdef UNIX + if ( close(fd) < 0 ) + { + perror( "close" ); + } + else + { + post( "mp3fileout~ : closed socket : %d", fd ); + } +#endif +#ifdef NT + closesocket(fd); +#else + sys_rmpollfn(fd); +#endif +} + +/* ------------------------ mp3fileout~ ----------------------------- */ + +static t_class *mp3fileout_class; + +typedef struct _mp3fileout +{ + t_object x_obj; + t_int x_socket; + t_int x_fd; /* file descriptor for the mp3 file */ + t_int x_eof; /* end of file is reached */ + t_int x_emit; /* indicates the ability to emit */ + t_int x_nbwaitloops;/* synchronization cycles count */ + t_int x_blocksize; /* actual blocksize */ + + void *x_inbuffer; /* accumulation buffer for read mp3 frames */ + t_int x_inwriteposition; + t_int x_inbuffersize; + t_int x_framesize; + t_int x_offset; /* offset used for decoding */ + t_int x_nbloops; /* number of perform loops */ + + void *x_outbuffer; /* buffer to be emitted */ + t_int x_outframes; /* number of frames emitted */ + t_int x_outbuffersize; + t_int x_outavable; /* number of available bytes to emit */ + + t_canvas *x_canvas; + + t_outlet *x_connected; /* indicates state of the connection */ + t_outlet *x_endreached;/* indicates the end of file */ + t_outlet *x_frames; /* indicates the number of frames emitted */ + +} t_mp3fileout; + +static int mp3fileout_search_header(t_mp3fileout *x) +{ + t_int i; + t_int length = 0; + struct frame hframe; + unsigned long cheader; + t_int ret = sizeof( unsigned long); + t_int foffset = 0; + unsigned int a,b,c,d; + unsigned char buf[sizeof(unsigned long)]; + t_float nbsamplesframe = 0; + + while( ret>0 ) + { + ret = read( x->x_fd, (void *)buf, sizeof( unsigned long ) ); + + foffset+=ret; + + if ( ret>0 ) + { + /* check for a valid header */ + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + cheader = 0; + cheader = a; + cheader <<= 8; + cheader |= b; + cheader <<= 8; + cheader |= c; + cheader <<= 8; + cheader |= d; + if ( head_check( cheader, 0 ) ) + { + decode_header( &hframe, cheader ); + // print_header_compact( &hframe ); + x->x_framesize = hframe.framesize; + nbsamplesframe = ( hframe.mpeg25 ? 576 : 1152 ); + x->x_nbwaitloops = (int)(nbsamplesframe/x->x_blocksize); + if ( x->x_nbwaitloops == 0 ) x->x_nbwaitloops = 1; + // post ( "mp3fileout~ : will wait %d loops", x->x_nbwaitloops ); + + // rewind file to the start of the frame + if ( lseek( x->x_fd, -sizeof(unsigned long), SEEK_CUR ) < 0 ) + { + post( "mp3fileout~ : could not rewind file." ); + } + if ( x->x_outframes == 0 ) + { + post( "mp3fileout~ : found firstframe @ %d", foffset ); + } + break; + } + // post( "mp3fileout~ : read %d bytes.", ret ); + } + else + { + if ( ret < 0 ) + { + post( "mp3fileout~ : error encountered ( ret=%d )...file reading done.", ret ); + perror( "read" ); + x->x_eof = 1; + } + else + { + post( "mp3fileout~ : file reading done.", ret ); + x->x_eof = 1; + outlet_bang( x->x_endreached ); + } + return -1; + } + + } + + return x->x_framesize; +} + +static int mp3fileout_read_frame(t_mp3fileout *x) +{ + int size, ret; + + if ( x->x_fd > 0 && !x->x_eof) + { + if ( ( size = mp3fileout_search_header( x ) ) > 0 ) + { + if ( size+sizeof(unsigned long) > INPUT_BUFFER_SIZE ) + { + post( "mp3fileout~ : cannot read frame : size too big : %d", size ); + return -1; + } + // post( "mp3fileout~ : reading a frame : size : %d", size ); + ret = read( x->x_fd, x->x_inbuffer, size+sizeof(unsigned long) ); + + if ( ret>0 ) + { + memcpy( x->x_outbuffer, x->x_inbuffer, ret ); + x->x_outavable += ret; + return ret; + } + else + { + return -1; + } + } + else + { + return -1; + } + } + else + { + return -1; + } +} + +static int mp3fileout_send_frame(t_mp3fileout *x) +{ + int ret=0; + + if ( x->x_socket > 0 && x->x_emit ) + { + if ( ( ret = send( x->x_socket, x->x_outbuffer, x->x_outavable, MSG_NOSIGNAL ) ) < 0 ) + { + post( "mp3fileout~ : connection lost." ); + perror( "send" ); + x->x_socket = -1; + x->x_emit = 0; + return -1; + } + else + { + memcpy( x->x_outbuffer, x->x_outbuffer+ret, x->x_outbuffersize-ret ); + x->x_outavable -= ret; + x->x_outframes++; + outlet_float( x->x_frames, x->x_outframes ); + // post( "mp3fileout~ : sent %d bytes, x->x_outavable : %d", ret, x->x_outavable ); + } + + } + else + { + // artificially empty buffer + x->x_outavable = 0; + } + return ret; +} + +static void mp3fileout_free(t_mp3fileout *x) +{ + if (x->x_socket > 0) { + post( "mp3fileout~ : closing socket" ); + mp3fileout_closesocket(x->x_socket); + x->x_socket = -1; + } + if ( x->x_fd > 0 ) + { + if ( close( x->x_fd ) < 0 ) + { + post( "mp3fileout~ : could not close file." ); + perror( "close" ); + } + } + if ( x->x_inbuffer ) freebytes( x->x_inbuffer, x->x_inbuffersize ); +} + +static t_int *mp3fileout_perform(t_int *w) +{ + t_mp3fileout *x = (t_mp3fileout*) (w[1]); + int ret; + int i = 0; + + x->x_blocksize = (t_int)(w[2]); + // check new incoming data + if ( x->x_socket > 0 ) + { + if ( x->x_nbloops % x->x_nbwaitloops == 0 ) + { + /* read a frame in the file */ + if ( mp3fileout_read_frame(x) > 0 ) + { + /* send the frame to the peer */ + mp3fileout_send_frame(x); + } + } + x->x_nbloops = ( x->x_nbloops+1 ) % x->x_nbwaitloops; + } + return (w+3); +} + +static void mp3fileout_dsp(t_mp3fileout *x, t_signal **sp) +{ + dsp_add(mp3fileout_perform, 2, x, sp[0]->s_n); +} + + /* start streaming */ +static void mp3fileout_start(t_mp3fileout *x) +{ + x->x_emit = 1; + if ( x->x_fd > 0 ) + { + // reset file pointer + if ( lseek( x->x_fd, 0, SEEK_SET ) < 0 ) + { + post ( "mp3fileout~ : could not reset file pointer."); + x->x_eof = 1; + return; + } + x->x_eof = 0; + x->x_outframes = 0; + outlet_float( x->x_frames, x->x_outframes ); + } +} + + /* resume file reading */ +static void mp3fileout_resume(t_mp3fileout *x) +{ + x->x_emit = 1; +} + + /* seek in file */ +static void mp3fileout_seek(t_mp3fileout *x, t_floatarg foffset) +{ + if ( foffset < 0 ) + { + post( "mp3fileout~ : wrong offset."); + return; + } + if ( x->x_fd > 0 ) + { + // reset file pointer + if ( lseek( x->x_fd, (int)foffset, SEEK_SET ) < 0 ) + { + post ( "mp3fileout~ : could not reset file pointer."); + x->x_eof = 1; + return; + } + x->x_eof = 0; + } +} + + /* stop streaming */ +static void mp3fileout_stop(t_mp3fileout *x) +{ + x->x_emit = 0; +} + + /* open mp3 file */ +static void mp3fileout_open(t_mp3fileout *x, t_symbol *filename) +{ + // first close previous file + if ( x->x_fd > 0 ) + { + if ( close( x->x_fd ) < 0 ) + { + post( "mp3fileout~ : could not close file." ); + perror( "close" ); + } + x->x_outframes = 0; + outlet_float( x->x_frames, x->x_outframes ); + } + + if ( ( x->x_fd = open( filename->s_name, O_RDONLY ) ) < 0 ) + { + post( "mp3fileout~ : could not open file : %s", filename->s_name ); + perror( "open" ); + x->x_eof = 1; + } + else + { + x->x_eof = 0; + post( "mp3fileout~ : opened file : %s ( fd = %d )", filename->s_name, x->x_fd ); + } +} + + /* connect to the peer */ +static void mp3fileout_connect(t_mp3fileout *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 */ + unsigned int len; + int sockfd; + +#ifdef _WIN32 + unsigned int ret; +#else + int ret; +#endif + + if (x->x_socket >= 0) + { + error("mp3fileout~: already connected"); + return; + } + + sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd < 0) + { + error("mp3fileout~: 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("mp3fileout~: bad host?"); +#ifdef _WIN32 + 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("mp3fileout~: connecting to port %d", portno); + if (connect(sockfd, (struct sockaddr *) &csocket, sizeof (csocket)) < 0) + { + error("mp3fileout~: connection failed!\n"); +#ifdef _WIN32 + closesocket(sockfd); +#else + close(sockfd); +#endif + return; + } + + x->x_socket = sockfd; + x->x_outframes = 0; + outlet_float( x->x_frames, x->x_outframes ); + outlet_float( x->x_connected, 1 ); + post( "mp3fileout~ : connected to peer" ); + +} + + /* close connection to the peer */ +static void mp3fileout_disconnect(t_mp3fileout *x) +{ + + int err = -1; + if(x->x_socket >= 0) /* close socket */ + { +#ifdef _WIN32 + closesocket(x->x_socket); +#else + close(x->x_socket); +#endif + x->x_socket = -1; + outlet_float( x->x_connected, 0 ); + x->x_outframes = 0; + outlet_float( x->x_frames, x->x_outframes ); + post("mp3fileout~: connection closed"); + } +} + +static void *mp3fileout_new(void) +{ + t_mp3fileout *x; + int i; + + x = (t_mp3fileout *)pd_new(mp3fileout_class); + x->x_connected = outlet_new( &x->x_obj, &s_float ); + x->x_frames = outlet_new( &x->x_obj, &s_float ); + x->x_endreached = outlet_new( &x->x_obj, &s_bang ); + + x->x_socket = -1; + x->x_fd = -1; + x->x_eof = 0; + x->x_canvas = canvas_getcurrent(); + + x->x_offset = 0; + x->x_inbuffersize = INPUT_BUFFER_SIZE; + x->x_inbuffer = (char*) getbytes( x->x_inbuffersize ); + if ( !x->x_inbuffer ) + { + post( "mp3fileout~ : could not allocate buffers." ); + return NULL; + } + memset( x->x_inbuffer, 0x0, INPUT_BUFFER_SIZE ); + + x->x_outbuffersize = OUTPUT_BUFFER_SIZE; + x->x_outbuffer = (char*) getbytes( x->x_outbuffersize ); + if ( !x->x_outbuffer ) + { + post( "mp3fileout~ : could not allocate buffers." ); + return NULL; + } + memset( x->x_outbuffer, 0x0, OUTPUT_BUFFER_SIZE ); + x->x_outavable = 0; + + x->x_inwriteposition = 0; + x->x_nbloops = 0; + x->x_nbwaitloops = 1; + + return (x); +} + +void mp3fileout_tilde_setup(void) +{ + post( mp3fileout_version ); + mp3fileout_class = class_new(gensym("mp3fileout~"), + (t_newmethod) mp3fileout_new, (t_method) mp3fileout_free, + sizeof(t_mp3fileout), 0, A_NULL); + + class_addmethod(mp3fileout_class, nullfn, gensym("signal"), 0); + class_addmethod(mp3fileout_class, (t_method) mp3fileout_dsp, gensym("dsp"), 0); + class_addmethod(mp3fileout_class, (t_method)mp3fileout_connect, gensym("connect"), A_SYMBOL, A_FLOAT, 0); + class_addmethod(mp3fileout_class, (t_method)mp3fileout_open, gensym("open"), A_SYMBOL, 0); + class_addmethod(mp3fileout_class, (t_method)mp3fileout_disconnect, gensym("disconnect"), 0); + class_addmethod(mp3fileout_class, (t_method)mp3fileout_start, gensym("start"), 0); + class_addmethod(mp3fileout_class, (t_method)mp3fileout_resume, gensym("resume"), 0); + class_addmethod(mp3fileout_class, (t_method)mp3fileout_seek, gensym("seek"), A_DEFFLOAT, 0); + class_addmethod(mp3fileout_class, (t_method)mp3fileout_stop, gensym("stop"), 0); +} -- cgit v1.2.1