Fixes 60 ms speech mode
[opus.git] / src / test_opus.c
index 2766421..797c872 100644 (file)
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
 
-   - Neither the name of the Xiph.org Foundation nor the names of its
-   contributors may be used to endorse or promote products derived from
-   this software without specific prior written permission.
-
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 #include <math.h>
 #include <string.h>
 #include "opus.h"
+#include "SKP_debug.h"
+
+
+#define MAX_PACKET 1500
 
+void print_usage( char* argv[] ) 
+{
+    fprintf(stderr, "Usage: %s <mode (0/1/2)> <sampling rate (Hz)> <channels> "
+        "<bits per second>  [options] <input> <output>\n\n", argv[0]);
+    fprintf(stderr, "mode: 0 for auto, 1 for voice, 2 for audio:\n" );
+    fprintf(stderr, "options:\n" );
+    fprintf(stderr, "-cbr                 : enable constant bitrate; default: VBR\n" );
+    fprintf(stderr, "-bandwidth <NB|MB|WB|SWB|FB>  : audio bandwidth (from narrowband to fullband); default: sampling rate\n" );
+    fprintf(stderr, "-framesize <2.5|5|10|20|40|60>  : frame size in ms; default: 20 \n" );
+    fprintf(stderr, "-max_payload <bytes> : maximum payload size in bytes, default: 1024\n" );
+    fprintf(stderr, "-complexity <comp>   : complexity, 0 (lowest) ... 10 (highest); default: 10\n" );
+    fprintf(stderr, "-inbandfec           : enable SILK inband FEC\n" );
+    fprintf(stderr, "-dtx                 : enable SILK DTX\n" );
+    fprintf(stderr, "-loss <perc>         : simulate packet loss, in percent (0-100); default: 0\n" );
+}
+
+#ifdef _WIN32
+#      define STR_CASEINSENSITIVE_COMPARE(x, y) _stricmp(x, y)
+#else
+#      define STR_CASEINSENSITIVE_COMPARE(x, y) strcasecmp(x, y)
+#endif 
 
