1 /* Assumptions (Bad things the code does that work here, but might not
4 the atomic execution sections are a hack that can only work on uniprocessor
10 make Esc behavior correct in editing menus by using temp cue/tag storage
15 proper printing out of file loading issues
17 allow sample name/tag change in edit menus */
18 #define CACHE_SAMPLES 44100*10
29 #include <sys/types.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 \
55 struct sched_param param; \
56 pthread_getschedparam(pthread_self(), &original_policy, ¶m); \
57 if(param.sched_priority==90){ \
58 fprintf(stderr,"ATOMIC sections do not stack at line %ld\n",__LINE__); \
61 original_priority=param.sched_priority; \
62 param.sched_priority=90; \
63 pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m); \
68 struct sched_param param; \
69 param.sched_priority=original_priority; \
70 pthread_setschedparam(pthread_self(), original_policy, ¶m); \
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;
91 static int playback_buffer_minfill
=0;
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;
99 static FILE *playfd
=NULL
;
100 static FILE *recfd
=NULL
;
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
){
118 return(malloc(bytes
));
119 return(realloc(in
,bytes
));
122 void addnlstr(const char *s
,int n
,char c
){
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 */
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;
153 char label
[CHANNEL_LABEL_LENGTH
];
155 /* real stuff not here yet */
158 static outchannel channel_list
[MAX_OUTPUT_CHANNELS
]={
166 static outchannel rchannel_list
[2]={
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) */
180 typedef int label_number
;
182 static label
*label_list
;
183 static int label_count
;
185 int new_label_number(){
187 for(i
=0;i
<label_count
;i
++)
188 if(!label_list
[i
].refcount
)break;
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
){
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
){
218 number
>=label_count
||
219 label_list
[number
].refcount
==0 ||
220 label_list
[number
].text
==NULL
)
222 return label_list
[number
].text
;
225 void release_label(int number
){
227 if(number
>=label_count
)return;
228 label
=label_list
+number
;
230 if(label
->refcount
<0)
234 int save_label(FILE *f
,int number
){
236 if(number
<=label_count
){
237 if(label_list
[number
].refcount
){
238 fprintf(f
,"LBL:%d %d:%s",number
,
239 strlen(label_text(number
)),
242 count
=fprintf(f
,"\n");
243 if(count
<1)return(-1);
249 int save_labels(FILE *f
){
251 for(i
=label_count
-1;i
>=0;i
--)
252 if(save_label(f
,i
))return(-1);
256 int load_label(FILE *f
){
257 int len
,count
,number
;
259 count
=fscanf(f
,":%d %d:",&number
,&len
);
261 addnlstr("LOAD ERROR (LBL): too few fields.",80,' ');
265 count
=fread(temp
,1,len
,f
);
268 addnlstr("LOAD ERROR (LBL): EOF reading string.",80,' ');
271 edit_label(number
,temp
);
275 /************* tag abstraction **********************/
277 typedef int tag_number
;
282 label_number sample_path
;
283 label_number sample_desc
;
298 long sample_position
;
300 long sample_loop_start
;
301 long sample_fade_start
;
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
];
315 struct sample_header
{
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(){
331 for(i
=0;i
<tag_count
;i
++)
332 if(!tag_list
[i
].refcount
)break;
336 static void _alloc_tag_if_needed(int number
){
337 if(number
>=tag_count
){
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
));
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
356 int load_sample(tag
*t
,const char *path
){
357 char *template=alloca(strlen(tempdir
)+20);
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);
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
);
379 _exit(system(buffer
));
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
;
393 fprintf(stderr
,"Failed to open converted file %s:\n\t(%s)\n",
394 tmp
,strerror(errno
));
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
)));
407 fprintf(stderr
,"Unable to determine length of file!\n\t%s\n",
408 strerror(ferror(cheat
)));
412 count
=fread(&head
,sizeof(head
),1,cheat
);
416 fprintf(stderr
,"Conversion file %s openable, but truncated\n",tmp
);
420 fprintf(stderr
,"Conversion file created by incorrect "
421 "version of convert.pl\n\t%s unreadable\n",tmp
);
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
);
432 fprintf(stderr
,"Unable to open %s fd for mmap:\n\t%d (%s)\n",
433 tmp
,errno
,strerror(errno
));
438 t
->samplelength
*sizeof(int16
)*t
->channels
+sizeof(head
),
439 PROT_READ
,MAP_PRIVATE
,fd
,0);
443 fprintf(stderr
,"Unable to mmap fd %d (%s):\n\t%d (%s)\n",
444 fd
,tmp
,errno
,strerror(errno
));
447 t
->data
=t
->basemap
+sizeof(head
);
448 if(madvise(t
->basemap
,
449 t
->samplelength
*sizeof(int16
)*t
->channels
+sizeof(head
),
451 fprintf(stderr
,"madvise() failed for %s mmap:\n\t%d (%s)\n",
452 tmp
,errno
,strerror(errno
));
456 fprintf(stderr
,"\tDONE\n\n");
463 int unload_sample(tag
*t
){
464 /* is this sample currently playing back? */
467 for(i
=0;i
<active_count
;i
++)
468 if(active_list
[i
]==t
)_playback_remove(i
);
471 for(i
=0;i
<cache_count
;i
++)
472 if(cache_list
[i
]==t
)_cache_remove(i
);
475 pthread_mutex_lock(&mmap_mutex
);
478 t
->samplelength
*sizeof(int16
)*
479 t
->channels
+sizeof(struct sample_header
));
485 pthread_mutex_unlock(&mmap_mutex
);
490 int edit_tag(int number
,tag t
){
491 _alloc_tag_if_needed(number
);
495 /* state is zeroed when we go to production mode. Done */
499 void acquire_tag(int number
){
501 _alloc_tag_if_needed(number
);
502 tag_list
[number
].refcount
++;
505 void release_tag(int number
){
507 if(number
>=tag_count
)return;
508 tag_list
[number
].refcount
--;
509 if(tag_list
[number
].refcount
==0){
510 tag
*t
=tag_list
+number
;
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
){
528 if(number
>=tag_count
)return(0);
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
,
535 count
=fprintf(f
,"\n");
536 if(count
<1)return(-1);
541 int save_tags(FILE *f
){
543 for(i
=tag_count
-1;i
>=0;i
--){
544 if(save_tag(f
,i
))return(-1);
549 int load_tag(FILE *f
){
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
);
558 addnlstr("LOAD ERROR (TAG): too few fields.",80,' ');
561 acquire_label(t
.sample_path
);
562 if(load_sample(&t
,label_text(t
.sample_path
))){
563 release_label(t
.sample_path
);
567 acquire_label(t
.sample_desc
);
572 /********************* cue abstraction ********************/
578 int outvol
[MAX_FILECHANNELS
][MAX_OUTPUT_CHANNELS
];
583 label_number cue_text
;
584 label_number cue_desc
;
591 static cue
*cue_list
;
595 void add_cue(int n
,cue c
){
597 if(cue_count
==cue_storage
){
601 cue_list
=m_realloc(cue_list
,cue_storage
*sizeof(*cue_list
));
608 memmove(cue_list
+n
+1,cue_list
+n
,sizeof(*cue_list
)*(cue_count
-n
));
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
){
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
);
626 memmove(cue_list
+n
,cue_list
+n
+1,sizeof(*cue_list
)*(cue_count
-n
));
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
){
637 if(n
>=0 && n
<cue_count
){
639 tag_number tag
=c
->tag
;
642 /* tag creation cue; have to delete following cues matching this
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 */
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
){
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
,
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);
686 int save_cues(FILE *f
){
688 for(i
=0;i
<cue_count
;i
++)
689 if(save_cue(f
,i
))return(-1);
693 int load_cue(FILE *f
){
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
,
704 &maxchannels
,&maxwavch
);
706 addnlstr("LOAD ERROR (CUE): too few fields.",80,' ');
710 if(c
.tag
>=tag_count
||
711 !tag_list
[c
.tag
].basemap
){
712 addnlstr("LOAD ERROR (CUE): references a bad TAG",80,' ');
717 for(i
=0;i
<maxchannels
;i
++){
720 count
=fscanf(f
,"%c",&ch
);
721 if(count
<0 || ch
!='<'){
722 addnlstr("LOAD ERROR (CUE): parse error looking for '<'.",80,' ');
725 for(j
=0;j
<maxwavch
;j
++){
726 count
=fscanf(f
,"%d",&v
);
728 addnlstr("LOAD ERROR (CUE): parse error looking for value.",80,' ');
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,' ');
741 acquire_label(c
.label
);
742 acquire_label(c
.cue_text
);
743 acquire_label(c
.cue_desc
);
746 add_cue(cue_count
,c
);
751 int load_val(FILE *f
, long *val
){
754 count
=fscanf(f
,": %ld",&t
);
756 addnlstr("LOAD ERROR (VAL): missing field.",80,' ');
763 int save_val(FILE *f
, char *p
, long val
){
765 fprintf(f
,"%s: %ld",p
,val
);
766 count
=fprintf(f
,"\n");
767 if(count
<1)return(-1);
771 int load_program(FILE *f
){
779 if(fread(buf
,1,3,f
)<3){
780 addnlstr("LOAD ERROR: truncated program.",80,' ');
784 if(!strncmp(buf
,"TAG",3)){
786 }else if(!strncmp(buf
,"CUE",3)){
788 }else if(!strncmp(buf
,"LBL",3)){
790 }else if(!strncmp(buf
,"MAS",3)){
791 ret
|=load_val(f
,&main_master_volume
);
796 if(count
=='\n' || count
==EOF
)break;
801 if(count
!='\n' || count
==EOF
)break;
813 int save_program(FILE *f
){
818 ret
|=save_val(f
,"MAS",main_master_volume
);
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
;
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
){
849 fputc((num
>>(i
<<3))&0xff,f
);
853 void WriteWav(FILE *f
,long channels
,long rate
,long bits
,long duration
){
856 PutNumLE(duration
+44-8,f
,4);
857 fprintf(f
,"WAVEfmt ");
860 PutNumLE(channels
,f
,2);
862 PutNumLE(rate
*channels
*((bits
-1)/8+1),f
,4);
863 PutNumLE(((bits
-1)/8+1)*channels
,f
,2);
866 PutNumLE(duration
,f
,4);
869 void *record_disk_thread(void *dummy
){
870 FILE *recdiskfd
=NULL
;
874 /* open a file; name the capture by time/date */
880 pthread_mutex_lock(&rec_mutex
);
881 if(!rec_flush_req
&& recdiskfd
){
882 WriteWav(recdiskfd
,2,44100,REC_SAMPLE_BYTES
*8,filesize
);
887 pthread_mutex_unlock(&rec_mutex
);
890 /* lock and check count; wait if none to flush */
891 pthread_mutex_lock(&rec_buffer_mutex
);
894 pthread_cond_wait(&rec_buffer_cond
,&rec_buffer_mutex
);
895 pthread_mutex_unlock(&rec_buffer_mutex
);
896 pthread_mutex_lock(&rec_mutex
);
898 pthread_mutex_unlock(&rec_buffer_mutex
);
899 pthread_mutex_lock(&rec_mutex
);
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
);
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
);
927 pthread_mutex_lock(&rec_mutex
);
928 if(filesize
>10*60*44100*2*3)break;
929 if(rec_flush_req
&& !recdiskfd
)break;
931 pthread_mutex_unlock(&rec_mutex
);
936 pthread_mutex_unlock(&rec_mutex
);
939 WriteWav(recdiskfd
,2,44100,REC_SAMPLE_BYTES
*8,filesize
);
944 ret
=stat("record",&buf
);
946 mkdir("record",0700);
947 ret
=stat("record",&buf
);
949 if(!ret
&& S_ISDIR(buf
.st_mode
)){
950 /* construct a new filename */
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");
961 WriteWav(recdiskfd
,2,44100,REC_SAMPLE_BYTES
*8,-1);
962 pthread_mutex_lock(&rec_mutex
);
966 pthread_mutex_lock(&rec_mutex
);
969 pthread_mutex_unlock(&rec_mutex
);
974 WriteWav(recdiskfd
,2,44100,REC_SAMPLE_BYTES
*8,filesize
);
979 fprintf(stderr
,"Record flush thread exit...\n");
980 pthread_mutex_unlock(&rec_mutex
);
984 void *record_thread(void *dummy
){
985 /* sound device startup */
986 int fd
=fileno(recfd
),i
,j
;
987 int format
=REC_SAMPLE_FMT
;
994 /* realtime schedule setup */
996 struct sched_param param
;
997 param
.sched_priority
=89;
998 if(pthread_setschedparam(pthread_self(), SCHED_FIFO
, ¶m
)){
999 fprintf(stderr
,"Could not set realtime priority for playback; am I suid root?\n");
1004 ret
=ioctl(fd
,SNDCTL_DSP_SETFMT
,&format
);
1005 if(ret
|| format
!=REC_SAMPLE_FMT
){
1006 fprintf(stderr
,"Could not set recording format\n");
1009 ret
=ioctl(fd
,SNDCTL_DSP_CHANNELS
,&channels
);
1010 if(ret
|| channels
!=2){
1011 fprintf(stderr
,"Could not set %d channel recording\n",2);
1014 ret
=ioctl(fd
,SNDCTL_DSP_SPEED
,&rate
);
1015 if(ret
|| rate
!=44100){
1016 fprintf(stderr
,"Could not set %dHz recording\n",44100);
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
);
1029 /* lock the buffer and check tail; block on space to record */
1031 pthread_mutex_lock(&rec_buffer_mutex
);
1032 if(sizeof(recordbuffer
)-record_count
<REC_BLOCK
)
1034 pthread_cond_wait(&rec_buffer_cond
,&rec_buffer_mutex
);
1037 pthread_mutex_unlock(&rec_buffer_mutex
);
1039 pthread_mutex_lock(&rec_mutex
);
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
;
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
);
1078 pthread_mutex_unlock(&rec_mutex
);
1083 fprintf(stderr
,"Record thread exit...\n");
1084 pthread_mutex_unlock(&rec_mutex
);
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
;
1103 long bytes
=t
->samplelength
;
1105 pthread_mutex_lock(&mmap_mutex
);
1106 pthread_mutex_unlock(&cache_mutex
);
1108 fill
*=2*t
->channels
;
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
));
1119 pthread_mutex_unlock(&mmap_mutex
);
1120 pthread_mutex_lock(&cache_mutex
);
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
];
1132 memmove(cache_list
+i
,
1134 (cache_count
-i
)*sizeof(*cache_list
));
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(){
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
);
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
;
1158 t
->sample_position
=0;
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 */
1169 struct sched_param param
;
1170 param
.sched_priority
=80;
1171 if(pthread_setschedparam(pthread_self(), SCHED_FIFO
, ¶m
)){
1172 fprintf(stderr
,"Could not set realtime priority for caching; am I suid root?\n");
1176 pthread_mutex_lock(&cache_mutex
);
1179 /* scan tags; service lowest fill. Active has priority over
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
){
1194 if(mintag_a
== -1 || t
->cache_fill
<minfill_a
){
1199 if(mintag_p
== -1 || t
->cache_fill
<minfill_p
){
1207 _cache_tag_lockahead(mintag_a
);
1211 _cache_tag_lockahead(mintag_p
);
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
];
1237 memmove(active_list
+i
,
1239 (active_count
-i
)*sizeof(*active_list
));
1240 for(i
=cache_count
-1;i
>=0;i
--)
1241 if(cache_list
[i
]==t
)
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
;
1252 if(t
->activep
)return;
1253 if(!t
->basemap
)return;
1257 active_list
[active_count
]=t
;
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
;
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
]);
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
]);
1295 write(ttypipe
[1],"",1);
1298 /* position in tag list */
1299 static inline void _playback_mix(int i
,int cuenum
){
1301 cue
*c
=cue_list
+cuenum
;
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
,
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
){
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
++){
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;
1346 value
=t
->data
[t
->sample_position
*t
->channels
+j
]*
1347 t
->master_vol_current
*.01;
1350 /* output split and mix */
1352 for(k
=0;k
<MAX_OUTPUT_CHANNELS
;k
++){
1353 staging
[k
]+=value
*ov_current
[k
];
1355 ov_current
[k
]+=ov_slew
[k
];
1357 ov_current
[k
]>ov_target
[k
]){
1359 ov_current
[k
]=ov_target
[k
];
1362 ov_current
[k
]<ov_target
[k
]){
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
){
1396 t
->sample_position
=t
->sample_loop_start
+t
->sample_lapping
;
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.){
1410 }else if(staging
[i
]<-32768.){
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
;
1434 int fragment
=0x7fff000d;
1435 int16 audiobuf
[256*MAX_OUTPUT_CHANNELS
];
1438 /* realtime schedule setup */
1440 struct sched_param param
;
1441 param
.sched_priority
=89;
1442 if(pthread_setschedparam(pthread_self(), SCHED_FIFO
, ¶m
)){
1443 fprintf(stderr
,"Could not set realtime priority for playback; am I suid root?\n");
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");
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
);
1460 ret
=ioctl(fd
,SNDCTL_DSP_SPEED
,&rate
);
1461 if(ret
|| rate
!=44100){
1462 fprintf(stderr
,"Could not set %dHz playback\n",44100);
1466 ioctl(fd
,SNDCTL_DSP_GETOSPACE
,&info
);
1467 playback_buffer_minfill
=totalsize
=info
.fragstotal
*info
.fragsize
;
1469 while(!playback_exit
){
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
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
);
1498 gettimeofday(&tv
,NULL
);
1499 foo
=tv
.tv_sec
*10+tv
.tv_usec
/100000;
1501 write(ttypipe
[1],"",1);
1509 /* sound device shutdown */
1511 ioctl(fd
,SNDCTL_DSP_RESET
);
1512 fprintf(stderr
,"Playback thread exit...\n");
1516 /* cuenum is the base number */
1517 /* add new tags before new mixes */
1518 int play_cue(int cuenum
){
1525 _playback_add(c
->tag
,x
);
1528 if(x
>=cue_count
|| cue_list
[x
].tag
==-1)break;
1531 cue
*c
=cue_list
+cuenum
;
1533 if(!c
->tag_create_p
)
1534 _playback_mix(c
->tag
,cuenum
);
1537 if(cuenum
>=cue_count
|| cue_list
[cuenum
].tag
==-1)break;
1543 /* caches new queue. cache cleanup is handled in caching thread */
1544 int cache_cue(int cuenum
){
1545 pthread_mutex_lock(&cache_mutex
);
1548 cue
*c
=cue_list
+cuenum
;
1554 if(cuenum
>=cue_count
|| cue_list
[cuenum
].tag
==-1)break;
1557 pthread_mutex_unlock(&cache_mutex
);
1563 int play_sample(int cuenum
){
1564 cue
*c
=cue_list
+cuenum
;
1568 _playback_add(c
->tag
,cuenum
);
1570 _playback_mix(c
->tag
,cuenum
);
1577 int halt_playback(){
1580 for(i
=0;i
<tag_count
;i
++){
1585 t
->master_vol_target
=-1;
1586 t
->master_vol_slew
=_slew_ms(100,t
->master_vol_current
,0);
1595 /***************** simple form entry fields *******************/
1597 enum field_type
{ FORM_YESNO
, FORM_PERCENTAGE
, FORM_PERCENTAGE_R
,
1598 FORM_NUMBER
, FORM_GENERIC
,
1601 enum field_type type
;
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
){
1637 if(f
->type
==FORM_BUTTON
)edit
=0;
1639 if(edit
&& f
->active
)
1659 int *var
=(int *)(f
->var
);
1663 for(i
=0;i
<f
->width
-5;i
++)
1668 case FORM_PERCENTAGE
:
1670 int var
=*(int *)(f
->var
);
1674 snprintf(buf
,80,"%*d",f
->width
-2,var
);
1678 case FORM_PERCENTAGE_R
:
1680 int var
=*(int *)(f
->var
);
1685 snprintf(buf
,80,"%*s",f
->width
-2,"DEL");
1687 snprintf(buf
,80,"%*d",f
->width
-2,var
);
1693 long var
=*(long *)(f
->var
);
1695 snprintf(buf
,80,"%*ld",f
->width
-2,var
);
1699 case FORM_GENERIC
:case FORM_BUTTON
:
1701 char *var
=(char *)(f
->var
);
1702 addnlstr(var
,f
->width
-2,' ');
1710 addnlstr("",f
->width
-2,'-');
1723 if(focus
&& edit
&& f
->type
==FORM_GENERIC
){
1725 move(f
->y
,f
->x
+1+f
->cursor
);
1731 void form_redraw(form
*f
){
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
){
1739 if(f
->storage
==n
)return(-1);
1740 /* add the struct, then draw contents */
1741 f
->fields
[n
].type
=type
;
1744 f
->fields
[n
].width
=width
;
1745 f
->fields
[n
].var
=var
;
1746 f
->fields
[n
].active
=1;
1749 draw_field(f
->fields
+n
,f
->edit
,n
==f
->cursor
);
1753 void field_state(form
*f
,int n
,int activep
){
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);
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);
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
;
1797 if(ff
->type
==FORM_BUTTON
){
1803 //draw_field(f->fields+f->cursor,f->edit,1);
1804 //form_next_field(f);
1813 case KEY_DOWN
:case '\t':
1822 int *val
=(int *)ff
->var
;
1842 case FORM_PERCENTAGE
:
1844 int *val
=(int *)ff
->var
;
1846 case '=':case KEY_RIGHT
:
1848 if(*val
>100)*val
=100;
1852 if(*val
>100)*val
=100;
1854 case '-':case KEY_LEFT
:
1868 case FORM_PERCENTAGE_R
:
1870 int *val
=(int *)ff
->var
;
1872 case '=':case KEY_RIGHT
:
1874 if(*val
>100)*val
=100;
1878 if(*val
>100)*val
=100;
1880 case '-':case KEY_LEFT
:
1896 long *val
=(long *)ff
->var
;
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))){
1905 case KEY_BACKSPACE
:case '\b':
1908 case KEY_RIGHT
:case '+':case '=':
1909 if(*val
<(int)rint(pow(10,ff
->width
-2)-1))
1912 case KEY_LEFT
:case '-':case '_':
1923 /* we assume the string for the GENERIC case is alloced to width */
1926 char *val
=(char *)ff
->var
;
1927 const char *ctrl
=unctrl(c
);
1931 if(ff
->cursor
<0)ff
->cursor
=0;
1935 if(ff
->cursor
>(int)strlen(val
))ff
->cursor
=strlen(val
);
1936 if(ff
->cursor
>ff
->width
-3)ff
->cursor
=ff
->width
-3;
1938 case KEY_BACKSPACE
:case '\b':
1940 memmove(val
+ff
->cursor
-1,val
+ff
->cursor
,strlen(val
)-ff
->cursor
+1);
1946 if((int)strlen(val
)<ff
->width
-3){
1947 memmove(val
+ff
->cursor
+1,val
+ff
->cursor
,strlen(val
)-ff
->cursor
+1);
1958 ff
->cursor
=strlen(val
);
1961 val
[ff
->cursor
]='\0';
1985 draw_field(f
->fields
+f
->cursor
,f
->edit
,1);
1989 /********************** main run screen ***********************/
1990 void main_update_master(int n
,int y
){
1991 if(menu
==MENU_MAIN
){
1996 main_master_volume
=n
;
2000 sprintf(buf
,"%3ld%%",main_master_volume
);
2005 void main_update_playbuffer(int y
){
2006 if(menu
==MENU_MAIN
){
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
;
2021 starve
=0; /* reset */
2024 if(playback_bufsize
)
2025 n
=rint(100.*n
/playback_bufsize
);
2032 addstr("playbuffer: ");
2033 sprintf(buf
,"%3d%% %s",n
,starve
?"***STARVE***":" ");
2040 pthread_mutex_lock(&rec_mutex
);
2047 pthread_mutex_unlock(&rec_mutex
);
2054 addstr(" STARTING ");
2057 addstr(" RECORDING ");
2062 nr
=rec_buffer_dma_min
;
2063 rec_buffer_dma_min
=100;
2064 pthread_mutex_unlock(&rec_mutex
);
2071 starver1
=0; /* reset */
2075 addstr("recbuffer (DMA):");
2076 sprintf(buf
," %3d%% %s",nr
,starver1
?"***OVERRUN***":" ");
2079 pthread_mutex_lock(&rec_mutex
);
2080 nr
=rec_buffer_disk_min
;
2081 rec_buffer_disk_min
=100;
2082 pthread_mutex_unlock(&rec_mutex
);
2089 starver2
=0; /* reset */
2093 addstr("recbuffer(disk):");
2094 sprintf(buf
," %3d%% %s",nr
,starver2
?"***OVERRUN***":"");
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
){
2114 if(menu
==MENU_MAIN
){
2115 for(i
=0;i
<MAX_OUTPUT_CHANNELS
;i
++){
2119 val
=channel_list
[i
].peak
;
2120 channel_list
[i
].peak
=0;
2129 if(clip
[i
]<0)clip
[i
]=0;
2139 val
=rint(todB_nn(val
/32768.)+100.);
2143 for(j
=0;j
<val
;j
++)buf
[j
]='=';
2144 for(;j
<10;j
++)buf
[j
]=' ';
2152 static int rclip
[2];
2153 void main_update_inchannel_levels(int y
){
2155 if(menu
==MENU_MAIN
){
2159 pthread_mutex_lock(&rec_mutex
);
2160 val
=rchannel_list
[i
].peak
;
2161 rchannel_list
[i
].peak
=0;
2162 pthread_mutex_unlock(&rec_mutex
);
2170 if(rclip
[i
]<0)rclip
[i
]=0;
2180 val
=rint(todB_nn(val
/8388607.)+100.);
2184 for(j
=0;j
<val
;j
++)buf
[j
]='=';
2185 for(;j
<10;j
++)buf
[j
]=' ';
2193 void main_update_channel_labels(int y
){
2196 if(menu
==MENU_MAIN
){
2197 for(i
=0;i
<MAX_OUTPUT_CHANNELS
;i
++){
2199 sprintf(buf
,"-[ ]+0dB ");
2201 addstr(channel_list
[i
].label
);
2205 sprintf(buf
,"-[ ]+0dB ");
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
;
2229 addnlstr("",71,' ');
2231 addnlstr("**** BEGIN",71,' ');
2232 }else if(cn
>=cue_count
){
2234 addnlstr("****** END",71,' ');
2237 addnlstr("",71,' ');
2240 addnlstr("",71,' ');
2242 addnlstr("",71,' ');
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,' ');
2257 while(++cn
<cue_count
)
2258 if(cue_list
[cn
].tag
==-1)break;
2265 /* assumes the existing tags/labels are not changing out from
2266 underneath playback. editing a tag *must* kill playback for
2268 void main_update_tags(int y
){
2269 if(menu
==MENU_MAIN
){
2271 static int last_tags
=0;
2278 addstr("playing tags:");
2280 for(i
=0;i
<active_count
;i
++){
2284 int vol
=active_list
[i
]->master_vol_current
;
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
;
2293 snprintf(buf
,20,"[loop %3d%%] ",vol
);
2295 snprintf(buf
,20,"[%3ds %3d%%] ",(ms
+500)/1000,vol
);
2297 addnlstr(label_text(path
),60,' ');
2304 for(i
=active_count
;i
<last_tags
;i
++){
2306 addnlstr("",60,' ');
2309 last_tags
=active_count
;
2314 static int editable
=1;
2315 void update_editable(){
2316 if(menu
==MENU_MAIN
){
2320 addstr(" EDIT LOCKED ");
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
++;
2334 cache_cue(cue_list_number
);
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
--;
2346 cache_cue(cue_list_number
);
2348 main_update_cues(10+MAX_OUTPUT_CHANNELS
);
2351 int save_top_level(char *fn
){
2354 char *buf
=alloca(strlen(fn
)*2+20);
2355 sprintf(buf
,"cp %s %s~ 2>/dev/null",fn
,fn
);
2356 /* create backup file */
2366 sprintf(buf
,"SAVE FAILED: %s",strerror(errno
));
2367 addnlstr(buf
,80,' ');
2372 if(save_program(f
)){
2374 sprintf(buf
,"SAVE FAILED: %s",strerror(ferror(f
)));
2375 addnlstr(buf
,80,' ');
2381 addnlstr("PROGRAM SAVED (any key to continue)",80,' ');
2390 addstr("MTG Beaverphonic build "VERSION
": ");
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);
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
);
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
);
2436 return(MENU_KEYPRESS
);
2440 addnlstr("Really quit? [y/N] ",80,' ');
2448 addnlstr("Save changes first? [Y/n] ",80,' ');
2451 if(ch
!='n' && ch
!='N')save_top_level(program
);
2456 addstr("MTG Beaverphonic build "VERSION
": ");
2462 if(editable
&& cue_list_number
<cue_count
)return(MENU_EDIT
);
2473 addnlstr("Really delete cue? [y/N] ",80,' ');
2479 delete_cue_bank(cue_list_number
);
2480 main_update_cues(10+MAX_OUTPUT_CHANNELS
);
2483 addstr("MTG Beaverphonic build "VERSION
": ");
2492 return(MENU_OUTPUT
);
2495 save_top_level(program
);
2500 main_update_master(main_master_volume
-1,5);
2504 main_update_master(main_master_volume
+1,5);
2507 play_cue(cue_list_number
);
2510 case KEY_UP
:case '\b':case KEY_BACKSPACE
:
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);
2532 // if(ctrl[0]=='^'){
2535 pthread_mutex_lock(&rec_mutex
);
2537 pthread_mutex_unlock(&rec_mutex
);
2540 pthread_mutex_lock(&rec_mutex
);
2542 pthread_mutex_unlock(&rec_mutex
);
2550 int main_keypress_menu(){
2553 mvaddstr(0,2," Keypresses for main menu ");
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");
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");
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");
2616 int ch
=form_handle_char(&f
,mgetch());
2618 case KEY_ENTER
:case 'x':case 'X':
2627 /* determine where in list cue is being added */
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
);
2653 void edit_keypress_menu(){
2656 mvaddstr(0,2," Keypresses for cue edit menu ");
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");
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");
2689 void edit_sample_menu(int n
){
2692 tag
*t
=tag_list
+cue_list
[n
].tag
;
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
);
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
);
2741 mvaddstr(13,5," L ");
2742 mvaddstr(13,10," R ");
2744 for(i
=0;i
<t
->channels
;i
++){
2745 mvaddch(13,6+i
*5,' ');
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");
2763 Add
/edit sample to cue
-------------------------------------------------------
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
|
2785 ----------------------------------------------------------------------------
2790 int ch
=form_handle_char(&f
,getch());
2801 case 'x':case 'X':case KEY_ENTER
:
2803 edit_label(t
->sample_desc
,tdesc
);
2811 void edit_mix_menu(int n
){
2814 tag
*t
=tag_list
+cue_list
[n
].tag
;
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
);
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
);
2857 mvaddstr(12,5," L ");
2858 mvaddstr(12,10," R ");
2860 for(i
=0;i
<t
->channels
;i
++){
2861 mvaddch(12,6+i
*5,' ');
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");
2879 Add
/edit mix change
---------------------------------------------------------
2887 volume master
[100]% fade
[ 15]
2889 --- L
-- R
-----------------------------------------------------------------
2890 | [100][ 0]% -> 0 offstage left
|
2891 | [ 0][100]% -> 1 offstage right
|
2898 ----------------------------------------------------------------------------
2905 int ch
=form_handle_char(&f
,getch());
2910 case 'x':case 'X':case KEY_ENTER
:
2920 int add_sample_menu(){
2921 /* two-stage... get sample first so the mixer is accurate */
2930 mvaddstr(0,2," Add new sample to cue ");
2933 mvaddstr(2,2,"sample path");
2934 field_add(&f
,FORM_GENERIC
,14,2,62,path
);
2940 int re
=form_handle_char(&f
,ch
);
2942 if(re
>=0 || !f
.edit
){
2948 /* try to load the sample! */
2950 memset(&t
,0,sizeof(t
));
2951 if(load_sample(&t
,path
)){
2952 fprintf(stderr
,"Press enter to continue\n");
2954 switch_to_ncurses();
2957 switch_to_ncurses();
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
,"");
2976 t
.loop_lapping
=1000;
2980 tagno
=new_tag_number();
2985 /* got it, add a new cue */
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
);
2994 memset(&c
.mix
,0,sizeof(mix
));
2995 c
.mix
.vol_master
=100;
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);
3009 int tagno_to_cueno(int tagno
){
3012 for(i
=0;i
<cue_count
;i
++)
3013 if(cue_list
[i
].tag
==tagno
)return(i
);
3025 mvaddstr(0,2," Add mixer change to cue ");
3028 mvaddstr(2,2,"tag number ");
3029 field_add(&f
,FORM_NUMBER
,13,2,12,&tagno
);
3037 addstr("' for a list of sample tags ");
3041 int re
=form_handle_char(&f
,ch
);
3043 if(re
>=0 || !f
.edit
){
3048 if(tagno
>=tag_count
|| tag_list
[tagno
].refcount
<=0){
3049 mvaddstr(4,2,"Bad tag number; any key to continue.");
3055 addstr("' for a list of sample tags ");
3063 /* add the new cue */
3064 /* prefill with original mix data from sample */
3068 int createno
=tagno_to_cueno(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
);
3077 memset(&c
.mix
,0,sizeof(mix
));
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);
3094 int edit_cue_menu(){
3100 /* determine first and last cue in bank */
3101 int base
=cue_list_number
;
3106 while(last
<cue_count
&& cue_list
[last
].tag
!=-1)last
++;
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
);
3127 mvaddstr(6,11,"--None--");
3130 field_add(&f
,FORM_BUTTON
,66,7+i
,11,"MAIN MENU");
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:");
3145 int ch
=form_handle_char(&f
,mgetch());
3149 edit_keypress_menu();
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
);
3170 if(f
.cursor
>=3 && f
.cursor
<3+actions
){
3171 int n
=first
+f
.cursor
-3;
3173 delete_cue_single(n
);
3184 play_cue(cue_list_number
);
3190 if(f
.cursor
==3+actions
){
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
);
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);
3201 edit_mix_menu(first
+f
.cursor
-3);
3228 menu
=main_keypress_menu();
3234 menu
=add_cue_menu();
3240 menu
=edit_cue_menu();
3247 pthread_mutex_t pipe_mutex
=PTHREAD_MUTEX_INITIALIZER
;
3249 void *tty_thread(void *dummy
){
3253 int ret
=read(ttyfd
,&buf
,1);
3255 write(ttypipe
[1],&buf
,1);
3258 if(playback_exit
)break;
3263 int main(int gratuitously
,char *different
[]){
3267 fprintf(stderr
,"Usage: beaverphonic <settingfile>\n");
3271 mkdir(tempdir
,0777);
3273 /* lock against other instances */
3274 lf
=open(lockfile
,O_CREAT
|O_RDWR
,0770);
3276 fprintf(stderr
,"unable to open lock file: %s.\n",strerror(errno
));
3280 if(flock(lf
,LOCK_EX
|LOCK_NB
)){
3281 fprintf(stderr
,"Another Beaverphonic process is running.\n"
3282 " (could not acquire lockfile)\n");
3286 playfd
=fopen("/dev/dsp1","wb");
3288 fprintf(stderr
,"unable to open audio device for playback: %s.\n",strerror(errno
));
3289 fprintf(stderr
,"\nPress enter to continue\n");
3293 recfd
=fopen("/dev/dsp1","rb");
3295 fprintf(stderr
,"unable to open audio device fo record: %s.\n",strerror(errno
));
3296 fprintf(stderr
,"\nPress enter to continue\n");
3301 /* set up the hack for interthread ncurses event triggering through
3303 ttyfd
=open("/dev/tty",O_RDONLY
);
3306 fprintf(stderr
,"Unable to open /dev/tty:\n"
3307 " %s\n",strerror(errno
));
3312 fprintf(stderr
,"Unable to open tty pipe:\n"
3313 " %s\n",strerror(errno
));
3319 pthread_create(&tty_thread_id
,NULL
,tty_thread
,NULL
);
3324 pthread_create(&playback_thread_id
,NULL
,playback_thread
,NULL
);
3325 pthread_create(&cache_thread_id
,NULL
,cache_thread
,NULL
);
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();
3339 intrflush(stdscr
, FALSE
);
3340 keypad(stdscr
, TRUE
);
3341 use_default_colors();
3342 signal(SIGINT
,SIG_IGN
);
3346 program
=strdup(different
[1]);
3348 FILE *f
=fopen(program
,"rb");
3350 if(load_program(f
)){
3351 fprintf(stderr
,"\nPress enter to continue\n");
3357 switch_to_ncurses();
3359 main_thread_id
=pthread_self();
3362 endwin(); /* restore original tty modes */
3366 pthread_mutex_lock(&rec_mutex
);
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
);
3376 if(!playback_active
)break;
3381 pthread_mutex_lock(&rec_mutex
);
3382 if(!rec_active1
&& !rec_active2
)break;
3383 pthread_mutex_unlock(&rec_mutex
);
3386 pthread_mutex_unlock(&rec_mutex
);
3399 0: [ ] built in OSS left
3400 1: [ ] built in OSS right