miniDSP
A small C library for audio DSP
Loading...
Searching...
No Matches
Signal Generators

Signal generators produce test signals from scratch — no audio file or microphone required. They are the "hello world" of DSP: a pure sine wave, an impulse, or a burst of noise gives you a known input that you can trace through any processing chain and verify at every step.

miniDSP ships generators as simple, stateless functions. They take an output buffer and a handful of parameters; no setup or teardown is needed.


Sine wave

A sine wave at frequency \(f\) Hz, amplitude \(A\), and sample rate \(f_s\):

\[x[n] = A \sin\!\left(\frac{2\pi f n}{f_s}\right), \quad n = 0, 1, \ldots, N-1 \]

Reading the formula in C:

// A -> amplitude, f -> freq, fs -> sample_rate, n -> i, x[n] -> output[i]
double phase_step = 2.0 * M_PI * freq / sample_rate; // precompute 2*pi*f / fs
for (unsigned i = 0; i < N; i++)
output[i] = amplitude * sin(phase_step * (double)i); // A * sin(2*pi*f*n / fs)

API:

void MD_sine_wave(double *output, unsigned N, double amplitude,
double freq, double sample_rate);
void MD_sine_wave(double *output, unsigned N, double amplitude, double freq, double sample_rate)
Generate a sine wave.

Listen — 440 Hz, 2 seconds:

Quick example — generate one second of A4 (440 Hz):

MD_sine_wave(signal, N, amplitude, freq_hz, sample_rate);

Verifying via spectrum

The clearest way to confirm the generator is correct is to feed its output to MD_magnitude_spectrum() and check that the peak lands on the expected bin:

unsigned N = 1024;
double fs = 16000.0, freq = 1000.0; /* bin 64 */
double sig[N], mag[N/2 + 1];
MD_sine_wave(sig, N, 1.0, freq, fs);
MD_magnitude_spectrum(sig, N, mag);
/* mag[64] should be the largest value */
void MD_magnitude_spectrum(const double *signal, unsigned N, double *mag_out)
Compute the magnitude spectrum of a real-valued signal.

See examples/sine_wave.c for a full runnable program that generates the spectrum and writes an interactive HTML plot.


Impulse

A discrete impulse (Kronecker delta) at position \(n_0\) with amplitude \(A\):

\[x[n] = \begin{cases} A & \text{if } n = n_0 \\ 0 & \text{otherwise} \end{cases} \]

Reading the formula in C:

// A -> amplitude, n0 -> position, x[n] -> output[n]
memset(output, 0, N * sizeof(double)); // set all samples to 0
output[position] = amplitude; // except at n = n0, where x[n] = A

The unit impulse ( \(A = 1\), \(n_0 = 0\)) is the identity element of convolution and has a perfectly flat magnitude spectrum — every frequency bin equals 1.0.

API:

void MD_impulse(double *output, unsigned N, double amplitude, unsigned position);
void MD_impulse(double *output, unsigned N, double amplitude, unsigned position)
Generate a discrete impulse (Kronecker delta).

Listen — impulse train (four clicks at 0.5-second intervals), 2 seconds:

Quick example — generate a unit impulse at sample 0:

MD_impulse(signal, N, amplitude, position);

Verifying via spectrum

Feed the impulse to MD_magnitude_spectrum() and confirm every bin has the same magnitude:

unsigned N = 1024;
double sig[N], mag[N/2 + 1];
MD_impulse(sig, N, 1.0, 0);
MD_magnitude_spectrum(sig, N, mag);
/* Every element of mag[] should be 1.0 */

See examples/impulse.c for a full runnable program that generates both time-domain and frequency-domain plots.


Chirp (swept sine)

A chirp sweeps frequency over time — either linearly or logarithmically. Chirps are the standard test signal for spectrograms and for measuring filter magnitude response.

Linear chirp

A linear chirp sweeps instantaneous frequency from \(f_0\) to \(f_1\) at a constant rate over duration \(T = (N-1)/f_s\):