-#define MAX_PACKET 1024
 
 int main(int argc, char *argv[])
 {
@@ -50,45 +70,139 @@ int main(int argc, char *argv[])
    FILE *fin, *fout;
    OpusEncoder *enc;
    OpusDecoder *dec;
-   int len;
+   int args;
+   int len[2];
    int frame_size, channels;
-   int bytes_per_packet;
-   unsigned char data[MAX_PACKET];
-   int rate;
-   int loss = 0;
-   int count = 0;
+   int bitrate_bps;
+   unsigned char *data[2];
+   int sampling_rate;
+   int use_vbr;
+   int internal_sampling_rate_Hz;
+   int max_payload_bytes;
+   int complexity;
+   int use_inbandfec;
+   int use_dtx;
+   int cvbr = 0;
+   int packet_loss_perc;
+   int count=0, count_act=0, k;
    int skip;
    int stop=0;
-   int vbr=0;
    int tot_read=0, tot_written=0;
    short *in, *out;
-   int mode=MODE_HYBRID;
-   double bits=0;
-   if (argc != 9 && argc != 8 && argc != 7)
+   int mode;
+   double bits=0.0, bits_act=0.0, bits2=0.0, nrg;
+   int bandwidth=-1;
+   const char *bandwidth_string;
+   int write_samples;
+   int lost, lost_prev = 1;
+   int toggle = 0;
+   int enc_final_range[2];
+
+   if (argc < 7 )
    {
-      fprintf (stderr, "Usage: test_opus <rate (kHz)> <channels> <frame size> "
-               " <bytes per packet>  [<VBR rate (kb/s)>] [<packet loss rate>] "
-               "<input> <output>\n");
+      print_usage( argv );
       return 1;
    }
 
-   rate = atoi(argv[1]);
-   channels = atoi(argv[2]);
-   frame_size = atoi(argv[3]);
+   mode = atoi(argv[1]) + OPUS_MODE_AUTO;
+   sampling_rate = atoi(argv[2]);
+   channels = atoi(argv[3]);
+   bitrate_bps = atoi(argv[4]);
+
+   frame_size = sampling_rate/50;
+
+   /* defaults: */
+   use_vbr = 1;
+   bandwidth=BANDWIDTH_AUTO;
+   internal_sampling_rate_Hz = sampling_rate;
+   max_payload_bytes = MAX_PACKET;
+   complexity = 10;
+   use_inbandfec = 0;
+   use_dtx = 0;
+   packet_loss_perc = 0;
 
-   bytes_per_packet = atoi(argv[4]);
+   args = 5;
+   while( args < argc - 2 ) {
+       /* process command line options */
+        if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-cbr" ) == 0 ) {
+            use_vbr = 0;
+            args++;
+        } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-bandwidth" ) == 0 ) {
+            if (strcmp(argv[ args + 1 ], "NB")==0)
+                bandwidth = BANDWIDTH_NARROWBAND;
+            else if (strcmp(argv[ args + 1 ], "MB")==0)
+                bandwidth = BANDWIDTH_MEDIUMBAND;
+            else if (strcmp(argv[ args + 1 ], "WB")==0)
+                bandwidth = BANDWIDTH_WIDEBAND;
+            else if (strcmp(argv[ args + 1 ], "SWB")==0)
+                bandwidth = BANDWIDTH_SUPERWIDEBAND;
+            else if (strcmp(argv[ args + 1 ], "FB")==0)
+                bandwidth = BANDWIDTH_FULLBAND;
+            else {
+                fprintf(stderr, "Unknown bandwidth %s. Supported are NB, MB, WB, SWB, FB.\n", argv[ args + 1 ]);
+                return 1;
+            }
+            args += 2;
+        } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-framesize" ) == 0 ) {
+            if (strcmp(argv[ args + 1 ], "2.5")==0)
+                frame_size = sampling_rate/400;
+            else if (strcmp(argv[ args + 1 ], "5")==0)
+                frame_size = sampling_rate/200;
+            else if (strcmp(argv[ args + 1 ], "10")==0)
+                frame_size = sampling_rate/100;
+            else if (strcmp(argv[ args + 1 ], "20")==0)
+                frame_size = sampling_rate/50;
+            else if (strcmp(argv[ args + 1 ], "40")==0)
+                frame_size = sampling_rate/25;
+            else if (strcmp(argv[ args + 1 ], "60")==0)
+                frame_size = 3*sampling_rate/50;
+            else {
+                fprintf(stderr, "Unsupported frame size: %s ms. Supported are 2.5, 5, 10, 20, 40, 60.\n", argv[ args + 1 ]);
+                return 1;
+            }
+            args += 2;
+        } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-max_payload" ) == 0 ) {
+            max_payload_bytes = atoi( argv[ args + 1 ] );
+            args += 2;
+        } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-complexity" ) == 0 ) {
+            complexity = atoi( argv[ args + 1 ] );
+            args += 2;
+        } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-inbandfec" ) == 0 ) {
+            use_inbandfec = 1;
+            args++;
+        } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-cvbr" ) == 0 ) {
+            cvbr = 1;
+            args++;
+        } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-dtx") == 0 ) {
+            use_dtx = 1;
+            args++;
+        } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-loss" ) == 0 ) {
+            packet_loss_perc = atoi( argv[ args + 1 ] );
+            args += 2;
+        } else {
+            printf( "Error: unrecognized setting: %s\n\n", argv[ args ] );
+            print_usage( argv );
+            return 1;
+        }
+   }
 
-   if (argc >= 8)
-       vbr = atoi(argv[5]);
-   if (argc >= 9)
-       loss = atoi(argv[6]);
+   if( mode < OPUS_MODE_AUTO || mode > OPUS_MODE_AUDIO) {
+      fprintf (stderr, "mode must be: 0, 1 or 2\n");
+      return 1;
+   }
 
