From d2eec74a4d8c21aad495ba61539486b24d7ab8dc Mon Sep 17 00:00:00 2001 From: Guenter Geiger Date: Wed, 9 Oct 2002 10:19:04 +0000 Subject: moved from zexy/zexy to zexy svn path=/trunk/externals/zexy/; revision=169 --- src/z_sfrecord.c | 592 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 592 insertions(+) create mode 100644 src/z_sfrecord.c (limited to 'src/z_sfrecord.c') diff --git a/src/z_sfrecord.c b/src/z_sfrecord.c new file mode 100644 index 0000000..027a246 --- /dev/null +++ b/src/z_sfrecord.c @@ -0,0 +1,592 @@ +/* +sfplay.c - Author: Winfried Ritsch - IEM Graz 10.Mai 99 - +Modified: +sfrecord.c - hacked from sfplay ::: 2308:forum::für::umläute:1999 @ iem + +please mail problems and ideas for improvements to +ritsch@iem.kug.ac.at +zmoelnig@iem.kug.ac.at +*/ + +/* to do :: add headers for wav, aiff etc files */ + + +/* #define DEBUG_ME // for debugging messages */ + +#include "zexy.h" + + +/* #include "m_imp.h" */ + +#define DACBLKSIZE 64 /* in m_imp.h, but error if it is included it here*/ + +#ifdef NT +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4305 ) +#endif + +#include +#include +#include + +/* ------------------------ sfrecord ----------------------------- */ +#define MAX_CHANS 8 /* channels for soundfiles 1,2,4,8 */ + +#ifdef NT +#define BINWRITEMODE "wb" +#endif +#ifdef linux +#include +#include +#define BINWRITEMODE "w" +#endif + +static t_class *sfrecord_class; + +typedef struct _sfrecord +{ + t_object x_obj; + + void* filep; /* pointer to file data read in mem */ + t_symbol* filename; /* filename */ + + /* + because there is no command queue, + flags are used instead + */ + t_int write; /* write: 1, stop: 0 */ + t_int please_stop; /* can be reset only by stop-state itself */ + t_int please_close; /* can be reset only by close-state */ + t_int x_channels; /* channels to write */ + t_float x_offset; /* offset to start writing */ + t_float offset; /* inlet value offset in secs */ + t_float x_skip; /* skip bytes because header */ + t_int skip; /* pending skip if 1 */ + t_float x_speed; /* write speed, not supported in this version */ + t_int size; /* size of file (if memory mapped) */ + t_int swap; /* swap bytes from l->b or b->m */ + FILE *fp; /* file oper non-NULL of open */ + t_int state; /* which state is writer in */ + t_int count; /* count for ticks before next step */ + +} t_sfrecord; + +/* states of statemachine */ +#define SFRECORD_WAIT 0 /* wait for open */ +#define SFRECORD_OPEN 1 +#define SFRECORD_CLOSE 2 +#define SFRECORD_SKIP 3 +#define SFRECORD_WRITE 4 +#define SFRECORD_STOP 5 +#define SFRECORD_ERROR -1 + +#define SFRECORD_WAITTICKS 10 /* 1 tick of 64 Samples is ca.1.5ms on 441000 */ + +/* split the os-calls in as many steps as possible +to split them on different ticks in steps of SFRECORD_WAITTICKS +to avoid peak performance */ + +/* like the one from garray */ +static int sfrecord_am_i_big_endian() +{ + unsigned short s = 1; + unsigned char c = *(char *) (&s); +#ifdef DEBUG_ME + post("i am %s-endian", c?"little":"big"); +#endif + return(c==0); +} + +static void state_out(t_sfrecord *x, int state) +{ /* outlet the actual state */ + outlet_float(x->x_obj.ob_outlet, state); +} + + +/* METHOD: "open" file */ + +/* this dont use memory map, because I dont know about this on NT ? +Use of the buffered functions fopen, fseek fread fclose instead the +non buffered ones open read close */ + +void sfrecord_open(t_sfrecord *x,t_symbol *filename,t_symbol *endian) +{ + + if(x->state != SFRECORD_WAIT) + { + post("sfrecord: first close %s before open %s",x->filename->s_name,filename->s_name); + return; + } + +/* test if big endian else asume little endian + should be 'l' but could be anything*/ + + if(sfrecord_am_i_big_endian()) + x->swap = !(endian->s_name[0] == 'b'); + else + x->swap = (endian->s_name[0] == 'b'); + +// x->skip = 1; /* skip header after open;; sometimes we´ll have to write a header using the x->skip; so don´t delete it completely*/ + + x->filename = filename; + +#ifdef DEBUG_ME + post("sfrecord: opening %s",x->filename->s_name); +#endif + + if (x->fp != NULL)fclose(x->fp); /* should not happen */ + + if (!(x->fp = fopen(x->filename->s_name,BINWRITEMODE))) + { + error("sfrecord: can't open %s", x->filename->s_name); + } +} + + + +/* METHOD: close */ +static void sfrecord_close(t_sfrecord *x) +{ + x->write = 0; + x->please_close = 1; + + /* now in state machine + if(x->fp != NULL) + { + fclose(x->fp); + x->fp = NULL; + } + */ + +#ifdef DEBUG_ME + post("sfrecord: closing "); +#endif + return; +} + +/* for skipping header of soundfile Don´t use this for memory map */ + +static int sfrecord_skip(t_sfrecord *x) +{ + if(!x->skip) return 0; + +#ifdef DEBUG_ME + post("sfrecord:skip to %f",x->x_skip); +#endif + + x->skip = 0; + return 1; +} + +/* Input, method for Start stop */ + +static void sfrecord_start(t_sfrecord *x) +{ +#ifdef DEBUG_ME + post("sfrecord: start at %d", x->x_offset); +#endif + + state_out(x, 1); + x->write=1; +} + +static void sfrecord_stop(t_sfrecord *x) +{ +#ifdef DEBUG_ME + post("sfrecord: stop"); +#endif + state_out(x, 0); + + x->write=0; + x->please_stop = 1; +} + +static void sfrecord_float(t_sfrecord *x, t_floatarg f) +{ + int t = f; + if (t) sfrecord_start(x); + else sfrecord_stop(x); +} + +/* say what state we´re in */ +void sfrecord_bang(t_sfrecord* x) +{ + if (x->state == SFRECORD_WRITE) state_out(x, 1); else state_out(x, 0); +} + +/* ******************************************************************************** */ +/* the work krow eht */ +/* ******************************************************************************** */ + + +static t_int *sfrecord_perform(t_int *w) +{ + t_sfrecord* x = (t_sfrecord*)(w[1]); + short* buf = x->filep; + short* bufstart = buf; + int c = x->x_channels; + + int i,j,n, s_n; + t_float* in[MAX_CHANS]; + + short s; + int swap = x->swap; + + for (i=0;istate){ + + /* just wait */ + case SFRECORD_WAIT: + + if(x->fp != NULL){ +#ifdef DEBUG_ME + post("wait -> open"); +#endif + x->state = SFRECORD_OPEN; + x->count = SFRECORD_WAITTICKS; + }; + break; + + /* if in open state, already opened but wait for skip */ + case SFRECORD_OPEN: /* file has opened wait some time */ + + if(!(x->count--)){ +#ifdef DEBUG_ME + post("open -> skip"); +#endif + x->state = SFRECORD_SKIP; + x->count = SFRECORD_WAITTICKS; + }; + + break; + + /* in skipmode wait until ready for stop */ + case SFRECORD_SKIP: + + if(x->count == SFRECORD_WAITTICKS) + { + if(!x->fp) + { + x->state = SFRECORD_CLOSE; + x->count=1; +#ifdef DEBUG_ME + post("skip -> close"); +#endif + break; + } + sfrecord_skip(x); + } + if(!(x->count--)) + { +#ifdef DEBUG_ME + post("skip -> stop"); +#endif + x->state = SFRECORD_STOP; + x->count = SFRECORD_WAITTICKS; + }; + break; + + case SFRECORD_STOP: /* in stop state mainly waits for write */ + + x->please_stop = 0; + + if(x->please_close) + { + x->state = SFRECORD_CLOSE; + x->count = SFRECORD_WAITTICKS; +#ifdef DEBUG_ME + post("stop -> close"); +#endif + } + else if(x->skip) + { + x->state = SFRECORD_SKIP; + x->count = SFRECORD_WAITTICKS; + +#ifdef DEBUG_ME + post("stop -> skip"); +#endif + + } + else if(x->write) + { + +#ifdef DEBUG_ME + post("stop -> write"); +#endif + x->state = SFRECORD_WRITE; + state_out(x, 1); + } + break; + + case SFRECORD_WRITE: /* yes write now */ + + if(!x->write || x->please_stop) + { + /* if closing dont need to go to stop */ + if(x->please_close) { + x->state = SFRECORD_CLOSE; + x->count = SFRECORD_WAITTICKS; +#ifdef DEBUG_ME + post("write -> close"); +#endif + state_out(x, 0); + + } + else { + x->state = SFRECORD_STOP; +#ifdef DEBUG_ME + post("write -> stop"); +#endif + }; + break; + } + + /* should never happen */ + if(!x->filep){ + x->state = SFRECORD_ERROR; + error("sfrecord: writing but no buffer ???? write"); + return (w+4+c); + } + + /* copy float to 16 Bit and swap if neccesairy */ /* LATER treat overflows */ + while (n--) { + for (i=0;i> 8); + *buf++ = s; + } + } + + /* then write soundfile 16 bit*/ + if ( (j = fwrite(bufstart, sizeof(short), c*s_n, x->fp)) < 1) { + x->state = SFRECORD_ERROR; + x->count = SFRECORD_WAITTICKS; +#ifdef DEBUG_ME + post("write -> write error\t %xd\t%xd\t%d\t%d", x->filep, buf, c*s_n*sizeof(short), j); +#endif + break; + } + +#if 0 + if((j=fwrite(buf,sizeof(short),c*n,x->fp)) < (unsigned int) n) + { + if(feof(x->fp)){ + + while (n--) { + for (i=0;i 0){ + s = *buf++; + if(swap) s = ((s & 0xFF)<< 8) | ((s& 0xFF00) >> 8); + *out[i]++ = s*(1./32768.); + } + else + *out[i]++ = 0; + } + } + } + + x->state = SFRECORD_STOP; + x->write = 0; + return(w+c+3); + } + /* or error if(ferror()) */ + x->state = SFRECORD_ERROR; + x->count = SFRECORD_WAITTICKS; +#ifdef DEBUG_ME + post("write -> write error"); +#endif + break; + }; +#endif //0 + return (w+c+3); /* writing was fine */ + + + /* ok :?: write error, please close */ + case SFRECORD_ERROR: + + if(!(x->count--)) { + x->state = SFRECORD_CLOSE; + sfrecord_close(x); +#ifdef DEBUG_ME + post("sfrecord error writing sf: error -> close"); +#endif + x->count = SFRECORD_WAITTICKS; + } + break; + + /* in close state go to wait afterwards */ + case SFRECORD_CLOSE: + + x->please_close = 0; + + /* wait until ready for close operation */ + if(!(x->count--)){ + + x->state = SFRECORD_WAIT; + x->count = SFRECORD_WAITTICKS; + + /* avoid openfiles */ + if(x->fp){fclose(x->fp);x->fp = NULL;}; + +#ifdef DEBUG_ME + post("sfrecord: close -> wait"); +#endif + } + break; + + }; /*case */ + + return(w+c+3); +} + + + + +/* ---------------------- Setup junk -------------------------- */ + +static void sfrecord_dsp(t_sfrecord *x, t_signal **sp) +{ + +#ifdef DEBUG_ME + post("sfrecord: dsp"); + post("offset = %f\tspeed = %f\t", x->offset, x->x_speed); +#endif + + + switch (x->x_channels) { + case 1: + dsp_add(sfrecord_perform, 3, x, + sp[0]->s_vec, /* in 1 */ + sp[0]->s_n); + break; + case 2: + dsp_add(sfrecord_perform, 4, x, + sp[0]->s_vec, + sp[1]->s_vec, + sp[0]->s_n); + break; + case 4: + dsp_add(sfrecord_perform, 5, x, + sp[0]->s_vec, + sp[1]->s_vec, + sp[2]->s_vec, + sp[3]->s_vec, + sp[0]->s_n); + break; + case 8: + dsp_add(sfrecord_perform, 7, x, + sp[0]->s_vec, + sp[1]->s_vec, + sp[2]->s_vec, + sp[3]->s_vec, + sp[4]->s_vec, + sp[5]->s_vec, + sp[6]->s_vec, + sp[7]->s_vec, + sp[0]->s_n); + break; + } +} + + +/* create sfrecord with args */ +static void *sfrecord_new(t_floatarg chan) +{ + t_sfrecord *x = (t_sfrecord *)pd_new(sfrecord_class); + t_int c = chan; + + switch(c){ + /* ok */ + case 1: case 2: case 8: break; + /* try it, good luck ... */ + case 3: c = 2; break; + case 5: case 6: case 7: c=7; break; + default: c=1; break; + } + + outlet_new(&x->x_obj, &s_float); + + x->x_channels = c; + x->x_skip = x->x_offset = 0; + x->skip = 1; + x->offset = 0.; + x->x_speed = 1.0; + x->write = 0; + x->please_stop = 0; + x->please_close = 0; + x->state = SFRECORD_WAIT; + x->count = 0; + x->filename = NULL; + x->fp = NULL; + x->swap = 1; + + c--; + + while (c--) { +#ifdef DEBUG_ME + post("create extra channel #%d", c); +#endif + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); /* channels inlet */ + } + + x->filep = t_getbytes(DACBLKSIZE*sizeof(short)*x->x_channels); + +#ifdef DEBUG_ME + post("get_bytes DACBLKSIZE*%d*%d->%ld",sizeof(short),x->x_channels,x->filep); + post("sfrecord: x_channels = %d, x_speed = %f, x_skip = %f",x->x_channels,x->x_speed,x->x_skip); +#endif + + return (x); +} + + +static void helper(void) +{ + post("\nsfplay :: a raw-data soundfile-recorder"); + post("\ncreation :: sfrecord \t: channels set the number of channels"); + post("\nopen [] []\t:: open b(ig) or l(ittle) endian file" + "\nclose\t\t\t:: close file (aka eject)" + "\nstart\t\t\t:: start playing" + "\nstop\t\t\t:: stop playing" + "\nbang\t\t\t:: outputs the current state (1_recording, 0_not-recording)"); + + post("\n\nyou can also start recording with a ´1´, and stop with a ´0´"); +} + + +static void sfrecord_free(t_sfrecord *x) +{ + freebytes(x->filep, DACBLKSIZE*sizeof(short)*x->x_channels); +} + +void z_sfrecord_setup(void) +{ + sfrecord_class = class_new(gensym("sfrecord"), (t_newmethod)sfrecord_new, (t_method)sfrecord_free, + sizeof(t_sfrecord), 0, A_DEFFLOAT, A_DEFFLOAT,0); + class_addmethod(sfrecord_class, nullfn, gensym("signal"), 0); + class_addmethod(sfrecord_class, (t_method)sfrecord_dsp, gensym("dsp"), 0); + + /* method open with filename */ + class_addmethod(sfrecord_class, (t_method)sfrecord_open, gensym("open"), A_SYMBOL,A_SYMBOL,A_NULL); + class_addmethod(sfrecord_class, (t_method)sfrecord_close, gensym("close"), A_NULL); + + class_addmethod(sfrecord_class, (t_method)sfrecord_start, gensym("start"), A_NULL); + class_addmethod(sfrecord_class, (t_method)sfrecord_stop, gensym("stop"), A_NULL); + + /* start/stop with 0/1 */ + class_addfloat(sfrecord_class, sfrecord_float); + + /* bang out the current-state to the outlet*/ + class_addbang(sfrecord_class,sfrecord_bang); + + /* some help */ + class_addmethod(sfrecord_class, (t_method)helper, gensym("help"), A_NULL); + class_sethelpsymbol(sfrecord_class, gensym("zexy/sf-play_record")); +} -- cgit v1.2.1