\[x[n] = A \sin\!\left(2\pi\!\left(f_0\,t + \frac{1}{2}\,\frac{f_1 - f_0}{T}\,t^2\right)\right), \quad t = n / f_s \]

Reading the formula in C:

// A -> amplitude, f0 -> f_start, f1 -> f_end, fs -> sample_rate
// t = n/fs, T = (N-1)/fs, x[n] -> output[i]
double T = (double)(N - 1) / sample_rate;
double chirp_rate = (f_end - f_start) / T; // (f1 - f0) / T
for (unsigned i = 0; i < N; i++) {
double t = (double)i / sample_rate; // t = n / fs
double phase = 2.0 * M_PI * (f_start * t + 0.5 * chirp_rate * t * t);
output[i] = amplitude * sin(phase); // A * sin(2*pi * (f0*t + 1/2 * (f1-f0)/T * t^2))
}

API:

void MD_chirp_linear(double *output, unsigned N, double amplitude,
double f_start, double f_end, double sample_rate);
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).

Listen — linear sweep from 20 Hz to 4000 Hz, 2 seconds:

Logarithmic chirp

A logarithmic chirp sweeps frequency exponentially, spending equal time per octave. This is ideal for audio systems whose response is best viewed on a log-frequency axis.

\[x[n] = A \sin\!\left(\frac{2\pi f_0 T}{\ln r}\!\left(r^{t/T} - 1\right)\right), \quad r = f_1 / f_0,\quad t = n / f_s \]

Reading the formula in C:

// A -> amplitude, f0 -> f_start, f1 -> f_end, fs -> sample_rate
// r = f1/f0, t = n/fs, T = (N-1)/fs, x[n] -> output[i]
double T = (double)(N - 1) / sample_rate;
double ratio = f_end / f_start; // r = f1 / f0
double log_ratio = log(ratio); // ln(r)
for (unsigned i = 0; i < N; i++) {
double t = (double)i / sample_rate; // t = n / fs
double phase = 2.0 * M_PI * f_start * T
* (pow(ratio, t / T) - 1.0) / log_ratio; // 2*pi*f0*T / ln(r) * (r^(t/T) - 1)
output[i] = amplitude * sin(phase);
}

API:

void MD_chirp_log(double *output, unsigned N, double amplitude,
double f_start, double f_end, double sample_rate);
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).

Requires \(f_0 > 0\), \(f_1 > 0\), and \(f_0 \ne f_1\).

Listen — logarithmic sweep from 20 Hz to 4000 Hz, 2 seconds:

Quick example — generate both chirp types and compare:

MD_chirp_linear(sig_lin, N, amplitude, f_start, f_end, sample_rate);
MD_chirp_log(sig_log, N, amplitude, f_start, f_end, sample_rate);

See examples/chirp_wave.c for a full runnable program that generates the magnitude spectra of both chirp types and writes an interactive HTML plot.


Square wave

A square wave at frequency \(f\) Hz alternates between \(+A\) and \(-A\):

\[x[n] = \begin{cases} +A & 0 < \phi < \pi \\ -A & \pi < \phi < 2\pi \\ 0 & \phi = 0 \text{ or } \phi = \pi \end{cases} \]

where \(\phi = 2\pi f n / f_s \pmod{2\pi}\).

Reading the formula in C:

// A -> amplitude, f -> freq, fs -> sample_rate
// phi -> phase, x[n] -> output[i]
double phase_step = 2.0 * M_PI * freq / sample_rate; // 2*pi*f / fs
for (unsigned i = 0; i < N; i++) {
double phase = fmod(phase_step * (double)i, 2.0 * M_PI); // phi = 2*pi*f*n/fs (mod 2*pi)
if (phase < M_PI)
output[i] = amplitude; // +A when 0 < phi < pi
else
output[i] = -amplitude; // -A when pi < phi < 2*pi
}

Its Fourier series contains only odd harmonics (1f, 3f, 5f, …) with amplitudes decaying as \(1/k\) — a textbook demonstration of the Gibbs phenomenon.

API:

