aboutsummaryrefslogtreecommitdiff
path: root/mp3write~
diff options
context:
space:
mode:
Diffstat (limited to 'mp3write~')
-rw-r--r--mp3write~/CHANGES.LOG8
-rw-r--r--mp3write~/INSTALL15
-rw-r--r--mp3write~/Makefile82
-rw-r--r--mp3write~/README94
-rw-r--r--mp3write~/help-mp3write~.pd43
-rw-r--r--mp3write~/interface.h32
-rw-r--r--mp3write~/mp3write~.c721
-rw-r--r--mp3write~/mpg123.h136
-rw-r--r--mp3write~/mpglib.h65
9 files changed, 1196 insertions, 0 deletions
diff --git a/mp3write~/CHANGES.LOG b/mp3write~/CHANGES.LOG
new file mode 100644
index 0000000..0071e2a
--- /dev/null
+++ b/mp3write~/CHANGES.LOG
@@ -0,0 +1,8 @@
+0.4
+ fixed title memory leak
+0.3
+ fixed includes to compile with lame > 0.90
+0.2
+ fixed multi-encoding problem
+0.1
+ first implementation
diff --git a/mp3write~/INSTALL b/mp3write~/INSTALL
new file mode 100644
index 0000000..75f5f91
--- /dev/null
+++ b/mp3write~/INSTALL
@@ -0,0 +1,15 @@
+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/mp3write~
+
+make
+
+make install
+
+you're set !!
diff --git a/mp3write~/Makefile b/mp3write~/Makefile
new file mode 100644
index 0000000..d5d082e
--- /dev/null
+++ b/mp3write~/Makefile
@@ -0,0 +1,82 @@
+NAME=mp3write~
+CSYM=mp3write_tilde
+
+current: pd_linux
+
+# ----------------------- NT -----------------------
+
+pd_nt: $(NAME).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
+
+.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
+
+.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
+
+.SUFFIXES: .pd_linux
+
+LINUXCFLAGS = -DPD -DUNIX -DICECAST -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 $*/$*.pd_linux ..
+
+# ----------------------------------------------------------
+
+install:
+ cp help-*.pd ../../doc/5.reference
+
+clean:
+ rm -f *.o *.pd_* so_locations
diff --git a/mp3write~/README b/mp3write~/README
new file mode 100644
index 0000000..3b43bbe
--- /dev/null
+++ b/mp3write~/README
@@ -0,0 +1,94 @@
+Version 0.01
+copyright (c) 2001 by Yves Degoyon
+
+mp3write~.dll is a MPEG I Layer III (mp3) file writer.
+
+To install mp3write~, 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.
+
+*****************************************************************************
+
+mp3write~ has been compiled for Linux using LAME 3.92.
+The newest version of LAME can be found via freshmeat.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.
+
+*****************************************************************************
+
+ using mp3write~ external for Pure Data
+
+Open the test-mp3write~.pd to understand how it works.
+In this patch, you must send the messages to mp3write~
+in the following order :
+
+1/ append|truncate if you wish to change file creation options ( default is append )
+2/ open /my/file
+3/ start
+5/ pd dsp 1
+4/ stop : the tag is written at this stage
+
+Parameters sent to mp3write~ object :
+
+ Sampling Rate (Hz):
+Possible values are 48000, 44100 and 32000. If Pd runs at a different sampling
+rate, LAME will resample the signal. Default value for mp3 sampling rate is Pd's
+sampling rate.
+
+ Bitrate (kbit/s):
+Possible values are 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256
+and 320. Default is 224.
+
+ Mode:
+Possible values are 0 (stereo), 1 (joint stereo, the default), 2 (dual channel)
+and 3 (mono).
+
+ Outlet:
+The outlet outputs an int, which the number of bytes written in this session.
+this might be different from file size if you're using append mode.
+
+
+ Known Problems :
+
+All combinations of samplerate, bitrate, quality factor will not be accepted.
+
+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 SAMPLERATE/BITRATES
+
+MPEG-1 layer III sample frequencies (kHz): 32 48 44.1
+bitrates (kbps): 32 40 48 56 64 80 96 112 128 160 192 224 256 320
+
+MPEG-2 layer III sample frequencies (kHz): 16 24 22.05
+bitrates (kbps): 8 16 24 32 40 48 56 64 80 96 112 128 144 160
+
+MPEG-2.5 layer III sample frequencies (kHz): 8 12 11.025
+bitrates (kbps): 8 16 24 32 40 48 56 64 80 96 112 128 144 160
+
+Furthermore, it seems that high quality factors will not work
+with this release of lame ( 3.92 ).
+The same errors can be obtained with the command line :
+lame -q 1 file.wav
+outputs errors and mp3write can't do better.
+
+
diff --git a/mp3write~/help-mp3write~.pd b/mp3write~/help-mp3write~.pd
new file mode 100644
index 0000000..0e575ba
--- /dev/null
+++ b/mp3write~/help-mp3write~.pd
@@ -0,0 +1,43 @@
+#N canvas 168 244 736 325 10;
+#X floatatom 53 24 0 40 16000;
+#X msg 168 278 \; pd dsp 1;
+#X text 287 196 <-- settings for mp3 stream;
+#X text 310 226 bitrate: bitrate of stream \, def. 224kbit/s;
+#X text 347 250 1 = joint stereo (default);
+#X text 307 288 quality: 1 = high \, 9 = low;
+#X text 308 208 (samplerate \, bitrate \, mode \, quality);
+#X text 311 238 mode: 0 = stereo;
+#X text 347 262 2 = dual channel;
+#X text 347 275 3 = mono;
+#X msg 171 143 print;
+#X msg 234 278 \; pd dsp 0;
+#X obj 51 59 osc~ 440;
+#X msg 169 174 mpeg 44100 128 1 4;
+#X msg 171 121 truncate;
+#X msg 171 98 append;
+#X msg 170 72 stop;
+#X msg 170 49 start;
+#X text 370 24 Open a file before any operations;
+#X text 228 95 Set recording mode to append ( which is the default
+);
+#X text 208 48 Start recording;
+#X text 231 118 Set recording mode to truncate;
+#X text 218 142 Print settings;
+#X floatatom 116 258 10 0 0;
+#X text 10 254 Bytes written;
+#X text 208 71 Stop recording ( this also writes a tag );
+#X msg 169 198 mpeg 44100 8 3 5;
+#X msg 170 25 open /tmp/track1.mp3;
+#X obj 116 234 mp3write~;
+#X connect 0 0 12 0;
+#X connect 10 0 28 0;
+#X connect 12 0 28 0;
+#X connect 12 0 28 1;
+#X connect 13 0 28 0;
+#X connect 14 0 28 0;
+#X connect 15 0 28 0;
+#X connect 16 0 28 0;
+#X connect 17 0 28 0;
+#X connect 26 0 28 0;
+#X connect 27 0 28 0;
+#X connect 28 0 23 0;
diff --git a/mp3write~/interface.h b/mp3write~/interface.h
new file mode 100644
index 0000000..de3136a
--- /dev/null
+++ b/mp3write~/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/mp3write~/mp3write~.c b/mp3write~/mp3write~.c
new file mode 100644
index 0000000..fe8cb47
--- /dev/null
+++ b/mp3write~/mp3write~.c
@@ -0,0 +1,721 @@
+/* ------------------------ mp3write~ ----------------------------------------- */
+/* */
+/* Tilde object to record audio in mp3 format */
+/* Note that it is, in no way, related to mp3play~ */
+/* Written by Yves Degoyon (ydegoyon@free.fr). */
+/* Tarballs and updates at 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 version 3.92 */
+/* Get it via http://www.freshmeat.net */
+/* */
+/* "All this talk of blood and iron is the cause of all my kicking" */
+/* Gang Of Four - "Guns Before Butter" */
+/* ---------------------------------------------------------------------------- */
+
+
+
+#ifdef NT
+#pragma warning( disable : 4244 )
+#pragma warning( disable : 4305 )
+#endif
+
+#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 <sys/types.h>
+#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 */
+
+#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 *mp3write_version = "mp3write~: mp3 file recorder version 0.4, written by Yves Degoyon";
+static int sockfd;
+
+#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 *mp3write_class;
+
+typedef struct _mp3write
+{
+ t_object x_obj;
+
+ /* LAME stuff */
+ int x_lame; /* info about encoder status */
+ int x_lamechunk; /* chunk size for LAME encoder */
+
+ /* 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 */
+ int x_mp3size; /* number of returned mp3 samples */
+ 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 */
+
+ /* recording stuff */
+ int x_fd; /* file descriptor of the mp3 output */
+ int x_file_open_mode; /* file opening mode */
+ int x_byteswritten; /* number of bytes written */
+ int x_recflag; /* recording flag toggled by messages "start" and "stop" */
+
+ t_float x_f; /* float needed for signal input */
+ char *x_title; /* title of the mp3 */
+
+#ifdef UNIX
+ lame_global_flags *lgfp; /* lame encoder configuration */
+#endif
+} t_mp3write;
+
+
+ /* encode PCM data to mp3 stream */
+static void mp3write_encode(t_mp3write *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 so that it won't interfere with inpoint */
+ if(x->x_start == -1)
+ {
+ post("mp3write~: reseting buffer positions");
+ /* 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("mp3write~: 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);
+ // post( "mp3write~ : encoding returned %d frames", x->x_mp3size );
+#endif
+
+ /* check result */
+#ifndef UNIX
+ if(err != BE_ERR_SUCCESSFUL)
+ {
+ closeStream(x->x_lame);
+ error("mp3write~: lameEncodeChunk() failed (%lu)", err);
+#else
+ if(x->x_mp3size<0)
+ {
+ lame_close( x->lgfp );
+ error("mp3write~: lame_encode_buffer_interleaved failed (%d)", x->x_mp3size);
+#endif
+ x->x_lame = -1;
+ }
+}
+
+ /* store mp3 frames in the file */
+static void mp3write_writeframes(t_mp3write *x)
+{
+ int err = -1; /* error return code */
+
+ if ( x->x_fd < 0 ) {
+ post( "mp3write~ : error : trying to write frames but no valid file is opened" );
+ return;
+ }
+
+#ifndef UNIX
+ err = _write(x->x_fd, x->x_mp3outbuf, x->x_mp3size);
+#else
+ err = write(x->x_fd, x->x_mp3outbuf, x->x_mp3size);
+#endif
+
+ if(err < 0)
+ {
+ error("mp3write~: could not write encoded data to file (%d)", err);
+#ifndef UNIX
+ closeStream(x->x_lame);
+#else
+ lame_close( x->lgfp );
+#endif
+ x->x_lame = -1;
+#ifndef UNIX
+ error("mp3write~: writing data");
+ _close(x->x_fd);
+#else
+ perror("mp3write~: writing data");
+ close(x->x_fd);
+#endif
+ x->x_fd = -1;
+ }
+ else
+ {
+ x->x_byteswritten += err;
+ outlet_float( x->x_obj.ob_outlet, x->x_byteswritten );
+ }
+ if((err > 0)&&(err != x->x_mp3size))error("mp3write~: %d bytes skipped", x->x_mp3size - err);
+}
+
+
+ /* buffer data as channel interleaved PCM */
+static t_int *mp3write_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_mp3write *x = (t_mp3write *)(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)&&(x->x_recflag))
+ {
+ /* count buffered samples when things are running */
+ x->x_bytesbuffered += n;
+
+ /* encode and send to server */
+ if(x->x_bytesbuffered > x->x_lamechunk)
+ {
+ mp3write_encode(x); /* encode to mp3 */
+ mp3write_writeframes(x); /* write mp3 to file */
+ x->x_bytesbuffered -= x->x_lamechunk;
+ }
+ }
+ else
+ {
+ x->x_start = -1;
+ }
+ return (w+5);
+}
+
+static void mp3write_dsp(t_mp3write *x, t_signal **sp)
+{
+ dsp_add(mp3write_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n);
+}
+
+ /* initialize the lame library */
+static int mp3write_tilde_lame_init(t_mp3write *x)
+{
+ time_t now;
+
+#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("mp3write~: error loading lame_enc.dll");
+ closesocket(x->x_fd);
+ x->x_fd = -1;
+ post("mp3write~: connection closed");
+ return -1;
+ }
+
+ /* 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("mp3write~: unable to get LAME interfaces");
+ closesocket(x->x_fd);
+ x->x_fd = -1;
+ post("mp3write~: connection closed");
+ return -1;
+ }
+
+ /* get LAME version number */
+ dllVersion(&lameVersion);
+
+ post( "mp3write~: 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( "mp3write~ : 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("mp3write~: error opening encoding stream (%lu)", ret);
+ return -1;
+ }
+
+#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( "mp3write~ : error : lame params initialization returned : %d", ret );
+ return -1;
+ } else {
+ x->x_lame=1;
+ /* magic formula copied from windows dll for MPEG-I */
+ x->x_lamechunk = 2*1152;
+
+ post( "mp3write~ : lame initialization done. (%d)", x->x_lame );
+ }
+ lame_init_bitstream( x->lgfp );
+
+ /* setting tag information */
+ id3tag_init(x->lgfp);
+ id3tag_v1_only(x->lgfp);
+ id3tag_space_v1(x->lgfp);
+ id3tag_set_artist(x->lgfp, "Pd Session");
+ now=time(NULL);
+ sprintf( x->x_title, "Started at %s", ctime(&now) );
+ id3tag_set_title(x->lgfp, x->x_title );
+
+#endif
+ return 0;
+
+}
+
+ /* open file and initialize lame */
+static void mp3write_open(t_mp3write *x, t_symbol *sfile)
+{
+ if ( mp3write_tilde_lame_init(x) < 0 ) {
+ error( "mp3write~ : lame initialization failed ... check parameters.");
+ return;
+ }
+
+ /* closing previous file descriptor */
+ if ( x->x_fd > 0 ) {
+#ifndef UNIX
+ if(_close(x->x_fd) < 0 )
+#else
+ if(close(x->x_fd) < 0)
+#endif
+ {
+ perror( "mp3write~ : closing file" );
+ }
+ }
+
+ if ( x->x_recflag ) {
+ x->x_recflag = 0;
+ }
+
+#ifndef UNIX
+ if ( ( x->x_fd = _open( sfile->s_name, x->x_file_open_mode, _S_IREAD|_S_IWRITE) ) < 0 )
+#else
+ if ( ( x->x_fd = open( sfile->s_name, x->x_file_open_mode, S_IRWXU|S_IRWXG|S_IRWXO ) ) < 0 )
+#endif
+ {
+ error( "mp3write~ : cannot open >%s<", sfile->s_name);
+ x->x_fd=-1;
+ return;
+ }
+ x->x_byteswritten = 0;
+ post( "mp3write~ : opened >%s< fd=%d", sfile->s_name, x->x_fd);
+}
+
+ /* setting file write mode to append */
+static void mp3write_append(t_mp3write *x)
+{
+#ifndef UNIX
+ x->x_file_open_mode = _O_CREAT|_O_WRONLY|_O_APPEND|_O_BINARY;
+#else
+ x->x_file_open_mode = O_CREAT|O_WRONLY|O_APPEND|O_NONBLOCK;
+#endif
+ if(x->x_fd>=0)post("mp3write~ : mode set to append : open a new file to make changes take effect! ");
+}
+
+ /* setting file write mode to truncate */
+static void mp3write_truncate(t_mp3write *x)
+{
+#ifndef UNIX
+ x->x_file_open_mode = _O_CREAT|_O_WRONLY|_O_TRUNC|_O_BINARY;
+#else
+ x->x_file_open_mode = O_CREAT|O_WRONLY|O_TRUNC|O_NONBLOCK;
+#endif
+ if(x->x_fd>=0)post("mp3write~ : mode set to truncate : open a new file to make changes take effect! ");
+}
+
+ /* settings for mp3 encoding */
+static void mp3write_mpeg(t_mp3write *x, t_floatarg fsamplerate, t_floatarg fbitrate,
+ t_floatarg fmode, t_floatarg fquality)
+{
+ x->x_samplerate = fsamplerate;
+ if(fbitrate > MAXDATARATE)
+ {
+ fbitrate = MAXDATARATE;
+ }
+ x->x_bitrate = fbitrate;
+ x->x_mp3mode = fmode;
+ x->x_mp3quality = fquality;
+ post("mp3write~: setting mp3 stream to %dHz, %dkbit/s, mode %d, quality %d",
+ x->x_samplerate, x->x_bitrate, x->x_mp3mode, x->x_mp3quality);
+ if(x->x_fd>=0)post("mp3write~ : restart recording to make changes take effect! ");
+}
+
+ /* print settings */
+static void mp3write_print(t_mp3write *x)
+{
+ const char * buf = 0;
+ post(mp3write_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);
+ }
+}
+
+ /* start recording */
+static void mp3write_start(t_mp3write *x)
+{
+ if ( x->x_fd < 0 ) {
+ post("mp3write~: start received but no file has been set ... ignored.");
+ return;
+ }
+
+ if ( x->x_recflag == 1 ) {
+ post("mp3write~: start received but recording is started ... ignored.");
+ return;
+ }
+
+ x->x_recflag = 1;
+ post("mp3write~: start recording");
+}
+
+ /* stop recording */
+static void mp3write_stop(t_mp3write *x)
+{
+ int err = -1;
+
+ if ( x->x_fd < 0 ) {
+ post("mp3write~: stop received but no file has been set ... ignored.");
+ return;
+ }
+
+ if ( x->x_recflag == 0 ) {
+ post("mp3write~: stop received but recording is stopped ... ignored.");
+ return;
+ }
+ /* first stop recording / buffering and so on, than do the rest */
+ x->x_recflag = 0;
+
+ /* flushing remaining frames and tag */
+#ifndef UNIX
+ deinitStream(x->x_lame, x->x_mp3outbuf, &x->x_mp3size);
+#else
+ x->x_mp3size = lame_encode_flush( x->lgfp, x->x_mp3outbuf, MY_MP3_MALLOC_OUT_SIZE );
+#endif
+
+ mp3write_writeframes(x); /* write mp3 to file */
+
+ x->x_recflag = 0;
+ post("mp3write~: stop recording, flushed %d bytes", x->x_mp3size);
+}
+
+ /* clean up */
+static void mp3write_free(t_mp3write *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
+ _close(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));
+ freebytes( x->x_title, 255 );
+}
+
+static void *mp3write_new(void)
+{
+ t_mp3write *x = (t_mp3write *)pd_new(mp3write_class);
+ inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym ("signal"), gensym ("signal"));
+ outlet_new (&x->x_obj, &s_float);
+ x->x_fd = -1;
+#ifndef UNIX
+ x->x_file_open_mode = _O_CREAT|_O_WRONLY|_O_APPEND|_O_BINARY;
+#else
+ x->x_file_open_mode = O_CREAT|O_WRONLY|O_APPEND|O_NONBLOCK;
+#endif
+ x->x_lame = -1;
+ x->x_samplerate = sys_getsr();
+ x->x_bitrate = 128;
+ x->x_byteswritten = 0;
+ x->x_mp3mode = 1;
+ x->lgfp = NULL;
+ x->x_title = getbytes( 255 );
+ 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*sizeof(char)); /* 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("mp3write~ : cannot allocate internal buffers");
+ return NULL;
+ }
+ x->x_bytesbuffered = 0;
+ x->x_inp = 0;
+ x->x_outp = 0;
+ x->x_start = -1;
+ return(x);
+}
+
+void mp3write_tilde_setup(void)
+{
+ post(mp3write_version);
+ mp3write_class = class_new(gensym("mp3write~"), (t_newmethod)mp3write_new, (t_method)mp3write_free,
+ sizeof(t_mp3write), 0, 0);
+ CLASS_MAINSIGNALIN(mp3write_class, t_mp3write, x_f );
+ class_sethelpsymbol(mp3write_class, gensym("help-mp3write~.pd"));
+ class_addmethod(mp3write_class, (t_method)mp3write_dsp, gensym("dsp"), 0);
+ class_addmethod(mp3write_class, (t_method)mp3write_open, gensym("open"), A_SYMBOL, 0);
+ class_addmethod(mp3write_class, (t_method)mp3write_mpeg, gensym("mpeg"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(mp3write_class, (t_method)mp3write_start, gensym("start"), 0);
+ class_addmethod(mp3write_class, (t_method)mp3write_stop, gensym("stop"), 0);
+ class_addmethod(mp3write_class, (t_method)mp3write_print, gensym("print"), 0);
+ class_addmethod(mp3write_class, (t_method)mp3write_append, gensym("append"), 0);
+ class_addmethod(mp3write_class, (t_method)mp3write_truncate, gensym("truncate"), 0);
+}
diff --git a/mp3write~/mpg123.h b/mp3write~/mpg123.h
new file mode 100644
index 0000000..1c530d3
--- /dev/null
+++ b/mp3write~/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/mp3write~/mpglib.h b/mp3write~/mpglib.h
new file mode 100644
index 0000000..1f4ef9a
--- /dev/null
+++ b/mp3write~/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
+
+
+