[xiph-cvs] cvs commit: postfish postfish.1 form.c postfish.c

Monty xiphmont at xiph.org
Sun Dec 1 18:17:27 PST 2002



xiphmont    02/12/01 21:17:27

  Modified:    .        form.c postfish.c
  Added:       .        postfish.1
  Log:
  yay!  Welcome to life, postfish!

Revision  Changes    Path
1.5       +2 -2      postfish/form.c

Index: form.c
===================================================================
RCS file: /usr/local/cvsroot/postfish/form.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- form.c	1 Dec 2002 08:34:40 -0000	1.4
+++ form.c	2 Dec 2002 02:17:27 -0000	1.5
@@ -277,9 +277,9 @@
 
 void form_next_field(form *f){
   int v=f->cursor;
-  int t=(v+1>=f->count?v+1:0);
+  int t=(v+1>=f->count?0:v+1);
   while(t!=v && !f->fields[t].active)
-    t=(t+1>=f->count?t+1:0);
+    t=(t+1>=f->count?0:t+1);
   
   f->cursor=t;
   draw_field(f->fields+v);

<p><p>1.5       +138 -111  postfish/postfish.c

Index: postfish.c
===================================================================
RCS file: /usr/local/cvsroot/postfish/postfish.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- postfish.c	1 Dec 2002 08:34:40 -0000	1.4
+++ postfish.c	2 Dec 2002 02:17:27 -0000	1.5
@@ -130,6 +130,11 @@
 
 static long block=8192;
 
+static int feedback_n_displayed=0;
+static int feedback_e_displayed=0;
+static int feedback_a_displayed=0;
+static int feedback_l_displayed=0;
+
 static double eqt_feedbackmax[BANDS]={
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@@ -529,7 +534,7 @@
   mvaddstr(D_Y+6,D_X+18," +30");
 
   mvaddstr(D_Y+8,D_X,"[l] Limiter                  dB");
-  mvaddstr(D_Y+9,D_X,"    Block period             ms");
+  mvaddstr(D_Y+9,D_X,"    Block Period             ms");
   mvaddstr(D_Y+11,D_X,"                             dB");
 
   mvvline(D_Y+11,D_X,0,1);
@@ -568,8 +573,8 @@
 
   T_field=field_add(&noneditf,FORM_TIME,D_X+4,D_Y+16,11,&T,NULL,0,0,99999999);
 
-  pre_field=field_add(&noneditf,FORM_DB,D_X+23,D_Y+5,5,&pre_var,NULL,0,-30,30);
-  post_field=field_add(&noneditf,FORM_DB,D_X+23,D_Y+11,5,&post_var,NULL,0,-30,0);
+  pre_field=field_add(&noneditf,FORM_DB,D_X+23,D_Y+5,5,&pre_var,NULL,0,-300,300);
+  post_field=field_add(&noneditf,FORM_DB,D_X+23,D_Y+11,5,&post_var,NULL,0,-300,00);
 
 }
 
@@ -659,31 +664,35 @@
   draw_field(B_field);
 }
 
