22static const unsigned char font_5x7[95][5] = {
23 {0x00, 0x00, 0x00, 0x00, 0x00},
24 {0x00, 0x00, 0x5F, 0x00, 0x00},
25 {0x00, 0x07, 0x00, 0x07, 0x00},
26 {0x14, 0x7F, 0x14, 0x7F, 0x14},
27 {0x24, 0x2A, 0x7F, 0x2A, 0x12},
28 {0x23, 0x13, 0x08, 0x64, 0x62},
29 {0x36, 0x49, 0x55, 0x22, 0x50},
30 {0x00, 0x05, 0x03, 0x00, 0x00},
31 {0x00, 0x1C, 0x22, 0x41, 0x00},
32 {0x00, 0x41, 0x22, 0x1C, 0x00},
33 {0x14, 0x08, 0x3E, 0x08, 0x14},
34 {0x08, 0x08, 0x3E, 0x08, 0x08},
35 {0x00, 0x50, 0x30, 0x00, 0x00},
36 {0x08, 0x08, 0x08, 0x08, 0x08},
37 {0x00, 0x60, 0x60, 0x00, 0x00},
38 {0x20, 0x10, 0x08, 0x04, 0x02},
39 {0x3E, 0x51, 0x49, 0x45, 0x3E},
40 {0x00, 0x42, 0x7F, 0x40, 0x00},
41 {0x42, 0x61, 0x51, 0x49, 0x46},
42 {0x21, 0x41, 0x45, 0x4B, 0x31},
43 {0x18, 0x14, 0x12, 0x7F, 0x10},
44 {0x27, 0x45, 0x45, 0x45, 0x39},
45 {0x3C, 0x4A, 0x49, 0x49, 0x30},
46 {0x01, 0x71, 0x09, 0x05, 0x03},
47 {0x36, 0x49, 0x49, 0x49, 0x36},
48 {0x06, 0x49, 0x49, 0x29, 0x1E},
49 {0x00, 0x36, 0x36, 0x00, 0x00},
50 {0x00, 0x56, 0x36, 0x00, 0x00},
51 {0x08, 0x14, 0x22, 0x41, 0x00},
52 {0x14, 0x14, 0x14, 0x14, 0x14},
53 {0x00, 0x41, 0x22, 0x14, 0x08},
54 {0x02, 0x01, 0x51, 0x09, 0x06},
55 {0x32, 0x49, 0x79, 0x41, 0x3E},
56 {0x7E, 0x11, 0x11, 0x11, 0x7E},
57 {0x7F, 0x49, 0x49, 0x49, 0x36},
58 {0x3E, 0x41, 0x41, 0x41, 0x22},
59 {0x7F, 0x41, 0x41, 0x22, 0x1C},
60 {0x7F, 0x49, 0x49, 0x49, 0x41},
61 {0x7F, 0x09, 0x09, 0x09, 0x01},
62 {0x3E, 0x41, 0x49, 0x49, 0x7A},
63 {0x7F, 0x08, 0x08, 0x08, 0x7F},
64 {0x00, 0x41, 0x7F, 0x41, 0x00},
65 {0x20, 0x40, 0x41, 0x3F, 0x01},
66 {0x7F, 0x08, 0x14, 0x22, 0x41},
67 {0x7F, 0x40, 0x40, 0x40, 0x40},
68 {0x7F, 0x02, 0x0C, 0x02, 0x7F},
69 {0x7F, 0x04, 0x08, 0x10, 0x7F},
70 {0x3E, 0x41, 0x41, 0x41, 0x3E},
71 {0x7F, 0x09, 0x09, 0x09, 0x06},
72 {0x3E, 0x41, 0x51, 0x21, 0x5E},
73 {0x7F, 0x09, 0x19, 0x29, 0x46},
74 {0x46, 0x49, 0x49, 0x49, 0x31},
75 {0x01, 0x01, 0x7F, 0x01, 0x01},
76 {0x3F, 0x40, 0x40, 0x40, 0x3F},
77 {0x1F, 0x20, 0x40, 0x20, 0x1F},
78 {0x3F, 0x40, 0x38, 0x40, 0x3F},
79 {0x63, 0x14, 0x08, 0x14, 0x63},
80 {0x07, 0x08, 0x70, 0x08, 0x07},
81 {0x61, 0x51, 0x49, 0x45, 0x43},
82 {0x00, 0x7F, 0x41, 0x41, 0x00},
83 {0x02, 0x04, 0x08, 0x10, 0x20},
84 {0x00, 0x41, 0x41, 0x7F, 0x00},
85 {0x04, 0x02, 0x01, 0x02, 0x04},
86 {0x40, 0x40, 0x40, 0x40, 0x40},
87 {0x00, 0x01, 0x02, 0x04, 0x00},
88 {0x20, 0x54, 0x54, 0x54, 0x78},
89 {0x7F, 0x48, 0x44, 0x44, 0x38},
90 {0x38, 0x44, 0x44, 0x44, 0x20},
91 {0x38, 0x44, 0x44, 0x48, 0x7F},
92 {0x38, 0x54, 0x54, 0x54, 0x18},
93 {0x08, 0x7E, 0x09, 0x01, 0x02},
94 {0x0C, 0x52, 0x52, 0x52, 0x3E},
95 {0x7F, 0x08, 0x04, 0x04, 0x78},
96 {0x00, 0x44, 0x7D, 0x40, 0x00},
97 {0x20, 0x40, 0x44, 0x3D, 0x00},
98 {0x7F, 0x10, 0x28, 0x44, 0x00},
99 {0x00, 0x41, 0x7F, 0x40, 0x00},
100 {0x7C, 0x04, 0x18, 0x04, 0x78},
101 {0x7C, 0x08, 0x04, 0x04, 0x78},
102 {0x38, 0x44, 0x44, 0x44, 0x38},
103 {0x7C, 0x14, 0x14, 0x14, 0x08},
104 {0x08, 0x14, 0x14, 0x18, 0x7C},
105 {0x7C, 0x08, 0x04, 0x04, 0x08},
106 {0x48, 0x54, 0x54, 0x54, 0x20},
107 {0x04, 0x3F, 0x44, 0x40, 0x20},
108 {0x3C, 0x40, 0x40, 0x20, 0x7C},
109 {0x1C, 0x20, 0x40, 0x20, 0x1C},
110 {0x3C, 0x40, 0x30, 0x40, 0x3C},
111 {0x44, 0x28, 0x10, 0x28, 0x44},
112 {0x0C, 0x50, 0x50, 0x50, 0x3C},
113 {0x44, 0x64, 0x54, 0x4C, 0x44},
114 {0x00, 0x08, 0x36, 0x41, 0x00},
115 {0x00, 0x00, 0x7F, 0x00, 0x00},
116 {0x00, 0x41, 0x36, 0x08, 0x00},
117 {0x10, 0x08, 0x08, 0x10, 0x10},
123static int pixel_at(
const char *text,
unsigned len,
unsigned col,
unsigned row)
125 unsigned char_width = 8;
126 unsigned char_idx = col / char_width;
127 unsigned col_in_char = col % char_width;
129 if (char_idx >= len)
return 0;
130 if (col_in_char >= 5)
return 0;
132 char ch = text[char_idx];
133 if (ch < 32 || ch > 126) ch =
'?';
135 unsigned glyph_idx = (unsigned)(ch - 32);
136 unsigned char col_byte = font_5x7[glyph_idx][col_in_char];
137 return (col_byte >> row) & 1;
146 double freq_lo,
double freq_hi,
147 double duration_sec,
double sample_rate)
155 "freq_hi must be < Nyquist", 0);
159 unsigned len = (unsigned)strlen(text);
160 unsigned grid_cols = len * 8 - 3;
161 unsigned grid_rows = 7;
163 unsigned col_samples = (unsigned)(duration_sec / (
double)grid_cols * sample_rate);
164 if (col_samples < 1) col_samples = 1;
166 unsigned total_samples = col_samples * grid_cols;
168 "max_len too small for total samples", 0);
171 unsigned fade_samples = (unsigned)(0.003 * sample_rate);
172 if (fade_samples > col_samples / 2) fade_samples = col_samples / 2;
176 for (
unsigned r = 0; r < grid_rows; r++) {
177 row_freq[r] = freq_hi - (double)r / (
double)(grid_rows - 1)
178 * (freq_hi - freq_lo);
182 double phase[7] = {0};
185 memset(output, 0, total_samples *
sizeof(
double));
187 for (
unsigned c = 0; c < grid_cols; c++) {
188 unsigned base = c * col_samples;
190 for (
unsigned r = 0; r < grid_rows; r++) {
191 int on_now =
pixel_at(text, len, c, r);
192 int on_prev = (c > 0) ?
pixel_at(text, len, c - 1, r) : 0;
193 int on_next = (c + 1 < grid_cols) ?
pixel_at(text, len, c + 1, r) : 0;
195 double dp = 2.0 * M_PI * row_freq[r] / sample_rate;
197 for (
unsigned s = 0; s < col_samples; s++) {
200 phase[r] = fmod(phase[r] + dp, 2.0 * M_PI);
204 double envelope = 1.0;
207 if (!on_prev && s < fade_samples && fade_samples > 0) {
208 double t = (double)s / (
double)fade_samples;
209 envelope *= 0.5 * (1.0 - cos(M_PI * t));
213 if (!on_next && s >= col_samples - fade_samples && fade_samples > 0) {
214 double t = (double)(col_samples - 1 - s) / (double)fade_samples;
215 envelope *= 0.5 * (1.0 - cos(M_PI * t));
218 output[base + s] += envelope * sin(phase[r]);
219 phase[r] = fmod(phase[r] + dp, 2.0 * M_PI);
226 for (
unsigned i = 0; i < total_samples; i++) {
227 double a = fabs(output[i]);
228 if (a > peak) peak = a;
231 double scale = 0.9 / peak;
232 for (
unsigned i = 0; i < total_samples; i++) {
237 return total_samples;
A mini library of DSP (Digital Signal Processing) routines.
@ MD_ERR_INVALID_SIZE
A size or count argument is invalid (e.g.
@ MD_ERR_INVALID_RANGE
A range or bound is invalid (e.g.
@ MD_ERR_NULL_POINTER
A required pointer argument is NULL.
Internal header for cross-file dependencies within the minidsp module.
#define MD_CHECK(cond, code, msg, retval)
Check a precondition in a function that returns a value.
static int pixel_at(const char *text, unsigned len, unsigned col, unsigned row)
Return the pixel state (0 or 1) at bitmap coordinate (col, row) for the given string.
unsigned MD_spectrogram_text(double *output, unsigned max_len, const char *text, double freq_lo, double freq_hi, double duration_sec, double sample_rate)
Synthesise audio that displays readable text in a spectrogram.