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 -
20
sfrecord.c - hacked from sfplay ::: 2308:forum::f�r::uml�ute:1999 @ iem
22
please mail problems and ideas for improvements to
24
zmoelnig@iem.kug.ac.at
27
/* TODO: deprecate this in favour of [sfplay~] */
29
/* #define DEBUG_ME for debugging messages */
34
/* #include "m_imp.h" */
36
#define DACBLKSIZE 64 /* in m_imp.h, but error if it is included it here*/
42
/* ------------------------ sfrecord ----------------------------- */
43
#define MAX_CHANS 8 /* channels for soundfiles 1,2,4,8 */
46
# define BINWRITEMODE "wb"
49
# include <sys/mman.h>
50
# define BINWRITEMODE "w"
53
static t_class *sfrecord_class;
55
typedef struct _sfrecord
59
void* filep; /* pointer to file data read in mem */
60
t_symbol* filename; /* filename */
63
because there is no command queue,
64
flags are used instead
66
t_int write; /* write: 1, stop: 0 */
67
t_int please_stop; /* can be reset only by stop-state itself */
68
t_int please_close; /* can be reset only by close-state */
69
t_int x_channels; /* channels to write */
70
t_float x_offset; /* offset to start writing */
71
t_float offset; /* inlet value offset in secs */
72
t_float x_skip; /* skip bytes because header */
73
t_int skip; /* pending skip if 1 */
74
t_float x_speed; /* write speed, not supported in this version */
75
t_int size; /* size of file (if memory mapped) */
76
t_int swap; /* swap bytes from l->b or b->m */
77
FILE *fp; /* file oper non-NULL of open */
78
t_int state; /* which state is writer in */
79
t_int count; /* count for ticks before next step */
83
/* states of statemachine */
84
#define SFRECORD_WAIT 0 /* wait for open */
85
#define SFRECORD_OPEN 1
86
#define SFRECORD_CLOSE 2
87
#define SFRECORD_SKIP 3
88
#define SFRECORD_WRITE 4
89
#define SFRECORD_STOP 5
90
#define SFRECORD_ERROR -1
92
#define SFRECORD_WAITTICKS 10 /* 1 tick of 64 Samples is ca.1.5ms on 441000 */
94
/* split the os-calls in as many steps as possible
95
to split them on different ticks in steps of SFRECORD_WAITTICKS
96
to avoid peak performance */
98
/* like the one from garray */
99
static int sfrecord_am_i_big_endian(void)
101
unsigned short s = 1;
102
unsigned char c = *(char *) (&s);
104
post("i am %s-endian", c?"little":"big");
109
static void state_out(t_sfrecord *x, int state)
110
{ /* outlet the actual state */
111
outlet_float(x->x_obj.ob_outlet, state);
115
/* METHOD: "open" file */
117
/* this dont use memory map, because I dont know about this on NT ?
118
Use of the buffered functions fopen, fseek fread fclose instead the
119
non buffered ones open read close */
121
static void sfrecord_open(t_sfrecord *x,t_symbol *filename,t_symbol *endian)
124
if(x->state != SFRECORD_WAIT)
126
post("sfrecord: first close %s before open %s",x->filename->s_name,filename->s_name);
130
/* test if big endian else asume little endian
131
* should be 'l' but could be anything
133
if(sfrecord_am_i_big_endian())
134
x->swap = !(endian->s_name[0] == 'b');
136
x->swap = (endian->s_name[0] == 'b');
139
* skip header after open;; sometimes we�ll have to write a header using the x->skip; so don�t delete it completely
143
x->filename = filename;
146
post("sfrecord: opening %s",x->filename->s_name);
149
if (x->fp != NULL)fclose(x->fp); /* should not happen */
151
if (!(x->fp = fopen(x->filename->s_name,BINWRITEMODE)))
153
error("sfrecord: can't open %s", x->filename->s_name);
160
static void sfrecord_close(t_sfrecord *x)
165
/* now in state machine
174
post("sfrecord: closing ");
179
/* for skipping header of soundfile Don�t use this for memory map */
181
static int sfrecord_skip(t_sfrecord *x)
183
if(!x->skip) return 0;
186
post("sfrecord:skip to %f",x->x_skip);
193
/* Input, method for Start stop */
195
static void sfrecord_start(t_sfrecord *x)
198
post("sfrecord: start at %d", x->x_offset);
205
static void sfrecord_stop(t_sfrecord *x)
208
post("sfrecord: stop");
216
static void sfrecord_float(t_sfrecord *x, t_floatarg f)
219
if (t) sfrecord_start(x);
220
else sfrecord_stop(x);
223
/* say what state we�re in */
224
static void sfrecord_bang(t_sfrecord* x)
226
if (x->state == SFRECORD_WRITE) state_out(x, 1); else state_out(x, 0);
229
/* ******************************************************************************** */
230
/* the work krow eht */
231
/* ******************************************************************************** */
234
static t_int *sfrecord_perform(t_int *w)
236
t_sfrecord* x = (t_sfrecord*)(w[1]);
237
short* buf = x->filep;
238
short* bufstart = buf;
239
int c = x->x_channels;
242
t_float* in[MAX_CHANS];
248
in[i] = (t_float *)(w[2+i]);
250
n = s_n = (int)(w[2+c]);
261
post("wait -> open");
263
x->state = SFRECORD_OPEN;
264
x->count = SFRECORD_WAITTICKS;
268
/* if in open state, already opened but wait for skip */
269
case SFRECORD_OPEN: /* file has opened wait some time */
273
post("open -> skip");
275
x->state = SFRECORD_SKIP;
276
x->count = SFRECORD_WAITTICKS;
281
/* in skipmode wait until ready for stop */
284
if(x->count == SFRECORD_WAITTICKS)
288
x->state = SFRECORD_CLOSE;
291
post("skip -> close");
300
post("skip -> stop");
302
x->state = SFRECORD_STOP;
303
x->count = SFRECORD_WAITTICKS;
307
case SFRECORD_STOP: /* in stop state mainly waits for write */
313
x->state = SFRECORD_CLOSE;
314
x->count = SFRECORD_WAITTICKS;
316
post("stop -> close");
321
x->state = SFRECORD_SKIP;
322
x->count = SFRECORD_WAITTICKS;
325
post("stop -> skip");
333
post("stop -> write");
335
x->state = SFRECORD_WRITE;
340
case SFRECORD_WRITE: /* yes write now */
342
if(!x->write || x->please_stop)
344
/* if closing dont need to go to stop */
345
if(x->please_close) {
346
x->state = SFRECORD_CLOSE;
347
x->count = SFRECORD_WAITTICKS;
349
post("write -> close");
355
x->state = SFRECORD_STOP;
357
post("write -> stop");
363
/* should never happen */
365
x->state = SFRECORD_ERROR;
366
error("sfrecord: writing but no buffer ???? write");
370
/* copy float to 16 Bit and swap if neccesairy */ /* LATER treat overflows */
373
s = *in[i]++ * 32768.;
374
if (swap) s = ((s & 0xFF)<< 8) | ((s& 0xFF00) >> 8);
379
/* then write soundfile 16 bit*/
380
if ( (j = fwrite(bufstart, sizeof(short), c*s_n, x->fp)) < 1) {
381
x->state = SFRECORD_ERROR;
382
x->count = SFRECORD_WAITTICKS;
384
post("write -> write error\t %xd\t%xd\t%d\t%d", x->filep, buf, c*s_n*sizeof(short), j);
390
if((j=fwrite(buf,sizeof(short),c*n,x->fp)) < (unsigned int) n)
398
if(swap) s = ((s & 0xFF)<< 8) | ((s& 0xFF00) >> 8);
399
*out[i]++ = s*(1./32768.);
407
x->state = SFRECORD_STOP;
411
/* or error if(ferror()) */
412
x->state = SFRECORD_ERROR;
413
x->count = SFRECORD_WAITTICKS;
415
post("write -> write error");
420
return (w+c+3); /* writing was fine */
423
/* ok :?: write error, please close */
427
x->state = SFRECORD_CLOSE;
430
post("sfrecord error writing sf: error -> close");
432
x->count = SFRECORD_WAITTICKS;
436
/* in close state go to wait afterwards */
441
/* wait until ready for close operation */
444
x->state = SFRECORD_WAIT;
445
x->count = SFRECORD_WAITTICKS;
447
/* avoid openfiles */
448
if(x->fp){fclose(x->fp);x->fp = NULL;};
451
post("sfrecord: close -> wait");
464
/* ---------------------- Setup junk -------------------------- */
466
static void sfrecord_dsp(t_sfrecord *x, t_signal **sp)
470
post("sfrecord: dsp");
471
post("offset = %f\tspeed = %f\t", x->offset, x->x_speed);
475
switch (x->x_channels) {
477
dsp_add(sfrecord_perform, 3, x,
478
sp[0]->s_vec, /* in 1 */
482
dsp_add(sfrecord_perform, 4, x,
488
dsp_add(sfrecord_perform, 6, x,
496
dsp_add(sfrecord_perform, 9, x,
511
/* create sfrecord with args <channels> <skip> */
512
static void *sfrecord_new(t_floatarg chan)
514
t_sfrecord *x = (t_sfrecord *)pd_new(sfrecord_class);
519
case 1: case 2: case 4: case 8: break;
520
/* try it, good luck ... */
521
case 3: c = 2; break;
522
case 5: case 6: case 7: c=7; break;
526
outlet_new(&x->x_obj, &s_float);
529
x->x_skip = x->x_offset = 0;
536
x->state = SFRECORD_WAIT;
546
post("create extra channel #%d", c);
548
inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); /* channels inlet */
551
x->filep = t_getbytes(DACBLKSIZE*sizeof(short)*x->x_channels);
554
post("get_bytes DACBLKSIZE*%d*%d->%ld",sizeof(short),x->x_channels,x->filep);
555
post("sfrecord: x_channels = %d, x_speed = %f, x_skip = %f",x->x_channels,x->x_speed,x->x_skip);
562
static void sfrecord_helper(void)
564
post("\nsfplay :: a raw-data soundfile-recorder");
565
post("\ncreation :: sfrecord <channels>\t: channels set the number of channels");
566
post("\nopen [<path>]<filename> [<endianity>]\t:: open b(ig) or l(ittle) endian file"
567
"\nclose\t\t\t:: close file (aka eject)"
568
"\nstart\t\t\t:: start playing"
569
"\nstop\t\t\t:: stop playing"
570
"\nbang\t\t\t:: outputs the current state (1_recording, 0_not-recording)");
572
post("\n\nyou can also start recording with a �1�, and stop with a �0�");
576
static void sfrecord_free(t_sfrecord *x)
578
freebytes(x->filep, DACBLKSIZE*sizeof(short)*x->x_channels);
581
void sfrecord_setup(void)
583
sfrecord_class = class_new(gensym("sfrecord"), (t_newmethod)sfrecord_new, (t_method)sfrecord_free,
584
sizeof(t_sfrecord), 0, A_DEFFLOAT, A_DEFFLOAT,0);
585
class_addmethod(sfrecord_class, nullfn, gensym("signal"), 0);
586
class_addmethod(sfrecord_class, (t_method)sfrecord_dsp, gensym("dsp"), 0);
588
/* method open with filename */
589
class_addmethod(sfrecord_class, (t_method)sfrecord_open, gensym("open"), A_SYMBOL,A_SYMBOL,A_NULL);
590
class_addmethod(sfrecord_class, (t_method)sfrecord_close, gensym("close"), A_NULL);
592
class_addmethod(sfrecord_class, (t_method)sfrecord_start, gensym("start"), A_NULL);
593
class_addmethod(sfrecord_class, (t_method)sfrecord_stop, gensym("stop"), A_NULL);
595
/* start/stop with 0/1 */
596
class_addfloat(sfrecord_class, sfrecord_float);
598
/* bang out the current-state to the outlet*/
599
class_addbang(sfrecord_class,sfrecord_bang);
602
class_addmethod(sfrecord_class, (t_method)sfrecord_helper, gensym("help"), A_NULL);
603
class_sethelpsymbol(sfrecord_class, gensym("sf-play_record"));
604
zexy_register("sfrecord");