shithub: sox

Download patch

ref: d0ab0542497b5523234cca504c710ca8dc1c4723
parent: e5059c2f7f5999f15c9282beb93f525860c2e3ac
author: robs <robs>
date: Mon May 14 14:57:29 EDT 2007

Allow simpler synth commands; null pointer idiom for format drivers.

--- a/sox.1
+++ b/sox.1
@@ -105,8 +105,7 @@
 .EE
 plays a collection of audio files whilst applying a bass boosting effect,
 .EX
-	play -c4 -n -c1 synth sin %-12 sin %-9 sin %-5 sin %-2 \(rs
-		vol 0.7 mixer fade q 0.1 1 0.1
+	play -n -c1 synth sin %-12 sin %-9 sin %-5 sin %-2 fade q 0.1 1 0.1
 .EE
 plays a synthesised `A minor seventh' chord with a pipe-organ sound,
 .EX
@@ -417,9 +416,9 @@
 and is useful mainly with effects that produce information about the
 audio instead of affecting it (such as \fBnoiseprof\fR or \fBstat\fR).
 .SP
-The number of channels and the sampling rate associated with a null file
-are by default 2 and 44\*d1\ kHz respectively, but, as with a normal
-file, these can be overridden if desired using command-line format
+The sampling rate associated with a null file
+is by default 44\*d1\ kHz, but, as with a normal
+file, this can be overridden if desired using command-line format
 options (see below).
 .SP
 One other use of \fB\-n\fR is to use it in conjunction with
--- a/soxeffect.7
+++ b/soxeffect.7
@@ -929,13 +929,13 @@
 effect that can has an associated length).
 .SP
 For example, the following produces a 3 second, 44\*d1\ kHz,
-stereo audio file containing a sine-wave swept from 300 to 3300\ Hz:
+audio file containing a sine-wave swept from 300 to 3300\ Hz:
 .EX
 	sox -n output.au synth 3 sine 300-3300
 .EE
-and this produces an 8\ kHz mono version:
+and this produces an 8\ kHz version:
 .EX
-	sox -r 8000 -c 1 -n output.au synth 3 sine 300-3300
+	sox -r 8000 -n output.au synth 3 sine 300-3300
 .EE
 Multiple channels can be synthesised by specifying the set of
 parameters shown between braces multiple times;
--- a/src/auto.c
+++ b/src/auto.c
@@ -184,7 +184,7 @@
 
     sox_debug("Detected file format type: %s", type);
     set_endianness_if_not_already_set(ft);
-    return (* ft->h->startread)(ft);
+    return ft->h->startread? (* ft->h->startread)(ft) : SOX_SUCCESS;
 }
 
 static int sox_autostartwrite(ft_t ft) 
--- a/src/flac.c
+++ b/src/flac.c
@@ -367,6 +367,7 @@
   /* FIXME: FLAC should not need to know about this oddity */
   if (format->signal.encoding < SOX_ENCODING_SIZE_IS_WORD)
     format->signal.size = SOX_SIZE_16BIT;
+  format->signal.encoding = SOX_ENCODING_FLAC;
 
   encoder->bits_per_sample = (format->signal.size > 4 ? 4 : format->signal.size) << 3;
 
--- a/src/nulfile.c
+++ b/src/nulfile.c
@@ -1,35 +1,45 @@
 /*
- * July 5, 1991
- * Copyright 1991 Lance Norskog And Sundry Contributors
- * This source code is freely redistributable and may be used for
- * any purpose.  This copyright notice must be maintained. 
- * Lance Norskog And Sundry Contributors are not responsible for 
- * the consequences of using this software.
+ * File format: null   (c) 2006-7 SoX contributers
+ * Based on an original idea by Carsten Borchardt
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This library 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 Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library.  If not, write to the Free Software Foundation,
+ * Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
  */
 
-/*
- * libSoX null file format driver.
- * Written by Carsten Borchardt 
- * The author is not responsible for the consequences 
- * of using this software
- */
-
 #include "sox_i.h"
 #include <string.h>
 
-static int sox_nulstartread(ft_t ft) 
+static int startread(ft_t ft)
 {
   /* If format parameters are not given, set somewhat arbitrary
    * (but commonly used) defaults: */
-  if (ft->signal.rate     == 0) ft->signal.rate     = 44100;
-  if (ft->signal.channels == 0) ft->signal.channels = 2;
-  if (ft->signal.size     ==-1) ft->signal.size     = SOX_SIZE_16BIT;
-  if (ft->signal.encoding == SOX_ENCODING_UNKNOWN) ft->signal.encoding = SOX_ENCODING_SIGN2;
-
+  if (!ft->signal.rate) {
+    ft->signal.rate = 44100;
+    sox_report("sample rate not specified; using %i", ft->signal.rate);
+  }
+  if (ft->signal.size <= 0) {
+    ft->signal.size = SOX_SIZE_16BIT;
+    sox_report("precision not specified; using %s", sox_size_bits_str[ft->signal.size]);
+  }
+  if (ft->signal.encoding == SOX_ENCODING_UNKNOWN) {
+    ft->signal.encoding = SOX_ENCODING_SIGN2;
+    sox_report("encoding not specified; using %s", sox_encodings_str[ft->signal.encoding]);
+  }
   return SOX_SUCCESS;
 }
 
-static sox_size_t sox_nulread(ft_t ft UNUSED, sox_ssample_t *buf, sox_size_t len) 
+static sox_size_t read(ft_t ft UNUSED, sox_ssample_t *buf, sox_size_t len)
 {
   /* Reading from null generates silence i.e. (sox_sample_t)0. */
   memset(buf, 0, sizeof(sox_ssample_t) * len);
@@ -36,33 +46,20 @@
   return len; /* Return number of samples "read". */
 }
 
-static sox_size_t sox_nulwrite(ft_t ft UNUSED, const sox_ssample_t *buf UNUSED, sox_size_t len) 
+static sox_size_t write(ft_t ft UNUSED, const sox_ssample_t *buf UNUSED, sox_size_t len)
 {
   /* Writing to null just discards the samples */
   return len; /* Return number of samples "written". */
 }
 
-static const char *nulnames[] = {
-  "null",
-  "nul",  /* For backwards compatibility with scripts that used -t nul. */
-  NULL,
-};
-
-static sox_format_t sox_nul_format = {
-  nulnames,
-  SOX_FILE_DEVICE | SOX_FILE_PHONY | SOX_FILE_NOSTDIO,
-  sox_nulstartread,
-  sox_nulread,
-  sox_format_nothing,
-  sox_format_nothing,
-  sox_nulwrite,
-  sox_format_nothing,
-  sox_format_nothing_seek
-};
-
 const sox_format_t *sox_nul_format_fn(void);
 
 const sox_format_t *sox_nul_format_fn(void)
 {
-    return &sox_nul_format;
+  static const char *names[] = { "null", "nul"/* with -t; deprecated*/, NULL};
+  static sox_format_t driver = {
+    names, SOX_FILE_DEVICE | SOX_FILE_PHONY | SOX_FILE_NOSTDIO,
+    startread, read, 0, 0, write, 0, 0
+  };
+  return &driver;
 }
--- a/src/skeleff.c
+++ b/src/skeleff.c
@@ -128,7 +128,7 @@
 /*
  * Effect descriptor.
  * If no specific processing is needed for any of
- * the 6 functions, then the function can be deleted
+ * the 6 functions, then the function above can be deleted
  * and 0 used in place of the its name below.
  */
 static sox_effect_t sox_skel_effect = {
--- a/src/skelform.c
+++ b/src/skelform.c
@@ -206,7 +206,11 @@
   NULL
 };
 
-/* Format descriptor */
+/* Format descriptor
+ * If no specific processing is needed for any of
+ * the 7 functions, then the function above can be deleted
+ * and 0 used in place of the its name below.
+ */
 static sox_format_t sox_skel_format = {
   names,
   SOX_FILE_SEEK,
--- a/src/sox.c
+++ b/src/sox.c
@@ -576,6 +576,14 @@
     exit(0);
   }
 
+  for (i = 0; i < input_count; i++) {
+    int j;
+    for (j =0; j < nuser_effects && !files[i]->desc->signal.channels; ++j)
+      files[i]->desc->signal.channels = user_efftab[j].ininfo.channels;
+    if (!files[i]->desc->signal.channels)
+      ++files[i]->desc->signal.channels;
+  }
+
   if (repeatable_random)
     sox_debug("Not reseeding PRNG; randomness is repeatable");
   else {
@@ -949,14 +957,20 @@
     f->desc->mode == 'r'? "Input File     " : "Output File    ", f->desc->filename);
   if (strcmp(f->desc->filename, "-") == 0 || (f->desc->h->flags & SOX_FILE_DEVICE))
     fprintf(stderr, " (%s)", f->desc->h->names[0]);
+  fprintf(stderr, "\n");
 
-  fprintf(stderr, "\n"
-    "Sample Size    : %s (%s)\n"
-    "Sample Encoding: %s\n"
+  if (f->desc->signal.size)
+    fprintf(stderr, "Sample Size    : %s (%s)\n",
+        sox_size_bits_str[f->desc->signal.size],
+        sox_sizes_str[f->desc->signal.size]);
+
+  if (f->desc->signal.encoding)
+    fprintf(stderr, "Sample Encoding: %s\n",
+        sox_encodings_str[f->desc->signal.encoding]);
+
+  fprintf(stderr,
     "Channels       : %u\n"
     "Sample Rate    : %u\n",
-    sox_size_bits_str[f->desc->signal.size], sox_sizes_str[f->desc->signal.size],
-    sox_encodings_str[f->desc->signal.encoding],
     f->desc->signal.channels,
     f->desc->signal.rate);
 
@@ -969,14 +983,15 @@
         ws, "~="[f->desc->signal.rate == 44100],
         (double)ws/ f->desc->signal.rate * 44100 / 588);
     }
-    fprintf(stderr,
-      "Endian Type    : %s\n"
-      "Reverse Nibbles: %s\n"
-      "Reverse Bits   : %s\n",
-      f->desc->signal.size == 1? "N/A" :
-        f->desc->signal.reverse_bytes != SOX_IS_BIGENDIAN ? "big" : "little",
-      no_yes[f->desc->signal.reverse_nibbles],
-      no_yes[f->desc->signal.reverse_bits]);
+    if (f->desc->signal.size > 1)
+      fprintf(stderr, "Endian Type    : %s\n",
+          f->desc->signal.reverse_bytes != SOX_IS_BIGENDIAN ? "big" : "little");
+    if (f->desc->signal.size)
+      fprintf(stderr,
+        "Reverse Nibbles: %s\n"
+        "Reverse Bits   : %s\n",
+        no_yes[f->desc->signal.reverse_nibbles],
+        no_yes[f->desc->signal.reverse_bits]);
   }
 
   if (f->replay_gain != HUGE_VAL)
@@ -1371,6 +1386,8 @@
   int effects_mask = 0;
   sox_bool need_rate = combiner.rate     != ofile->desc->signal.rate;
   sox_bool need_chan = combiner.channels != ofile->desc->signal.channels;
+  int user_mchan = -1;
+  sox_size_t channels = combiner.channels;
 
   { /* Check if we have to add effects to change rate/chans or if the
        user has specified effects to do this, in which case, check if
@@ -1386,14 +1403,17 @@
         need_rate = sox_false;
         ++user_rate_effects;
       }
+      if (user_efftab[i].h->flags & SOX_EFF_MCHAN)
+        user_mchan = i;
     }
     if (user_chan_effects > 1) {
       sox_fail("Cannot specify multiple effects that change number of channels");
       exit(2);
     }
-    if (user_rate_effects > 1)
-      sox_report("Cannot specify multiple effects that change sample rate");
-      /* FIXME: exit here or add comment as to why not */
+    if (user_rate_effects > 1) {
+      sox_fail("Cannot specify multiple effects that change sample rate");
+      exit(2);
+    }
   }
 
   /* --------- add the effects ------------------------ */
@@ -1401,28 +1421,26 @@
   /* efftab[0] is always the input stream and always exists */
   neffects = 1;
 
-  /* If reducing channels, it's faster to do so before all other effects: */
-  if (need_chan && combiner.channels > ofile->desc->signal.channels) {
-    add_default_effect("mixer", &effects_mask);
-    need_chan = sox_false;
-  }
-  /* If reducing rate, it's faster to do so before all other effects
-   * (except reducing channels): */
-  if (need_rate && combiner.rate > ofile->desc->signal.rate) {
-    add_default_effect("resample", &effects_mask);
-    need_rate = sox_false;
-  }
   /* Copy user specified effects into the real efftab */
-  for (i = 0; i < nuser_effects; i++) {
-    memcpy(&efftab[neffects], &user_efftab[i], sizeof(efftab[0]));
-    add_effect(&effects_mask);
+  for (i = 0; i <= nuser_effects; i++) {
+    /* If reducing channels, it's faster to do so before all other effects: */
+    if ((int)i > user_mchan && need_chan && combiner.channels > ofile->desc->signal.channels) {
+      add_default_effect("mixer", &effects_mask);
+      channels = ofile->desc->signal.channels;
+      need_chan = sox_false;
+    }
+    /* If reducing rate, it's faster to do so before all other effects
+     * (except reducing channels): */
+    if (need_rate)
+      if (i == nuser_effects || (channels <= 2 && combiner.rate > ofile->desc->signal.rate)) {
+        add_default_effect("resample", &effects_mask);
+        need_rate = sox_false;
+      }
+    if (i < nuser_effects) {
+      memcpy(&efftab[neffects], &user_efftab[i], sizeof(efftab[0]));
+      add_effect(&effects_mask);
+    }
   }
-  /* If rate/channels-changing effects are needed but haven't yet been
-   * added, then do it here.  Change rate before channels because it's
-   * faster to change rate on a smaller # of channels and # of channels
-   * can not be reduced, only increased, at this point. */
-  if (need_rate)
-    add_default_effect("resample", &effects_mask);
   if (need_chan)
     add_default_effect("mixer", &effects_mask);
 }
@@ -1467,6 +1485,10 @@
   }
   for (i = 1; i < neffects; ++i) {
     struct sox_effect * e = &efftab[i];
+    if (e->ininfo.channels > 2 && !(e->h->flags & SOX_EFF_MCHAN)) {
+      sox_fail("Sorry, effect '%s' cannot handle multiple channels, and SoX can only help out in the case of 2 channels", e->name);
+      return SOX_EOF;
+    }
     sox_report("Effects chain: %-10s %-6s %uHz", e->name,
         e->ininfo.channels < 2 ? "mono" :
         (e->h->flags & SOX_EFF_MCHAN)? "multi" : "stereo", e->ininfo.rate);
@@ -1633,7 +1655,7 @@
 
   /* I have no input data ? */
   if (efftab[e - 1].odone == efftab[e - 1].olen) {
-    sox_debug("%s no data to pull to me!", efftab[e].name);
+    sox_debug_more("%s no data to pull to me!", efftab[e].name);
     return 0;
   }
 
@@ -1713,7 +1735,7 @@
   if (effstatus == SOX_EOF)
     return SOX_EOF;
   if (done == 0) {
-    sox_fail("Effect took & gave no samples!");
+    sox_fail("'%s' effect took & gave no samples!", efftab[e].name);
     exit(2);
   }
   return SOX_SUCCESS;
--- a/src/soxio.c
+++ b/src/soxio.c
@@ -154,7 +154,7 @@
       set_endianness_if_not_already_set(ft);
 
     /* Read and write starters can change their formats. */
-    if ((*ft->h->startread)(ft) != SOX_SUCCESS)
+    if (ft->h->startread && (*ft->h->startread)(ft) != SOX_SUCCESS)
     {
         sox_fail("Failed reading `%s': %s", ft->filename, ft->sox_errstr);
         goto input_error;
@@ -164,8 +164,8 @@
      * This is because libsox usually doesn't set this for mono file
      * formats (for historical reasons).
      */
-    if (ft->signal.channels == 0)
-        ft->signal.channels = 1;
+    if (!(ft->h->flags & SOX_FILE_PHONY) && !ft->signal.channels)
+      ft->signal.channels = 1;
 
     if (sox_checkformat(ft) )
     {
@@ -295,7 +295,7 @@
     set_endianness_if_not_already_set(ft);
 
     /* Read and write starters can change their formats. */
-    if ((*ft->h->startwrite)(ft) != SOX_SUCCESS)
+    if (ft->h->startwrite && (*ft->h->startwrite)(ft) != SOX_SUCCESS)
     {
         sox_fail("Failed writing %s: %s", ft->filename, ft->sox_errstr);
         goto output_error;
@@ -320,13 +320,13 @@
 
 sox_size_t sox_read(ft_t f, sox_ssample_t * buf, sox_size_t len)
 {
-  sox_size_t actual = (*f->h->read)(f, buf, len);
+  sox_size_t actual = f->h->read? (*f->h->read)(f, buf, len) : 0;
   return (actual > len? 0 : actual);
 }
 
 sox_size_t sox_write(ft_t ft, const sox_ssample_t *buf, sox_size_t len)
 {
-    return (*ft->h->write)(ft, buf, len);
+    return ft->h->write? (*ft->h->write)(ft, buf, len) : 0;
 }
 
 #define TWIDDLE_BYTE(ub, type) \
@@ -451,9 +451,9 @@
     int rc;
 
     if (ft->mode == 'r')
-        rc = (*ft->h->stopread)(ft);
+        rc = ft->h->stopread? (*ft->h->stopread)(ft) : SOX_SUCCESS;
     else
-        rc = (*ft->h->stopwrite)(ft);
+        rc = ft->h->stopwrite? (*ft->h->stopwrite)(ft) : SOX_SUCCESS;
 
     if (!(ft->h->flags & SOX_FILE_NOSTDIO))
         fclose(ft->fp);
@@ -467,17 +467,17 @@
     return rc;
 }
 
-int sox_seek(ft_t ft, sox_size_t offset, int whence)       
-{       
-    /* FIXME: Implement SOX_SEEK_CUR and SOX_SEEK_END. */         
-    if (whence != SOX_SEEK_SET)          
-        return SOX_EOF; /* FIXME: return SOX_EINVAL */    
+int sox_seek(ft_t ft, sox_size_t offset, int whence)
+{
+    /* FIXME: Implement SOX_SEEK_CUR and SOX_SEEK_END. */
+    if (whence != SOX_SEEK_SET)
+        return SOX_EOF; /* FIXME: return SOX_EINVAL */
 
-    /* If file is a seekable file and this handler supports seeking,    
-     * the invoke handlers function.    
-     */         
-    if (ft->seekable  && (ft->h->flags & SOX_FILE_SEEK))         
-        return (*ft->h->seek)(ft, offset);      
-    else        
-        return SOX_EOF; /* FIXME: return SOX_EBADF */     
+    /* If file is a seekable file and this handler supports seeking,
+     * the invoke handlers function.
+     */
+    if (ft->seekable  && (ft->h->flags & SOX_FILE_SEEK))
+        return ft->h->seek? (*ft->h->seek)(ft, offset) : SOX_EOF;
+    else
+        return SOX_EOF; /* FIXME: return SOX_EBADF */
 }
--- a/src/synth.c
+++ b/src/synth.c
@@ -22,7 +22,7 @@
   synth_trapezium,
   synth_trapetz  = synth_trapezium,   /* Deprecated name for trapezium */
   synth_exp,
-                                      /* Repetetives above, noises below */
+                                      /* Tones above, noises below */
   synth_whitenoise,
   synth_noise = synth_whitenoise,     /* Just a handy alias */
   synth_pinknoise,
@@ -365,6 +365,16 @@
       NUMERIC_PARAMETER(p1,   0, 100)
     } while (0);
   }
+
+  /* If no channels parameters were given, create one default channel: */
+  if (!synth->number_of_channels) {
+    synth->channels = xmalloc(sizeof(*synth->channels));
+    create_channel(&synth->channels[synth->number_of_channels++]);
+  }
+
+  if (!effp->ininfo.channels)
+    effp->ininfo.channels = synth->number_of_channels;
+
   return SOX_SUCCESS;
 }
 
@@ -384,12 +394,6 @@
       sox_fail(effp->h->usage);
       return SOX_EOF;
     }
-
-  /* If no channels parameters were given, create one default channel: */
-  if (!synth->number_of_channels) {
-    synth->channels = xmalloc(sizeof(*synth->channels));
-    create_channel(&synth->channels[synth->number_of_channels++]);
-  }
 
   /* If too few channel parameters were given, copy channels: */
   if (synth->number_of_channels < effp->ininfo.channels) {