Continued macro block modes section.
[xiph/unicode.git] / MTG / soundboard.c
blob75d18e1042baf75121f146f064372424e7779796
1 /* Assumptions (Bad things the code does that work here, but might not
2 work elsewhere):
4 the atomic execution sections are a hack that can only work on uniprocessor
6 */
8 /* TODO:
10 make Esc behavior correct in editing menus by using temp cue/tag storage
11 tag list menu
12 sane arrow keying
13 autocompletion
14 logarhythmic fades
15 proper printing out of file loading issues
16 abstracted output
17 allow sample name/tag change in edit menus */
18 #define CACHE_SAMPLES 44100*10
19 #define _REENTRANT 1
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <stdio.h>
23 #include <ctype.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <limits.h>
27 #include <sys/mman.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/time.h>
31 #include <sys/file.h>
32 #define __USE_GNU 1
33 #include <pthread.h>
34 #include <string.h>
35 #include <math.h>
36 #include <signal.h>
37 #include <curses.h>
38 #include <fcntl.h>
40 #include <sys/soundcard.h>
41 #include <sys/ioctl.h>
43 /* we need some cooperative multitasking to eliminate locking (and
44 thus latency) in the realtime playback thread.
45 pthread_setschedparam can give us this by way of implementing
46 atomic execution blocks. Convention here is that an 'atomic
47 execution block' is actually a section wrapped in a realtime
48 schedule change with a priority of 90; to be effective, this must
49 be the highest realtime priority used in the application */
51 static int original_priority;
52 static int original_policy;
53 #define BEGIN_ATOMIC \
54 { \
55 struct sched_param param; \
56 pthread_getschedparam(pthread_self(), &original_policy, &param); \
57 if(param.sched_priority==90){ \
58 fprintf(stderr,"ATOMIC sections do not stack at line %ld\n",__LINE__); \
59 exit(1); \
60 } \
61 original_priority=param.sched_priority; \
62 param.sched_priority=90; \
63 pthread_setschedparam(pthread_self(), SCHED_FIFO, &param); \
66 #define END_ATOMIC \
67 { \
68 struct sched_param param; \
69 param.sched_priority=original_priority; \
70 pthread_setschedparam(pthread_self(), original_policy, &param); \
75 #define int16 short
76 static char *tempdir="/tmp/beaverphonic/";
77 static char *lockfile="/tmp/beaverphonic/lock";
78 //static char *installdir="/usr/local/beaverphonic/";
79 static char *installdir="/home/xiphmont/MotherfishCVS/MTG/";
80 #define VERSION "$Id: soundboard.c,v 1.14 2003/10/02 17:14:19 xiphmont Exp $"
82 enum menutype {MENU_MAIN,MENU_KEYPRESS,MENU_ADD,MENU_EDIT,MENU_OUTPUT,MENU_QUIT};
84 pthread_mutex_t cache_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
85 pthread_mutex_t rec_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
86 pthread_mutex_t mmap_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
87 pthread_cond_t cache_cond=PTHREAD_COND_INITIALIZER;
89 static long main_master_volume=50;
90 char *program;
91 static int playback_buffer_minfill=0;
92 static int running=1;
93 static enum menutype menu=MENU_MAIN;
94 static int cue_list_position=0;
95 static int cue_list_number=0;
96 static int firstsave=0;
97 static int unsaved=0;
99 static FILE *playfd=NULL;
100 static FILE *recfd=NULL;
101 int ttyfd;
102 int ttypipe[2];
104 static inline void _playback_remove(int i);
105 static void _cache_remove(int i);
107 pthread_t main_thread_id;
108 pthread_t playback_thread_id;
109 pthread_t record_thread_id;
110 pthread_t record_disk_thread_id;
111 pthread_t tty_thread_id;
112 pthread_t cache_thread_id;
114 void main_update_tags(int y);
116 void *m_realloc(void *in,int bytes){
117 if(!in)
118 return(malloc(bytes));
119 return(realloc(in,bytes));
122 void addnlstr(const char *s,int n,char c){
123 int len=strlen(s),i;
124 addnstr(s,n);
125 n-=len;
126 for(i=0;i<n;i++)addch(c);
129 void switch_to_stderr(){
130 def_prog_mode(); /* save current tty modes */
131 endwin(); /* restore original tty modes */
134 void switch_to_ncurses(){
135 refresh(); /* restore save modes, repaint screen */
138 int mgetch(){
139 while(1){
140 int ret=getch();
141 if(ret>0)return(ret);
145 /******** channel mappings. All hardwired for now... ***********/
146 // only OSS stereo builin for now
147 #define MAX_OUTPUT_CHANNELS 6
148 #define MAX_FILECHANNELS 2
149 #define CHANNEL_LABEL_LENGTH 50
150 int playback_bufsize=0;
152 typedef struct {
153 char label[CHANNEL_LABEL_LENGTH];
154 int peak;
155 /* real stuff not here yet */
156 } outchannel;
158 static outchannel channel_list[MAX_OUTPUT_CHANNELS]={
159 {"house left",0},
160 {"house right",0},
161 {"stage left",0},
162 {"stage right",0},
163 {"rear left",0},
164 {"rear right",0},
166 static outchannel rchannel_list[2]={
167 {"left",0},
168 {"right",0},
170 static int channel_count=MAX_OUTPUT_CHANNELS;
172 /******** label abstraction code; use this for all alloced strings
173 that need to be saved to config file (shared or not) */
175 typedef struct {
176 char *text;
177 int refcount;
178 } label;
180 typedef int label_number;
182 static label *label_list;
183 static int label_count;
185 int new_label_number(){
186 int i;
187 for(i=0;i<label_count;i++)
188 if(!label_list[i].refcount)break;
189 return(i);
192 static void _alloc_label_if_needed(int number){
193 if(number>=label_count){
194 int prev=label_count;
195 label_count=number+1;
196 label_list=m_realloc(label_list,sizeof(*label_list)*label_count);
197 memset(label_list+prev,0,sizeof(*label_list)*(label_count-prev));
201 void edit_label(int number,char *t){
202 label *label;
203 _alloc_label_if_needed(number);
205 label=label_list+number;
206 label->text=m_realloc(label->text,strlen(t)+1);
208 strcpy(label->text,t);
211 void acquire_label(int number){
212 _alloc_label_if_needed(number);
213 label_list[number].refcount++;
216 const char *label_text(int number){
217 if(number<0 ||
218 number>=label_count ||
219 label_list[number].refcount==0 ||
220 label_list[number].text==NULL)
221 return"";
222 return label_list[number].text;
225 void release_label(int number){
226 label *label;
227 if(number>=label_count)return;
228 label=label_list+number;
229 label->refcount--;
230 if(label->refcount<0)
231 label->refcount=0;
234 int save_label(FILE *f,int number){
235 int count;
236 if(number<=label_count){
237 if(label_list[number].refcount){
238 fprintf(f,"LBL:%d %d:%s",number,
239 strlen(label_text(number)),
240 label_text(number));
242 count=fprintf(f,"\n");
243 if(count<1)return(-1);
246 return(0);
249 int save_labels(FILE *f){
250 int i;
251 for(i=label_count-1;i>=0;i--)
252 if(save_label(f,i))return(-1);
253 return(0);
256 int load_label(FILE *f){
257 int len,count,number;
258 char *temp;
259 count=fscanf(f,":%d %d:",&number,&len);
260 if(count<2){
261 addnlstr("LOAD ERROR (LBL): too few fields.",80,' ');
262 return(-1);
264 temp=alloca(len+1);
265 count=fread(temp,1,len,f);
266 temp[len]='\0';
267 if(count<len){
268 addnlstr("LOAD ERROR (LBL): EOF reading string.",80,' ');
269 return(-1);
271 edit_label(number,temp);
272 return(0);
275 /************* tag abstraction **********************/
277 typedef int tag_number;
279 typedef struct {
280 int refcount;
282 label_number sample_path;
283 label_number sample_desc;
284 long loop_p;
285 long loop_start;
286 long loop_lapping;
287 long fade_out;
289 void *basemap;
290 int16 *data;
291 int channels;
292 long samplelength;
294 /* state */
295 int activep;
296 int cachep;
298 long sample_position;
299 long sample_lapping;
300 long sample_loop_start;
301 long sample_fade_start;
303 long cache_fill;
305 double master_vol_current;
306 double master_vol_target;
307 double master_vol_slew;
309 double outvol_current[MAX_FILECHANNELS][MAX_OUTPUT_CHANNELS];
310 double outvol_target[MAX_FILECHANNELS][MAX_OUTPUT_CHANNELS];
311 double outvol_slew[MAX_FILECHANNELS][MAX_OUTPUT_CHANNELS];
313 } tag;
315 struct sample_header{
316 long sync;
317 long channels;
320 static tag *tag_list;
321 static int tag_count;
323 static tag **active_list;
324 static int active_count;
326 static tag **cache_list;
327 static int cache_count;
329 int new_tag_number(){
330 int i;
331 for(i=0;i<tag_count;i++)
332 if(!tag_list[i].refcount)break;
333 return(i);
336 static void _alloc_tag_if_needed(int number){
337 if(number>=tag_count){
338 int prev=tag_count;
339 BEGIN_ATOMIC
340 tag_count=number+1;
341 tag_list=m_realloc(tag_list,sizeof(*tag_list)*tag_count);
342 active_list=m_realloc(active_list,sizeof(*active_list)*tag_count);
343 cache_list=m_realloc(cache_list,sizeof(*cache_list)*tag_count);
345 memset(tag_list+prev,0,sizeof(*tag_list)*(tag_count-prev));
346 END_ATOMIC
350 /* UI convention: this has too many useful things to report if
351 sample opening goes awry, and I don't want to define a huge
352 message reporting system through ncurses in C, so we go back to
353 tty-like for reporting here, waiting for a keypress if nonzero
354 return */
356 int load_sample(tag *t,const char *path){
357 char *template=alloca(strlen(tempdir)+20);
358 char *tmp,*buffer;
359 int ret=0;
361 fprintf(stderr,"Loading %s...\n",path);
362 /* parse the file; use preexisting tools via external Perl glue */
363 /* select a temp file for the glue to convert file into */
364 /* valid use of mktemp; only one Beaverphonic is running a time,
365 and we need a *name*, not a *file*. */
367 sprintf(template,"%sconversion.XXXXXX",tempdir);
368 tmp=mktemp(template);
369 if(!tmp)return(-1);
371 buffer=alloca(strlen(installdir)+strlen(tmp)+strlen(path)+20);
372 fprintf(stderr,"\tcopying/converting...\n");
373 sprintf(buffer,"%sconvert.pl \'%s\' \'%s\'",installdir,path,tmp);
375 if(fork()){
376 wait(&ret);
377 }else{
378 setuid(getuid());
379 _exit(system(buffer));
381 if(ret)goto out;
383 /* our temp file is a host-ordered 44.1kHz 16 bit PCM file, n
384 channels uninterleaved. first word is channels */
385 fprintf(stderr,"\treading...\n");
387 FILE *cheat=fopen(tmp,"rb");
388 struct sample_header head;
389 int count;
390 int fd;
391 long length;
392 if(!cheat){
393 fprintf(stderr,"Failed to open converted file %s:\n\t(%s)\n",
394 tmp,strerror(errno));
395 ret=-1;goto out;
398 /* find length,etc */
399 if(fseek(cheat,-sizeof(head),SEEK_END)){
400 fprintf(stderr,"Unable to seek to end of file!\n\t%s\n",
401 strerror(ferror(cheat)));
402 fclose(cheat);
403 ret=-1;goto out;
405 length=ftell(cheat);
406 if(length<0){
407 fprintf(stderr,"Unable to determine length of file!\n\t%s\n",
408 strerror(ferror(cheat)));
409 fclose(cheat);
410 ret=-1;goto out;
412 count=fread(&head,sizeof(head),1,cheat);
414 fclose(cheat);
415 if(count<1){
416 fprintf(stderr,"Conversion file %s openable, but truncated\n",tmp);
417 ret=-1;goto out;
419 if(head.sync!=55){
420 fprintf(stderr,"Conversion file created by incorrect "
421 "version of convert.pl\n\t%s unreadable\n",tmp);
422 ret=-1;goto out;
425 t->channels=head.channels;
426 t->samplelength=(length-sizeof(head))/(t->channels*2);
428 /* mmap the sample file */
429 fprintf(stderr,"\tmmaping...\n");
430 fd=open(tmp,O_RDONLY);
431 if(fd<0){
432 fprintf(stderr,"Unable to open %s fd for mmap:\n\t%d (%s)\n",
433 tmp,errno,strerror(errno));
434 ret=-1;goto out;
436 t->basemap=
437 mmap(NULL,
438 t->samplelength*sizeof(int16)*t->channels+sizeof(head),
439 PROT_READ,MAP_PRIVATE,fd,0);
440 close(fd);
442 if(!t->basemap){
443 fprintf(stderr,"Unable to mmap fd %d (%s):\n\t%d (%s)\n",
444 fd,tmp,errno,strerror(errno));
445 ret=-1;goto out;
447 t->data=t->basemap+sizeof(head);
448 if(madvise(t->basemap,
449 t->samplelength*sizeof(int16)*t->channels+sizeof(head),
450 MADV_RANDOM)){
451 fprintf(stderr,"madvise() failed for %s mmap:\n\t%d (%s)\n",
452 tmp,errno,strerror(errno));
453 ret=-1;goto out;
456 fprintf(stderr,"\tDONE\n\n");
458 out:
459 if(tmp)unlink(tmp);
460 return(ret);
463 int unload_sample(tag *t){
464 /* is this sample currently playing back? */
465 int i;
466 if(t->activep){
467 for(i=0;i<active_count;i++)
468 if(active_list[i]==t)_playback_remove(i);
470 if(t->cachep){
471 for(i=0;i<cache_count;i++)
472 if(cache_list[i]==t)_cache_remove(i);
475 pthread_mutex_lock(&mmap_mutex);
476 if(t->basemap)
477 munmap(t->basemap,
478 t->samplelength*sizeof(int16)*
479 t->channels+sizeof(struct sample_header));
481 t->basemap=NULL;
482 t->data=NULL;
483 t->samplelength=0;
484 t->channels=0;
485 pthread_mutex_unlock(&mmap_mutex);
487 return(0);
490 int edit_tag(int number,tag t){
491 _alloc_tag_if_needed(number);
493 tag_list[number]=t;
495 /* state is zeroed when we go to production mode. Done */
496 return(0);
499 void acquire_tag(int number){
500 if(number<0)return;
501 _alloc_tag_if_needed(number);
502 tag_list[number].refcount++;
505 void release_tag(int number){
506 if(number<0)return;
507 if(number>=tag_count)return;
508 tag_list[number].refcount--;
509 if(tag_list[number].refcount==0){
510 tag *t=tag_list+number;
512 if(t->basemap)
513 unload_sample(t); /* <- locked here; this gets it off the play
514 list and eliminates any playback thread
515 interdependancies; it can't get back on
516 active play list if we're here */
517 release_label(t->sample_path);
518 release_label(t->sample_desc);
520 if(tag_list[number].refcount<0)
521 tag_list[number].refcount=0;
524 int save_tag(FILE *f,int number){
525 tag *t;
526 int count;
528 if(number>=tag_count)return(0);
529 t=tag_list+number;
530 if(t->refcount){
531 fprintf(f,"TAG:%d %d %d %ld %ld %ld %ld",
532 number,t->sample_path,t->sample_desc,
533 t->loop_p,t->loop_start,t->loop_lapping,
534 t->fade_out);
535 count=fprintf(f,"\n");
536 if(count<1)return(-1);
538 return(0);
541 int save_tags(FILE *f){
542 int i;
543 for(i=tag_count-1;i>=0;i--){
544 if(save_tag(f,i))return(-1);
546 return(0);
549 int load_tag(FILE *f){
550 tag t;
551 int count,number;
552 memset(&t,0,sizeof(t));
554 count=fscanf(f,":%d %d %d %ld %ld %ld %ld",
555 &number,&t.sample_path,&t.sample_desc,&t.loop_p,&t.loop_start,
556 &t.loop_lapping,&t.fade_out);
557 if(count<7){
558 addnlstr("LOAD ERROR (TAG): too few fields.",80,' ');
559 return(-1);
561 acquire_label(t.sample_path);
562 if(load_sample(&t,label_text(t.sample_path))){
563 release_label(t.sample_path);
564 return(-1);
566 edit_tag(number,t);
567 acquire_label(t.sample_desc);
569 return(0);
572 /********************* cue abstraction ********************/
574 typedef struct {
575 int vol_master;
576 long vol_ms;
578 int outvol[MAX_FILECHANNELS][MAX_OUTPUT_CHANNELS];
579 } mix;
581 typedef struct {
582 label_number label;
583 label_number cue_text;
584 label_number cue_desc;
586 tag_number tag;
587 int tag_create_p;
588 mix mix;
589 } cue;
591 static cue *cue_list;
592 int cue_count;
593 int cue_storage;
595 void add_cue(int n,cue c){
597 if(cue_count==cue_storage){
598 BEGIN_ATOMIC
599 cue_storage++;
600 cue_storage*=2;
601 cue_list=m_realloc(cue_list,cue_storage*sizeof(*cue_list));
602 END_ATOMIC
605 /* copy insert */
606 BEGIN_ATOMIC
607 if(cue_count-n)
608 memmove(cue_list+n+1,cue_list+n,sizeof(*cue_list)*(cue_count-n));
609 cue_count++;
611 cue_list[n]=c;
612 END_ATOMIC
615 /* inefficient. delete one, shift-copy list, delete one, shift-copy
616 list, delete one, gaaaah. Not worth optimizing */
617 static void _delete_cue_helper(int n){
618 if(n>=0 && n<cue_count){
619 BEGIN_ATOMIC
620 release_label(cue_list[n].label);
621 release_label(cue_list[n].cue_text);
622 release_label(cue_list[n].cue_desc);
623 release_tag(cue_list[n].tag);
624 cue_count--;
625 if(n<cue_count)
626 memmove(cue_list+n,cue_list+n+1,sizeof(*cue_list)*(cue_count-n));
627 END_ATOMIC
631 /* slightly more complicated that just removing the cue from memory;
632 we need to remove any tag mixer modification cues that follow if
633 this cue creates a sample tag */
635 void delete_cue_single(int n){
636 int i;
637 if(n>=0 && n<cue_count){
638 cue *c=cue_list+n;
639 tag_number tag=c->tag;
641 if(c->tag_create_p){
642 /* tag creation cue; have to delete following cues matching this
643 tag */
644 for(i=cue_count-1;i>n;i--)
645 if(cue_list[i].tag==tag)
646 _delete_cue_helper(i);
648 _delete_cue_helper(n);
652 /* this deletes all cues of a cue bank, and also chases the tags of
653 sample creation cues */
654 void delete_cue_bank(int n){
655 /* find first cue number */
656 int first=n,last=n;
658 while(first>0 && cue_list[first].label==cue_list[first-1].label)first--;
659 while(last+1<cue_count &&
660 cue_list[last].label==cue_list[last+1].label)last++;
662 for(;last>=first;last--)
663 delete_cue_single(last);
666 int save_cue(FILE *f,int n){
667 if(n<cue_count){
668 int count,i;
669 cue *c=cue_list+n;
670 fprintf(f,"CUE:%d %d %d %d %d %d %ld :%d:%d:",
671 c->label,c->cue_text,c->cue_desc,
672 c->tag,c->tag_create_p,
673 c->mix.vol_master,
674 c->mix.vol_ms,
675 MAX_OUTPUT_CHANNELS,
676 MAX_FILECHANNELS);
678 for(i=0;i<MAX_OUTPUT_CHANNELS;i++)
679 fprintf(f,"<%d %d>",c->mix.outvol[0][i],c->mix.outvol[1][i]);
680 count=fprintf(f,"\n");
681 if(count<1)return(-1);
683 return(0);
686 int save_cues(FILE *f){
687 int i;
688 for(i=0;i<cue_count;i++)
689 if(save_cue(f,i))return(-1);
690 return(0);
693 int load_cue(FILE *f){
694 cue c;
695 int count,i,j;
696 int maxchannels,maxwavch;
697 memset(&c,0,sizeof(c));
699 count=fscanf(f,":%d %d %d %d %d %d %ld :%d:%d:",
700 &c.label,&c.cue_text,&c.cue_desc,
701 &c.tag,&c.tag_create_p,
702 &c.mix.vol_master,
703 &c.mix.vol_ms,
704 &maxchannels,&maxwavch);
705 if(count<9){
706 addnlstr("LOAD ERROR (CUE): too few fields.",80,' ');
707 return(-1);
709 if(c.tag!=-1){
710 if(c.tag>=tag_count ||
711 !tag_list[c.tag].basemap){
712 addnlstr("LOAD ERROR (CUE): references a bad TAG",80,' ');
713 return(-1);
717 for(i=0;i<maxchannels;i++){
718 int v;
719 char ch=' ';
720 count=fscanf(f,"%c",&ch);
721 if(count<0 || ch!='<'){
722 addnlstr("LOAD ERROR (CUE): parse error looking for '<'.",80,' ');
723 return(-1);
725 for(j=0;j<maxwavch;j++){
726 count=fscanf(f,"%d",&v);
727 if(count<1){
728 addnlstr("LOAD ERROR (CUE): parse error looking for value.",80,' ');
729 return(-1);
731 if(j<MAX_FILECHANNELS)
732 c.mix.outvol[j][i]=v;
734 count=fscanf(f,"%c",&ch);
735 if(count<0 || ch!='>'){
736 addnlstr("LOAD ERROR (CUE): parse error looking for '>'.",80,' ');
737 return(-1);
741 acquire_label(c.label);
742 acquire_label(c.cue_text);
743 acquire_label(c.cue_desc);
745 acquire_tag(c.tag);
746 add_cue(cue_count,c);
748 return(0);
751 int load_val(FILE *f, long *val){
752 int count;
753 long t;
754 count=fscanf(f,": %ld",&t);
755 if(count<1){
756 addnlstr("LOAD ERROR (VAL): missing field.",80,' ');
757 return(-1);
759 *val=t;
760 return(0);
763 int save_val(FILE *f, char *p, long val){
764 int count;
765 fprintf(f,"%s: %ld",p,val);
766 count=fprintf(f,"\n");
767 if(count<1)return(-1);
768 return(0);
771 int load_program(FILE *f){
772 char buf[3];
773 int ret=0,count;
775 move(0,0);
776 attron(A_BOLD);
778 while(1){
779 if(fread(buf,1,3,f)<3){
780 addnlstr("LOAD ERROR: truncated program.",80,' ');
781 ret=-1;
782 break;
784 if(!strncmp(buf,"TAG",3)){
785 ret|=load_tag(f);
786 }else if(!strncmp(buf,"CUE",3)){
787 ret|=load_cue(f);
788 }else if(!strncmp(buf,"LBL",3)){
789 ret|=load_label(f);
790 }else if(!strncmp(buf,"MAS",3)){
791 ret|=load_val(f,&main_master_volume);
794 while(1){
795 count=fgetc(f);
796 if(count=='\n' || count==EOF)break;
798 if(count==EOF)break;
799 while(1){
800 count=fgetc(f);
801 if(count!='\n' || count==EOF)break;
803 if(count!=EOF)
804 ungetc(count,f);
805 if(count==EOF)break;
808 attroff(A_BOLD);
809 if(ret)mgetch();
810 return(ret);
813 int save_program(FILE *f){
814 int ret=0;
815 ret|=save_labels(f);
816 ret|=save_tags(f);
817 ret|=save_cues(f);
818 ret|=save_val(f,"MAS",main_master_volume);
819 return ret;
822 /*************** threaded record ****************************/
824 #define REC_SAMPLE_BYTES 3
825 #define REC_SAMPLE_FMT AFMT_S24_LE
826 #define REC_SAMPLE_CH 2
827 #define REC_BLOCK (REC_SAMPLE_CH * REC_SAMPLE_BYTES * 512)
828 unsigned char recordbuffer[REC_BLOCK*512];
830 pthread_mutex_t rec_buffer_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
831 pthread_cond_t rec_buffer_cond = PTHREAD_COND_INITIALIZER;
833 int record_head=0;
834 int record_count=0;
835 int record_tail=0;
836 int rec_active1=0;
837 int rec_active2=0;
838 int rec_exit=0;
839 int rec_flush_req=0;
840 int rec_flush_ok=0;
842 int rec_buffer_disk_min=100;
843 int rec_buffer_dma_min=100;
845 /* writes a wav header without the length set. */
846 void PutNumLE(long num,FILE *f,int bytes){
847 int i=0;
848 while(bytes--){
849 fputc((num>>(i<<3))&0xff,f);
850 i++;
853 void WriteWav(FILE *f,long channels,long rate,long bits,long duration){
854 fseek(f,0,SEEK_SET);
855 fprintf(f,"RIFF");
856 PutNumLE(duration+44-8,f,4);
857 fprintf(f,"WAVEfmt ");
858 PutNumLE(16,f,4);
859 PutNumLE(1,f,2);
860 PutNumLE(channels,f,2);
861 PutNumLE(rate,f,4);
862 PutNumLE(rate*channels*((bits-1)/8+1),f,4);
863 PutNumLE(((bits-1)/8+1)*channels,f,2);
864 PutNumLE(bits,f,2);
865 fprintf(f,"data");
866 PutNumLE(duration,f,4);
869 void *record_disk_thread(void *dummy){
870 FILE *recdiskfd=NULL;
871 long filesize=0;
873 while(1){
874 /* open a file; name the capture by time/date */
875 struct stat buf;
876 int ret;
878 while(1){
880 pthread_mutex_lock(&rec_mutex);
881 if(!rec_flush_req && recdiskfd){
882 WriteWav(recdiskfd,2,44100,REC_SAMPLE_BYTES*8,filesize);
883 fclose(recdiskfd);
884 recdiskfd=NULL;
885 rec_flush_ok=0;
887 pthread_mutex_unlock(&rec_mutex);
889 while(1){
890 /* lock and check count; wait if none to flush */
891 pthread_mutex_lock(&rec_buffer_mutex);
892 if(record_count<=0){
893 /* wait for it */
894 pthread_cond_wait(&rec_buffer_cond,&rec_buffer_mutex);
895 pthread_mutex_unlock(&rec_buffer_mutex);
896 pthread_mutex_lock(&rec_mutex);
897 }else{
898 pthread_mutex_unlock(&rec_buffer_mutex);
899 pthread_mutex_lock(&rec_mutex);
900 break;
903 if(rec_exit)break;
904 pthread_mutex_unlock(&rec_mutex);
907 /* rec mutex lock fell through */
909 int percent=rint(100.-record_count*100./sizeof(recordbuffer));
910 if(rec_buffer_disk_min>percent)rec_buffer_disk_min=percent;
912 pthread_mutex_unlock(&rec_mutex);
914 /* flush to disk */
916 if(recdiskfd)ret=fwrite(recordbuffer+record_tail,1,REC_BLOCK,recdiskfd);
918 /* update counters, alert dma that we have space in ring buffer */
919 pthread_mutex_lock(&rec_buffer_mutex);
920 record_tail+=REC_BLOCK;
921 if((unsigned)record_tail>=sizeof(recordbuffer))record_tail=0;
922 record_count-=REC_BLOCK;
923 pthread_cond_signal(&rec_buffer_cond);
924 pthread_mutex_unlock(&rec_buffer_mutex);
925 filesize+=REC_BLOCK;
927 pthread_mutex_lock(&rec_mutex);
928 if(filesize>10*60*44100*2*3)break;
929 if(rec_flush_req && !recdiskfd)break;
930 if(rec_exit)break;
931 pthread_mutex_unlock(&rec_mutex);
935 if(rec_exit)break;
936 pthread_mutex_unlock(&rec_mutex);
938 if(recdiskfd){
939 WriteWav(recdiskfd,2,44100,REC_SAMPLE_BYTES*8,filesize);
940 fclose(recdiskfd);
941 filesize=0;
944 ret=stat("record",&buf);
945 if(ret){
946 mkdir("record",0700);
947 ret=stat("record",&buf);
949 if(!ret && S_ISDIR(buf.st_mode)){
950 /* construct a new filename */
951 struct tm *now;
952 char buf2[4096];
953 char buf1[256];
954 time_t nows;
955 nows=time(NULL);
956 now=localtime(&nows);
957 strftime(buf1,256,"%Y%m%d_%H:%M:%S",now);
958 sprintf(buf2,"record/%s.wav",buf1);
959 recdiskfd=fopen(buf2,"wb");
960 if(recdiskfd){
961 WriteWav(recdiskfd,2,44100,REC_SAMPLE_BYTES*8,-1);
962 pthread_mutex_lock(&rec_mutex);
963 rec_flush_ok=1;
964 filesize=0;
965 }else{
966 pthread_mutex_lock(&rec_mutex);
967 rec_flush_ok=0;
969 pthread_mutex_unlock(&rec_mutex);
973 if(recdiskfd){
974 WriteWav(recdiskfd,2,44100,REC_SAMPLE_BYTES*8,filesize);
975 fclose(recdiskfd);
978 rec_active2=0;
979 fprintf(stderr,"Record flush thread exit...\n");
980 pthread_mutex_unlock(&rec_mutex);
981 return(NULL);
984 void *record_thread(void *dummy){
985 /* sound device startup */
986 int fd=fileno(recfd),i,j;
987 int format=REC_SAMPLE_FMT;
988 int channels=2;
989 int rate=44100;
990 long totalsize;
991 int ret;
992 audio_buf_info info;
994 /* realtime schedule setup */
996 struct sched_param param;
997 param.sched_priority=89;
998 if(pthread_setschedparam(pthread_self(), SCHED_FIFO, &param)){
999 fprintf(stderr,"Could not set realtime priority for playback; am I suid root?\n");
1000 exit(1);
1004 ret=ioctl(fd,SNDCTL_DSP_SETFMT,&format);
1005 if(ret || format!=REC_SAMPLE_FMT){
1006 fprintf(stderr,"Could not set recording format\n");
1007 exit(1);
1009 ret=ioctl(fd,SNDCTL_DSP_CHANNELS,&channels);
1010 if(ret || channels!=2){
1011 fprintf(stderr,"Could not set %d channel recording\n",2);
1012 exit(1);
1014 ret=ioctl(fd,SNDCTL_DSP_SPEED,&rate);
1015 if(ret || rate!=44100){
1016 fprintf(stderr,"Could not set %dHz recording\n",44100);
1017 exit(1);
1020 ioctl(fd,SNDCTL_DSP_GETISPACE,&info);
1021 totalsize=info.fragstotal*info.fragsize;
1024 pthread_create(&record_disk_thread_id,NULL,record_disk_thread,NULL);
1026 while(1){
1027 int ret;
1029 /* lock the buffer and check tail; block on space to record */
1030 while(1){
1031 pthread_mutex_lock(&rec_buffer_mutex);
1032 if(sizeof(recordbuffer)-record_count<REC_BLOCK)
1033 /* wait for it */
1034 pthread_cond_wait(&rec_buffer_cond,&rec_buffer_mutex);
1035 else
1036 break;
1037 pthread_mutex_unlock(&rec_buffer_mutex);
1039 pthread_mutex_lock(&rec_mutex);
1040 if(rec_exit)break;
1041 pthread_mutex_unlock(&rec_mutex);
1044 pthread_mutex_unlock(&rec_buffer_mutex);
1046 /* update ISPACE min */
1047 ioctl(fd,SNDCTL_DSP_GETISPACE,&info);
1049 int percent=rint((totalsize-info.bytes)*100./totalsize);
1050 pthread_mutex_lock(&rec_mutex);
1051 if(rec_buffer_dma_min>percent)rec_buffer_dma_min=percent;
1052 pthread_mutex_unlock(&rec_mutex);
1055 ret=fread(recordbuffer+record_head,1,REC_BLOCK,recfd);
1057 pthread_mutex_lock(&rec_mutex);
1058 for(i=record_head;i<record_head+REC_BLOCK;)
1059 for(j=0;j<REC_SAMPLE_CH;j++){
1060 int val=((recordbuffer[i]<<8)|(recordbuffer[i+1]<<16)|(recordbuffer[i+2]<<24))>>8;
1061 //int val=((recordbuffer[i]<<16)|(recordbuffer[i+1]<<24))>>8;
1062 if(labs(val)>rchannel_list[j].peak)
1063 rchannel_list[j].peak=labs(val);
1064 i+=REC_SAMPLE_BYTES;
1066 if(rec_exit)break;
1068 if(rec_flush_req){
1069 pthread_mutex_unlock(&rec_mutex);
1071 pthread_mutex_lock(&rec_buffer_mutex);
1072 record_head+=REC_BLOCK;
1073 if((unsigned)record_head>=sizeof(recordbuffer))record_head=0;
1074 record_count+=REC_BLOCK;
1075 pthread_cond_signal(&rec_buffer_cond);
1076 pthread_mutex_unlock(&rec_buffer_mutex);
1077 }else{
1078 pthread_mutex_unlock(&rec_mutex);
1082 rec_active1=0;
1083 fprintf(stderr,"Record thread exit...\n");
1084 pthread_mutex_unlock(&rec_mutex);
1086 return(NULL);
1089 /*************** threaded precache ****************************/
1091 static void wake_cache(){
1092 /* signal the precache thread to wake up and work */
1093 pthread_mutex_lock(&cache_mutex);
1094 pthread_cond_signal(&cache_cond);
1095 pthread_mutex_unlock(&cache_mutex);
1098 /* master is singly locked upon call */
1099 static void _cache_tag_lockahead(int i){
1100 tag *t=cache_list[i];
1101 long fill=t->cache_fill;
1102 long new;
1103 long bytes=t->samplelength;
1105 pthread_mutex_lock(&mmap_mutex);
1106 pthread_mutex_unlock(&cache_mutex);
1108 fill*=2*t->channels;
1109 new=fill+65536;
1110 bytes*=2*t->channels;
1112 if(new>bytes)new=bytes;
1113 if(mlock(t->data,new)){
1114 fprintf(stderr,"mlock failed: %s\n",strerror(errno));
1116 new/=2;
1117 new/=t->channels;
1119 pthread_mutex_unlock(&mmap_mutex);
1120 pthread_mutex_lock(&cache_mutex);
1121 t->cache_fill=new;
1124 static void _cache_remove(int i){
1125 /* remove the tag from the precaching list, then free its cache;
1126 this is the only place a cache free can happen, so we only need
1127 lock/check against async read */
1129 tag *t=cache_list[i];
1130 cache_count--;
1131 if(i<cache_count)
1132 memmove(cache_list+i,
1133 cache_list+i+1,
1134 (cache_count-i)*sizeof(*cache_list));
1135 t->cachep=0;
1136 munlock(t->basemap,t->samplelength*sizeof(int16)*t->channels+sizeof(struct sample_header));
1139 /* unlocks cache of all tags not currently active */
1140 static void cache_cull(){
1141 int i;
1142 pthread_mutex_lock(&cache_mutex);
1143 for(i=cache_count-1;i>=0;i--)
1144 if(!cache_list[i]->activep)_cache_remove(i);
1145 pthread_mutex_unlock(&cache_mutex);
1146 refresh();
1147 write(ttypipe[1],"",1);
1150 static void _cache_add(int tagnum){
1151 tag *t=tag_list+tagnum;
1153 if(!t->basemap)return;
1154 if(t->cachep)return;
1155 cache_list[cache_count]=t;
1156 cache_count++;
1157 t->cache_fill=0;
1158 t->sample_position=0;
1159 t->cachep=1;
1160 refresh();
1161 write(ttypipe[1],"",1);
1164 static void *cache_thread(void *dummy){
1165 /* complete our self-setup; we need to be a realtime thread just
1166 behind playback and record */
1167 int i;
1169 struct sched_param param;
1170 param.sched_priority=80;
1171 if(pthread_setschedparam(pthread_self(), SCHED_FIFO, &param)){
1172 fprintf(stderr,"Could not set realtime priority for caching; am I suid root?\n");
1173 exit(1);
1176 pthread_mutex_lock(&cache_mutex);
1177 while(1){
1179 /* scan tags; service lowest fill. Active has priority over
1180 precache */
1182 long minfill_a=0;
1183 long mintag_a=-1;
1184 long minfill_p=0;
1185 long mintag_p=-1;
1186 for(i=0;i<cache_count;i++){
1187 tag *t=cache_list[i];
1188 if(t->cache_fill<t->samplelength){
1189 long minfill=t->cache_fill-t->sample_position;
1191 if(minfill<CACHE_SAMPLES){
1193 if(t->activep){
1194 if(mintag_a == -1 || t->cache_fill<minfill_a){
1195 minfill_a=minfill;
1196 mintag_a=i;
1198 }else{
1199 if(mintag_p == -1 || t->cache_fill<minfill_p){
1200 minfill_p=minfill;
1201 mintag_p=i;
1206 if(mintag_a>=0){
1207 _cache_tag_lockahead(mintag_a);
1208 continue;
1210 if(mintag_p>=0){
1211 _cache_tag_lockahead(mintag_p);
1212 continue;
1217 /* if there was no work to do, we sleep and wait for a signal */
1218 pthread_cond_wait(&cache_cond, &cache_mutex);
1222 /*************** threaded playback ****************************/
1224 static inline double _slew_ms(long ms,double now,double target){
1226 return((target-now)/(ms*44.1));
1230 /* position in active list */
1231 static inline void _playback_remove(int i){
1232 tag *t=active_list[i];
1233 t->activep=0;
1234 refresh();
1235 active_count--;
1236 if(i<active_count)
1237 memmove(active_list+i,
1238 active_list+i+1,
1239 (active_count-i)*sizeof(*active_list));
1240 for(i=cache_count-1;i>=0;i--)
1241 if(cache_list[i]==t)
1242 _cache_remove(i);
1243 write(ttypipe[1],"",1);
1246 /* position in tag list */
1247 static inline void _playback_add(int tagnum,int cuenum){
1248 tag *t=tag_list+tagnum;
1249 cue *c=cue_list+cuenum;
1250 int j,k;
1252 if(t->activep)return;
1253 if(!t->basemap)return;
1254 t->activep=1;
1255 refresh();
1257 active_list[active_count]=t;
1258 active_count++;
1259 t->sample_position=0;
1260 t->sample_lapping=t->loop_lapping*44.1;
1261 if(t->sample_lapping>(t->samplelength-t->sample_loop_start)/2)
1262 t->sample_lapping=(t->samplelength-t->sample_loop_start)/2;
1264 t->sample_loop_start=t->loop_start*44.1;
1265 if(t->sample_loop_start>t->samplelength)
1266 t->sample_loop_start=t->samplelength;
1268 t->sample_fade_start=t->samplelength-t->fade_out*44.1;
1269 if(t->sample_fade_start<0)t->sample_fade_start=0;
1271 if(c->mix.vol_ms==0)
1272 t->master_vol_current=c->mix.vol_master;
1273 else
1274 t->master_vol_current=0;
1276 t->master_vol_target=c->mix.vol_master;
1277 t->master_vol_slew=_slew_ms(c->mix.vol_ms,0,c->mix.vol_master);
1279 if(c->mix.vol_ms==0)
1280 for(j=0;j<MAX_OUTPUT_CHANNELS;j++)
1281 for(k=0;k<t->channels;k++){
1282 t->outvol_current[k][j]=c->mix.outvol[k][j];
1283 t->outvol_target[k][j]=c->mix.outvol[k][j];
1284 t->outvol_slew[k][j]=_slew_ms(c->mix.vol_ms,0,c->mix.outvol[k][j]);
1286 else
1287 for(j=0;j<MAX_OUTPUT_CHANNELS;j++)
1288 for(k=0;k<t->channels;k++){
1289 t->outvol_current[k][j]=0;
1290 t->outvol_target[k][j]=c->mix.outvol[k][j];
1291 t->outvol_slew[k][j]=_slew_ms(c->mix.vol_ms,0,c->mix.outvol[k][j]);
1294 _cache_add(tagnum);
1295 write(ttypipe[1],"",1);
1298 /* position in tag list */
1299 static inline void _playback_mix(int i,int cuenum){
1300 tag *t=tag_list+i;
1301 cue *c=cue_list+cuenum;
1302 int j,k;
1304 if(!t->activep)return;
1306 t->master_vol_target=c->mix.vol_master;
1307 t->master_vol_slew=_slew_ms(c->mix.vol_ms,t->master_vol_current,
1308 c->mix.vol_master);
1310 for(j=0;j<MAX_OUTPUT_CHANNELS;j++)
1311 for(k=0;k<t->channels;k++){
1312 t->outvol_target[k][j]=c->mix.outvol[k][j];
1313 t->outvol_slew[k][j]=_slew_ms(c->mix.vol_ms,t->outvol_current[k][j],
1314 c->mix.outvol[k][j]);
1318 static inline void _next_sample(int16 *out){
1319 int i,j,k;
1320 double staging[MAX_OUTPUT_CHANNELS];
1321 double mmv=main_master_volume*.0001;
1323 memset(staging,0,sizeof(staging));
1325 /* iterate through the active sample list */
1326 for(i=active_count-1;i>=0;i--){
1327 tag *t=active_list[i];
1328 for(j=0;j<t->channels;j++){
1329 double value;
1330 int lappoint=t->samplelength-t->sample_lapping;
1332 double *ov_slew=t->outvol_slew[j];
1333 double *ov_target=t->outvol_target[j];
1334 double *ov_current=t->outvol_current[j];
1336 /* get the base value, depending on loop or no */
1337 if(t->loop_p && t->sample_position>=lappoint){
1338 long looppos=t->sample_position-lappoint+t->sample_loop_start;
1339 double value2=t->data[looppos*t->channels+j];
1340 value=t->data[t->sample_position*t->channels+j];
1342 value=(value2*looppos/t->sample_lapping+
1343 value-value*looppos/t->sample_lapping)*t->master_vol_current*.01;
1345 }else{
1346 value=t->data[t->sample_position*t->channels+j]*
1347 t->master_vol_current*.01;
1350 /* output split and mix */
1351 value*=mmv;
1352 for(k=0;k<MAX_OUTPUT_CHANNELS;k++){
1353 staging[k]+=value*ov_current[k];
1355 ov_current[k]+=ov_slew[k];
1356 if(ov_slew[k]>0. &&
1357 ov_current[k]>ov_target[k]){
1358 ov_slew[k]=0.;
1359 ov_current[k]=ov_target[k];
1361 if(ov_slew[k]<0. &&
1362 ov_current[k]<ov_target[k]){
1363 ov_slew[k]=0.;
1364 ov_current[k]=ov_target[k];
1369 /* update master volume */
1370 if(t->master_vol_slew){
1371 t->master_vol_current+=t->master_vol_slew;
1372 if(t->master_vol_slew>0 && t->master_vol_current>t->master_vol_target){
1373 t->master_vol_slew=0;
1374 t->master_vol_current=t->master_vol_target;
1376 if(t->master_vol_slew<0 && t->master_vol_current<t->master_vol_target){
1377 t->master_vol_slew=0;
1378 t->master_vol_current=t->master_vol_target;
1380 if(t->master_vol_current<0)
1381 _playback_remove(i);
1384 /* determine if fade out has begun */
1385 if(t->sample_position==t->sample_fade_start && !t->loop_p){
1386 /* effect a master volume slew *now* */
1387 t->master_vol_slew=_slew_ms(t->fade_out,
1388 t->master_vol_current,0);
1389 t->master_vol_target=0;
1392 /* update playback position */
1393 t->sample_position++;
1394 if(t->sample_position>=t->samplelength){
1395 if(t->loop_p){
1396 t->sample_position=t->sample_loop_start+t->sample_lapping;
1397 }else{
1398 _playback_remove(i);
1404 /* declipping, conversion */
1405 for(i=0;i<MAX_OUTPUT_CHANNELS;i++){
1406 if(channel_list[i].peak<fabs(staging[i]))
1407 channel_list[i].peak=fabs(staging[i]);
1408 if(staging[i]>32767.){
1409 out[i]=32767;
1410 }else if(staging[i]<-32768.){
1411 out[i]=-32768;
1412 }else
1413 out[i]=(int)(rint(staging[i]));
1417 static int playback_active=0;
1418 static int playback_exit=0;
1420 /* NO LOCKING in the playback thread. We're the highest priority
1421 thread and will not be preempted. The data structures we depend on
1422 are 'locked' at lower levels by atomic assignment blocks. */
1424 void *playback_thread(void *dummy){
1425 /* sound device startup */
1426 audio_buf_info info;
1427 int fd=fileno(playfd),i;
1428 int format=AFMT_S16_NE;
1429 int channels=MAX_OUTPUT_CHANNELS;
1430 int rate=44100;
1431 long last=0;
1432 long delay=10;
1433 long totalsize;
1434 int fragment=0x7fff000d;
1435 int16 audiobuf[256*MAX_OUTPUT_CHANNELS];
1436 int ret;
1438 /* realtime schedule setup */
1440 struct sched_param param;
1441 param.sched_priority=89;
1442 if(pthread_setschedparam(pthread_self(), SCHED_FIFO, &param)){
1443 fprintf(stderr,"Could not set realtime priority for playback; am I suid root?\n");
1444 exit(1);
1448 ioctl(fd,SNDCTL_DSP_SETFRAGMENT,&fragment);
1449 ret=ioctl(fd,SNDCTL_DSP_SETFMT,&format);
1450 if(ret || format!=AFMT_S16_NE){
1451 fprintf(stderr,"Could not set AFMT_S16_NE playback\n");
1452 exit(1);
1454 ret=ioctl(fd,SNDCTL_DSP_CHANNELS,&channels);
1455 if(ret || channels!=MAX_OUTPUT_CHANNELS){
1456 fprintf(stderr,"Could not set %d channel playback\n",MAX_OUTPUT_CHANNELS);
1457 exit(1);
1460 ret=ioctl(fd,SNDCTL_DSP_SPEED,&rate);
1461 if(ret || rate!=44100){
1462 fprintf(stderr,"Could not set %dHz playback\n",44100);
1463 exit(1);
1466 ioctl(fd,SNDCTL_DSP_GETOSPACE,&info);
1467 playback_buffer_minfill=totalsize=info.fragstotal*info.fragsize;
1469 while(!playback_exit){
1470 int samples;
1471 int ret;
1473 delay--;
1474 if(delay<0){
1475 delay=0;
1476 ioctl(fd,SNDCTL_DSP_GETOSPACE,&info);
1478 playback_bufsize=totalsize;
1479 samples=playback_bufsize-info.bytes;
1480 if(playback_buffer_minfill>samples)
1481 playback_buffer_minfill=samples-64; // sample fragment
1485 for(i=0;i<256;i++)
1486 _next_sample(audiobuf+i*MAX_OUTPUT_CHANNELS);
1488 /* this is a calculated race; the race would not trip except in
1489 situations where our locking latency would also cause the
1490 realtime house of cards to come crashing down anyway */
1491 pthread_cond_signal(&cache_cond);
1493 fwrite(audiobuf,2*MAX_OUTPUT_CHANNELS,256,playfd);
1496 struct timeval tv;
1497 long foo;
1498 gettimeofday(&tv,NULL);
1499 foo=tv.tv_sec*10+tv.tv_usec/100000;
1500 if(last!=foo)
1501 write(ttypipe[1],"",1);
1502 last=foo;
1507 playback_active=0;
1509 /* sound device shutdown */
1511 ioctl(fd,SNDCTL_DSP_RESET);
1512 fprintf(stderr,"Playback thread exit...\n");
1513 return(NULL);
1516 /* cuenum is the base number */
1517 /* add new tags before new mixes */
1518 int play_cue(int cuenum){
1519 int x=cuenum;
1520 BEGIN_ATOMIC
1521 while(1){
1522 cue *c=cue_list+x;
1523 if(c->tag>=0){
1524 if(c->tag_create_p)
1525 _playback_add(c->tag,x);
1527 x++;
1528 if(x>=cue_count || cue_list[x].tag==-1)break;
1530 while(1){
1531 cue *c=cue_list+cuenum;
1532 if(c->tag>=0){
1533 if(!c->tag_create_p)
1534 _playback_mix(c->tag,cuenum);
1536 cuenum++;
1537 if(cuenum>=cue_count || cue_list[cuenum].tag==-1)break;
1539 END_ATOMIC
1540 return(0);
1543 /* caches new queue. cache cleanup is handled in caching thread */
1544 int cache_cue(int cuenum){
1545 pthread_mutex_lock(&cache_mutex);
1547 while(1){
1548 cue *c=cue_list+cuenum;
1549 if(c->tag>=0){
1550 if(c->tag_create_p)
1551 _cache_add(c->tag);
1553 cuenum++;
1554 if(cuenum>=cue_count || cue_list[cuenum].tag==-1)break;
1557 pthread_mutex_unlock(&cache_mutex);
1559 wake_cache();
1560 return(0);
1563 int play_sample(int cuenum){
1564 cue *c=cue_list+cuenum;
1565 BEGIN_ATOMIC
1566 if(c->tag>=0){
1567 if(c->tag_create_p)
1568 _playback_add(c->tag,cuenum);
1569 else
1570 _playback_mix(c->tag,cuenum);
1573 END_ATOMIC
1574 return(0);
1577 int halt_playback(){
1578 int i;
1579 BEGIN_ATOMIC
1580 for(i=0;i<tag_count;i++){
1581 tag *t=tag_list+i;
1583 if(t->activep){
1585 t->master_vol_target=-1;
1586 t->master_vol_slew=_slew_ms(100,t->master_vol_current,0);
1590 END_ATOMIC
1591 return(0);
1595 /***************** simple form entry fields *******************/
1597 enum field_type { FORM_YESNO, FORM_PERCENTAGE, FORM_PERCENTAGE_R,
1598 FORM_NUMBER, FORM_GENERIC,
1599 FORM_BUTTON } ;
1600 typedef struct {
1601 enum field_type type;
1602 int x;
1603 int y;
1604 int width;
1605 void *var;
1607 int active;
1608 int cursor;
1609 } formfield;
1611 typedef struct {
1612 formfield *fields;
1613 int count;
1614 int storage;
1615 int edit;
1617 int cursor;
1618 } form;
1620 void form_init(form *f,int maxstorage){
1621 memset(f,0,sizeof(*f));
1622 f->fields=calloc(maxstorage,sizeof(formfield));
1623 f->storage=maxstorage;
1626 void form_clear(form *f){
1627 if(f->fields)free(f->fields);
1628 memset(f,0,sizeof(*f));
1631 void draw_field(formfield *f,int edit,int focus){
1632 int y,x;
1633 int i;
1634 getyx(stdscr,y,x);
1635 move(f->y,f->x);
1637 if(f->type==FORM_BUTTON)edit=0;
1639 if(edit && f->active)
1640 attron(A_REVERSE);
1642 addch('[');
1644 if(f->active){
1646 if(edit){
1647 attrset(0);
1648 }else{
1649 if(focus){
1650 attron(A_REVERSE);
1651 }else{
1652 attron(A_BOLD);
1656 switch(f->type){
1657 case FORM_YESNO:
1659 int *var=(int *)(f->var);
1660 char *s="No ";
1661 if(*var)
1662 s="Yes";
1663 for(i=0;i<f->width-5;i++)
1664 addch(' ');
1665 addstr(s);
1667 break;
1668 case FORM_PERCENTAGE:
1670 int var=*(int *)(f->var);
1671 char buf[80];
1672 if(var<0)var=0;
1673 if(var>100)var=100;
1674 snprintf(buf,80,"%*d",f->width-2,var);
1675 addstr(buf);
1677 break;
1678 case FORM_PERCENTAGE_R:
1680 int var=*(int *)(f->var);
1681 char buf[80];
1682 if(var<-1)var=-1;
1683 if(var>100)var=100;
1684 if(var==-1)
1685 snprintf(buf,80,"%*s",f->width-2,"DEL");
1686 else
1687 snprintf(buf,80,"%*d",f->width-2,var);
1688 addstr(buf);
1690 break;
1691 case FORM_NUMBER:
1693 long var=*(long *)(f->var);
1694 char buf[80];
1695 snprintf(buf,80,"%*ld",f->width-2,var);
1696 addstr(buf);
1698 break;
1699 case FORM_GENERIC:case FORM_BUTTON:
1701 char *var=(char *)(f->var);
1702 addnlstr(var,f->width-2,' ');
1704 break;
1707 attrset(0);
1708 }else{
1709 attrset(0);
1710 addnlstr("",f->width-2,'-');
1713 if(edit &&
1714 f->active)
1715 attron(A_REVERSE);
1717 addch(']');
1719 attrset(0);
1721 /* cursor? */
1722 move(y,x);
1723 if(focus && edit && f->type==FORM_GENERIC){
1724 curs_set(1);
1725 move(f->y,f->x+1+f->cursor);
1726 }else{
1727 curs_set(0);
1731 void form_redraw(form *f){
1732 int i;
1733 for(i=0;i<f->count;i++)
1734 draw_field(f->fields+i,f->edit,i==f->cursor);
1737 int field_add(form *f,enum field_type type,int x,int y,int width,void *var){
1738 int n=f->count;
1739 if(f->storage==n)return(-1);
1740 /* add the struct, then draw contents */
1741 f->fields[n].type=type;
1742 f->fields[n].x=x;
1743 f->fields[n].y=y;
1744 f->fields[n].width=width;
1745 f->fields[n].var=var;
1746 f->fields[n].active=1;
1747 f->count++;
1749 draw_field(f->fields+n,f->edit,n==f->cursor);
1750 return(n);
1753 void field_state(form *f,int n,int activep){
1754 if(n<f->count){
1755 f->fields[n].active=activep;
1756 draw_field(f->fields+n,f->edit,n==f->cursor);
1760 void form_next_field(form *f){
1761 formfield *ff=f->fields+f->cursor;
1762 draw_field(f->fields+f->cursor,0,0);
1764 while(1){
1765 f->cursor++;
1766 if(f->cursor>=f->count)f->cursor=0;
1767 ff=f->fields+f->cursor;
1768 if(ff->active)break;
1771 draw_field(f->fields+f->cursor,f->edit,1);
1774 void form_prev_field(form *f){
1775 formfield *ff=f->fields+f->cursor;
1776 draw_field(f->fields+f->cursor,0,0);
1778 while(1){
1779 f->cursor--;
1780 if(f->cursor<0)f->cursor=f->count-1;
1781 ff=f->fields+f->cursor;
1782 if(ff->active)break;
1785 draw_field(f->fields+f->cursor,f->edit,1);
1788 /* returns >=0 if it does not handle the character */
1789 int form_handle_char(form *f,int c){
1790 formfield *ff=f->fields+f->cursor;
1791 int ret=-1;
1793 switch(c){
1794 case KEY_ENTER:
1795 case '\n':
1796 case '\r':
1797 if(ff->type==FORM_BUTTON){
1798 f->edit=0;
1799 ret=KEY_ENTER;
1800 }else{
1801 if(f->edit){
1802 f->edit=0;
1803 //draw_field(f->fields+f->cursor,f->edit,1);
1804 //form_next_field(f);
1805 }else{
1806 f->edit=1;
1809 break;
1810 case KEY_UP:
1811 form_prev_field(f);
1812 break;
1813 case KEY_DOWN:case '\t':
1814 form_next_field(f);
1815 break;
1816 default:
1817 if(f->edit){
1818 BEGIN_ATOMIC
1819 switch(ff->type){
1820 case FORM_YESNO:
1822 int *val=(int *)ff->var;
1823 switch(c){
1824 case 'y':case 'Y':
1825 *val=1;
1826 break;
1827 case 'n':case 'N':
1828 *val=0;
1829 break;
1830 case ' ':
1831 if(*val)
1832 *val=0;
1833 else
1834 *val=1;
1835 break;
1836 default:
1837 ret=c;
1838 break;
1841 break;
1842 case FORM_PERCENTAGE:
1844 int *val=(int *)ff->var;
1845 switch(c){
1846 case '=':case KEY_RIGHT:
1847 (*val)++;
1848 if(*val>100)*val=100;
1849 break;
1850 case '+':
1851 (*val)+=10;
1852 if(*val>100)*val=100;
1853 break;
1854 case '-':case KEY_LEFT:
1855 (*val)--;
1856 if(*val<0)*val=0;
1857 break;
1858 case '_':
1859 (*val)-=10;
1860 if(*val<0)*val=0;
1861 break;
1862 default:
1863 ret=c;
1864 break;
1867 break;
1868 case FORM_PERCENTAGE_R:
1870 int *val=(int *)ff->var;
1871 switch(c){
1872 case '=':case KEY_RIGHT:
1873 (*val)++;
1874 if(*val>100)*val=100;
1875 break;
1876 case '+':
1877 (*val)+=10;
1878 if(*val>100)*val=100;
1879 break;
1880 case '-':case KEY_LEFT:
1881 (*val)--;
1882 if(*val<-1)*val=-1;
1883 break;
1884 case '_':
1885 (*val)-=10;
1886 if(*val<-1)*val=-1;
1887 break;
1888 default:
1889 ret=c;
1890 break;
1893 break;
1894 case FORM_NUMBER:
1896 long *val=(long *)ff->var;
1897 switch(c){
1898 case '0':case '1':case '2':case '3':case '4':
1899 case '5':case '6':case '7':case '8':case '9':
1900 if(*val<(int)rint(pow(10,ff->width-3))){
1901 (*val)*=10;
1902 (*val)+=c-48;
1904 break;
1905 case KEY_BACKSPACE:case '\b':
1906 (*val)/=10;
1907 break;
1908 case KEY_RIGHT:case '+':case '=':
1909 if(*val<(int)rint(pow(10,ff->width-2)-1))
1910 (*val)++;
1911 break;
1912 case KEY_LEFT:case '-':case '_':
1913 if(*val>0)
1914 (*val)--;
1915 break;
1916 default:
1917 ret=c;
1918 break;
1921 break;
1923 /* we assume the string for the GENERIC case is alloced to width */
1924 case FORM_GENERIC:
1926 char *val=(char *)ff->var;
1927 const char *ctrl=unctrl(c);
1928 switch(c){
1929 case KEY_LEFT:
1930 ff->cursor--;
1931 if(ff->cursor<0)ff->cursor=0;
1932 break;
1933 case KEY_RIGHT:
1934 ff->cursor++;
1935 if(ff->cursor>(int)strlen(val))ff->cursor=strlen(val);
1936 if(ff->cursor>ff->width-3)ff->cursor=ff->width-3;
1937 break;
1938 case KEY_BACKSPACE:case '\b':
1939 if(ff->cursor>0){
1940 memmove(val+ff->cursor-1,val+ff->cursor,strlen(val)-ff->cursor+1);
1941 ff->cursor--;
1943 break;
1944 default:
1945 if(isprint(c)){
1946 if((int)strlen(val)<ff->width-3){
1947 memmove(val+ff->cursor+1,val+ff->cursor,strlen(val)-ff->cursor+1);
1948 val[ff->cursor]=c;
1949 ff->cursor++;
1951 }else{
1952 if(ctrl[0]=='^'){
1953 switch(ctrl[1]){
1954 case 'A':case 'a':
1955 ff->cursor=0;
1956 break;
1957 case 'E':case 'e':
1958 ff->cursor=strlen(val);
1959 break;
1960 case 'K':case 'k':
1961 val[ff->cursor]='\0';
1962 break;
1963 default:
1964 ret=c;
1965 break;
1967 }else{
1968 ret=c;
1971 break;
1974 break;
1975 default:
1976 ret=c;
1977 break;
1979 END_ATOMIC
1981 }else{
1982 ret=c;
1985 draw_field(f->fields+f->cursor,f->edit,1);
1986 return(ret);
1989 /********************** main run screen ***********************/
1990 void main_update_master(int n,int y){
1991 if(menu==MENU_MAIN){
1992 char buf[4];
1993 if(n>300)n=300;
1994 if(n<0)n=0;
1996 main_master_volume=n;
1998 move(y,8);
1999 addstr("master: ");
2000 sprintf(buf,"%3ld%%",main_master_volume);
2001 addstr(buf);
2005 void main_update_playbuffer(int y){
2006 if(menu==MENU_MAIN){
2007 char buf[20];
2008 int n,nr;
2009 static int starve=0;
2010 static int starver1=0;
2011 static int starver2=0;
2013 n=playback_buffer_minfill;
2014 playback_buffer_minfill=playback_bufsize;
2016 if(n==0){
2017 starve=15;
2019 starve--;
2020 if(starve<0){
2021 starve=0; /* reset */
2024 if(playback_bufsize)
2025 n=rint(100.*n/playback_bufsize);
2026 else{
2027 n=0;
2028 starve=0;
2031 move(y,4);
2032 addstr("playbuffer: ");
2033 sprintf(buf,"%3d%% %s",n,starve?"***STARVE***":" ");
2034 addstr(buf);
2037 move(y+1,63);
2039 int state=0;
2040 pthread_mutex_lock(&rec_mutex);
2041 if(rec_flush_req){
2042 if(rec_flush_ok)
2043 state=2;
2044 else
2045 state=1;
2047 pthread_mutex_unlock(&rec_mutex);
2049 switch(state){
2050 case 0:
2051 addstr(" OFF ");
2052 break;
2053 case 1:
2054 addstr(" STARTING ");
2055 break;
2056 case 2:
2057 addstr(" RECORDING ");
2058 break;
2062 nr=rec_buffer_dma_min;
2063 rec_buffer_dma_min=100;
2064 pthread_mutex_unlock(&rec_mutex);
2066 if(nr==0){
2067 starver1=15;
2069 starver1--;
2070 if(starver1<0){
2071 starver1=0; /* reset */
2074 move(y,42);
2075 addstr("recbuffer (DMA):");
2076 sprintf(buf," %3d%% %s",nr,starver1?"***OVERRUN***":" ");
2077 addstr(buf);
2079 pthread_mutex_lock(&rec_mutex);
2080 nr=rec_buffer_disk_min;
2081 rec_buffer_disk_min=100;
2082 pthread_mutex_unlock(&rec_mutex);
2084 if(nr==0){
2085 starver2=15;
2087 starver2--;
2088 if(starver2<0){
2089 starver2=0; /* reset */
2092 move(y+1,42);
2093 addstr("recbuffer(disk):");
2094 sprintf(buf," %3d%% %s",nr,starver2?"***OVERRUN***":"");
2095 addstr(buf);
2099 #define todB_nn(x) ((x)==0.f?-400.f:log((x))*8.6858896f)
2100 static int dBmap[100]={0,0,1,1,1,1,1,1,1,1,
2101 1,1,1,1,1,1,1,1,1,1,
2102 1,1,1,1,1,1,1,1,1,1,
2103 1,1,1,1,1,2,2,2,2,2,
2104 2,2,2,2,2,2,2,2,2,2,
2105 3,3,3,3,3,3,3,3,3,3,
2106 3,3,4,4,4,4,4,4,4,4,
2107 4,4,5,5,5,5,5,5,5,5,
2108 6,6,6,6,6,6,7,7,7,7,
2109 7,8,8,8,8,9,9,9,10,10};
2111 static int clip[MAX_OUTPUT_CHANNELS];
2112 void main_update_outchannel_levels(int y){
2113 int i,j;
2114 if(menu==MENU_MAIN){
2115 for(i=0;i<MAX_OUTPUT_CHANNELS;i++){
2116 int val;
2117 char buf[11];
2118 BEGIN_ATOMIC
2119 val=channel_list[i].peak;
2120 channel_list[i].peak=0;
2121 END_ATOMIC
2123 move(y+i+1,17);
2124 if(val>=32767){
2125 clip[i]=15;
2126 val=32767;
2128 clip[i]--;
2129 if(clip[i]<0)clip[i]=0;
2130 if(clip[i]){
2131 attron(A_BOLD);
2132 addstr("CLIP");
2133 }else{
2134 addstr("+0dB");
2136 attron(A_BOLD);
2138 move(y+i+1,6);
2139 val=rint(todB_nn(val/32768.)+100.);
2140 if(val<0)val=0;
2141 if(val>99)val=99;
2142 val=dBmap[val];
2143 for(j=0;j<val;j++)buf[j]='=';
2144 for(;j<10;j++)buf[j]=' ';
2145 buf[j]='\0';
2146 addstr(buf);
2147 attroff(A_BOLD);
2152 static int rclip[2];
2153 void main_update_inchannel_levels(int y){
2154 int i,j;
2155 if(menu==MENU_MAIN){
2156 for(i=0;i<2;i++){
2157 int val;
2158 char buf[11];
2159 pthread_mutex_lock(&rec_mutex);
2160 val=rchannel_list[i].peak;
2161 rchannel_list[i].peak=0;
2162 pthread_mutex_unlock(&rec_mutex);
2164 move(y+i+1,55);
2165 if(val>=0x7fffff){
2166 rclip[i]=15;
2167 val=0x7fffff;
2169 rclip[i]--;
2170 if(rclip[i]<0)rclip[i]=0;
2171 if(rclip[i]){
2172 attron(A_BOLD);
2173 addstr("CLIP");
2174 }else{
2175 addstr("+0dB");
2177 attron(A_BOLD);
2179 move(y+i+1,44);
2180 val=rint(todB_nn(val/8388607.)+100.);
2181 if(val<0)val=0;
2182 if(val>99)val=99;
2183 val=dBmap[val];
2184 for(j=0;j<val;j++)buf[j]='=';
2185 for(;j<10;j++)buf[j]=' ';
2186 buf[j]='\0';
2187 addstr(buf);
2188 attroff(A_BOLD);
2193 void main_update_channel_labels(int y){
2194 int i;
2195 char buf[80];
2196 if(menu==MENU_MAIN){
2197 for(i=0;i<MAX_OUTPUT_CHANNELS;i++){
2198 move(y+i+1,4);
2199 sprintf(buf,"-[ ]+0dB ");
2200 addstr(buf);
2201 addstr(channel_list[i].label);
2203 for(i=0;i<2;i++){
2204 move(y+i+1,42);
2205 sprintf(buf,"-[ ]+0dB ");
2206 addstr(buf);
2207 pthread_mutex_lock(&rec_mutex);
2208 addstr(rchannel_list[i].label);
2209 pthread_mutex_unlock(&rec_mutex);
2213 main_update_outchannel_levels(y);
2214 main_update_inchannel_levels(y);
2217 void main_update_cues(int y){
2218 if(menu==MENU_MAIN){
2219 int cn=cue_list_number-1,i;
2221 for(i=-1;i<2;i++){
2222 move(y+i*3+3,0);
2223 if(i==0){
2224 addstr("NEXT => ");
2225 attron(A_REVERSE);
2227 if(cn<0){
2228 move(y+i*3+3,8);
2229 addnlstr("",71,' ');
2230 move(y+i*3+4,8);
2231 addnlstr("**** BEGIN",71,' ');
2232 }else if(cn>=cue_count){
2233 move(y+i*3+3,8);
2234 addnlstr("****** END",71,' ');
2235 attroff(A_REVERSE);
2236 move(y+i*3+4,8);
2237 addnlstr("",71,' ');
2238 if(i==0){
2239 move(y+i*3+6,8);
2240 addnlstr("",71,' ');
2241 move(y+i*3+7,8);
2242 addnlstr("",71,' ');
2244 break;
2245 }else{
2246 cue *c;
2247 c=cue_list+cn;
2249 move(y+i*3+3,8);
2250 addnlstr(label_text(c->label),12,' ');
2251 addnlstr(label_text(c->cue_text),59,' ');
2253 mvaddstr(y+i*3+4,8," ");
2254 addnlstr(label_text(c->cue_desc),59,' ');
2256 attroff(A_BOLD);
2257 while(++cn<cue_count)
2258 if(cue_list[cn].tag==-1)break;
2260 attroff(A_REVERSE);
2265 /* assumes the existing tags/labels are not changing out from
2266 underneath playback. editing a tag *must* kill playback for
2267 stability */
2268 void main_update_tags(int y){
2269 if(menu==MENU_MAIN){
2270 int i;
2271 static int last_tags=0;
2273 move(y,0);
2275 BEGIN_ATOMIC
2277 if(active_count){
2278 addstr("playing tags:");
2280 for(i=0;i<active_count;i++){
2281 int loop;
2282 int ms;
2283 char buf[20];
2284 int vol=active_list[i]->master_vol_current;
2285 label_number path;
2287 move(y+i,14);
2288 loop=active_list[i]->loop_p;
2289 ms=(active_list[i]->samplelength-active_list[i]->sample_position)/44.1;
2290 path=active_list[i]->sample_desc;
2292 if(loop)
2293 snprintf(buf,20,"[loop %3d%%] ",vol);
2294 else
2295 snprintf(buf,20,"[%3ds %3d%%] ",(ms+500)/1000,vol);
2296 addstr(buf);
2297 addnlstr(label_text(path),60,' ');
2300 }else{
2301 addstr(" ");
2304 for(i=active_count;i<last_tags;i++){
2305 move(y+i,14);
2306 addnlstr("",60,' ');
2309 last_tags=active_count;
2310 END_ATOMIC
2314 static int editable=1;
2315 void update_editable(){
2316 if(menu==MENU_MAIN){
2317 move(0,67);
2318 attron(A_BOLD);
2319 if(!editable)
2320 addstr(" EDIT LOCKED ");
2321 else
2322 addstr(" ");
2323 attroff(A_BOLD);
2327 void move_next_cue(){
2328 if(cue_list_number<cue_count){
2329 while(++cue_list_number<cue_count)
2330 if(cue_list[cue_list_number].tag==-1)break;
2331 cue_list_position++;
2333 cache_cull();
2334 cache_cue(cue_list_number);
2335 wake_cache();
2336 main_update_cues(10+MAX_OUTPUT_CHANNELS);
2339 void move_prev_cue(){
2340 if(cue_list_number>0){
2341 while(--cue_list_number>0)
2342 if(cue_list[cue_list_number].tag==-1)break;
2343 cue_list_position--;
2345 cache_cull();
2346 cache_cue(cue_list_number);
2347 wake_cache();
2348 main_update_cues(10+MAX_OUTPUT_CHANNELS);
2351 int save_top_level(char *fn){
2352 FILE *f;
2353 if(!firstsave){
2354 char *buf=alloca(strlen(fn)*2+20);
2355 sprintf(buf,"cp %s %s~ 2>/dev/null",fn,fn);
2356 /* create backup file */
2357 system(buf);
2358 firstsave=1;
2360 move(0,0);
2361 attron(A_BOLD);
2363 f=fopen(fn,"w");
2364 if(f==NULL){
2365 char buf[80];
2366 sprintf(buf,"SAVE FAILED: %s",strerror(errno));
2367 addnlstr(buf,80,' ');
2368 attroff(A_BOLD);
2369 return(1);
2372 if(save_program(f)){
2373 char buf[80];
2374 sprintf(buf,"SAVE FAILED: %s",strerror(ferror(f)));
2375 addnlstr(buf,80,' ');
2376 attroff(A_BOLD);
2377 fclose(f);
2378 return(1);
2380 fclose(f);
2381 addnlstr("PROGRAM SAVED (any key to continue)",80,' ');
2382 attroff(A_BOLD);
2383 unsaved=0;
2384 return(0);
2387 int main_menu(){
2388 clear();
2389 move(0,0);
2390 addstr("MTG Beaverphonic build "VERSION": ");
2391 attron(A_BOLD);
2392 addstr(program);
2393 attroff(A_BOLD);
2394 update_editable();
2396 mvvline(3,2,0,MAX_OUTPUT_CHANNELS+5);
2397 mvvline(3,77,0,MAX_OUTPUT_CHANNELS+5);
2398 mvvline(3,40,0,MAX_OUTPUT_CHANNELS+5);
2399 mvhline(2,2,0,76);
2400 mvhline(7,2,0,76);
2401 mvhline(8+MAX_OUTPUT_CHANNELS,2,0,76);
2402 mvaddch(2,2,ACS_ULCORNER);
2403 mvaddch(2,77,ACS_URCORNER);
2404 mvaddch(2,40,ACS_TTEE);
2405 mvaddch(8+MAX_OUTPUT_CHANNELS,2,ACS_LLCORNER);
2406 mvaddch(8+MAX_OUTPUT_CHANNELS,40,ACS_BTEE);
2407 mvaddch(8+MAX_OUTPUT_CHANNELS,77,ACS_LRCORNER);
2409 mvaddch(7,2,ACS_LTEE);
2410 mvaddch(7,40,ACS_PLUS);
2411 mvaddch(7,77,ACS_RTEE);
2413 move(2,7);
2414 addstr(" output ");
2416 move(2,45);
2417 addstr(" input ");
2419 mvhline(9+MAX_OUTPUT_CHANNELS,0,0,80);
2420 mvhline(18+MAX_OUTPUT_CHANNELS,0,0,80);
2422 main_update_master(main_master_volume,5);
2423 main_update_playbuffer(4);
2424 main_update_channel_labels(7);
2426 main_update_cues(10+MAX_OUTPUT_CHANNELS);
2427 main_update_tags(19+MAX_OUTPUT_CHANNELS);
2428 curs_set(0);
2430 refresh();
2432 while(1){
2433 int ch=getch();
2434 switch(ch){
2435 case '?':
2436 return(MENU_KEYPRESS);
2437 case 'q':
2438 move(0,0);
2439 attron(A_BOLD);
2440 addnlstr("Really quit? [y/N] ",80,' ');
2441 attroff(A_BOLD);
2442 refresh();
2443 ch=mgetch();
2444 if(ch=='y'){
2445 if(unsaved){
2446 move(0,0);
2447 attron(A_BOLD);
2448 addnlstr("Save changes first? [Y/n] ",80,' ');
2449 attroff(A_BOLD);
2450 ch=mgetch();
2451 if(ch!='n' && ch!='N')save_top_level(program);
2453 return(MENU_QUIT);
2455 move(0,0);
2456 addstr("MTG Beaverphonic build "VERSION": ");
2457 attron(A_BOLD);
2458 addstr(program);
2459 attroff(A_BOLD);
2460 break;
2461 case 'e':
2462 if(editable && cue_list_number<cue_count)return(MENU_EDIT);
2463 break;
2464 case 'a':
2465 if(editable)
2466 return(MENU_ADD);
2467 break;
2468 case 'd':
2469 if(editable){
2470 halt_playback();
2471 move(0,0);
2472 attron(A_BOLD);
2473 addnlstr("Really delete cue? [y/N] ",80,' ');
2474 attroff(A_BOLD);
2475 refresh();
2476 ch=mgetch();
2477 if(ch=='y'){
2478 unsaved=1;
2479 delete_cue_bank(cue_list_number);
2480 main_update_cues(10+MAX_OUTPUT_CHANNELS);
2482 move(0,0);
2483 addstr("MTG Beaverphonic build "VERSION": ");
2484 attron(A_BOLD);
2485 addstr(program);
2486 attroff(A_BOLD);
2489 break;
2490 case 'o':
2491 if(editable)
2492 return(MENU_OUTPUT);
2493 break;
2494 case 's':
2495 save_top_level(program);
2496 mgetch();
2497 return(MENU_MAIN);
2498 case '-':case '_':
2499 unsaved=1;
2500 main_update_master(main_master_volume-1,5);
2501 break;
2502 case '+':case '=':
2503 unsaved=1;
2504 main_update_master(main_master_volume+1,5);
2505 break;
2506 case ' ':
2507 play_cue(cue_list_number);
2508 move_next_cue();
2509 break;
2510 case KEY_UP:case '\b':case KEY_BACKSPACE:
2511 move_prev_cue();
2512 break;
2513 case KEY_DOWN:
2514 move_next_cue();
2515 break;
2516 case 'H':
2517 halt_playback();
2518 break;
2519 case 'l':
2520 if(editable)
2521 editable=0;
2522 else
2523 editable=1;
2524 update_editable();
2525 case 0:
2526 main_update_tags(19+MAX_OUTPUT_CHANNELS);
2527 main_update_playbuffer(4);
2528 main_update_outchannel_levels(7);
2529 main_update_inchannel_levels(7);
2530 break;
2531 // default:
2532 // if(ctrl[0]=='^'){
2533 // switch(ctrl[1]){
2534 case 'r':
2535 pthread_mutex_lock(&rec_mutex);
2536 rec_flush_req=1;
2537 pthread_mutex_unlock(&rec_mutex);
2538 break;
2539 case 'R':
2540 pthread_mutex_lock(&rec_mutex);
2541 rec_flush_req=0;
2542 pthread_mutex_unlock(&rec_mutex);
2543 break;
2544 // }
2545 // }
2550 int main_keypress_menu(){
2551 clear();
2552 box(stdscr,0,0);
2553 mvaddstr(0,2," Keypresses for main menu ");
2554 attron(A_BOLD);
2555 mvaddstr(2,2, " ?");
2556 mvaddstr(4,2, " space");
2557 mvaddstr(5,2, " up/down");
2558 mvaddstr(6,2, "backspace");
2559 mvaddstr(8,2, " a");
2560 mvaddstr(9,2, " e");
2561 mvaddstr(10,2," d");
2562 mvaddstr(11,2," o");
2563 mvaddstr(12,2," l");
2564 mvaddstr(14,2," s");
2565 mvaddstr(15,2," +/-");
2566 mvaddstr(16,2," H");
2567 mvaddstr(17,2," R");
2569 attroff(A_BOLD);
2570 mvaddstr(2,12,"keypress menu (you're there now)");
2571 mvaddstr(4,12,"play next cue");
2572 mvaddstr(5,12,"move cursor to prev/next cue");
2573 mvaddstr(6,12,"move cursor to previous cue");
2574 mvaddstr(8,12,"add new cue at current cursor position");
2575 mvaddstr(9,12,"edit cue at current cursor position");
2576 mvaddstr(10,12,"delete cue at current cursor position");
2577 mvaddstr(11,12,"output channel configuration");
2578 mvaddstr(12,12,"lock non-modifiable mode (production)");
2580 mvaddstr(14,12,"save program");
2581 mvaddstr(15,12,"master volume up/down");
2582 mvaddstr(16,12,"halt playback");
2583 mvaddstr(17,12,"record to disk");
2585 mvaddstr(19,12,"any key to return to main menu");
2586 refresh();
2587 mgetch();
2588 return(MENU_MAIN);
2591 int add_cue_menu(){
2592 form f;
2594 char label[13]="";
2595 char text[61]="";
2596 char desc[61]="";
2598 form_init(&f,4);
2599 clear();
2600 box(stdscr,0,0);
2601 mvaddstr(0,2," Add new cue ");
2603 mvaddstr(2,2," cue label");
2604 mvaddstr(3,2," cue text");
2605 mvaddstr(4,2,"cue description");
2607 field_add(&f,FORM_GENERIC,18,2,12,label);
2608 field_add(&f,FORM_GENERIC,18,3,59,text);
2609 field_add(&f,FORM_GENERIC,18,4,59,desc);
2611 field_add(&f,FORM_BUTTON,68,6,9,"ADD CUE");
2613 refresh();
2615 while(1){
2616 int ch=form_handle_char(&f,mgetch());
2617 switch(ch){
2618 case KEY_ENTER:case 'x':case 'X':
2619 goto enter;
2620 case 27: /* esc */
2621 goto out;
2625 enter:
2627 /* determine where in list cue is being added */
2628 cue c;
2630 unsaved=1;
2631 memset(&c,0,sizeof(c));
2632 c.tag=-1; /* placeholder cue for this cue bank */
2633 /* acquire label locks, populate */
2634 c.label=new_label_number();
2635 edit_label(c.label,label);
2636 acquire_label(c.label);
2637 c.cue_text=new_label_number();
2638 edit_label(c.cue_text,text);
2639 acquire_label(c.cue_text);
2640 c.cue_desc=new_label_number();
2641 edit_label(c.cue_desc,desc);
2642 acquire_label(c.cue_desc);
2644 add_cue(cue_list_number,c);
2645 move_next_cue();
2647 out:
2648 form_clear(&f);
2649 return(MENU_MAIN);
2653 void edit_keypress_menu(){
2654 clear();
2655 box(stdscr,0,0);
2656 mvaddstr(0,2," Keypresses for cue edit menu ");
2657 attron(A_BOLD);
2658 mvaddstr(2,2, " ?");
2659 mvaddstr(3,2, " up/down");
2660 mvaddstr(4,2, " tab");
2661 mvaddstr(5,2, " x");
2663 mvaddstr(7,2, " a");
2664 mvaddstr(8,2, " m");
2665 mvaddstr(9,2, " d");
2666 mvaddstr(10,2," enter");
2667 mvaddstr(11,2," l");
2668 mvaddstr(12,2," space");
2669 mvaddstr(13,2," H");
2671 attroff(A_BOLD);
2672 mvaddstr(2,12,"keypress menu (you're there now)");
2673 mvaddstr(3,12,"move cursor to prev/next field");
2674 mvaddstr(4,12,"move cursor to next field");
2675 mvaddstr(5,12,"return to main menu");
2676 mvaddstr(7,12,"add new sample to cue");
2677 mvaddstr(8,12,"add new mixer change to cue");
2678 mvaddstr(9,12,"delete highlighted action");
2679 mvaddstr(10,12,"edit highlighted action");
2680 mvaddstr(11,12,"list all samples and sample tags");
2681 mvaddstr(12,12,"play cue");
2682 mvaddstr(13,12,"halt playback");
2684 mvaddstr(15,12,"any key to return to cue edit menu");
2685 refresh();
2686 mgetch();
2689 void edit_sample_menu(int n){
2690 int i,j;
2691 cue *c=cue_list+n;
2692 tag *t=tag_list+cue_list[n].tag;
2693 char tdesc[61]="";
2694 char buf[82];
2695 form f;
2697 clear();
2698 box(stdscr,0,0);
2699 mvaddstr(0,2," Add/Edit sample cue ");
2701 mvaddstr(10,2,"loop crosslap ms");
2702 mvaddstr(11,2,"volume master % fade / ms");
2704 form_init(&f,7+MAX_OUTPUT_CHANNELS*t->channels);
2705 strcpy(tdesc,label_text(t->sample_desc));
2707 field_add(&f,FORM_GENERIC,18,7,59,tdesc);
2709 field_add(&f,FORM_YESNO,18,10,5,&t->loop_p);
2710 field_add(&f,FORM_NUMBER,38,10,7,&t->loop_lapping);
2712 field_add(&f,FORM_PERCENTAGE,18,11,5,&c->mix.vol_master);
2713 field_add(&f,FORM_NUMBER,30,11,7,&c->mix.vol_ms);
2714 field_add(&f,FORM_NUMBER,38,11,7,&t->fade_out);
2716 mvaddstr(2,2,"cue label ");
2717 addnlstr(label_text(c->label),59,' ');
2718 mvaddstr(3,2,"cue text ");
2719 addnlstr(label_text(c->cue_text),59,' ');
2720 mvaddstr(4,2,"cue description ");
2721 addnlstr(label_text(c->cue_desc),59,' ');
2723 mvaddstr(6,2,"sample path ");
2724 addnlstr(label_text(t->sample_path),59,' ');
2726 mvaddstr(7,2,"sample desc");
2727 mvaddstr(8,2,"sample tag ");
2728 sprintf(buf,"%d",c->tag);
2729 addstr(buf);
2731 mvhline(13,3,0,74);
2732 mvhline(14+MAX_OUTPUT_CHANNELS,3,0,74);
2733 mvvline(14,2,0,MAX_OUTPUT_CHANNELS);
2734 mvvline(14,77,0,MAX_OUTPUT_CHANNELS);
2735 mvaddch(13,2,ACS_ULCORNER);
2736 mvaddch(13,77,ACS_URCORNER);
2737 mvaddch(14+MAX_OUTPUT_CHANNELS,2,ACS_LLCORNER);
2738 mvaddch(14+MAX_OUTPUT_CHANNELS,77,ACS_LRCORNER);
2740 if(t->channels==2){
2741 mvaddstr(13,5," L ");
2742 mvaddstr(13,10," R ");
2743 }else{
2744 for(i=0;i<t->channels;i++){
2745 mvaddch(13,6+i*5,' ');
2746 addch(48+i);
2747 addch(' ');
2751 for(i=0;i<MAX_OUTPUT_CHANNELS;i++){
2752 for(j=0;j<t->channels && j<MAX_FILECHANNELS;j++)
2753 field_add(&f,FORM_PERCENTAGE,4+j*5,14+i,5,&c->mix.outvol[j][i]);
2754 sprintf(buf,"%% ->%2d ",i);
2755 mvaddstr(14+i,4+t->channels*5,buf);
2756 addnlstr(channel_list[i].label,76-11-t->channels*5,' ');
2759 field_add(&f,FORM_BUTTON,61,17+MAX_OUTPUT_CHANNELS,16,"ACCEPT CHANGES");
2762 #if 0
2763 Add/edit sample to cue -------------------------------------------------------
2765 cue label [ ]
2766 cue text [ ]
2767 cue description [ ]
2769 sample path [ ]
2770 sample desc [ ]
2771 sample tag 0
2773 loop [No ] crosslap [----]ms
2774 volume master [100]% fade [ 15]/[ 15]ms
2776 --- L -- R -----------------------------------------------------------------
2777 | [100][ 0]% -> 0 offstage left |
2778 | [ 0][100]% -> 1 offstage right |
2779 | [ 0][ 0]% -> 2 |
2780 | [ 0][ 0]% -> 3 |
2781 | [ 0][ 0]% -> 4 |
2782 | [ 0][ 0]% -> 5 |
2783 | [ 0][ 0]% -> 6 |
2784 | [ 0][ 0]% -> 7 |
2785 ----------------------------------------------------------------------------
2787 #endif
2789 while(1){
2790 int ch=form_handle_char(&f,getch());
2792 switch(ch){
2793 case ' ':
2794 play_sample(n);
2795 break;
2796 case 'H':
2797 halt_playback();
2798 break;
2799 case 27:
2800 goto out;
2801 case 'x':case 'X':case KEY_ENTER:
2802 unsaved=1;
2803 edit_label(t->sample_desc,tdesc);
2804 goto out;
2807 out:
2808 form_clear(&f);
2811 void edit_mix_menu(int n){
2812 int i,j;
2813 cue *c=cue_list+n;
2814 tag *t=tag_list+cue_list[n].tag;
2815 char tdesc[61]="";
2816 char buf[82];
2817 form f;
2819 clear();
2820 box(stdscr,0,0);
2821 mvaddstr(0,2," Add/Edit mix cue ");
2823 mvaddstr(10,2,"volume master % fade ms");
2825 form_init(&f,3+MAX_OUTPUT_CHANNELS*t->channels);
2826 strcpy(tdesc,label_text(t->sample_desc));
2828 field_add(&f,FORM_PERCENTAGE_R,18,10,5,&c->mix.vol_master);
2829 field_add(&f,FORM_NUMBER,30,10,7,&c->mix.vol_ms);
2831 mvaddstr(2,2,"cue label ");
2832 addnlstr(label_text(c->label),59,' ');
2833 mvaddstr(3,2,"cue text ");
2834 addnlstr(label_text(c->cue_text),59,' ');
2835 mvaddstr(4,2,"cue description ");
2836 addnlstr(label_text(c->cue_desc),59,' ');
2838 mvaddstr(6,2,"sample path ");
2839 addnlstr(label_text(t->sample_path),59,' ');
2841 mvaddstr(7,2,"sample desc ");
2842 addnlstr(label_text(t->sample_desc),59,' ');
2843 mvaddstr(8,2,"sample tag ");
2844 sprintf(buf,"%d",c->tag);
2845 addstr(buf);
2847 mvhline(12,3,0,74);
2848 mvhline(13+MAX_OUTPUT_CHANNELS,3,0,74);
2849 mvvline(13,2,0,MAX_OUTPUT_CHANNELS);
2850 mvvline(13,77,0,MAX_OUTPUT_CHANNELS);
2851 mvaddch(12,2,ACS_ULCORNER);
2852 mvaddch(12,77,ACS_URCORNER);
2853 mvaddch(13+MAX_OUTPUT_CHANNELS,2,ACS_LLCORNER);
2854 mvaddch(13+MAX_OUTPUT_CHANNELS,77,ACS_LRCORNER);
2856 if(t->channels==2){
2857 mvaddstr(12,5," L ");
2858 mvaddstr(12,10," R ");
2859 }else{
2860 for(i=0;i<t->channels;i++){
2861 mvaddch(12,6+i*5,' ');
2862 addch(48+i);
2863 addch(' ');
2867 for(i=0;i<MAX_OUTPUT_CHANNELS;i++){
2868 for(j=0;j<t->channels && j<MAX_FILECHANNELS;j++)
2869 field_add(&f,FORM_PERCENTAGE,4+j*5,13+i,5,&c->mix.outvol[j][i]);
2870 sprintf(buf,"%% ->%2d ",i);
2871 mvaddstr(13+i,4+t->channels*5,buf);
2872 addnlstr(channel_list[i].label,76-11-t->channels*5,' ');
2875 field_add(&f,FORM_BUTTON,61,16+MAX_OUTPUT_CHANNELS,16,"ACCEPT CHANGES");
2878 #if 0
2879 Add/edit mix change ---------------------------------------------------------
2881 cue label [ ]
2882 cue text [ ]
2883 cue description [ ]
2885 modify tag [ 0]
2887 volume master [100]% fade [ 15]
2889 --- L -- R -----------------------------------------------------------------
2890 | [100][ 0]% -> 0 offstage left |
2891 | [ 0][100]% -> 1 offstage right |
2892 | [ 0][ 0]% -> 2 |
2893 | [ 0][ 0]% -> 3 |
2894 | [ 0][ 0]% -> 4 |
2895 | [ 0][ 0]% -> 5 |
2896 | [ 0][ 0]% -> 6 |
2897 | [ 0][ 0]% -> 7 |
2898 ----------------------------------------------------------------------------
2900 (l: list tags)
2902 #endif
2904 while(1){
2905 int ch=form_handle_char(&f,getch());
2907 switch(ch){
2908 case 27:
2909 goto out;
2910 case 'x':case 'X':case KEY_ENTER:
2911 unsaved=1;
2912 goto out;
2915 out:
2916 form_clear(&f);
2920 int add_sample_menu(){
2921 /* two-stage... get sample first so the mixer is accurate */
2922 tag t;
2923 tag_number tagno;
2925 form f;
2926 char path[64]="";
2928 clear();
2929 box(stdscr,0,0);
2930 mvaddstr(0,2," Add new sample to cue ");
2932 form_init(&f,1);
2933 mvaddstr(2,2,"sample path");
2934 field_add(&f,FORM_GENERIC,14,2,62,path);
2935 f.edit=1;
2936 form_redraw(&f);
2938 while(1){
2939 int ch=mgetch();
2940 int re=form_handle_char(&f,ch);
2942 if(re>=0 || !f.edit){
2943 if(ch==27){
2944 form_clear(&f);
2945 return(MENU_EDIT);
2948 /* try to load the sample! */
2949 switch_to_stderr();
2950 memset(&t,0,sizeof(t));
2951 if(load_sample(&t,path)){
2952 fprintf(stderr,"Press enter to continue\n");
2953 getc(stdin);
2954 switch_to_ncurses();
2955 f.edit=1;
2956 }else{
2957 switch_to_ncurses();
2958 break;
2963 unsaved=1;
2965 /* finish the tag */
2967 t.sample_path=new_label_number();
2968 acquire_label(t.sample_path);
2969 edit_label(t.sample_path,path);
2971 t.sample_desc=new_label_number();
2972 acquire_label(t.sample_desc);
2973 edit_label(t.sample_desc,"");
2974 t.loop_p=0;
2975 t.loop_start=0;
2976 t.loop_lapping=1000;
2978 t.fade_out=15;
2980 tagno=new_tag_number();
2981 edit_tag(tagno,t);
2982 acquire_tag(tagno);
2985 /* got it, add a new cue */
2987 cue c;
2989 acquire_label(c.label=cue_list[cue_list_number].label);
2990 acquire_label(c.cue_text=cue_list[cue_list_number].cue_text);
2991 acquire_label(c.cue_desc=cue_list[cue_list_number].cue_desc);
2992 c.tag=tagno;
2993 c.tag_create_p=1;
2994 memset(&c.mix,0,sizeof(mix));
2995 c.mix.vol_master=100;
2996 c.mix.vol_ms=10;
2997 c.mix.outvol[0][0]=100;
2998 c.mix.outvol[1][1]=100;
3000 add_cue(cue_list_number+1,c);
3003 /* go to main edit menu */
3004 edit_sample_menu(cue_list_number+1);
3006 return(MENU_EDIT);
3009 int tagno_to_cueno(int tagno){
3010 int i;
3012 for(i=0;i<cue_count;i++)
3013 if(cue_list[i].tag==tagno)return(i);
3015 return(-1);
3018 int add_mix_menu(){
3019 tag_number tagno=0;
3021 form f;
3023 clear();
3024 box(stdscr,0,0);
3025 mvaddstr(0,2," Add mixer change to cue ");
3027 form_init(&f,1);
3028 mvaddstr(2,2,"tag number ");
3029 field_add(&f,FORM_NUMBER,13,2,12,&tagno);
3030 f.edit=1;
3031 form_redraw(&f);
3033 mvaddstr(4,2,"'");
3034 attron(A_BOLD);
3035 addstr("l");
3036 attroff(A_BOLD);
3037 addstr("' for a list of sample tags ");
3039 while(1){
3040 int ch=mgetch();
3041 int re=form_handle_char(&f,ch);
3043 if(re>=0 || !f.edit){
3044 if(ch==27){
3045 form_clear(&f);
3046 return(MENU_EDIT);
3048 if(tagno>=tag_count || tag_list[tagno].refcount<=0){
3049 mvaddstr(4,2,"Bad tag number; any key to continue.");
3050 mgetch();
3051 mvaddstr(4,2,"'");
3052 attron(A_BOLD);
3053 addstr("l");
3054 attroff(A_BOLD);
3055 addstr("' for a list of sample tags ");
3056 f.edit=1;
3057 form_redraw(&f);
3058 }else
3059 break;
3063 /* add the new cue */
3064 /* prefill with original mix data from sample */
3066 cue c;
3067 int i,j;
3068 int createno=tagno_to_cueno(tagno);
3069 unsaved=1;
3071 acquire_tag(tagno);
3072 acquire_label(c.label=cue_list[cue_list_number].label);
3073 acquire_label(c.cue_text=cue_list[cue_list_number].cue_text);
3074 acquire_label(c.cue_desc=cue_list[cue_list_number].cue_desc);
3075 c.tag=tagno;
3076 c.tag_create_p=0;
3077 memset(&c.mix,0,sizeof(mix));
3078 if(createno>-1){
3079 c.mix.vol_master=cue_list[createno].mix.vol_master;
3080 c.mix.vol_ms=cue_list[createno].mix.vol_ms;
3081 for(j=0;j<MAX_FILECHANNELS;j++)
3082 for(i=0;i<MAX_OUTPUT_CHANNELS;i++)
3083 c.mix.outvol[j][i]=cue_list[createno].mix.outvol[j][i];
3085 add_cue(cue_list_number+1,c);
3088 /* go to main edit menu */
3089 edit_mix_menu(cue_list_number+1);
3091 return(MENU_EDIT);
3094 int edit_cue_menu(){
3095 form f;
3096 char label[12]="";
3097 char text[61]="";
3098 char desc[61]="";
3100 /* determine first and last cue in bank */
3101 int base=cue_list_number;
3102 int first=base+1;
3103 int last=first;
3104 int actions,i;
3106 while(last<cue_count && cue_list[last].tag!=-1)last++;
3107 actions=last-first;
3109 form_init(&f,4+actions);
3110 strcpy(label,label_text(cue_list[base].label));
3111 strcpy(text,label_text(cue_list[base].cue_text));
3112 strcpy(desc,label_text(cue_list[base].cue_desc));
3114 field_add(&f,FORM_GENERIC,18,2,12,label);
3115 field_add(&f,FORM_GENERIC,18,3,59,text);
3116 field_add(&f,FORM_GENERIC,18,4,59,desc);
3118 for(i=0;i<actions;i++){
3119 char *buf=alloca(81);
3120 int tag=cue_list[first+i].tag;
3121 snprintf(buf,80,"%s sample %d (%s)",
3122 cue_list[first+i].tag_create_p?"ADD":"MIX",tag,
3123 label_text(tag_list[tag].sample_path));
3124 field_add(&f,FORM_BUTTON,11,6+i,66,buf);
3126 if(actions==0){
3127 mvaddstr(6,11,"--None--");
3128 i++;
3130 field_add(&f,FORM_BUTTON,66,7+i,11,"MAIN MENU");
3132 while(1){
3133 int loop=1;
3134 clear();
3135 box(stdscr,0,0);
3136 mvaddstr(0,2," Cue edit ");
3138 mvaddstr(2,2," cue label");
3139 mvaddstr(3,2," cue text");
3140 mvaddstr(4,2,"cue description");
3141 mvaddstr(6,2,"actions:");
3142 form_redraw(&f);
3144 while(loop){
3145 int ch=form_handle_char(&f,mgetch());
3147 switch(ch){
3148 case '?':case '/':
3149 edit_keypress_menu();
3150 loop=0;
3151 break;
3152 case 27:
3153 goto out;
3154 case 'x':case 'X':
3155 unsaved=1;
3156 edit_label(cue_list[base].label,label);
3157 edit_label(cue_list[base].cue_text,text);
3158 edit_label(cue_list[base].cue_desc,desc);
3159 goto out;
3160 case 'a':case 'A':
3161 add_sample_menu();
3162 form_clear(&f);
3163 return(MENU_EDIT);
3164 case 'm':case 'M':
3165 add_mix_menu();
3166 form_clear(&f);
3167 return(MENU_EDIT);
3168 case 'd': case 'D':
3169 if(actions>0){
3170 if(f.cursor>=3 && f.cursor<3+actions){
3171 int n=first+f.cursor-3;
3172 unsaved=1;
3173 delete_cue_single(n);
3174 form_clear(&f);
3175 return(MENU_EDIT);
3178 break;
3179 case 'l': case 'L':
3180 //list_tag_menu();
3181 loop=0;
3182 break;
3183 case ' ':
3184 play_cue(cue_list_number);
3185 break;
3186 case 'H':
3187 halt_playback();
3188 break;
3189 case KEY_ENTER:
3190 if(f.cursor==3+actions){
3191 unsaved=1;
3192 edit_label(cue_list[base].label,label);
3193 edit_label(cue_list[base].cue_text,text);
3194 edit_label(cue_list[base].cue_desc,desc);
3195 goto out;
3197 /* ... else we're an action edit */
3198 if(cue_list[first+f.cursor-3].tag_create_p)
3199 edit_sample_menu(first+f.cursor-3);
3200 else
3201 edit_mix_menu(first+f.cursor-3);
3202 loop=0;
3203 break;
3208 out:
3209 form_clear(&f);
3210 return(MENU_MAIN);
3213 int menu_output(){
3214 return(MENU_MAIN);
3217 int main_loop(){
3219 cache_cue(0);
3220 wake_cache();
3222 while(running){
3223 switch(menu){
3224 case MENU_MAIN:
3225 menu=main_menu();
3226 break;
3227 case MENU_KEYPRESS:
3228 menu=main_keypress_menu();
3229 break;
3230 case MENU_QUIT:
3231 running=0;
3232 break;
3233 case MENU_ADD:
3234 menu=add_cue_menu();
3235 break;
3236 case MENU_OUTPUT:
3237 menu=menu_output();
3238 break;
3239 case MENU_EDIT:
3240 menu=edit_cue_menu();
3241 break;
3244 return 0;
3247 pthread_mutex_t pipe_mutex=PTHREAD_MUTEX_INITIALIZER;
3249 void *tty_thread(void *dummy){
3250 char buf;
3252 while(1){
3253 int ret=read(ttyfd,&buf,1);
3254 if(ret==1){
3255 write(ttypipe[1],&buf,1);
3258 if(playback_exit)break;
3260 return NULL;
3263 int main(int gratuitously,char *different[]){
3264 int lf;
3266 if(gratuitously<2){
3267 fprintf(stderr,"Usage: beaverphonic <settingfile>\n");
3268 exit(1);
3271 mkdir(tempdir,0777);
3273 /* lock against other instances */
3274 lf=open(lockfile,O_CREAT|O_RDWR,0770);
3275 if(lf<0){
3276 fprintf(stderr,"unable to open lock file: %s.\n",strerror(errno));
3277 exit(1);
3280 if(flock(lf,LOCK_EX|LOCK_NB)){
3281 fprintf(stderr,"Another Beaverphonic process is running.\n"
3282 " (could not acquire lockfile)\n");
3283 exit(1);
3286 playfd=fopen("/dev/dsp1","wb");
3287 if(!playfd){
3288 fprintf(stderr,"unable to open audio device for playback: %s.\n",strerror(errno));
3289 fprintf(stderr,"\nPress enter to continue\n");
3290 getc(stdin);
3293 recfd=fopen("/dev/dsp1","rb");
3294 if(!recfd){
3295 fprintf(stderr,"unable to open audio device fo record: %s.\n",strerror(errno));
3296 fprintf(stderr,"\nPress enter to continue\n");
3297 getc(stdin);
3301 /* set up the hack for interthread ncurses event triggering through
3302 input subversion */
3303 ttyfd=open("/dev/tty",O_RDONLY);
3305 if(ttyfd<0){
3306 fprintf(stderr,"Unable to open /dev/tty:\n"
3307 " %s\n",strerror(errno));
3309 exit(1);
3311 if(pipe(ttypipe)){
3312 fprintf(stderr,"Unable to open tty pipe:\n"
3313 " %s\n",strerror(errno));
3315 exit(1);
3317 dup2(ttypipe[0],0);
3319 pthread_create(&tty_thread_id,NULL,tty_thread,NULL);
3322 pthread_t dummy;
3323 playback_active=1;
3324 pthread_create(&playback_thread_id,NULL,playback_thread,NULL);
3325 pthread_create(&cache_thread_id,NULL,cache_thread,NULL);
3328 pthread_t dummy;
3329 rec_active1=1;
3330 rec_active2=1;
3331 pthread_create(&record_thread_id,NULL,record_thread,NULL);
3334 pthread_create(&cache_thread_id,NULL,cache_thread,NULL);
3336 /* load the sound config if the file exists, else create it */
3337 initscr(); cbreak(); noecho();
3338 nonl();
3339 intrflush(stdscr, FALSE);
3340 keypad(stdscr, TRUE);
3341 use_default_colors();
3342 signal(SIGINT,SIG_IGN);
3344 clear();
3345 switch_to_stderr();
3346 program=strdup(different[1]);
3348 FILE *f=fopen(program,"rb");
3349 if(f){
3350 if(load_program(f)){
3351 fprintf(stderr,"\nPress enter to continue\n");
3352 getc(stdin);
3354 fclose(f);
3357 switch_to_ncurses();
3359 main_thread_id=pthread_self();
3361 main_loop();
3362 endwin(); /* restore original tty modes */
3363 close(lf);
3364 halt_playback();
3365 playback_exit=1;
3366 pthread_mutex_lock(&rec_mutex);
3367 rec_exit=1;
3368 pthread_mutex_unlock(&rec_mutex);
3370 /* wake the record producer/consumer if either is waiting */
3371 pthread_mutex_lock(&rec_buffer_mutex);
3372 pthread_cond_broadcast(&rec_buffer_cond);
3373 pthread_mutex_unlock(&rec_buffer_mutex);
3375 while(1){
3376 if(!playback_active)break;
3377 sched_yield();
3380 while(1){
3381 pthread_mutex_lock(&rec_mutex);
3382 if(!rec_active1 && !rec_active2)break;
3383 pthread_mutex_unlock(&rec_mutex);
3384 sched_yield();
3386 pthread_mutex_unlock(&rec_mutex);
3387 fclose(playfd);
3388 fclose(recfd);
3390 unlink(lockfile);
3391 return 0;
3395 #if 0
3397 OUTPUT CHANNELS
3399 0: [ ] built in OSS left
3400 1: [ ] built in OSS right
3401 2: [ ] Quattro 0
3404 #endif