History

spectrogram effect

robs committed 18 years ago

Commit 52f4fa01 parent 7cacd5310 files · +677 −7Browse files

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

Unified · Split

src/spectrogram.c+536−0
@@ -0,0 +1,536 @@
1/* libSoX effect: Spectrogram (c) 2008 robs@users.sourceforge.net
2 *
3 * This library is free software; you can redistribute it and/or modify it
4 * under the terms of the GNU Lesser General Public License as published by
5 * the Free Software Foundation; either version 2.1 of the License, or (at
6 * your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18#ifdef NDEBUG /* Enable assert always. */
19#undef NDEBUG /* Must undef above assert.h or other that might include it. */
20#endif
21
22#include "sox_i.h"
23#include "fft4g.h"
24#include "getopt.h"
25#include <assert.h>
26#include <math.h>
27#include <png.h>
28
29#define malloc lsx_malloc
30#define calloc lsx_calloc
31#define realloc lsx_realloc
32#define FROM_SOX SOX_SAMPLE_TO_FLOAT_64BIT
33#define DFT_BASE_SIZE 512
34#define MAX_DFT_SIZE_SHIFT 3
35#define MAX_DFT_SIZE (DFT_BASE_SIZE << MAX_DFT_SIZE_SHIFT)
36#define MAX_COLS 999 /* Also max seconds */
37
38static void apply_hann(double h[], const int num_points)
39{
40 int i, m = num_points - 1;
41 for (i = 0; i < num_points; ++i) {
42 double x = 2 * M_PI * i / m;
43 h[i] *= .5 - .5 * cos(x);
44 }
45}
46
47static void apply_hamming(double h[], const int num_points)
48{
49 int i, m = num_points - 1;
50 for (i = 0; i < num_points; ++i) {
51 double x = 2 * M_PI * i / m;
52 h[i] *= .53836 - .46164 * cos(x);
53 }
54}
55
56static void apply_bartlett(double h[], const int num_points)
57{
58 int i, m = num_points - 1;
59 for (i = 0; i < num_points; ++i) {
60 h[i] *= 2. / m * (m / 2. - fabs(i - m / 2.));
61 }
62}
63
64static double kaiser_beta(double att)
65{
66 if (att > 100 ) return .1117 * att - 1.11;
67 if (att > 50 ) return .1102 * (att - 8.7);
68 if (att > 20.96) return .58417 * pow(att -20.96, .4) + .07886 * (att - 20.96);
69 return 0;
70}
71
72static void apply_kaiser(double h[], const int num_points, double beta)
73{
74 int i, m = num_points - 1;
75 for (i = 0; i <= m; ++i) {
76 double x = 2. * i / m - 1;
77 h[i] *= bessel_I_0(beta * sqrt(1 - x * x)) / bessel_I_0(beta);
78 }
79}
80
81typedef enum {Window_Hann, Window_Hamming, Window_Bartlett, Window_Rectangular, Window_Kaiser} win_type_t;
82static enum_item const window_options[] = {
83 ENUM_ITEM(Window_,Hann)
84 ENUM_ITEM(Window_,Hamming)
85 ENUM_ITEM(Window_,Bartlett)
86 ENUM_ITEM(Window_,Rectangular)
87 ENUM_ITEM(Window_,Kaiser)
88 {0, 0}};
89
90typedef struct {
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;
101 char const * out_name, * title, * comment;
102 sox_bool no_axes;
103 win_type_t win_type;
104} priv_t;
105
106#define GETOPT_NUMERIC(ch, name, min, max) case ch:{ \
107 char * end_ptr; \
108 double d = strtod(optarg, &end_ptr); \
109 if (end_ptr == optarg || d < min || d > max || *end_ptr != '\0') {\
110 sox_fail("parameter `%s' must be between %g and %g", #name, (double)min, (double)max); \
111 return lsx_usage(effp); \
112 } \
113 p->name = d; \
114 break; \
115}
116
117static int enum_option(int c, enum_item const * items)
118{
119 enum_item const * p = find_enum_text(optarg, items);
120 if (p == NULL) {
121 unsigned len = 1;
122 char * set = lsx_malloc(len);
123 *set = 0;
124 for (p = items; p->text; ++p) {
125 set = lsx_realloc(set, len += 2 + strlen(p->text));
126 strcat(set, ", "); strcat(set, p->text);
127 }
128 sox_fail("-%c: '%s' is not one of: %s.", c, optarg, set + 2);
129 free(set);
130 return INT_MAX;
131 }
132 return p->value;
133}
134
135static int getopts(sox_effect_t * effp, int argc, char **argv)
136{
137 priv_t * p = (priv_t *)effp->priv;
138 int c, callers_optind = optind, callers_opterr = opterr;
139
140 assert(array_length(p->bit_rev_table) >= (size_t)dft_br_len(MAX_DFT_SIZE));
141
142 --argv, ++argc, optind = 1, opterr = 0; /* re-jig for getopt */
143 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
146 while ((c = getopt(argc, argv, "+x:y:z:g:s:w:t:c:o:a")) != -1) switch (c) {
147 GETOPT_NUMERIC('x', pixels_per_sec, 1 , 5000)
148 GETOPT_NUMERIC('y', y_size , 1 , 1 + MAX_DFT_SIZE_SHIFT)
149 GETOPT_NUMERIC('z', dB_range , 40 , 180)
150 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;
153 case 't': p->title = optarg; break;
154 case 'c': p->comment = optarg; break;
155 case 'o': p->out_name = optarg; break;
156 case 'a': p->no_axes = sox_true; break;
157 default: sox_fail("unknown option `-%c'", optopt); return lsx_usage(effp);
158 }
159 p->y_size -= 1, p->style -= 1;
160 argc -= optind, optind = callers_optind, opterr = callers_opterr;
161 return argc || p->win_type == INT_MAX? lsx_usage(effp) : SOX_SUCCESS;
162}
163
164static int make_window(priv_t * p, int end)
165{
166 double sum = 0, * w = end < 0? p->window : p->window + end;
167 int i, n = p->dft_size - abs(end);
168
169 if (end) memset(p->window, 0, sizeof(p->window));
170 for (i = 0; i < n; ++i) w[i] = 1;
171 switch (p->win_type) {
172 case Window_Hann: apply_hann(w, n); break;
173 case Window_Hamming: apply_hamming(w, n); break;
174 case Window_Bartlett: apply_bartlett(w, n); break;
175 case Window_Rectangular: break;
176 default: apply_kaiser(w, n, kaiser_beta(p->dB_range + 10.));
177 }
178 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
180 * sqr((double)n / p->dft_size); /* empirical small window adjustment */
181 return sum + .5;
182}
183
184static int start(sox_effect_t * effp)
185{
186 priv_t * p = (priv_t *)effp->priv;
187 double actual;
188
189 if (effp->in_signal.channels != 1) {
190 sox_fail("only 1 channel is supported");
191 return SOX_EOF;
192 }
193 p->end = p->dft_size = DFT_BASE_SIZE << p->y_size;
194 p->rows = (p->dft_size >> 1) + 1;
195 p->step_size = make_window(p, p->last_end = 0);
196 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;
198 p->block_steps = floor((double)p->block_steps / p->step_size +.5);
199 p->block_norm = 1. / p->block_steps;
200 actual = effp->in_signal.rate / p->step_size / p->block_steps;
201 if (actual != p->pixels_per_sec)
202 sox_report("actual pixels/s = %g", actual);
203 sox_debug("step_size=%i block_steps=%i", p->step_size, p->block_steps);
204 p->max = -p->dB_range;
205 p->read = (p->step_size - p->dft_size) / 2;
206 return SOX_SUCCESS;
207}
208
209static int do_column(sox_effect_t * effp)
210{
211 priv_t * p = (priv_t *)effp->priv;
212 int i;
213
214 if (p->cols == MAX_COLS) {
215 sox_warn("PNG truncated at %g seconds", (double)MAX_COLS * p->step_size * p->block_steps / effp->in_signal.rate);
216 return SOX_EOF;
217 }
218 ++p->cols;
219 p->dBfs = realloc(p->dBfs, p->cols * p->rows * sizeof(*p->dBfs));
220 for (i = 0; i < p->rows; ++i) {
221 double dBfs = 10 * log10(p->magnitudes[i] * p->block_norm);
222 p->dBfs[(p->cols - 1) * p->rows + i] = dBfs + p->gain;
223 if (dBfs > p->max)
224 p->max = dBfs;
225 }
226 memset(p->magnitudes, 0, p->rows * sizeof(*p->magnitudes));
227 p->block_num = 0;
228 return SOX_SUCCESS;
229}
230
231static int flow(sox_effect_t * effp,
232 const sox_sample_t * ibuf, sox_sample_t * obuf,
233 sox_size_t * isamp, sox_size_t * osamp)
234{
235 priv_t * p = (priv_t *)effp->priv;
236 sox_size_t len = min(*isamp, *osamp), dummy = 0; /* No need to clip count */
237 int i;
238
239 memcpy(obuf, ibuf, len * sizeof(*obuf)); /* Pass on audio unaffected */
240 *isamp = *osamp = len;
241
242 while (sox_true) {
243 if (p->read == p->step_size) {
244 memmove(p->buf, p->buf + p->step_size,
245 (p->dft_size - p->step_size) * sizeof(*p->buf));
246 p->read = 0;
247 }
248 for (; len && p->read < p->step_size; --len, ++p->read, --p->end)
249 p->buf[p->dft_size - p->step_size + p->read] = FROM_SOX(*ibuf++, dummy);
250 if (p->read != p->step_size)
251 break;
252
253 if ((p->end = max(p->end, p->end_min)) != p->last_end)
254 make_window(p, p->last_end = p->end);
255 for (i = 0; i < p->dft_size; ++i) p->dft_buf[i] = p->buf[i] * p->window[i];
256 rdft(p->dft_size, 1, p->dft_buf, p->bit_rev_table, p->sin_cos_table);
257 p->magnitudes[0] += sqr(p->dft_buf[0]);
258 for (i = 1; i < p->dft_size >> 1; ++i)
259 p->magnitudes[i] += sqr(p->dft_buf[2*i]) + sqr(p->dft_buf[2*i+1]);
260 p->magnitudes[p->dft_size >> 1] += sqr(p->dft_buf[1]);
261
262 if (++p->block_num == p->block_steps && do_column(effp) == SOX_EOF)
263 return SOX_EOF;
264 }
265 return SOX_SUCCESS;
266}
267
268static int drain(sox_effect_t * effp, sox_sample_t * obuf_, sox_size_t * osamp)
269{
270 priv_t * p = (priv_t *)effp->priv;
271
272 if (p->cols != MAX_COLS) {
273 sox_sample_t * ibuf = calloc(p->dft_size, sizeof(*ibuf));
274 sox_sample_t * obuf = calloc(p->dft_size, sizeof(*obuf));
275 sox_size_t isamp = (p->dft_size - p->step_size) / 2;
276 int left_over = (isamp + p->read) % p->step_size;
277
278 if (left_over >= p->step_size >> 1)
279 isamp += p->step_size - left_over;
280 sox_debug("cols=%i left=%i end=%i", p->cols, p->read, p->end);
281 p->end = 0;
282 p->end_min = -p->dft_size;
283 if (flow(effp, ibuf, obuf, &isamp, &isamp) == SOX_SUCCESS && p->block_num) {
284 p->block_norm *= (double)p->block_steps / p->block_num;
285 do_column(effp);
286 }
287 sox_debug("flushed cols=%i left=%i end=%i", p->cols, p->read, p->end);
288 free(obuf);
289 free(ibuf);
290 }
291 (void)obuf_, *osamp = 0;
292 return SOX_SUCCESS;
293}
294
295#define spectrum_points (1 + 250)
296enum {Background = spectrum_points, Text, Labels, Grid, palette_size};
297
298static unsigned colour(double x, int range)
299{
300 x = range_limit(x, -range, 0);
301 return (1 + x / range) * (spectrum_points - 1) + .5;
302}
303
304static void make_palette(png_byte * palette, int style)
305{
306 int i, j, at;
307 for (i = j = 0; j < spectrum_points; i += 3, ++j) {
308 double r, g, b, x = (double)j / (spectrum_points - 1);
309 if (style > 1)
310 palette[i+2] = palette[i+1] = palette[i+0] = style == 2? j : 255 - j;
311 else {
312 at = style? (spectrum_points - 1) * 3 - i : i;
313 if (x < .16) r = 0;
314 else if (x < .72) r = .99 * sin((x - .16) / .56 * M_PI / 2);
315 else r = .01 * (x - .72) / .28 + .99;
316 if (x < .52) g = 0;
317 else if (x < .86) g = .99 * .5 * (1 - cos((x - .52) / .34 * M_PI));
318 else g = .01 * (x - .86) / .14 + .99;
319 if (x < .34) b = .5 * sin((x - .00) / .34 * M_PI / 2);
320 else if (x < .63) b = .5 * .5 * (1 + cos((x - .34) / .29 * M_PI));
321 else b = (1 - cos((x - .63) / .37 * M_PI / 2));
322 palette[at + 0] = r * 255 + .5;
323 palette[at + 1] = g * 255 + .5;
324 palette[at + 2] = b * 255 + .5;
325 }
326 }
327 i = spectrum_points * 3;
328 switch (style) {
329 case 0: case 2:
330 memcpy(palette + i, "\0\0\0" , 3), i+= 3;
331 memcpy(palette + i, "\377\377\377", 3), i+= 3;
332 memcpy(palette + i, "\277\277\277", 3), i+= 3;
333 memcpy(palette + i, "\177\177\177", 3), i+= 3;
334 return;
335 case 1: memcpy(palette + i, "\335\330\320", 3), i+= 3; break;
336 default: memcpy(palette + i, "\337\337\337", 3), i+= 3;
337 }
338 memcpy(palette + i, "\0\0\0" , 3), i+= 3;
339 memcpy(palette + i, "\077\077\077", 3), i+= 3;
340 memcpy(palette + i, "\077\077\077", 3), i+= 3;
341}
342
343static const Bytef fixed[] =
344 "x\332eT\241\266\2450\fDVV>Y\371$re%2\237\200|2\22YY\211D\"+\337'<y\345\312"
345 "\375\fd\345f\222\224\313\236\235{\270\344L\247a\232\4\246\351\201d\230\222"
346 "\304D\364^ \352\362S\"m\347\311\237\237\27\64K\243\2302\265\35\v\371<\363y"
347 "\354_\226g\354\214)e \2458\341\17\20J4\215[z<\271\277\367\0\63\64@\177\330c"
348 "\227\204 Ir.\5$U\200\260\224\326S\17\200=\\k\20QA\334%\342\20*\303P\234\211"
349 "\366\36#\370R\276_\316s-\345\222Dlz\363my*;\217\373\346z\267\343\236\364\246"
350 "\236\365\2419\305p\333\267\23(\207\265\333\233\325Y\342\243\265\357\262\215"
351 "\263t\271$\276\226ea\271.\367&\320\347\202_\234\27\377\345\222\253?\3422\364"
352 "\207y\256\236\229\331\33\f\376\227\266\"\356\253j\366\363\347\334US\34]\371?"
353 "\255\371\336\372z\265v\34\226\247\32\324\217\334\337\317U4\16\316{N\370\31"
354 "\365\357iL\231y\33y\264\211D7\337\4\244\261\220D\346\1\261\357\355>\3\342"
355 "\223\363\0\303\277\f[\214A,p\34`\255\355\364\37\372\224\342\277\f\207\255\36"
356 "_V\7\34\241^\316W\257\177\b\242\300\34\f\276\33?/9_\331f\346\36\25Y)\2301"
357 "\257\2414|\35\365\237\3424k\3\244\3\242\261\6\b\275>z$\370\215:\270\363w\36/"
358 "\265kF\v\20o6\242\301\364\336\27\325\257\321\364fs\231\215G\32=\257\305Di"
359 "\304^\177\304R\364Q=\225\373\33\320\375\375\372\200\337\37\374}\334\337\20"
360 "\364\310(]\304\267b\177\326Yrj\312\277\373\233\37\340";
361static unsigned char * font;
362#define font_x 5
363#define font_y 12
364#define font_X (font_x + 1)
365
366#define pixel(x,y) pixels[(y) * cols + (x)]
367#define print_at(x,y,c,t) print_at_(pixels,cols,x,y,c,t,0)
368#define print_up(x,y,c,t) print_at_(pixels,cols,x,y,c,t,1)
369
370static void print_at_(png_byte * pixels, int cols, int x, int y, int c, char const * text, int orientation)
371{
372 for (;*text; ++text) {
373 int pos = (*text - ' ') * font_y;
374 int i, j;
375 for (i = 0; i < font_y; ++i) {
376 unsigned line = font[pos++];
377 for (j = 0; j < font_x; ++j, line <<= 1)
378 if (line & 0x80) switch (orientation) {
379 case 0: pixel(x + j, y - i) = c; break;
380 case 1: pixel(x + i, y + j) = c; break;
381 }
382 }
383 switch (orientation) {
384 case 0: x += font_X; break;
385 case 1: y += font_X; break;
386 }
387 }
388}
389
390static int axis(double to, int max_steps, int * limit, char * * prefix)
391{
392 double scale = 1, step = 1;
393 int i, prefix_num = 0;
394 if (max_steps) {
395 double try, log_10 = HUGE_VAL, min_step = (to *= 10) / max_steps;
396 for (i = 5; i; i >>= 1) if ((try = ceil(log10(min_step * i))) <= log_10)
397 step = pow(10., log_10 = try) / i, log_10 -= i > 1;
398 prefix_num = floor(log_10 / 3);
399 scale = pow(10., -3. * prefix_num);
400 }
401 *prefix = "pnum-kMGTPE" + prefix_num + (prefix_num? 4 : 11);
402 *limit = to * scale + .5;
403 return step * scale + .5;
404}
405
406#define below 48
407#define left 58
408#define between 37
409#define spectrum_width 14
410#define right 35
411
412static int stop(sox_effect_t * effp)
413{
414 priv_t * p = (priv_t *) effp->priv;
415 FILE * file = fopen(p->out_name, "w");
416 uLong font_len = 96 * font_y;
417 int rows = below + p->rows + 30 + 20 * !!p->title;
418 int cols = left + p->cols + between + spectrum_width + right;
419 sox_bool do_frame = !p->no_axes;
420 png_byte * pixels = malloc(cols * rows * sizeof(*pixels));
421 png_bytepp png_rows = malloc(rows * sizeof(*png_rows));
422 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0,0);
423 png_infop png_info = png_create_info_struct(png);
424 png_byte palette[palette_size * 3];
425 int i, j, step, limit, tick_len = 2 + do_frame;
426 char text[200], * prefix;
427
428 if (!file) {
429 sox_fail("failed to create `%s' :(", p->out_name);
430 png_destroy_write_struct(&png, &png_info);
431 free(png_rows);
432 free(pixels);
433 free(p->dBfs);
434 return SOX_EOF;
435 }
436 sox_debug("signal-max=%g", p->max);
437 font = malloc(font_len);
438 assert(uncompress(font, &font_len, fixed, sizeof(fixed)-1) == Z_OK);
439 make_palette(palette, p->style);
440 memset(pixels, Background, cols * rows * sizeof(*pixels));
441 png_init_io(png, file);
442 png_set_PLTE(png, png_info, (png_colorp)palette, palette_size);
443 png_set_IHDR(png, png_info, (size_t)cols, (size_t)rows, 8,
444 PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
445 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
446 for (j = 0; j < rows; ++j)
447 png_rows[rows - 1 - j] = (png_bytep)(pixels + j * cols);
448
449 if (p->title && (i = (int)strlen(p->title) * font_X) < cols + 1) /* Title */
450 print_at((cols - i) / 2, rows - font_y, Text, p->title);
451 if ((int)strlen(p->comment) * font_X < cols + 1) /* Footer comment */
452 print_at(1, font_y, Text, p->comment);
453
454 /* Spectrogram */
455 for (j = 0; j < p->rows; ++j) {
456 for (i = 0; i < p->cols; ++i)
457 pixel(left + i, below + j) = colour(p->dBfs[i*p->rows + j], p->dB_range);
458 if (do_frame) /* Frame verticals */
459 pixel(left - 1, below + j) = pixel(left + p->cols,below + j) = Grid;
460 }
461 if (do_frame) for (i = -1; i <= p->cols; ++i) /* Frame horizontals */
462 pixel(left + i, below - 1) = pixel(left + i, below + p->rows) = Grid;
463
464 /* X-axis */
465 step = axis((double)p->cols * p->step_size * p->block_steps / effp->in_signal.rate, p->cols / (font_X * 9 / 2), &limit, &prefix);
466 sprintf(text, "Time (%.1ss)", prefix); /* Axis label */
467 print_at(left + (p->cols - font_X * (int)strlen(text)) / 2, 24, Text, text);
468 for (i = 0; i <= limit; i += step) {
469 int y, x = limit? (double)i / limit * p->cols + .5 : 0;
470 for (y = 0; y < tick_len; ++y) /* Ticks */
471 pixel(left - 1 + x, below - 1 - y) = pixel(left - 1 + x, below + p->rows + y) = Grid;
472 if (step == 5 && (i%10))
473 continue;
474 sprintf(text, "%g", .1 * i); /* Tick labels */
475 x = left + x - 3 * strlen(text);
476 print_at(x, below - 6, Labels, text);
477 print_at(x, below + p->rows + 14, Labels, text);
478 }
479
480 /* Y-axis */
481 step = axis(effp->in_signal.rate / 2, (p->rows - 1) / ((font_y * 3 + 1) >> 1), &limit, &prefix);
482 sprintf(text, "Frequency (%.1sHz)", prefix); /* Axis label */
483 print_up(10, below + (p->rows - font_X * (int)strlen(text)) / 2, Text, text);
484 for (i = 0; i <= limit; i += step) {
485 int x, y = limit? (double)i / limit * (p->rows - 1) + .5 : 0;
486 for (x = 0; x < tick_len; ++x) /* Ticks */
487 pixel(left - 1 - x, below + y) = pixel(left + p->cols + x, below + y) = Grid;
488 if (step == 5 && (i%10))
489 continue;
490 sprintf(text, i?"%5g":" DC", .1 * i); /* Tick labels */
491 print_at(left - 4 - font_X * 5, below + y + 5, Labels, text);
492 sprintf(text, i?"%g":"DC", .1 * i);
493 print_at(left + p->cols + 6, below + y + 5, Labels, text);
494 }
495
496 /* Z-axis */
497 print_at(cols - right - 2 - font_X, below - 13, Text, "dBFS");/* Axis label */
498 for (j = 0; j < p->rows; ++j) { /* Spectrum */
499 png_byte b = colour(p->dB_range * (j / (p->rows - 1.) - 1), p->dB_range);
500 for (i = 0; i < spectrum_width; ++i)
501 pixel(cols - right - 1 - i, below + j) = b;
502 }
503 for (i = 0; i <= p->dB_range; i += 10) { /* (Tick) labels */
504 int y = (double)i / p->dB_range * (p->rows - 1) + .5;
505 sprintf(text, "%+i", i - p->gain - p->dB_range);
506 print_at(cols - right + 1, below + y + 5, Labels, text);
507 }
508
509 free(font);
510 png_set_rows(png, png_info, png_rows);
511 png_write_png(png, png_info, PNG_TRANSFORM_IDENTITY, NULL);
512 png_destroy_write_struct(&png, &png_info);
513 free(png_rows);
514 free(pixels);
515 fclose(file);
516 free(p->dBfs);
517 return SOX_SUCCESS;
518}
519
520sox_effect_handler_t const * sox_spectrogram_effect_fn(void)
521{
522 static sox_effect_handler_t handler = {
523 "spectrogram", "[options]\n"
524 " -x num X-axis pixels/second, default 100\n"
525 " -y num Y-axis resolution (1 - 4), default 2\n"
526 " -z num Z-axis (colour) range in dB, default 120\n"
527 " -g num Apply num dB gain; to shift Z-axis\n"
528 " -w name Window: Hann (default), Hamming, Bartlett, Rectangular, Kaiser\n"
529 " -s num Display style (1 - 4), default 1\n"
530 " -a Toggle the presence of the axes' lines\n"
531 " -t text Title text\n"
532 " -c text Comment text\n"
533 " -o text Name of the spectrogram output file, default `spectrogram.png'\n",
534 0, getopts, start, flow, drain, stop, NULL, sizeof(priv_t)};
535 return &handler;
536}

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