shithub: sox

ref: ee4a8b7f2a9fad2e0782b427d068117371c4f7cf
dir: /src/rabbit.c/

View raw version
/*
 * libsamplerate (aka Secret Rabbit Code) support for sox
 * (c) Reuben Thomas <rrt@sc3d.org> 2006
 *
 * 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.  */

#include "sox_i.h"

#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <samplerate.h>

/* Private data for resampling */
typedef struct {
  int converter_type;           /* SRC converter type */
  double out_rate;
  SRC_STATE *state;             /* SRC state struct */
  SRC_DATA *data;               /* SRC_DATA control struct */
  sox_size_t i_alloc, o_alloc;  /* Samples allocated in data->data_{in,out} */
} *rabbit_t;

/*
 * Process options
 */
static int getopts(sox_effect_t * effp, int argc, char **argv)
{
  rabbit_t r = (rabbit_t) effp->priv;
  char dummy;     /* To check for extraneous chars. */

  r->converter_type = SRC_SINC_BEST_QUALITY;

  if (argc) {
    if (!strcmp(argv[0], "-c0")) {
      r->converter_type = SRC_SINC_BEST_QUALITY;
      argc--; argv++;
    } else if (!strcmp(argv[0], "-c1")) {
      r->converter_type = SRC_SINC_MEDIUM_QUALITY;
      argc--; argv++;
    } else if (!strcmp(argv[0], "-c2")) {
      r->converter_type = SRC_SINC_FASTEST;
      argc--; argv++;
    } else if (!strcmp(argv[0], "-c3")) {
      r->converter_type = SRC_ZERO_ORDER_HOLD;
      argc--; argv++;
    } else if (!strcmp(argv[0], "-c4")) {
      r->converter_type = SRC_LINEAR;
      argc--; argv++;
    }
  }

  r->out_rate = HUGE_VAL;
  if (argc) {
    if (sscanf(*argv, "%lf %c", &r->out_rate, &dummy) != 1 || r->out_rate <= 0) 
      return sox_usage(effp);
    argc--; argv++;
  }

  return argc? sox_usage(effp) : SOX_SUCCESS;
}

/*
 * Prepare processing.
 */
static int start(sox_effect_t * effp)
{
  rabbit_t r = (rabbit_t) effp->priv;
  int err = 0;
  double out_rate = r->out_rate != HUGE_VAL? r->out_rate : effp->outinfo.rate;

  if (effp->ininfo.rate == out_rate)
    return SOX_EFF_NULL;
          
  effp->outinfo.channels = effp->ininfo.channels;
  effp->outinfo.rate = out_rate;

  r->data = (SRC_DATA *)xcalloc(1, sizeof(SRC_DATA));
  r->data->src_ratio = out_rate / effp->ininfo.rate;
  r->i_alloc = r->o_alloc = 0;
  r->state = src_new(r->converter_type, effp->ininfo.channels, &err);
  if (err) {
    free(r->data);
    sox_fail("cannot initialise rabbit: %s", src_strerror(err));
    return SOX_EOF;
  }

  return SOX_SUCCESS;
}

/*
 * Read, convert, return data.
 */
static int flow(sox_effect_t * effp, const sox_ssample_t *ibuf, sox_ssample_t *obuf UNUSED,
                   sox_size_t *isamp, sox_size_t *osamp)
{
  rabbit_t r = (rabbit_t) effp->priv;
  SRC_DATA *d = r->data;
  unsigned int channels = effp->ininfo.channels;
  sox_size_t i;
  sox_size_t isamples0 = d->input_frames * channels;
  sox_size_t isamples = isamples0 + *isamp;
  sox_size_t osamples = isamples * (d->src_ratio + 0.01) + 8;

  if (osamples > *osamp) {
    osamples = *osamp;
    isamples = (osamples - 8) / (d->src_ratio + 0.01);
  }

  if (r->i_alloc < isamples) {
    d->data_in = xrealloc(d->data_in, isamples * sizeof(float));
    r->i_alloc = isamples;
  }
  if (r->o_alloc < osamples) {
    d->data_out = xrealloc(d->data_out, osamples * sizeof(float));
    r->o_alloc = osamples;
    d->output_frames = osamples / channels;
  }

  for (i = 0; i < isamples - isamples0; i++)
    d->data_in[isamples0 + i] = SOX_SAMPLE_TO_FLOAT_32BIT(ibuf[i], effp->clips);
  *isamp = i;

  d->input_frames = isamples / channels;

  *osamp = 0;
  while (d->input_frames > 0 || d->end_of_input != 0) {
    if (src_process(r->state, r->data) != 0) {
      sox_fail("%s", src_strerror(src_error(r->state)));
      return SOX_EOF;
    }
    d->input_frames -= d->input_frames_used;
    if (d->input_frames)
       memmove(d->data_in, d->data_in + d->input_frames_used * sizeof(float),
           d->input_frames * sizeof(float));

    for (i = 0; i < (sox_size_t)d->output_frames_gen * channels; i++)
      obuf[i] = SOX_FLOAT_32BIT_TO_SAMPLE(d->data_out[i], effp->clips);
    *osamp += i;

    if (!d->output_frames_gen || d->end_of_input)
      break;
  }

  return SOX_SUCCESS;
}

/*
 * Process samples and write output.
 */
static int drain(sox_effect_t * effp, sox_ssample_t *obuf, sox_size_t *osamp)
{
  rabbit_t r = (rabbit_t) effp->priv;
  static sox_size_t isamp = 0;
  r->data->end_of_input = 1;
  return flow(effp, NULL, obuf, &isamp, osamp);
}

/*
 * Do anything required when you stop reading samples.
 */
static int stop(sox_effect_t * effp)
{
  rabbit_t r = (rabbit_t) effp->priv;

  free(r->data);
  src_delete(r->state);
  return SOX_SUCCESS;
}

const sox_effect_handler_t *sox_rabbit_effect_fn(void)
{
  static sox_effect_handler_t handler = {
    "rabbit", "[-c0|-c1|-c2|-c3|-c4] [rate]",
    SOX_EFF_RATE | SOX_EFF_MCHAN,
    getopts, start, flow, drain, stop, NULL
  };

  return &handler;
}