Sigrid Solveig Haflínudóttirbuild libsox on Plan 9f261dd33 years agoHistory
284 lines · 9.2 KB · C
RawDownload
/* libSoX effect: remix   Copyright (c) 2008-9 robs@users.sourceforge.net * * 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 <string.h> typedef struct {  enum {semi, automatic, manual} mode;  sox_bool mix_power;  unsigned num_out_channels, min_in_channels;  struct {    char * str;          /* Command-line argument to parse for this out_spec */    unsigned num_in_channels;    struct in_spec {      unsigned channel_num;      double   multiplier;    } * in_specs;  } * out_specs;} priv_t; #define PARSE(SEP, SCAN, VAR, MIN, SEPARATORS) do {\  end = strpbrk(text, SEPARATORS); \  if (end == text) \    SEP = *text++; \  else { \    SEP = (SEPARATORS)[strlen(SEPARATORS) - 1]; \    n = sscanf(text, SCAN"%c", &VAR, &SEP); \    if (n == 0 || VAR < MIN || (n == 2 && !strchr(SEPARATORS, SEP))) \      return lsx_usage(effp); \    text = end? end + 1 : text + strlen(text); \  } \} while (0) static int parse(sox_effect_t * effp, char * * argv, unsigned channels){  priv_t * p = (priv_t *)effp->priv;  unsigned i, j;  double mult;   p->min_in_channels = 0;  for (i = 0; i < p->num_out_channels; ++i) {    sox_bool mul_spec = sox_false;    char * text, * end;    if (argv) /* 1st parse only */      p->out_specs[i].str = lsx_strdup(argv[i]);    for (j = 0, text = p->out_specs[i].str; *text;) {      static char const separators[] = "-vpi,";      char sep1, sep2;      int chan1 = 1, chan2 = channels, n;      double multiplier = HUGE_VAL;       PARSE(sep1, "%i", chan1, 0, separators);      if (!chan1) {       if (j || *text)         return lsx_usage(effp);       continue;      }      if (sep1 == '-')        PARSE(sep1, "%i", chan2, 0, separators + 1);      else chan2 = chan1;      if (sep1 != ',') {        multiplier = sep1 == 'v' ? 1 : 0;        PARSE(sep2, "%lf", multiplier, -HUGE_VAL, separators + 4);        if (sep1 != 'v')          multiplier = (sep1 == 'p'? 1 : -1) * dB_to_linear(multiplier);        mul_spec = sox_true;      }      if (chan2 < chan1) {int t = chan1; chan1 = chan2; chan2 = t;}      p->out_specs[i].in_specs = lsx_realloc(p->out_specs[i].in_specs,          (j + chan2 - chan1 + 1) * sizeof(*p->out_specs[i].in_specs));      while (chan1 <= chan2) {        p->out_specs[i].in_specs[j].channel_num = chan1++ - 1;        p->out_specs[i].in_specs[j++].multiplier = multiplier;      }      p->min_in_channels = max(p->min_in_channels, (unsigned)chan2);    }    p->out_specs[i].num_in_channels = j;    mult = 1. / (p->mix_power? sqrt((double)j) : j);    for (j = 0; j < p->out_specs[i].num_in_channels; ++j)      if (p->out_specs[i].in_specs[j].multiplier == HUGE_VAL)        p->out_specs[i].in_specs[j].multiplier = (p->mode == automatic || (p->mode == semi && !mul_spec)) ? mult : 1;  }  effp->out_signal.channels = p->num_out_channels;  return SOX_SUCCESS;} static int show(priv_t *p){  unsigned i, j;   for (j = 0; j < p->num_out_channels; j++) {    lsx_debug("%i: ", j);    for (i = 0; i < p->out_specs[j].num_in_channels; i++)      lsx_debug("\t%i %g", p->out_specs[j].in_specs[i].channel_num, p->out_specs[j].in_specs[i].multiplier);  }  return SOX_SUCCESS;} static int soxcreate(sox_effect_t * effp, int argc, char * * argv){  priv_t * p = (priv_t *)effp->priv;  --argc, ++argv;  if (argc && !strcmp(*argv, "-m")) p->mode = manual   , ++argv, --argc;  if (argc && !strcmp(*argv, "-a")) p->mode = automatic, ++argv, --argc;  if (argc && !strcmp(*argv, "-p")) p->mix_power = sox_true, ++argv, --argc;  if (!argc) {    lsx_fail("must specify at least one output channel");    return SOX_EOF;  }  p->num_out_channels = argc;  p->out_specs = lsx_calloc(p->num_out_channels, sizeof(*p->out_specs));  return parse(effp, argv, 1); /* No channels yet; parse with dummy */} static int start(sox_effect_t * effp){  priv_t * p = (priv_t *)effp->priv;  double max_sum = 0;  unsigned i, j;  int non_integer = 0;   parse(effp, NULL, effp->in_signal.channels);  if (effp->in_signal.channels < p->min_in_channels) {    lsx_fail("too few input channels");    return SOX_EOF;  }   for (j = 0; j < effp->out_signal.channels; j++) {    double sum = 0;    for (i = 0; i < p->out_specs[j].num_in_channels; i++) {      double mult = p->out_specs[j].in_specs[i].multiplier;      sum += fabs(mult);      non_integer += floor(mult) != mult;    }    max_sum = max(max_sum, sum);  }  if (effp->in_signal.mult && max_sum > 1)    *effp->in_signal.mult /= max_sum;  if (!non_integer)    effp->out_signal.precision = effp->in_signal.precision;  else    effp->out_signal.precision = SOX_SAMPLE_PRECISION;  show(p);  return SOX_SUCCESS;} static int flow(sox_effect_t * effp, const sox_sample_t * ibuf,    sox_sample_t * obuf, size_t * isamp, size_t * osamp){  priv_t * p = (priv_t *)effp->priv;  unsigned i, j, len;  len =  min(*isamp / effp->in_signal.channels, *osamp / effp->out_signal.channels);  *isamp = len * effp->in_signal.channels;  *osamp = len * effp->out_signal.channels;   for (; len--; ibuf += effp->in_signal.channels) for (j = 0; j < effp->out_signal.channels; j++) {    double out = 0;    for (i = 0; i < p->out_specs[j].num_in_channels; i++)      out += ibuf[p->out_specs[j].in_specs[i].channel_num] * p->out_specs[j].in_specs[i].multiplier;    *obuf++ = SOX_ROUND_CLIP_COUNT(out, effp->clips);  }  return SOX_SUCCESS;} static int closedown(sox_effect_t * effp){  priv_t * p = (priv_t *)effp->priv;  unsigned i;  for (i = 0; i < p->num_out_channels; ++i) {    free(p->out_specs[i].str);    free(p->out_specs[i].in_specs);  }  free(p->out_specs);  return SOX_SUCCESS;} sox_effect_handler_t const * lsx_remix_effect_fn(void){  static sox_effect_handler_t handler = {    "remix", "[-m|-a] [-p] <0|in-chan[v|p|i volume]{,in-chan[v|p|i volume]}>",    SOX_EFF_MCHAN | SOX_EFF_CHAN | SOX_EFF_GAIN | SOX_EFF_PREC,    soxcreate, start, flow, NULL, NULL, closedown, sizeof(priv_t)  };  return &handler;} /*----------------------- The `channels' effect alias ------------------------*/ static int channels_create(sox_effect_t * effp, int argc, char * * argv){  priv_t * p = (priv_t *)effp->priv;  char dummy;     /* To check for extraneous chars. */   if (argc == 2) {    if (sscanf(argv[1], "%d %c", (int *)&p->num_out_channels,          &dummy) != 1 || (int)p->num_out_channels <= 0)      return lsx_usage(effp);    effp->out_signal.channels = p->num_out_channels;  }  else if (argc != 1)    return lsx_usage(effp);  return SOX_SUCCESS;} static int channels_start(sox_effect_t * effp){  priv_t * p = (priv_t *)effp->priv;  unsigned num_out_channels = p->num_out_channels != 0 ?      p->num_out_channels : effp->out_signal.channels;  unsigned i, j;   p->out_specs = lsx_calloc(num_out_channels, sizeof(*p->out_specs));  if (effp->in_signal.channels == num_out_channels)    return SOX_EFF_NULL;   if (effp->in_signal.channels > num_out_channels) {    for (j = 0; j < num_out_channels; j++) {      unsigned in_per_out = (effp->in_signal.channels +          num_out_channels - 1 - j) / num_out_channels;      lsx_valloc(p->out_specs[j].in_specs, in_per_out);      p->out_specs[j].num_in_channels = in_per_out;      for (i = 0; i < in_per_out; ++i) {        p->out_specs[j].in_specs[i].channel_num = i * num_out_channels + j;        p->out_specs[j].in_specs[i].multiplier = 1. / in_per_out;      }    }  }  else for (j = 0; j < num_out_channels; j++) {    lsx_valloc(p->out_specs[j].in_specs, 1);    p->out_specs[j].num_in_channels = 1;    p->out_specs[j].in_specs[0].channel_num = j % effp->in_signal.channels;    p->out_specs[j].in_specs[0].multiplier = 1;  }  effp->out_signal.channels = p->num_out_channels = num_out_channels;  effp->out_signal.precision = (effp->in_signal.channels > num_out_channels) ?    SOX_SAMPLE_PRECISION : effp->in_signal.precision;  show(p);  return SOX_SUCCESS;} sox_effect_handler_t const * lsx_channels_effect_fn(void){  static sox_effect_handler_t handler;  handler = *lsx_remix_effect_fn();  handler.name = "channels";  handler.usage = "number";  handler.flags &= ~SOX_EFF_GAIN;  handler.getopts = channels_create;  handler.start = channels_start;  return &handler;} /*------------------------- The `oops' effect alias --------------------------*/ static int oops_getopts(sox_effect_t *effp, int argc, char **argv){  char *args[] = {0, "1,2i", "1,2i"};  args[0] = argv[0];  return --argc? lsx_usage(effp) : soxcreate(effp, 3, args);} sox_effect_handler_t const * lsx_oops_effect_fn(void){  static sox_effect_handler_t handler;  handler = *lsx_remix_effect_fn();  handler.name = "oops";  handler.usage = NULL;  handler.getopts = oops_getopts;  return &handler;}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t