shithub: opus-tools

Download patch

ref: 686823647f4b0971c685865e14b5cd0422c8fdd0
parent: 89c244d6a2a0e96c998ce31510a5a114e9006008
author: Gregory Maxwell <greg@xiph.org>
date: Mon Jan 13 06:55:10 EST 2014

Add support in opusdec to write out 32-bit floating-point.

--- a/man/opusdec.1
+++ b/man/opusdec.1
@@ -17,6 +17,8 @@
 ] [
 .B --no-dither
 ] [
+.B --float
+] [
 .B --force-wav
 ] [
 .B --packet-loss pct
@@ -61,6 +63,8 @@
 Adjust the output volume n dB, negative values make the signal quieter.
 .IP "--no-dither"
 Do not dither 16-bit output
+.IP "--float"
+32-bit floating-point files instead of 16-bit files
 .IP "--force-wav"
 Force including a wav header on output (e.g. for non-wav extensions and stdout)
 .IP "--packet-loss"
--- a/src/opusdec.c
+++ b/src/opusdec.c
@@ -278,7 +278,7 @@
    }
 }
 
-FILE *out_file_open(char *outFile, int *wav_format, int rate, int mapping_family, int *channels)
+FILE *out_file_open(char *outFile, int *wav_format, int rate, int mapping_family, int *channels, int fp)
 {
    FILE *fout=NULL;
    /*Open output file*/
@@ -429,7 +429,7 @@
       }
       if (*wav_format)
       {
-         *wav_format = write_wav_header(fout, rate, mapping_family, *channels);
+         *wav_format = write_wav_header(fout, rate, mapping_family, *channels, fp);
          if (*wav_format < 0)
          {
             fprintf (stderr, "Error writing WAV header.\n");
@@ -460,6 +460,7 @@
    printf (" --rate n              Force decoding at sampling rate n Hz\n");
    printf (" --gain n              Manually adjust gain by n.nn dB (0 default)\n");
    printf (" --no-dither           Do not dither 16-bit output\n");
+   printf (" --float               32-bit floating-point output\n");
    printf (" --force-wav           Force wav header on output\n");
    printf (" --packet-loss n       Simulate n %% random packet loss\n");
    printf (" --save-range file     Saves check values for every frame to a file\n");
@@ -558,7 +559,7 @@
 }
 
 opus_int64 audio_write(float *pcm, int channels, int frame_size, FILE *fout, SpeexResamplerState *resampler,
-                       int *skip, shapestate *shapemem, int file, opus_int64 maxout)
+                       int *skip, shapestate *shapemem, int file, opus_int64 maxout, int fp)
 {
    opus_int64 sampout=0;
    int i,ret,tmp_skip;
@@ -590,17 +591,20 @@
        frame_size=0;
      }
 
-     /*Convert to short and save to output file*/
-     if (shapemem){
-       shape_dither_toshort(shapemem,out,output,out_len,channels);
-     }else{
-       for (i=0;i<(int)out_len*channels;i++)
-         out[i]=(short)float2int(fmaxf(-32768,fminf(output[i]*32768.f,32767)));
+     if(!file||!fp)
+     {
+        /*Convert to short and save to output file*/
+        if (shapemem){
+          shape_dither_toshort(shapemem,out,output,out_len,channels);
+        }else{
+          for (i=0;i<(int)out_len*channels;i++)
+            out[i]=(short)float2int(fmaxf(-32768,fminf(output[i]*32768.f,32767)));
+        }
+        if ((le_short(1)!=1)&&file){
+          for (i=0;i<(int)out_len*channels;i++)
+            out[i]=le_short(out[i]);
+        }
      }
-     if ((le_short(1)!=1)&&file){
-       for (i=0;i<(int)out_len*channels;i++)
-         out[i]=le_short(out[i]);
-     }
 
      if(maxout>0)
      {
@@ -617,7 +621,7 @@
          else fprintf(stderr, "Error playing audio.\n");
        }else
 #endif
-         ret=fwrite(out, 2*channels, out_len<maxout?out_len:maxout, fout);
+         ret=fwrite(fp?(char *)output:(char *)out, (fp?4:2)*channels, out_len<maxout?out_len:maxout, fout);
        sampout+=ret;
        maxout-=ret;
      }
@@ -650,6 +654,7 @@
       {"rate", required_argument, NULL, 0},
       {"gain", required_argument, NULL, 0},
       {"no-dither", no_argument, NULL, 0},
+      {"float", no_argument, NULL, 0},
       {"force-wav", no_argument, NULL, 0},
       {"packet-loss", required_argument, NULL, 0},
       {"save-range", required_argument, NULL, 0},
@@ -675,6 +680,7 @@
    int has_tags_packet=0;
    ogg_int32_t opus_serialno;
    int dither=1;
+   int fp=0;
    shapestate shapemem;
    SpeexResamplerState *resampler=NULL;
    float gain=1;
@@ -733,6 +739,9 @@
          } else if (strcmp(long_options[option_index].name,"no-dither")==0)
          {
             dither=0;
+         } else if (strcmp(long_options[option_index].name,"float")==0)
+         {
+            fp=1;
          } else if (strcmp(long_options[option_index].name,"force-wav")==0)
          {
             forcewav=1;
@@ -799,7 +808,11 @@
        relevant for playback. Many audio devices sound
        better at 48kHz and not resampling also saves CPU.*/
      if(rate==0)rate=48000;
+     /*Playback is 16-bit only.*/
+     fp=0;
    }
+   /*If the output is floating point, don't dither.*/
+   if(fp)dither=0;
 
    /*Open input file*/
    if (strcmp(inFile, "-")==0)
