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_sfplay.c | 660 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 660 insertions(+) create mode 100644 src/z_sfplay.c (limited to 'src/z_sfplay.c') diff --git a/src/z_sfplay.c b/src/z_sfplay.c new file mode 100644 index 0000000..5ba4320 --- /dev/null +++ b/src/z_sfplay.c @@ -0,0 +1,660 @@ +/* +sfplay.c - Author: Winfried Ritsch - IEM Graz 10.Mai 99 - +Modified: + + Description: + + Soundfile player for playing many soundfiles in single speed. + (Made for "3 Farben Schwarz" - exhibition in Graz 99 ) + + Filename must have the path or actual directory, since pathname + search ist not supported to garantuee a fast open call. + + They idea is a state machine which handles open, skip, play, close, error + so that a minimum intervall between OS-calls are made, to avoid peak load. + + It has shown, that the open call is slow if there are a lot of files + to search for, then with the first skip the first part of a + soundfile is also loaded by the OS. + + I experimented with asynchronous buffering with paralell + process,which has shown no much performance hit, since more + processes has to be handled and the modern OS's do caching anyway + also caching is done in modern hard disk, so an additional cache + woud be an overhead, if not special behaviour is needed (big jumps + etc). + + This sfplayers should be used with an appropriate audio buffer for + good performance, so also buffering on the object is an overhead. + + The sfread for linux using mmap has also not much improvement over this, if plain playing in one + direction is done for random access the sfread should be used, even not knowing how to mmap in + NT. + +Todo: + Add the SPEED feature, but therefore there should be an own external using e.g. a 4-point interpolation. + so overhead is reduced in this one. + + Split open to an own object called sfopen to hold more soundfiles + then players and to enable glueless switching between soundfiles. + +please mail problems and ideas for improvements to +ritsch@iem.kug.ac.at */ + +/*#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 ) +#pragma warning( disable : 4018 ) +#endif + +#include +#include +#include + +/* ------------------------ sfplay ----------------------------- */ +#define MAX_CHANS 8 /* channels for soundfiles 1,2,4,8 */ + +#ifdef NT +#define BINREADMODE "rb" +#endif +#ifdef unix +#include +#include +#define BINREADMODE "r" +#endif + +static t_class *sfplay_class; + +typedef struct _sfplay +{ + t_object x_obj; + + t_outlet *bangout; /* end of file */ + + 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 play; /* play: 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 play */ + t_float x_offset; /* offsetto start reading */ + 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; /* play 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 player in */ + t_int count; /* count for ticks before next step */ + +} t_sfplay; + +/* states of statemachine */ +#define SFPLAY_WAIT 0 /* wait for open */ +#define SFPLAY_OPEN 1 +#define SFPLAY_CLOSE 2 +#define SFPLAY_SKIP 3 +#define SFPLAY_PLAY 4 +#define SFPLAY_STOP 5 +#define SFPLAY_ERROR -1 + +#define SFPLAY_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 SFPLAY_WAITTICKS +to avoid peak performance */ + +/* like the one from garray */ +static int sfplay_am_i_big_endian() +{ + unsigned short s = 1; + unsigned char c = *(char *) (&s); + return(c==0); +} + + +void helper(t_sfplay *x) +{ + post("\nsfplay :: a soundfile-player (c) winfried ritsch 1999"); + post("\ncreation :: sfplay : channels set the number of channels, bytes skip fileheader"); + 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" + "\nrewind\t\t\t::rewind tape" + "\ngoto \t\t::play from byte n"); + post("\n\nyou can also start playing with a ´bang´ or a ´1´, and stop with a ´0´" + "\nthe last outlet will do a bang after the last sample has been played"); + +} + + +/* 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 sfplay_open(t_sfplay *x,t_symbol *filename,t_symbol *endian) +{ + + if(x->state != SFPLAY_WAIT) + { + post("sfplay: 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(sfplay_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 */ + + x->filename = filename; + +#ifdef DEBUG_ME + post("sfplay: filename = %s",x->filename->s_name); +#endif + + if (x->fp != NULL)fclose(x->fp); /* should not happen */ + + if (!(x->fp = fopen(x->filename->s_name,BINREADMODE))) + { + error("sfplay: can't open %s", x->filename->s_name); + } +} + + + +/* METHOD: close */ +static void sfplay_close(t_sfplay *x) +{ + x->play = 0; + x->please_close = 1; + + /* now in state machine + if(x->fp != NULL) + { + fclose(x->fp); + x->fp = NULL; + } + */ + +#ifdef DEBUG_ME + post("sfplay: close "); +#endif + return; +} + +/* for skipping header of soundfile Dont use this for memory map */ + +static int sfplay_skip(t_sfplay *x) +{ + if(!x->skip) return 0; + + x->skip = 0; + + if(fseek(x->fp, (long) x->x_offset, SEEK_SET) < 0) + { + error(" sfplay can't seek to byte %ld",(long) x->x_offset); + x->x_offset = x->x_skip; + x->skip = 1; + return 0; + } + +#ifdef DEBUG_ME + post("sfplay:skip to %f",x->x_offset); +#endif + return 1; +} + +/* Input, method for Start stop */ + +static void sfplay_start(t_sfplay *x) +{ + long of = x->offset * sys_getsr() * x->x_channels; + + if(of < 0) of = x->x_skip; + else of += x->x_skip; /* offset in sec */ + + of &= ~0x111l; /* no odds please (8 channels boundary) */ + +#ifdef DEBUG_ME + post("sfplay: start"); +#endif + + /* new offset postion ? (fom inlet offset) */ + if( ((float) of) != x->x_offset) + { + x->skip=1; + x->x_offset = of; + } + x->play=1; +} + +static void sfplay_stop(t_sfplay *x) +{ +#ifdef DEBUG_ME + post("sfplay: stop"); +#endif + + x->play=0; + x->please_stop = 1; +} + +static void sfplay_float(t_sfplay *x, t_floatarg f) +{ + int t = f; + if (t) sfplay_start(x); + else sfplay_stop(x); +} + +/* start playing at position offset*/ +static void sfplay_offset(t_sfplay *x, t_floatarg f) +{ + x->offset = f; + x->skip = 1; + /* correction in sfplay_play() */ + +#ifdef DEBUG_ME + post("sfplay: offset %f",f); +#endif + return; +} + +static void sfplay_rewind(t_sfplay *x) +{ +#ifdef DEBUG_ME + post("sfplay: rewind to %f",x->x_skip); +#endif + + if(!x->fp)return; + + x->play=0; + fseek(x->fp,(long) x->x_skip,SEEK_SET); +} + +/* restart with bang */ + +void sfplay_bang(t_sfplay* x) +{ + x->skip = 1; + sfplay_start(x); +} + +static t_int *sfplay_perform(t_int *w) +{ + t_sfplay* x = (t_sfplay*)(w[1]); + short* buf = x->filep; + int c = x->x_channels; + + int i,j,n; + t_float* out[MAX_CHANS]; + + short s; + int swap = x->swap; + + for (i=0;istate){ + + /* just wait */ + case SFPLAY_WAIT: + + if(x->fp != NULL){ +#ifdef DEBUG_ME + post("wait -> open"); +#endif + x->state = SFPLAY_OPEN; + x->count = SFPLAY_WAITTICKS; + }; + break; + + /* if in open state, already opened but wait for skip */ + case SFPLAY_OPEN: /* file hase opened wait some time */ + + if(!(x->count--)){ +#ifdef DEBUG_ME + post("open -> skip"); +#endif + x->state = SFPLAY_SKIP; + x->count = SFPLAY_WAITTICKS; + }; + + break; + + + /* in skipmode wait until ready for stop */ + case SFPLAY_SKIP: + + + if(x->count == SFPLAY_WAITTICKS) + { + if(!x->fp) + { + x->state = SFPLAY_CLOSE; + x->count=1; +#ifdef DEBUG_ME + post("skip -> close"); +#endif + break; + } + sfplay_skip(x); + } + if(!(x->count--)) + { +#ifdef DEBUG_ME + post("skip -> stop"); +#endif + x->state = SFPLAY_STOP; + x->count = SFPLAY_WAITTICKS; + }; + break; + + + + case SFPLAY_STOP: /* in stop state mainly waits for play */ + + x->please_stop = 0; + + if(x->please_close) + { + x->state = SFPLAY_CLOSE; + x->count = SFPLAY_WAITTICKS; +#ifdef DEBUG_ME + post("stop -> close"); +#endif + } + else if(x->skip) + { + x->state = SFPLAY_SKIP; + x->count = SFPLAY_WAITTICKS; + +#ifdef DEBUG_ME + post("stop -> skip"); +#endif + + } + else if(x->play) + { + +#ifdef DEBUG_ME + post("stop -> play"); +#endif + x->state = SFPLAY_PLAY; + } + break; + + + case SFPLAY_PLAY: /* yes play now */ + + + if(!x->play || x->please_stop) + { + + /* if closing dont need o go to stop */ + if(x->please_close) + { + x->state = SFPLAY_CLOSE; + x->count = SFPLAY_WAITTICKS; +#ifdef DEBUG_ME + post("play -> close"); +#endif + } + else + { + x->state = SFPLAY_STOP; +#ifdef DEBUG_ME + post("play -> stop"); +#endif + }; + break; + } + + /* should never happen */ + if(!x->filep){ + x->state = SFPLAY_ERROR; + error("sfplay: playing but no buffer ???? play"); + return (w+4+c); + } + + /* first read soundfile 16 bit*/ + if((j=fread(buf,sizeof(short),c*n,x->fp)) < (unsigned int) n) + { + + outlet_bang(x->bangout); + + 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 = SFPLAY_STOP; + x->play = 0; + return(w+c+4); + } + + /* or error if(ferror()) */ + x->state = SFPLAY_ERROR; + x->count = SFPLAY_WAITTICKS; + +#ifdef DEBUG_ME + post("play -> read error"); +#endif + break; + }; + + /* copy 16 Bit to floats and swap if neccesairy */ + while (n--) { + for (i=0;i> 8); + *out[i]++ = s*(1./32768.); + } + } + return (w+c+4); /* dont zero out outs */ + + /* ok read error please close */ + case SFPLAY_ERROR: + + if(!(x->count--)){ + x->state = SFPLAY_CLOSE; + sfplay_close(x); +#ifdef DEBUG_ME + post("sfplay error reading sf: error -> close"); +#endif + x->count = SFPLAY_WAITTICKS; + } + break; + + /* in close state go to wait afterwards */ + case SFPLAY_CLOSE: + + x->please_close = 0; + + /* wait until ready for close operation */ + if(!(x->count--)){ + + x->state = SFPLAY_WAIT; + x->count = SFPLAY_WAITTICKS; + + /* avoid openfiles */ + if(x->fp){fclose(x->fp);x->fp = NULL;}; + +#ifdef DEBUG_ME + post("sfplay: close -> wait"); +#endif + } + break; + + }; /*case */ + + /* zero out outs */ + while (n--) { + for (i=0;ix_channels) { + case 1: + dsp_add(sfplay_perform, 4, x, + sp[0]->s_vec, + sp[1]->s_vec, /* out 1 */ + sp[0]->s_n); + break; + case 2: + dsp_add(sfplay_perform, 5, x, + sp[0]->s_vec, /* out 1*/ + sp[1]->s_vec, /* out 2*/ + sp[2]->s_vec, + sp[0]->s_n); + break; + case 4: + dsp_add(sfplay_perform, 6, x, + sp[0]->s_vec, + sp[1]->s_vec, + sp[2]->s_vec, + sp[3]->s_vec, + sp[4]->s_vec, + sp[0]->s_n); + break; + + case 8: + dsp_add(sfplay_perform, 8, 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[8]->s_vec, + sp[0]->s_n); + break; + + } +} + + +/* create sfplay with args */ +static void *sfplay_new(t_floatarg chan,t_floatarg skip) +{ + t_sfplay *x = (t_sfplay *)pd_new(sfplay_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; + } + + floatinlet_new(&x->x_obj, &x->offset); /* inlet 2 */ + /* floatinlet_new(&x->x_obj, &x->speed); *//* inlet 3 */ + + x->x_channels = c; + x->x_skip = x->x_offset = skip; + x->offset = 0.; + x->skip = 1; + x->x_speed = 1.0; + x->play = 0; + x->please_stop = 0; + x->please_close = 0; + x->state = SFPLAY_WAIT; + x->count = 0; + x->filename = NULL; + x->fp = NULL; + x->swap = 1; + + while (c--) { + outlet_new(&x->x_obj, gensym("signal")); /* channels outlet */ + } + x->bangout = outlet_new(&x->x_obj, &s_bang); + + 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("sfplay: x_channels = %d, x_speed = %f, x_skip = %f",x->x_channels,x->x_speed,x->x_skip); +#endif + + return (x); +} + + +static void sfplay_free(t_sfplay *x) +{ + freebytes(x->filep, DACBLKSIZE*sizeof(short)*x->x_channels); +} + +void z_sfplay_setup(void) +{ + sfplay_class = class_new(gensym("sfplay"), (t_newmethod)sfplay_new, (t_method)sfplay_free, + sizeof(t_sfplay), 0, A_DEFFLOAT, A_DEFFLOAT,0); + class_addmethod(sfplay_class, nullfn, gensym("signal"), 0); + class_addmethod(sfplay_class, (t_method)sfplay_dsp, gensym("dsp"), 0); + + class_addmethod(sfplay_class, (t_method)helper, gensym("help"), A_NULL); + class_sethelpsymbol(sfplay_class, gensym("zexy/sf-play_record")); + + /* method open with filename */ + class_addmethod(sfplay_class, (t_method)sfplay_open, gensym("open"), A_SYMBOL,A_SYMBOL,A_NULL); + class_addmethod(sfplay_class, (t_method)sfplay_close, gensym("close"), A_NULL); + + class_addmethod(sfplay_class, (t_method)sfplay_start, gensym("start"), A_NULL); + class_addmethod(sfplay_class, (t_method)sfplay_stop, gensym("stop"), A_NULL); + class_addmethod(sfplay_class, (t_method)sfplay_rewind, gensym("rewind"), A_NULL); + class_addmethod(sfplay_class, (t_method)sfplay_offset, gensym("goto"), A_DEFFLOAT, A_NULL); + + /* start stop with 0 and 1 */ + class_addfloat(sfplay_class, sfplay_float); + /* start with bang */ + class_addbang(sfplay_class,sfplay_bang); +} -- cgit v1.2.1