1009 lines · 32.8 KB · C
RawDownload
/* libSoX effect: Spectrogram       (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 */ #ifdef NDEBUG /* Enable assert always. */#undef NDEBUG /* Must undef above assert.h or other that might include it. */#endif #include "sox_i.h"#include "fft4g.h"#include <assert.h>#include <math.h>#ifdef HAVE_LIBPNG_PNG_H#include <libpng/png.h>#else#include <png.h>#endif#include <zlib.h> /* For SET_BINARY_MODE: */#include <fcntl.h>#ifdef HAVE_IO_H  #include <io.h>#endif #define is_p2(x) !(x & (x - 1)) #define MAX_X_SIZE 200000 #if SSIZE_MAX < UINT32_MAX#define MAX_Y_SIZE 16384 /* avoid multiplication overflow on 32-bit systems */#else#define MAX_Y_SIZE 200000#endif typedef enum {  Window_Hann,  Window_Hamming,  Window_Bartlett,  Window_Rectangular,  Window_Kaiser,  Window_Dolph} win_type_t; static const lsx_enum_item window_options[] = {  LSX_ENUM_ITEM(Window_,Hann)  LSX_ENUM_ITEM(Window_,Hamming)  LSX_ENUM_ITEM(Window_,Bartlett)  LSX_ENUM_ITEM(Window_,Rectangular)  LSX_ENUM_ITEM(Window_,Kaiser)  LSX_ENUM_ITEM(Window_,Dolph)  {0, 0}}; typedef struct {  /* Parameters */  double     pixels_per_sec;  double     window_adjust;  int        x_size0;  int        y_size;  int        Y_size;  int        dB_range;  int        gain;  int        spectrum_points;  int        perm;  sox_bool   monochrome;  sox_bool   light_background;  sox_bool   high_colour;  sox_bool   slack_overlap;  sox_bool   no_axes;  sox_bool   normalize;  sox_bool   raw;  sox_bool   alt_palette;  sox_bool   truncate;  win_type_t win_type;  const char *out_name;  const char *title;  const char *comment;  const char *duration_str;  const char *start_time_str;  sox_bool   using_stdout; /* output image to stdout */   /* Shared work area */  double     *shared;  double     **shared_ptr;   /* Per-channel work area */  int        WORK;  /* Start of work area is marked by this dummy variable. */  uint64_t   skip;  int        dft_size;  int        step_size;  int        block_steps;  int        block_num;  int        rows;  int        cols;  int        read;  int        x_size;  int        end;  int        end_min;  int        last_end;  sox_bool   truncated;  double     *buf;              /* [dft_size] */  double     *dft_buf;          /* [dft_size] */  double     *window;           /* [dft_size + 1] */  double     block_norm;  double     max;  double     *magnitudes;       /* [dft_size / 2 + 1] */  float      *dBfs;} priv_t; #define secs(cols) \  ((double)(cols) * p->step_size * p->block_steps / effp->in_signal.rate) static const unsigned char alt_palette[] = {  0x00, 0x00, 0x00,  0x00, 0x00, 0x03,  0x00, 0x01, 0x05,  0x00, 0x01, 0x08,  0x00, 0x01, 0x0a,  0x00, 0x01, 0x0b,  0x00, 0x01, 0x0e,  0x01, 0x02, 0x10,  0x01, 0x02, 0x12,  0x01, 0x02, 0x15,  0x01, 0x02, 0x16,  0x01, 0x02, 0x18,  0x01, 0x03, 0x1b,  0x01, 0x03, 0x1d,  0x01, 0x03, 0x1f,  0x01, 0x03, 0x20,  0x01, 0x03, 0x22,  0x01, 0x03, 0x24,  0x01, 0x03, 0x25,  0x01, 0x03, 0x27,  0x01, 0x03, 0x28,  0x01, 0x03, 0x2a,  0x01, 0x03, 0x2c,  0x01, 0x03, 0x2e,  0x01, 0x03, 0x2f,  0x01, 0x03, 0x30,  0x01, 0x03, 0x32,  0x01, 0x03, 0x34,  0x02, 0x03, 0x36,  0x04, 0x03, 0x38,  0x05, 0x03, 0x39,  0x07, 0x03, 0x3b,  0x09, 0x03, 0x3d,  0x0b, 0x03, 0x3f,  0x0e, 0x03, 0x41,  0x0f, 0x02, 0x42,  0x11, 0x02, 0x44,  0x13, 0x02, 0x46,  0x15, 0x02, 0x48,  0x17, 0x02, 0x4a,  0x18, 0x02, 0x4b,  0x1a, 0x02, 0x4d,  0x1d, 0x02, 0x4f,  0x20, 0x02, 0x51,  0x24, 0x02, 0x53,  0x28, 0x02, 0x55,  0x2b, 0x02, 0x57,  0x30, 0x02, 0x5a,  0x33, 0x02, 0x5c,  0x37, 0x02, 0x5f,  0x3b, 0x02, 0x61,  0x3e, 0x02, 0x63,  0x42, 0x02, 0x65,  0x45, 0x02, 0x68,  0x49, 0x02, 0x6a,  0x4d, 0x02, 0x6c,  0x51, 0x02, 0x6e,  0x55, 0x02, 0x70,  0x5a, 0x02, 0x72,  0x5f, 0x02, 0x74,  0x63, 0x02, 0x75,  0x68, 0x02, 0x76,  0x6c, 0x02, 0x78,  0x70, 0x03, 0x7a,  0x75, 0x03, 0x7c,  0x7a, 0x03, 0x7d,  0x7e, 0x03, 0x7e,  0x83, 0x03, 0x80,  0x87, 0x03, 0x82,  0x8c, 0x03, 0x84,  0x90, 0x03, 0x85,  0x93, 0x03, 0x83,  0x96, 0x03, 0x80,  0x98, 0x03, 0x7e,  0x9b, 0x03, 0x7c,  0x9e, 0x03, 0x7a,  0xa0, 0x03, 0x78,  0xa3, 0x03, 0x75,  0xa6, 0x03, 0x73,  0xa9, 0x03, 0x71,  0xab, 0x03, 0x6f,  0xae, 0x03, 0x6d,  0xb1, 0x03, 0x6a,  0xb3, 0x03, 0x68,  0xb6, 0x03, 0x66,  0xba, 0x03, 0x62,  0xbc, 0x03, 0x5e,  0xc0, 0x03, 0x5a,  0xc3, 0x03, 0x56,  0xc7, 0x03, 0x52,  0xca, 0x03, 0x4e,  0xcd, 0x03, 0x4a,  0xd1, 0x03, 0x46,  0xd4, 0x03, 0x43,  0xd7, 0x03, 0x3e,  0xdb, 0x03, 0x3a,  0xde, 0x03, 0x36,  0xe2, 0x03, 0x32,  0xe4, 0x03, 0x2f,  0xe6, 0x07, 0x2d,  0xe8, 0x0d, 0x2c,  0xea, 0x11, 0x2b,  0xec, 0x17, 0x2a,  0xed, 0x1b, 0x29,  0xee, 0x20, 0x28,  0xf0, 0x26, 0x27,  0xf2, 0x2a, 0x26,  0xf4, 0x2f, 0x24,  0xf5, 0x34, 0x23,  0xf6, 0x39, 0x23,  0xf8, 0x3e, 0x21,  0xfa, 0x43, 0x20,  0xfc, 0x49, 0x20,  0xfc, 0x4f, 0x22,  0xfc, 0x56, 0x26,  0xfc, 0x5d, 0x2a,  0xfc, 0x64, 0x2c,  0xfc, 0x6b, 0x30,  0xfc, 0x72, 0x33,  0xfc, 0x7a, 0x37,  0xfd, 0x81, 0x3b,  0xfd, 0x88, 0x3e,  0xfd, 0x8f, 0x42,  0xfd, 0x96, 0x45,  0xfd, 0x9e, 0x49,  0xfd, 0xa5, 0x4d,  0xfd, 0xac, 0x50,  0xfd, 0xb1, 0x54,  0xfd, 0xb7, 0x58,  0xfd, 0xbc, 0x5c,  0xfd, 0xc1, 0x61,  0xfd, 0xc6, 0x65,  0xfd, 0xcb, 0x69,  0xfd, 0xd0, 0x6d,  0xfe, 0xd5, 0x71,  0xfe, 0xda, 0x76,  0xfe, 0xdf, 0x7a,  0xfe, 0xe4, 0x7e,  0xfe, 0xe9, 0x82,  0xfe, 0xee, 0x86,  0xfe, 0xf3, 0x8b,  0xfd, 0xf5, 0x8f,  0xfc, 0xf6, 0x93,  0xfb, 0xf7, 0x98,  0xfa, 0xf7, 0x9c,  0xf9, 0xf8, 0xa1,  0xf8, 0xf9, 0xa5,  0xf7, 0xf9, 0xaa,  0xf6, 0xfa, 0xae,  0xf5, 0xfa, 0xb3,  0xf4, 0xfb, 0xb7,  0xf3, 0xfc, 0xbc,  0xf1, 0xfd, 0xc0,  0xf0, 0xfd, 0xc5,  0xf0, 0xfe, 0xc9,  0xef, 0xfe, 0xcc,  0xef, 0xfe, 0xcf,  0xf0, 0xfe, 0xd1,  0xf0, 0xfe, 0xd4,  0xf0, 0xfe, 0xd6,  0xf0, 0xfe, 0xd8,  0xf0, 0xfe, 0xda,  0xf1, 0xff, 0xdd,  0xf1, 0xff, 0xdf,  0xf1, 0xff, 0xe1,  0xf1, 0xff, 0xe4,  0xf1, 0xff, 0xe6,  0xf2, 0xff, 0xe8,};#define alt_palette_len (array_length(alt_palette) / 3) static int getopts(sox_effect_t *effp, int argc, char **argv){  priv_t *p = effp->priv;  uint64_t dummy;  const char *next;  int c;  lsx_getopt_t optstate;   lsx_getopt_init(argc, argv, "+S:d:x:X:y:Y:z:Z:q:p:W:w:st:c:AarmnlhTo:",                  NULL, lsx_getopt_flag_none, 1, &optstate);   p->dB_range = 120;  p->spectrum_points = 249;  p->perm = 1;  p->out_name = "spectrogram.png";  p->comment = "Created by SoX";   while ((c = lsx_getopt(&optstate)) != -1) {    switch (c) {      GETOPT_NUMERIC(optstate, 'x', x_size0,        100, MAX_X_SIZE)      GETOPT_NUMERIC(optstate, 'X', pixels_per_sec,   1, 5000)      GETOPT_NUMERIC(optstate, 'y', y_size,          64, MAX_Y_SIZE)      GETOPT_NUMERIC(optstate, 'Y', Y_size,         130, MAX_Y_SIZE)      GETOPT_NUMERIC(optstate, 'z', dB_range,        20, 180)      GETOPT_NUMERIC(optstate, 'Z', gain,          -100, 100)      GETOPT_NUMERIC(optstate, 'q', spectrum_points,  0, p->spectrum_points)      GETOPT_NUMERIC(optstate, 'p', perm,             1, 6)      GETOPT_NUMERIC(optstate, 'W', window_adjust,  -10, 10)      case 'w': p->win_type = lsx_enum_option(c, optstate.arg, window_options);                break;      case 's': p->slack_overlap    = sox_true;   break;      case 'A': p->alt_palette      = sox_true;   break;      case 'a': p->no_axes          = sox_true;   break;      case 'r': p->raw              = sox_true;   break;      case 'm': p->monochrome       = sox_true;   break;      case 'n': p->normalize        = sox_true;   break;      case 'l': p->light_background = sox_true;   break;      case 'h': p->high_colour      = sox_true;   break;      case 'T': p->truncate         = sox_true;   break;      case 't': p->title            = optstate.arg; break;      case 'c': p->comment          = optstate.arg; break;      case 'o': p->out_name         = optstate.arg; break;      case 'S':        next = lsx_parseposition(0, optstate.arg, NULL, 0, 0, '=');        if (next && !*next) {          p->start_time_str = lsx_strdup(optstate.arg);          break;        }        return lsx_usage(effp);      case 'd':        next = lsx_parsesamples(1e5, optstate.arg, &dummy, 't');        if (next && !*next) {          p->duration_str = lsx_strdup(optstate.arg);          break;        }        return lsx_usage(effp);      default:        lsx_fail("invalid option `-%c'", optstate.opt);        return lsx_usage(effp);    }  }   if (!!p->x_size0 + !!p->pixels_per_sec + !!p->duration_str > 2) {    lsx_fail("only two of -x, -X, -d may be given");    return SOX_EOF;  }   if (p->y_size && p->Y_size) {    lsx_fail("only one of -y, -Y may be given");    return SOX_EOF;  }   p->gain = -p->gain;  --p->perm;  p->spectrum_points += 2;  if (p->alt_palette)    p->spectrum_points = min(p->spectrum_points, alt_palette_len);  p->shared_ptr = &p->shared;   if (!strcmp(p->out_name, "-")) {    if (effp->global_info->global_info->stdout_in_use_by) {      lsx_fail("stdout already in use by `%s'",               effp->global_info->global_info->stdout_in_use_by);      return SOX_EOF;    }    effp->global_info->global_info->stdout_in_use_by = effp->handler.name;    p->using_stdout = sox_true;  }   return optstate.ind != argc || p->win_type == INT_MAX ?    lsx_usage(effp) : SOX_SUCCESS;} static double make_window(priv_t *p, int end){  double sum = 0;  double *w = end < 0 ? p->window : p->window + end;  double beta;  int n = 1 + p->dft_size - abs(end);  int i;   if (end)    memset(p->window, 0, sizeof(*p->window) * (p->dft_size + 1));   for (i = 0; i < n; ++i)    w[i] = 1;   switch (p->win_type) {    case Window_Hann:        lsx_apply_hann(w, n); break;    case Window_Hamming:     lsx_apply_hamming(w, n); break;    case Window_Bartlett:    lsx_apply_bartlett(w, n); break;    case Window_Rectangular: break;    case Window_Kaiser:      beta = lsx_kaiser_beta((p->dB_range + p->gain) *                             (1.1 + p->window_adjust / 50), .1);      lsx_apply_kaiser(w, n, beta);      break;    default:      lsx_apply_dolph(w, n, (p->dB_range + p->gain) *                      (1.005 + p->window_adjust / 50) + 6);  }   for (i = 0; i < p->dft_size; ++i)    sum += p->window[i];   /* empirical small window adjustment */  for (--n, i = 0; i < p->dft_size; ++i)    p->window[i] *= 2 / sum * sqr((double)n / p->dft_size);   return sum;} static double *rdft_init(size_t n){  double *q = lsx_malloc(2 * (n / 2 + 1) * n * sizeof(*q));  double *p = q;  int i, j;   for (j = 0; j <= n / 2; ++j) {    for (i = 0; i < n; ++i) {      *p++ = cos(2 * M_PI * j * i / n);      *p++ = sin(2 * M_PI * j * i / n);    }  }   return q;} #define _ re += in[i] * *q++, im += in[i++] * *q++,static void rdft_p(const double *q, const double *in, double *out, int n){  int i, j;   for (j = 0; j <= n / 2; ++j) {    double re = 0, im = 0;     for (i = 0; i < (n & ~7);)      _ _ _ _ _ _ _ _ (void)0;     while (i < n)      _ (void)0;     *out++ += re * re + im * im;  }} static int start(sox_effect_t *effp){  priv_t *p = effp->priv;  double actual;  double duration = 0.0;  double start_time = 0.0;  double pixels_per_sec = p->pixels_per_sec;  uint64_t d;   memset(&p->WORK, 0, sizeof(*p) - field_offset(priv_t, WORK));   if (p->duration_str) {    lsx_parsesamples(effp->in_signal.rate, p->duration_str, &d, 't');    duration = d / effp->in_signal.rate;  }   if (p->start_time_str) {    uint64_t in_length = effp->in_signal.length != SOX_UNKNOWN_LEN ?      effp->in_signal.length / effp->in_signal.channels : SOX_UNKNOWN_LEN;     if (!lsx_parseposition(effp->in_signal.rate, p->start_time_str, &d,                           0, in_length, '=') || d == SOX_UNKNOWN_LEN) {      lsx_fail("-S option: audio length is unknown");      return SOX_EOF;    }     start_time = d / effp->in_signal.rate;    p->skip = d;  }   p->x_size = p->x_size0;   while (sox_true) {    if (!pixels_per_sec && p->x_size && duration)      pixels_per_sec = min(5000, p->x_size / duration);    else if (!p->x_size && pixels_per_sec && duration)      p->x_size = min(MAX_X_SIZE, (int)(pixels_per_sec * duration + .5));     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)        duration = 1;      continue;    } else if (!p->x_size) {      p->x_size = 800;      continue;    } else if (!pixels_per_sec) {      pixels_per_sec = 100;      continue;    }    break;  }   if (p->y_size) {    p->dft_size = 2 * (p->y_size - 1);    if (!is_p2(p->dft_size) && !effp->flow)      p->shared = rdft_init(p->dft_size);  } else {   int y = max(32, (p->Y_size? p->Y_size : 550) / effp->in_signal.channels - 2);   for (p->dft_size = 128; p->dft_size <= y; p->dft_size <<= 1);  }   /* Now that dft_size is set, allocate variable-sized elements of priv_t */  p->buf        = lsx_calloc(p->dft_size, sizeof(*p->buf));  p->dft_buf    = lsx_calloc(p->dft_size, sizeof(*p->dft_buf));  p->window     = lsx_calloc(p->dft_size + 1, sizeof(*p->window));  p->magnitudes = lsx_calloc(p->dft_size / 2 + 1, sizeof(*p->magnitudes));   if (is_p2(p->dft_size) && !effp->flow)    lsx_safe_rdft(p->dft_size, 1, p->dft_buf);   lsx_debug("duration=%g x_size=%i pixels_per_sec=%g dft_size=%i",            duration, p->x_size, pixels_per_sec, p->dft_size);   p->end = p->dft_size;  p->rows = (p->dft_size >> 1) + 1;  actual = make_window(p, p->last_end = 0);  lsx_debug("window_density=%g", actual / p->dft_size);  p->step_size = (p->slack_overlap ? sqrt(actual * p->dft_size) : actual) + 0.5;  p->block_steps = effp->in_signal.rate / pixels_per_sec;  p->step_size =    p->block_steps / ceil((double)p->block_steps / p->step_size) + 0.5;  p->block_steps = floor((double)p->block_steps / p->step_size + 0.5);  p->block_norm = 1.0 / p->block_steps;  actual = effp->in_signal.rate / p->step_size / p->block_steps;  if (!effp->flow && actual != pixels_per_sec)    lsx_report("actual pixels/s = %g", actual);  lsx_debug("step_size=%i block_steps=%i", p->step_size, p->block_steps);  p->max = -p->dB_range;  p->read = (p->step_size - p->dft_size) / 2;   return SOX_SUCCESS;} static int do_column(sox_effect_t *effp){  priv_t *p = effp->priv;  int i;   if (p->cols == p->x_size) {    p->truncated = sox_true;    if (!effp->flow)      lsx_report("PNG truncated at %g seconds", secs(p->cols));    return p->truncate ? SOX_EOF : SOX_SUCCESS;  }   ++p->cols;  p->dBfs = lsx_realloc(p->dBfs, p->cols * p->rows * sizeof(*p->dBfs));   /* FIXME: allocate in larger steps (for several columns) */  for (i = 0; i < p->rows; ++i) {    double dBfs = 10 * log10(p->magnitudes[i] * p->block_norm);    p->dBfs[(p->cols - 1) * p->rows + i] = dBfs + p->gain;    p->max = max(dBfs, p->max);  }   memset(p->magnitudes, 0, p->rows * sizeof(*p->magnitudes));  p->block_num = 0;   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 = effp->priv;  size_t len = *isamp = *osamp = min(*isamp, *osamp);  int i;   memcpy(obuf, ibuf, len * sizeof(*obuf)); /* Pass on audio unaffected */   if (p->skip) {    if (p->skip >= len) {      p->skip -= len;      return SOX_SUCCESS;    }    ibuf += p->skip;    len -= p->skip;    p->skip = 0;  }   while (!p->truncated) {    if (p->read == p->step_size) {      memmove(p->buf, p->buf + p->step_size,          (p->dft_size - p->step_size) * sizeof(*p->buf));

Lines 1–500 of 1009.

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