Adding some LPC extension code
authorJean-Marc Valin <jmvalin@jmvalin.ca>
Sat, 6 May 2017 05:25:38 +0000 (01:25 -0400)
committerJean-Marc Valin <jmvalin@jmvalin.ca>
Sat, 6 May 2017 05:25:38 +0000 (01:25 -0400)
src/opusenc.c

index 80ff938..7f197bc 100644 (file)
@@ -48,6 +48,8 @@
 #define MAX_CHANNELS 8
 
 #define LPC_PADDING 120
+#define LPC_ORDER 24
+#define LPC_INPUT 480
 
 /* Allow up to 2 seconds for delayed decision. */
 #define MAX_LOOKAHEAD 96000
@@ -610,6 +612,8 @@ OPE_EXPORT int ope_get_page(OggOpusEnc *enc, unsigned char **page, int *len, int
   }
 }
 
+static void extend_signal(float *x, int before, int after, int channels);
+
 int ope_drain(OggOpusEnc *enc) {
   if (enc->unrecoverable) return OPE_UNRECOVERABLE;
   /* Check if it's already been drained. */
@@ -620,6 +624,7 @@ int ope_drain(OggOpusEnc *enc) {
   shift_buffer(enc);
   /* FIXME: Do LPC extension instead. */
   memset(&enc->buffer[enc->channels*enc->buffer_end], 0, pad_samples*enc->channels);
+  extend_signal(&enc->buffer[enc->channels*enc->buffer_end], enc->buffer_end, LPC_PADDING, enc->channels);
   enc->decision_delay = 0;
   enc->buffer_end += pad_samples;
   assert(enc->buffer_end <= BUFFER_SAMPLES);
@@ -855,3 +860,127 @@ int ope_encoder_ctl(OggOpusEnc *enc, int request, ...) {
   assert(ret == 0 || ret < -10);
   return ret;
 }
+
+
+static void vorbis_lpc_from_data(float *data, float *lpci, int n, int stride);
+
+static void extend_signal(float *x, int before, int after, int channels) {
+  int c;
+  before = MIN(before, LPC_INPUT);
+  if (before < 4*LPC_ORDER) {
+    int i;
+    for (i=0;i<after*channels;i++) x[i] = 0;
+    return;
+  }
+  for (c=0;c<channels;c++) {
+    int i;
+    float lpc[LPC_ORDER];
+    vorbis_lpc_from_data(x-channels*before, lpc, before, channels);
+    for (i=0;i<after;i++) {
+      float sum;
+      int j;
+      sum = 0;
+      for (j=0;j<LPC_ORDER;j++) sum -= x[i-j-1]*lpc[j];
+      x[i] = sum;
+    }
+  }
+}
+
+/* Some of these routines (autocorrelator, LPC coefficient estimator)
+   are derived from code written by Jutta Degener and Carsten Bormann;
+   thus we include their copyright below.  The entirety of this file
+   is freely redistributable on the condition that both of these
+   copyright notices are preserved without modification.  */
+
+/* Preserved Copyright: *********************************************/
+
+/* Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann,
+Technische Universita"t Berlin
+
+Any use of this software is permitted provided that this notice is not
+removed and that neither the authors nor the Technische Universita"t
+Berlin are deemed to have made any representations as to the
+suitability of this software for any purpose nor are held responsible
+for any defects of this software. THERE IS ABSOLUTELY NO WARRANTY FOR
+THIS SOFTWARE.
+
+As a matter of courtesy, the authors request to be informed about uses
+this software has found, about bugs in this software, and about any
+improvements that may be of general interest.
+
+Berlin, 28.11.1994
+Jutta Degener
+Carsten Bormann
+
+*********************************************************************/
+
+static void vorbis_lpc_from_data(float *data, float *lpci, int n, int stride) {
+  double aut[LPC_ORDER+1];
+  double lpc[LPC_ORDER];
+  double error;
+  double epsilon;
+  int i,j;
+
+  /* FIXME: Apply a window to the input. */
+  /* autocorrelation, p+1 lag coefficients */
+  j=LPC_ORDER+1;
+  while(j--){
+    double d=0; /* double needed for accumulator depth */
+    for(i=j;i<n;i++)d+=(double)data[i*stride]*data[(i-j)*stride];
+    aut[j]=d;
+  }
+
+  /* FIXME: Apply lag windowing (better than bandwidth expansion) */
+  /* Generate lpc coefficients from autocorr values */
+
+  /* FIXME: This noise floor is insane! */
+  /* set our noise floor to about -100dB */
+  error=aut[0] * (1. + 1e-10);
+  epsilon=1e-9*aut[0]+1e-10;
+
+  for(i=0;i<LPC_ORDER;i++){
+    double r= -aut[i+1];
+
+    if(error<epsilon){
+      memset(lpc+i,0,(LPC_ORDER-i)*sizeof(*lpc));
+      goto done;
+    }
+
+    /* Sum up this iteration's reflection coefficient; note that in
+       Vorbis we don't save it.  If anyone wants to recycle this code
+       and needs reflection coefficients, save the results of 'r' from
+       each iteration. */
+
+    for(j=0;j<i;j++)r-=lpc[j]*aut[i-j];
+    r/=error;
+
+    /* Update LPC coefficients and total error */
+
+    lpc[i]=r;
+    for(j=0;j<i/2;j++){
+      double tmp=lpc[j];
+
+      lpc[j]+=r*lpc[i-1-j];
+      lpc[i-1-j]+=r*tmp;
+    }
+    if(i&1)lpc[j]+=lpc[j]*r;
+
+    error*=1.-r*r;
+
+  }
+
+ done:
+
+  /* slightly damp the filter */
+  {
+    double g = .99;
+    double damp = g;
+    for(j=0;j<LPC_ORDER;j++){
+      lpc[j]*=damp;
+      damp*=g;
+    }
+  }
+
+  for(j=0;j<LPC_ORDER;j++)lpci[j]=(float)lpc[j];
+}
+