-   if (bytes_per_packet < 0 || bytes_per_packet > MAX_PACKET)
+   if (max_payload_bytes < 0 || max_payload_bytes > MAX_PACKET)
    {
-      fprintf (stderr, "bytes per packet must be between 0 and %d\n",
+      fprintf (stderr, "max_payload_bytes must be between 0 and %d\n",
                         MAX_PACKET);
       return 1;
    }
+   if (bitrate_bps < 0 || bitrate_bps*frame_size/sampling_rate > max_payload_bytes*8)
+   {
+      fprintf (stderr, "bytes per packet must be between 0 and %d\n",
+                        max_payload_bytes);
+      return 1;
+   }
 
    inFile = argv[argc-2];
    fin = fopen(inFile, "rb");
@@ -105,20 +219,59 @@ int main(int argc, char *argv[])
       return 1;
    }
 
-   enc = opus_encoder_create(rate, channels);
-   dec = opus_decoder_create(rate, channels);
+   enc = opus_encoder_create(sampling_rate, channels);
+   dec = opus_decoder_create(sampling_rate, channels);
 
-   mode = MODE_HYBRID;
-   opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(BANDWIDTH_FULLBAND));
    opus_encoder_ctl(enc, OPUS_SET_MODE(mode));
+   opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate_bps));
+   opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(bandwidth));
+   opus_encoder_ctl(enc, OPUS_SET_VBR_FLAG(use_vbr));
+   opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(cvbr));
+   opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
+   opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC_FLAG(use_inbandfec));
+   opus_encoder_ctl(enc, OPUS_SET_DTX_FLAG(use_dtx));
+   opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(packet_loss_perc));
 
-   skip = 5*rate/1000 + 10;
+   skip = 5*sampling_rate/1000;
+   /* When SILK resamples, add 18 samples delay */
+   /*if (mode != MODE_SILK_ONLY || sampling_rate > 16000)
+          skip += 18;*/
+
+   switch(bandwidth)
+   {
+   case BANDWIDTH_NARROWBAND:
+          bandwidth_string = "narrowband";
+          break;
+   case BANDWIDTH_MEDIUMBAND:
+          bandwidth_string = "mediumband";
+          break;
+   case BANDWIDTH_WIDEBAND:
+          bandwidth_string = "wideband";
+          break;
+   case BANDWIDTH_SUPERWIDEBAND:
+          bandwidth_string = "superwideband";
+          break;
+   case BANDWIDTH_FULLBAND:
+          bandwidth_string = "fullband";
+          break;
+   case BANDWIDTH_AUTO:
+          bandwidth_string = "auto";
+          break;
+   default:
+          bandwidth_string = "unknown";
+   }
+
+   printf("Encoding %d Hz input at %.3f kb/s in %s mode with %d-sample frames.\n", sampling_rate, bitrate_bps*0.001, bandwidth_string, frame_size);
 
    in = (short*)malloc(frame_size*channels*sizeof(short));
    out = (short*)malloc(frame_size*channels*sizeof(short));
+   data[0] = (unsigned char*)calloc(max_payload_bytes,sizeof(char));
+   if( use_inbandfec ) {
+       data[1] = (unsigned char*)calloc(max_payload_bytes,sizeof(char));
+   }
    while (!stop)
    {
-      int write_samples;
+
       err = fread(in, sizeof(short), frame_size*channels, fin);
       tot_read += err;
       if (err < frame_size*channels)
@@ -127,28 +280,80 @@ int main(int argc, char *argv[])
           for (i=err;i<frame_size*channels;i++)
               in[i] = 0;
       }
