26double MD_dot(
const double *a,
const double *b,
unsigned N)
29 for (
unsigned i = 0; i < N; i++) {
49 if (N == 1)
return a[0] * a[0];
81 double p = fmax(1.0e-10,
MD_power(a, N));
82 return 10.0 * log10(p);
98 double oldmin,
double oldmax,
99 double newmin,
double newmax)
101 return (in - oldmin) * (newmax - newmin) / (oldmax - oldmin) + newmin;
108 double oldmin,
double oldmax,
109 double newmin,
double newmax)
117 double scale = (newmax - newmin) / (oldmax - oldmin);
118 for (
unsigned i = 0; i < N; i++) {
119 out[i] = (in[i] - oldmin) * scale + newmin;
131 double newmin,
double newmax)
139 double in_min = in[0];
140 double in_max = in[0];
141 for (
unsigned i = 1; i < N; i++) {
142 if (in[i] < in_min) in_min = in[i];
143 if (in[i] > in_max) in_max = in[i];
146 if (in_min >= newmin && in_max <= newmax) {
148 for (
unsigned i = 0; i < N; i++) {
152 MD_scale_vec(in, out, N, in_min, in_max, newmin, newmax);
167 unsigned N,
double dblevel)
170 double desired_power = pow(10.0, dblevel / 10.0);
180 if (input_energy == 0.0) {
181 for (
unsigned i = 0; i < N; i++) out[i] = in[i];
185 double gain = sqrt((desired_power * (
double)N) / input_energy);
188 bool out_of_range =
false;
189 for (
unsigned i = 0; i < N; i++) {
190 out[i] = in[i] * gain;
191 if (out[i] > 1.0 || out[i] < -1.0) {
222 if (N <= 1)
return 0.0;
225 double max_entropy = log2((
double)N);
230 for (
unsigned i = 0; i < N; i++) {
231 total += (a[i] < 0.0) ? 0.0 : a[i];
234 for (
unsigned i = 0; i < N; i++) {
235 total += a[i] * a[i];
241 if (total == 0.0)
return 0.0;
244 double entropy = 0.0;
245 for (
unsigned i = 0; i < N; i++) {
246 if (a[i] == 0.0)
continue;
247 if (clip && a[i] < 0.0)
continue;
253 p = (a[i] * a[i]) / total;
256 entropy += p * log2(p);
260 return -entropy / max_entropy;
268#define MD_F0_ACF_PEAK_THRESHOLD 0.15
274 double denom = y_left - 2.0 * y_mid + y_right;
275 if (fabs(denom) < 1e-12)
return 0.0;
277 double delta = 0.5 * (y_left - y_right) / denom;
278 if (delta < -0.5) delta = -0.5;
279 if (delta > 0.5) delta = 0.5;
288double MD_rms(
const double *a,
unsigned N)
305 unsigned crossings = 0;
306 for (
unsigned i = 1; i < N; i++) {
307 if ((a[i] < 0.0) != (a[i - 1] < 0.0))
310 return (
double)crossings / (double)(N - 1);
322 double *out,
unsigned max_lag)
331 memset(out, 0, max_lag *
sizeof(
double));
334 for (
unsigned tau = 0; tau < max_lag; tau++) {
335 out[tau] =
MD_dot(a, a + tau, N - tau) / r0;
350 unsigned min_distance,
unsigned *peaks_out,
351 unsigned *num_peaks_out)
359 unsigned last_peak = 0;
361 for (
unsigned i = 1; i + 1 < N; i++) {
362 if (a[i] > a[i - 1] && a[i] > a[i + 1] && a[i] >= threshold) {
363 if (count == 0 || i - last_peak >= min_distance) {
364 peaks_out[count++] = i;
369 *num_peaks_out = count;
374 double min_freq_hz,
double max_freq_hz)
382 unsigned lag_min = (unsigned)floor(sample_rate / max_freq_hz);
383 unsigned lag_max = (unsigned)ceil(sample_rate / min_freq_hz);
385 if (lag_min < 1) lag_min = 1;
386 if (lag_max > N - 2) lag_max = N - 2;
387 if (lag_min > lag_max)
return 0.0;
388 if (lag_max < 2)
return 0.0;
390 double *acf = malloc((lag_max + 1) *
sizeof(
double));
391 if (!acf)
return 0.0;
395 unsigned start = (lag_min < 1) ? 1 : lag_min;
396 unsigned stop = (lag_max > N - 2) ? (N - 2) : lag_max;
398 double best_peak = -DBL_MAX;
399 unsigned best_lag = 0;
401 for (
unsigned lag = start; lag <= stop; lag++) {
404 if (y <= acf[lag - 1] || y <= acf[lag + 1])
continue;
417 double lag_est = (double)best_lag;
418 if (best_lag > 0 && best_lag + 1 <= lag_max) {
425 if (lag_est <= 0.0)
return 0.0;
426 return sample_rate / lag_est;
436void MD_mix(
const double *a,
const double *b,
double *out,
437 unsigned N,
double w_a,
double w_b)
442 for (
unsigned i = 0; i < N; i++) {
443 out[i] = w_a * a[i] + w_b * b[i];
452 unsigned delay_samples,
double feedback,
453 double dry,
double wet)
461 double *delay = calloc(delay_samples,
sizeof(
double));
465 for (
unsigned n = 0; n < N; n++) {
467 double d = delay[idx];
468 out[n] = dry * x + wet * d;
469 delay[idx] = x + feedback * d;
471 if (idx == delay_samples) idx = 0;
478 double rate_hz,
double depth,
double sample_rate)
487 double phase_step = 2.0 * M_PI * rate_hz / sample_rate;
488 for (
unsigned n = 0; n < N; n++) {
489 double lfo = 0.5 * (1.0 + sin(phase_step * (
double)n));
490 double gain = (1.0 - depth) + depth * lfo;
491 out[n] = in[n] * gain;
496 unsigned delay_samples,
double feedback,
497 double dry,
double wet)
505 double *comb = calloc(delay_samples,
sizeof(
double));
509 for (
unsigned n = 0; n < N; n++) {
511 double delayed = comb[idx];
512 double y_comb = x + feedback * delayed;
514 out[n] = dry * x + wet * y_comb;
516 if (idx == delay_samples) idx = 0;
536 double half_x = x / 2.0;
538 for (
unsigned k = 1; k < 300; k++) {
539 term *= (half_x / (double)k);
540 double term_sq = term * term;
542 if (term_sq < 1e-15 * sum)
break;
554 if (fabs(x) < 1e-12)
return 1.0;
555 double px = M_PI * x;
584 double n_minus_1 = (double)(n - 1);
585 for (
unsigned i = 0; i < n; i++) {
586 out[i] = 0.5 * (1.0 - cos(2.0 * M_PI * (
double)i / n_minus_1));
605 double n_minus_1 = (double)(n - 1);
606 for (
unsigned i = 0; i < n; i++) {
607 out[i] = 0.54 - 0.46 * cos(2.0 * M_PI * (
double)i / n_minus_1);
629 double n_minus_1 = (double)(n - 1);
630 for (
unsigned i = 0; i < n; i++) {
631 double p = 2.0 * M_PI * (double)i / n_minus_1;
632 out[i] = 0.42 - 0.5 * cos(p) + 0.08 * cos(2.0 * p);
643 for (
unsigned i = 0; i < n; i++) {
669 double n_minus_1 = (double)(n - 1);
671 for (
unsigned i = 0; i < n; i++) {
672 double t = 2.0 * (double)i / n_minus_1 - 1.0;
673 double arg = 1.0 - t * t;
674 if (arg < 0.0) arg = 0.0;
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_ALLOC_FAILED
A memory allocation failed.
@ MD_ERR_NULL_POINTER
A required pointer argument is NULL.
void MD_Gen_Kaiser_Win(double *out, unsigned n, double beta)
Generate a Kaiser window.
double MD_power(const double *a, unsigned N)
Signal power: energy divided by the number of samples.
#define MD_F0_ACF_PEAK_THRESHOLD
Minimum normalised autocorrelation peak height accepted as voiced F0.
void MD_Gen_Blackman_Win(double *out, unsigned n)
Generate a Blackman window.
double MD_zero_crossing_rate(const double *a, unsigned N)
Zero-crossing rate: fraction of adjacent sample pairs that differ in sign.
void MD_scale_vec(double *in, double *out, unsigned N, double oldmin, double oldmax, double newmin, double newmax)
Apply MD_scale() to every element of a vector.
static double md_parabolic_offset(double y_left, double y_mid, double y_right)
Three-point parabolic refinement around a discrete peak index.
void MD_Gen_Hamming_Win(double *out, unsigned n)
Generate a Hamming window.
double MD_bessel_i0(double x)
Zeroth-order modified Bessel function of the first kind.
void MD_tremolo(const double *in, double *out, unsigned N, double rate_hz, double depth, double sample_rate)
Tremolo effect (amplitude modulation by a sinusoidal LFO).
double MD_scale(double in, double oldmin, double oldmax, double newmin, double newmax)
Linearly map a value from one range to another.
void MD_Gen_Hann_Win(double *out, unsigned n)
Generate a Hanning (raised cosine) window.
double MD_energy(const double *a, unsigned N)
Signal energy: the sum of squared samples.
void MD_peak_detect(const double *a, unsigned N, double threshold, unsigned min_distance, unsigned *peaks_out, unsigned *num_peaks_out)
Peak detection: find local maxima above a threshold.
void MD_mix(const double *a, const double *b, double *out, unsigned N, double w_a, double w_b)
Signal mixing: weighted sum of two signals.
double MD_entropy(const double *a, unsigned N, bool clip)
Compute the normalised entropy of a distribution.
double MD_f0_autocorrelation(const double *signal, unsigned N, double sample_rate, double min_freq_hz, double max_freq_hz)
Estimate the fundamental frequency (F0) using autocorrelation.
double MD_sinc(double x)
Normalized sinc function: sin(πx) / (πx).
double MD_power_db(const double *a, unsigned N)
Signal power expressed in decibels (dB).
void MD_delay_echo(const double *in, double *out, unsigned N, unsigned delay_samples, double feedback, double dry, double wet)
Delay line / echo effect using a circular buffer with feedback.
double MD_dot(const double *a, const double *b, unsigned N)
Dot product of two vectors a and b, each of length N.
void MD_Gen_Rect_Win(double *out, unsigned n)
Generate a rectangular window (all ones).
void MD_comb_reverb(const double *in, double *out, unsigned N, unsigned delay_samples, double feedback, double dry, double wet)
Comb-filter reverb (feedback comb filter with dry/wet mix).
void MD_autocorrelation(const double *a, unsigned N, double *out, unsigned max_lag)
Normalised autocorrelation for lags 0..max_lag-1.
void MD_fit_within_range(double *in, double *out, unsigned N, double newmin, double newmax)
Squeeze values into [newmin, newmax] only if they don't already fit.
double MD_rms(const double *a, unsigned N)
Root mean square: the standard measure of signal "loudness".
void MD_adjust_dblevel(const double *in, double *out, unsigned N, double dblevel)
Automatic Gain Control (AGC): adjust a signal to a target dB level.
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.
#define MD_CHECK_VOID(cond, code, msg)
Check a precondition in a void function.