limit.c 5.74 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 47
} limit_state;

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

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

/* called only by initial setup */
77
int limit_load(int ch){
78 79 80
  int i;
  memset(&limitstate,0,sizeof(limitstate));

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

87 88 89 90 91 92
  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];
  }

93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
  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 */
109
int limit_reset(void){
110 111
  /* reset cached pipe state */
  while(pull_limit_feedback(NULL,NULL));
112
  memset(limitstate.iir,0,limitstate.out.channels*sizeof(&limitstate.iir));
113
  limitstate.initted=0;
114 115 116 117 118 119 120 121 122
  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){
123 124 125
  int ch=limitstate.out.channels;
  float peakfeed[ch];
  float attfeed[ch];
126

127 128 129
  int activeC=limit_active;
  int activeP=limitstate.prev_active;

130
  int visible=limit_visible;
131
  int bypass;
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
  int i,k;

  float thresh=limitset.thresh/10.-.01;
  float depth=limitset.depth;
  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;
  }

147 148 149 150 151
  if(!limitstate.initted){
    limitstate.initted=1;
    limitstate.prev_active=activeC;
  }

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

155
  for(i=0;i<ch;i++){
156 157
    localpeak=0.;
    localatt=0.;
158 159

    bypass=!(activeC || activeP || visible) || mute_channel_muted(in->active,i);
160
    
161 162
    if((activeC || activeP) && !mute_channel_muted(in->active,i)){
      
163 164 165
      float *inx=in->data[i];
      float *x=limitstate.out.data[i];
      
166 167 168 169 170
      /* 'knee' the actual samples, compute attenuation depth */
      for(k=0;k<in->samples;k++){
	float dB=todB(inx[k]);
	float knee=limit_knee(dB-thresh,depth)+thresh;
	float att=dB-knee;
171
	
172
	if(att>localatt)localatt=att;
173
	
174 175
	x[k]=att;
      }
176
	
177
      compute_iir_decayonly2(x,input_size,limitstate.iir+i,&limitstate.decay);
178 179 180 181 182 183 184 185 186 187 188
      
      
      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];
	}
189
      }
190 191 192 193 194 195 196 197 198
      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{
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
      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)
225
      ff->peak=malloc(ch*sizeof(*ff->peak));
226 227
    
    if(!ff->att)
228
      ff->att=malloc(ch*sizeof(*ff->att));
229 230 231 232 233 234 235 236
    
    memcpy(ff->peak,peakfeed,sizeof(peakfeed));
    memcpy(ff->att,attfeed,sizeof(attfeed));

    feedback_push(&limitstate.feedpool,(feedback_generic *)ff);
  }
   
  {
237
    int tozero=input_size-limitstate.out.samples;
238 239 240 241 242
    if(tozero)
      for(i=0;i<limitstate.out.channels;i++)
        memset(limitstate.out.data[i]+limitstate.out.samples,0,sizeof(**limitstate.out.data)*tozero);
  }

243 244
  limitstate.out.active=in->active;
  limitstate.prev_active=activeC;
245 246 247
  return &limitstate.out;
}