diff options
Diffstat (limited to 'mp3live~')
-rw-r--r-- | mp3live~/INSTALL | 21 | ||||
-rw-r--r-- | mp3live~/Makefile | 88 | ||||
-rw-r--r-- | mp3live~/README | 65 | ||||
-rw-r--r-- | mp3live~/help-mp3live~.pd | 118 | ||||
-rw-r--r-- | mp3live~/interface.h | 32 | ||||
-rw-r--r-- | mp3live~/mp3fileout~.c | 553 | ||||
-rw-r--r-- | mp3live~/mp3streamin~.c | 665 | ||||
-rw-r--r-- | mp3live~/mp3streamout~.c | 733 | ||||
-rw-r--r-- | mp3live~/mpg123.h | 136 | ||||
-rw-r--r-- | mp3live~/mpglib.h | 65 | ||||
-rw-r--r-- | mp3live~/test-streaming-mp3.pd | 96 |
11 files changed, 2572 insertions, 0 deletions
diff --git a/mp3live~/INSTALL b/mp3live~/INSTALL new file mode 100644 index 0000000..63db4c7 --- /dev/null +++ b/mp3live~/INSTALL @@ -0,0 +1,21 @@ +You need to get lame > v3.90 installed first.
+libmp3lame.so is searched in /usr/local/lib
+( no time to write configure scripts ).
+if it's installed elsewhere, change the Makefile,
+you won't die from that.
+
+untar in /my/pd/dir/externs
+
+cd /my/pd/dir/externs/mp3live~
+
+make clean
+
+make
+
+make install
+
+open help-mp3live~.pd
+
+Thanx for getting here.
+
+Yves/
diff --git a/mp3live~/Makefile b/mp3live~/Makefile new file mode 100644 index 0000000..a9d6228 --- /dev/null +++ b/mp3live~/Makefile @@ -0,0 +1,88 @@ +NAME=mp3streamout~
+CSYM=mp3streamout_tilde
+NAMEB=mp3streamin~
+CSYMB=mp3streamin_tilde
+NAMEC=mp3fileout~
+CSYMC=mp3fileout_tilde
+
+current: pd_linux
+
+# ----------------------- NT -----------------------
+
+pd_nt: $(NAME).dll $(NAMEB).dll $(NAMEC).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 $(NAMEC).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 $(NAMEC).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 $(NAMEC).pd_linux
+
+.SUFFIXES: .pd_linux
+
+LINUXCFLAGS = -g -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 -lc -lm -L/usr/local/lib -lmp3lame
+ #strip --strip-unneeded $*.pd_linux
+ rm -f $*.o ../$*.pd_linux
+ ln -s mp3live~/$*.pd_linux ..
+
+
+
+# ----------------------------------------------------------
+
+install:
+ cp help-*.pd ../../doc/5.reference
+
+clean:
+ rm -f *.o *.pd_* so_locations
diff --git a/mp3live~/README b/mp3live~/README new file mode 100644 index 0000000..3af0dbb --- /dev/null +++ b/mp3live~/README @@ -0,0 +1,65 @@ +Version 0.01 +copyleft (c) 2001 by Yves Degoyon + +mp3live~ is a peer-to-peer mp3 streamer package +consisting of three objects : mp3streamout~, mp3fileout~ and mp3streamin~. + +To install mp3live~, follow the steps from INSTALL + +This software is published under GPL terms. + +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. + +***************************************************************************** + +mp3live~ has been compiled for Linux using LAME 3.92. +The newest version of LAME can be found at sourceforge.net + +COPYING: you may use this source under GPL terms! + +PLEASE NOTE: This software may contain patented alogrithm (at least + patented in some countries). It may be not allowed to sell/use products + based on this source code in these countries. Check this out first! + +COPYRIGHT of MP3 music: + Please note, that the duplicating of copyrighted music without explicit + permission violates the rights of the owner. + And remember, copyrighted music sucks [ as well as corporate ] + +***************************************************************************** + + using mp3live~ external for Pure Data + +Open the help-mp3live~.pd to understand how it works. + +A note about MPEG encoding : + +ALLOWED QUALITY FACTOR : + + -q <arg> <arg> = 0...9. Default -q 5 + -q 0: Highest quality, very slow + -q 9: Poor quality, but fast + -h Same as -q 2. Recommended. + -f Same as -q 7. Fast, ok quality + +ALLOWED BITRATES : + +bitrates (kbps): 32 40 48 56 64 80 96 112 128 160 192 224 256 320 + +BUGS : + +1. +You cannot create more than MAX_DECODERS mp3streamin~ objects. The actual value is 100. + +2. +Current version of lame ( 3.92 ) produces a lot of errors for quality < 5. Blame it on lame !!!! + +3. +Mono is not supported. Some additional code should be added for mp3streamin~. Blame it on me !!! + +4. +Resampling is not supported. Blame it on me !!! diff --git a/mp3live~/help-mp3live~.pd b/mp3live~/help-mp3live~.pd new file mode 100644 index 0000000..ac68464 --- /dev/null +++ b/mp3live~/help-mp3live~.pd @@ -0,0 +1,118 @@ +#N canvas 11 -3 941 684 10; +#X msg 35 594 \; pd dsp 1; +#X text 432 123 <-- settings for mp3 stream; +#X text 492 177 1 = joint stereo (default); +#X text 456 165 mode: 0 = stereo; +#X text 492 189 2 = dual channel; +#X msg 101 594 \; pd dsp 0; +#X obj 551 317 dac~; +#X msg 368 80 disconnect; +#X floatatom 221 227 5 0 0; +#X obj 221 199 mp3streamout~; +#X msg 367 54 connect localhost 5000; +#X msg 767 69 bang; +#X obj 767 89 openpanel; +#X obj 766 186 soundfiler; +#X floatatom 767 210 10 0 0; +#X obj 767 111 t s b; +#X obj 767 148 pack s s; +#X msg 766 169 read -resize \$1 \$2; +#X obj 803 111 float \$0; +#X text 755 50 Step 1 : Load a sound file; +#X obj 767 128 route float; +#X obj 123 149 tabplay~ \$0-sample; +#X msg 123 120 bang; +#X obj 114 627 table \$0-sample; +#X obj 821 148 makefilename %d-sample; +#X text 371 34 Step 2 : connect the streamer; +#X text 8 100 Step 3 : emit a sound through the streamer; +#X obj 234 276 env~; +#X obj 67 310 timer; +#X obj 207 276 > 0; +#X obj 159 276 route 1; +#X obj 122 276 t b f; +#X floatatom 121 340 5 0 0; +#X obj 80 276 spigot; +#X msg 102 256 0; +#X msg 130 256 1; +#X text 453 135 (bitrate \, mode \, quality); +#X text 453 227 Note : resampling is not supported for now; +#X text 454 238 Note : see the README for allowed bitrate; +#X text 30 9 mp3live~ : mp3streamout~ / mp3streamin~; +#X text 30 20 peer-to-peer mp3 streaming \, written by ydegoyon@free.fr +; +#X msg 342 147 mpeg 32 2 9; +#X text 492 202 3 = mono ( not supported ); +#X text 455 153 bitrate: bitrate of stream \, def. 128kbit/s; +#X text 452 215 quality: 5 = high \, 9 = low; +#X text 85 360 streamer latency (ms); +#X obj 35 573 loadbang; +#X msg 343 123 mpeg 128 0 5; +#X obj 536 270 mp3streamin~ 5000 1; +#X symbolatom 639 300 10 0 0; +#X text 622 316 Incomer's address; +#X floatatom 229 496 5 0 0; +#X floatatom 327 498 5 0 0; +#X text 282 366 Step 3 bis : emit a file through the streamer; +#X msg 298 386 bang; +#X msg 299 423 open \$1; +#X msg 393 410 disconnect; +#X msg 393 391 connect localhost 5000; +#X msg 393 429 start; +#X msg 438 430 stop; +#X obj 298 406 openpanel; +#X text 292 522 Number of frames emitted; +#X text 160 515 Connection state; +#X text 31 36 Warning : mp3fileout~ will not read ANY mp3 file \,; +#X text 30 46 but \, at least \, those produced with mp3write~.; +#X msg 474 430 resume; +#X msg 524 429 seek 10000; +#X text 391 503 A bang is emitted at the end of the file; +#X obj 301 454 mp3fileout~; +#X obj 389 485 print thisistheend; +#X connect 7 0 9 0; +#X connect 9 0 8 0; +#X connect 10 0 9 0; +#X connect 11 0 12 0; +#X connect 12 0 15 0; +#X connect 13 0 14 0; +#X connect 15 0 16 0; +#X connect 15 1 18 0; +#X connect 16 0 17 0; +#X connect 17 0 13 0; +#X connect 18 0 20 0; +#X connect 20 0 24 0; +#X connect 21 0 9 0; +#X connect 21 0 9 1; +#X connect 22 0 21 0; +#X connect 22 0 28 0; +#X connect 22 0 35 0; +#X connect 24 0 16 1; +#X connect 27 0 29 0; +#X connect 28 0 32 0; +#X connect 29 0 30 0; +#X connect 30 0 31 0; +#X connect 31 0 33 0; +#X connect 32 0 34 0; +#X connect 33 0 28 1; +#X connect 34 0 33 1; +#X connect 35 0 33 1; +#X connect 41 0 9 0; +#X connect 46 0 0 0; +#X connect 47 0 9 0; +#X connect 48 0 6 0; +#X connect 48 0 27 0; +#X connect 48 1 6 1; +#X connect 48 2 49 0; +#X connect 54 0 60 0; +#X connect 55 0 68 0; +#X connect 56 0 68 0; +#X connect 57 0 68 0; +#X connect 58 0 68 0; +#X connect 59 0 68 0; +#X connect 60 0 55 0; +#X connect 65 0 68 0; +#X connect 66 0 68 0; +#X connect 68 0 51 0; +#X connect 68 1 52 0; +#X connect 68 2 69 0; diff --git a/mp3live~/interface.h b/mp3live~/interface.h new file mode 100644 index 0000000..de3136a --- /dev/null +++ b/mp3live~/interface.h @@ -0,0 +1,32 @@ +/* +** Copyright (C) 2000 Albert L. Faber +** +** 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. +** +** 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. +*/ + +#ifndef INTERFACE_H_INCLUDED +#define INTERFACE_H_INCLUDED + +// #include "common.h" +#include "interface.h" + +BOOL InitMP3(PMPSTR mp); +int decodeMP3(PMPSTR mp,unsigned char *inmemory,int inmemsize,char *outmemory,int outmemsize,int *done); +void ExitMP3(PMPSTR mp); + +/* added remove_buf to support mpglib seeking */ +void remove_buf(PMPSTR mp); + +#endif diff --git a/mp3live~/mp3fileout~.c b/mp3live~/mp3fileout~.c new file mode 100644 index 0000000..edadf8e --- /dev/null +++ b/mp3live~/mp3fileout~.c @@ -0,0 +1,553 @@ +/* ------------------------ 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 <m_imp.h> +#include <g_canvas.h> + +#include <sys/types.h> +#include <string.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> +#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 SOCKET_ERROR -1 +#else +#include <winsock.h> +#endif + +#ifdef NT +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4305 ) +#endif + +#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); +#endif + sys_rmpollfn(fd); +} + +/* ------------------------ 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; + +#ifndef UNIX + 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?"); +#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("mp3fileout~: connecting to port %d", portno); + if (connect(sockfd, (struct sockaddr *) &csocket, sizeof (csocket)) < 0) + { + error("mp3fileout~: connection failed!\n"); +#ifndef UNIX + 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 */ + { +#ifndef UNIX + 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); + class_sethelpsymbol(mp3fileout_class, gensym("help-mp3live~.pd")); +} diff --git a/mp3live~/mp3streamin~.c b/mp3live~/mp3streamin~.c new file mode 100644 index 0000000..954af64 --- /dev/null +++ b/mp3live~/mp3streamin~.c @@ -0,0 +1,665 @@ +/* ------------------------ mp3streamin~ -------------------------------------- */ +/* */ +/* Tilde object to receive an mp3-stream sent by a peer using mp3streamout~. */ +/* 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. */ +/* */ +/* "Repackage sex, your interests." */ +/* "Somehow, maintain the interest." */ +/* Gang Of Four -- Natural's Not In It */ +/* ---------------------------------------------------------------------------- */ + + +#include <m_imp.h> +#include <g_canvas.h> + +#include <sys/types.h> +#include <string.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> +#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 SOCKET_ERROR -1 +#else +#include <winsock.h> +#endif + +#ifdef NT +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4305 ) +#endif + +#define MIN_AUDIO_INPUT 8064 // we must a least have 8 chunks to play a correct sound +#define INPUT_BUFFER_SIZE MIN_AUDIO_INPUT +#define OUTPUT_BUFFER_SIZE 131072 /* 128k*/ +#define LAME_AUDIO_CHUNK_SIZE 1152 +#define BARHEIGHT 10 +#define MAX_DECODERS 10 + +/* 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 *mp3streamin_version = "mp3streamin~: mp3 peer-to-peer streamer version 0.3, written by ydegoyon@free.fr"; + +extern void sys_sockerror(char *s); + +void mp3streamin_closesocket(int fd) +{ +#ifdef UNIX + if ( close(fd) < 0 ) + { + perror( "close" ); + } + else + { + post( "mp3streamin~ : 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("mp3streamin~ : setsockopt TCP_NODELAY failed"); + perror( "setsockopt" ); + return -1; + } + else + { + post("mp3streamin~ : TCP_NODELAY set"); + } + +#ifdef UNIX + sockopt = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(int)) < 0) + { + post("mp3streamin~ : setsockopt SO_REUSEADDR failed"); + perror( "setsockopt" ); + return -1; + } + else + { + post("mp3streamin~ : setsockopt SO_REUSEADDR done."); + } +#endif + return 0; +} + + +/* ------------------------ mp3streamin~ ----------------------------- */ + +static t_class *mp3streamin_class; + +typedef struct _mp3streamin +{ + t_object x_obj; + t_int x_instance; + t_int x_socket; + t_int x_shutdown; + t_outlet *x_connectionip; + t_int x_serversocket; + 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 mp3 frames */ + t_int x_inwriteposition; + t_int x_inbuffersize; + + t_float *x_outbuffer; /* buffer to store audio decoded data */ + t_int x_outwriteposition; + t_int x_outreadposition; + t_int x_outunread; + t_int x_outbuffersize; + + 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_int x_bitrateindex; /* remember the bitrate index */ + +} t_mp3streamin; + +/* too bad, this needs to be static, + handling an array to enable several decoders in pd */ +static MPSTR mp[MAX_DECODERS]; /* decoder buffer */ +int nbinstances = 0; + +void mp3streamin_tilde_mpglib_init(t_mp3streamin *x) +{ + int ret; + + InitMP3(&mp[x->x_instance]); +} + +static int mp3streamin_decode_input(t_mp3streamin *x) +{ + int i; + int alength = 0; + struct frame hframe; + unsigned int a,b,c,d; + unsigned long cheader; + static char out[8192]; + signed short int *p = (signed short int *) out; + int pbytes; + int ret; + + if ( !x->x_shutdown ) + { + /* decode first 4 bytes as the header */ + a = *((unsigned char*)x->x_inbuffer); + b = *((unsigned char*)x->x_inbuffer+1); + c = *((unsigned char*)x->x_inbuffer+2); + d = *((unsigned char*)x->x_inbuffer+3); + + cheader = 0; + cheader = a; + cheader <<= 8; + cheader |= b; + cheader <<= 8; + cheader |= c; + cheader <<= 8; + cheader |= d; + if ( head_check( cheader, 0 ) ) + { + // post( "mp3streamin~ : valid header ( packet=%d)", x->x_inpackets ); + decode_header( &hframe, cheader ); + // print_header_compact( &hframe ); + // when the bitrate change, reinit decoder + if ( x->x_bitrateindex != hframe.bitrate_index ) + { + ExitMP3(&mp[x->x_instance]); + InitMP3(&mp[x->x_instance]); + x->x_bitrateindex = hframe.bitrate_index; + } + } + else + { + post( "mp3streamin~ : error : mp3 packet received without header" ); + // ignore data + x->x_inwriteposition = 0; + x->x_inpackets--; + return( -1 ); + } + + // post( "mp3streamin~ : decoding %d bytes framesize=%d", x->x_inwriteposition, hframe.framesize ); + ret = + decodeMP3(&mp[x->x_instance], (unsigned char*)x->x_inbuffer, + x->x_inwriteposition, (char *) p, sizeof(out), &pbytes); + + switch (ret) { + case MP3_OK: + switch (mp[x->x_instance].fr.stereo) { + case 1: + case 2: + alength = ((mp[x->x_instance].fr.stereo==1)?pbytes >> 1 : pbytes >> 2); + // post( "mp3streamin~ : processed %d samples", alength ); + // update outbuffer contents + for ( i=0; i<alength; i++ ) + { + if ( x->x_outunread >= x->x_outbuffersize-2 ) + { + post( "mp3streamin~ : too much input ... ignored" ); + continue; + } + *(x->x_outbuffer+x->x_outwriteposition) = ((t_float)(*p++))/32767.0; + x->x_outwriteposition = (x->x_outwriteposition + 1)%x->x_outbuffersize; + *(x->x_outbuffer+x->x_outwriteposition) = + ((mp[x->x_instance].fr.stereo==2)?((t_float)(*p++))/32767.0 : 0.0); + x->x_outwriteposition = (x->x_outwriteposition + 1)%x->x_outbuffersize; + x->x_outunread+=2; + + if ( x->x_outunread > MIN_AUDIO_INPUT && !x->x_stream ) + { + post( "mp3streamin~ : stream connected." ); + x->x_stream = 1; + } + } + + break; + default: + alength = -1; + break; + } + break; + + case MP3_NEED_MORE: + post( "mp3streamin~ : retry lame decoding (more data needed)." ); + alength = 0; + break; + + case MP3_ERR: + post( "mp3streamin~ : lame decoding failed." ); + alength = -1; + break; + + } + + x->x_inwriteposition = 0; + } + else + { + if ( x->x_outunread == 0 ) + { + post( "mp3streamin~ : connection closed" ); + mp3streamin_closesocket(x->x_socket); + x->x_stream = 0; + x->x_newstream = 0; + x->x_inpackets = 0; + x->x_inwriteposition = 0; + x->x_bitrateindex = -1; + x->x_socket=-1; + outlet_symbol( x->x_connectionip, gensym("") ); + } + } + + 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/LAME_AUDIO_CHUNK_SIZE )-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/LAME_AUDIO_CHUNK_SIZE)/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; + } + else + { + if ( x->x_shutdown ) + { + x->x_shutdown=0; + 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 ); + } + } + } + } + return alength; +} + +static void mp3streamin_recv(t_mp3streamin *x) +{ + int ret; + + if ( ( ret = recv(x->x_socket, (void*) (x->x_inbuffer + x->x_inwriteposition), + (size_t)((x->x_inbuffersize-x->x_inwriteposition)), + MSG_NOSIGNAL) ) < 0 ) + { + post( "mp3_streamin~ : receive error" ); + perror( "recv" ); + return; + } + else + { + // post( "streamin~ : received %d bytes at %d on %d ( up to %d)", + // ret, x->x_inwriteposition, x->x_socket, + // x->x_inbuffersize-x->x_inwriteposition*sizeof( unsigned long) ); + + if ( ret == 0 ) + { + /* initiate the shutdown phase */ + x->x_shutdown=1; + } + else + { + // check we don't overflow input buffer + if ( (x->x_inpackets+1)*x->x_packetsize > x->x_inbuffersize ) + { + post( "mp3streamin~ : too much input...resetting" ); + x->x_inpackets=0; + x->x_inwriteposition=0; + return; + } + x->x_inpackets++; + x->x_packetsize=ret; + if ( x->x_inpackets % 100 == 0 ) + { + // post( "mp3streamin~ : received %d packets", x->x_inpackets ); + } + x->x_inwriteposition += ret; + } + + mp3streamin_decode_input(x); + } +} + +static void mp3streamin_acceptconnection(t_mp3streamin *x) +{ + struct sockaddr_in incomer_address; + int sockaddrl = (int) sizeof( struct sockaddr ); + + int fd = accept(x->x_serversocket, (struct sockaddr*)&incomer_address, &sockaddrl ); + + if (fd < 0) { + post("mp3streamin~: accept failed"); + return; + } + + if (x->x_socket > 0) { + sys_addpollfn(fd, (t_fdpollfn)mp3streamin_recv, x); + if ( x->x_outunread != 0 ) + { + post("mp3streamin~: still have some data to decode, retry later you %s.", + inet_ntoa( incomer_address.sin_addr )); + mp3streamin_closesocket( fd ); + return; + } + post("mp3streamin~: the source has changed to %s.", + inet_ntoa( incomer_address.sin_addr )); + mp3streamin_closesocket(x->x_socket); + } + + x->x_socket = fd; + sys_addpollfn(x->x_socket, (t_fdpollfn)mp3streamin_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 mp3streamin_startservice(t_mp3streamin* 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"); + mp3streamin_closesocket(sockfd); + return (0); + } + + if (listen(sockfd, 5) < 0) { + sys_sockerror("listen"); + mp3streamin_closesocket(sockfd); + } + else + { + x->x_serversocket = sockfd; + sys_addpollfn(x->x_serversocket, (t_fdpollfn)mp3streamin_acceptconnection, x); + } + + return 1; +} + +static void mp3streamin_free(t_mp3streamin *x) +{ + post( "mp3streamin~ : free %x", x ); + if (x->x_serversocket > 0) { + post( "mp3streamin~ : closing server socket" ); + mp3streamin_closesocket(x->x_serversocket); + x->x_serversocket = -1; + } + if (x->x_socket > 0) { + post( "mp3streamin~ : closing socket" ); + mp3streamin_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_instance == nbinstances-1 ) + { + nbinstances--; + } +} + +static t_int *mp3streamin_perform(t_int *w) +{ + t_mp3streamin *x = (t_mp3streamin*) (w[1]); + t_float *out1 = (t_float *)(w[2]); + t_float *out2 = (t_float *)(w[3]); + int n = (int)(w[4]); + int bsize = n; + int ret; + int i = 0; + + while( n-- ) + { + if ( ( ( x->x_outunread > MIN_AUDIO_INPUT ) && x->x_newstream ) || // wait the buffer to load + ( ( x->x_shutdown ) && ( x->x_outunread >= 2 ) ) || // clean disconnection + ( x->x_stream ) // check that the stream provides enough data + ) + { + if ( x->x_newstream && !x->x_shutdown ) + { + x->x_newstream = 0; + x->x_stream = 1; + } + *out1++=*(x->x_outbuffer+x->x_outreadposition); + x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize; + *out2++=*(x->x_outbuffer+x->x_outreadposition); + x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize; + x->x_outunread-=2; + if ( n == 1 ) x->x_pblocks++; + } + else + { + *out1++=0.0; + *out2++=0.0; + } + } + + if ( ( x->x_outunread <= MIN_AUDIO_INPUT/10 ) && ( x->x_stream ) ) + { + post( "mp3streamin~ : stream lost (too little input)" ); + x->x_stream = 0; + x->x_newstream = 1; // waiting for a new stream + } + + if ( x->x_pblocks == LAME_AUDIO_CHUNK_SIZE/bsize ) + { + x->x_inpackets--; + x->x_pblocks = 0; + } + +#ifdef DO_MY_OWN_SELECT + // check new incoming data + if ( x->x_socket > 0 ) + { + fd_set readset; + fd_set exceptset; + + FD_ZERO(&readset); + FD_ZERO(&exceptset); + FD_SET(x->x_socket, &readset ); + FD_SET(x->x_socket, &exceptset ); + + if ( select( maxfd+1, &readset, NULL, &exceptset, &ztout ) >0 ) + { + if ( FD_ISSET( x->x_socket, &readset) || FD_ISSET( x->x_socket, &exceptset ) ) + { + /* receive data or error and decode it */ + mp3streamin_recv(x); + } + } + } +#endif + return (w+5); +} + +static void mp3streamin_dsp(t_mp3streamin *x, t_signal **sp) +{ + dsp_add(mp3streamin_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + + +static void *mp3streamin_new(t_floatarg fportno, t_floatarg fdographics) +{ + t_mp3streamin *x; + int i; + + if ( fportno < 0 || fportno > 65535 ) + { + post( "mp3streamin~ : error : wrong portnumber : %d", (int)fportno ); + return NULL; + } + if ( ((int)fdographics != 0) && ((int)fdographics != 1.) ) + { + post( "mp3streamin~ : error : constructor : mp3streamin~ <portnumber> [graphic flag = 0 | 1 ] ( got = %f)", fdographics ); + return NULL; + } + + x = (t_mp3streamin *)pd_new(mp3streamin_class); + post( "mp3streamin~ : new %x (instance = %d) %d", x, nbinstances, sizeof( MPSTR ) ); + outlet_new(&x->x_obj, &s_signal); + 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_shutdown = 0; + x->x_inpackets = 0; + x->x_dpacket = -1; + + 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 ); + + if ( !x->x_inbuffer || !x->x_outbuffer ) + { + post( "mp3streamin~ : could not allocate buffers." ); + return NULL; + } + + if ( nbinstances < MAX_DECODERS ) + { + x->x_instance = nbinstances++; + } + else + { + post( "mp3streamin~ : cannot create more decoders (memory issues), sorry" ); + return NULL; + } + + x->x_inwriteposition = 0; + x->x_outreadposition = 0; + x->x_outwriteposition = 0; + x->x_outunread = 0; + + ztout.tv_sec = 0; + ztout.tv_usec = 0; + + x->x_graphic = (int)fdographics; + + post( "mp3streamin~ : starting service on port %d", (int)fportno ); + mp3streamin_startservice(x, (int)fportno); + + // init lame decoder + mp3streamin_tilde_mpglib_init(x); + + return (x); +} + + +void mp3streamin_tilde_setup(void) +{ + post( mp3streamin_version ); + mp3streamin_class = class_new(gensym("mp3streamin~"), + (t_newmethod) mp3streamin_new, (t_method) mp3streamin_free, + sizeof(t_mp3streamin), CLASS_NOINLET, A_DEFFLOAT, A_DEFFLOAT, A_NULL); + + class_addmethod(mp3streamin_class, nullfn, gensym("signal"), 0); + class_addmethod(mp3streamin_class, (t_method) mp3streamin_dsp, gensym("dsp"), 0); + class_sethelpsymbol(mp3streamin_class, gensym("help-mp3live~.pd")); +} diff --git a/mp3live~/mp3streamout~.c b/mp3live~/mp3streamout~.c new file mode 100644 index 0000000..2bf561f --- /dev/null +++ b/mp3live~/mp3streamout~.c @@ -0,0 +1,733 @@ +/* ------------------------ mp3streamout~ ------------------------------------- */ +/* */ +/* Tilde object to send mp3-stream 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. */ +/* */ +/* "I'd sell my soul to god" */ +/* "If it could take away the pain." */ +/* Theo Hakola -- */ +/* ---------------------------------------------------------------------------- */ + + + +#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> +#include <lame/lame.h> /* lame encoder stuff */ +#define SOCKET_ERROR -1 +#else +#include <io.h> +#include <windows.h> +#include <winsock.h> +#include <windef.h> +#include "lame_enc.h" /* lame encoder stuff */ +#endif + +#include "m_pd.h" /* standard pd stuff */ + +#include "mpg123.h" /* sub-library MPGLIB included in lame */ +/* 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 ); + +#define MY_MP3_MALLOC_IN_SIZE 65536 + /* max size taken from lame readme */ +#define MY_MP3_MALLOC_OUT_SIZE 1.25*MY_MP3_MALLOC_IN_SIZE+7200 + +#define MAXDATARATE 320 /* maximum mp3 data rate is 320kbit/s */ +#define STRBUF_SIZE 32 + +static char *mp3streamout_version = "mp3streamout~: mp3 peer-to-peer streamer version 0.3, written by ydegoyon@free.fr"; + +#ifndef UNIX +static HINSTANCE dll = NULL; +static BEINITSTREAM initStream = NULL; +static BEENCODECHUNK encodeChunk = NULL; +static BEDEINITSTREAM deinitStream = NULL; +static BECLOSESTREAM closeStream = NULL; +static BEVERSION dllVersion = NULL; +static BEWRITEVBRHEADER writeVBRHeader = NULL; +#endif + +static t_class *mp3streamout_class; + +typedef struct _mp3streamout +{ + t_object x_obj; + + /* LAME stuff */ + int x_lame; /* info about encoder status */ + int x_lamechunk; /* chunk size for LAME encoder */ + int x_mp3size; /* number of returned mp3 samples */ + + /* buffer stuff */ + unsigned short x_inp; /* in position for buffer */ + unsigned short x_outp; /* out position for buffer*/ + short *x_mp3inbuf; /* data to be sent to LAME */ + char *x_mp3outbuf; /* data returned by LAME -> our mp3 stream */ + short *x_buffer; /* data to be buffered */ + int x_bytesbuffered; /* number of unprocessed bytes in buffer */ + int x_start; + + /* mp3 format stuff */ + int x_samplerate; + int x_bitrate; /* bitrate of mp3 stream */ + int x_mp3mode; /* mode (mono, joint stereo, stereo, dual mono) */ + int x_mp3quality; /* quality of encoding */ + + /* connection data */ + int x_fd; /* info about connection status */ + int x_outpackets; /* mp3 packets sent */ + + t_float x_f; /* float needed for signal input */ + +#ifdef UNIX + lame_global_flags* lgfp; +#endif +} t_mp3streamout; + + + /* encode PCM data to mp3 stream */ +static void mp3streamout_encode(t_mp3streamout *x) +{ + unsigned short i, wp; + int err = -1; + int n = x->x_lamechunk; + +#ifdef UNIX + if(x->x_lamechunk < (int)sizeof(x->x_mp3inbuf)) +#else + if(x->x_lamechunk < sizeof(x->x_mp3inbuf)) +#endif + { + error("not enough memory!"); + return; + } + + /* on start/reconnect set outpoint that it not interferes with inpoint */ + if(x->x_start == -1) + { + post("mp3streamout~: initializing buffers"); + /* we try to keep 2.5 times the data the encoder needs in the buffer */ + if(x->x_inp > (2 * x->x_lamechunk)) + { + x->x_outp = (short) x->x_inp - (2.5 * x->x_lamechunk); + } + else if(x->x_inp < (2 * x->x_lamechunk)) + { + x->x_outp = (short) MY_MP3_MALLOC_IN_SIZE - (2.5 * x->x_lamechunk); + } + x->x_start = 1; + } + if((unsigned short)(x->x_outp - x->x_inp) < x->x_lamechunk)error("mp3streamout~: buffers overlap!"); + + i = MY_MP3_MALLOC_IN_SIZE - x->x_outp; + + /* read from buffer */ + if(x->x_lamechunk <= i) + { + /* enough data until end of buffer */ + for(n = 0; n < x->x_lamechunk; n++) /* fill encode buffer */ + { + x->x_mp3inbuf[n] = x->x_buffer[n + x->x_outp]; + } + x->x_outp += x->x_lamechunk; + } + else /* split data */ + { + for(wp = 0; wp < i; wp++) /* data at end of buffer */ + { + x->x_mp3inbuf[wp] = x->x_buffer[wp + x->x_outp]; + } + + for(wp = i; wp < x->x_lamechunk; wp++) /* write rest of data at beginning of buffer */ + { + x->x_mp3inbuf[wp] = x->x_buffer[wp - i]; + } + x->x_outp = x->x_lamechunk - i; + } + + /* encode mp3 data */ +#ifndef UNIX + err = encodeChunk(x->x_lame, x->x_lamechunk, x->x_mp3inbuf, x->x_mp3outbuf, &x->x_mp3size); +#else + x->x_mp3size = lame_encode_buffer_interleaved(x->lgfp, x->x_mp3inbuf, + x->x_lamechunk/lame_get_num_channels(x->lgfp), + x->x_mp3outbuf, MY_MP3_MALLOC_OUT_SIZE); + x->x_mp3size+=lame_encode_flush( x->lgfp, x->x_mp3outbuf+x->x_mp3size, MY_MP3_MALLOC_OUT_SIZE-x->x_mp3size ); + // post( "mp3streamout~ : encoding returned %d frames", x->x_mp3size ); +#endif + + /* check result */ +#ifndef UNIX + if(err != BE_ERR_SUCCESSFUL) + { + closeStream(x->x_lame); + error("mp3streamout~: lameEncodeChunk() failed (%lu)", err); +#else + if(x->x_mp3size<0) + { + lame_close( x->lgfp ); + error("mp3streamout~: lame_encode_buffer_interleaved failed (%d)", x->x_mp3size); +#endif + x->x_lame = -1; + } +} + + /* stream mp3 to the peer */ +static void mp3streamout_stream(t_mp3streamout *x) +{ + int count = -1, i; + struct frame hframe; + + /* header needs to be included in each packet */ + + for( i=0; i<x->x_mp3size; i++ ) + { + // track valid data + if ( head_check( *((unsigned long*)x->x_mp3outbuf+i), 3 ) ) + { + // post( "valid header emitted @ %d (byte %d)", i, i*sizeof(unsigned long) ); + } + } + + count = send(x->x_fd, x->x_mp3outbuf, x->x_mp3size, MSG_NOSIGNAL); + if(count < 0) + { + error("mp3streamout~: could not send encoded data to the peer (%d)", count); +#ifndef UNIX + closeStream(x->x_lame); +#else + lame_close( x->lgfp ); +#endif + x->x_lame = -1; +#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++; + if ( x->x_outpackets%100 == 0 ) + { + // post( "mp3streamout~ : emitted %d bytes (packets = %d)", count, x->x_outpackets ); + } + } + if((count > 0)&&(count != x->x_mp3size)) + { + error("mp3streamout~: %d bytes skipped", x->x_mp3size - count); + } +} + + + /* buffer data as channel interleaved PCM */ +static t_int *mp3streamout_perform(t_int *w) +{ + t_float *in1 = (t_float *)(w[1]); /* left audio inlet */ + t_float *in2 = (t_float *)(w[2]); /* right audio inlet */ + t_mp3streamout *x = (t_mp3streamout *)(w[3]); + int n = (int)(w[4]); /* number of samples */ + unsigned short i,wp; + float in; + + /* copy the data into the buffer */ + i = MY_MP3_MALLOC_IN_SIZE - x->x_inp; /* space left at the end of buffer */ + + n *= 2; /* two channels go into one buffer */ + + if( n <= i ) + { + /* the place between inp and MY_MP3_MALLOC_IN_SIZE */ + /* is big enough to hold the data */ + + for(wp = 0; wp < n; wp++) + { + if(wp%2) + { + in = *(in2++); /* right channel / inlet */ + } + else + { + in = *(in1++); /* left channel / inlet */ + } + if (in > 1.0) { in = 1.0; } + if (in < -1.0) { in = -1.0; } + x->x_buffer[wp + x->x_inp] = (short) (32767.0 * in); + } + x->x_inp += n; /* n more samples written to buffer */ + } + else + { + /* the place between inp and MY_MP3_MALLOC_IN_SIZE is not */ + /* big enough to hold the data */ + /* writing will take place in two turns, one from */ + /* x->x_inp -> MY_MP3_MALLOC_IN_SIZE, then from 0 on */ + + for(wp = 0; wp < i; wp++) /* fill up to end of buffer */ + { + if(wp%2) + { + in = *(in2++); + } + else + { + in = *(in1++); + } + if (in > 1.0) { in = 1.0; } + if (in < -1.0) { in = -1.0; } + x->x_buffer[wp + x->x_inp] = (short) (32767.0 * in); + } + for(wp = i; wp < n; wp++) /* write rest at start of buffer */ + { + if(wp%2) + { + in = *(in2++); + } + else + { + in = *(in1++); + } + if (in > 1.0) { in = 1.0; } + if (in < -1.0) { in = -1.0; } + x->x_buffer[wp - i] = (short) (32767.0 * in); + } + x->x_inp = n - i; /* new writeposition in buffer */ + } + + if((x->x_fd >= 0)&&(x->x_lame >= 0)) + { + /* count buffered samples when things are running */ + x->x_bytesbuffered += n; + + /* encode and send to the peer */ + if(x->x_bytesbuffered > x->x_lamechunk) + { + mp3streamout_encode(x); /* encode to mp3 */ + mp3streamout_stream(x); /* stream mp3 to the peer */ + x->x_bytesbuffered -= x->x_lamechunk; + } + } + else + { + x->x_start = -1; + } + return (w+5); +} + +static void mp3streamout_dsp(t_mp3streamout *x, t_signal **sp) +{ + dsp_add(mp3streamout_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n); +} + + /* initialize the lame library */ +static void mp3streamout_tilde_lame_init(t_mp3streamout *x) +{ +#ifndef UNIX + /* encoder related stuff (calculating buffer size) */ + BE_VERSION lameVersion = {0,}; /* version number of LAME */ + BE_CONFIG lameConfig = {0,}; /* config structure of LAME */ + unsigned int ret; +#else + int ret; + x->lgfp = lame_init(); /* set default parameters for now */ +#endif + +#ifndef UNIX + /* load lame_enc.dll library */ + + dll=LoadLibrary("lame_enc.dll"); + if(dll==NULL) + { + error("mp3streamout~: error loading lame_enc.dll"); + closesocket(x->x_fd); + x->x_fd = -1; + outlet_float(x->x_obj.ob_outlet, 0); + post("mp3streamout~: connection closed"); + return; + } + + /* get Interface functions */ + initStream = (BEINITSTREAM) GetProcAddress(dll, TEXT_BEINITSTREAM); + encodeChunk = (BEENCODECHUNK) GetProcAddress(dll, TEXT_BEENCODECHUNK); + deinitStream = (BEDEINITSTREAM) GetProcAddress(dll, TEXT_BEDEINITSTREAM); + closeStream = (BECLOSESTREAM) GetProcAddress(dll, TEXT_BECLOSESTREAM); + dllVersion = (BEVERSION) GetProcAddress(dll, TEXT_BEVERSION); + writeVBRHeader = (BEWRITEVBRHEADER) GetProcAddress(dll,TEXT_BEWRITEVBRHEADER); + + /* check if all interfaces are present */ + if(!initStream || !encodeChunk || !deinitStream || !closeStream || !dllVersion || !writeVBRHeader) + { + + error("mp3streamout~: unable to get LAME interfaces"); + closesocket(x->x_fd); + x->x_fd = -1; + outlet_float(x->x_obj.ob_outlet, 0); + post("mp3streamout~: connection closed"); + return; + } + + /* get LAME version number */ + dllVersion(&lameVersion); + + post( "mp3streamout~: lame_enc.dll version %u.%02u (%u/%u/%u)\n" + " lame_enc engine %u.%02u", + lameVersion.byDLLMajorVersion, lameVersion.byDLLMinorVersion, + lameVersion.byDay, lameVersion.byMonth, lameVersion.wYear, + lameVersion.byMajorVersion, lameVersion.byMinorVersion); + + memset(&lameConfig,0,sizeof(lameConfig)); /* clear all fields */ +#else + { + const char *lameVersion = get_lame_version(); + post( "mp3streamout~ : using lame version : %s", lameVersion ); + } +#endif + +#ifndef UNIX + + /* use the LAME config structure */ + lameConfig.dwConfig = BE_CONFIG_LAME; + + /* set the mpeg format flags */ + lameConfig.format.LHV1.dwStructVersion = 1; + lameConfig.format.LHV1.dwStructSize = sizeof(lameConfig); + lameConfig.format.LHV1.dwSampleRate = (int)sys_getsr(); /* input frequency - pd's sample rate */ + lameConfig.format.LHV1.dwReSampleRate = x->x_samplerate; /* output s/r - resample if necessary */ + lameConfig.format.LHV1.nMode = x->x_mp3mode; /* output mode */ + lameConfig.format.LHV1.dwBitrate = x->x_bitrate; /* mp3 bitrate */ + lameConfig.format.LHV1.nPreset = x->x_mp3quality; /* mp3 encoding quality */ + lameConfig.format.LHV1.dwMpegVersion = MPEG1; /* use MPEG1 */ + lameConfig.format.LHV1.dwPsyModel = 0; /* USE DEFAULT PSYCHOACOUSTIC MODEL */ + lameConfig.format.LHV1.dwEmphasis = 0; /* NO EMPHASIS TURNED ON */ + lameConfig.format.LHV1.bOriginal = TRUE; /* SET ORIGINAL FLAG */ + lameConfig.format.LHV1.bCopyright = TRUE; /* SET COPYRIGHT FLAG */ + lameConfig.format.LHV1.bNoRes = TRUE; /* no bit resorvoir */ + + /* init the MP3 stream */ + ret = initStream(&lameConfig, &x->x_lamechunk, &x->x_mp3size, &x->x_lame); + + /* check result */ + if(ret != BE_ERR_SUCCESSFUL) + { + post("mp3streamout~: error opening encoding stream (%lu)", ret); + return; + } + +#else + /* setting lame parameters */ + lame_set_num_channels( x->lgfp, 2); + lame_set_in_samplerate( x->lgfp, sys_getsr() ); + lame_set_out_samplerate( x->lgfp, x->x_samplerate ); + lame_set_brate( x->lgfp, x->x_bitrate ); + lame_set_mode( x->lgfp, x->x_mp3mode ); + lame_set_quality( x->lgfp, x->x_mp3quality ); + lame_set_emphasis( x->lgfp, 1 ); + lame_set_original( x->lgfp, 1 ); + lame_set_copyright( x->lgfp, 1 ); /* viva free music societies !!! */ + lame_set_disable_reservoir( x->lgfp, 0 ); + lame_set_padding_type( x->lgfp, PAD_NO ); + ret = lame_init_params( x->lgfp ); + if ( ret<0 ) { + post( "mp3streamout~ : error : lame params initialization returned : %d", ret ); + } else { + x->x_lame=1; + /* magic formula copied from windows dll for MPEG-I */ + x->x_lamechunk = 2*1152; + + post( "mp3streamout~ : lame initialization done. (%d)", x->x_lame ); + } + lame_init_bitstream( x->lgfp ); +#endif + + +} + + /* connect to the peer */ +static void mp3streamout_connect(t_mp3streamout *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; + char resp[STRBUF_SIZE]; + unsigned int len; + int sockfd; + +#ifndef UNIX + unsigned int ret; +#else + int ret; +#endif + + if (x->x_fd >= 0) + { + error("mp3streamout~: already connected"); + return; + } + + sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd < 0) + { + error("mp3streamout~: 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("mp3streamout~: 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("mp3streamout~: connecting to port %d", portno); + if (connect(sockfd, (struct sockaddr *) &csocket, sizeof (csocket)) < 0) + { + error("mp3streamout~: 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( "mp3streamout~ : connected to peer" ); + + mp3streamout_tilde_lame_init(x); + +} + + /* close connection to the peer */ +static void mp3streamout_disconnect(t_mp3streamout *x) +{ + + int err = -1; + if(x->x_lame >= 0) + { +#ifndef UNIX + /* deinit the stream */ + err = deinitStream(x->x_lame, x->x_mp3outbuf, &x->x_mp3size); + + /* check result */ + if(err != BE_ERR_SUCCESSFUL) + { + error("exiting mp3 stream failed (%lu)", err); + } + closeStream(x->x_lame); /* close mp3 encoder stream */ +#else + /* ignore remaining bytes */ + if ( x->x_mp3size = lame_encode_flush( x->lgfp, x->x_mp3outbuf, 0) < 0 ) { + post( "mp3streamout~ : warning : remaining encoded bytes" ); + } + lame_close( x->lgfp ); +#endif + x->x_lame = -1; + post("mp3streamout~: encoder stream closed"); + } + + 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("mp3streamout~: connection closed"); + } +} + + /* settings for mp3 encoding */ +static void mp3streamout_mpeg(t_mp3streamout *x, t_floatarg fbitrate, + t_floatarg fmode, t_floatarg fquality) +{ + if ( fbitrate != 32 && fbitrate != 40 && fbitrate != 48 && fbitrate != 56 && + fbitrate != 64 && fbitrate != 80 && fbitrate != 96 && fbitrate != 112 && + fbitrate != 128 && fbitrate != 160 && fbitrate != 192 && fbitrate != 224 && + fbitrate != 256 && fbitrate != 320 ) { + post( "mp3streamout~ : wrong bitrate." ); + return; + } + if ( fmode <0 || fmode>2 ) { + post( "mp3streamout~ : wrong mp3 mode." ); + if ( fmode == 3 ) + { + post( "mp3streamout~ : mone is not supported by streamout~ for now." ); + } + return; + } + /* there is a bug in lame 3.92 and quality below 5 will not work */ + /* WAIT FOR A FIX */ + if ( fquality <5 || fquality>9 ) { + post( "mp3streamout~ : wrong quality." ); + return; + } + x->x_bitrate = fbitrate; + x->x_mp3mode = fmode; + x->x_mp3quality = (int)fquality; + post("mp3streamout~: setting mp3 stream to %dHz, %dkbit/s, mode %d, quality %d", + x->x_samplerate, x->x_bitrate, x->x_mp3mode, x->x_mp3quality); + mp3streamout_tilde_lame_init(x); +} + + /* print settings */ +static void mp3streamout_print(t_mp3streamout *x) +{ + const char * buf = 0; + + post(mp3streamout_version); + post(" LAME mp3 settings:\n" + " output sample rate: %d Hz\n" + " bitrate: %d kbit/s", x->x_samplerate, x->x_bitrate); + switch(x->x_mp3mode) + { + case 0 : + buf = "stereo"; + break; + case 1 : + buf = "joint stereo"; + break; + case 2 : + buf = "dual channel"; + break; + case 3 : + buf = "mono"; + break; + } + post(" mode: %s\n" + " quality: %d", buf, x->x_mp3quality); +#ifndef UNIX + if(x->x_lamechunk!=0)post(" calculated mp3 chunk size: %d", x->x_lamechunk); +#else + post(" mp3 chunk size: %d", x->x_lamechunk); +#endif + if(x->x_samplerate!=sys_getsr()) + { + post(" resampling from %d to %d Hz!", (int)sys_getsr(), x->x_samplerate); + } +} + + /* clean up */ +static void mp3streamout_free(t_mp3streamout *x) +{ + + if(x->x_lame >= 0) +#ifndef UNIX + closeStream(x->x_lame); +#else + lame_close( x->lgfp ); +#endif + if(x->x_fd >= 0) +#ifndef UNIX + closesocket(x->x_fd); +#else + close(x->x_fd); +#endif + freebytes(x->x_mp3inbuf, MY_MP3_MALLOC_IN_SIZE*sizeof(short)); + freebytes(x->x_mp3outbuf, MY_MP3_MALLOC_OUT_SIZE); + freebytes(x->x_buffer, MY_MP3_MALLOC_IN_SIZE*sizeof(short)); +} + +static void *mp3streamout_new(void) +{ + t_mp3streamout *x = (t_mp3streamout *)pd_new(mp3streamout_class); + inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym ("signal"), gensym ("signal")); + outlet_new( &x->x_obj, &s_float ); + x->lgfp = NULL; + x->x_fd = -1; + x->x_outpackets = 0; + x->x_lame = -1; + x->x_samplerate = sys_getsr(); + x->x_bitrate = 128; + x->x_mp3mode = 1; + x->x_mp3quality = 5; + x->x_mp3inbuf = getbytes(MY_MP3_MALLOC_IN_SIZE*sizeof(short)); /* buffer for encoder input */ + x->x_mp3outbuf = getbytes(MY_MP3_MALLOC_OUT_SIZE); /* our mp3 stream */ + x->x_buffer = getbytes(MY_MP3_MALLOC_IN_SIZE*sizeof(short)); /* what we get from pd, converted to PCM */ + if ((!x->x_buffer)||(!x->x_mp3inbuf)||(!x->x_mp3outbuf)) /* check buffers... */ + { + error("out of memory!"); + } + x->x_bytesbuffered = 0; + x->x_inp = 0; + x->x_outp = 0; + x->x_start = -1; + return(x); +} + +void mp3streamout_tilde_setup(void) +{ + post(mp3streamout_version); + mp3streamout_class = class_new(gensym("mp3streamout~"), (t_newmethod)mp3streamout_new, (t_method)mp3streamout_free, + sizeof(t_mp3streamout), 0, 0); + CLASS_MAINSIGNALIN(mp3streamout_class, t_mp3streamout, x_f ); + class_sethelpsymbol(mp3streamout_class, gensym("help-mp3live~.pd")); + class_addmethod(mp3streamout_class, (t_method)mp3streamout_dsp, gensym("dsp"), 0); + class_addmethod(mp3streamout_class, (t_method)mp3streamout_connect, gensym("connect"), A_SYMBOL, A_FLOAT, 0); + class_addmethod(mp3streamout_class, (t_method)mp3streamout_disconnect, gensym("disconnect"), 0); + class_addmethod(mp3streamout_class, (t_method)mp3streamout_mpeg, gensym("mpeg"), A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(mp3streamout_class, (t_method)mp3streamout_print, gensym("print"), 0); +} + diff --git a/mp3live~/mpg123.h b/mp3live~/mpg123.h new file mode 100644 index 0000000..1c530d3 --- /dev/null +++ b/mp3live~/mpg123.h @@ -0,0 +1,136 @@ +#ifndef MPG123_H_INCLUDED +#define MPG123_H_INCLUDED + +#include <stdio.h> + +#define STDC_HEADERS + +#ifdef STDC_HEADERS +# include <string.h> +#else +# ifndef HAVE_STRCHR +# define strchr index +# define strrchr rindex +# endif +char *strchr (), *strrchr (); +# ifndef HAVE_MEMCPY +# define memcpy(d, s, n) bcopy ((s), (d), (n)) +# define memmove(d, s, n) bcopy ((s), (d), (n)) +# endif +#endif + +#include <signal.h> + + +#if defined(__riscos__) && defined(FPA10) +#include "ymath.h" +#else +#include <math.h> +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_SQRT2 +#define M_SQRT2 1.41421356237309504880 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + + +#ifdef REAL_IS_FLOAT +# define real float +#elif defined(REAL_IS_LONG_DOUBLE) +# define real long double +#else +# define real double +#endif + +#define FALSE 0 +#define TRUE 1 + +#define SBLIMIT 32 +#define SSLIMIT 18 + +#define MPG_MD_STEREO 0 +#define MPG_MD_JOINT_STEREO 1 +#define MPG_MD_DUAL_CHANNEL 2 +#define MPG_MD_MONO 3 + +#define MAXFRAMESIZE 1792 + +/* AF: ADDED FOR LAYER1/LAYER2 */ +#define SCALE_BLOCK 12 + + +/* Pre Shift fo 16 to 8 bit converter table */ +#define AUSHIFT (3) + +struct frame { + int stereo; + int jsbound; + int single; + int lsf; + int mpeg25; + int header_change; + int lay; + int error_protection; + int bitrate_index; + int sampling_frequency; + int padding; + int extension; + int mode; + int mode_ext; + int copyright; + int original; + int emphasis; + int framesize; /* computed framesize */ + + /* AF: ADDED FOR LAYER1/LAYER2 */ +#if defined(USE_LAYER_2) || defined(USE_LAYER_1) + int II_sblimit; + struct al_table2 *alloc; + int down_sample_sblimit; + int down_sample; + +#endif + +}; + +struct gr_info_s { + int scfsi; + unsigned part2_3_length; + unsigned big_values; + unsigned scalefac_compress; + unsigned block_type; + unsigned mixed_block_flag; + unsigned table_select[3]; + unsigned subblock_gain[3]; + unsigned maxband[3]; + unsigned maxbandl; + unsigned maxb; + unsigned region1start; + unsigned region2start; + unsigned preflag; + unsigned scalefac_scale; + unsigned count1table_select; + real *full_gain[3]; + real *pow2gain; +}; + +struct III_sideinfo +{ + unsigned main_data_begin; + unsigned private_bits; + struct { + struct gr_info_s gr[2]; + } ch[2]; +}; + + +#endif diff --git a/mp3live~/mpglib.h b/mp3live~/mpglib.h new file mode 100644 index 0000000..1f4ef9a --- /dev/null +++ b/mp3live~/mpglib.h @@ -0,0 +1,65 @@ +// #include "lame-analysis.h" + +#define NOANALYSIS + +#ifndef NOANALYSIS +extern plotting_data *mpg123_pinfo; +#endif + +struct buf { + unsigned char *pnt; + long size; + long pos; + struct buf *next; + struct buf *prev; +}; + +struct framebuf { + struct buf *buf; + long pos; + struct frame *next; + struct frame *prev; +}; + +typedef struct mpstr_tag { + struct buf *head,*tail; + int vbr_header; /* 1 if valid Xing vbr header detected */ + int num_frames; /* set if vbr header present */ + int enc_delay; /* set if vbr header present */ + int enc_padding; /* set if vbr header present */ + int header_parsed; + int side_parsed; + int data_parsed; + int free_format; /* 1 = free format frame */ + int old_free_format; /* 1 = last frame was free format */ + int bsize; + int framesize; + int ssize; + int dsize; + int fsizeold; + int fsizeold_nopadding; + struct frame fr; + unsigned char bsspace[2][MAXFRAMESIZE+512]; /* MAXFRAMESIZE */ + real hybrid_block[2][2][SBLIMIT*SSLIMIT]; + int hybrid_blc[2]; + unsigned long header; + int bsnum; + real synth_buffs[2][2][0x110]; + int synth_bo; + int sync_bitstream; + +} MPSTR, *PMPSTR; + + +#if ( defined(_MSC_VER) || defined(__BORLANDC__) ) + typedef int BOOL; /* windef.h contains the same definition */ +#else + #define BOOL int +#endif + +#define MP3_ERR -1 +#define MP3_OK 0 +#define MP3_NEED_MORE 1 + + + diff --git a/mp3live~/test-streaming-mp3.pd b/mp3live~/test-streaming-mp3.pd new file mode 100644 index 0000000..e9e6b2e --- /dev/null +++ b/mp3live~/test-streaming-mp3.pd @@ -0,0 +1,96 @@ +#N canvas 15 9 986 678 10; +#X msg 63 58 bang; +#X obj 63 78 openpanel; +#X obj 63 100 t s b; +#X obj 63 137 pack s s; +#X obj 99 100 float \$0; +#X text 51 39 Step 1 : Load a sound file; +#X obj 117 137 makefilename %d-sample; +#X msg 443 280 \; pd dsp 1; +#X msg 509 280 \; pd dsp 0; +#X obj 454 254 loadbang; +#X obj 62 217 mp3streamout~; +#X floatatom 63 240 5 0 0; +#X obj 62 181 readsf~; +#X msg 62 158 open \$1 \$2; +#X msg 33 148 1; +#X msg 265 185 disconnect; +#X obj 63 117 route float; +#X msg 569 47 bang; +#X obj 569 67 openpanel; +#X obj 569 89 t s b; +#X obj 569 126 pack s s; +#X obj 605 89 float \$0; +#X text 557 28 Step 1 : Load a sound file; +#X obj 623 126 makefilename %d-sample; +#X obj 568 206 mp3streamout~; +#X floatatom 569 229 5 0 0; +#X obj 568 170 readsf~; +#X msg 568 147 open \$1 \$2; +#X msg 539 137 1; +#X msg 784 177 disconnect; +#X obj 569 106 route float; +#X obj 364 462 dac~; +#X obj 361 438 *~; +#X floatatom 416 465 5 0 0; +#X symbolatom 459 437 15 0 0; +#X obj 407 440 / 100; +#X obj 122 470 dac~; +#X obj 119 446 *~; +#X floatatom 174 473 5 0 0; +#X symbolatom 217 445 10 0 0; +#X obj 165 448 / 100; +#X obj 96 419 mp3streamin~ 5001 1; +#X obj 345 410 mp3streamin~ 5000 1; +#X msg 777 149 connect yves 5001; +#X msg 246 228 mpeg 32 2 5; +#X msg 246 228 mpeg 32 2 5; +#X msg 246 264 mpeg 224 2 5; +#X msg 258 157 connect localhost 5000; +#X msg 263 125 connect dregs 5000; +#X connect 0 0 1 0; +#X connect 1 0 2 0; +#X connect 2 0 3 0; +#X connect 2 1 4 0; +#X connect 3 0 13 0; +#X connect 4 0 16 0; +#X connect 6 0 3 1; +#X connect 9 0 7 0; +#X connect 10 0 11 0; +#X connect 12 0 10 0; +#X connect 12 0 10 1; +#X connect 13 0 12 0; +#X connect 14 0 12 0; +#X connect 15 0 10 0; +#X connect 16 0 6 0; +#X connect 17 0 18 0; +#X connect 18 0 19 0; +#X connect 19 0 20 0; +#X connect 19 1 21 0; +#X connect 20 0 27 0; +#X connect 21 0 30 0; +#X connect 23 0 20 1; +#X connect 24 0 25 0; +#X connect 26 0 24 0; +#X connect 26 0 24 1; +#X connect 27 0 26 0; +#X connect 28 0 26 0; +#X connect 29 0 24 0; +#X connect 30 0 23 0; +#X connect 32 0 31 0; +#X connect 32 0 31 1; +#X connect 33 0 35 0; +#X connect 35 0 32 1; +#X connect 37 0 36 0; +#X connect 37 0 36 1; +#X connect 38 0 40 0; +#X connect 40 0 37 1; +#X connect 41 0 37 0; +#X connect 41 2 39 0; +#X connect 42 0 32 0; +#X connect 42 2 34 0; +#X connect 43 0 24 0; +#X connect 44 0 10 0; +#X connect 46 0 10 0; +#X connect 47 0 10 0; +#X connect 48 0 10 0; |