| @@ -43,7 +43,7 @@ typedef struct { · 42 unmodified lines |
| 43 | double pixels_per_sec, duration, start_time, window_adjust; | 43 | double pixels_per_sec, duration, start_time, window_adjust; |
| 44 | int x_size0, y_size, Y_size, dB_range, gain, spectrum_points, perm; | 44 | int x_size0, y_size, Y_size, dB_range, gain, spectrum_points, perm; |
| 45 | sox_bool monochrome, light_background, high_colour, slack_overlap, no_axes; | 45 | sox_bool monochrome, light_background, high_colour, slack_overlap, no_axes; |
| 46 | sox_bool alt_palette, truncate; | 46 | sox_bool raw, alt_palette, truncate; |
| 47 | win_type_t win_type; | 47 | win_type_t win_type; |
| 48 | char const * out_name, * title, * comment; | 48 | char const * out_name, * title, * comment; |
| 49 | | 49 | |
| @@ -95,7 +95,7 @@ static int getopts(sox_effect_t * effp, int argc, char **argv) · 45 unmodified lines |
| 95 | p->dB_range = 120, p->spectrum_points = 249, p->perm = 1; /* Non-0 defaults */ | 95 | p->dB_range = 120, p->spectrum_points = 249, p->perm = 1; /* Non-0 defaults */ |
| 96 | p->out_name = "spectrogram.png", p->comment = "Created by SoX"; | 96 | p->out_name = "spectrogram.png", p->comment = "Created by SoX"; |
| 97 | | 97 | |
| 98 | while ((c = lsx_getopt(argc, argv, "+S:d:x:X:y:Y:z:Z:q:p:W:w:st:c:AamlhTo:")) != -1) switch (c) { | 98 | while ((c = lsx_getopt(argc, argv, "+S:d:x:X:y:Y:z:Z:q:p:W:w:st:c:AarmlhTo:")) != -1) switch (c) { |
| 99 | GETOPT_NUMERIC('x', x_size0 , 100, 5000) | 99 | GETOPT_NUMERIC('x', x_size0 , 100, 5000) |
| 100 | GETOPT_NUMERIC('X', pixels_per_sec, 1 , 5000) | 100 | GETOPT_NUMERIC('X', pixels_per_sec, 1 , 5000) |
| 101 | GETOPT_NUMERIC('y', y_size , 64 , 1200) | 101 | GETOPT_NUMERIC('y', y_size , 64 , 1200) |
| @@ -109,6 +109,7 @@ static int getopts(sox_effect_t * effp, int argc, char **argv) · 7 unmodified lines |
| 109 | case 's': p->slack_overlap = sox_true; break; | 109 | case 's': p->slack_overlap = sox_true; break; |
| 110 | case 'A': p->alt_palette = sox_true; break; | 110 | case 'A': p->alt_palette = sox_true; break; |
| 111 | case 'a': p->no_axes = sox_true; break; | 111 | case 'a': p->no_axes = sox_true; break; |
| | 112 | case 'r': p->raw = sox_true; break; |
| 112 | case 'm': p->monochrome = sox_true; break; | 113 | case 'm': p->monochrome = sox_true; break; |
| 113 | case 'l': p->light_background = sox_true; break; | 114 | case 'l': p->light_background = sox_true; break; |
| 114 | case 'h': p->high_colour = sox_true; break; | 115 | case 'h': p->high_colour = sox_true; break; |
| @@ -489,8 +490,8 @@ static int stop(sox_effect_t * effp) · 374 unmodified lines |
| 489 | uLong font_len = 96 * font_y; | 490 | uLong font_len = 96 * font_y; |
| 490 | int chans = effp->in_signal.channels; | 491 | int chans = effp->in_signal.channels; |
| 491 | int c_rows = p->rows * chans + chans - 1; | 492 | int c_rows = p->rows * chans + chans - 1; |
| 492 | int rows = below + c_rows + 30 + 20 * !!p->title; | 493 | int rows = p->raw? c_rows : below + c_rows + 30 + 20 * !!p->title; |
| 493 | int cols = left + p->cols + between + spectrum_width + right; | 494 | int cols = p->raw? p->cols : left + p->cols + between + spectrum_width + right; |
| 494 | png_byte * pixels = lsx_malloc(cols * rows * sizeof(*pixels)); | 495 | png_byte * pixels = lsx_malloc(cols * rows * sizeof(*pixels)); |
| 495 | png_bytepp png_rows = lsx_malloc(rows * sizeof(*png_rows)); | 496 | png_bytepp png_rows = lsx_malloc(rows * sizeof(*png_rows)); |
| 496 | png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0,0); | 497 | png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0,0); |
| @@ -518,77 +519,79 @@ static int stop(sox_effect_t * effp) · 21 unmodified lines |
| 518 | for (j = 0; j < rows; ++j) /* Put (0,0) at bottom-left of PNG */ | 519 | for (j = 0; j < rows; ++j) /* Put (0,0) at bottom-left of PNG */ |
| 519 | png_rows[rows - 1 - j] = (png_bytep)(pixels + j * cols); | 520 | png_rows[rows - 1 - j] = (png_bytep)(pixels + j * cols); |
| 520 | | 521 | |
| 521 | if (p->title && (i = (int)strlen(p->title) * font_X) < cols + 1) /* Title */ | | |
| 522 | print_at((cols - i) / 2, rows - font_y, Text, p->title); | | |
| 523 | if ((int)strlen(p->comment) * font_X < cols + 1) /* Footer comment */ | | |
| 524 | print_at(1, font_y, Text, p->comment); | | |
| 525 | | | |
| 526 | /* Spectrogram */ | 522 | /* Spectrogram */ |
| 527 | for (k = 0; k < chans; ++k) { | 523 | for (k = 0; k < chans; ++k) { |
| 528 | priv_t * q = (priv_t *)(effp - effp->flow + k)->priv; | 524 | priv_t * q = (priv_t *)(effp - effp->flow + k)->priv; |
| 529 | base = below + (chans - 1 - k) * (p->rows + 1); | 525 | base = !p->raw * below + (chans - 1 - k) * (p->rows + 1); |
| 530 | for (j = 0; j < p->rows; ++j) { | 526 | for (j = 0; j < p->rows; ++j) { |
| 531 | for (i = 0; i < p->cols; ++i) | 527 | for (i = 0; i < p->cols; ++i) |
| 532 | pixel(left + i, base + j) = colour(p, q->dBfs[i*p->rows + j]); | 528 | pixel(!p->raw * left + i, base + j) = colour(p, q->dBfs[i*p->rows + j]); |
| 533 | if (!p->no_axes) /* Y-axis lines */ | 529 | if (!p->raw && !p->no_axes) /* Y-axis lines */ |
| 534 | pixel(left - 1, base + j) = pixel(left + p->cols, base + j) = Grid; | 530 | pixel(left - 1, base + j) = pixel(left + p->cols, base + j) = Grid; |
| 535 | } | 531 | } |
| 536 | if (!p->no_axes) for (i = -1; i <= p->cols; ++i) /* X-axis lines */ | 532 | if (!p->raw && !p->no_axes) for (i = -1; i <= p->cols; ++i) /* X-axis lines */ |
| 537 | pixel(left + i, base - 1) = pixel(left + i, base + p->rows) = Grid; | 533 | pixel(left + i, base - 1) = pixel(left + i, base + p->rows) = Grid; |
| 538 | } | 534 | } |
| 539 | | 535 | |
| 540 | /* X-axis */ | 536 | if (!p->raw) { |
| 541 | step = axis(secs(p->cols), p->cols / (font_X * 9 / 2), &limit, &prefix); | 537 | if (p->title && (i = (int)strlen(p->title) * font_X) < cols + 1) /* Title */ |
| 542 | sprintf(text, "Time (%.1ss)", prefix); /* Axis label */ | 538 | print_at((cols - i) / 2, rows - font_y, Text, p->title); |
| 543 | print_at(left + (p->cols - font_X * (int)strlen(text)) / 2, 24, Text, text); | | |
| 544 | for (i = 0; i <= limit; i += step) { | | |
| 545 | int y, x = limit? (double)i / limit * p->cols + .5 : 0; | | |
| 546 | for (y = 0; y < tick_len; ++y) /* Ticks */ | | |
| 547 | pixel(left-1+x, below-1-y) = pixel(left-1+x, below+c_rows+y) = Grid; | | |
| 548 | if (step == 5 && (i%10)) | | |
| 549 | continue; | | |
| 550 | sprintf(text, "%g", .1 * i); /* Tick labels */ | | |
| 551 | x = left + x - 3 * strlen(text); | | |
| 552 | print_at(x, below - 6, Labels, text); | | |
| 553 | print_at(x, below + c_rows + 14, Labels, text); | | |
| 554 | } | | |
| 555 | | 539 | |
| 556 | /* Y-axis */ | 540 | if ((int)strlen(p->comment) * font_X < cols + 1) /* Footer comment */ |
| 557 | step = axis(effp->in_signal.rate / 2, | 541 | print_at(1, font_y, Text, p->comment); |
| 558 | (p->rows - 1) / ((font_y * 3 + 1) >> 1), &limit, &prefix); | 542 | |
| 559 | sprintf(text, "Frequency (%.1sHz)", prefix); /* Axis label */ | 543 | /* X-axis */ |
| 560 | print_up(10, below + (c_rows - font_X * (int)strlen(text)) / 2, Text, text); | 544 | step = axis(secs(p->cols), p->cols / (font_X * 9 / 2), &limit, &prefix); |
| 561 | for (k = 0; k < chans; ++k) { | 545 | sprintf(text, "Time (%.1ss)", prefix); /* Axis label */ |
| 562 | base = below + k * (p->rows + 1); | 546 | print_at(left + (p->cols - font_X * (int)strlen(text)) / 2, 24, Text, text); |
| 563 | for (i = 0; i <= limit; i += step) { | 547 | for (i = 0; i <= limit; i += step) { |
| 564 | int x, y = limit? (double)i / limit * (p->rows - 1) + .5 : 0; | 548 | int y, x = limit? (double)i / limit * p->cols + .5 : 0; |
| 565 | for (x = 0; x < tick_len; ++x) /* Ticks */ | 549 | for (y = 0; y < tick_len; ++y) /* Ticks */ |
| 566 | pixel(left-1-x, base+y) = pixel(left+p->cols+x, base+y) = Grid; | 550 | pixel(left-1+x, below-1-y) = pixel(left-1+x, below+c_rows+y) = Grid; |
| 567 | if ((step == 5 && (i%10)) || (!i && k && chans > 1)) | 551 | if (step == 5 && (i%10)) |
| 568 | continue; | 552 | continue; |
| 569 | sprintf(text, i?"%5g":" DC", .1 * i); /* Tick labels */ | 553 | sprintf(text, "%g", .1 * i); /* Tick labels */ |
| 570 | print_at(left - 4 - font_X * 5, base + y + 5, Labels, text); | 554 | x = left + x - 3 * strlen(text); |
| 571 | sprintf(text, i?"%g":"DC", .1 * i); | 555 | print_at(x, below - 6, Labels, text); |
| 572 | print_at(left + p->cols + 6, base + y + 5, Labels, text); | 556 | print_at(x, below + c_rows + 14, Labels, text); |
| 573 | } | 557 | } |
| 574 | } | | |
| 575 | | 558 | |
| 576 | /* Z-axis */ | 559 | /* Y-axis */ |
| 577 | k = min(400, c_rows); | 560 | step = axis(effp->in_signal.rate / 2, |
| 578 | base = below + (c_rows - k) / 2; | 561 | (p->rows - 1) / ((font_y * 3 + 1) >> 1), &limit, &prefix); |
| 579 | print_at(cols - right - 2 - font_X, base - 13, Text, "dBFS");/* Axis label */ | 562 | sprintf(text, "Frequency (%.1sHz)", prefix); /* Axis label */ |
| 580 | for (j = 0; j < k; ++j) { /* Spectrum */ | 563 | print_up(10, below + (c_rows - font_X * (int)strlen(text)) / 2, Text, text); |
| 581 | png_byte b = colour(p, p->dB_range * (j / (k - 1.) - 1)); | 564 | for (k = 0; k < chans; ++k) { |
| 582 | for (i = 0; i < spectrum_width; ++i) | 565 | base = below + k * (p->rows + 1); |
| 583 | pixel(cols - right - 1 - i, base + j) = b; | 566 | for (i = 0; i <= limit; i += step) { |
| 584 | } | 567 | int x, y = limit? (double)i / limit * (p->rows - 1) + .5 : 0; |
| 585 | step = 10 * ceil(p->dB_range / 10. * (font_y + 2) / (k - 1)); | 568 | for (x = 0; x < tick_len; ++x) /* Ticks */ |
| 586 | for (i = 0; i <= p->dB_range; i += step) { /* (Tick) labels */ | 569 | pixel(left-1-x, base+y) = pixel(left+p->cols+x, base+y) = Grid; |
| 587 | int y = (double)i / p->dB_range * (k - 1) + .5; | 570 | if ((step == 5 && (i%10)) || (!i && k && chans > 1)) |
| 588 | sprintf(text, "%+i", i - p->gain - p->dB_range); | 571 | continue; |
| 589 | print_at(cols - right + 1, base + y + 5, Labels, text); | 572 | sprintf(text, i?"%5g":" DC", .1 * i); /* Tick labels */ |
| 590 | } | 573 | print_at(left - 4 - font_X * 5, base + y + 5, Labels, text); |
| | 574 | sprintf(text, i?"%g":"DC", .1 * i); |
| | 575 | print_at(left + p->cols + 6, base + y + 5, Labels, text); |
| | 576 | } |
| | 577 | } |
| 591 | | 578 | |
| | 579 | /* Z-axis */ |
| | 580 | k = min(400, c_rows); |
| | 581 | base = below + (c_rows - k) / 2; |
| | 582 | print_at(cols - right - 2 - font_X, base - 13, Text, "dBFS");/* Axis label */ |
| | 583 | for (j = 0; j < k; ++j) { /* Spectrum */ |
| | 584 | png_byte b = colour(p, p->dB_range * (j / (k - 1.) - 1)); |
| | 585 | for (i = 0; i < spectrum_width; ++i) |
| | 586 | pixel(cols - right - 1 - i, base + j) = b; |
| | 587 | } |
| | 588 | step = 10 * ceil(p->dB_range / 10. * (font_y + 2) / (k - 1)); |
| | 589 | for (i = 0; i <= p->dB_range; i += step) { /* (Tick) labels */ |
| | 590 | int y = (double)i / p->dB_range * (k - 1) + .5; |
| | 591 | sprintf(text, "%+i", i - p->gain - p->dB_range); |
| | 592 | print_at(cols - right + 1, base + y + 5, Labels, text); |
| | 593 | } |
| | 594 | } |
| 592 | free(font); | 595 | free(font); |
| 593 | png_set_rows(png, png_info, png_rows); | 596 | png_set_rows(png, png_info, png_rows); |
| 594 | png_write_png(png, png_info, PNG_TRANSFORM_IDENTITY, NULL); | 597 | png_write_png(png, png_info, PNG_TRANSFORM_IDENTITY, NULL); |
| @@ -619,6 +622,7 @@ sox_effect_handler_t const * lsx_spectrogram_effect_fn(void) · 24 unmodified lines |
| 619 | "\t-W num\tWindow adjust parameter (-10 - 10); applies only to Kaiser", | 622 | "\t-W num\tWindow adjust parameter (-10 - 10); applies only to Kaiser", |
| 620 | "\t-s\tSlack overlap of windows", | 623 | "\t-s\tSlack overlap of windows", |
| 621 | "\t-a\tSuppress axis lines", | 624 | "\t-a\tSuppress axis lines", |
| | 625 | "\t-r\tRaw spectrogram; no axes or legends", |
| 622 | "\t-l\tLight background", | 626 | "\t-l\tLight background", |
| 623 | "\t-m\tMonochrome", | 627 | "\t-m\tMonochrome", |
| 624 | "\t-h\tHigh colour", | 628 | "\t-h\tHigh colour", |