miniDSP
A small C library for audio DSP
Loading...
Searching...
No Matches
minidsp_generators.c
Go to the documentation of this file.
1
8
9#include "minidsp.h"
10#include "minidsp_internal.h"
11
12/* -----------------------------------------------------------------------
13 * Public API: Signal generators
14 * -----------------------------------------------------------------------*/
15
16void MD_sine_wave(double *output, unsigned N, double amplitude,
17 double freq, double sample_rate)
18{
19 MD_CHECK_VOID(output != NULL, MD_ERR_NULL_POINTER, "output is NULL");
20 MD_CHECK_VOID(N > 0, MD_ERR_INVALID_SIZE, "N must be > 0");
21 MD_CHECK_VOID(sample_rate > 0.0, MD_ERR_INVALID_RANGE, "sample_rate must be > 0");
22 double phase_step = 2.0 * M_PI * freq / sample_rate;
23 for (unsigned i = 0; i < N; i++)
24 output[i] = amplitude * sin(phase_step * (double)i);
25}
26
27void MD_white_noise(double *output, unsigned N, double amplitude,
28 unsigned seed)
29{
30 MD_CHECK_VOID(output != NULL, MD_ERR_NULL_POINTER, "output is NULL");
31 MD_CHECK_VOID(N > 0, MD_ERR_INVALID_SIZE, "N must be > 0");
32
33 /* 64-bit LCG (Knuth MMIX constants) for uniform doubles in (0, 1).
34 * Self-contained so we don't need POSIX drand48. */
35 unsigned long long state = (unsigned long long)seed;
36
37 /* Box-Muller transform: convert pairs of uniform random numbers
38 * into pairs of Gaussian random numbers (mean 0, std dev 1),
39 * then scale by the requested amplitude.
40 *
41 * For each pair (u1, u2) drawn from (0, 1]:
42 * z0 = sqrt(-2 ln u1) * cos(2 pi u2)
43 * z1 = sqrt(-2 ln u1) * sin(2 pi u2) */
44 unsigned i = 0;
45 while (i + 1 < N) {
46 state = state * 6364136223846793005ULL + 1442695040888963407ULL;
47 double u1 = (double)(state >> 11) * 0x1.0p-53;
48 if (u1 < DBL_MIN) u1 = DBL_MIN;
49 state = state * 6364136223846793005ULL + 1442695040888963407ULL;
50 double u2 = (double)(state >> 11) * 0x1.0p-53;
51
52 double r = sqrt(-2.0 * log(u1));
53 double theta = 2.0 * M_PI * u2;
54 output[i] = amplitude * r * cos(theta);
55 output[i + 1] = amplitude * r * sin(theta);
56 i += 2;
57 }
58 /* If N is odd, generate one extra pair and use only the first value */
59 if (i < N) {
60 state = state * 6364136223846793005ULL + 1442695040888963407ULL;
61 double u1 = (double)(state >> 11) * 0x1.0p-53;
62 if (u1 < DBL_MIN) u1 = DBL_MIN;
63 state = state * 6364136223846793005ULL + 1442695040888963407ULL;
64 double u2 = (double)(state >> 11) * 0x1.0p-53;
65 output[i] = amplitude * sqrt(-2.0 * log(u1)) * cos(2.0 * M_PI * u2);
66 }
67}
68
69void MD_impulse(double *output, unsigned N, double amplitude, unsigned position)
70{
71 MD_CHECK_VOID(output != NULL, MD_ERR_NULL_POINTER, "output is NULL");
72 MD_CHECK_VOID(N > 0, MD_ERR_INVALID_SIZE, "N must be > 0");
73 MD_CHECK_VOID(position < N, MD_ERR_INVALID_RANGE, "position must be < N");
74 memset(output, 0, N * sizeof(double));
75 output[position] = amplitude;
76}
77
78void MD_chirp_linear(double *output, unsigned N, double amplitude,
79 double f_start, double f_end, double sample_rate)
80{
81 MD_CHECK_VOID(output != NULL, MD_ERR_NULL_POINTER, "output is NULL");
82 MD_CHECK_VOID(N > 0, MD_ERR_INVALID_SIZE, "N must be > 0");
83 MD_CHECK_VOID(sample_rate > 0.0, MD_ERR_INVALID_RANGE, "sample_rate must be > 0");
84
85 if (N == 1) {
86 output[0] = 0.0;
87 return;
88 }
89
90 double T = (double)(N - 1) / sample_rate;
91 double chirp_rate = (f_end - f_start) / T;
92
93 for (unsigned i = 0; i < N; i++) {
94 double t = (double)i / sample_rate;
95 double phase = 2.0 * M_PI * (f_start * t + 0.5 * chirp_rate * t * t);
96 output[i] = amplitude * sin(phase);
97 }
98}
99
100void MD_chirp_log(double *output, unsigned N, double amplitude,
101 double f_start, double f_end, double sample_rate)
102{
103 MD_CHECK_VOID(output != NULL, MD_ERR_NULL_POINTER, "output is NULL");
104 MD_CHECK_VOID(N > 0, MD_ERR_INVALID_SIZE, "N must be > 0");
105 MD_CHECK_VOID(sample_rate > 0.0, MD_ERR_INVALID_RANGE, "sample_rate must be > 0");
106 MD_CHECK_VOID(f_start > 0.0, MD_ERR_INVALID_RANGE, "f_start must be > 0");
107 MD_CHECK_VOID(f_end > 0.0, MD_ERR_INVALID_RANGE, "f_end must be > 0");
108 MD_CHECK_VOID(f_start != f_end, MD_ERR_INVALID_RANGE, "f_start must != f_end");
109
110 if (N == 1) {
111 output[0] = 0.0;
112 return;
113 }
114
115 double T = (double)(N - 1) / sample_rate;
116 double ratio = f_end / f_start;
117 double log_ratio = log(ratio);
118
119 for (unsigned i = 0; i < N; i++) {
120 double t = (double)i / sample_rate;
121 double phase = 2.0 * M_PI * f_start * T
122 * (pow(ratio, t / T) - 1.0) / log_ratio;
123 output[i] = amplitude * sin(phase);
124 }
125}
126
127void MD_square_wave(double *output, unsigned N, double amplitude,
128 double freq, double sample_rate)
129{
130 MD_CHECK_VOID(output != NULL, MD_ERR_NULL_POINTER, "output is NULL");
131 MD_CHECK_VOID(N > 0, MD_ERR_INVALID_SIZE, "N must be > 0");
132 MD_CHECK_VOID(sample_rate > 0.0, MD_ERR_INVALID_RANGE, "sample_rate must be > 0");
133 double phase_step = 2.0 * M_PI * freq / sample_rate;
134 for (unsigned i = 0; i < N; i++) {
135 double phase = fmod(phase_step * (double)i, 2.0 * M_PI);
136 if (phase < 0.0) phase += 2.0 * M_PI;
137 if (phase == 0.0 || phase == M_PI)
138 output[i] = 0.0;
139 else if (phase < M_PI)
140 output[i] = amplitude;
141 else
142 output[i] = -amplitude;
143 }
144}
145
146void MD_sawtooth_wave(double *output, unsigned N, double amplitude,
147 double freq, double sample_rate)
148{
149 MD_CHECK_VOID(output != NULL, MD_ERR_NULL_POINTER, "output is NULL");
150 MD_CHECK_VOID(N > 0, MD_ERR_INVALID_SIZE, "N must be > 0");
151 MD_CHECK_VOID(sample_rate > 0.0, MD_ERR_INVALID_RANGE, "sample_rate must be > 0");
152 double phase_step = 2.0 * M_PI * freq / sample_rate;
153 for (unsigned i = 0; i < N; i++) {
154 double phase = fmod(phase_step * (double)i, 2.0 * M_PI);
155 if (phase < 0.0) phase += 2.0 * M_PI;
156 output[i] = amplitude * (phase / M_PI - 1.0);
157 }
158}
159
160void MD_shepard_tone(double *output, unsigned N, double amplitude,
161 double base_freq, double sample_rate,
162 double rate_octaves_per_sec, unsigned num_octaves)
163{
164 MD_CHECK_VOID(output != NULL, MD_ERR_NULL_POINTER, "output is NULL");
165 MD_CHECK_VOID(N > 0, MD_ERR_INVALID_SIZE, "N must be > 0");
166 MD_CHECK_VOID(sample_rate > 0.0, MD_ERR_INVALID_RANGE, "sample_rate must be > 0");
167 MD_CHECK_VOID(base_freq > 0.0, MD_ERR_INVALID_RANGE, "base_freq must be > 0");
168 MD_CHECK_VOID(num_octaves > 0, MD_ERR_INVALID_SIZE, "num_octaves must be > 0");
169
170 double rate = rate_octaves_per_sec;
171 double center = (double)(num_octaves - 1) / 2.0;
172 double sigma = (double)num_octaves / 4.0;
173 double nyquist = sample_rate / 2.0;
174 double margin = 5.0 * sigma;
175
176 /* Determine the range of layer indices needed.
177 *
178 * Layer k has octave offset from the Gaussian centre at time t:
179 * d_k(t) = k − center + rate · t
180 *
181 * A layer is relevant if |d_k(t)| ≤ 5σ for at least one t in
182 * [0, duration]. For rate > 0 (rising), d_k increases with time,
183 * so early layers start below centre and later ones rise above it.
184 * Extra layers below the initial configuration are needed to replace
185 * those that drift above the Gaussian on the high side. For rate < 0
186 * (falling), the mirror logic applies. */
187 double duration = (N > 1) ? (double)(N - 1) / sample_rate : 0.0;
188 double drift = rate * duration;
189
190 int k_min = (int)floor(center - margin - fmax(0.0, drift));
191 int k_max = (int)ceil(center + margin - fmin(0.0, drift));
192 unsigned total_layers = (unsigned)(k_max - k_min + 1);
193
194 /* Per-layer phase accumulators (zero-initialised). */
195 double *phases = calloc(total_layers, sizeof(double));
196 MD_CHECK_VOID(phases != NULL, MD_ERR_ALLOC_FAILED, "calloc failed");
197 memset(output, 0, N * sizeof(double));
198
199 for (unsigned i = 0; i < N; i++) {
200 double t = (double)i / sample_rate;
201 for (int k = k_min; k <= k_max; k++) {
202 /* d = octave distance from Gaussian centre (continuous) */
203 double d = (double)k - center + rate * t;
204
205 /* Skip layers outside the significant Gaussian tail */
206 if (fabs(d) > margin) continue;
207
208 double freq = base_freq * pow(2.0, d);
209 unsigned idx = (unsigned)(k - k_min);
210 phases[idx] += 2.0 * M_PI * freq / sample_rate;
211
212 /* Skip frequencies outside the audible / Nyquist range */
213 if (freq >= nyquist || freq < 20.0) continue;
214
215 double gauss = exp(-0.5 * d * d / (sigma * sigma));
216 /* Keep phase bounded to preserve floating-point precision */
217 if (phases[idx] > 4.0 * M_PI)
218 phases[idx] = fmod(phases[idx], 2.0 * M_PI);
219 output[i] += gauss * sin(phases[idx]);
220 }
221 }
222
223 free(phases);
224
225 /* Normalise so the peak absolute value equals the requested amplitude. */
226 double peak = 0.0;
227 for (unsigned i = 0; i < N; i++) {
228 double a = fabs(output[i]);
229 if (a > peak) peak = a;
230 }
231 if (peak > 0.0) {
232 double scale = amplitude / peak;
233 for (unsigned i = 0; i < N; i++)
234 output[i] *= scale;
235 }
236}
A mini library of DSP (Digital Signal Processing) routines.
@ MD_ERR_INVALID_SIZE
A size or count argument is invalid (e.g.
Definition minidsp.h:63
@ MD_ERR_INVALID_RANGE
A range or bound is invalid (e.g.
Definition minidsp.h:64
@ MD_ERR_ALLOC_FAILED
A memory allocation failed.
Definition minidsp.h:65
@ MD_ERR_NULL_POINTER
A required pointer argument is NULL.
Definition minidsp.h:62
void MD_chirp_linear(double *output, unsigned N, double amplitude, double f_start, double f_end, double sample_rate)
Generate a linear chirp (swept sine with linearly increasing frequency).
void MD_white_noise(double *output, unsigned N, double amplitude, unsigned seed)
Generate Gaussian white noise.
void MD_sine_wave(double *output, unsigned N, double amplitude, double freq, double sample_rate)
Generate a sine wave.
void MD_impulse(double *output, unsigned N, double amplitude, unsigned position)
Generate a discrete impulse (Kronecker delta).
void MD_shepard_tone(double *output, unsigned N, double amplitude, double base_freq, double sample_rate, double rate_octaves_per_sec, unsigned num_octaves)
Generate a Shepard tone — the auditory illusion of endlessly rising or falling pitch.
void MD_sawtooth_wave(double *output, unsigned N, double amplitude, double freq, double sample_rate)
Generate a sawtooth wave.
void MD_square_wave(double *output, unsigned N, double amplitude, double freq, double sample_rate)
Generate a square wave.
void MD_chirp_log(double *output, unsigned N, double amplitude, double f_start, double f_end, double sample_rate)
Generate a logarithmic chirp (swept sine with exponentially increasing frequency).
Internal header for cross-file dependencies within the minidsp module.
#define MD_CHECK_VOID(cond, code, msg)
Check a precondition in a void function.