[xiph-cvs] cvs commit: MTG soundboard.c

Monty xiphmont at xiph.org
Thu Oct 2 10:11:58 PDT 2003



xiphmont    03/10/02 13:11:58

  Modified:    .        soundboard.c
  Log:
  Of course, it helps to commit the right version.

Revision  Changes    Path
1.13      +404 -149  MTG/soundboard.c

Index: soundboard.c
===================================================================
RCS file: /usr/local/cvsroot/MTG/soundboard.c,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -r1.12 -r1.13
--- soundboard.c	2 Oct 2003 17:08:48 -0000	1.12
+++ soundboard.c	2 Oct 2003 17:11:58 -0000	1.13
@@ -1,3 +1,10 @@
+/* Assumptions (Bad things the code does that work here, but might not
+   work elsewhere):
+
+   the atomic execution sections are a hack that can only work on uniprocessor
+
+*/
+
 /* TODO:
 
    make Esc behavior correct in editing menus by using temp cue/tag storage
@@ -7,18 +14,21 @@
    logarhythmic fades
    proper printing out of file loading issues
    abstracted output
-   allow sample name/tag change in edit menus
-*/
+   allow sample name/tag change in edit menus */
+#define CACHE_SAMPLES 44100*10
 #define _REENTRANT 1
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdio.h>
+#include <ctype.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <limits.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/time.h>
+#include <sys/file.h>
 #define __USE_GNU 1
 #include <pthread.h>
 #include <string.h>
@@ -30,17 +40,52 @@
 #include <sys/soundcard.h>
 #include <sys/ioctl.h>
 
+/* we need some cooperative multitasking to eliminate locking (and
+   thus latency) in the realtime playback thread.
+   pthread_setschedparam can give us this by way of implementing
+   atomic execution blocks.  Convention here is that an 'atomic
+   execution block' is actually a section wrapped in a realtime
+   schedule change with a priority of 90; to be effective, this must
+   be the highest realtime priority used in the application */
+
+static int original_priority;
+static int original_policy;
+#define BEGIN_ATOMIC \
+   { \
+     struct sched_param param; \
+     pthread_getschedparam(pthread_self(), &original_policy, &param); \
+     if(param.sched_priority==90){ \
+       fprintf(stderr,"ATOMIC sections do not stack at line %ld\n",__LINE__); \
+       exit(1); \
+     } \
+     original_priority=param.sched_priority; \
+     param.sched_priority=90; \
+     pthread_setschedparam(pthread_self(), SCHED_FIFO, &param); \
+   }
+
+#define END_ATOMIC \
+   { \
+     struct sched_param param; \
+     param.sched_priority=original_priority; \
+     pthread_setschedparam(pthread_self(), original_policy, &param); \
+   }
+
+
+
 #define int16 short
 static char *tempdir="/tmp/beaverphonic/";
 static char *lockfile="/tmp/beaverphonic/lock";
 //static char *installdir="/usr/local/beaverphonic/";
 static char *installdir="/home/xiphmont/MotherfishCVS/MTG/";
-#define VERSION "20020203.0"
+#define VERSION "$Id: soundboard.c,v 1.13 2003/10/02 17:11:58 xiphmont Exp $"
 
 enum menutype {MENU_MAIN,MENU_KEYPRESS,MENU_ADD,MENU_EDIT,MENU_OUTPUT,MENU_QUIT};
 
-pthread_mutex_t master_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+pthread_mutex_t cache_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
 pthread_mutex_t rec_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+pthread_mutex_t mmap_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+pthread_cond_t cache_cond=PTHREAD_COND_INITIALIZER;
+
 static long main_master_volume=50;
 char *program;
 static int playback_buffer_minfill=0;
@@ -57,12 +102,14 @@
 int ttypipe[2];
 
 static inline void _playback_remove(int i);
+static void _cache_remove(int i);
 
 pthread_t main_thread_id;
 pthread_t playback_thread_id;
 pthread_t record_thread_id;
 pthread_t record_disk_thread_id;
 pthread_t tty_thread_id;
+pthread_t cache_thread_id;
 
 void main_update_tags(int y);
 
@@ -161,7 +208,7 @@
   strcpy(label->text,t);
 }
 
