shithub: sox

Download patch

ref: 542cab5f35508e4bd8dd52db069a562f5f86d33a
parent: 226e3a5e7cce21ee4dc0c10a8da84af4473e8adf
parent: c97d3bb141f953c4aa07a4462fe2495a53b0e1c9
author: Ulrich Klauer <ulrich@chirlu.de>
date: Mon Oct 15 23:37:18 EDT 2012

Merge branch 'dot' into master

Get the fixes from dot into the master branch.

--- a/ChangeLog
+++ b/ChangeLog
@@ -43,6 +43,36 @@
 sox-14.4.1	20xx-xx-xx
 ----------
 
+Newly deprecated features (to be removed in future):
+
+  Deprec-  Feature    [O(ption)]                           Removal
+  ated in  [F(ormat)] [E(ffect)]   Replacement             due after
+  -------  ----------------------  ----------------------  -------
+  14.4.1   OpenMP < 3.0            OpenMP >= 3.0           14.4.1
+
+File formats:
+
+  o Fix pipe file-type detection regression. (robs)
+  o MAUD write fixes. [3507927] (Carl Eric Codere and Ulrich Klauer)
+  o Fix crash when seeking within a FLAC file. [3476843] (Eric Wong)
+  o Fix Ogg Vorbis files with certain numbers of channels being
+    truncated. (Ulrich Klauer)
+
+Audio device drivers:
+
+  o Check whether pulseaudio is available before choosing it as
+    default. (robs)
+
+Effects:
+
+  o Restore 8 seconds default for spectrogram, if the input length is
+    not known. (Ulrich Klauer)
+
+Other bug fixes:
+
+  o Fix input length calculation for combine methods other than
+    concatenate. (Ulrich Klauer)
+
 
 sox-14.4.0	2012-03-04
 ----------
--- a/sox.1
+++ b/sox.1
@@ -1448,7 +1448,7 @@
 The following parameters are used with, and have the same meaning for,
 several effects:
 .TP
-\fIcentre\fR[\fBk\fR]
+\fIcenter\fR[\fBk\fR]
 See
 .IR frequency .
 .TP
@@ -3313,8 +3313,10 @@
 which the pitch (and tempo) should be adjusted: greater than 0
 increases, less than 0 decreases.
 .SP
-By default, the speed change is performed by resampling with the \fBrate\fR
-effect using its default quality/speed.  For higher quality or higher speed
+Technically, the speed effect only changes the sample rate information,
+leaving the samples themselves untouched.  The \fBrate\fR effect is invoked
+automatically to resample to the output sample rate, using its default
+quality/speed.  For higher quality or higher speed
 resampling, in addition to the \fBspeed\fR effect, specify
 the \fBrate\fR effect with the desired quality option.
 .SP
--- a/src/flac.c
+++ b/src/flac.c
@@ -35,9 +35,10 @@
   uint64_t total_samples;
 
   /* Decode buffer: */
-  FLAC__int32 const * const * decoded_wide_samples;
-  unsigned number_of_wide_samples;
-  unsigned wide_sample_number;
+  sox_sample_t *req_buffer; /* this may be on the stack */
+  size_t number_of_requested_samples;
+  sox_sample_t *leftover_buf; /* heap */
+  unsigned number_of_leftover_samples;
 
   FLAC__StreamDecoder * decoder;
   FLAC__bool eof;
@@ -158,6 +159,11 @@
 {
   sox_format_t * ft = (sox_format_t *) client_data;
   priv_t * p = (priv_t *)ft->priv;
+  sox_sample_t * dst = p->req_buffer;
+  unsigned channel;
+  unsigned nsamples = frame->header.blocksize;
+  unsigned sample = 0;
+  size_t actual = nsamples * p->channels;
 
   (void) flac;
 
@@ -165,11 +171,47 @@
     lsx_fail_errno(ft, SOX_EINVAL, "FLAC ERROR: parameters differ between frame and header");
     return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
   }
+  if (dst == NULL) {
+    lsx_warn("FLAC ERROR: entered write callback without a buffer (SoX bug)");
+    return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+  }
 