void MD_square_wave(double *output, unsigned N, double amplitude,
double freq, double sample_rate);
void MD_square_wave(double *output, unsigned N, double amplitude, double freq, double sample_rate)
Generate a square wave.

Listen — 440 Hz, 2 seconds:

Quick example:

MD_square_wave(sq_sig, N, amplitude, freq_hz, sample_rate);

See examples/square_sawtooth.c for a full program that compares the square and sawtooth spectra side by side.


Sawtooth wave

A sawtooth wave ramps linearly from \(-A\) to \(+A\) over each period:

\[x[n] = A \left(\frac{\phi}{\pi} - 1\right) \]

where \(\phi = 2\pi f n / f_s \pmod{2\pi}\).

Reading the formula in C:

// A -> amplitude, f -> freq, fs -> sample_rate
// phi -> phase, x[n] -> output[i]
double phase_step = 2.0 * M_PI * freq / sample_rate; // 2*pi*f / fs
for (unsigned i = 0; i < N; i++) {
double phase = fmod(phase_step * (double)i, 2.0 * M_PI); // phi = 2*pi*f*n/fs (mod 2*pi)
output[i] = amplitude * (phase / M_PI - 1.0); // A * (phi/pi - 1)
}

Unlike the square wave, the sawtooth contains all integer harmonics (1f, 2f, 3f, …), each decaying as \(1/k\). Comparing the two spectra shows how waveform shape determines harmonic content.

API:

void MD_sawtooth_wave(double *output, unsigned N, double amplitude,
double freq, double sample_rate);
void MD_sawtooth_wave(double *output, unsigned N, double amplitude, double freq, double sample_rate)
Generate a sawtooth wave.

Listen — 440 Hz, 2 seconds:

Quick example:

MD_sawtooth_wave(saw_sig, N, amplitude, freq_hz, sample_rate);

See examples/square_sawtooth.c for the full runnable program.


White noise

Gaussian white noise has equal power at all frequencies — its power spectral density (PSD) is approximately flat across the entire band. It is the standard broadband test signal for filter characterisation, impulse response measurement, and SNR experiments.

Each sample is drawn independently from a normal distribution with mean 0 and standard deviation \(\sigma\):

\[x[n] \sim \mathrm{N}(0,\, \sigma^2), \quad n = 0, 1, \ldots, N-1 \]

Samples are generated with the Box-Muller transform seeded by seed, so the same seed always produces the same sequence — useful for reproducible tests.

Reading the formula in C — the Box-Muller transform turns uniform random numbers into Gaussian samples:

// sigma -> amplitude, x[n] -> output[i]
// u1, u2 are uniform random numbers in (0, 1)
double r = sqrt(-2.0 * log(u1)); // sqrt(-2 * ln(u1))
double theta = 2.0 * M_PI * u2; // 2 * pi * u2
output[i] = amplitude * r * cos(theta); // sigma * sqrt(-2*ln(u1)) * cos(2*pi*u2)
output[i + 1] = amplitude * r * sin(theta); // sigma * sqrt(-2*ln(u1)) * sin(2*pi*u2)

API:

void MD_white_noise(double *output, unsigned N, double amplitude,
unsigned seed);
void MD_white_noise(double *output, unsigned N, double amplitude, unsigned seed)
Generate Gaussian white noise.

amplitude is the standard deviation \(\sigma\) of the distribution.

Listen — Gaussian white noise (sigma 0.25), 2 seconds:

Quick example — generate 4096 samples of unit-variance noise:

MD_white_noise(signal, N, amplitude, seed);

Verifying via PSD

Feed the noise to MD_power_spectral_density() and confirm the spectrum is approximately flat — no bin should dominate:

unsigned N = 4096;
double *sig = malloc(N * sizeof(double));
double *psd = malloc((N/2 + 1) * sizeof(double));
MD_white_noise(sig, N, 1.0, 42);
/* psd[] should fluctuate around a constant level */
void MD_power_spectral_density(const double *signal, unsigned N, double *psd_out)
Compute the power spectral density (PSD) of a real-valued signal.

See examples/white_noise.c for a full runnable program that computes and plots the power spectral density.


Further reading