1
/******************************************************
3
* zexy - implementation file
5
* copyleft (c) IOhannes m zm�lnig
7
* 1999:forum::f�r::uml�ute:2004
9
* institute of electronic music and acoustics (iem)
11
******************************************************
13
* license: GNU General Public License v.2
15
******************************************************/
18
sfplay.c - Author: Winfried Ritsch - IEM Graz 10.Mai 99 -
23
Soundfile player for playing many soundfiles in single speed.
24
(Made for "3 Farben Schwarz" - exhibition in Graz 99 )
26
Filename must have the path or actual directory, since pathname
27
search ist not supported to garantuee a fast open call.
29
They idea is a state machine which handles open, skip, play, close, error
30
so that a minimum intervall between OS-calls are made, to avoid peak load.
32
It has shown, that the open call is slow if there are a lot of files
33
to search for, then with the first skip the first part of a
34
soundfile is also loaded by the OS.
36
I experimented with asynchronous buffering with paralell
37
process,which has shown no much performance hit, since more
38
processes has to be handled and the modern OS's do caching anyway
39
also caching is done in modern hard disk, so an additional cache
40
woud be an overhead, if not special behaviour is needed (big jumps
43
This sfplayers should be used with an appropriate audio buffer for
44
good performance, so also buffering on the object is an overhead.
46
The sfread for linux using mmap has also not much improvement over this, if plain playing in one
47
direction is done for random access the sfread should be used, even not knowing how to mmap in
51
Add the SPEED feature, but therefore there should be an own external using e.g. a 4-point interpolation.
52
so overhead is reduced in this one.
54
Split open to an own object called sfopen to hold more soundfiles
55
then players and to enable glueless switching between soundfiles.
57
please mail problems and ideas for improvements to
58
ritsch@iem.kug.ac.at */
60
/*#define DEBUG_ME // for debugging messages */
64
#define DACBLKSIZE 64 /* in m_imp.h, but error if it is included it here*/
70
/* ------------------------ sfplay ----------------------------- */
71
#define MAX_CHANS 8 /* channels for soundfiles 1,2,4,8 */
74
# define BINREADMODE "rb"
77
# include <sys/mman.h>
78
# define BINREADMODE "r"
81
static t_class *sfplay_class;
83
typedef struct _sfplay
87
t_outlet *bangout; /* end of file */
89
void* filep; /* pointer to file data read in mem */
90
t_symbol* filename; /* filename */
92
because there is no command queue,
93
flags are used instead
95
t_int play; /* play: 1, stop: 0 */
96
t_int please_stop; /* can be reset only by stop-state itself */
97
t_int please_close; /* can be reset only by close-state */
98
t_int x_channels; /* channels to play */
99
t_float x_offset; /* offsetto start reading */
100
t_float offset; /* inlet value offset in secs */
101
t_float x_skip; /* skip bytes because header */
102
t_int skip; /* pending skip if 1 */
103
t_float x_speed; /* play speed, not supported in this version */
104
t_int size; /* size of file (if memory mapped) */
105
t_int swap; /* swap bytes from l->b or b->m */
106
FILE *fp; /* file oper non-NULL of open */
107
t_int state; /* which state is player in */
108
t_int count; /* count for ticks before next step */
112
/* states of statemachine */
113
#define SFPLAY_WAIT 0 /* wait for open */
114
#define SFPLAY_OPEN 1
115
#define SFPLAY_CLOSE 2
116
#define SFPLAY_SKIP 3
117
#define SFPLAY_PLAY 4
118
#define SFPLAY_STOP 5
119
#define SFPLAY_ERROR -1
121
#define SFPLAY_WAITTICKS 10 /* 1 tick of 64 Samples is ca. 1.5ms on 441000 */
123
/* split the os-calls in as many steps as possible
124
to split them on different ticks in steps of SFPLAY_WAITTICKS
125
to avoid peak performance */
127
/* like the one from garray */
128
static int sfplay_am_i_big_endian(void)
130
unsigned short s = 1;
131
unsigned char c = *(char *) (&s);
136
static void sfplay_helper(t_sfplay *x)
139
post("\nsfplay :: a soundfile-player (c) winfried ritsch 1999");
140
post("\ncreation :: sfplay <channels> <bytes> : channels set the number of channels, bytes skip fileheader");
141
post("\nopen [<path>]<filename> [<endianity>]\t::open b(ig) or l(ittle) endian file"
142
"\nclose\t\t\t::close file (aka eject)"
143
"\nstart\t\t\t::start playing"
144
"\nstop\t\t\t::stop playing"
145
"\nrewind\t\t\t::rewind tape"
146
"\ngoto <n>\t\t::play from byte n");
147
post("\n\nyou can also start playing with a �bang� or a �1�, and stop with a �0�"
148
"\nthe last outlet will do a bang after the last sample has been played");
153
/* METHOD: "open" file */
155
/* this dont use memory map, because I dont know about this on NT ?
156
Use of the buffered functions fopen, fseek fread fclose instead the
157
non buffered ones open read close */
159
static void sfplay_open(t_sfplay *x,t_symbol *filename,t_symbol *endian)
162
if(x->state != SFPLAY_WAIT)
164
post("sfplay: first close %s before open %s",x->filename->s_name,filename->s_name);
168
/* test if big endian else asume little endian
169
should be 'l' but could be anything*/
171
if(sfplay_am_i_big_endian())
172
x->swap = !(endian->s_name[0] == 'b');
174
x->swap = (endian->s_name[0] == 'b');
176
x->skip = 1; /* skip header after open */
178
x->filename = filename;
181
post("sfplay: filename = %s",x->filename->s_name);
184
if (x->fp != NULL)fclose(x->fp); /* should not happen */
186
if (!(x->fp = fopen(x->filename->s_name,BINREADMODE)))
188
error("sfplay: can't open %s", x->filename->s_name);
195
static void sfplay_close(t_sfplay *x)
200
/* now in state machine
209
post("sfplay: close ");
214
/* for skipping header of soundfile Dont use this for memory map */
216
static int sfplay_skip(t_sfplay *x)
218
if(!x->skip) return 0;
222
if(fseek(x->fp, (long) x->x_offset, SEEK_SET) < 0)
224
error(" sfplay can't seek to byte %ld",(long) x->x_offset);
225
x->x_offset = x->x_skip;
231
post("sfplay:skip to %f",x->x_offset);
236
/* Input, method for Start stop */
238
static void sfplay_start(t_sfplay *x)
240
long of = x->offset * sys_getsr() * x->x_channels;
242
if(of < 0) of = x->x_skip;
243
else of += x->x_skip; /* offset in sec */
245
of &= ~0x111l; /* no odds please (8 channels boundary) */
248
post("sfplay: start");
251
/* new offset postion ? (fom inlet offset) */
252
if( ((t_float) of) != x->x_offset)
260
static void sfplay_stop(t_sfplay *x)
263
post("sfplay: stop");
270
static void sfplay_float(t_sfplay *x, t_floatarg f)
273
if (t) sfplay_start(x);
277
/* start playing at position offset*/
278
static void sfplay_offset(t_sfplay *x, t_floatarg f)
282
/* correction in sfplay_play() */
285
post("sfplay: offset %f",f);
290
static void sfplay_rewind(t_sfplay *x)
293
post("sfplay: rewind to %f",x->x_skip);
299
fseek(x->fp,(long) x->x_skip,SEEK_SET);
302
/* restart with bang */
304
static void sfplay_bang(t_sfplay* x)
310
static t_int *sfplay_perform(t_int *w)
312
t_sfplay* x = (t_sfplay*)(w[1]);
313
short* buf = x->filep;
314
int c = x->x_channels;
317
t_float* out[MAX_CHANS];
323
out[i] = (t_float *)(w[3+i]);
337
post("wait -> open");
339
x->state = SFPLAY_OPEN;
340
x->count = SFPLAY_WAITTICKS;
344
/* if in open state, already opened but wait for skip */
345
case SFPLAY_OPEN: /* file hase opened wait some time */
349
post("open -> skip");
351
x->state = SFPLAY_SKIP;
352
x->count = SFPLAY_WAITTICKS;
358
/* in skipmode wait until ready for stop */
362
if(x->count == SFPLAY_WAITTICKS)
366
x->state = SFPLAY_CLOSE;
369
post("skip -> close");
378
post("skip -> stop");
380
x->state = SFPLAY_STOP;
381
x->count = SFPLAY_WAITTICKS;
387
case SFPLAY_STOP: /* in stop state mainly waits for play */
393
x->state = SFPLAY_CLOSE;
394
x->count = SFPLAY_WAITTICKS;
396
post("stop -> close");
401
x->state = SFPLAY_SKIP;
402
x->count = SFPLAY_WAITTICKS;
405
post("stop -> skip");
413
post("stop -> play");
415
x->state = SFPLAY_PLAY;
420
case SFPLAY_PLAY: /* yes play now */
423
if(!x->play || x->please_stop)
426
/* if closing dont need o go to stop */
429
x->state = SFPLAY_CLOSE;
430
x->count = SFPLAY_WAITTICKS;
432
post("play -> close");
437
x->state = SFPLAY_STOP;
439
post("play -> stop");
445
/* should never happen */
447
x->state = SFPLAY_ERROR;
448
error("sfplay: playing but no buffer ???? play");
452
/* first read soundfile 16 bit*/
453
if((j=fread(buf,sizeof(short),c*n,x->fp)) < n)
456
outlet_bang(x->bangout);
464
if(swap) s = ((s & 0xFF)<< 8) | ((s& 0xFF00) >> 8);
465
*out[i]++ = s*(1./32768.);
471
x->state = SFPLAY_STOP;
476
/* or error if(ferror()) */
477
x->state = SFPLAY_ERROR;
478
x->count = SFPLAY_WAITTICKS;
481
post("play -> read error");
486
/* copy 16 Bit to floats and swap if neccesairy */
490
if(swap) s = ((s & 0xFF)<< 8) | ((s& 0xFF00) >> 8);
491
*out[i]++ = s*(1./32768.);
494
return (w+c+4); /* dont zero out outs */
496
/* ok read error please close */
500
x->state = SFPLAY_CLOSE;
503
post("sfplay error reading sf: error -> close");
505
x->count = SFPLAY_WAITTICKS;
509
/* in close state go to wait afterwards */
514
/* wait until ready for close operation */
517
x->state = SFPLAY_WAIT;
518
x->count = SFPLAY_WAITTICKS;
520
/* avoid openfiles */
521
if(x->fp){fclose(x->fp);x->fp = NULL;};
524
post("sfplay: close -> wait");
541
/* ---------------------- Setup junk -------------------------- */
543
static void sfplay_dsp(t_sfplay *x, t_signal **sp)
550
switch (x->x_channels) {
552
dsp_add(sfplay_perform, 4, x,
554
sp[1]->s_vec, /* out 1 */
558
dsp_add(sfplay_perform, 5, x,
559
sp[0]->s_vec, /* out 1*/
560
sp[1]->s_vec, /* out 2*/
565
dsp_add(sfplay_perform, 7, x,
574
dsp_add(sfplay_perform, 11, x,
590
/* create sfplay with args <channels> <skip> */
591
static void *sfplay_new(t_floatarg chan,t_floatarg skip)
593
t_sfplay *x = (t_sfplay *)pd_new(sfplay_class);
598
case 1: case 2: case 4: case 8: break;
599
/* try it, good luck ... */
600
case 3: c = 2; break;
601
case 5: case 6: case 7: c=7; break;
605
floatinlet_new(&x->x_obj, &x->offset); /* inlet 2 */
606
/* floatinlet_new(&x->x_obj, &x->speed); *//* inlet 3 */
609
x->x_skip = x->x_offset = skip;
616
x->state = SFPLAY_WAIT;
623
outlet_new(&x->x_obj, gensym("signal")); /* channels outlet */
625
x->bangout = outlet_new(&x->x_obj, &s_bang);
627
x->filep = t_getbytes(DACBLKSIZE*sizeof(short)*x->x_channels);
630
post("get_bytes DACBLKSIZE*%d*%d->%ld",sizeof(short),x->x_channels,x->filep);
631
post("sfplay: x_channels = %d, x_speed = %f, x_skip = %f",x->x_channels,x->x_speed,x->x_skip);
638
static void sfplay_free(t_sfplay *x)
640
freebytes(x->filep, DACBLKSIZE*sizeof(short)*x->x_channels);
643
void sfplay_setup(void)
645
sfplay_class = class_new(gensym("sfplay"), (t_newmethod)sfplay_new, (t_method)sfplay_free,
646
sizeof(t_sfplay), 0, A_DEFFLOAT, A_DEFFLOAT,0);
647
class_addmethod(sfplay_class, nullfn, gensym("signal"), 0);
648
class_addmethod(sfplay_class, (t_method)sfplay_dsp, gensym("dsp"), 0);
650
class_addmethod(sfplay_class, (t_method)sfplay_helper, gensym("help"), A_NULL);
651
class_sethelpsymbol(sfplay_class, gensym("sf-play_record"));
653
/* method open with filename */
654
class_addmethod(sfplay_class, (t_method)sfplay_open, gensym("open"), A_SYMBOL,A_SYMBOL,A_NULL);
655
class_addmethod(sfplay_class, (t_method)sfplay_close, gensym("close"), A_NULL);
657
class_addmethod(sfplay_class, (t_method)sfplay_start, gensym("start"), A_NULL);
658
class_addmethod(sfplay_class, (t_method)sfplay_stop, gensym("stop"), A_NULL);
659
class_addmethod(sfplay_class, (t_method)sfplay_rewind, gensym("rewind"), A_NULL);
660
class_addmethod(sfplay_class, (t_method)sfplay_offset, gensym("goto"), A_DEFFLOAT, A_NULL);
662
/* start stop with 0 and 1 */
663
class_addfloat(sfplay_class, sfplay_float);
664
/* start with bang */
665
class_addbang(sfplay_class,sfplay_bang);
666
zexy_register("sfplay");