-  /* FIXME: We're skating on thin ice here: buffer may be on the stack! */
-  p->decoded_wide_samples = buffer;
-  p->number_of_wide_samples = frame->header.blocksize;
-  p->wide_sample_number = 0;
+  /* FLAC may give us too much data, prepare the leftover buffer */
+  if (actual > p->number_of_requested_samples) {
+    size_t to_stash = actual - p->number_of_requested_samples;
+
+    p->leftover_buf = lsx_malloc(to_stash * sizeof(sox_sample_t));
+    p->number_of_leftover_samples = to_stash;
+    nsamples = p->number_of_requested_samples / p->channels;
+
+    p->req_buffer += p->number_of_requested_samples;
+    p->number_of_requested_samples = 0;
+  } else {
+    p->req_buffer += actual;
+    p->number_of_requested_samples -= actual;
+  }
+
+leftover_copy:
+
+  for (; sample < nsamples; sample++) {
+    for (channel = 0; channel < p->channels; channel++) {
+      FLAC__int32 d = buffer[channel][sample];
+      switch (p->bits_per_sample) {
+      case  8: *dst++ = SOX_SIGNED_8BIT_TO_SAMPLE(d,); break;
+      case 16: *dst++ = SOX_SIGNED_16BIT_TO_SAMPLE(d,); break;
+      case 24: *dst++ = SOX_SIGNED_24BIT_TO_SAMPLE(d,); break;
+      case 32: *dst++ = SOX_SIGNED_32BIT_TO_SAMPLE(d,); break;
+      }
+    }
+  }
+
+  /* copy into the leftover buffer if we've prepared it */
+  if (sample < frame->header.blocksize) {
+    nsamples = frame->header.blocksize;
+    dst = p->leftover_buf;
+    goto leftover_copy;
+  }
+
   return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
 }
 
@@ -224,35 +266,66 @@
 static size_t read_samples(sox_format_t * const ft, sox_sample_t * sampleBuffer, size_t const requested)
 {
   priv_t * p = (priv_t *)ft->priv;
-  size_t actual = 0;
+  size_t prev_requested;
 
   if (p->seek_pending) {
     p->seek_pending = sox_false; 
-    p->wide_sample_number = p->number_of_wide_samples = 0;
-    if (!FLAC__stream_decoder_seek_absolute(p->decoder, (FLAC__uint64)(p->seek_offset / ft->signal.channels)))
+
+    /* discard leftover decoded data */
+    free(p->leftover_buf);
+    p->leftover_buf = NULL;
+    p->number_of_leftover_samples = 0;
+
+    p->req_buffer = sampleBuffer;
+    p->number_of_requested_samples = requested;
+
+    /* calls decoder_write_callback */
+    if (!FLAC__stream_decoder_seek_absolute(p->decoder, (FLAC__uint64)(p->seek_offset / ft->signal.channels))) {
+      p->req_buffer = NULL;
       return 0;
-  }
-  while (!p->eof && actual < requested) {
-    if (p->wide_sample_number >= p->number_of_wide_samples)
-      FLAC__stream_decoder_process_single(p->decoder);
-    if (p->wide_sample_number >= p->number_of_wide_samples)
-      p->eof = sox_true;
-    else { /* FIXME: this block should really be inside the decode callback */
-      unsigned channel;
+    }
+  } else if (p->number_of_leftover_samples > 0) {
 
-      for (channel = 0; channel < p->channels; channel++, actual++) {
-        FLAC__int32 d = p->decoded_wide_samples[channel][p->wide_sample_number];
-        switch (p->bits_per_sample) {
-        case  8: *sampleBuffer++ = SOX_SIGNED_8BIT_TO_SAMPLE(d,); break;
-        case 16: *sampleBuffer++ = SOX_SIGNED_16BIT_TO_SAMPLE(d,); break;
-        case 24: *sampleBuffer++ = SOX_SIGNED_24BIT_TO_SAMPLE(d,); break;
-        case 32: *sampleBuffer++ = SOX_SIGNED_32BIT_TO_SAMPLE(d,); break;
-        }
-      }
-      ++p->wide_sample_number;
+    /* small request, no need to decode more samples since we have leftovers */
+    if (requested < p->number_of_leftover_samples) {
+      size_t req_bytes = requested * sizeof(sox_sample_t);
+
+      memcpy(sampleBuffer, p->leftover_buf, req_bytes);
+      p->number_of_leftover_samples -= requested;
+      memmove(p->leftover_buf, (char *)p->leftover_buf + req_bytes,
+              (size_t)p->number_of_leftover_samples * sizeof(sox_sample_t));
+      return requested;
     }
+
+    /* first, give them all of our leftover data: */
+    memcpy(sampleBuffer, p->leftover_buf,
+           p->number_of_leftover_samples * sizeof(sox_sample_t));
+
+    p->req_buffer = sampleBuffer + p->number_of_leftover_samples;
+    p->number_of_requested_samples = requested - p->number_of_leftover_samples;
+
+    free(p->leftover_buf);
+    p->leftover_buf = NULL;
+    p->number_of_leftover_samples = 0;
+
+    /* continue invoking decoder below */
+  } else {
+    p->req_buffer = sampleBuffer;
+    p->number_of_requested_samples = requested;
   }
-  return actual;
+
+  /* invoke the decoder, calls decoder_write_callback */
+  while ((prev_requested = p->number_of_requested_samples) && !p->eof) {
+    if (!FLAC__stream_decoder_process_single(p->decoder))
+      break; /* error, but maybe got earlier in the loop, though */
+
+    /* number_of_requested_samples decrements as the decoder progresses */
+    if (p->number_of_requested_samples == prev_requested)
+      p->eof = sox_true;
+  }
+  p->req_buffer = NULL;
+
+  return requested - p->number_of_requested_samples;
 }
 
 
@@ -263,6 +336,10 @@
   if (!FLAC__stream_decoder_finish(p->decoder) && p->eof)
     lsx_warn("decoder MD5 checksum mismatch.");
   FLAC__stream_decoder_delete(p->decoder);
+
+  free(p->leftover_buf);
+  p->leftover_buf = NULL;
+  p->number_of_leftover_samples = 0;
   return SOX_SUCCESS;
 }
 
