ref: d79c8a1a83db4ee07a48df6100e003dfc9010321
parent: 784ea2ae8fca17ecaa52815271e7079eaa48407b
author: robs <robs>
date: Sun Jun 22 16:15:55 EDT 2008
clean-ups only
--- a/soxeffect.7
+++ b/soxeffect.7
@@ -1051,7 +1051,8 @@
X-axis pixels/second, default 100. This controls the width of the
spectrogram;
.I num
-can be from 1 (low time resolution) to 5000 (high time resolution). SoX
+can be from 1 (low time resolution) to 5000 (high time resolution);
+non-integer values (e.g. 1\*d5) can be used here. SoX
may make a slight adjustment to the given number for processing
quantisation reasons; if so, SoX will report the actual number used
(viewable when
--- a/src/spectrogram.c
+++ b/src/spectrogram.c
@@ -15,6 +15,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+/* TODO
+ * o Two-channel support.
+ * o Option for a larger font (for use with image down-scaling).
+ */
+
#ifdef NDEBUG /* Enable assert always. */
#undef NDEBUG /* Must undef above assert.h or other that might include it. */
#endif
@@ -88,19 +93,28 @@
{0, 0}};
typedef struct {
- int y_size, dft_size, rows, cols, step_size, block_steps, block_num;
- int dB_range, gain, style, read, end, end_min, last_end;
- int spectrum_points, perm;
- char const * out_name, * title, * comment;
- sox_bool high_colour, slack_overlap, no_axes, truncated;
+ /* Parameters */
+ double pixels_per_sec;
+ int y_size, dB_range, gain, spectrum_points, perm;
+ sox_bool monochrome, light_background, high_colour, slack_overlap, no_axes;
win_type_t win_type;
+ char const * out_name, * title, * comment;
+
+ /* Work area */
+ int WORK; /* Start of work area is marked by this dummy variable. */
+ int dft_size, step_size, block_steps, block_num, rows, cols, read;
+ int end, end_min, last_end;
+ sox_bool truncated;
double buf[MAX_DFT_SIZE], dft_buf[MAX_DFT_SIZE], window[MAX_DFT_SIZE];
- double pixels_per_sec, block_norm, max, magnitudes[(MAX_DFT_SIZE>>1) + 1];
- int bit_rev_table[100]; /* For Ooura fft */
+ double block_norm, max, magnitudes[(MAX_DFT_SIZE>>1) + 1];
+ int bit_rev_table[100]; /* For Ooura fft */
double sin_cos_table[dft_sc_len(MAX_DFT_SIZE)]; /* ditto */
float * dBfs;
} priv_t;
+#define secs(cols) \
+ ((double)(cols) * p->step_size * p->block_steps / effp->in_signal.rate)
+
#define GETOPT_NUMERIC(ch, name, min, max) case ch:{ \
char * end_ptr; \
double d = strtod(optarg, &end_ptr); \
@@ -134,7 +148,6 @@
{
priv_t * p = (priv_t *)effp->priv;
int c, callers_optind = optind, callers_opterr = opterr;
- sox_bool monochrome = sox_false, light_background = sox_false;
assert(array_length(p->bit_rev_table) >= (size_t)dft_br_len(MAX_DFT_SIZE));
@@ -141,7 +154,7 @@
--argv, ++argc, optind = 1, opterr = 0; /* re-jig for getopt */
p->pixels_per_sec = 100, p->y_size = 2, p->dB_range = 120;/* non-0 defaults */
p->spectrum_points = 249, p->perm = 1;
- p->style = 1, p->out_name = "spectrogram.png", p->comment = "Created by SoX";
+ p->out_name = "spectrogram.png", p->comment = "Created by SoX";
while ((c = getopt(argc, argv, "+x:y:z:Z:q:p:w:st:c:amlho:")) != -1) switch (c) {
GETOPT_NUMERIC('x', pixels_per_sec, 1 , 5000)
@@ -155,15 +168,14 @@
case 't': p->title = optarg; break;
case 'c': p->comment = optarg; break;
case 'a': p->no_axes = sox_true; break;
- case 'm': monochrome = sox_true; break;
- case 'l': light_background = sox_true; break;
+ case 'm': p->monochrome = sox_true; break;
+ case 'l': p->light_background = sox_true; break;
case 'h': p->high_colour = sox_true; break;
case 'o': p->out_name = optarg; break;
default: sox_fail("unknown option `-%c'", optopt); return lsx_usage(effp);
}
- p->gain = - p->gain;
+ p->gain = -p->gain;
--p->y_size, --p->perm;
- p->style = 2 * monochrome + light_background;
p->spectrum_points += 2;
argc -= optind, optind = callers_optind, opterr = callers_opterr;
return argc || p->win_type == INT_MAX? lsx_usage(effp) : SOX_SUCCESS;
@@ -198,6 +210,7 @@
sox_fail("only 1 channel is supported");
return SOX_EOF;
}
+ memset(&p->WORK, 0, sizeof(*p) - field_offset(priv_t, WORK));
p->end = p->dft_size = DFT_BASE_SIZE << p->y_size;
p->rows = (p->dft_size >> 1) + 1;
actual = make_window(p, p->last_end = 0);
@@ -222,7 +235,7 @@
int i;
if (p->cols == MAX_COLS) {
- sox_warn("PNG truncated at %g seconds", (double)MAX_COLS * p->step_size * p->block_steps / effp->in_signal.rate);
+ sox_warn("PNG truncated at %g seconds", secs(p->cols));
p->truncated = sox_true;
return SOX_EOF;
}
@@ -231,8 +244,7 @@
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;
- if (dBfs > p->max)
- p->max = dBfs;
+ p->max = max(dBfs, p->max);
}
memset(p->magnitudes, 0, p->rows * sizeof(*p->magnitudes));
p->block_num = 0;
@@ -289,8 +301,7 @@
if (left_over >= p->step_size >> 1)
isamp += p->step_size - left_over;
sox_debug("cols=%i left=%i end=%i", p->cols, p->read, p->end);
- p->end = 0;
- p->end_min = -p->dft_size;
+ p->end = 0, p->end_min = -p->dft_size;
if (flow(effp, ibuf, obuf, &isamp, &isamp) == SOX_SUCCESS && p->block_num) {
p->block_norm *= (double)p->block_steps / p->block_num;
do_column(effp);
@@ -316,8 +327,8 @@
{
int i;
- if (p->style & 1) {
- memcpy(palette++, (p->style & 2)? "\337\337\337":"\335\330\320", 3);
+ if (p->light_background) {
+ memcpy(palette++, (p->monochrome)? "\337\337\337":"\335\330\320", 3);
memcpy(palette++, "\0\0\0" , 3);
memcpy(palette++, "\077\077\077", 3);
memcpy(palette++, "\077\077\077", 3);
@@ -328,9 +339,9 @@
memcpy(palette++, "\177\177\177", 3);
}
for (i = 0; i < p->spectrum_points; ++i) {
- double c[3] , x = (double)i / (p->spectrum_points - 1);
- int at = (p->style & 1)? p->spectrum_points - 1 - i : i;
- if (p->style > 1) {
+ double c[3], x = (double)i / (p->spectrum_points - 1);
+ int at = (p->light_background)? p->spectrum_points - 1 - i : i;
+ if (p->monochrome) {
c[2] = c[1] = c[0] = x;
if (p->high_colour) {
c[(1 + p->perm) % 3] = x < .4? 0 : 5 / 3. * (x - .4);
@@ -345,13 +356,13 @@
if (p->high_colour) {
static const int states[3][7] = {
{4,5,0,0,2,1,1}, {0,0,2,1,1,3,2}, {4,1,1,3,0,0,2}};
- int j, phase_num = min(6, x * 7);
+ int j, phase_num = min(7 * x, 6);
for (j = 0; j < 3; ++j) switch (states[j][phase_num]) {
case 0: c[j] = 0; break;
case 1: c[j] = 1; break;
case 2: c[j] = sin((7 * x - phase_num) * M_PI / 2); break;
case 3: c[j] = cos((7 * x - phase_num) * M_PI / 2); break;
- case 4: c[j] = 7 * x - phase_num; break;
+ case 4: c[j] = 7 * x - phase_num; break;
case 5: c[j] = 1 - (7 * x - phase_num); break;
}
} else {
@@ -447,13 +458,12 @@
uLong font_len = 96 * font_y;
int rows = below + p->rows + 30 + 20 * !!p->title;
int cols = left + p->cols + between + spectrum_width + right;
- sox_bool do_frame = !p->no_axes;
png_byte * pixels = malloc(cols * rows * sizeof(*pixels));
png_bytepp png_rows = malloc(rows * sizeof(*png_rows));
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0,0);
png_infop png_info = png_create_info_struct(png);
png_color palette[256];
- int i, j, step, tick_len = 2 + do_frame;
+ int i, j, step, tick_len = 3 - p->no_axes;
char text[200], * prefix;
double limit;
@@ -475,7 +485,7 @@
png_set_IHDR(png, png_info, (size_t)cols, (size_t)rows, 8,
PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
- for (j = 0; j < rows; ++j)
+ for (j = 0; j < rows; ++j) /* Put (0,0) at bottom-left of PNG */
png_rows[rows - 1 - j] = (png_bytep)(pixels + j * cols);
if (p->title && (i = (int)strlen(p->title) * font_X) < cols + 1) /* Title */
@@ -487,20 +497,20 @@
for (j = 0; j < p->rows; ++j) {
for (i = 0; i < p->cols; ++i)
pixel(left + i, below + j) = colour(p, p->dBfs[i*p->rows + j]);
- if (do_frame) /* Frame verticals */
+ if (!p->no_axes) /* Y-axis lines */
pixel(left - 1, below + j) = pixel(left + p->cols,below + j) = Grid;
}
- if (do_frame) for (i = -1; i <= p->cols; ++i) /* Frame horizontals */
+ if (!p->no_axes) for (i = -1; i <= p->cols; ++i) /* X-axis lines */
pixel(left + i, below - 1) = pixel(left + i, below + p->rows) = Grid;
/* X-axis */
- step = axis((double)p->cols * p->step_size * p->block_steps / effp->in_signal.rate, p->cols / (font_X * 9 / 2), &limit, &prefix);
+ step = axis(secs(p->cols), p->cols / (font_X * 9 / 2), &limit, &prefix);
sprintf(text, "Time (%.1ss)", prefix); /* Axis label */
print_at(left + (p->cols - font_X * (int)strlen(text)) / 2, 24, Text, text);
for (i = 0; i <= limit; i += step) {
int y, x = limit? (double)i / limit * p->cols + .5 : 0;
for (y = 0; y < tick_len; ++y) /* Ticks */
- pixel(left - 1 + x, below - 1 - y) = pixel(left - 1 + x, below + p->rows + y) = Grid;
+ pixel(left-1+x, below-1-y) = pixel(left-1+x, below+p->rows+y) = Grid;
if (step == 5 && (i%10))
continue;
sprintf(text, "%g", .1 * i); /* Tick labels */
@@ -510,13 +520,14 @@
}
/* Y-axis */
- step = axis(effp->in_signal.rate / 2, (p->rows - 1) / ((font_y * 3 + 1) >> 1), &limit, &prefix);
+ step = axis(effp->in_signal.rate / 2,
+ (p->rows - 1) / ((font_y * 3 + 1) >> 1), &limit, &prefix);
sprintf(text, "Frequency (%.1sHz)", prefix); /* Axis label */
print_up(10, below + (p->rows - font_X * (int)strlen(text)) / 2, Text, text);
for (i = 0; i <= limit; i += step) {
int x, y = limit? (double)i / limit * (p->rows - 1) + .5 : 0;
for (x = 0; x < tick_len; ++x) /* Ticks */
- pixel(left - 1 - x, below + y) = pixel(left + p->cols + x, below + y) = Grid;
+ pixel(left-1-x, below+y) = pixel(left+p->cols+x, below+y) = Grid;
if (step == 5 && (i%10))
continue;
sprintf(text, i?"%5g":" DC", .1 * i); /* Tick labels */
--- a/src/util.h
+++ b/src/util.h
@@ -69,6 +69,7 @@
#define assert_static(e,f) enum {assert_static__##f = 1/(e)}
#define range_limit(x, lower, upper) (min(max(x, lower), upper))
#define array_length(a) (sizeof(a)/sizeof(a[0]))
+#define field_offset(type, field) ((unsigned)&(((type *)0)->field))
#define sqr(a) ((a) * (a))
#define dB_to_linear(x) exp((x) * M_LN10 * 0.05)