| @@ -99,7 +99,7 @@ typedef struct { · 98 unmodified lines |
| 99 | double pixels_per_sec, block_norm, max; | 99 | double pixels_per_sec, block_norm, max; |
| 100 | int dB_range, gain, style, read, end, end_min, last_end; | 100 | int dB_range, gain, style, read, end, end_min, last_end; |
| 101 | char const * out_name, * title, * comment; | 101 | char const * out_name, * title, * comment; |
| 102 | sox_bool no_axes; | 102 | sox_bool slack_overlap, no_axes; |
| 103 | win_type_t win_type; | 103 | win_type_t win_type; |
| 104 | } priv_t; | 104 | } priv_t; |
| 105 | | 105 | |
| @@ -136,6 +136,7 @@ static int getopts(sox_effect_t * effp, int argc, char **argv) · 30 unmodified lines |
| 136 | { | 136 | { |
| 137 | priv_t * p = (priv_t *)effp->priv; | 137 | priv_t * p = (priv_t *)effp->priv; |
| 138 | int c, callers_optind = optind, callers_opterr = opterr; | 138 | int c, callers_optind = optind, callers_opterr = opterr; |
| | 139 | sox_bool monochrome = sox_false, light_background = sox_false; |
| 139 | | 140 | |
| 140 | assert(array_length(p->bit_rev_table) >= (size_t)dft_br_len(MAX_DFT_SIZE)); | 141 | assert(array_length(p->bit_rev_table) >= (size_t)dft_br_len(MAX_DFT_SIZE)); |
| 141 | | 142 | |
| @@ -143,25 +144,28 @@ static int getopts(sox_effect_t * effp, int argc, char **argv) · 1 unmodified line |
| 143 | p->pixels_per_sec = 100, p->y_size = 2, p->dB_range = 120;/* non-0 defaults */ | 144 | p->pixels_per_sec = 100, p->y_size = 2, p->dB_range = 120;/* non-0 defaults */ |
| 144 | p->style = 1, p->out_name = "spectrogram.png", p->comment = "Created by SoX"; | 145 | p->style = 1, p->out_name = "spectrogram.png", p->comment = "Created by SoX"; |
| 145 | | 146 | |
| 146 | while ((c = getopt(argc, argv, "+x:y:z:g:s:w:t:c:o:a")) != -1) switch (c) { | 147 | while ((c = getopt(argc, argv, "+x:y:z:g:w:st:c:amlo:")) != -1) switch (c) { |
| 147 | GETOPT_NUMERIC('x', pixels_per_sec, 1 , 5000) | 148 | GETOPT_NUMERIC('x', pixels_per_sec, 1 , 5000) |
| 148 | GETOPT_NUMERIC('y', y_size , 1 , 1 + MAX_DFT_SIZE_SHIFT) | 149 | GETOPT_NUMERIC('y', y_size , 1 , 1 + MAX_DFT_SIZE_SHIFT) |
| 149 | GETOPT_NUMERIC('z', dB_range , 40 , 180) | 150 | GETOPT_NUMERIC('z', dB_range , 40 , 180) |
| 150 | GETOPT_NUMERIC('g', gain ,-100, 100) | 151 | GETOPT_NUMERIC('g', gain ,-100, 100) |
| 151 | GETOPT_NUMERIC('s', style , 1 , 4) | | |
| 152 | case 'w': p->win_type = enum_option(c, window_options); break; | 152 | case 'w': p->win_type = enum_option(c, window_options); break; |
| | 153 | case 's': p->slack_overlap = sox_true; break; |
| 153 | case 't': p->title = optarg; break; | 154 | case 't': p->title = optarg; break; |
| 154 | case 'c': p->comment = optarg; break; | 155 | case 'c': p->comment = optarg; break; |
| 155 | case 'o': p->out_name = optarg; break; | | |
| 156 | case 'a': p->no_axes = sox_true; break; | 156 | case 'a': p->no_axes = sox_true; break; |
| | 157 | case 'm': monochrome = sox_true; break; |
| | 158 | case 'l': light_background = sox_true; break; |
| | 159 | case 'o': p->out_name = optarg; break; |
| 157 | default: sox_fail("unknown option `-%c'", optopt); return lsx_usage(effp); | 160 | default: sox_fail("unknown option `-%c'", optopt); return lsx_usage(effp); |
| 158 | } | 161 | } |
| 159 | p->y_size -= 1, p->style -= 1; | 162 | p->y_size -= 1; |
| | 163 | p->style = 2 * monochrome + light_background; |
| 160 | argc -= optind, optind = callers_optind, opterr = callers_opterr; | 164 | argc -= optind, optind = callers_optind, opterr = callers_opterr; |
| 161 | return argc || p->win_type == INT_MAX? lsx_usage(effp) : SOX_SUCCESS; | 165 | return argc || p->win_type == INT_MAX? lsx_usage(effp) : SOX_SUCCESS; |
| 162 | } | 166 | } |
| 163 | | 167 | |
| 164 | static int make_window(priv_t * p, int end) | 168 | static double make_window(priv_t * p, int end) |
| 165 | { | 169 | { |
| 166 | double sum = 0, * w = end < 0? p->window : p->window + end; | 170 | double sum = 0, * w = end < 0? p->window : p->window + end; |
| 167 | int i, n = p->dft_size - abs(end); | 171 | int i, n = p->dft_size - abs(end); |
| @@ -178,7 +182,7 @@ static int make_window(priv_t * p, int end) · 10 unmodified lines |
| 178 | for (i = 0; i < p->dft_size; ++i) sum += p->window[i]; | 182 | for (i = 0; i < p->dft_size; ++i) sum += p->window[i]; |
| 179 | for (i = 0; i < p->dft_size; ++i) p->window[i] *= 2 / sum | 183 | for (i = 0; i < p->dft_size; ++i) p->window[i] *= 2 / sum |
| 180 | * sqr((double)n / p->dft_size); /* empirical small window adjustment */ | 184 | * sqr((double)n / p->dft_size); /* empirical small window adjustment */ |
| 181 | return sum + .5; | 185 | return sum; |
| 182 | } | 186 | } |
| 183 | | 187 | |
| 184 | static int start(sox_effect_t * effp) | 188 | static int start(sox_effect_t * effp) |
| @@ -192,7 +196,9 @@ static int start(sox_effect_t * effp) · 7 unmodified lines |
| 192 | } | 196 | } |
| 193 | p->end = p->dft_size = DFT_BASE_SIZE << p->y_size; | 197 | p->end = p->dft_size = DFT_BASE_SIZE << p->y_size; |
| 194 | p->rows = (p->dft_size >> 1) + 1; | 198 | p->rows = (p->dft_size >> 1) + 1; |
| 195 | p->step_size = make_window(p, p->last_end = 0); | 199 | actual = make_window(p, p->last_end = 0); |
| | 200 | sox_debug("window_density=%g", actual / p->dft_size); |
| | 201 | p->step_size = (p->slack_overlap? sqrt(actual * p->dft_size) : actual) + .5; |
| 196 | p->block_steps = effp->in_signal.rate / p->pixels_per_sec; | 202 | p->block_steps = effp->in_signal.rate / p->pixels_per_sec; |
| 197 | p->step_size = p->block_steps / ceil((double)p->block_steps / p->step_size) +.5; | 203 | p->step_size = p->block_steps / ceil((double)p->block_steps / p->step_size) +.5; |
| 198 | p->block_steps = floor((double)p->block_steps / p->step_size +.5); | 204 | p->block_steps = floor((double)p->block_steps / p->step_size +.5); |
| @@ -370,7 +376,7 @@ static unsigned char * font; · 171 unmodified lines |
| 370 | static void print_at_(png_byte * pixels, int cols, int x, int y, int c, char const * text, int orientation) | 376 | static void print_at_(png_byte * pixels, int cols, int x, int y, int c, char const * text, int orientation) |
| 371 | { | 377 | { |
| 372 | for (;*text; ++text) { | 378 | for (;*text; ++text) { |
| 373 | int pos = (*text - ' ') * font_y; | 379 | int pos = ((*text < ' ' || *text > '~'? '~' + 1 : *text) - ' ') * font_y; |
| 374 | int i, j; | 380 | int i, j; |
| 375 | for (i = 0; i < font_y; ++i) { | 381 | for (i = 0; i < font_y; ++i) { |
| 376 | unsigned line = font[pos++]; | 382 | unsigned line = font[pos++]; |
| @@ -387,7 +393,7 @@ static void print_at_(png_byte * pixels, int cols, int x, int y, int c, char con · 10 unmodified lines |
| 387 | } | 393 | } |
| 388 | } | 394 | } |
| 389 | | 395 | |
| 390 | static int axis(double to, int max_steps, int * limit, char * * prefix) | 396 | static int axis(double to, int max_steps, double * limit, char * * prefix) |
| 391 | { | 397 | { |
| 392 | double scale = 1, step = 1; | 398 | double scale = 1, step = 1; |
| 393 | int i, prefix_num = 0; | 399 | int i, prefix_num = 0; |
| @@ -422,8 +428,9 @@ static int stop(sox_effect_t * effp) · 28 unmodified lines |
| 422 | png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0,0); | 428 | png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0,0); |
| 423 | png_infop png_info = png_create_info_struct(png); | 429 | png_infop png_info = png_create_info_struct(png); |
| 424 | png_byte palette[palette_size * 3]; | 430 | png_byte palette[palette_size * 3]; |
| 425 | int i, j, step, limit, tick_len = 2 + do_frame; | 431 | int i, j, step, tick_len = 2 + do_frame; |
| 426 | char text[200], * prefix; | 432 | char text[200], * prefix; |
| | 433 | double limit; |
| 427 | | 434 | |
| 428 | if (!file) { | 435 | if (!file) { |
| 429 | sox_fail("failed to create `%s' :(", p->out_name); | 436 | sox_fail("failed to create `%s' :(", p->out_name); |
| @@ -526,11 +533,13 @@ sox_effect_handler_t const * sox_spectrogram_effect_fn(void) · 96 unmodified lines |
| 526 | " -z num Z-axis (colour) range in dB, default 120\n" | 533 | " -z num Z-axis (colour) range in dB, default 120\n" |
| 527 | " -g num Apply num dB gain; to shift Z-axis\n" | 534 | " -g num Apply num dB gain; to shift Z-axis\n" |
| 528 | " -w name Window: Hann (default), Hamming, Bartlett, Rectangular, Kaiser\n" | 535 | " -w name Window: Hann (default), Hamming, Bartlett, Rectangular, Kaiser\n" |
| 529 | " -s num Display style (1 - 4), default 1\n" | 536 | " -s Slack overlap\n" |
| 530 | " -a Toggle the presence of the axes' lines\n" | 537 | " -a Suppress axes' lines\n" |
| 531 | " -t text Title text\n" | 538 | " -t text Title text\n" |
| 532 | " -c text Comment text\n" | 539 | " -c text Comment text\n" |
| 533 | " -o text Name of the spectrogram output file, default `spectrogram.png'\n", | 540 | " -l Light background\n" |
| | 541 | " -m Monochrome display\n" |
| | 542 | " -o text output file name, default `spectrogram.png'\n", |
| 534 | 0, getopts, start, flow, drain, stop, NULL, sizeof(priv_t)}; | 543 | 0, getopts, start, flow, drain, stop, NULL, sizeof(priv_t)}; |
| 535 | return &handler; | 544 | return &handler; |
| 536 | } | 545 | } |