/* 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); }