--- a/src/formats.c
+++ b/src/formats.c
@@ -343,7 +343,7 @@
     return SOX_EOF;
   }
   if (!ft->signal.precision) {
-    lsx_fail_errno(ft,SOX_EFMT,"data encoding was not specified");
+    lsx_fail_errno(ft,SOX_EFMT,"data encoding or sample size was not specified");
     return SOX_EOF;
   }
   return SOX_SUCCESS;
--- a/src/maud.c
+++ b/src/maud.c
@@ -2,6 +2,8 @@
  *
  * supports: mono and stereo, linear, a-law and u-law reading and writing
  *
+ * an IFF format; description at http://lclevy.free.fr/amiga/MAUDINFO.TXT
+ *
  * Copyright 1998-2006 Chris Bagwell and SoX Contributors
  * This source code is freely redistributable and may be used for
  * any purpose.  This copyright notice must be maintained.
@@ -225,6 +227,11 @@
 {
         /* All samples are already written out. */
 
+        priv_t *p = (priv_t*)ft->priv;
+        uint32_t mdat_size; /* MDAT chunk size */
+        mdat_size = p->nsamples * (ft->encoding.bits_per_sample >> 3);
+        lsx_padbytes(ft, (size_t) (mdat_size%2));
+
         if (lsx_seeki(ft, (off_t)0, 0) != 0)
         {
             lsx_fail_errno(ft,errno,"can't rewind output file to rewrite MAUD header");
@@ -235,13 +242,16 @@
         return(SOX_SUCCESS);
 }
 
-#define MAUDHEADERSIZE (4+(4+4+32)+(4+4+32)+(4+4))
+#define MAUDHEADERSIZE (4+(4+4+32)+(4+4+19+1)+(4+4))
 static void maudwriteheader(sox_format_t * ft)
 {
         priv_t * p = (priv_t *) ft->priv;
+        uint32_t mdat_size; /* MDAT chunk size */
 
+        mdat_size = p->nsamples * (ft->encoding.bits_per_sample >> 3);
+
         lsx_writes(ft, "FORM");
-        lsx_writedw(ft, (p->nsamples* (ft->encoding.bits_per_sample >> 3)) + MAUDHEADERSIZE);  /* size of file */
+        lsx_writedw(ft, MAUDHEADERSIZE + mdat_size + mdat_size%2);  /* size of file */
         lsx_writes(ft, "MAUD"); /* File type */
 
         lsx_writes(ft, "MHDR");
@@ -306,8 +316,9 @@
         lsx_writedw(ft, 0); /* reserved */
 
         lsx_writes(ft, "ANNO");
-        lsx_writedw(ft, 30); /* length of block */
-        lsx_writes(ft, "file create by Sound eXchange ");
+        lsx_writedw(ft, 19); /* length of block */
+        lsx_writes(ft, "file created by SoX");
+        lsx_padbytes(ft, (size_t)1);
 
         lsx_writes(ft, "MDAT");
         lsx_writedw(ft, p->nsamples * (ft->encoding.bits_per_sample >> 3)); /* samples in file */
--- a/src/sox.c
+++ b/src/sox.c
@@ -1618,6 +1618,7 @@
     /* Report all input files; do this only the 1st time process() is called: */
     if (!current_input) for (i = 0; i < input_count; i++)
       report_file_info(files[i]);
+    combiner_signal.length = SOX_UNKNOWN_LEN;
   } else {
     size_t total_channels = 0;
     size_t min_channels = SOX_SIZE_MAX;
@@ -1624,7 +1625,7 @@
     size_t max_channels = 0;
     size_t min_rate = SOX_SIZE_MAX;
     size_t max_rate = 0;
-    uint64_t total_length = 0, max_length = 0;
+    uint64_t total_length = 0, max_length_ws = 0;
 
     /* Report all input files and gather info on differing rates & numbers of
      * channels, and on the resulting output audio length: */
@@ -1635,7 +1636,9 @@
       max_channels = max(max_channels, files[i]->ft->signal.channels);
       min_rate     = min(min_rate    , files[i]->ft->signal.rate);
       max_rate     = max(max_rate    , files[i]->ft->signal.rate);
-      max_length   = max(max_length  , files[i]->ft->signal.length);
+      max_length_ws = files[i]->ft->signal.length ?
+          max(max_length_ws, files[i]->ft->signal.length / files[i]->ft->signal.channels) :
+          SOX_UNKNOWN_LEN;
       if (total_length != SOX_UNKNOWN_LEN && files[i]->ft->signal.length)
         total_length += files[i]->ft->signal.length;
       else
@@ -1656,14 +1659,15 @@
     if (min_rate != max_rate)
       exit(1);
 
-    if (combine_method == sox_concatenate)
-      combiner_signal.length = total_length;
-    else if (is_parallel(combine_method))
-      combiner_signal.length = max_length;
-
     /* Store the calculated # of combined channels: */
     combiner_signal.channels =
       combine_method == sox_merge? total_channels : max_channels;
+
+    if (combine_method == sox_concatenate)
+      combiner_signal.length = total_length;
+    else if (is_parallel(combine_method))
+      combiner_signal.length = max_length_ws != SOX_UNKNOWN_LEN ?
+          max_length_ws * combiner_signal.channels : SOX_UNKNOWN_LEN;
   }
 } /* calculate_combiner_signal_parameters */
 
