History

more colour control

robs committed 18 years ago

Commit 784ea2a1 parent d2b295b2 files · +155 −101Browse files

Showing the changes to src/spectrogram.c (1 of 2 files). Show all

Unified · Split

src/spectrogram.c+99−71
@@ -89,18 +89,16 @@ static enum_item const window_options[] = { · 88 unmodified lines
89 89
90typedef struct {90typedef struct {
91 int y_size, dft_size, rows, cols, step_size, block_steps, block_num;91 int y_size, dft_size, rows, cols, step_size, block_steps, block_num;
92 double buf[MAX_DFT_SIZE];
93 double dft_buf[MAX_DFT_SIZE];
94 double window[MAX_DFT_SIZE];
95 int bit_rev_table[100]; /* For Ooura fft */
96 double sin_cos_table[dft_sc_len(MAX_DFT_SIZE)]; /* ditto */
97 double magnitudes[(MAX_DFT_SIZE >> 1) + 1];
98 float * dBfs;
99 double pixels_per_sec, block_norm, max;
100 int dB_range, gain, style, read, end, end_min, last_end;92 int dB_range, gain, style, read, end, end_min, last_end;
93 int spectrum_points, perm;
101 char const * out_name, * title, * comment;94 char const * out_name, * title, * comment;
102 sox_bool slack_overlap, no_axes;95 sox_bool high_colour, slack_overlap, no_axes, truncated;
103 win_type_t win_type;96 win_type_t win_type;
97 double buf[MAX_DFT_SIZE], dft_buf[MAX_DFT_SIZE], window[MAX_DFT_SIZE];
98 double pixels_per_sec, block_norm, max, magnitudes[(MAX_DFT_SIZE>>1) + 1];
99 int bit_rev_table[100]; /* For Ooura fft */
100 double sin_cos_table[dft_sc_len(MAX_DFT_SIZE)]; /* ditto */
101 float * dBfs;
104} priv_t;102} priv_t;
105 103
106#define GETOPT_NUMERIC(ch, name, min, max) case ch:{ \104#define GETOPT_NUMERIC(ch, name, min, max) case ch:{ \
@@ -142,13 +140,16 @@ static int getopts(sox_effect_t * effp, int argc, char **argv) · 35 unmodified lines
142 140
143 --argv, ++argc, optind = 1, opterr = 0; /* re-jig for getopt */141 --argv, ++argc, optind = 1, opterr = 0; /* re-jig for getopt */
144 p->pixels_per_sec = 100, p->y_size = 2, p->dB_range = 120;/* non-0 defaults */142 p->pixels_per_sec = 100, p->y_size = 2, p->dB_range = 120;/* non-0 defaults */
143 p->spectrum_points = 249, p->perm = 1;
145 p->style = 1, p->out_name = "spectrogram.png", p->comment = "Created by SoX";144 p->style = 1, p->out_name = "spectrogram.png", p->comment = "Created by SoX";
146 145
147 while ((c = getopt(argc, argv, "+x:y:z:g:w:st:c:amlo:")) != -1) switch (c) {146 while ((c = getopt(argc, argv, "+x:y:z:Z:q:p:w:st:c:amlho:")) != -1) switch (c) {
148 GETOPT_NUMERIC('x', pixels_per_sec, 1 , 5000)147 GETOPT_NUMERIC('x', pixels_per_sec, 1 , 5000)
149 GETOPT_NUMERIC('y', y_size , 1 , 1 + MAX_DFT_SIZE_SHIFT)148 GETOPT_NUMERIC('y', y_size , 1 , 1 + MAX_DFT_SIZE_SHIFT)
150 GETOPT_NUMERIC('z', dB_range , 40 , 180)149 GETOPT_NUMERIC('z', dB_range , 20 , 180)
151 GETOPT_NUMERIC('g', gain ,-100, 100)150 GETOPT_NUMERIC('Z', gain ,-100, 100)
151 GETOPT_NUMERIC('q', spectrum_points, 0 , p->spectrum_points)
152 GETOPT_NUMERIC('p', perm , 1 , 6)
152 case 'w': p->win_type = enum_option(c, window_options); break;153 case 'w': p->win_type = enum_option(c, window_options); break;
153 case 's': p->slack_overlap = sox_true; break;154 case 's': p->slack_overlap = sox_true; break;
154 case 't': p->title = optarg; break;155 case 't': p->title = optarg; break;
@@ -156,11 +157,14 @@ static int getopts(sox_effect_t * effp, int argc, char **argv) · 1 unmodified line
156 case 'a': p->no_axes = sox_true; break;157 case 'a': p->no_axes = sox_true; break;
157 case 'm': monochrome = sox_true; break;158 case 'm': monochrome = sox_true; break;
158 case 'l': light_background = sox_true; break;159 case 'l': light_background = sox_true; break;
160 case 'h': p->high_colour = sox_true; break;
159 case 'o': p->out_name = optarg; break;161 case 'o': p->out_name = optarg; break;
160 default: sox_fail("unknown option `-%c'", optopt); return lsx_usage(effp);162 default: sox_fail("unknown option `-%c'", optopt); return lsx_usage(effp);
161 }163 }
162 p->y_size -= 1;164 p->gain = - p->gain;
165 --p->y_size, --p->perm;
163 p->style = 2 * monochrome + light_background;166 p->style = 2 * monochrome + light_background;
167 p->spectrum_points += 2;
164 argc -= optind, optind = callers_optind, opterr = callers_opterr;168 argc -= optind, optind = callers_optind, opterr = callers_opterr;
165 return argc || p->win_type == INT_MAX? lsx_usage(effp) : SOX_SUCCESS;169 return argc || p->win_type == INT_MAX? lsx_usage(effp) : SOX_SUCCESS;
166}170}
@@ -219,6 +223,7 @@ static int do_column(sox_effect_t * effp) · 52 unmodified lines
219 223
220 if (p->cols == MAX_COLS) {224 if (p->cols == MAX_COLS) {
221 sox_warn("PNG truncated at %g seconds", (double)MAX_COLS * p->step_size * p->block_steps / effp->in_signal.rate);225 sox_warn("PNG truncated at %g seconds", (double)MAX_COLS * p->step_size * p->block_steps / effp->in_signal.rate);
226 p->truncated = sox_true;
222 return SOX_EOF;227 return SOX_EOF;
223 }228 }
224 ++p->cols;229 ++p->cols;
@@ -275,7 +280,7 @@ static int drain(sox_effect_t * effp, sox_sample_t * obuf_, sox_size_t * osamp) · 50 unmodified lines
275{280{
276 priv_t * p = (priv_t *)effp->priv;281 priv_t * p = (priv_t *)effp->priv;
277 282
278 if (p->cols != MAX_COLS) {283 if (!p->truncated) {
279 sox_sample_t * ibuf = calloc(p->dft_size, sizeof(*ibuf));284 sox_sample_t * ibuf = calloc(p->dft_size, sizeof(*ibuf));
280 sox_sample_t * obuf = calloc(p->dft_size, sizeof(*obuf));285 sox_sample_t * obuf = calloc(p->dft_size, sizeof(*obuf));
281 sox_size_t isamp = (p->dft_size - p->step_size) / 2;286 sox_size_t isamp = (p->dft_size - p->step_size) / 2;
@@ -298,52 +303,72 @@ static int drain(sox_effect_t * effp, sox_sample_t * obuf_, sox_size_t * osamp) · 16 unmodified lines
298 return SOX_SUCCESS;303 return SOX_SUCCESS;
299}304}
300 305
301#define spectrum_points (1 + 250)306enum {Background, Text, Labels, Grid, fixed_palette};
302enum {Background = spectrum_points, Text, Labels, Grid, palette_size};
303 307
304static unsigned colour(double x, int range)308static unsigned colour(priv_t const * p, double x)
305{309{
306 x = range_limit(x, -range, 0);310 unsigned c = x < -p->dB_range? 0 : x >= 0? p->spectrum_points - 1 :
307 return (1 + x / range) * (spectrum_points - 1) + .5;311 1 + (1 + x / p->dB_range) * (p->spectrum_points - 2);
312 return fixed_palette + c;
308}313}
309 314
310static void make_palette(png_byte * palette, int style)315static void make_palette(priv_t const * p, png_color * palette)
311{316{
312 int i, j, at;317 int i;
313 for (i = j = 0; j < spectrum_points; i += 3, ++j) {318
314 double r, g, b, x = (double)j / (spectrum_points - 1);319 if (p->style & 1) {
315 if (style > 1)320 memcpy(palette++, (p->style & 2)? "\337\337\337":"\335\330\320", 3);
316 palette[i+2] = palette[i+1] = palette[i+0] = style == 2? j : 255 - j;321 memcpy(palette++, "\0\0\0" , 3);
317 else {322 memcpy(palette++, "\077\077\077", 3);
318 at = style? (spectrum_points - 1) * 3 - i : i;323 memcpy(palette++, "\077\077\077", 3);
319 if (x < .16) r = 0;324 } else {
320 else if (x < .72) r = .99 * sin((x - .16) / .56 * M_PI / 2);325 memcpy(palette++, "\0\0\0" , 3);
321 else r = .01 * (x - .72) / .28 + .99;326 memcpy(palette++, "\377\377\377", 3);
322 if (x < .52) g = 0;327 memcpy(palette++, "\277\277\277", 3);
323 else if (x < .86) g = .99 * .5 * (1 - cos((x - .52) / .34 * M_PI));328 memcpy(palette++, "\177\177\177", 3);
324 else g = .01 * (x - .86) / .14 + .99;
325 if (x < .34) b = .5 * sin((x - .00) / .34 * M_PI / 2);
326 else if (x < .63) b = .5 * .5 * (1 + cos((x - .34) / .29 * M_PI));
327 else b = (1 - cos((x - .63) / .37 * M_PI / 2));
328 palette[at + 0] = r * 255 + .5;
329 palette[at + 1] = g * 255 + .5;
330 palette[at + 2] = b * 255 + .5;
331 }
332 }329 }
333 i = spectrum_points * 3;330 for (i = 0; i < p->spectrum_points; ++i) {
334 switch (style) {331 double c[3] , x = (double)i / (p->spectrum_points - 1);
335 case 0: case 2:332 int at = (p->style & 1)? p->spectrum_points - 1 - i : i;
336 memcpy(palette + i, "\0\0\0" , 3), i+= 3;333 if (p->style > 1) {
337 memcpy(palette + i, "\377\377\377", 3), i+= 3;334 c[2] = c[1] = c[0] = x;
338 memcpy(palette + i, "\277\277\277", 3), i+= 3;335 if (p->high_colour) {
339 memcpy(palette + i, "\177\177\177", 3), i+= 3;336 c[(1 + p->perm) % 3] = x < .4? 0 : 5 / 3. * (x - .4);
340 return;337 if (p->perm < 3)
341 case 1: memcpy(palette + i, "\335\330\320", 3), i+= 3; break;338 c[(2 + p->perm) % 3] = x < .4? 0 : 5 / 3. * (x - .4);
342 default: memcpy(palette + i, "\337\337\337", 3), i+= 3;339 }
340 palette[at].red = .5 + 255 * c[0];
341 palette[at].green= .5 + 255 * c[1];
342 palette[at].blue = .5 + 255 * c[2];
343 continue;
344 }
345 if (p->high_colour) {
346 static const int states[3][7] = {
347 {4,5,0,0,2,1,1}, {0,0,2,1,1,3,2}, {4,1,1,3,0,0,2}};
348 int j, phase_num = min(6, x * 7);
349 for (j = 0; j < 3; ++j) switch (states[j][phase_num]) {
350 case 0: c[j] = 0; break;
351 case 1: c[j] = 1; break;
352 case 2: c[j] = sin((7 * x - phase_num) * M_PI / 2); break;
353 case 3: c[j] = cos((7 * x - phase_num) * M_PI / 2); break;
354 case 4: c[j] = 7 * x - phase_num; break;
355 case 5: c[j] = 1 - (7 * x - phase_num); break;
356 }
357 } else {
358 if (x < .16) c[0] = 0;
359 else if (x < .72) c[0] = .99 * sin((x - .16) / .56 * M_PI / 2);
360 else c[0] = .01 * (x - .72) / .28 + .99;
361 if (x < .52) c[1] = 0;
362 else if (x < .86) c[1] = .99 * .5 * (1 - cos((x - .52) / .34 * M_PI));
363 else c[1] = .01 * (x - .86) / .14 + .99;
364 if (x < .34) c[2] = .5 * sin((x - .00) / .34 * M_PI / 2);
365 else if (x < .63) c[2] = .5 * .5 * (1 + cos((x - .34) / .29 * M_PI));
366 else c[2] = (1 - cos((x - .63) / .37 * M_PI / 2));
367 }
368 palette[at].red = .5 + 255 * c[p->perm % 3];
369 palette[at].green= .5 + 255 * c[(1 + p->perm + (p->perm % 2)) % 3];
370 palette[at].blue = .5 + 255 * c[(2 + p->perm - (p->perm % 2)) % 3];
343 }371 }
344 memcpy(palette + i, "\0\0\0" , 3), i+= 3;
345 memcpy(palette + i, "\077\077\077", 3), i+= 3;
346 memcpy(palette + i, "\077\077\077", 3), i+= 3;
347}372}
348 373
349static const Bytef fixed[] =374static const Bytef fixed[] =
@@ -405,7 +430,7 @@ static int axis(double to, int max_steps, double * limit, char * * prefix) · 55 unmodified lines
405 scale = pow(10., -3. * prefix_num);430 scale = pow(10., -3. * prefix_num);
406 }431 }
407 *prefix = "pnum-kMGTPE" + prefix_num + (prefix_num? 4 : 11);432 *prefix = "pnum-kMGTPE" + prefix_num + (prefix_num? 4 : 11);
408 *limit = to * scale + .5;433 *limit = to * scale;
409 return step * scale + .5;434 return step * scale + .5;
410}435}
411 436
@@ -427,7 +452,7 @@ static int stop(sox_effect_t * effp) · 15 unmodified lines
427 png_bytepp png_rows = malloc(rows * sizeof(*png_rows));452 png_bytepp png_rows = malloc(rows * sizeof(*png_rows));
428 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0,0);453 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0,0);
429 png_infop png_info = png_create_info_struct(png);454 png_infop png_info = png_create_info_struct(png);
430 png_byte palette[palette_size * 3];455 png_color palette[256];
431 int i, j, step, tick_len = 2 + do_frame;456 int i, j, step, tick_len = 2 + do_frame;
432 char text[200], * prefix;457 char text[200], * prefix;
433 double limit;458 double limit;
@@ -443,10 +468,10 @@ static int stop(sox_effect_t * effp) · 9 unmodified lines
443 sox_debug("signal-max=%g", p->max);468 sox_debug("signal-max=%g", p->max);
444 font = malloc(font_len);469 font = malloc(font_len);
445 assert(uncompress(font, &font_len, fixed, sizeof(fixed)-1) == Z_OK);470 assert(uncompress(font, &font_len, fixed, sizeof(fixed)-1) == Z_OK);
446 make_palette(palette, p->style);471 make_palette(p, palette);
447 memset(pixels, Background, cols * rows * sizeof(*pixels));472 memset(pixels, Background, cols * rows * sizeof(*pixels));
448 png_init_io(png, file);473 png_init_io(png, file);
449 png_set_PLTE(png, png_info, (png_colorp)palette, palette_size);474 png_set_PLTE(png, png_info, palette, fixed_palette + p->spectrum_points);
450 png_set_IHDR(png, png_info, (size_t)cols, (size_t)rows, 8,475 png_set_IHDR(png, png_info, (size_t)cols, (size_t)rows, 8,
451 PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,476 PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
452 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);477 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
@@ -461,7 +486,7 @@ static int stop(sox_effect_t * effp) · 8 unmodified lines
461 /* Spectrogram */486 /* Spectrogram */
462 for (j = 0; j < p->rows; ++j) {487 for (j = 0; j < p->rows; ++j) {
463 for (i = 0; i < p->cols; ++i)488 for (i = 0; i < p->cols; ++i)
464 pixel(left + i, below + j) = colour(p->dBfs[i*p->rows + j], p->dB_range);489 pixel(left + i, below + j) = colour(p, p->dBfs[i*p->rows + j]);
465 if (do_frame) /* Frame verticals */490 if (do_frame) /* Frame verticals */
466 pixel(left - 1, below + j) = pixel(left + p->cols,below + j) = Grid;491 pixel(left - 1, below + j) = pixel(left + p->cols,below + j) = Grid;
467 }492 }
@@ -503,7 +528,7 @@ static int stop(sox_effect_t * effp) · 35 unmodified lines
503 /* Z-axis */528 /* Z-axis */
504 print_at(cols - right - 2 - font_X, below - 13, Text, "dBFS");/* Axis label */529 print_at(cols - right - 2 - font_X, below - 13, Text, "dBFS");/* Axis label */
505 for (j = 0; j < p->rows; ++j) { /* Spectrum */530 for (j = 0; j < p->rows; ++j) { /* Spectrum */
506 png_byte b = colour(p->dB_range * (j / (p->rows - 1.) - 1), p->dB_range);531 png_byte b = colour(p, p->dB_range * (j / (p->rows - 1.) - 1));
507 for (i = 0; i < spectrum_width; ++i)532 for (i = 0; i < spectrum_width; ++i)
508 pixel(cols - right - 1 - i, below + j) = b;533 pixel(cols - right - 1 - i, below + j) = b;
509 }534 }
@@ -528,18 +553,21 @@ sox_effect_handler_t const * sox_spectrogram_effect_fn(void) · 18 unmodified lines
528{553{
529 static sox_effect_handler_t handler = {554 static sox_effect_handler_t handler = {
530 "spectrogram", "[options]\n"555 "spectrogram", "[options]\n"
531 " -x num X-axis pixels/second, default 100\n"556 "\t-x num\tX-axis pixels/second, default 100\n"
532 " -y num Y-axis resolution (1 - 4), default 2\n"557 "\t-y num\tY-axis resolution (1 - 4), default 2\n"
533 " -z num Z-axis (colour) range in dB, default 120\n"558 "\t-z num\tZ-axis range in dB, default 120\n"
534 " -g num Apply num dB gain; to shift Z-axis\n"559 "\t-Z num\tZ-axis maximum in dBFS, default 0\n"
535 " -w name Window: Hann (default), Hamming, Bartlett, Rectangular, Kaiser\n"560 "\t-q num\tZ-axis quantisation, default 249\n"
536 " -s Slack overlap\n"561 "\t-w name\tWindow: Hann (default), Hamming, Bartlett, Rectangular, Kaiser\n"
537 " -a Suppress axes' lines\n"562 "\t-s\tSlack overlap\n"
538 " -t text Title text\n"563 "\t-a\tSuppress axis lines\n"
539 " -c text Comment text\n"564 "\t-l\tLight background\n"
540 " -l Light background\n"565 "\t-m\tMonochrome\n"
541 " -m Monochrome display\n"566 "\t-h\tHigh colour\n"
542 " -o text output file name, default `spectrogram.png'\n",567 "\t-p num\tPermute colours\n"
568 "\t-t text\tTitle text\n"
569 "\t-c text\tComment text\n"
570 "\t-o text\toutput file name, default `spectrogram.png'\n",
543 0, getopts, start, flow, drain, stop, NULL, sizeof(priv_t)};571 0, getopts, start, flow, drain, stop, NULL, sizeof(priv_t)};
544 return &handler;572 return &handler;
545}573}

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