limit.c 6.18 KB
Newer Older
1 2 3 4
/*
 *
 *  postfish
 *    
Monty Montgomery's avatar
Monty Montgomery committed
5
 *      Copyright (C) 2002-2005 Monty and Xiph.Org
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 *
 *  Postfish is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  Postfish is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with Postfish; see the file COPYING.  If not, write to the
 *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * 
 */

#include "postfish.h"
#include "feedback.h"
#include "bessel.h"
#include "limit.h"
28
#include "window.h"
29 30 31 32 33 34 35 36 37 38 39 40 41

extern int input_size;
extern int input_rate;

sig_atomic_t limit_active;
sig_atomic_t limit_visible;

typedef struct{
  time_linkage out;
  feedback_generic_pool feedpool;

  iir_state *iir;
  iir_filter decay;
42
  iir_filter limit;
43

44 45
  int prev_active;
  int initted;
46 47 48

  float pthresh;
  float pdepth;
49 50 51 52
} limit_state;

limit_settings limitset;
limit_state limitstate;
53
static float *window;
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73

/* feedback! */
typedef struct limit_feedback{
  feedback_generic parent_class;
  float *peak;
  float *att;
} limit_feedback;

static feedback_generic *new_limit_feedback(void){
  limit_feedback *ret=calloc(1,sizeof(*ret));
  return (feedback_generic *)ret;
}


int pull_limit_feedback(float *peak,float *att){
  limit_feedback *f=(limit_feedback *)feedback_pull(&limitstate.feedpool);
  
  if(!f)return 0;
  
  if(peak)
74
    memcpy(peak,f->peak,sizeof(*peak)*limitstate.out.channels);
75
  if(att)
76
    memcpy(att,f->att,sizeof(*att)*limitstate.out.channels);
77 78 79 80 81
  feedback_old(&limitstate.feedpool,(feedback_generic *)f);
  return 1;
}

/* called only by initial setup */
82
int limit_load(int ch){
83 84 85
  int i;
  memset(&limitstate,0,sizeof(limitstate));

86 87 88 89
  limitstate.iir=calloc(ch,sizeof(*limitstate.iir));
  limitstate.out.channels=ch;
  limitstate.out.data=malloc(ch*sizeof(*limitstate.out.data));
  for(i=0;i<ch;i++)
90 91
    limitstate.out.data[i]=malloc(input_size*sizeof(**limitstate.out.data));

92
  window=window_get(1,input_size);
93

94 95 96 97
  return(0);
}

static void filter_set(float msec,
98
		       int order,
99 100 101 102 103
                       iir_filter *filter){
  float alpha;
  float corner_freq= 500./msec;
  
  alpha=corner_freq/input_rate;
104
  filter->g=mkbessel(alpha,order,filter->c);
105 106 107 108 109 110
  filter->alpha=alpha;
  filter->Hz=alpha*input_rate;
  filter->ms=msec;
}

/* called only in playback thread */
111
int limit_reset(void){
112 113
  /* reset cached pipe state */
  while(pull_limit_feedback(NULL,NULL));
114
  memset(limitstate.iir,0,limitstate.out.channels*sizeof(*limitstate.iir));
115
  limitstate.initted=0;
116 117 118 119 120 121 122 123 124
  return 0;
}

static inline float limit_knee(float x,float d){
  return (sqrtf(x*x+d)-x)*-.5f;
}