@@ -2536,6 +2540,7 @@
   sox_format_handler_t const * handler = sox_find_format(name, sox_false);
   if (handler) {
     sox_format_t format, * ft = &format;
+    lsx_debug("Looking for a default device: trying format `%s'", name);
     memset(ft, 0, sizeof(*ft));
     ft->filename = (char *)device_name(name);
     ft->priv = lsx_calloc(1, handler->priv_size);
--- a/src/spectrogram.c
+++ b/src/spectrogram.c
@@ -225,7 +225,7 @@
       pixels_per_sec = min(5000, p->x_size / duration);
     else if (!p->x_size && pixels_per_sec && duration)
       p->x_size = min(5000, (int)(pixels_per_sec * duration + .5));
-    if (!duration && effp->in_signal.length) {
+    if (!duration && effp->in_signal.length != SOX_UNKNOWN_LEN) {
       duration = effp->in_signal.length / (effp->in_signal.rate * effp->in_signal.channels);
       duration -= start_time;
       if (duration <= 0)
--- a/src/vorbis.c
+++ b/src/vorbis.c
@@ -146,6 +146,7 @@
 
   /* Setup buffer */
   vb->buf_len = DEF_BUF_LEN;
+  vb->buf_len -= vb->buf_len % (vi->channels*2); /* 2 bytes per sample */
   vb->buf = lsx_calloc(vb->buf_len, sizeof(char));
   vb->start = vb->end = 0;