-void update_ui(){
+void update_ui(int up){
   int i,j;
-
+  int valM[BANDS],valA[BANDS];
+  
   pthread_mutex_lock(&master_mutex); 
   T=cursor_to_time(cursor);
   pthread_mutex_unlock(&master_mutex); 
   draw_field(T_field);
-
+  
+  pthread_mutex_lock(&master_mutex);
   for(i=0;i<BANDS;i++){
-    int valM,valA,valT;
-    valT=rint(wc.eqt[i]/30.+10);
-    pthread_mutex_lock(&master_mutex);
-    valM=rint(todB(eqt_feedbackmax[i])/5.7142857+21);
-    valA=rint(todB(eqt_feedbackav[i]/eqt_feedbackcount[i])/5.7142857+21);
-    pthread_mutex_unlock(&master_mutex);
+    valM[i]=rint(todB(eqt_feedbackmax[i])/5.7142857+21);
+    valA[i]=rint(todB(eqt_feedbackav[i]/eqt_feedbackcount[i])/5.7142857+21);
+  }
+  if(up)feedback_e_displayed=1;
+  pthread_mutex_unlock(&master_mutex);
 
+  for(i=0;i<BANDS;i++){
+    int valT=rint(wc.eqt[i]/30.+10);
+    
     move(E_Y+3+i,E_X+9);
-    for(j=0;j<valA && j<21;j++){
+    for(j=0;j<valA[i] && j<21;j++){
       if(j==valT)
         addch(ACS_VLINE);
       else
         addch(' ');
     }
 
-    for(;j<=valM && j<21;j++){
+    for(;j<=valM[i] && j<21;j++){
       if(j==valT)
         addch(ACS_PLUS);
       else
@@ -699,10 +708,12 @@
   }
 }
 
-void update_play(){
+void update_play(int up){
   int i,j;
+  int valM[BANDS],valA[BANDS];
+
   pthread_mutex_lock(&master_mutex); 
-  update_ui();
+  update_ui(up);
   if(playback_active){
     if(B!=-1){
       pthread_mutex_unlock(&master_mutex);
@@ -722,21 +733,22 @@
         mvaddstr(D_Y+16,D_X,"|||");
   }
   
+  pthread_mutex_lock(&master_mutex);
   for(i=0;i<BANDS;i++){
-    int valM,valA;
-    pthread_mutex_lock(&master_mutex);
-
     if(noiset_feedbackcount[i]){
-      valM=rint(todB(noiset_feedbackmax[i])/3.+10);
-      valA=rint(todB(noiset_feedbackav[i]/noiset_feedbackcount[i])/3.+10);
+      valM[i]=rint(todB(noiset_feedbackmax[i])/3.+10);
+      valA[i]=rint(todB(noiset_feedbackav[i]/noiset_feedbackcount[i])/3.+10);
     }else{
-      valM=-1;
-      valA=-1;
+      valM[i]=-1;
+      valA[i]=-1;
     }
-    pthread_mutex_unlock(&master_mutex);
+  }
+  if(up)feedback_n_displayed=1;
+  pthread_mutex_unlock(&master_mutex);
 
+  for(i=0;i<BANDS;i++){
     move(N_Y+3+i,N_X+10);
-    for(j=0;j<valA && j<=21;j++){
+    for(j=0;j<valA[i] && j<=21;j++){
       if(j==21)
         addch('+');
       else
@@ -746,7 +758,7 @@
           addch(' ');
     }
 
-    for(;j<=valM && j<=21;j++){
+    for(;j<=valM[i] && j<=21;j++){
       if(j==21)
         addch('+');
       else
@@ -764,16 +776,6 @@
         addch(' ');
     }
 
-
-    noiset_feedbackav[i]/=2;
-    noiset_feedbackmax[i]/=2;
-    noiset_feedbackcount[i]/=2;
-  }
-
-  for(i=0;i<BANDS;i++){
-    eqt_feedbackav[i]/=2;
-    eqt_feedbackmax[i]/=2;
-    eqt_feedbackcount[i]/=2;
   }
 
   /* pre-limit bargraph */
@@ -784,7 +786,8 @@
     pthread_mutex_lock(&master_mutex);
     if(maxtimeprehold){
       val=maxtimepre;
-      pre_var=rint(todB(maxtimeprehold));
+      pre_var=rint(todB(maxtimeprehold)*10.);
+      if(up)feedback_a_displayed=1;
       pthread_mutex_unlock(&master_mutex);
       
       ival=(todB(val)+30.)/3.;
@@ -808,7 +811,6 @@
           addch(' ');
       }
       
-      maxtimepre/=2;
       draw_field(pre_field);
     }else{
       pthread_mutex_unlock(&master_mutex);
@@ -829,7 +831,8 @@
     pthread_mutex_lock(&master_mutex);
     if(&wc.dyn_p && maxtimeposthold){
       val=maxtimepost;
-      post_var=rint(todB(maxtimeposthold));
+      post_var=rint(todB(maxtimeposthold)*10.);
+      if(up)feedback_l_displayed=1;
       pthread_mutex_unlock(&master_mutex);
       
       if(todB(val)<wc.dynt*.1){
@@ -861,7 +864,6 @@
           addch(' ');
       }
       
-      maxtimepost/=2;
       draw_field(post_field);
     }else{
       pthread_mutex_unlock(&master_mutex);
@@ -917,6 +919,14 @@
     double y=0;
     double d=0;
     long ms=wc.dynms/10;
+
+    if(feedback_a_displayed)
+      maxtimepre/=2;
+    if(feedback_l_displayed)
+      maxtimepost/=2;
+    feedback_a_displayed=0;
+    feedback_l_displayed=0;
+
     pthread_mutex_unlock(&master_mutex);
     
     for(j=0;j<ch;j++)
@@ -1016,19 +1026,29 @@
   int i;
   double dv;
   pthread_mutex_lock(&master_mutex);
-  if(wc.noise_p){
-    if(noise_dirty){
-      refresh_thresh_array(noise_tfull,wc.noiset);
-      noise_dirty=0;
+
+  if(noise_dirty){
+    refresh_thresh_array(noise_tfull,wc.noiset);
+    noise_dirty=0;
+  }
+  
+  if(feedback_n_displayed)
+    for(i=0;i<BANDS;i++){
+      noiset_feedbackav[i]/=2;
+      noiset_feedbackmax[i]/=2;
+      noiset_feedbackcount[i]/=2;
     }
-      
-    pthread_mutex_unlock(&master_mutex);
-    
-    dv=fabs(b[0])/noise_tfull[0];
-    if(noiset_feedbackmax[oc[0]]<dv)noiset_feedbackmax[oc[0]]=dv;
-    noiset_feedbackav[oc[0]]+=dv;
-    noiset_feedbackcount[oc[0]]++;
+  feedback_n_displayed=0;
+
+  pthread_mutex_unlock(&master_mutex);
+  
+  dv=fabs(b[0])/noise_tfull[0];
+  if(noiset_feedbackmax[oc[0]]<dv)noiset_feedbackmax[oc[0]]=dv;
+  noiset_feedbackav[oc[0]]+=dv;
+  noiset_feedbackcount[oc[0]]++;
+  
 
+  if(wc.noise_p){
     if(b[0]<0){
       b[0]+=noise_tfull[0];
       if(b[0]>0)b[0]=0;
@@ -1036,15 +1056,17 @@
       b[0]-=noise_tfull[0];
       if(b[0]<0)b[0]=0;
     }
+  }
 
-    for(i=1;i<block/2;i++){
-      double M=sqrt(b[i*2]*b[i*2]+b[i*2-1]*b[i*2-1]);
-      
-      dv=M/noise_tfull[i];
-      if(noiset_feedbackmax[oc[i]]<dv)noiset_feedbackmax[oc[i]]=dv;
-      noiset_feedbackav[oc[i]]+=dv;
-      noiset_feedbackcount[oc[i]]++;
-
+  for(i=1;i<block/2;i++){
+    double M=sqrt(b[i*2]*b[i*2]+b[i*2-1]*b[i*2-1]);
+    
+    dv=M/noise_tfull[i];
+    if(noiset_feedbackmax[oc[i]]<dv)noiset_feedbackmax[oc[i]]=dv;
+    noiset_feedbackav[oc[i]]+=dv;
+    noiset_feedbackcount[oc[i]]++;
+    
+    if(wc.noise_p){
       if(M-noise_tfull[i]>0){
         double div=(M-noise_tfull[i])/M;
         b[i*2]*=div;
@@ -1054,12 +1076,14 @@
         b[i*2-1]=0;
       }
     }
-
-    dv=fabs(b[block-1])/noise_tfull[block/2];
-    if(noiset_feedbackmax[oc[block/2]]<dv)noiset_feedbackmax[oc[block/2]]=dv;
-    noiset_feedbackav[oc[block/2]]+=dv;
-    noiset_feedbackcount[oc[block/2]]++;
-
+  }
+  
+  dv=fabs(b[block-1])/noise_tfull[block/2];
+  if(noiset_feedbackmax[oc[block/2]]<dv)noiset_feedbackmax[oc[block/2]]=dv;
+  noiset_feedbackav[oc[block/2]]+=dv;
+  noiset_feedbackcount[oc[block/2]]++;
+  
+  if(wc.noise_p){
     if(b[i*2-1]<0){
       b[i*2-1]+=noise_tfull[block/2];
       if(b[i*2-1]>0)b[i*2-1]=0;
@@ -1067,9 +1091,6 @@
       b[i*2-1]-=noise_tfull[block/2];
       if(b[i*2-1]<0)b[i*2-1]=0;
     }
-
-  }else{
-    pthread_mutex_unlock(&master_mutex);
   }
 }
 
@@ -1077,41 +1098,48 @@
   int i;
   double dv;
   pthread_mutex_lock(&master_mutex);
-  if(wc.eq_p){
 
-    if(eq_dirty){
-      refresh_thresh_array(eq_tfull,wc.eqt);
-      eq_dirty=0;
-    }
+  if(eq_dirty){
+    refresh_thresh_array(eq_tfull,wc.eqt);
+    eq_dirty=0;
+  }
       
-    pthread_mutex_unlock(&master_mutex);
-    
+  if(feedback_e_displayed)
+    for(i=0;i<BANDS;i++){
+      eqt_feedbackav[i]/=2;
+      eqt_feedbackmax[i]/=2;
+      eqt_feedbackcount[i]/=2;
+    }
+  feedback_e_displayed=0;
+  pthread_mutex_unlock(&master_mutex);
+  
+  if(wc.eq_p)
     b[0]*=eq_tfull[0];
-    dv=fabs(b[0]);
-    if(eqt_feedbackmax[oc[0]]<dv)eqt_feedbackmax[oc[0]]=dv;
-    eqt_feedbackav[oc[0]]+=dv;
-    eqt_feedbackcount[oc[0]]++;
-    
-    for(i=1;i<block/2;i++){
+  dv=fabs(b[0]);
+  if(eqt_feedbackmax[oc[0]]<dv)eqt_feedbackmax[oc[0]]=dv;
+  eqt_feedbackav[oc[0]]+=dv;
+  eqt_feedbackcount[oc[0]]++;
+  
+  for(i=1;i<block/2;i++){
+    if(wc.eq_p){
       b[i*2]*=eq_tfull[i];
       b[i*2-1]*=eq_tfull[i];
-      
-      dv=sqrt(b[i*2]*b[i*2]+b[i*2-1]*b[i*2-1]);
-      if(eqt_feedbackmax[oc[i]]<dv)eqt_feedbackmax[oc[i]]=dv;
-      eqt_feedbackav[oc[i]]+=dv;
-      eqt_feedbackcount[oc[i]]++;
-
     }
-
-    b[block-1]*=eq_tfull[block/2-1];
-    dv=fabs(b[block-1]);
+    
+    dv=sqrt(b[i*2]*b[i*2]+b[i*2-1]*b[i*2-1]);
     if(eqt_feedbackmax[oc[i]]<dv)eqt_feedbackmax[oc[i]]=dv;
     eqt_feedbackav[oc[i]]+=dv;
     eqt_feedbackcount[oc[i]]++;
-
-  }else{
-    pthread_mutex_unlock(&master_mutex);
+    
   }
+  
+  if(wc.eq_p)
+    b[block-1]*=eq_tfull[block/2-1];
+  dv=fabs(b[block-1]);
+  if(eqt_feedbackmax[oc[i]]<dv)eqt_feedbackmax[oc[i]]=dv;
+  eqt_feedbackav[oc[i]]+=dv;
+  eqt_feedbackcount[oc[i]]++;
+ 
 }
 
 int aseek(off_t pos){
@@ -1897,7 +1925,7 @@
     form_init(&editf,120,1);
     form_init(&noneditf,50,0);
     box(stdscr,0,0);
-    mvaddstr(0, 2, " Postfish Filter $Id: postfish.c,v 1.4 2002/12/01 08:34:40 xiphmont Exp $ ");
+    mvaddstr(0, 2, " Postfish Filter $Id: postfish.c,v 1.5 2002/12/02 02:17:27 xiphmont Exp $ ");
     mvaddstr(LINES-1, 2, 
              "  [<]<<   [,]<   [Spc] Play/Pause   [Bksp] Stop/Cue   [.]>   [>]>>  ");
 
@@ -1915,7 +1943,7 @@
     update_a();
     update_b();
     update_C();
-    update_play();
+    update_play(0);
     update_static_0();
 
     refresh();
@@ -1938,25 +1966,25 @@
         pthread_mutex_lock(&master_mutex);
         aseek(cursor-rate*ch*inbytes*60);
         pthread_mutex_unlock(&master_mutex);
-	update_ui();
+	update_ui(0);
         break;
       case ',':
         pthread_mutex_lock(&master_mutex);
         aseek(cursor-rate*ch*inbytes*5);
         pthread_mutex_unlock(&master_mutex);
-	update_ui();
+	update_ui(0);
         break;
       case '>':
         pthread_mutex_lock(&master_mutex);
         aseek(cursor+rate*ch*inbytes*60);
         pthread_mutex_unlock(&master_mutex);
-	update_ui();
+	update_ui(0);
         break;
       case '.':
         pthread_mutex_lock(&master_mutex);
         aseek(cursor+rate*ch*inbytes*5);
         pthread_mutex_unlock(&master_mutex);
-	update_ui();
+	update_ui(0);
         break;
       case 'n':
         pthread_mutex_lock(&master_mutex);
@@ -1997,7 +2025,7 @@
             pthread_mutex_lock(&master_mutex);
             aseek(time_to_cursor(TX[num]));
             pthread_mutex_unlock(&master_mutex);
-	    update_play();
+	    update_play(0);
           }else{
             pthread_mutex_lock(&master_mutex);
             TX[num]=cursor_to_time(cursor);
@@ -2005,7 +2033,7 @@
             update_0();
           }
         }
-	update_ui();
+	update_ui(0);
         break;
       case ')':
         TX[0]=-1;update_0();break;
@@ -2043,7 +2071,7 @@
         }else{
           aseek(Acursor);
           pthread_mutex_unlock(&master_mutex);
-	  update_play();
+	  update_play(0);
         }
         break;	    
       case 'b':
@@ -2061,7 +2089,7 @@
           update_b();
         }else
           pthread_mutex_unlock(&master_mutex);
-	  update_play();
+	  update_play(0);
         break;
       case 'A':
         pthread_mutex_lock(&master_mutex);
@@ -2094,14 +2122,14 @@
         }
         aseek(Acursor);
         pthread_mutex_unlock(&master_mutex);
-	update_play();
+	update_play(0);
         break;
       case ' ':
         pthread_mutex_lock(&master_mutex);
         if(!playback_active){
           playback_active=1;
           pthread_mutex_unlock(&master_mutex);
-	  update_play();
+	  update_play(0);
           pthread_create(&playback_thread_id,NULL,&playback_thread,NULL);
         }else{
           playback_exit=1;
@@ -2116,7 +2144,7 @@
               break;
           }
           pthread_mutex_unlock(&master_mutex);
-	  update_play();
+	  update_play(0);
         }
         break;
       case 'v':case 'w':case 'x':case 'y':case 'z':
@@ -2136,7 +2164,7 @@
           update_C();
           update_N();
           update_E();
-	  update_ui();
+	  update_ui(0);
           form_redraw(&editf);
           form_redraw(&noneditf);
           update_D();
@@ -2158,18 +2186,17 @@
           update_C();
           update_N();
           update_E();
-	  update_ui();
+	  update_ui(0);
           form_redraw(&editf);
           form_redraw(&noneditf);
           update_D();
         }
         break;
       case 0:
-	update_play();
-	update_ui();
+	update_play(1);
         break;
       default:
-	update_ui();
+	update_ui(0);
         break;
       }
     }

<p><p>1.1                  postfish/postfish.1

Index: postfish.1
===================================================================
.TH POSTFISH 1 "01 Dec 2002"
.SH NAME
postfish \- a lightweight, ncurses-based live recording postprocessor
.SH SYNOPSIS
.B postfish
[
.B \< input WAV 
]
[ 
.B input WAV list 
]
[
.B \> WAV file or device
]
.SH DESCRIPTION

.B postfish
takes a live stream or input file on stdin (or a list of contiguous
WAV files in an argument list), optionally performs one or all of
spectral noise gating, equalization, amplitude attenuation, dynamic
range attenuation, and limiting.  The processed audio is sent to
stdout as a WAV, which may be redirected to a file, redirected to an
audio device or piped to another process.  

Input is a WAV format data on stdin or as a list or arguments; if
input appears on stdin, the argument list is ignored. If input is
specified as a file list, Postfish assumes that the files are
matching, contiguous WAVs, as would be produced by Beaverphonic; the
audio in these files will be treated as a single, seamless audio
stream during processing and navigation.
.B postfish
is not capable of setting up a recording audio device, nor the
real-time scheduling necessary to guard against skips when sampling;
input can not be taken directly from a audio device.

If stdout is directed to a file or piped to another application,
output will be in 16 bit WAV format with the same sampling rate and
number of channels as input.  If stdout is redirected to an audio
device, this device will be used for real-time playback.  If stdout is
neither piped nor redirected,
.B postfish 
assumes audio device output on /dev/dsp.

.SH OPTIONS

None.  The application is interactive via an ncurses interface.

.SH UI NAVIGATION

Configurable fields nominally appear in 
.B bold
or
.B high intensity
text.  The field that currently responds to editing is represented in
reverse video. The arrow keys can be used to move the edit cursor
left, right, up and down; tab moves to the next field.  A selected
numeric field is incremented/decremented slowly via the '=' and '-'
keys.  For fast increment/decrement, use '+' and '_'.  The A and B
time cue fields can be edited directly like a text input area with a
text cursor.

Toggle fields are marked on-screen with the key used to activate or
toggle the field value in brackets.  For example, the equalizer is
turned on/off by pressing 'e'.  Toggle keys do not move the edit
cursor.

.SH NOISE GATING

A noise gate is used to attenuate/eliminate noise from a recording;
this noise might be white electrical noise or background room noise.
The 
.B postfish
noise gate is an FFT threshold-based rejector; any spectral line value
less than the interactively chosen threshold value is rejected
entirely.  The lines on the graph displayed during playback are
dB-scale average-to-max values for that spectral band relative to the
desired threshold value.  For example, if the input spectral energy
of a given band exceeds the threshold, the spectral line will extend
to the right past the center graph line.  If the spectral band is
being rejected completely, the spectral line will lie entirely to the
left of the center line.  If the band is currently only being
partially rejected, the spectral line will straddle the center line.

Each quarter-octave band has an independent, configurable threshold
value; these values are absolute dB values of the input signal
relative to the maximum possible signal level (0dB).  A lower (more
negative) value supplies less rejection; A higher (more positive)
value specifies more rejection.

.TP
.RB [ n ]
toggles the noise gate on and off.  When off, the noise gate is still
editable but
.B postfish
does not perform gating. The on-screen graph is primarily intended to
assist in selecting configuration settings and is unaltered by the
noise gate being active or inactive.
.TP
.RB [ N ]
toggles the noise gate edit mode between narrow-band and wideband mode.
In narrow-band mode, incrementing/decrementing a band threshold level
affects only that band.  Wideband mode allows 'painting with a broader
brush' and allows the increment/decrement to also pull up or push down
the values of neighboring bins into a wide, smooth bump or pit in the
spectrum.

.SH EQUALIZATION

An equalizer is used to adjust or correct the frequency
response/spectral bias of a recording, or to implement special effects
(such as a broad boost to bass).  

.B postfish
implements a 35 band quarter-octave equalizer that allows up to 30 dB
of cut/boost per band.  The center line of the EQ graph is perturbed
left or right to reflect the configured cut/boost.  A horizontal
signal level line spans from average to maximum signal level in that
band.  The displayed signal is post-gating/post-eq and prior any
attenuation or limiting.

.TP
.RB [ e ]
toggles the equalizer on and off.  When off, the equalizer is still
editable, but
.B postfish
does not perform equalization.  The on-screen graph displays spectral
data according to equalizer mode; if equalization is inactive, the
graph represents unequalized data.
.TP
.RB [ E ]
toggles the equalizer edit mode between narrow-band and wideband mode.
In narrow-band mode, incrementing/decrementing a band level affects
only that band.  Wideband mode allows 'painting with a broader brush'
and allows the increment/decrement to also pull up or push down the
values of neighboring bins into a wide, smooth bump or pit in the
spectrum.

.SH MASTER AMPLITUDE ATTENUATION

A fancy name for a volume knob.  The attenuation is performed after
gating and EQ, but before any limiting.  The upper single-bar graph on
the far right hand side of the terminal displays the peak output
signal level after any attenuation and before any limiting.  The bar
represents the instantaneous peak and the number to the right of the
bar displays peak signal since playback began.

Note that +0dB is the maximum representable output signal level; if
the signal level reaches 0dB, it begins to clip (see
limiting/soft-clipping below).

Master attenuation is expressed in dB; positive dB will increase
the signal, negative dB will decrease it.

.TP
.RB [ m ]
toggles Master Attenuation on and off.  Even when off, 
.B postfish 
updates the bargraph with unattenuated signal level.

.SH DYNAMIC RANGE ATTENUATION

Dynamic range attenuation is used to shrink or expand to total signal
depth of a recording.

A shallow signal depth (negative attenuation) makes sound levels
of different elements of a recording sound closer together in
amplitude, typically resulting in making softer elements sound louder,
but keeping already loud aspects the same.  Most 'pop' music has
substantial negative dynamic range attenuation.

Deepening signal depth (positive attenuation) broadens the perceived
volume range of a recording.  This can be used to restore depth to a
recording that has previously had its dynamic range shrunk, or to hide
low level noise by growing the range, making soft noise even softer.

Dynamic range attenuation is performed at the same time as amplitude
attenuation (after gating/EQ and before limiting). The upper
single-bar graph on the far right hand side of the terminal displays
the peak output signal level after attenuation and before any
limiting.  The bar represents the instantaneous peak and the number to
the right of the bar displays peak signal since playback began.

Note that +0dB is the maximum representable output signal level; if
the signal level reaches 0dB, it begins to clip (see
limiting/soft-clipping below).

.TP
.RB [ r ]
toggles Dynamic Range attenuation on and off.  Even when off, 
.B postfish 
updates the bargraph with unattenuated signal level.

.SH FRAME SIZE

.B postfish
performs most processing operations in the frequency domain.  Data is
taken from PCM representation to frequency domain via a windowed,
50%-lapped FFT transform.  An FFT must operate on frames of data at a
time; the size of these frames is configurable here.

Unlike all other
.B postfish
configuration fields which take effect immediately during
playback/processing, frame size configuration takes effect only when
playback/processing is stopped.  To change frame size during playback,
alter the requested frame size, then pause and restart playback (such
as by hitting
.RB [ space ]
twice).

The choice of frame size represents a tradeoff between time
resolution and frequency resolution.  For most applications, there's a
nice wide sweet-spot of appropriate frame sizes in the middle of the
range.  In summary:

.TP
a larger frame size yields higher-quality, more stable output
(especially for noise gating), but the filters 'react' more slowly.
.TP
a smaller frame size yields faster 'reactions', but because the
smaller transform is more prone to sidelobe energy leakage, the
filters are lower quality and can produce increasingly odd artifacts
when used aggressively.

.SH LIMITING / SOFT CLIPPING

When a digital PCM signal exceeds exactly 0dB, the loudest
representable sound, it hits a brick wall.  It can get no louder and
instantly saturates, creating a sharp 'corner' in the waveform that
shows up as potentially very high energy, spread-spectrum noise.

One generally strives to avoid audio intentionally exceeding
representation range.  However, attenuating an entire recording of
soft music, for example, to avoid clipping during applause at the end
may be undesirable.  Dynamic limiting is an easy, effective way to
cheat.
.B postfish
offers two limiter types: a block attenuator and a soft-clipper. 

Soft Clip applies a non-linear function directly to the
instantaneous waveform amplitude. At amplitudes under the configured
threshold, the waveform is linear as normal.  At amplitudes over the
threshold, the waveform amplitude is rolled off in a standard 1/x
inverse relationship asymptotically approaching 0dB.  The end result
is to 'round off' the corners of any clipping that would have
happened.  Soft clipping doesn't eliminate clipping noise, but it can
substantially hide it.

Block Attenuation works on frames of data and behaves much like the
\'record-auto-level' feature on portable tape recorders.  It selects
the peak amplitude seen over a configurable time period (the Block
Period setting), determines a scaling value for the entire
current frame by applying the same nonlinear transfer function as in
the Soft Clipper to the peak value and then attenuating the
entire frame by that scaling value.  The effect is much like a
hyper-fast volume knob (how fast can be configured using the Block Period
field) keeping the output data from clipping.

The second single-bar graph on the far right hand side of the terminal
shows output level after applying limiting.  As the graph is most
useful for configuration, it is updated as if limiting occurred even
when the Limiter is turned off.  The center line of the graph, marked
\'A', denotes the limiter threshold where the inverse roll-off begins.
The numeric value to the right of the bargraph indicates the largest
peak-limited amplitude since playback began.

.TP
.RB [ l ]
flips between the three possible Limiter modes: Soft Clip, Block
Attenuate and Off.

.SH CUEING / FILE NAVIGATION

If the input is a seekable file or files, 
.B postfish
provides seeking and cueing functions in the form of editable A and B
cue points, A-B looped playback mode, and ten settable cue 'tabs'.

The A and B fields may be edited directly by selecting via
.RB [ tab ]
or the arrow keys, and then entering a numeric value using the keyboard.
The time fields are expressed in HH:MM:SS.FF format where HH is hours,
MM is minutes, SS is seconds and FF is hundredths of a second.

In addition, the A and B fields, if empty, may be set directly from
the playback time cursor (displayed below A and B) by pressing
.RB [ a ]
or
.RB [ b ].
If the A field already contains a time, pressing
.RB [ a ]
will instantly cue the playback cursor to the time in the A field.  If
playback is in progress, playback will jump directly to A.  If
playback is stopped, the playback cursor will cue to A but playback
will not begin.  If both A and B contain times, playback will be in
\'A-B' mode, in which playback will continuously loop between points A
and B. Pressing 
.RB [ A ] 
or 
.RB [ B ]
will clear the time fields A and B, respectively.  Clearing the B field
will exit A-B loop mode.

The cue tabs behave like the A field, but are not keyboard
editable. Set a cue tab from the current playback cursor by pressing
.RB [ 0-9 ]
to select an empty cue tab.  If a cue tab is already set, pressing 
.RB [ 0-9 ]
will instantly cue the playback cursor to that time.  Cue tabs may be
cleared using
.RB [ )-( ],
that is, 0 through 9 shifted.

Other playback navigation controls are included below.
.TP
.RB [ < ]
Jump back one minute
.TP
.RB [ > ]
Jump ahead one minute
.TP
.RB [ , ]
Jump back five seconds
.TP
.RB [ . ]
Jump ahead five seconds
.TP
.RB [ space ]
Toggle between playback and pause.  Does not affect cueing.
.TP
.RB [ Backspace ]
halt playback and cue playback cursor to time in field A.
.TP
.RB [ a ]
If A is empty, store current playback cursor in A.  Else, cue playback
cursor to A.
.TP
.RB [ A ]
Clear field A.
.TP
.RB [ b ]
If B is empty, store current playback cursor in B; this places
playback in A-B loop mode.
.TP
.RB [ B ]
Clear field B.
.TP
.RB [ 0-9 ]
If cue tab is empty, store current playback cursor in tab.  Else, cue
playback cursor to tab.
.TP
.RB [ )-( ]
Clear cue tab.

.SH CONFIGURATION BANKING

.B postfish
allows a user to flip back and forth quickly amongst several
configurations.  These configurations are stored in a bank-like
system.  Switching from bank to bank requires a single keypress and
the new configuration applies to playback instantly.  All noise gate,
EQ, attenuation and limiting parameters are handled by bank.  Time
queueing information and edit modes are global settings and not stored
by bank.

The five configuration banks are labelled v through z and may be
selected by pressing
.RB [ v-z ].  
Visible settings are stored in the configuration bank marked
\'Current'.  Selecting another bank marked \'full' will switch
instantly to the configuration in that bank.  Returning to the
previous bank will restore the previous settings.  Selecting an empty
bank will switch to that bank, but retain current configuration.  This
interface prevents any accidental loss of data when switching banks;
the current configuration is always stored somewhere upon a switch.

Clear a bank by pressing 
.RB [ V-Z ].
When a current bank is cleared, it reverts to default settings.

All configuration data, including all banks, the current active bank
and all global configuration is saved into the file \'.postfishrc' in
the current working directory upon quitting the application with
.RB [ q ].
This configuration file is loaded upon application startup.  The
\'.postfishrc' allows the new
.RB postfish
process to pick up exactly where the previous one left off.  The only
unsaved parameters are the current playback cursor and the current
edit cursor position.

.P
.SH AUTHOR
Monty <monty at xiph.org>

.B postfish 
lives at cvs.xiph.org:/usr/local/cvsroot, username 'anoncvs', password
\'anoncvs', module 'postfish'. Sorry, no homepage.

<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