shithub: sox

ref: 2a869a13a9d160481cbccd9bfb20bcf2c2146e99
dir: /src/repeat.c/

View raw version
/* libSoX repeat effect  Copyright (c) 2004 Jan Paul Schmidt <jps@fundament.org>
 *
 * 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.1 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,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "sox_i.h"

#include <stdio.h>
#include <string.h>
#include <sys/types.h>  /* for off_t on OS/2 and possibly others */

typedef struct {
  FILE * tmp_file;
  int first_drain;
  sox_size_t total;
  sox_size_t remaining;
  int repeats;
} priv_t;

static int getopts(sox_effect_t * effp, int argc, char **argv)
{
  priv_t * p = (priv_t *)effp->priv;
  do {NUMERIC_PARAMETER(repeats, 0, 1e6)} while (0);
  return argc? lsx_usage(effp) : SOX_SUCCESS;
}

static int start(sox_effect_t * effp)
{
  priv_t * p = (priv_t *)effp->priv;
  if (p->repeats == 0)
    return SOX_EFF_NULL;

  if ((p->tmp_file = tmpfile()) == NULL) {
    sox_fail("can't create temporary file: %s", strerror(errno));
    return SOX_EOF;
  }
  p->first_drain = 1;
  return SOX_SUCCESS;
}

static int flow(sox_effect_t * effp, const sox_sample_t * ibuf,
    sox_sample_t * obuf, sox_size_t * isamp, sox_size_t * osamp)
{
  priv_t * p = (priv_t *)effp->priv;
  if (fwrite(ibuf, sizeof(*ibuf), *isamp, p->tmp_file) != *isamp) {
    sox_fail("error writing temporary file: %s", strerror(errno));
    return SOX_EOF;
  }
  (void)obuf, *osamp = 0; /* samples not output until drain */
  return SOX_SUCCESS;
}

static int drain(sox_effect_t * effp, sox_sample_t * obuf, sox_size_t * osamp)
{
  priv_t * p = (priv_t *)effp->priv;
  size_t read = 0;
  sox_sample_t *buf;
  sox_size_t samp;
  sox_size_t done;

  if (p->first_drain == 1) {
    p->first_drain = 0;

    fseeko(p->tmp_file, (off_t) 0, SEEK_END);
    p->total = ftello(p->tmp_file);

    if ((p->total % sizeof(sox_sample_t)) != 0) {
      sox_fail("corrupted temporary file");
      return (SOX_EOF);
    }

    p->total /= sizeof(sox_sample_t);
    p->remaining = p->total;

    fseeko(p->tmp_file, (off_t) 0, SEEK_SET);
  }

  if (p->remaining == 0) {
    if (p->repeats == 0) {
      *osamp = 0;
      return (SOX_EOF);
    } else {
      p->repeats--;
      fseeko(p->tmp_file, (off_t) 0, SEEK_SET);
      p->remaining = p->total;
    }
  }
  if (*osamp > p->remaining) {
    buf = obuf;
    samp = p->remaining;

    read = fread(buf, sizeof(sox_sample_t), samp, p->tmp_file);
    if (read != samp) {
      perror(strerror(errno));
      sox_fail("read error on temporary file");
      return (SOX_EOF);
    }

    done = samp;
    buf = &obuf[samp];
    p->remaining = 0;

    while (p->repeats > 0) {
      p->repeats--;
      fseeko(p->tmp_file, (off_t) 0, SEEK_SET);

      if (p->total >= *osamp - done) {
        samp = *osamp - done;
      } else {
        samp = p->total;
        if (samp > *osamp - done)
          samp = *osamp - done;
      }

      p->remaining = p->total - samp;

      read = fread(buf, sizeof(sox_sample_t), samp, p->tmp_file);
      if (read != samp) {
        perror(strerror(errno));
        sox_fail("repeat2: read error on temporary " "file\n");
        return (SOX_EOF);
      }

      done += samp;
      if (done == *osamp)
        break;
    }
    *osamp = done;
  } else {
    read = fread(obuf, sizeof(sox_sample_t), *osamp, p->tmp_file);
    if (read != *osamp) {
      perror(strerror(errno));
      sox_fail("repeat3: read error on temporary file");
      return (SOX_EOF);
    }
    p->remaining -= *osamp;
  }

  if (p->remaining == 0)
    return SOX_EOF;
  else
    return SOX_SUCCESS;
}

static int stop(sox_effect_t * effp)
{
  priv_t * p = (priv_t *)effp->priv;
  fclose(p->tmp_file);
  return SOX_SUCCESS;
}

const sox_effect_handler_t *sox_repeat_effect_fn(void)
{
  static sox_effect_handler_t effect = {"repeat", "count", SOX_EFF_MCHAN |
    SOX_EFF_LENGTH, getopts, start, flow, drain, stop, NULL, sizeof(priv_t)};
  return &effect;
}