-      len = opus_encode(enc, in, frame_size, data, bytes_per_packet);
-      if (len <= 0)
+
+      len[toggle] = opus_encode(enc, in, frame_size, data[toggle], max_payload_bytes);
+#if OPUS_TEST_RANGE_CODER_STATE
+      enc_final_range[toggle] = opus_encoder_get_final_range( enc );
+#endif
+      if (len[toggle] < 0)
       {
-         fprintf (stderr, "opus_encode() returned %d\n", len);
+         fprintf (stderr, "opus_encode() returned %d\n", len[toggle]);
          return 1;
       }
-      bits += len*8;
-      opus_decode(dec, rand()%100<loss ? NULL : data, len, out, frame_size);
-      count++;
-      tot_written += (frame_size-skip)*channels;
-      write_samples = frame_size;
-      if (tot_written > tot_read && skip==0)
-      {
-          write_samples -= (tot_written-tot_read)/channels;
-          stop = 1;
+
+      lost = rand()%100 < packet_loss_perc || len[toggle]==0;
+      if( count >= use_inbandfec ) {
+          /* delay by one packet when using in-band FEC */
+          if( use_inbandfec  ) {
+              if( lost_prev ) {
+                  /* attempt to decode with in-band FEC from next packet */
+                  opus_decode(dec, lost ? NULL : data[toggle], len[toggle], out, frame_size, 1);
+              } else {
+                  /* regular decode */
+                  opus_decode(dec, data[1-toggle], len[1-toggle], out, frame_size, 0);
+              }
+          } else {
+              opus_decode(dec, lost ? NULL : data[toggle], len[toggle], out, frame_size, 0);
+          }
+          write_samples = frame_size-skip;
+          tot_written += write_samples*channels;
+          if (tot_written > tot_read)
+          {
+              write_samples -= (tot_written-tot_read)/channels;
+              stop = 1;
+          }
+          fwrite(out+skip, sizeof(short), write_samples*channels, fout);
+          skip = 0;
+      }
+
+#if OPUS_TEST_RANGE_CODER_STATE
+      /* compare final range encoder rng values of encoder and decoder */
+      if( !lost && !lost_prev && opus_decoder_get_final_range( dec ) != enc_final_range[toggle^use_inbandfec] ) {
+          fprintf (stderr, "Error: Range coder state mismatch between encoder and decoder.\n");
+          return 0;
+      }
+#endif
+
+      lost_prev = lost;
+
+      /* count bits */
+      bits += len[toggle]*8;
+      if( count >= use_inbandfec ) {
+          nrg = 0.0;
+          for ( k = 0; k < frame_size * channels; k++ ) {
+              nrg += in[ k ] * (double)in[ k ];
+          }
+          if ( ( nrg / ( frame_size * channels ) ) > 1e5 ) {
+              bits_act += len[toggle]*8;
+              count_act++;
+          }
+             /* Variance */
+             bits2 += len[toggle]*len[toggle]*64;
       }
-      fwrite(out+skip, sizeof(short), (write_samples-skip)*channels, fout);
-      skip = 0;
+      count++;
+      toggle = (toggle + use_inbandfec) & 1;
    }
-   fprintf (stderr, "average bit-rate: %f kb/s\n", bits*rate/(frame_size*(double)count));
+   fprintf (stderr, "average bitrate:             %7.3f kb/s\n", 1e-3*bits*sampling_rate/(frame_size*(double)count));
+   fprintf (stderr, "active bitrate:              %7.3f kb/s\n", 1e-3*bits_act*sampling_rate/(frame_size*(double)count_act));
+   fprintf (stderr, "bitrate standard deviation:  %7.3f kb/s\n", 1e-3*sqrt(bits2/count - bits*bits/(count*(double)count))*sampling_rate/frame_size);
+   /* Close any files to which intermediate results were stored */
+   DEBUG_STORE_CLOSE_FILES
+   SKP_TimerSave("opus_timing.txt");
    opus_encoder_destroy(enc);
    opus_decoder_destroy(dec);
+   free(data[0]);
+   if (use_inbandfec)
+          free(data[1]);
    fclose(fin);
    fclose(fout);
    free(in);