Recognizes if input is ogg or not.
[xiph.git] / postfish / input.c
blob515c17eeab6e4f580b844187517bc27272dd6375
1 /*
3 * postfish
4 *
5 * Copyright (C) 2002-2005 Monty
7 * Postfish is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
12 * Postfish is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Postfish; see the file COPYING. If not, write to the
19 * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "postfish.h"
25 #include "feedback.h"
26 #include "input.h"
28 static off_t Acursor=0;
29 static off_t Bcursor=-1;
30 static off_t cursor=0;
32 sig_atomic_t loop_active;
33 int input_seekable;
35 int input_rate;
36 int input_ch;
37 int input_size;
39 pthread_mutex_t input_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
41 typedef struct {
42 FILE *f;
44 off_t begin;
45 off_t end;
46 off_t data;
48 int bytes;
49 int signp;
50 int endian;
51 char *name;
53 } file_entry;
55 typedef struct{
56 int ch;
58 int files;
59 file_entry *file_list;
61 int current_file_entry_number;
62 file_entry *current_file_entry;
64 } group_entry;
66 static int groups=0;
67 static group_entry *group_list=0;
69 typedef struct input_feedback{
70 feedback_generic parent_class;
71 off_t cursor;
72 float *rms;
73 float *peak;
74 } input_feedback;
76 static feedback_generic_pool feedpool;
78 static time_linkage out;
80 void input_Acursor_set(off_t c){
81 pthread_mutex_lock(&input_mutex);
82 Acursor=c;
83 pthread_mutex_unlock(&input_mutex);
86 void input_Bcursor_set(off_t c){
87 pthread_mutex_lock(&input_mutex);
88 Bcursor=c;
89 pthread_mutex_unlock(&input_mutex);
92 off_t input_time_to_cursor(const char *t){
93 char temp[14];
94 char *c;
96 int hd;
97 int s;
98 int m;
99 int h;
101 strncpy(temp,t,14);
103 /* hundredths */
104 c=strchr(temp,'.');
105 if(c){
106 *c=0;
107 hd=atoi(c+1);
108 if(hd>99)hd=99;
109 if(hd<0)hd=0;
110 }else
111 hd=0;
113 /* seconds */
114 c=strrchr(temp,':');
115 if(c){
116 *c=0;
117 s=atoi(c+1);
118 if(s>59)s=59;
119 if(s<0)s=0;
120 }else{
121 s=atoi(temp);
122 *temp=0;
125 /* minutes */
126 c=strrchr(temp,':');
127 if(c){
128 *c=0;
129 m=atoi(c+1);
130 if(m>59)m=59;
131 if(m<0)m=0;
132 }else{
133 m=atoi(temp);
134 *temp=0;
137 /* hours */
138 h=atoi(temp);
139 if(h>9999)h=9999;
140 if(h<0)h=0;
142 return ((off_t)hd + (off_t)s*100 + (off_t)m*60*100 + (off_t)h*60*60*100) *
143 input_rate / 100;
146 void time_fix(char *buffer){
147 if(buffer[0]=='0')buffer[0]=' ';
148 if(!strncmp(buffer," 0",2))buffer[1]=' ';
149 if(!strncmp(buffer," 0",3))buffer[2]=' ';
150 if(!strncmp(buffer," 0",4))buffer[3]=' ';
151 if(!strncmp(buffer," :0",6))buffer[5]=' ';
152 if(!strncmp(buffer," : 0",7))buffer[6]=' ';
154 if(buffer[0]!=' ' && buffer[1]==' ')buffer[1]='0';
155 if(buffer[1]!=' ' && buffer[2]==' ')buffer[2]='0';
156 if(buffer[2]!=' ' && buffer[3]==' ')buffer[3]='0';
157 if(buffer[3]!=' ' && buffer[5]==' ')buffer[5]='0';
158 if(buffer[5]!=' ' && buffer[6]==' ')buffer[6]='0';
161 void input_cursor_to_time(off_t cursor,char *t){
162 int h,m,s,hd;
164 h=cursor/60/60/input_rate;
165 cursor%=(off_t)60*60*input_rate;
166 m=cursor/60/input_rate;
167 cursor%=(off_t)60*input_rate;
168 s=cursor/input_rate;
169 hd=cursor%input_rate*100/input_rate;
170 if(h>9999)h=9999;
172 sprintf(t,"%04d:%02d:%02d.%02d",h,m,s,hd);
173 time_fix(t);
176 void input_parse(char *filename,int newgroup){
177 if(newgroup){
178 /* add a group */
180 if(!groups){
181 group_list=calloc(1,sizeof(*group_list));
182 }else{
183 group_list=realloc(group_list,sizeof(*group_list)*(groups+1));
184 memset(group_list+groups,0,sizeof(*group_list));
186 groups++;
190 group_entry *g=group_list+groups-1;
191 file_entry *fe;
193 if(g->files==0){
194 g->file_list=calloc(1,sizeof(*g->file_list));
195 }else{
196 g->file_list=realloc(g->file_list,
197 sizeof(*g->file_list)*(g->files+1));
198 memset(g->file_list+g->files,0,sizeof(*g->file_list));
200 fe=g->file_list+g->files;
201 g->files++;
203 fe->name=strdup(filename);
207 /* Macros to read header data */
208 #define READ_U32_LE(buf) \
209 (((buf)[3]<<24)|((buf)[2]<<16)|((buf)[1]<<8)|((buf)[0]&0xff))
211 #define READ_U16_LE(buf) \
212 (((buf)[1]<<8)|((buf)[0]&0xff))
214 #define READ_U32_BE(buf) \
215 (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|((buf)[3]&0xff))
217 #define READ_U16_BE(buf) \
218 (((buf)[0]<<8)|((buf)[1]&0xff))
220 double read_IEEE80(unsigned char *buf){
221 int s=buf[0]&0xff;
222 int e=((buf[0]&0x7f)<<8)|(buf[1]&0xff);
223 double f=((unsigned long)(buf[2]&0xff)<<24)|
224 ((buf[3]&0xff)<<16)|
225 ((buf[4]&0xff)<<8) |
226 (buf[5]&0xff);
228 if(e==32767){
229 if(buf[2]&0x80)
230 return HUGE_VAL; /* Really NaN, but this won't happen in reality */
231 else{
232 if(s)
233 return -HUGE_VAL;
234 else
235 return HUGE_VAL;
239 f=ldexp(f,32);
240 f+= ((buf[6]&0xff)<<24)|
241 ((buf[7]&0xff)<<16)|
242 ((buf[8]&0xff)<<8) |
243 (buf[9]&0xff);
245 return ldexp(f, e-16446);
248 static int find_chunk(FILE *in, char *type, unsigned int *len, int endian){
249 unsigned int i;
250 unsigned char buf[8];
252 while(1){
253 if(fread(buf,1,8,in) <8)return 0;
255 if(endian)
256 *len = READ_U32_BE(buf+4);
257 else
258 *len = READ_U32_LE(buf+4);
260 if(memcmp(buf,type,4)){
262 if((*len) & 0x1)(*len)++;
264 for(i=0;i<*len;i++)
265 if(fgetc(in)==EOF)return 0;
267 }else return 1;
271 int input_load(void){
273 int stdinp=0,i,k;
275 input_ch=0;
277 if(groups==0){
278 /* look at stdin... is it a file, pipe, tty...? */
279 if(isatty(STDIN_FILENO)){
280 fprintf(stderr,
281 "Postfish requires input either as a list of contiguous WAV\n"
282 "files on the command line, or WAV data piped|redirected to\n"
283 "stdin. postfish -h will give more details.\n");
284 return 1;
286 stdinp=1; /* file coming in via stdin */
288 group_list=calloc(1,sizeof(*group_list));
289 group_list[0].file_list=calloc(1,sizeof(*group_list[0].file_list));
291 groups=1;
292 group_list[0].files=1;
293 group_list[0].file_list[0].name="stdin";
296 for(k=0;k<groups;k++){
297 group_entry *g=group_list+k;
298 off_t total=0;
300 for(i=0;i<g->files;i++){
301 file_entry *fe=g->file_list+i;
302 FILE *f;
303 char *fname="stdin";
305 if(stdinp){
306 int newfd=dup(STDIN_FILENO);
307 f=fdopen(newfd,"rb");
308 }else{
309 fname=g->file_list[i].name;
310 f=fopen(fname,"rb");
313 /* Crappy! Use a lib to do this for pete's sake! */
314 if(f){
315 char headerid[12];
316 off_t filelength;
317 fe->f=f;
319 /* parse header (well, sort of) and get file size */
320 input_seekable=(fseek(f,0,SEEK_CUR)?0:1);
321 if(!input_seekable){
322 filelength=-1;
323 }else{
324 fseek(f,0,SEEK_END);
325 filelength=ftello(f);
326 fseek(f,0,SEEK_SET);
329 fread(headerid,1,12,f);
330 if(!strncmp(headerid,"RIFF",4) && !strncmp(headerid+8,"WAVE",4)){
331 unsigned int chunklen;
333 if(find_chunk(f,"fmt ",&chunklen,0)){
334 int ltype;
335 int lch;
336 int lrate;
337 int lbits;
338 unsigned char *buf=alloca(chunklen);
340 fread(buf,1,chunklen,f);
342 ltype = READ_U16_LE(buf);
343 lch = READ_U16_LE(buf+2);
344 lrate = READ_U32_LE(buf+4);
345 lbits = READ_U16_LE(buf+14);
347 if(ltype!=1){
348 fprintf(stderr,"%s:\n\tWAVE file not PCM.\n",fname);
349 return 1;
352 fe->bytes=(lbits+7)/8;
353 fe->signp=0;
354 fe->endian=0;
355 if(fe->bytes>1)fe->signp=1;
357 if(lrate<4000 || lrate>192000){
358 fprintf(stderr,"%s:\n\tSampling rate out of bounds\n",fname);
359 return 1;
362 if(k==0 && i==0){
363 input_rate=lrate;
364 }else if(input_rate!=lrate){
365 fprintf(stderr,"%s:\n\tInput files must all be same sampling rate.\n",fname);
366 return 1;
369 if(i==0){
370 g->ch=lch;
371 input_ch+=lch;
372 }else{
373 if(g->ch!=lch){
374 fprintf(stderr,"%s:\n\tInput files must all have same number of channels.\n",fname);
375 return 1;
379 if(find_chunk(f,"data",&chunklen,0)){
380 off_t pos=ftello(f);
382 if(input_seekable)
383 filelength=
384 (filelength-pos)/
385 (g->ch*fe->bytes)*
386 (g->ch*fe->bytes)+pos;
388 if(chunklen==0UL ||
389 chunklen==0x7fffffffUL ||
390 chunklen==0xffffffffUL){
391 if(filelength==-1){
392 fe->begin=total;
393 total=fe->end=-1;
394 fprintf(stderr,"%s: Incomplete header; assuming stream.\n",fname);
395 }else{
396 fe->begin=total;
397 total=fe->end=total+(filelength-pos)/(g->ch*fe->bytes);
398 fprintf(stderr,"%s: Incomplete header; using actual file size.\n",fname);
400 }else if(filelength==-1 || chunklen+pos<=filelength){
401 fe->begin=total;
402 total=fe->end=total+ (chunklen/(g->ch*fe->bytes));
403 fprintf(stderr,"%s: Using declared file size.\n",fname);
405 }else{
406 fe->begin=total;
407 total=fe->end=total+(filelength-pos)/(g->ch*fe->bytes);
408 fprintf(stderr,"%s: File truncated; Using actual file size.\n",fname);
410 fe->data=ftello(f);
411 } else {
412 fprintf(stderr,"%s: WAVE file has no \"data\" chunk following \"fmt \".\n",fname);
413 return 1;
415 }else{
416 fprintf(stderr,"%s: WAVE file has no \"fmt \" chunk.\n",fname);
417 return 1;
420 }else if(!strncmp(headerid,"FORM",4) && !strncmp(headerid+8,"AIF",3)){
421 unsigned int len;
422 int aifc=0;
423 if(headerid[11]=='C')aifc=1;
424 unsigned char *buffer;
425 char buf2[8];
427 int lch;
428 int lbits;
429 int lrate;
431 /* look for COMM */
432 if(!find_chunk(f, "COMM", &len,1)){
433 fprintf(stderr,"%s: AIFF file has no \"COMM\" chunk.\n",fname);
434 return 1;
437 if(len < 18 || (aifc && len<22)) {
438 fprintf(stderr,"%s: AIFF COMM chunk is truncated.\n",fname);
439 return 1;
442 buffer = alloca(len);
444 if(fread(buffer,1,len,f) < len){
445 fprintf(stderr, "%s: Unexpected EOF in reading AIFF header\n",fname);
446 return 1;
449 lch = READ_U16_BE(buffer);
450 lbits = READ_U16_BE(buffer+6);
451 lrate = (int)read_IEEE80(buffer+8);
453 fe->endian = 1; // default
455 fe->bytes=(lbits+7)/8;
456 fe->signp=1;
458 if(lrate<4000 || lrate>192000){
459 fprintf(stderr,"%s:\n\tSampling rate out of bounds\n",fname);
460 return 1;
463 if(k==0 && i==0){
464 input_rate=lrate;
465 }else if(input_rate!=lrate){
466 fprintf(stderr,"%s:\n\tInput files must all be same sampling rate.\n",fname);
467 return 1;
470 if(i==0){
471 g->ch=lch;
472 input_ch+=lch;
473 }else{
474 if(g->ch!=lch){
475 fprintf(stderr,"%s:\n\tInput files must all have same number of channels.\n",fname);
476 return 1;
480 if(aifc){
481 if(!memcmp(buffer+18, "NONE", 4)) {
482 fe->endian = 1;
483 }else if(!memcmp(buffer+18, "sowt", 4)) {
484 fe->endian = 0;
485 }else{
486 fprintf(stderr, "%s: Postfish supports only linear PCM AIFF-C files.\n",fname);
487 return 1;
491 if(!find_chunk(f, "SSND", &len, 1)){
492 fprintf(stderr,"%s: AIFF file has no \"SSND\" chunk.\n",fname);
493 return 1;
496 if(fread(buf2,1,8,f) < 8){
497 fprintf(stderr,"%s: Unexpected EOF reading AIFF header\n",fname);
498 return 1;
502 int loffset = READ_U32_BE(buf2);
503 int lblocksize = READ_U32_BE(buf2+4);
505 /* swallow some data */
506 for(i=0;i<loffset;i++)
507 if(fgetc(f)==EOF)break;
509 if( lblocksize == 0 && (lbits == 32 || lbits == 24 || lbits == 16 || lbits == 8)){
511 off_t pos=ftello(f);
513 if(input_seekable)
514 filelength=
515 (filelength-pos)/
516 (g->ch*fe->bytes)*
517 (g->ch*fe->bytes)+pos;
519 if(len==0UL ||
520 len==0x7fffffffUL ||
521 len==0xffffffffUL){
522 if(filelength==-1){
523 fe->begin=total;
524 total=fe->end=-1;
525 fprintf(stderr,"%s: Incomplete header; assuming stream.\n",fname);
526 }else{
527 fe->begin=total;
528 total=fe->end=total+(filelength-pos)/(g->ch*fe->bytes);
529 fprintf(stderr,"%s: Incomplete header; using actual file size.\n",fname);
531 }else if(filelength==-1 || (len+pos-loffset-8)<=filelength){
532 fe->begin=total;
533 total=fe->end=total+ ((len-loffset-8)/(g->ch*fe->bytes));
534 fprintf(stderr,"%s: Using declared file size.\n",fname);
536 }else{
537 fe->begin=total;
538 total=fe->end=total+(filelength-pos)/(g->ch*fe->bytes);
539 fprintf(stderr,"%s: File truncated; Using actual file size.\n",fname);
541 fe->data=pos;
542 }else{
543 fprintf(stderr, "%s: Postfish supports only linear PCM AIFF-C files.\n",fname);
544 return 1;
548 } else {
550 fprintf(stderr,"%s: Postfish supports only linear PCM WAV and AIFF[-C] files.\n",fname);
551 return 1;
554 }else{
555 fprintf(stderr,"%s: Unable to open file.\n",fname);
556 return 1;
562 /* 192000: 8192
564 96000: 4096
565 88200: 4096
566 64000: 4096
568 48000: 2048
569 44100: 2048
570 32000: 1024
572 22050: 1024
573 16000: 1024
575 11025: 512
576 8000: 512
578 4000: 256 */
580 if(input_rate<6000){
581 input_size=256;
582 }else if(input_rate<15000){
583 input_size=512;
584 }else if(input_rate<25000){
585 input_size=1024;
586 }else if(input_rate<50000){
587 input_size=2048;
588 }else if(input_rate<100000){
589 input_size=4096;
590 }else
591 input_size=8192;
593 out.channels=input_ch;
595 out.data=malloc(sizeof(*out.data)*input_ch);
596 for(i=0;i<input_ch;i++)
597 out.data[i]=malloc(sizeof(**out.data)*input_size);
599 return 0;
602 static off_t input_seek_i(off_t pos,int ps){
603 int i,k;
604 int flag=0;
605 off_t maxpos=0;
607 if(pos<0)pos=0;
608 if(!input_seekable){
609 for(i=0;i<groups;i++){
610 group_list[i].current_file_entry=group_list[i].file_list;
611 group_list[i].current_file_entry_number=0;
613 return -1;
616 pthread_mutex_lock(&input_mutex);
617 if(ps)playback_seeking=1;
619 /* seek has to happen correctly in all groups */
620 for(k=0;k<groups;k++){
621 group_entry *g=group_list+k;
623 for(i=0;i<g->files;i++){
624 file_entry *fe=g->current_file_entry=g->file_list+i;
625 g->current_file_entry_number=i;
627 if(fe->begin<=pos && fe->end>pos){
628 flag=1;
629 fseeko(fe->f,
630 (pos-fe->begin)*(g->ch*fe->bytes)+fe->data,
631 SEEK_SET);
632 break;
636 if(i==g->files){
637 /* this group isn't that long; seek to the end of it */
638 file_entry *fe=g->current_file_entry;
640 fseeko(fe->f,(fe->end-fe->begin)*(g->ch*fe->bytes)+fe->data,SEEK_SET);
642 if(fe->end>maxpos)maxpos=fe->end;
646 if(flag){
647 cursor=pos;
648 pthread_mutex_unlock(&input_mutex);
649 }else{
650 cursor=maxpos;
651 pthread_mutex_unlock(&input_mutex);
654 return cursor;
657 off_t input_seek(off_t pos){
658 return input_seek_i(pos,1);
661 off_t input_time_seek_rel(float s){
662 return input_seek(cursor+input_rate*s);
665 static feedback_generic *new_input_feedback(void){
666 input_feedback *ret=malloc(sizeof(*ret));
667 ret->rms=malloc(input_ch*sizeof(*ret->rms));
668 ret->peak=malloc(input_ch*sizeof(*ret->peak));
669 return (feedback_generic *)ret;
672 static void push_input_feedback(float *peak,float *rms, off_t cursor){
673 input_feedback *f=(input_feedback *)
674 feedback_new(&feedpool,new_input_feedback);
675 f->cursor=cursor;
676 memcpy(f->rms,rms,input_ch*sizeof(*rms));
677 memcpy(f->peak,peak,input_ch*sizeof(*peak));
678 feedback_push(&feedpool,(feedback_generic *)f);
681 int pull_input_feedback(float *peak,float *rms,off_t *cursor){
682 input_feedback *f=(input_feedback *)feedback_pull(&feedpool);
683 if(!f)return 0;
684 if(rms)memcpy(rms,f->rms,sizeof(*rms)*input_ch);
685 if(peak)memcpy(peak,f->peak,sizeof(*peak)*input_ch);
686 if(cursor)*cursor=f->cursor;
687 feedback_old(&feedpool,(feedback_generic *)f);
688 return 1;
691 static void LEconvert(float **data,
692 unsigned char *readbuf, int dataoff,
693 int ch,int bytes, int signp, int n){
694 int i,j,k=0;
695 int32_t xor=(signp?0:0x80000000UL);
696 float scale=1./2147483648.;
698 k=0;
699 switch(bytes){
700 case 1:
702 for(i=dataoff;i<dataoff+n;i++)
703 for(j=0;j<ch;j++){
704 data[j][i]=((readbuf[k]<<24)^xor)*scale;
705 k++;
707 break;
709 case 2:
711 for(i=dataoff;i<dataoff+n;i++)
712 for(j=0;j<ch;j++){
713 data[j][i]=(((readbuf[k]<<16)|(readbuf[k+1]<<24))^xor)*scale;
714 k+=2;
716 break;
718 case 3:
720 for(i=dataoff;i<dataoff+n;i++)
721 for(j=0;j<ch;j++){
722 data[j][i]=(((readbuf[k]<<8)|(readbuf[k+1]<<16)|(readbuf[k+2]<<24))^xor)*scale;
723 k+=3;
725 break;
727 case 4:
729 for(i=dataoff;i<dataoff+n;i++)
730 for(j=0;j<ch;j++){
731 data[j][i]=(((readbuf[k])|(readbuf[k+1]<<8)|(readbuf[k+2]<<16)|(readbuf[k+3]<<24))^xor)*scale;
732 k+=4;
734 break;
738 static void BEconvert(float **data,
739 unsigned char *readbuf, int dataoff,
740 int ch,int bytes, int signp, int n){
741 int i,j,k=0;
742 int32_t xor=(signp?0:0x80000000UL);
743 float scale=1./2147483648.;
745 k=0;
746 switch(bytes){
747 case 1:
749 for(i=dataoff;i<dataoff+n;i++)
750 for(j=0;j<ch;j++){
751 data[j][i]=((readbuf[k]<<24)^xor)*scale;
752 k++;
754 break;
756 case 2:
758 for(i=dataoff;i<dataoff+n;i++)
759 for(j=0;j<ch;j++){
760 data[j][i]=(((readbuf[k+1]<<16)|(readbuf[k]<<24))^xor)*scale;
761 k+=2;
763 break;
765 case 3:
767 for(i=dataoff;i<dataoff+n;i++)
768 for(j=0;j<ch;j++){
769 data[j][i]=(((readbuf[k+2]<<8)|(readbuf[k+1]<<16)|(readbuf[k]<<24))^xor)*scale;
770 k+=3;
772 break;
774 case 4:
776 for(i=dataoff;i<dataoff+n;i++)
777 for(j=0;j<ch;j++){
778 data[j][i]=(((readbuf[k+3])|(readbuf[k+2]<<8)|(readbuf[k+1]<<16)|(readbuf[k]<<24))^xor)*scale;
779 k+=4;
781 break;
785 static void zero(float **data, int dataoff, int ch, int n){
786 int i,j;
788 for(i=dataoff;i<dataoff+n;i++)
789 for(j=0;j<ch;j++)
790 data[j][i]=0;
793 /* no locking within as the only use of input_read is locked in the
794 playback thread (must be locked there because the real lock needs
795 to avoid a seeking race) */
797 time_linkage *input_read(void){
798 int h,i,j;
799 int groupread_s=0;
801 float *rms=alloca(sizeof(*rms)*(out.channels));
802 float *peak=alloca(sizeof(*peak)*(out.channels));
804 memset(rms,0,sizeof(*rms)*(out.channels));
805 memset(peak,0,sizeof(*peak)*(out.channels));
807 out.samples=0;
809 /* the non-streaming case */
810 if(!loop_active && input_seekable){
811 for(i=0;i<groups;i++)
812 if(cursor<group_list[i].file_list[group_list[i].files-1].end)
813 break;
814 if(i==groups)goto tidy_up;
817 /* the streaming case */
818 if(!input_seekable && feof(group_list[0].current_file_entry->f)){
819 goto tidy_up;
822 /* If we're A-B looping, we might need several loops/seeks */
823 while(groupread_s<input_size){
824 int chcount=0;
825 int max_read_s=0;
827 if(loop_active && cursor>=Bcursor){
828 input_seek_i(Acursor,0);
831 /* each read section is by group */
832 for(h=0;h<groups;h++){
833 group_entry *g=group_list+h;
834 int toread_s=input_size-groupread_s;
835 int fileread_s=0;
837 if(input_seekable && loop_active && toread_s>Bcursor-cursor)
838 toread_s = Bcursor-cursor;
840 /* inner loop in case the read spans multiple files within the group */
841 while(toread_s){
842 file_entry *fe=g->current_file_entry;
843 off_t ret;
845 /* span forward to next file entry in the group? */
846 if(cursor+fileread_s>=fe->end &&
847 g->current_file_entry_number+1<g->files){
848 fe=++g->current_file_entry;
849 g->current_file_entry_number++;
850 fseeko(fe->f,fe->data,SEEK_SET);
853 /* perform read/conversion of this file entry */
855 off_t read_this_loop=(fe->end<0?input_size:fe->end-cursor-fileread_s);
856 unsigned char readbuf[input_size*(g->ch*fe->bytes)];
857 if(read_this_loop>toread_s)read_this_loop=toread_s;
859 ret=fread(readbuf,1,read_this_loop*(g->ch*fe->bytes),fe->f);
861 if(ret>0){
862 ret/=(g->ch*fe->bytes);
864 if(fe->endian)
865 BEconvert(out.data+chcount,readbuf,
866 fileread_s+groupread_s,g->ch,fe->bytes,fe->signp,ret);
867 else
868 LEconvert(out.data+chcount,readbuf,
869 fileread_s+groupread_s,g->ch,fe->bytes,fe->signp,ret);
871 fileread_s+=ret;
872 toread_s-=ret;
874 }else{
875 if(g->current_file_entry_number+1>=g->files){
877 /* end of group before full frame */
878 zero(out.data+chcount,fileread_s+groupread_s,g->ch,toread_s);
879 toread_s=0;
885 if(max_read_s<fileread_s)max_read_s=fileread_s;
886 chcount+=g->ch;
889 groupread_s+=max_read_s;
890 cursor+=max_read_s;
892 if(!loop_active || cursor<Bcursor) break;
896 out.samples=groupread_s;
898 for(i=0;i<groupread_s;i++)
899 for(j=0;j<out.channels;j++){
900 float dval=out.data[j][i];
901 dval*=dval;
902 if(dval>peak[j])peak[j]=dval;
903 rms[j]+= dval;
906 for(j=0;j<out.channels;j++)
907 rms[j]/=out.samples;
909 push_input_feedback(peak,rms,cursor);
911 tidy_up:
914 int tozero=input_size-out.samples;
915 if(tozero)
916 for(j=0;j<out.channels;j++)
917 memset(out.data[j]+out.samples,0,sizeof(**out.data)*tozero);
920 return &out;
923 void input_reset(void){
924 while(pull_input_feedback(NULL,NULL,NULL));
925 return;