time_linkage *limit_read(time_linkage *in){
125 126 127
  int ch=limitstate.out.channels;
  float peakfeed[ch];
  float attfeed[ch];
128

129 130 131
  int activeC=limit_active;
  int activeP=limitstate.prev_active;

132
  int visible=limit_visible;
133
  int bypass;
134 135 136 137
  int i,k;

  float thresh=limitset.thresh/10.-.01;
  float depth=limitset.depth;
138

139 140 141 142
  float localpeak;
  float localatt;

  float decayms=limitset.decay*.1;
143 144 145 146
  if(decayms!=limitstate.decay.ms){
    filter_set(decayms,2,&limitstate.decay);
    filter_set(decayms,1,&limitstate.limit);
  }
147 148 149 150 151 152

  if(in->samples==0){
    limitstate.out.samples=0;
    return &limitstate.out;
  }

153 154 155
  depth=depth*.2;
  depth*=depth;

156 157 158 159
  if(!limitstate.initted){
    limitstate.initted=1;
    limitstate.prev_active=activeC;

160 161 162
    limitstate.pthresh=thresh;
    limitstate.pdepth=depth;
  }
163

164
  for(i=0;i<ch;i++){
165 166
    localpeak=0.;
    localatt=0.;
167 168

    bypass=!(activeC || activeP || visible) || mute_channel_muted(in->active,i);
169
    
170 171
    if((activeC || activeP) && !mute_channel_muted(in->active,i)){
      
172 173
      float *inx=in->data[i];
      float *x=limitstate.out.data[i];
174 175 176 177 178 179

      float prev_thresh=limitstate.pthresh;
      float prev_depth=limitstate.pdepth;

      float thresh_add=(thresh-prev_thresh)/input_size;
      float depth_add=(depth-prev_depth)/input_size;
180
      
181

182 183 184
      /* 'knee' the actual samples, compute attenuation depth */
      for(k=0;k<in->samples;k++){
	float dB=todB(inx[k]);
185
	float knee=limit_knee(dB-prev_thresh,prev_depth)+prev_thresh;
186
	float att=dB-knee;
187
	
188
	if(att>localatt)localatt=att;
189
	
190
	x[k]=att;
191 192 193

	prev_depth+=depth_add;
	prev_thresh+=thresh_add;
194
      }
195
	
196 197
      compute_iir_decay_limited(x,input_size,limitstate.iir+i,
				&limitstate.decay,&limitstate.limit);
198 199 200 201 202 203 204 205 206 207
      
      for(k=0;k<in->samples;k++)
	x[k]=inx[k]*fromdB(-x[k]);
      
      if(activeP && !activeC){
	/* transition to inactive */
	for(k=0;k<input_size;k++){
	  float w2=1.-window[k];
	  x[k]= x[k]*w2 + in->data[i][k]*window[k];
	}
208
      }
209 210 211 212 213 214 215 216 217
      if(!activeP && activeC){
	/* transition to active */
	for(k=0;k<input_size;k++){
	  float w2=1.-window[k];
	  x[k]= x[k]*window[k] + in->data[i][k]*w2;
	}
      }
      
    }else{
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
      float *temp=in->data[i];
      in->data[i]=limitstate.out.data[i];
      limitstate.out.data[i]=temp;
    }

    if(!bypass){
      float *x=limitstate.out.data[i];
      /* get peak feedback */
      for(k=0;k<in->samples;k++)
	if(fabs(x[k])>localpeak)localpeak=fabs(x[k]);

    }

    peakfeed[i]=todB(localpeak);
    attfeed[i]=localatt;
    
  }

  limitstate.out.samples=in->samples;
  
  /* finish up the state feedabck */
  {
    limit_feedback *ff=
      (limit_feedback *)feedback_new(&limitstate.feedpool,new_limit_feedback);
    
    if(!ff->peak)
244
      ff->peak=malloc(ch*sizeof(*ff->peak));
245 246
    
    if(!ff->att)
247
      ff->att=malloc(ch*sizeof(*ff->att));
248 249 250 251 252 253 254 255
    
    memcpy(ff->peak,peakfeed,sizeof(peakfeed));
    memcpy(ff->att,attfeed,sizeof(attfeed));

    feedback_push(&limitstate.feedpool,(feedback_generic *)ff);
  }
   
  {
256
    int tozero=input_size-limitstate.out.samples;
257 258 259 260 261
    if(tozero)
      for(i=0;i<limitstate.out.channels;i++)
        memset(limitstate.out.data[i]+limitstate.out.samples,0,sizeof(**limitstate.out.data)*tozero);
  }

262 263
  limitstate.out.active=in->active;
  limitstate.prev_active=activeC;
264 265 266
  limitstate.pthresh=thresh;
  limitstate.pdepth=depth;

267 268 269
  return &limitstate.out;
}