@@ -934,7 +947,7 @@
                      fprintf(stderr, "resampler error: %s\n", speex_resampler_strerror(err));
                   speex_resampler_skip_zeros(resampler);
                }
-               if(!fout)fout=out_file_open(outFile, &wav_format, rate, mapping_family, &channels);
+               if(!fout)fout=out_file_open(outFile, &wav_format, rate, mapping_family, &channels, fp);
             } else if (packet_count==1)
             {
                if (!quiet)
@@ -983,7 +996,7 @@
                if(!quiet){
                   /*Display a progress spinner while decoding.*/
                   static const char spinner[]="|/-\\";
-                  double coded_seconds = (double)audio_size/(channels*rate*sizeof(short));
+                  double coded_seconds = (double)audio_size/(channels*rate*(fp?4:2));
                   if(coded_seconds>=last_coded_seconds+1){
                      fprintf(stderr,"\r[%c] %02d:%02d:%02d", spinner[last_spin&3],
                              (int)(coded_seconds/3600),(int)(coded_seconds/60)%60,
@@ -1025,9 +1038,9 @@
                  the final end-trim by not letting the output sample count
                  get ahead of the granpos indicated value.*/
                maxout=((page_granule-gran_offset)*rate/48000)-link_out;
-               outsamp=audio_write(output, channels, frame_size, fout, resampler, &preskip, dither?&shapemem:0, strlen(outFile)!=0,0>maxout?0:maxout);
+               outsamp=audio_write(output, channels, frame_size, fout, resampler, &preskip, dither?&shapemem:0, strlen(outFile)!=0,0>maxout?0:maxout,fp);
                link_out+=outsamp;
-               audio_size+=sizeof(short)*outsamp*channels;
+               audio_size+=(fp?4:2)*outsamp*channels;
             }
             packet_count++;
          }
@@ -1044,9 +1057,9 @@
                int tmp = drain;
                if (tmp > 100)
                   tmp = 100;
-               outsamp=audio_write(zeros, channels, tmp, fout, resampler, NULL, &shapemem, strlen(outFile)!=0, ((page_granule-gran_offset)*rate/48000)-link_out);
+               outsamp=audio_write(zeros, channels, tmp, fout, resampler, NULL, &shapemem, strlen(outFile)!=0, ((page_granule-gran_offset)*rate/48000)-link_out,fp);
                link_out+=outsamp;
-               audio_size+=sizeof(short)*outsamp*channels;
+               audio_size+=(fp?4:2)*outsamp*channels;
                drain -= tmp;
             } while (drain>0);
             free(zeros);
--- a/src/wav_io.c
+++ b/src/wav_io.c
@@ -70,7 +70,7 @@
    return fwrite(buf,2,1,file);
 }
 
-int write_wav_header(FILE *file, int rate, int mapping_family, int channels)
+int write_wav_header(FILE *file, int rate, int mapping_family, int channels, int fp)
 {
    int ret;
    int extensible;
@@ -79,17 +79,20 @@
       proper channel meanings. */
    extensible = mapping_family == 1 && 3 <= channels && channels <= 8;
 
+   /* >16 bit audio also requires WAVEFORMATEXTENSIBLE. */
+   extensible |= fp;
+
    ret = fprintf (file, "RIFF") >= 0;
    ret &= fwrite_le32 (0x7fffffff, file);
 
    ret &= fprintf (file, "WAVEfmt ") >= 0;
    ret &= fwrite_le32 (extensible ? 40 : 16, file);
-   ret &= fwrite_le16 (extensible ? 0xfffe : 1, file);
+   ret &= fwrite_le16 (extensible ? 0xfffe : (fp?3:1), file);
    ret &= fwrite_le16 (channels, file);
    ret &= fwrite_le32 (rate, file);
-   ret &= fwrite_le32 (2*channels*rate, file);
-   ret &= fwrite_le16 (2*channels, file);
-   ret &= fwrite_le16 (16, file);
+   ret &= fwrite_le32 ((fp?4:2)*channels*rate, file);
+   ret &= fwrite_le16 ((fp?4:2)*channels, file);
+   ret &= fwrite_le16 (fp?32:16, file);
 
    if(extensible)
    {
@@ -101,6 +104,14 @@
         0x80, 0x00,
         0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
       };
+      static const unsigned char ksdataformat_subtype_float[16]=
+      {
+        0x03, 0x00, 0x00, 0x00,
+        0x00, 0x00,
+        0x10, 0x00,
+        0x80, 0x00,
+        0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
+      };
       static const int wav_channel_masks[8] =
       {
          1,                      /* 1.0 mono */
@@ -113,9 +124,14 @@
          1|2|4|8|16|32|512|1024, /* 7.1 */
       };
       ret &= fwrite_le16 (22, file);
-      ret &= fwrite_le16 (16, file);
+      ret &= fwrite_le16 (fp?32:16, file);
       ret &= fwrite_le32 (wav_channel_masks[channels-1], file);
-      ret &= fwrite (ksdataformat_subtype_pcm, 16, 1, file);
+      if (!fp)
+      {
+         ret &= fwrite (ksdataformat_subtype_pcm, 16, 1, file);
+      } else {
+         ret &= fwrite (ksdataformat_subtype_float, 16, 1, file);
+      }
    }
 
    ret &= fprintf (file, "data") >= 0;
--- a/src/wav_io.h
+++ b/src/wav_io.h
@@ -57,6 +57,6 @@
 
 void adjust_wav_mapping(int mapping_family, int channels, unsigned char *stream_map);
 
-int write_wav_header(FILE *file, int rate, int mapping_family, int channels);
+int write_wav_header(FILE *file, int rate, int mapping_family, int channels, int fp);
 
 #endif