-void aquire_label(int number){
+void acquire_label(int number){
   _alloc_label_if_needed(number);
   label_list[number].refcount++;
 }
@@ -246,12 +293,15 @@
 
   /* state */
   int    activep;
+  int    cachep;
   
   long   sample_position;
   long   sample_lapping;
   long   sample_loop_start;
   long   sample_fade_start;
 
+  long   cache_fill;
+
   double master_vol_current;
   double master_vol_target;
   double master_vol_slew;
@@ -273,6 +323,9 @@
 static tag       **active_list;
 static int         active_count;
 
+static tag       **cache_list;
+static int         cache_count;
+
 int new_tag_number(){
   int i;
   for(i=0;i<tag_count;i++)
@@ -283,13 +336,14 @@
 static void _alloc_tag_if_needed(int number){
   if(number>=tag_count){
     int prev=tag_count;
-    pthread_mutex_lock(&master_mutex);
+    BEGIN_ATOMIC
     tag_count=number+1;
     tag_list=m_realloc(tag_list,sizeof(*tag_list)*tag_count);
     active_list=m_realloc(active_list,sizeof(*active_list)*tag_count);
+    cache_list=m_realloc(cache_list,sizeof(*cache_list)*tag_count);
     
     memset(tag_list+prev,0,sizeof(*tag_list)*(tag_count-prev));
-    pthread_mutex_unlock(&master_mutex);
+    END_ATOMIC
   }
 }
 
@@ -317,7 +371,13 @@
   buffer=alloca(strlen(installdir)+strlen(tmp)+strlen(path)+20);  
   fprintf(stderr,"\tcopying/converting...\n");
   sprintf(buffer,"%sconvert.pl \'%s\' \'%s\'",installdir,path,tmp);
-  ret=system(buffer);
+
+  if(fork()){
+    wait(&ret);
+  }else{
+    setuid(getuid());
+    _exit(system(buffer));
+  }
   if(ret)goto out;
   
   /* our temp file is a host-ordered 44.1kHz 16 bit PCM file, n
@@ -326,7 +386,7 @@
   {
     FILE *cheat=fopen(tmp,"rb");
     struct sample_header head;
-    int count,i;
+    int count;
     int fd;
     long length;
     if(!cheat){
@@ -385,6 +445,13 @@
       ret=-1;goto out;
     }
     t->data=t->basemap+sizeof(head);
+    if(madvise(t->basemap, 
+	       t->samplelength*sizeof(int16)*t->channels+sizeof(head),
+	       MADV_RANDOM)){
+      fprintf(stderr,"madvise() failed for %s mmap:\n\t%d (%s)\n",
+	      tmp,errno,strerror(errno));
+      ret=-1;goto out;
+    }
   }
   fprintf(stderr,"\tDONE\n\n");
   
@@ -395,15 +462,17 @@
 
 int unload_sample(tag *t){
   /* is this sample currently playing back? */
-  pthread_mutex_lock(&master_mutex);
+  int i;
   if(t->activep){
-    int i;
     for(i=0;i<active_count;i++)
       if(active_list[i]==t)_playback_remove(i);
   }
-  pthread_mutex_unlock(&master_mutex);
+  if(t->cachep){
+    for(i=0;i<cache_count;i++)
+      if(cache_list[i]==t)_cache_remove(i);
+  }
 
-  /* unmap */
+  pthread_mutex_lock(&mmap_mutex);
   if(t->basemap)
     munmap(t->basemap,
            t->samplelength*sizeof(int16)*
@@ -413,12 +482,12 @@
   t->data=NULL;
   t->samplelength=0;
   t->channels=0;
+  pthread_mutex_unlock(&mmap_mutex);
+
   return(0);
 }
 
 int edit_tag(int number,tag t){
-  int ret;
-  int refcount;
   _alloc_tag_if_needed(number);
 
   tag_list[number]=t;
@@ -427,7 +496,7 @@
   return(0);
 }
 
-void aquire_tag(int number){
+void acquire_tag(int number){
   if(number<0)return;
   _alloc_tag_if_needed(number);
   tag_list[number].refcount++;
@@ -479,7 +548,7 @@
 
 int load_tag(FILE *f){
   tag t;
-  int len,count,number;
+  int count,number;
   memset(&t,0,sizeof(t));
   
   count=fscanf(f,":%d %d %d %ld %ld %ld %ld",
@@ -489,13 +558,13 @@
     addnlstr("LOAD ERROR (TAG): too few fields.",80,' ');
     return(-1);
   }
-  aquire_label(t.sample_path);
+  acquire_label(t.sample_path);
   if(load_sample(&t,label_text(t.sample_path))){
     release_label(t.sample_path);
     return(-1);
   }
   edit_tag(number,t);
-  aquire_label(t.sample_desc);
+  acquire_label(t.sample_desc);
 
   return(0);
 }
@@ -524,31 +593,30 @@
 int                cue_storage;
 
 void add_cue(int n,cue c){
-  int i;
 
   if(cue_count==cue_storage){
-    pthread_mutex_lock(&master_mutex);
+    BEGIN_ATOMIC
     cue_storage++;
     cue_storage*=2;
     cue_list=m_realloc(cue_list,cue_storage*sizeof(*cue_list));
-    pthread_mutex_unlock(&master_mutex);
+    END_ATOMIC
   }
 
   /* copy insert */
-  pthread_mutex_lock(&master_mutex);
+  BEGIN_ATOMIC
   if(cue_count-n)
     memmove(cue_list+n+1,cue_list+n,sizeof(*cue_list)*(cue_count-n));
   cue_count++;
 
   cue_list[n]=c;
-  pthread_mutex_unlock(&master_mutex);
+  END_ATOMIC
 }
 
 /* inefficient.  delete one, shift-copy list, delete one, shift-copy
    list, delete one, gaaaah.  Not worth optimizing */
 static void _delete_cue_helper(int n){
   if(n>=0 && n<cue_count){
-    pthread_mutex_lock(&master_mutex);
+    BEGIN_ATOMIC
     release_label(cue_list[n].label);
     release_label(cue_list[n].cue_text);
     release_label(cue_list[n].cue_desc);
@@ -556,7 +624,7 @@
     cue_count--;
     if(n<cue_count)
       memmove(cue_list+n,cue_list+n+1,sizeof(*cue_list)*(cue_count-n));
-    pthread_mutex_unlock(&master_mutex);
+    END_ATOMIC
   }
 }
 
@@ -624,7 +692,7 @@
 
 int load_cue(FILE *f){
   cue c;
-  int len,count,i,j;
+  int count,i,j;
   int maxchannels,maxwavch;
   memset(&c,0,sizeof(c));
   
@@ -648,7 +716,6 @@
 
   for(i=0;i<maxchannels;i++){
     int v;
-    long lv;
     char ch=' ';
     count=fscanf(f,"%c",&ch);
     if(count<0 || ch!='<'){
@@ -671,11 +738,11 @@
     }
   }
   
-  aquire_label(c.label);
-  aquire_label(c.cue_text);
-  aquire_label(c.cue_desc);
+  acquire_label(c.label);
+  acquire_label(c.cue_text);
+  acquire_label(c.cue_desc);
 
-  aquire_tag(c.tag);
+  acquire_tag(c.tag);
   add_cue(cue_count,c);
 
   return(0);
@@ -683,7 +750,7 @@
 
 int load_val(FILE *f, long *val){
   int count;
-  int t;  
+  long t;  
   count=fscanf(f,": %ld",&t);
   if(count<1){
     addnlstr("LOAD ERROR (VAL): missing field.",80,' ');
@@ -754,8 +821,8 @@
 
 /*************** threaded record ****************************/
 
-#define REC_SAMPLE_BYTES 2
-#define REC_SAMPLE_FMT AFMT_S16_LE
+#define REC_SAMPLE_BYTES 3
+#define REC_SAMPLE_FMT AFMT_S24_LE
 #define REC_SAMPLE_CH 2
 #define REC_BLOCK (REC_SAMPLE_CH * REC_SAMPLE_BYTES * 512) 
 unsigned char recordbuffer[REC_BLOCK*512];
@@ -851,7 +918,7 @@
       /* update counters, alert dma that we have space in ring buffer */
       pthread_mutex_lock(&rec_buffer_mutex);
       record_tail+=REC_BLOCK;
-      if(record_tail>=sizeof(recordbuffer))record_tail=0;
+      if((unsigned)record_tail>=sizeof(recordbuffer))record_tail=0;
       record_count-=REC_BLOCK;
       pthread_cond_signal(&rec_buffer_cond);
       pthread_mutex_unlock(&rec_buffer_mutex);
@@ -920,9 +987,8 @@
   int format=REC_SAMPLE_FMT;
   int channels=2;
   int rate=44100;
-  long last=0;
   long totalsize;
-  int count=0,ret;
+  int ret;
   audio_buf_info info;
 
   ret=ioctl(fd,SNDCTL_DSP_SETFMT,&format);
@@ -981,9 +1047,8 @@
     pthread_mutex_lock(&rec_mutex);
     for(i=record_head;i<record_head+REC_BLOCK;)
       for(j=0;j<REC_SAMPLE_CH;j++){
-	//int val=((recordbuffer[i]<<8)|(recordbuffer[i+1]<<16)|(recordbuffer[i+2]<<24))>>8;
-	int val=((recordbuffer[i]<<16)|
-		 (recordbuffer[i+1]<<24))>>8;
+	int val=((recordbuffer[i]<<8)|(recordbuffer[i+1]<<16)|(recordbuffer[i+2]<<24))>>8;
+	//int val=((recordbuffer[i]<<16)|(recordbuffer[i+1]<<24))>>8;
         if(labs(val)>rchannel_list[j].peak)
           rchannel_list[j].peak=labs(val);
         i+=REC_SAMPLE_BYTES;
@@ -995,7 +1060,7 @@
 
       pthread_mutex_lock(&rec_buffer_mutex);
       record_head+=REC_BLOCK;
-      if(record_head>=sizeof(recordbuffer))record_head=0;
+      if((unsigned)record_head>=sizeof(recordbuffer))record_head=0;
       record_count+=REC_BLOCK;
       pthread_cond_signal(&rec_buffer_cond);
       pthread_mutex_unlock(&rec_buffer_mutex);
@@ -1011,6 +1076,139 @@
   return(NULL);
 }
 
+/*************** threaded precache ****************************/
+
+static void wake_cache(){
+  /* signal the precache thread to wake up and work */
+  pthread_mutex_lock(&cache_mutex);
+  pthread_cond_signal(&cache_cond);
+  pthread_mutex_unlock(&cache_mutex);
+}
+
+/* master is singly locked upon call */
+static void _cache_tag_lockahead(int i){
+  tag *t=cache_list[i];
+  long fill=t->cache_fill;
+  long new;
+  long bytes=t->samplelength;
+  
+  pthread_mutex_lock(&mmap_mutex);
+  pthread_mutex_unlock(&cache_mutex);
+
+  fill*=2*t->channels;
+  new=fill+65536;
+  bytes*=2*t->channels;
+
+  if(new>bytes)new=bytes;
+  if(mlock(t->data,new)){
+    fprintf(stderr,"mlock failed: %s\n",strerror(errno));
+  }
+  new/=2;
+  new/=t->channels;
+
+  pthread_mutex_unlock(&mmap_mutex);
+  pthread_mutex_lock(&cache_mutex);
+  t->cache_fill=new;
+}
+
+static void _cache_remove(int i){
+  /* remove the tag from the precaching list, then free its cache;
+     this is the only place a cache free can happen, so we only need
+     lock/check against async read */
+
+  tag *t=cache_list[i];
+  cache_count--;
+  if(i<cache_count)
+    memmove(cache_list+i,
+	    cache_list+i+1,
+	    (cache_count-i)*sizeof(*cache_list));
+  t->cachep=0;
+  munlock(t->basemap,t->samplelength*sizeof(int16)*t->channels+sizeof(struct sample_header));
+}
+
+/* unlocks cache of all tags not currently active */
+static void cache_cull(){
+  int i;
+  pthread_mutex_lock(&cache_mutex);
+  for(i=cache_count-1;i>=0;i--)
+    if(!cache_list[i]->activep)_cache_remove(i);
+  pthread_mutex_unlock(&cache_mutex);
+  refresh();
+  write(ttypipe[1],"",1);
+}
+
+static void _cache_add(int tagnum){
+  tag *t=tag_list+tagnum;
+
+  if(!t->basemap)return;
+  if(t->cachep)return;
+  cache_list[cache_count]=t;
+  cache_count++;
+  t->cache_fill=0;
+  t->sample_position=0;
+  t->cachep=1;
+  refresh();
+  write(ttypipe[1],"",1);
+}
+
+static void *cache_thread(void *dummy){
+  /* complete our self-setup; we need to be a realtime thread just
+     behind playback and record */
+  int i;
+
+  struct sched_param param;
+  param.sched_priority=80;
+  if(pthread_setschedparam(pthread_self(), SCHED_FIFO, &param)){
+    fprintf(stderr,"Could not set realtime priority for caching; am I suid root?\n");
+    exit(1);
+  }
+
+  pthread_mutex_lock(&cache_mutex);    
+  while(1){
+
+    /* scan tags; service lowest fill.  Active has priority over
+       precache */
+    {
+      long minfill_a=0;
+      long mintag_a=-1;
+      long minfill_p=0;
+      long mintag_p=-1;
+      for(i=0;i<cache_count;i++){
+	tag *t=cache_list[i];
+	if(t->cache_fill<t->samplelength){
+	  long minfill=t->cache_fill-t->sample_position;
+
+	  if(minfill<CACHE_SAMPLES){
+	    
+	    if(t->activep){
+	      if(mintag_a == -1 || t->cache_fill<minfill_a){
+		minfill_a=minfill;
+		mintag_a=i;
+	      }
+	    }else{
+	      if(mintag_p == -1 || t->cache_fill<minfill_p){
+		minfill_p=minfill;
+	      mintag_p=i;
+	      }
+	    }
+	  }
+	}
+	if(mintag_a>=0){
+	  _cache_tag_lockahead(mintag_a);
+	  continue;
+	}
+	if(mintag_p>=0){
+	  _cache_tag_lockahead(mintag_p);
+	  continue;
+	}
+      }
+    }
+    
+    /* if there was no work to do, we sleep and wait for a signal */
+    pthread_cond_wait(&cache_cond, &cache_mutex);
+  }
+}
+
 /*************** threaded playback ****************************/
 
 static inline double _slew_ms(long ms,double now,double target){
@@ -1029,6 +1227,9 @@
     memmove(active_list+i,
             active_list+i+1,
             (active_count-i)*sizeof(*active_list));
+  for(i=cache_count-1;i>=0;i--)
+    if(cache_list[i]==t)
+      _cache_remove(i);
   write(ttypipe[1],"",1);
 }
 
@@ -1056,17 +1257,31 @@
 
   t->sample_fade_start=t->samplelength-t->fade_out*44.1;
   if(t->sample_fade_start<0)t->sample_fade_start=0;
-  t->master_vol_current=0;
+
+  if(c->mix.vol_ms==0)
+    t->master_vol_current=c->mix.vol_master;
+  else
+    t->master_vol_current=0;
 
   t->master_vol_target=c->mix.vol_master;
   t->master_vol_slew=_slew_ms(c->mix.vol_ms,0,c->mix.vol_master);
                              
-  for(j=0;j<MAX_OUTPUT_CHANNELS;j++)
-    for(k=0;k<t->channels;k++){
-      t->outvol_current[k][j]=0;
-      t->outvol_target[k][j]=c->mix.outvol[k][j];
-      t->outvol_slew[k][j]=_slew_ms(c->mix.vol_ms,0,c->mix.outvol[k][j]);
-    }
+  if(c->mix.vol_ms==0)
+    for(j=0;j<MAX_OUTPUT_CHANNELS;j++)
+      for(k=0;k<t->channels;k++){
+	t->outvol_current[k][j]=c->mix.outvol[k][j];
+	t->outvol_target[k][j]=c->mix.outvol[k][j];
+	t->outvol_slew[k][j]=_slew_ms(c->mix.vol_ms,0,c->mix.outvol[k][j]);
+      }
+  else
+    for(j=0;j<MAX_OUTPUT_CHANNELS;j++)
+      for(k=0;k<t->channels;k++){
+	t->outvol_current[k][j]=0;
+	t->outvol_target[k][j]=c->mix.outvol[k][j];
+	t->outvol_slew[k][j]=_slew_ms(c->mix.vol_ms,0,c->mix.outvol[k][j]);
+      }
+
+  _cache_add(tagnum);
   write(ttypipe[1],"",1);
 }
 
@@ -1092,7 +1307,8 @@
 
 static inline void _next_sample(int16 *out){
   int i,j,k;
-  int staging[MAX_OUTPUT_CHANNELS];
+  double staging[MAX_OUTPUT_CHANNELS];
+  double mmv=main_master_volume*.0001;
 
   memset(staging,0,sizeof(staging));
 
@@ -1103,6 +1319,10 @@
       double value;
       int lappoint=t->samplelength-t->sample_lapping;
 
+      double *ov_slew=t->outvol_slew[j];
+      double *ov_target=t->outvol_target[j];
+      double *ov_current=t->outvol_current[j];
+
       /* get the base value, depending on loop or no */
       if(t->loop_p && t->sample_position>=lappoint){
         long looppos=t->sample_position-lappoint+t->sample_loop_start;
@@ -1118,37 +1338,39 @@
       }
 
       /* output split and mix */
+      value*=mmv;
       for(k=0;k<MAX_OUTPUT_CHANNELS;k++){
-	staging[k]+=value*t->outvol_current[j][k]*main_master_volume*.0001;
+	staging[k]+=value*ov_current[k];
         
-	t->outvol_current[j][k]+=t->outvol_slew[j][k];
-	if(t->outvol_slew[j][k]>0 && 
-	   t->outvol_current[j][k]>t->outvol_target[j][k]){
-	  t->outvol_slew[j][k]=0;
-	  t->outvol_current[j][k]=t->outvol_target[j][k];
+	ov_current[k]+=ov_slew[k];
+	if(ov_slew[k]>0. && 
+	   ov_current[k]>ov_target[k]){
+	  ov_slew[k]=0.;
+	  ov_current[k]=ov_target[k];
         }
-	t->outvol_current[j][k]+=t->outvol_slew[j][k];
-	if(t->outvol_slew[j][k]<0 && 
-	   t->outvol_current[j][k]<t->outvol_target[j][k]){
-	  t->outvol_slew[j][k]=0;
-	  t->outvol_current[j][k]=t->outvol_target[j][k];
+	if(ov_slew[k]<0. && 
+	   ov_current[k]<ov_target[k]){
+	  ov_slew[k]=0.;
+	  ov_current[k]=ov_target[k];
         }
       }
     }
 
     /* update master volume */
-    t->master_vol_current+=t->master_vol_slew;
-    if(t->master_vol_slew>0 && t->master_vol_current>t->master_vol_target){
-      t->master_vol_slew=0;
-      t->master_vol_current=t->master_vol_target;
-    }
-    if(t->master_vol_slew<0 && t->master_vol_current<t->master_vol_target){
-      t->master_vol_slew=0;
-      t->master_vol_current=t->master_vol_target;
+    if(t->master_vol_slew){
+      t->master_vol_current+=t->master_vol_slew;
+      if(t->master_vol_slew>0 && t->master_vol_current>t->master_vol_target){
+	t->master_vol_slew=0;
+	t->master_vol_current=t->master_vol_target;
+      }
+      if(t->master_vol_slew<0 && t->master_vol_current<t->master_vol_target){
+	t->master_vol_slew=0;
+	t->master_vol_current=t->master_vol_target;
+      }
+      if(t->master_vol_current<0)
+	_playback_remove(i);
     }
-    if(t->master_vol_current<0)
-      _playback_remove(i);
-    
+
     /* determine if fade out has begun */
     if(t->sample_position==t->sample_fade_start && !t->loop_p){
       /* effect a master volume slew *now* */
@@ -1185,6 +1407,10 @@
 static int playback_active=0;
 static int playback_exit=0;
 
+/* NO LOCKING in the playback thread.  We're the highest priority
+   thread and will not be preempted.  The data structures we depend on
+   are 'locked' at lower levels by atomic assignment blocks. */
+
 void *playback_thread(void *dummy){
   /* sound device startup */
   audio_buf_info info;
@@ -1197,7 +1423,17 @@
   long totalsize;
   int fragment=0x7fff000d;
   int16 audiobuf[256*MAX_OUTPUT_CHANNELS];
-  int count=0,ret;
+  int ret;
+
+  /* realtime schedule setup */
+  {
+    struct sched_param param;
+    param.sched_priority=89;
+    if(pthread_setschedparam(pthread_self(), SCHED_FIFO, &param)){
+      fprintf(stderr,"Could not set realtime priority for playback; am I suid root?\n");
+      exit(1);
+    }
+  }
 
   ioctl(fd,SNDCTL_DSP_SETFRAGMENT,&fragment);
   ret=ioctl(fd,SNDCTL_DSP_SETFMT,&format);
@@ -1218,9 +1454,7 @@
   }
 
   ioctl(fd,SNDCTL_DSP_GETOSPACE,&info);
-  pthread_mutex_lock(&master_mutex);
   playback_buffer_minfill=totalsize=info.fragstotal*info.fragsize;
-  pthread_mutex_unlock(&master_mutex);
 
   while(!playback_exit){
     int samples;
@@ -1231,22 +1465,22 @@
       delay=0;
       ioctl(fd,SNDCTL_DSP_GETOSPACE,&info);
 
-
-      pthread_mutex_lock(&master_mutex);
       playback_bufsize=totalsize;      
       samples=playback_bufsize-info.bytes;
       if(playback_buffer_minfill>samples)
         playback_buffer_minfill=samples-64; // sample fragment
-      pthread_mutex_unlock(&master_mutex);
 
     }
 
-    pthread_mutex_lock(&master_mutex);
     for(i=0;i<256;i++)
       _next_sample(audiobuf+i*MAX_OUTPUT_CHANNELS);
-    pthread_mutex_unlock(&master_mutex);
 
-    ret=fwrite(audiobuf,2*MAX_OUTPUT_CHANNELS,256,playfd);
+    /* this is a calculated race; the race would not trip except in
+       situations where our locking latency would also cause the
+       realtime house of cards to come crashing down anyway */
+    pthread_cond_signal(&cache_cond);
+
+    fwrite(audiobuf,2*MAX_OUTPUT_CHANNELS,256,playfd);
     
     {
       struct timeval tv;
@@ -1260,57 +1494,81 @@
 
   }
   
-  pthread_mutex_lock(&master_mutex);
   playback_active=0;
   
   /* sound device shutdown */
   
   ioctl(fd,SNDCTL_DSP_RESET);
-  //pthread_mutex_unlock(&master_mutex);
   fprintf(stderr,"Playback thread exit...\n");
-  pthread_mutex_unlock(&master_mutex);
-
   return(NULL);
 }
 
 /* cuenum is the base number */ 
+/* add new tags before new mixes */
 int play_cue(int cuenum){
-  pthread_mutex_lock(&master_mutex);
+  int x=cuenum;
+  BEGIN_ATOMIC
   while(1){
-    cue *c=cue_list+cuenum;
+    cue *c=cue_list+x;
     if(c->tag>=0){
       if(c->tag_create_p)
-	_playback_add(c->tag,cuenum);
-      else
+	_playback_add(c->tag,x);
+    }
+    x++;
+    if(x>=cue_count || cue_list[x].tag==-1)break;
+  }
+  while(1){
+    cue *c=cue_list+cuenum;
+    if(c->tag>=0){
+      if(!c->tag_create_p)
         _playback_mix(c->tag,cuenum);
     }
     cuenum++;
     if(cuenum>=cue_count || cue_list[cuenum].tag==-1)break;
   }
-  pthread_mutex_unlock(&master_mutex);
+  END_ATOMIC
+  return(0);
+}
+
+/* caches new queue.  cache cleanup is handled in caching thread */
+int cache_cue(int cuenum){
+  pthread_mutex_lock(&cache_mutex);
+
+  while(1){
+    cue *c=cue_list+cuenum;
+    if(c->tag>=0){
+      if(c->tag_create_p)
+	_cache_add(c->tag);
+    }
+    cuenum++;
+    if(cuenum>=cue_count || cue_list[cuenum].tag==-1)break;
+  }
+
+  pthread_mutex_unlock(&cache_mutex);
+
+  wake_cache();
   return(0);
 }
 
 int play_sample(int cuenum){
   cue *c=cue_list+cuenum;
-  pthread_mutex_lock(&master_mutex);
+  BEGIN_ATOMIC
   if(c->tag>=0){
     if(c->tag_create_p)
       _playback_add(c->tag,cuenum);
-      else
-	_playback_mix(c->tag,cuenum);
+    else
+      _playback_mix(c->tag,cuenum);
   }
-  
-  pthread_mutex_unlock(&master_mutex);
+
+  END_ATOMIC
   return(0);
 }
 
 int halt_playback(){
   int i;
-  pthread_mutex_lock(&master_mutex);
+  BEGIN_ATOMIC
   for(i=0;i<tag_count;i++){
     tag *t=tag_list+i;
-    int j,k;
 
     if(t->activep){
 
@@ -1319,7 +1577,7 @@
       
     }
   }
-  pthread_mutex_unlock(&master_mutex);
+  END_ATOMIC
   return(0);
 }
 
@@ -1424,7 +1682,7 @@
       {
         long var=*(long *)(f->var);
         char buf[80];
-	snprintf(buf,80,"%*d",f->width-2,var);
+	snprintf(buf,80,"%*ld",f->width-2,var);
         addstr(buf);
       }
       break;
@@ -1547,7 +1805,7 @@
     break;
   default:
     if(f->edit){
-      pthread_mutex_lock(&master_mutex);
+      BEGIN_ATOMIC
       switch(ff->type){
       case FORM_YESNO:
         {
@@ -1708,7 +1966,7 @@
         ret=c;
         break;
       }
-      pthread_mutex_unlock(&master_mutex);
+      END_ATOMIC
 
     }else{
       ret=c;
@@ -1725,13 +1983,11 @@
     if(n>300)n=300;
     if(n<0)n=0;
 
-    pthread_mutex_lock(&master_mutex);
     main_master_volume=n;
-    pthread_mutex_unlock(&master_mutex);
     
     move(y,8);
     addstr("master: ");
-    sprintf(buf,"%3d%%",main_master_volume);
+    sprintf(buf,"%3ld%%",main_master_volume);
     addstr(buf);
   }
 }
@@ -1744,10 +2000,8 @@
     static int starver1=0;
     static int starver2=0;
 
-    pthread_mutex_lock(&master_mutex);
     n=playback_buffer_minfill;
     playback_buffer_minfill=playback_bufsize;
-    pthread_mutex_unlock(&master_mutex);
     
     if(n==0){
       starve=15;
@@ -1774,11 +2028,12 @@
     {
       int state=0;
       pthread_mutex_lock(&rec_mutex);
-      if(rec_flush_req)
+      if(rec_flush_req){
         if(rec_flush_ok)
           state=2;
         else
           state=1;
+      }
       pthread_mutex_unlock(&rec_mutex);
     
       switch(state){
@@ -1837,11 +2092,11 @@
                     1,1,1,1,1,1,1,1,1,1,
                     1,1,1,1,1,2,2,2,2,2,
                     2,2,2,2,2,2,2,2,2,2,
-		    2,2,2,2,2,2,2,2,2,2,
-		    3,3,3,3,3,3,3,3,3,3,
                     3,3,3,3,3,3,3,3,3,3,
-		    4,4,4,4,4,5,5,5,5,5,
-		    6,6,6,6,8,8,8,9,9,10};
+		    3,3,4,4,4,4,4,4,4,4,
+		    4,4,5,5,5,5,5,5,5,5,
+		    6,6,6,6,6,6,7,7,7,7,
+		    7,8,8,8,8,9,9,9,10,10};
 
 static int clip[MAX_OUTPUT_CHANNELS];
 void main_update_outchannel_levels(int y){
@@ -1850,10 +2105,10 @@
     for(i=0;i<MAX_OUTPUT_CHANNELS;i++){
       int val;
       char buf[11];
-      pthread_mutex_lock(&master_mutex);
+      BEGIN_ATOMIC
       val=channel_list[i].peak;
       channel_list[i].peak=0;
-      pthread_mutex_unlock(&master_mutex);
+      END_ATOMIC
       
       move(y+i+1,17);
       if(val>=32767){
@@ -1931,15 +2186,13 @@
   if(menu==MENU_MAIN){
     for(i=0;i<MAX_OUTPUT_CHANNELS;i++){
       move(y+i+1,4);
-      sprintf(buf,"-[          ]+0dB ",i);
+      sprintf(buf,"-[          ]+0dB ");
       addstr(buf);
-      pthread_mutex_lock(&master_mutex);
       addstr(channel_list[i].label);
-      pthread_mutex_unlock(&master_mutex);
     }
     for(i=0;i<2;i++){
       move(y+i+1,42);
-      sprintf(buf,"-[          ]+0dB ",i);
+      sprintf(buf,"-[          ]+0dB ");
       addstr(buf);
       pthread_mutex_lock(&rec_mutex);
       addstr(rchannel_list[i].label);
@@ -1956,7 +2209,6 @@
     int cn=cue_list_number-1,i;
 
     for(i=-1;i<2;i++){
-      char buf[80];
       move(y+i*3+3,0);
       if(i==0){
         addstr("NEXT => ");
@@ -2010,9 +2262,9 @@
 
     move(y,0);
     
-    pthread_mutex_lock(&master_mutex);
+    BEGIN_ATOMIC
+
     if(active_count){
-      int len;
       addstr("playing tags:");
 
       for(i=0;i<active_count;i++){
@@ -2034,6 +2286,7 @@
         addstr(buf);
         addnlstr(label_text(path),60,' ');
       }
+
     }else{
       addstr("             ");
     }
@@ -2044,7 +2297,7 @@
     }
 
     last_tags=active_count;
-    pthread_mutex_unlock(&master_mutex);
+    END_ATOMIC
   }
 }
 
@@ -2067,6 +2320,9 @@
       if(cue_list[cue_list_number].tag==-1)break;
     cue_list_position++;
   }
+  cache_cull();
+  cache_cue(cue_list_number);
+  wake_cache();
   main_update_cues(10+MAX_OUTPUT_CHANNELS);
 }
 
@@ -2076,6 +2332,9 @@
       if(cue_list[cue_list_number].tag==-1)break;
     cue_list_position--;
   }
+  cache_cull();
+  cache_cue(cue_list_number);
+  wake_cache();
   main_update_cues(10+MAX_OUTPUT_CHANNELS);
 }
 
@@ -2094,7 +2353,7 @@
   f=fopen(fn,"w");
   if(f==NULL){
     char buf[80];
-    sprintf(buf,"SAVE FAILED: %s",strerror(ferror(f)));
+    sprintf(buf,"SAVE FAILED: %s",strerror(errno));
     addnlstr(buf,80,' ');
     attroff(A_BOLD);
     return(1);
@@ -2162,7 +2421,6 @@
 
   while(1){
     int ch=getch();
-    const char *ctrl=unctrl(ch);
     switch(ch){
     case '?':
       return(MENU_KEYPRESS);
@@ -2321,7 +2579,6 @@
 }
 
 int add_cue_menu(){
-  int tnum=new_tag_number();
   form f;
 
   char label[13]="";
@@ -2363,16 +2620,16 @@
     unsaved=1;
     memset(&c,0,sizeof(c));
     c.tag=-1; /* placeholder cue for this cue bank */
-	/* aquire label locks, populate */
+	/* acquire label locks, populate */
     c.label=new_label_number();
     edit_label(c.label,label);
-    aquire_label(c.label);
+    acquire_label(c.label);
     c.cue_text=new_label_number();
     edit_label(c.cue_text,text);
-    aquire_label(c.cue_text);
+    acquire_label(c.cue_text);
     c.cue_desc=new_label_number();
     edit_label(c.cue_desc,desc);
-    aquire_label(c.cue_desc);
+    acquire_label(c.cue_desc);
     
     add_cue(cue_list_number,c);
     move_next_cue();
@@ -2698,11 +2955,11 @@
   /* finish the tag */
   {
     t.sample_path=new_label_number();
-    aquire_label(t.sample_path);
+    acquire_label(t.sample_path);
     edit_label(t.sample_path,path);
 
     t.sample_desc=new_label_number();
-    aquire_label(t.sample_desc);
+    acquire_label(t.sample_desc);
     edit_label(t.sample_desc,"");
     t.loop_p=0;
     t.loop_start=0;
@@ -2712,16 +2969,16 @@
     
     tagno=new_tag_number();
     edit_tag(tagno,t);
-    aquire_tag(tagno);
+    acquire_tag(tagno);
   }
 
   /* got it, add a new cue */
   {
     cue c;
     
-    aquire_label(c.label=cue_list[cue_list_number].label);
-    aquire_label(c.cue_text=cue_list[cue_list_number].cue_text);
-    aquire_label(c.cue_desc=cue_list[cue_list_number].cue_desc);
+    acquire_label(c.label=cue_list[cue_list_number].label);
+    acquire_label(c.cue_text=cue_list[cue_list_number].cue_text);
+    acquire_label(c.cue_desc=cue_list[cue_list_number].cue_desc);
     c.tag=tagno;
     c.tag_create_p=1;
     memset(&c.mix,0,sizeof(mix));
@@ -2801,10 +3058,10 @@
     int createno=tagno_to_cueno(tagno);
     unsaved=1;
 
-    aquire_tag(tagno);
-    aquire_label(c.label=cue_list[cue_list_number].label);
-    aquire_label(c.cue_text=cue_list[cue_list_number].cue_text);
-    aquire_label(c.cue_desc=cue_list[cue_list_number].cue_desc);
+    acquire_tag(tagno);
+    acquire_label(c.label=cue_list[cue_list_number].label);
+    acquire_label(c.cue_text=cue_list[cue_list_number].cue_text);
+    acquire_label(c.cue_desc=cue_list[cue_list_number].cue_desc);
     c.tag=tagno;
     c.tag_create_p=0;
     memset(&c.mix,0,sizeof(mix));
@@ -2949,6 +3206,9 @@
 
 int main_loop(){
   
+  cache_cue(0);
+  wake_cache();
+
   while(running){
     switch(menu){
     case MENU_MAIN:
@@ -2971,6 +3231,7 @@
       break;
     }
   }
+  return 0;
 }
 
 pthread_mutex_t pipe_mutex=PTHREAD_MUTEX_INITIALIZER;
@@ -2984,17 +3245,13 @@
       write(ttypipe[1],&buf,1);
     }
     
-    pthread_mutex_lock(&master_mutex);
     if(playback_exit)break;
-    pthread_mutex_unlock(&master_mutex);
   }
-  pthread_mutex_unlock(&master_mutex);
-
+  return NULL;
 }
 
 int main(int gratuitously,char *different[]){
   int lf;
-  int flags;
 
   if(gratuitously<2){
     fprintf(stderr,"Usage: beaverphonic <settingfile>\n");
@@ -3012,7 +3269,7 @@
   
   if(flock(lf,LOCK_EX|LOCK_NB)){
     fprintf(stderr,"Another Beaverphonic process is running.\n"
-	    "  (could not aquire lockfile)\n");
+	    "  (could not acquire lockfile)\n");
     exit(1);
   }
 
@@ -3055,6 +3312,7 @@
     pthread_t dummy;
     playback_active=1;
     pthread_create(&playback_thread_id,NULL,playback_thread,NULL);
+    pthread_create(&cache_thread_id,NULL,cache_thread,NULL);
   }
   {
     pthread_t dummy;
@@ -3063,6 +3321,7 @@
     pthread_create(&record_thread_id,NULL,record_thread,NULL);
   }
 
+  pthread_create(&cache_thread_id,NULL,cache_thread,NULL);
 
   /* load the sound config if the file exists, else create it */
   initscr(); cbreak(); noecho();
@@ -3093,9 +3352,7 @@
   endwin();                  /* restore original tty modes */  
   close(lf);
   halt_playback();
-  pthread_mutex_lock(&master_mutex);
   playback_exit=1;
-  pthread_mutex_unlock(&master_mutex);
   pthread_mutex_lock(&rec_mutex);
   rec_exit=1;
   pthread_mutex_unlock(&rec_mutex);
@@ -3106,12 +3363,9 @@
   pthread_mutex_unlock(&rec_buffer_mutex);
 
   while(1){
-    pthread_mutex_lock(&master_mutex);
     if(!playback_active)break;
-    pthread_mutex_unlock(&master_mutex);
     sched_yield();
   }
-  pthread_mutex_unlock(&master_mutex);
 
   while(1){
     pthread_mutex_lock(&rec_mutex);
@@ -3124,6 +3378,7 @@
   fclose(recfd);
 
   unlink(lockfile);
+  return 0;
 }  
 
 

<p><p>--- >8 ----
List archives:  http://www.xiph.org/archives/
Ogg project homepage: http://www.xiph.org/ogg/
To unsubscribe from this list, send a message to 'cvs-request at xiph.org'
containing only the word 'unsubscribe' in the body.  No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.



More information about the commits mailing list