limit.c 6.14 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
/*
 *
 *  postfish
 *    
 *      Copyright (C) 2002-2004 Monty and Xiph.Org
 *
 *  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"

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 43
  int prev_active;
  int initted;
44 45 46

  float pthresh;
  float pdepth;
47 48 49 50
} limit_state;

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

/* 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)
72
    memcpy(peak,f->peak,sizeof(*peak)*limitstate.out.channels);
73
  if(att)
74
    memcpy(att,f->att,sizeof(*att)*limitstate.out.channels);
75 76 77 78 79
  feedback_old(&limitstate.feedpool,(feedback_generic *)f);
  return 1;
}

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

84 85 86 87
  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++)
88 89
    limitstate.out.data[i]=malloc(input_size*sizeof(**limitstate.out.data));

90 91 92 93 94 95
  window=malloc(input_size*sizeof(*window));
  for(i=0;i<input_size;i++){
    window[i]=sin((i+.5)/input_size*M_PI*.5);
    window[i]*=window[i];
  }

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
  return(0);
}

static void filter_set(float msec,
                       iir_filter *filter){
  float alpha;
  float corner_freq= 500./msec;
  
  alpha=corner_freq/input_rate;
  filter->g=mkbessel(alpha,2,filter->c);
  filter->alpha=alpha;
  filter->Hz=alpha*input_rate;
  filter->ms=msec;
}

/* called only in playback thread */
112
int limit_reset(void){
113 114
  /* reset cached pipe state */
  while(pull_limit_feedback(NULL,NULL));
115
  memset(limitstate.iir,0,limitstate.out.channels*sizeof(*limitstate.iir));
116
  limitstate.initted=0;
117 118 119 120 121 122 123 124 125
  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){
126 127 128
  int ch=limitstate.out.channels;
  float peakfeed[ch];
  float attfeed[ch];
129

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

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

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

140 141 142 143 144 145 146 147 148 149 150
  float localpeak;
  float localatt;

  float decayms=limitset.decay*.1;
  if(decayms!=limitstate.decay.ms)filter_set(decayms,&limitstate.decay);

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

151 152 153
  depth=depth*.2;
  depth*=depth;

154 155 156 157
  if(!limitstate.initted){
    limitstate.initted=1;
    limitstate.prev_active=activeC;

158 159 160
    limitstate.pthresh=thresh;
    limitstate.pdepth=depth;
  }
161

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

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

      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;
178
      
179

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

	prev_depth+=depth_add;
	prev_thresh+=thresh_add;
192
      }
193
	
194
      compute_iir_decayonly2(x,input_size,limitstate.iir+i,&limitstate.decay);
195 196 197 198 199 200 201 202 203 204 205
      
      
      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];
	}
206
      }
207 208 209 210 211 212 213 214 215
      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{
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
      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)
242
      ff->peak=malloc(ch*sizeof(*ff->peak));
243 244
    
    if(!ff->att)
245
      ff->att=malloc(ch*sizeof(*ff->att));
246 247 248 249 250 251 252 253
    
    memcpy(ff->peak,peakfeed,sizeof(peakfeed));
    memcpy(ff->att,attfeed,sizeof(attfeed));

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

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

265 266 267
  return &limitstate.out;
}