first commit

This commit is contained in:
Uladzimir Karpenka
2026-06-01 15:58:45 +03:00
commit 89c8a0e2b5
313 changed files with 945364 additions and 0 deletions
@@ -0,0 +1,354 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "adaptive_denoiser.h"
#include "shared/configurations.h"
#include "shared/gain_estimation/gain_estimators.h"
#include "shared/noise_estimation/adaptive_noise_estimator.h"
#include "shared/post_estimation/noise_floor_manager.h"
#include "shared/post_estimation/postfilter.h"
#include "shared/pre_estimation/critical_bands.h"
#include "shared/pre_estimation/noise_scaling_criterias.h"
#include "shared/pre_estimation/spectral_smoother.h"
#include "shared/utils/denoise_mixer.h"
#include "shared/utils/spectral_features.h"
#include "shared/utils/spectral_utils.h"
#include <float.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
typedef struct SpectralAdaptiveDenoiser {
uint32_t fft_size;
uint32_t real_spectrum_size;
uint32_t sample_rate;
uint32_t hop;
float default_oversubtraction;
float default_undersubtraction;
AdaptiveDenoiserParameters parameters;
float* alpha;
float* beta;
float* gain_spectrum;
float* residual_spectrum;
float* denoised_spectrum;
float* noise_profile;
SpectrumType spectrum_type;
CriticalBandType band_type;
GainEstimationType gain_estimation_type;
TimeSmoothingType time_smoothing_type;
DenoiseMixer* mixer;
NoiseScalingCriterias* noise_scaling_criteria;
SpectralSmoother* spectrum_smoothing;
PostFilter* postfiltering;
AdaptiveNoiseEstimator* adaptive_estimator;
SpectralFeatures* spectral_features;
NoiseFloorManager* noise_floor_manager;
bool postfiltering_enabled;
bool whitening_enabled;
} SpectralAdaptiveDenoiser;
SpectralProcessorHandle spectral_adaptive_denoiser_initialize(
const uint32_t sample_rate, const uint32_t fft_size,
const uint32_t overlap_factor) {
if (sample_rate == 0 || fft_size == 0 || overlap_factor == 0) {
return NULL;
}
SpectralAdaptiveDenoiser* self =
(SpectralAdaptiveDenoiser*)calloc(1U, sizeof(SpectralAdaptiveDenoiser));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->sample_rate = sample_rate;
self->hop = self->fft_size / overlap_factor;
self->default_oversubtraction = DEFAULT_OVERSUBTRACTION;
self->default_undersubtraction = DEFAULT_UNDERSUBTRACTION;
self->spectrum_type = SPECTRAL_TYPE_SPEECH;
self->band_type = CRITICAL_BANDS_TYPE_SPEECH;
self->gain_estimation_type = GAIN_ESTIMATION_TYPE_SPEECH;
self->time_smoothing_type = TIME_SMOOTHING_TYPE_SPEECH;
self->postfiltering_enabled = POSTFILTER_ENABLED_SPEECH;
self->whitening_enabled = WHITENING_ENABLED_SPEECH;
self->gain_spectrum = (float*)calloc(self->fft_size, sizeof(float));
if (!self->gain_spectrum) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
(void)initialize_spectrum_with_value(self->gain_spectrum, self->fft_size,
1.F);
self->alpha = (float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->alpha) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
(void)initialize_spectrum_with_value(self->alpha, self->real_spectrum_size,
1.F);
self->beta = (float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->beta) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
self->noise_profile = (float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->noise_profile) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
self->adaptive_estimator = louizou_estimator_initialize(
self->real_spectrum_size, sample_rate, fft_size);
if (!self->adaptive_estimator) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
self->residual_spectrum = (float*)calloc((self->fft_size), sizeof(float));
if (!self->residual_spectrum) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
self->denoised_spectrum = (float*)calloc((self->fft_size), sizeof(float));
if (!self->denoised_spectrum) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
if (self->postfiltering_enabled) {
self->postfiltering = postfilter_initialize(self->fft_size);
if (!self->postfiltering) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
}
self->spectrum_smoothing =
spectral_smoothing_initialize(self->fft_size, self->time_smoothing_type);
if (!self->spectrum_smoothing) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
self->noise_scaling_criteria = noise_scaling_criterias_initialize(
self->fft_size, self->band_type, self->sample_rate, self->spectrum_type);
if (!self->noise_scaling_criteria) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
self->spectral_features =
spectral_features_initialize(self->real_spectrum_size);
if (!self->spectral_features) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
self->mixer =
denoise_mixer_initialize(self->fft_size, self->sample_rate, self->hop);
if (!self->mixer) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
self->noise_floor_manager = noise_floor_manager_initialize(
self->fft_size, self->sample_rate, self->hop);
if (!self->noise_floor_manager) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
return self;
}
void spectral_adaptive_denoiser_free(SpectralProcessorHandle instance) {
SpectralAdaptiveDenoiser* self = (SpectralAdaptiveDenoiser*)instance;
if (!self) {
return;
}
if (self->adaptive_estimator) {
if (self->parameters.noise_estimation_method == SPP_MMSE_METHOD) {
spp_mmse_estimator_free(self->adaptive_estimator);
} else {
louizou_estimator_free(self->adaptive_estimator);
}
}
if (self->spectral_features) {
spectral_features_free(self->spectral_features);
}
if (self->noise_scaling_criteria) {
noise_scaling_criterias_free(self->noise_scaling_criteria);
}
if (self->spectrum_smoothing) {
spectral_smoothing_free(self->spectrum_smoothing);
}
if (self->postfiltering) {
postfilter_free(self->postfiltering);
}
if (self->mixer) {
denoise_mixer_free(self->mixer);
}
if (self->residual_spectrum) {
free(self->residual_spectrum);
}
if (self->noise_floor_manager) {
noise_floor_manager_free(self->noise_floor_manager);
}
if (self->denoised_spectrum) {
free(self->denoised_spectrum);
}
if (self->noise_profile) {
free(self->noise_profile);
}
if (self->gain_spectrum) {
free(self->gain_spectrum);
}
if (self->alpha) {
free(self->alpha);
}
if (self->beta) {
free(self->beta);
}
free(self);
}
bool load_adaptive_reduction_parameters(SpectralProcessorHandle instance,
AdaptiveDenoiserParameters parameters) {
if (!instance) {
return false;
}
SpectralAdaptiveDenoiser* self = (SpectralAdaptiveDenoiser*)instance;
// Check if noise estimation method has changed
bool method_changed = (self->parameters.noise_estimation_method !=
parameters.noise_estimation_method);
self->parameters = parameters;
// If method changed, reinitialize the adaptive estimator
if (method_changed && self->adaptive_estimator) {
louizou_estimator_free(self->adaptive_estimator);
self->adaptive_estimator = NULL;
// Initialize the appropriate estimator based on the method
if (self->parameters.noise_estimation_method == SPP_MMSE_METHOD) {
self->adaptive_estimator = spp_mmse_estimator_initialize(
self->real_spectrum_size, self->sample_rate, self->fft_size);
} else {
// Default to Louizou method
self->adaptive_estimator = louizou_estimator_initialize(
self->real_spectrum_size, self->sample_rate, self->fft_size);
}
if (!self->adaptive_estimator) {
return false;
}
}
return true;
}
bool spectral_adaptive_denoiser_run(SpectralProcessorHandle instance,
float* fft_spectrum) {
if (!fft_spectrum || !instance) {
return false;
}
SpectralAdaptiveDenoiser* self = (SpectralAdaptiveDenoiser*)instance;
float* reference_spectrum =
get_spectral_feature(self->spectral_features, fft_spectrum,
self->fft_size, self->spectrum_type);
// Estimate noise using the selected method
if (self->parameters.noise_estimation_method == SPP_MMSE_METHOD) {
spp_mmse_estimator_run(self->adaptive_estimator, reference_spectrum,
self->noise_profile);
} else {
// Default to Louizou method
louizou_estimator_run(self->adaptive_estimator, reference_spectrum,
self->noise_profile);
}
float whitening_factor =
self->whitening_enabled ? self->parameters.whitening_factor : 0.0f;
// Scale estimated noise profile for oversubtraction
NoiseScalingParameters oversubtraction_parameters = (NoiseScalingParameters){
.oversubtraction =
self->default_oversubtraction + self->parameters.noise_rescale,
.undersubtraction = self->parameters.reduction_amount,
.scaling_type = self->parameters.noise_scaling_type,
};
apply_noise_scaling_criteria(self->noise_scaling_criteria, reference_spectrum,
self->noise_profile, self->alpha, self->beta,
oversubtraction_parameters);
TimeSmoothingParameters spectral_smoothing_parameters =
(TimeSmoothingParameters){
.smoothing = self->parameters.smoothing_factor,
};
spectral_smoothing_run(self->spectrum_smoothing,
spectral_smoothing_parameters, reference_spectrum);
estimate_gains(self->real_spectrum_size, self->fft_size, reference_spectrum,
self->noise_profile, self->gain_spectrum, self->alpha,
self->beta, self->gain_estimation_type);
noise_floor_manager_apply(
self->noise_floor_manager, self->real_spectrum_size, self->fft_size,
self->gain_spectrum, self->noise_profile,
self->parameters.reduction_amount, whitening_factor);
if (self->postfiltering_enabled) {
PostFiltersParameters post_filter_parameters = (PostFiltersParameters){
.snr_threshold = self->parameters.post_filter_threshold,
.gain_floor = self->parameters.reduction_amount,
};
postfilter_apply(self->postfiltering, fft_spectrum, self->gain_spectrum,
post_filter_parameters);
}
DenoiseMixerParameters mixer_parameters = (DenoiseMixerParameters){
.noise_level = self->parameters.reduction_amount,
.residual_listen = self->parameters.residual_listen,
.whitening_amount = whitening_factor,
};
denoise_mixer_run(self->mixer, fft_spectrum, self->gain_spectrum,
mixer_parameters);
return true;
}
@@ -0,0 +1,54 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECTRAL_ADAPTIVE_DENOISER_H
#define SPECTRAL_ADAPTIVE_DENOISER_H
#include "shared/spectral_processor.h"
#include <stdbool.h>
#include <stdint.h>
#include "shared/noise_estimation/adaptive_noise_estimator.h"
typedef struct AdaptiveDenoiserParameters {
float reduction_amount;
int noise_scaling_type;
float noise_rescale;
float smoothing_factor;
float whitening_factor;
float post_filter_threshold;
bool residual_listen;
/* Method used for adaptive noise estimation.
* LOUIZOU_METHOD uses minimum statistics (default), SPP_MMSE_METHOD uses
* Speech Presence Probability with MMSE estimation for lower complexity
* and unbiased noise tracking. */
AdaptiveNoiseEstimationMethod noise_estimation_method;
} AdaptiveDenoiserParameters;
SpectralProcessorHandle spectral_adaptive_denoiser_initialize(
uint32_t sample_rate, uint32_t fft_size, uint32_t overlap_factor);
void spectral_adaptive_denoiser_free(SpectralProcessorHandle instance);
bool load_adaptive_reduction_parameters(SpectralProcessorHandle instance,
AdaptiveDenoiserParameters parameters);
bool spectral_adaptive_denoiser_run(SpectralProcessorHandle instance,
float* fft_spectrum);
#endif
@@ -0,0 +1,317 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "spectral_denoiser.h"
#include "shared/configurations.h"
#include "shared/gain_estimation/gain_estimators.h"
#include "shared/noise_estimation/noise_estimator.h"
#include "shared/post_estimation/noise_floor_manager.h"
#include "shared/post_estimation/postfilter.h"
#include "shared/pre_estimation/critical_bands.h"
#include "shared/pre_estimation/noise_scaling_criterias.h"
#include "shared/pre_estimation/spectral_smoother.h"
#include "shared/utils/denoise_mixer.h"
#include "shared/utils/spectral_features.h"
#include "shared/utils/spectral_utils.h"
#include <float.h>
#include <stdlib.h>
#include <string.h>
typedef struct SbSpectralDenoiser {
uint32_t fft_size;
uint32_t real_spectrum_size;
uint32_t sample_rate;
uint32_t hop;
float default_oversubtraction;
float default_undersubtraction;
float* gain_spectrum;
float* alpha;
float* beta;
float* noise_spectrum;
SpectrumType spectrum_type;
CriticalBandType band_type;
DenoiserParameters denoise_parameters;
GainEstimationType gain_estimation_type;
TimeSmoothingType time_smoothing_type;
NoiseEstimatorType noise_estimator_type;
NoiseEstimator* noise_estimator;
PostFilter* postfiltering;
NoiseProfile* noise_profile;
SpectralFeatures* spectral_features;
DenoiseMixer* mixer;
NoiseScalingCriterias* noise_scaling_criteria;
SpectralSmoother* spectrum_smoothing;
NoiseFloorManager* noise_floor_manager;
bool postfiltering_enabled;
bool whitening_enabled;
} SbSpectralDenoiser;
SpectralProcessorHandle spectral_denoiser_initialize(
const uint32_t sample_rate, const uint32_t fft_size,
const uint32_t overlap_factor, NoiseProfile* noise_profile) {
if (!noise_profile || sample_rate == 0 || fft_size == 0 ||
overlap_factor == 0) {
return NULL;
}
SbSpectralDenoiser* self =
(SbSpectralDenoiser*)calloc(1U, sizeof(SbSpectralDenoiser));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->hop = self->fft_size / overlap_factor;
self->sample_rate = sample_rate;
self->spectrum_type = SPECTRAL_TYPE_GENERAL;
self->band_type = CRITICAL_BANDS_TYPE;
self->default_oversubtraction = DEFAULT_OVERSUBTRACTION;
self->default_undersubtraction = DEFAULT_UNDERSUBTRACTION;
self->gain_estimation_type = GAIN_ESTIMATION_TYPE;
self->time_smoothing_type = TIME_SMOOTHING_TYPE;
self->postfiltering_enabled = POSTFILTER_ENABLED_GENERAL;
self->whitening_enabled = WHITENING_ENABLED_GENERAL;
self->gain_spectrum = (float*)calloc(self->fft_size, sizeof(float));
if (!self->gain_spectrum) {
spectral_denoiser_free(self);
return NULL;
}
(void)initialize_spectrum_with_value(self->gain_spectrum, self->fft_size,
1.F);
self->alpha = (float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->alpha) {
spectral_denoiser_free(self);
return NULL;
}
(void)initialize_spectrum_with_value(self->alpha, self->real_spectrum_size,
1.F);
self->beta = (float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->beta) {
spectral_denoiser_free(self);
return NULL;
}
self->noise_profile = noise_profile;
self->noise_spectrum =
(float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->noise_spectrum) {
spectral_denoiser_free(self);
return NULL;
}
self->noise_estimator =
noise_estimation_initialize(self->fft_size, noise_profile);
if (!self->noise_estimator) {
spectral_denoiser_free(self);
return NULL;
}
self->spectral_features =
spectral_features_initialize(self->real_spectrum_size);
if (!self->spectral_features) {
spectral_denoiser_free(self);
return NULL;
}
if (self->postfiltering_enabled) {
self->postfiltering = postfilter_initialize(self->fft_size);
if (!self->postfiltering) {
spectral_denoiser_free(self);
return NULL;
}
}
self->spectrum_smoothing =
spectral_smoothing_initialize(self->fft_size, self->time_smoothing_type);
if (!self->spectrum_smoothing) {
spectral_denoiser_free(self);
return NULL;
}
self->noise_scaling_criteria = noise_scaling_criterias_initialize(
self->fft_size, self->band_type, self->sample_rate, self->spectrum_type);
if (!self->noise_scaling_criteria) {
spectral_denoiser_free(self);
return NULL;
}
self->mixer =
denoise_mixer_initialize(self->fft_size, self->sample_rate, self->hop);
if (!self->mixer) {
spectral_denoiser_free(self);
return NULL;
}
self->noise_floor_manager = noise_floor_manager_initialize(
self->fft_size, self->sample_rate, self->hop);
if (!self->noise_floor_manager) {
spectral_denoiser_free(self);
return NULL;
}
return self;
}
void spectral_denoiser_free(SpectralProcessorHandle instance) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self) {
return;
}
// Don't free noise profile used as reference here
if (self->noise_estimator) {
noise_estimation_free(self->noise_estimator);
}
if (self->spectral_features) {
spectral_features_free(self->spectral_features);
}
if (self->spectrum_smoothing) {
spectral_smoothing_free(self->spectrum_smoothing);
}
if (self->noise_scaling_criteria) {
noise_scaling_criterias_free(self->noise_scaling_criteria);
}
if (self->postfiltering) {
postfilter_free(self->postfiltering);
}
if (self->mixer) {
denoise_mixer_free(self->mixer);
}
if (self->gain_spectrum) {
free(self->gain_spectrum);
}
if (self->noise_floor_manager) {
noise_floor_manager_free(self->noise_floor_manager);
}
if (self->alpha) {
free(self->alpha);
}
if (self->beta) {
free(self->beta);
}
if (self->noise_spectrum) {
free(self->noise_spectrum);
}
free(self);
}
bool load_reduction_parameters(SpectralProcessorHandle instance,
DenoiserParameters parameters) {
if (!instance) {
return false;
}
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
self->denoise_parameters = parameters;
return true;
}
bool spectral_denoiser_run(SpectralProcessorHandle instance,
float* fft_spectrum) {
if (!fft_spectrum || !instance) {
return false;
}
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
float* reference_spectrum =
get_spectral_feature(self->spectral_features, fft_spectrum,
self->fft_size, self->spectrum_type);
if (self->denoise_parameters.learn_noise > 0) {
// Learn all modes simultaneously
for (int mode = ROLLING_MEAN; mode <= MAX; mode++) {
noise_estimation_run(self->noise_estimator, (NoiseEstimatorType)mode,
reference_spectrum);
}
} else if (is_noise_estimation_available(
self->noise_profile,
self->denoise_parameters.noise_reduction_mode)) {
memcpy(self->noise_spectrum,
get_noise_profile(self->noise_profile,
self->denoise_parameters.noise_reduction_mode),
self->real_spectrum_size * sizeof(float));
NoiseScalingParameters oversubtraction_parameters =
(NoiseScalingParameters){
.oversubtraction = (self->default_oversubtraction +
self->denoise_parameters.noise_rescale),
.undersubtraction = self->denoise_parameters.reduction_amount,
.scaling_type = self->denoise_parameters.noise_scaling_type,
};
float whitening_factor = self->whitening_enabled
? self->denoise_parameters.whitening_factor
: 0.0f;
apply_noise_scaling_criteria(
self->noise_scaling_criteria, reference_spectrum, self->noise_spectrum,
self->alpha, self->beta, oversubtraction_parameters);
TimeSmoothingParameters spectral_smoothing_parameters =
(TimeSmoothingParameters){
.smoothing = self->denoise_parameters.smoothing_factor,
};
spectral_smoothing_run(self->spectrum_smoothing,
spectral_smoothing_parameters, reference_spectrum);
estimate_gains(self->real_spectrum_size, self->fft_size, reference_spectrum,
self->noise_spectrum, self->gain_spectrum, self->alpha,
self->beta, self->gain_estimation_type);
noise_floor_manager_apply(
self->noise_floor_manager, self->real_spectrum_size, self->fft_size,
self->gain_spectrum, self->noise_spectrum,
self->denoise_parameters.reduction_amount, whitening_factor);
if (self->postfiltering_enabled) {
PostFiltersParameters post_filter_parameters = (PostFiltersParameters){
.snr_threshold = self->denoise_parameters.post_filter_threshold,
.gain_floor = self->denoise_parameters.reduction_amount,
};
postfilter_apply(self->postfiltering, fft_spectrum, self->gain_spectrum,
post_filter_parameters);
}
DenoiseMixerParameters mixer_parameters = (DenoiseMixerParameters){
.noise_level = self->denoise_parameters.reduction_amount,
.residual_listen = self->denoise_parameters.residual_listen,
.whitening_amount = whitening_factor,
};
denoise_mixer_run(self->mixer, fft_spectrum, self->gain_spectrum,
mixer_parameters);
}
return true;
}
@@ -0,0 +1,50 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECTRAL_DENOISER_H
#define SPECTRAL_DENOISER_H
#include "shared/noise_estimation/noise_profile.h"
#include "shared/spectral_processor.h"
#include <stdbool.h>
#include <stdint.h>
typedef struct DenoiserParameters {
float reduction_amount;
int noise_scaling_type;
float noise_rescale;
bool residual_listen;
int learn_noise;
int noise_reduction_mode;
float smoothing_factor;
float whitening_factor;
float post_filter_threshold;
} DenoiserParameters;
SpectralProcessorHandle spectral_denoiser_initialize(
uint32_t sample_rate, uint32_t fft_size, uint32_t overlap_factor,
NoiseProfile* noise_profile);
void spectral_denoiser_free(SpectralProcessorHandle instance);
bool load_reduction_parameters(SpectralProcessorHandle instance,
DenoiserParameters parameters);
bool spectral_denoiser_run(SpectralProcessorHandle instance,
float* fft_spectrum);
#endif
+6
View File
@@ -0,0 +1,6 @@
processors_sources = files(
'denoiser/spectral_denoiser.c',
'adaptivedenoiser/adaptive_denoiser.c',
'specbleach_adenoiser.c',
'specbleach_denoiser.c',
)
@@ -0,0 +1,137 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "specbleach_adenoiser.h"
#include "adaptivedenoiser/adaptive_denoiser.h"
#include "shared/configurations.h"
#include "shared/stft/stft_processor.h"
#include "shared/utils/general_utils.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
typedef struct SbAdaptiveDenoiser {
uint32_t sample_rate;
AdaptiveDenoiserParameters denoise_parameters;
SpectralProcessorHandle adaptive_spectral_denoiser;
StftProcessor* stft_processor;
} SbAdaptiveDenoiser;
SpectralBleachHandle specbleach_adaptive_initialize(const uint32_t sample_rate,
float frame_size) {
SbAdaptiveDenoiser* self =
(SbAdaptiveDenoiser*)calloc(1U, sizeof(SbAdaptiveDenoiser));
if (!self) {
return NULL;
}
self->sample_rate = sample_rate;
self->stft_processor = stft_processor_initialize(
sample_rate, frame_size, OVERLAP_FACTOR_SPEECH,
PADDING_CONFIGURATION_SPEECH, ZEROPADDING_AMOUNT_SPEECH,
INPUT_WINDOW_TYPE_SPEECH, OUTPUT_WINDOW_TYPE_SPEECH);
if (!self->stft_processor) {
specbleach_adaptive_free(self);
return NULL;
}
const uint32_t fft_size = get_stft_fft_size(self->stft_processor);
self->adaptive_spectral_denoiser = spectral_adaptive_denoiser_initialize(
self->sample_rate, fft_size, OVERLAP_FACTOR_SPEECH);
if (!self->adaptive_spectral_denoiser) {
specbleach_adaptive_free(self);
return NULL;
}
return self;
}
void specbleach_adaptive_free(SpectralBleachHandle instance) {
SbAdaptiveDenoiser* self = (SbAdaptiveDenoiser*)instance;
if (!self) {
return;
}
if (self->adaptive_spectral_denoiser) {
spectral_adaptive_denoiser_free(self->adaptive_spectral_denoiser);
}
if (self->stft_processor) {
stft_processor_free(self->stft_processor);
}
free(self);
}
uint32_t specbleach_adaptive_get_latency(SpectralBleachHandle instance) {
SbAdaptiveDenoiser* self = (SbAdaptiveDenoiser*)instance;
return get_stft_latency(self->stft_processor);
}
bool specbleach_adaptive_process(SpectralBleachHandle instance,
const uint32_t number_of_samples,
const float* input, float* output) {
if (!instance || number_of_samples == 0 || !input || !output) {
return false;
}
SbAdaptiveDenoiser* self = (SbAdaptiveDenoiser*)instance;
stft_processor_run(self->stft_processor, number_of_samples, input, output,
&spectral_adaptive_denoiser_run,
self->adaptive_spectral_denoiser);
return true;
}
bool specbleach_adaptive_load_parameters(
SpectralBleachHandle instance,
SpectralBleachAdaptiveParameters parameters) {
if (!instance) {
return false;
}
SbAdaptiveDenoiser* self = (SbAdaptiveDenoiser*)instance;
// clang-format off
self->denoise_parameters = (AdaptiveDenoiserParameters){
.residual_listen = parameters.residual_listen,
.reduction_amount =
from_db_to_coefficient(parameters.reduction_amount * -1.F),
.noise_rescale = from_db_to_coefficient(parameters.noise_rescale),
.noise_scaling_type = parameters.noise_scaling_type,
.smoothing_factor = remap_percentage_log_like_unity(parameters.smoothing_factor / 100.F),
.whitening_factor = parameters.whitening_factor / 100.F,
.post_filter_threshold = from_db_to_coefficient(parameters.post_filter_threshold),
.noise_estimation_method = parameters.noise_estimation_method,
};
// clang-format on
load_adaptive_reduction_parameters(self->adaptive_spectral_denoiser,
self->denoise_parameters);
return true;
}
Binary file not shown.
@@ -0,0 +1,277 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "specbleach_denoiser.h"
#include "denoiser/spectral_denoiser.h"
#include "shared/configurations.h"
#include "shared/noise_estimation/noise_profile.h"
#include "shared/stft/stft_processor.h"
#include "shared/utils/general_utils.h"
#include <stdlib.h>
#include <string.h>
typedef struct SbSpectralDenoiser {
uint32_t sample_rate;
DenoiserParameters denoise_parameters;
NoiseProfile* noise_profile;
SpectralProcessorHandle spectral_denoiser;
StftProcessor* stft_processor;
} SbSpectralDenoiser;
SpectralBleachHandle specbleach_initialize(const uint32_t sample_rate,
float frame_size) {
if (sample_rate < 4000 || sample_rate > 192000 || frame_size <= 0.0f) {
return NULL;
}
SbSpectralDenoiser* self =
(SbSpectralDenoiser*)calloc(1U, sizeof(SbSpectralDenoiser));
if (!self) {
return NULL;
}
self->sample_rate = sample_rate;
self->stft_processor = stft_processor_initialize(
sample_rate, frame_size, OVERLAP_FACTOR_GENERAL,
PADDING_CONFIGURATION_GENERAL, ZEROPADDING_AMOUNT_GENERAL,
INPUT_WINDOW_TYPE_GENERAL, OUTPUT_WINDOW_TYPE_GENERAL);
if (!self->stft_processor) {
specbleach_free(self);
return NULL;
}
const uint32_t fft_size = get_stft_fft_size(self->stft_processor);
const uint32_t real_spectrum_size =
get_stft_real_spectrum_size(self->stft_processor);
self->noise_profile = noise_profile_initialize(real_spectrum_size);
if (!self->noise_profile) {
specbleach_free(self);
return NULL;
}
self->spectral_denoiser = spectral_denoiser_initialize(
self->sample_rate, fft_size, OVERLAP_FACTOR_GENERAL, self->noise_profile);
if (!self->spectral_denoiser) {
specbleach_free(self);
return NULL;
}
return self;
}
void specbleach_free(SpectralBleachHandle instance) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self) {
return;
}
if (self->noise_profile) {
noise_profile_free(self->noise_profile);
}
if (self->spectral_denoiser) {
spectral_denoiser_free(self->spectral_denoiser);
}
if (self->stft_processor) {
stft_processor_free(self->stft_processor);
}
free(self);
}
uint32_t specbleach_get_latency(SpectralBleachHandle instance) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self || !self->stft_processor) {
return 0;
}
return get_stft_latency(self->stft_processor);
}
bool specbleach_process(SpectralBleachHandle instance,
const uint32_t number_of_samples, const float* input,
float* output) {
if (!instance || number_of_samples == 0 || !input || !output) {
return false;
}
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
stft_processor_run(self->stft_processor, number_of_samples, input, output,
&spectral_denoiser_run, self->spectral_denoiser);
return true;
}
uint32_t specbleach_get_noise_profile_size(SpectralBleachHandle instance) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self || !self->noise_profile) {
return 0;
}
return get_noise_profile_size(self->noise_profile);
}
uint32_t specbleach_get_noise_profile_blocks_averaged(
SpectralBleachHandle instance) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self || !self->noise_profile) {
return 0;
}
return get_noise_profile_blocks_averaged(
self->noise_profile, self->denoise_parameters.noise_reduction_mode);
}
float* specbleach_get_noise_profile(SpectralBleachHandle instance) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self || !self->noise_profile) {
return NULL;
}
return get_noise_profile(self->noise_profile,
self->denoise_parameters.noise_reduction_mode);
}
bool specbleach_load_noise_profile(SpectralBleachHandle instance,
const float* restored_profile,
const uint32_t profile_size,
const uint32_t averaged_blocks) {
if (!instance || !restored_profile) {
return false;
}
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (profile_size != get_noise_profile_size(self->noise_profile)) {
return false;
}
set_noise_profile(self->noise_profile,
self->denoise_parameters.noise_reduction_mode,
restored_profile, profile_size, averaged_blocks);
return true;
}
bool specbleach_load_noise_profile_for_mode(SpectralBleachHandle instance,
const float* restored_profile,
const uint32_t profile_size,
const uint32_t averaged_blocks,
const int mode) {
if (!instance || !restored_profile || mode < 1 || mode > 3) {
return false;
}
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (profile_size != get_noise_profile_size(self->noise_profile)) {
return false;
}
set_noise_profile(self->noise_profile, mode, restored_profile, profile_size,
averaged_blocks);
return true;
}
bool specbleach_reset_noise_profile(SpectralBleachHandle instance) {
if (!instance) {
return false;
}
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
reset_noise_profile(self->noise_profile);
return true;
}
bool specbleach_noise_profile_available(SpectralBleachHandle instance) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
return is_noise_estimation_available(
self->noise_profile, self->denoise_parameters.noise_reduction_mode);
}
uint32_t specbleach_get_noise_profile_blocks_averaged_for_mode(
SpectralBleachHandle instance, int mode) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self || mode < 1 || mode > 3) {
return 0;
}
return get_noise_profile_blocks_averaged(self->noise_profile, mode);
}
float* specbleach_get_noise_profile_for_mode(SpectralBleachHandle instance,
int mode) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self || mode < 1 || mode > 3) {
return NULL;
}
return get_noise_profile(self->noise_profile, mode);
}
bool specbleach_noise_profile_available_for_mode(SpectralBleachHandle instance,
int mode) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self || mode < 1 || mode > 3) {
return false;
}
return is_noise_estimation_available(self->noise_profile, mode);
}
bool specbleach_load_parameters(SpectralBleachHandle instance,
SpectralBleachDenoiserParameters parameters) {
if (!instance) {
return false;
}
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
// clang-format off
self->denoise_parameters = (DenoiserParameters){
.learn_noise = parameters.learn_noise,
.noise_reduction_mode = parameters.noise_reduction_mode,
.residual_listen = parameters.residual_listen,
.noise_scaling_type = parameters.noise_scaling_type,
.reduction_amount =
from_db_to_coefficient(parameters.reduction_amount * -1.F),
.noise_rescale = from_db_to_coefficient(parameters.noise_rescale),
.smoothing_factor = remap_percentage_log_like_unity(parameters.smoothing_factor / 100.F),
.whitening_factor = parameters.whitening_factor / 100.F,
.post_filter_threshold = from_db_to_coefficient(parameters.post_filter_threshold),
};
// clang-format on
load_reduction_parameters(self->spectral_denoiser, self->denoise_parameters);
return true;
}
Binary file not shown.
+188
View File
@@ -0,0 +1,188 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef MODULES_CONFIGURATIONS_H
#define MODULES_CONFIGURATIONS_H
#include "utils/spectral_utils.h"
#include <stdbool.h>
#include <stdint.h>
// Compile-time assertions for configuration validity
_Static_assert(HANN_WINDOW >= 0 && HANN_WINDOW <= 3,
"HANN_WINDOW must be between 0 and 3");
_Static_assert(HAMMING_WINDOW >= 0 && HAMMING_WINDOW <= 3,
"HAMMING_WINDOW must be between 0 and 3");
_Static_assert(BLACKMAN_WINDOW >= 0 && BLACKMAN_WINDOW <= 3,
"BLACKMAN_WINDOW must be between 0 and 3");
_Static_assert(VORBIS_WINDOW >= 0 && VORBIS_WINDOW <= 3,
"VORBIS_WINDOW must be between 0 and 3");
// Additional C17 compile-time validations
_Static_assert(sizeof(uint32_t) == 4, "uint32_t must be exactly 32 bits");
#ifndef M_PI
#define M_PI (3.14159265358979323846)
#endif
#ifndef M_PIf
#define M_PIf (3.14159265358979323846F)
#endif
/* --------------------------------------------------------------------- */
/* ------------------- Shared Modules configurations ------------------- */
/* --------------------------------------------------------------------- */
// Absolute hearing thresholds
#define REFERENCE_SINE_WAVE_FREQ (1000.F)
#define REFERENCE_LEVEL (90.F)
#define SINE_AMPLITUDE (1.F)
// Spectral Whitening
#define WHITENING_DECAY_RATE (1000.F)
#define WHITENING_FLOOR (0.01F)
// Masking Thresholds
#define BIAS false
#define HIGH_FREQ_BIAS 20.F
#if BIAS
// clang-format off
#define relative_thresholds \
(float[25]){-16.F, -17.F, -18.F, -19.F, -20.F, -21.F, -22.F, -23.F, -24.F, \
-25.F, -25.F, -25.F, -25.F, -25.F, -25.F, -24.F, -23.F, -22.F, \
-19.F, -18.F, -18.F, -18.F, -18.F, -18.F, -18.F}
// clang-format on
#endif
// Postfilter SNR Threshold
#define POSTFILTER_SCALE (10.0F)
#define PRESERVE_MINIMUN_GAIN (true)
#define POSTFILTER_MIN_GAIN_DB (-15.0F)
// Gain Estimators
#define GSS_EXPONENT \
2.0F // 2 Power Subtraction / 1 Magnitude Subtraxtion / 0.5 Spectral
// Subtraction
// Oversubtraction criteria
#define ALPHA_MAX (6.F)
#define ALPHA_MIN (1.F)
#define BETA_MAX (0.01F)
#define BETA_MIN (0.F)
#define DEFAULT_OVERSUBTRACTION (ALPHA_MIN)
#define DEFAULT_UNDERSUBTRACTION (BETA_MAX)
#define LOWER_SNR (0.F)
#define HIGHER_SNR (20.F)
// Adaptive Estimator
#define N_SMOOTH (0.7F)
#define BETA_AT (0.8F)
#define GAMMA (0.998F)
#define ALPHA_P (0.2F)
#define ALPHA_D (0.85F)
#define CROSSOVER_POINT1 (1000.F)
#define CROSSOVER_POINT2 (3000.F)
#define BAND_1_LEVEL (2.F)
#define BAND_2_LEVEL (2.F)
#define BAND_3_LEVEL (5.F)
// SPP-MMSE Estimator Constants
#define SPP_PRIOR_H1 (0.5F) // P(H1) - Speech present prior
#define SPP_PRIOR_H0 (0.5F) // P(H0) - Speech absent prior
#define SPP_FIXED_XI_H1 (31.62F) // Fixed a priori SNR (15 dB in linear)
#define SPP_ALPHA_POW (0.8F) // Power spectrum smoothing factor
#define SPP_SMOOTH_SPP (0.9F) // SPP smoothing for stagnation control
#define SPP_CURRENT_SPP (0.1F) // Current SPP weighting for stagnation control
#define SPP_STAGNATION_CAP (0.99F) // Maximum SPP value to prevent locking
/* --------------------------------------------------------------- */
/* ------------------- Denoiser configurations ------------------- */
/* --------------------------------------------------------------- */
// STFT configurations - Frame size in milliseconds
#define OVERLAP_FACTOR_GENERAL 4
#define INPUT_WINDOW_TYPE_GENERAL HANN_WINDOW
#define OUTPUT_WINDOW_TYPE_GENERAL HANN_WINDOW
// Fft configuration
#define PADDING_CONFIGURATION_GENERAL NO_PADDING
#define ZEROPADDING_AMOUNT_GENERAL 50 // Even Number
// Spectral Type
#define SPECTRAL_TYPE_GENERAL POWER_SPECTRUM
// Transient protection
#define UPPER_LIMIT (5.F)
#define DEFAULT_TRANSIENT_THRESHOLD (2.F)
// Masking
#define CRITICAL_BANDS_TYPE OPUS_SCALE
// Noise Estimator
#define MIN_NUMBER_OF_WINDOWS_NOISE_AVERAGED 5
#define NUMBER_OF_MEDIAN_SPECTRUM 5
// Noise Scaling strategy
#define NOISE_SCALING_TYPE_GENERAL MASKING_THRESHOLDS
#define GAIN_ESTIMATION_TYPE WIENER
// Time Smoothing
#define TIME_SMOOTHING_TYPE FIXED
// Postfilter
#define POSTFILTER_ENABLED_GENERAL true
// Whitening
#define WHITENING_ENABLED_GENERAL true
/* ------------------------------------------------------------------------ */
/* ------------------- Adaptive Denoiser configurations ------------------- */
/* ------------------------------------------------------------------------ */
// STFT configurations - Frame size in milliseconds
#define OVERLAP_FACTOR_SPEECH 2
#define INPUT_WINDOW_TYPE_SPEECH VORBIS_WINDOW
#define OUTPUT_WINDOW_TYPE_SPEECH VORBIS_WINDOW
// Fft configurations
#define PADDING_CONFIGURATION_SPEECH NO_PADDING
#define ZEROPADDING_AMOUNT_SPEECH 50 // Even Number
// Spectral Type
#define SPECTRAL_TYPE_SPEECH POWER_SPECTRUM
// Masking
#define CRITICAL_BANDS_TYPE_SPEECH OPUS_SCALE
// Noise Scaling strategy
#define NOISE_SCALING_TYPE_SPEECH MASKING_THRESHOLDS
#define GAIN_ESTIMATION_TYPE_SPEECH WIENER
// Time Smoothing
#define TIME_SMOOTHING_TYPE_SPEECH FIXED
// Postfilter
#define POSTFILTER_ENABLED_SPEECH true
// Whitening
#define WHITENING_ENABLED_SPEECH true
#endif // ifndef
@@ -0,0 +1,130 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gain_estimators.h"
#include "../configurations.h"
#include "../utils/general_utils.h"
#include <float.h>
#include <math.h>
#include <stddef.h>
static void wiener_subtraction(const uint32_t real_spectrum_size,
const uint32_t fft_size, const float* spectrum,
const float* noise_spectrum,
float* gain_spectrum) {
for (uint32_t k = 0U; k < real_spectrum_size; k++) {
if (noise_spectrum[k] > FLT_MIN) {
if (spectrum[k] > noise_spectrum[k]) {
gain_spectrum[k] = (spectrum[k] - (noise_spectrum[k])) / spectrum[k];
} else {
gain_spectrum[k] = 0.F;
}
} else {
gain_spectrum[k] = 1.F;
}
if (k > 0U && k < (fft_size - k)) {
gain_spectrum[fft_size - k] = gain_spectrum[k];
}
}
}
static void spectral_gating(const uint32_t real_spectrum_size,
const uint32_t fft_size, const float* spectrum,
const float* noise_spectrum, float* gain_spectrum) {
for (uint32_t k = 0U; k < real_spectrum_size; k++) {
if (noise_spectrum[k] > FLT_MIN) {
if (spectrum[k] >= noise_spectrum[k]) {
gain_spectrum[k] = 1.F;
} else {
gain_spectrum[k] = 0.F;
}
} else {
gain_spectrum[k] = 1.F;
}
if (k > 0U && k < (fft_size - k)) {
gain_spectrum[fft_size - k] = gain_spectrum[k];
}
}
}
static void generalized_spectral_subtraction(
const uint32_t real_spectrum_size, const uint32_t fft_size,
const float* spectrum, const float* noise_spectrum, float* gain_spectrum,
const float* alpha, const float* beta) {
for (uint32_t k = 0U; k < real_spectrum_size; k++) {
if (spectrum[k] > FLT_MIN) {
if (powf((noise_spectrum[k] / spectrum[k]), GSS_EXPONENT) <
(1.F / (alpha[k] + beta[k]))) {
gain_spectrum[k] =
fmaxf(powf(1.F - (alpha[k] * powf((noise_spectrum[k] / spectrum[k]),
GSS_EXPONENT)),
1.F / GSS_EXPONENT),
0.F);
} else {
gain_spectrum[k] = fmaxf(
powf(
beta[k] * powf((noise_spectrum[k] / spectrum[k]), GSS_EXPONENT),
1.F / GSS_EXPONENT),
0.F);
}
} else {
gain_spectrum[k] = 1.F;
}
if (k > 0U && k < (fft_size - k)) {
gain_spectrum[fft_size - k] = gain_spectrum[k];
}
}
}
static void scale_noise_profile(uint32_t real_spectrum_size,
float* noise_spectrum, const float* alpha) {
for (uint32_t k = 0U; k < real_spectrum_size; k++) {
noise_spectrum[k] *= alpha[k];
}
}
void estimate_gains(uint32_t real_spectrum_size, uint32_t fft_size,
const float* spectrum, float* noise_spectrum,
float* gain_spectrum, const float* alpha, const float* beta,
GainEstimationType type) {
switch (type) {
case GATES:
scale_noise_profile(real_spectrum_size, noise_spectrum, alpha);
spectral_gating(real_spectrum_size, fft_size, spectrum, noise_spectrum,
gain_spectrum);
break;
case WIENER:
scale_noise_profile(real_spectrum_size, noise_spectrum, alpha);
wiener_subtraction(real_spectrum_size, fft_size, spectrum, noise_spectrum,
gain_spectrum);
break;
case GENERALIZED_SPECTRALSUBTRACION:
generalized_spectral_subtraction(real_spectrum_size, fft_size, spectrum,
noise_spectrum, gain_spectrum, alpha,
beta);
break;
default:
break;
}
}
@@ -0,0 +1,38 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef GAIN_ESTIMATORS_H
#define GAIN_ESTIMATORS_H
#include <stdbool.h>
#include <stdint.h>
typedef enum GainEstimationType {
WIENER = 0,
GATES = 1,
GENERALIZED_SPECTRALSUBTRACION = 2,
} GainEstimationType;
void estimate_gains(uint32_t real_spectrum_size, uint32_t fft_size,
const float* spectrum, float* noise_spectrum,
float* gain_spectrum, const float* alpha, const float* beta,
GainEstimationType type);
#endif
@@ -0,0 +1,3 @@
shared_sources += files(
'gain_estimators.c',
)
+7
View File
@@ -0,0 +1,7 @@
shared_sources = []
subdir('gain_estimation')
subdir('noise_estimation')
subdir('post_estimation')
subdir('pre_estimation')
subdir('stft')
subdir('utils')
@@ -0,0 +1,401 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "adaptive_noise_estimator.h"
#include "../configurations.h"
#include "../utils/general_utils.h"
#include "../utils/spectral_utils.h"
#include <float.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
typedef struct FrameSpectrum {
float* smoothed_spectrum;
float* local_minimum_spectrum;
float* speech_present_probability_spectrum;
} FrameSpectrum;
static FrameSpectrum* frame_spectrum_initialize(uint32_t frame_size);
static void frame_spectrum_free(FrameSpectrum* self);
static void compute_auto_thresholds(AdaptiveNoiseEstimator* self,
uint32_t sample_rate,
uint32_t noise_spectrum_size,
uint32_t fft_size);
static void update_frame_spectums(AdaptiveNoiseEstimator* self,
const float* noise_spectrum);
// SPP-MMSE helper functions
static float compute_spp_probability(float observation_power,
float previous_noise_psd);
static float compute_mmse_noise_estimate(float spp_h1, float spp_h0,
float observation_power,
float previous_noise_psd);
struct AdaptiveNoiseEstimator {
uint32_t noise_spectrum_size;
float noisy_speech_ratio;
FrameSpectrum* current;
FrameSpectrum* previous;
float* minimum_detection_thresholds;
float* previous_noise_spectrum;
float* time_frequency_smoothing_constant;
uint32_t* speech_presence_detection;
bool is_first_frame;
// SPP-MMSE specific fields (optional, used when SPP method is selected)
float* spp_previous_noise_psd; // Previous noise PSD estimate
float* spp_smoothed_spp; // Smoothed SPP for stagnation control
};
// SPP-MMSE helper function implementations
static float compute_spp_probability(float observation_power,
float previous_noise_psd) {
// Avoid division by zero and ensure numerical stability
if (previous_noise_psd < 1e-12F) {
previous_noise_psd = 1e-12F;
}
// Compute the exponent: -(|y|^2 / σ_N²(l-1)) * (ξ_H1 / (1 + ξ_H1))
float ratio = observation_power / previous_noise_psd;
float exponent = -ratio * (SPP_FIXED_XI_H1 / (1.F + SPP_FIXED_XI_H1));
// Compute exp(exponent) with numerical stability check
float exp_term = expf(exponent);
if (!isfinite(exp_term)) {
exp_term = (exponent > 0.F) ? FLT_MAX : 0.F;
}
// Compute the ratio: P(H0)/P(H1) * (1 + ξ_H1) * exp(...)
// Since P(H0) = P(H1) = 0.5, P(H0)/P(H1) = 1
float denominator_ratio = (1.F + SPP_FIXED_XI_H1) * exp_term;
// Compute SPP: 1 / (1 + denominator_ratio)
float spp = 1.F / (1.F + denominator_ratio);
// Ensure SPP is in valid range [0, 1]
spp = fmaxf(0.F, fminf(1.F, spp));
return spp;
}
static float compute_mmse_noise_estimate(float spp_h1, float spp_h0,
float observation_power,
float previous_noise_psd) {
// MMSE estimate: E{|N|²|y} = P(H0|y) * |y|² + P(H1|y) * σ_N²(l-1)
return (spp_h0 * observation_power) + (spp_h1 * previous_noise_psd);
}
AdaptiveNoiseEstimator* louizou_estimator_initialize(
const uint32_t noise_spectrum_size, const uint32_t sample_rate,
const uint32_t fft_size) {
AdaptiveNoiseEstimator* self =
(AdaptiveNoiseEstimator*)calloc(1U, sizeof(AdaptiveNoiseEstimator));
if (!self) {
return NULL;
}
self->noise_spectrum_size = noise_spectrum_size;
self->minimum_detection_thresholds =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
self->time_frequency_smoothing_constant =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
self->speech_presence_detection =
(uint32_t*)calloc(self->noise_spectrum_size, sizeof(uint32_t));
self->previous_noise_spectrum =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
self->spp_previous_noise_psd =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
self->spp_smoothed_spp =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
if (!self->minimum_detection_thresholds ||
!self->time_frequency_smoothing_constant ||
!self->speech_presence_detection || !self->previous_noise_spectrum ||
!self->spp_previous_noise_psd || !self->spp_smoothed_spp) {
louizou_estimator_free(self);
return NULL;
}
compute_auto_thresholds(self, sample_rate, noise_spectrum_size, fft_size);
self->current = frame_spectrum_initialize(noise_spectrum_size);
self->previous = frame_spectrum_initialize(noise_spectrum_size);
if (!self->current || !self->previous) {
louizou_estimator_free(self);
return NULL;
}
self->noisy_speech_ratio = 0.F;
self->is_first_frame = true;
return self;
}
AdaptiveNoiseEstimator* spp_mmse_estimator_initialize(
const uint32_t noise_spectrum_size, const uint32_t sample_rate,
const uint32_t fft_size) {
AdaptiveNoiseEstimator* self =
(AdaptiveNoiseEstimator*)calloc(1U, sizeof(AdaptiveNoiseEstimator));
if (!self) {
return NULL;
}
self->noise_spectrum_size = noise_spectrum_size;
self->minimum_detection_thresholds =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
self->time_frequency_smoothing_constant =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
self->speech_presence_detection =
(uint32_t*)calloc(self->noise_spectrum_size, sizeof(uint32_t));
self->previous_noise_spectrum =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
self->spp_previous_noise_psd =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
self->spp_smoothed_spp =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
if (!self->minimum_detection_thresholds ||
!self->time_frequency_smoothing_constant ||
!self->speech_presence_detection || !self->previous_noise_spectrum ||
!self->spp_previous_noise_psd || !self->spp_smoothed_spp) {
spp_mmse_estimator_free(self);
return NULL;
}
compute_auto_thresholds(self, sample_rate, noise_spectrum_size, fft_size);
self->current = frame_spectrum_initialize(noise_spectrum_size);
self->previous = frame_spectrum_initialize(noise_spectrum_size);
if (!self->current || !self->previous) {
spp_mmse_estimator_free(self);
return NULL;
}
self->noisy_speech_ratio = 0.F;
self->is_first_frame = true;
return self;
}
void spp_mmse_estimator_free(AdaptiveNoiseEstimator* self) {
louizou_estimator_free(self); // Reuse the same cleanup logic
}
void louizou_estimator_free(AdaptiveNoiseEstimator* self) {
if (!self) {
return;
}
free(self->minimum_detection_thresholds);
free(self->time_frequency_smoothing_constant);
free(self->speech_presence_detection);
free(self->previous_noise_spectrum);
free(self->spp_previous_noise_psd);
free(self->spp_smoothed_spp);
frame_spectrum_free(self->current);
frame_spectrum_free(self->previous);
free(self);
}
bool louizou_estimator_run(AdaptiveNoiseEstimator* self, const float* spectrum,
float* noise_spectrum) {
if (!self || !spectrum || !noise_spectrum) {
return false;
}
if (self->is_first_frame) {
for (uint32_t k = 0U; k < self->noise_spectrum_size; k++) {
self->current->smoothed_spectrum[k] = spectrum[k];
self->current->local_minimum_spectrum[k] = spectrum[k];
noise_spectrum[k] = spectrum[k];
}
self->is_first_frame = false;
} else {
for (uint32_t k = 0U; k < self->noise_spectrum_size; k++) {
self->current->smoothed_spectrum[k] =
(N_SMOOTH * self->previous->smoothed_spectrum[k]) +
((1.F - N_SMOOTH) * spectrum[k]);
if (self->previous->local_minimum_spectrum[k] <
self->current->smoothed_spectrum[k]) {
self->current->local_minimum_spectrum[k] =
(GAMMA * self->previous->local_minimum_spectrum[k]) +
(((1.F - GAMMA) / (1.F - BETA_AT)) *
(self->current->smoothed_spectrum[k] -
(BETA_AT * self->previous->smoothed_spectrum[k])));
} else {
self->current->local_minimum_spectrum[k] =
self->current->smoothed_spectrum[k];
}
self->noisy_speech_ratio = sanitize_denormal(
self->current->smoothed_spectrum[k] /
(self->current->local_minimum_spectrum[k] + 1e-12F));
if (self->noisy_speech_ratio > self->minimum_detection_thresholds[k]) {
self->speech_presence_detection[k] = 1U;
} else {
self->speech_presence_detection[k] = 0U;
}
self->current->speech_present_probability_spectrum[k] =
(ALPHA_P * self->previous->speech_present_probability_spectrum[k]) +
((1.F - ALPHA_P) * (float)self->speech_presence_detection[k]);
self->time_frequency_smoothing_constant[k] =
ALPHA_D + ((1.F - ALPHA_D) *
self->current->speech_present_probability_spectrum[k]);
noise_spectrum[k] =
(self->time_frequency_smoothing_constant[k] *
self->previous_noise_spectrum[k]) +
((1.F - self->time_frequency_smoothing_constant[k]) * spectrum[k]);
}
}
update_frame_spectums(self, noise_spectrum);
return true;
}
bool spp_mmse_estimator_run(AdaptiveNoiseEstimator* self, const float* spectrum,
float* noise_spectrum) {
if (!self || !spectrum || !noise_spectrum) {
return false;
}
if (self->is_first_frame) {
// Initialize with first frame (assume noise-only)
for (uint32_t k = 0U; k < self->noise_spectrum_size; k++) {
self->spp_previous_noise_psd[k] = spectrum[k];
self->spp_smoothed_spp[k] = 0.F; // Initialize smoothed SPP to 0
noise_spectrum[k] = spectrum[k];
}
self->is_first_frame = false;
} else {
for (uint32_t k = 0U; k < self->noise_spectrum_size; k++) {
// Step 1: Compute A Posteriori Speech Presence Probability
float spp_h1 =
compute_spp_probability(spectrum[k], self->spp_previous_noise_psd[k]);
// Step 2: Apply stagnation control
// If smoothed SPP > 0.99, cap current SPP at 0.99 to allow noise update
if (self->spp_smoothed_spp[k] > SPP_STAGNATION_CAP) {
spp_h1 = fminf(spp_h1, SPP_STAGNATION_CAP);
}
float spp_h0 = 1.F - spp_h1;
// Step 3: Compute MMSE noise periodogram estimate
float mmse_noise_estimate = compute_mmse_noise_estimate(
spp_h1, spp_h0, spectrum[k], self->spp_previous_noise_psd[k]);
// Step 4: Temporal smoothing
// σ_N²(l) = α_pow * σ_N²(l-1) + (1 - α_pow) * E{|N|²|y}
noise_spectrum[k] = (SPP_ALPHA_POW * self->spp_previous_noise_psd[k]) +
((1.F - SPP_ALPHA_POW) * mmse_noise_estimate);
// Step 5: Update smoothed SPP for next frame's stagnation control
// P̄(l) = 0.9 * P̄(l-1) + 0.1 * P(H1|y)
self->spp_smoothed_spp[k] = (SPP_SMOOTH_SPP * self->spp_smoothed_spp[k]) +
(SPP_CURRENT_SPP * spp_h1);
// Step 6: Store current noise estimate for next frame
self->spp_previous_noise_psd[k] = noise_spectrum[k];
}
}
// Update frame spectrums (reuse existing infrastructure for compatibility)
update_frame_spectums(self, noise_spectrum);
return true;
}
static void update_frame_spectums(AdaptiveNoiseEstimator* self,
const float* noise_spectrum) {
memcpy(self->previous_noise_spectrum, noise_spectrum,
sizeof(float) * self->noise_spectrum_size);
memcpy(self->previous->local_minimum_spectrum,
self->current->local_minimum_spectrum,
sizeof(float) * self->noise_spectrum_size);
memcpy(self->previous->smoothed_spectrum, self->current->smoothed_spectrum,
sizeof(float) * self->noise_spectrum_size);
memcpy(self->previous->speech_present_probability_spectrum,
self->current->speech_present_probability_spectrum,
sizeof(float) * self->noise_spectrum_size);
}
static FrameSpectrum* frame_spectrum_initialize(const uint32_t frame_size) {
FrameSpectrum* self = (FrameSpectrum*)calloc(1U, sizeof(FrameSpectrum));
if (!self) {
return NULL;
}
self->smoothed_spectrum = (float*)calloc(frame_size, sizeof(float));
self->local_minimum_spectrum = (float*)calloc(frame_size, sizeof(float));
self->speech_present_probability_spectrum =
(float*)calloc(frame_size, sizeof(float));
if (!self->smoothed_spectrum || !self->local_minimum_spectrum ||
!self->speech_present_probability_spectrum) {
frame_spectrum_free(self);
return NULL;
}
(void)initialize_spectrum_with_value(self->local_minimum_spectrum, frame_size,
FLT_MIN);
return self;
}
static void frame_spectrum_free(FrameSpectrum* self) {
if (!self) {
return;
}
free(self->smoothed_spectrum);
free(self->local_minimum_spectrum);
free(self->speech_present_probability_spectrum);
free(self);
}
static void compute_auto_thresholds(AdaptiveNoiseEstimator* self,
const uint32_t sample_rate,
const uint32_t noise_spectrum_size,
const uint32_t fft_size) {
uint32_t lf = freq_to_fft_bin(CROSSOVER_POINT1, sample_rate, fft_size);
uint32_t mf = freq_to_fft_bin(CROSSOVER_POINT2, sample_rate, fft_size);
for (uint32_t k = 0U; k < noise_spectrum_size; k++) {
if (k <= lf) {
self->minimum_detection_thresholds[k] = BAND_1_LEVEL;
}
if (k > lf && k < mf) {
self->minimum_detection_thresholds[k] = BAND_2_LEVEL;
}
if (k >= mf) {
self->minimum_detection_thresholds[k] = BAND_3_LEVEL;
}
}
}
@@ -0,0 +1,48 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef ADAPTIVE_NOISE_ESTIMATOR_H
#define ADAPTIVE_NOISE_ESTIMATOR_H
#include <stdbool.h>
#include <stdint.h>
typedef enum AdaptiveNoiseEstimationMethod {
LOUIZOU_METHOD = 0, // Original minimum statistics method (default)
SPP_MMSE_METHOD = 1, // Speech Presence Probability - MMSE method
} AdaptiveNoiseEstimationMethod;
typedef struct AdaptiveNoiseEstimator AdaptiveNoiseEstimator;
AdaptiveNoiseEstimator* louizou_estimator_initialize(
uint32_t noise_spectrum_size, uint32_t sample_rate, uint32_t fft_size);
void louizou_estimator_free(AdaptiveNoiseEstimator* self);
bool louizou_estimator_run(AdaptiveNoiseEstimator* self, const float* spectrum,
float* noise_spectrum);
// SPP-MMSE based adaptive noise estimator (Real-Time Unbiased MMSE Noise PSD
// Tracking)
AdaptiveNoiseEstimator* spp_mmse_estimator_initialize(
uint32_t noise_spectrum_size, uint32_t sample_rate, uint32_t fft_size);
void spp_mmse_estimator_free(AdaptiveNoiseEstimator* self);
bool spp_mmse_estimator_run(AdaptiveNoiseEstimator* self, const float* spectrum,
float* noise_spectrum);
#endif
@@ -0,0 +1,5 @@
shared_sources += files(
'adaptive_noise_estimator.c',
'noise_estimator.c',
'noise_profile.c',
)
@@ -0,0 +1,110 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "noise_estimator.h"
#include "../configurations.h"
#include "../utils/spectral_trailing_buffer.h"
#include "../utils/spectral_utils.h"
#include <stdlib.h>
#include <string.h>
struct NoiseEstimator {
uint32_t fft_size;
uint32_t real_spectrum_size;
SpectralTrailingBuffer* median_buffer;
NoiseProfile* noise_profile;
};
NoiseEstimator* noise_estimation_initialize(const uint32_t fft_size,
NoiseProfile* noise_profile) {
NoiseEstimator* self = (NoiseEstimator*)calloc(1U, sizeof(NoiseEstimator));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->noise_profile = noise_profile;
self->median_buffer = spectral_trailing_buffer_initialize(
self->real_spectrum_size, NUMBER_OF_MEDIAN_SPECTRUM);
if (!self->median_buffer) {
noise_estimation_free(self);
return NULL;
}
return self;
}
void noise_estimation_free(NoiseEstimator* self) {
if (!self) {
return;
}
// Don't free noise profile used as reference here
spectral_trailing_buffer_free(self->median_buffer);
free(self);
}
bool noise_estimation_run(NoiseEstimator* self,
const NoiseEstimatorType noise_estimator_type,
float* signal_spectrum) {
if (!self || !signal_spectrum) {
return false;
}
float* noise_profile =
get_noise_profile(self->noise_profile, noise_estimator_type);
switch (noise_estimator_type) {
case ROLLING_MEAN:
get_rolling_mean_spectrum(noise_profile, signal_spectrum,
get_noise_profile_blocks_averaged(
self->noise_profile, noise_estimator_type),
self->real_spectrum_size);
increment_blocks_averaged(self->noise_profile, noise_estimator_type);
break;
case MEDIAN:
spectral_trailing_buffer_push_back(self->median_buffer, signal_spectrum);
bool is_valid_median = get_rolling_median_spectrum(
noise_profile, get_trailing_spectral_buffer(self->median_buffer),
get_spectrum_buffer_size(self->median_buffer),
get_spectrum_size(self->median_buffer));
if (is_valid_median) {
set_noise_profile_available(self->noise_profile, noise_estimator_type);
}
break;
case MAX:
(void)max_spectrum(noise_profile, signal_spectrum,
self->real_spectrum_size);
set_noise_profile_available(self->noise_profile, noise_estimator_type);
break;
default:
break;
}
return true;
}
@@ -0,0 +1,44 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef NOISE_ESTIMATOR_H
#define NOISE_ESTIMATOR_H
#include "noise_profile.h"
#include <stdbool.h>
#include <stdint.h>
typedef struct NoiseEstimator NoiseEstimator;
typedef enum NoiseEstimatorType {
OFF = 0,
ROLLING_MEAN = 1,
MEDIAN = 2,
MAX = 3,
} NoiseEstimatorType;
NoiseEstimator* noise_estimation_initialize(uint32_t fft_size,
NoiseProfile* noise_profile);
void noise_estimation_free(NoiseEstimator* self);
bool noise_estimation_run(NoiseEstimator* self,
NoiseEstimatorType noise_estimator_type,
float* signal_spectrum);
#endif
@@ -0,0 +1,142 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "noise_profile.h"
#include "../configurations.h"
#include "../utils/spectral_utils.h"
#include <stdlib.h>
#include <string.h>
struct NoiseProfile {
uint32_t noise_profile_size;
uint32_t noise_profile_blocks_averaged[NOISE_PROFILE_MODES];
float* noise_profiles[NOISE_PROFILE_MODES];
bool noise_spectrum_available[NOISE_PROFILE_MODES];
};
NoiseProfile* noise_profile_initialize(const uint32_t size) {
NoiseProfile* self = (NoiseProfile*)calloc(1U, sizeof(NoiseProfile));
if (!self) {
return NULL;
}
self->noise_profile_size = size;
for (int i = 0; i < NOISE_PROFILE_MODES; i++) {
self->noise_profile_blocks_averaged[i] = 0U;
self->noise_spectrum_available[i] = false;
self->noise_profiles[i] = (float*)calloc(size, sizeof(float));
if (!self->noise_profiles[i]) {
noise_profile_free(self);
return NULL;
}
}
return self;
}
void noise_profile_free(NoiseProfile* self) {
if (self) {
for (int i = 0; i < NOISE_PROFILE_MODES; i++) {
if (self->noise_profiles[i]) {
free(self->noise_profiles[i]);
}
}
free(self);
}
}
bool is_noise_estimation_available(NoiseProfile* self, int mode) {
if (mode < 1 || mode > 3) {
return false;
}
return self->noise_spectrum_available[mode - 1];
}
float* get_noise_profile(NoiseProfile* self, int mode) {
if (mode < 1 || mode > 3) {
return NULL;
}
return self->noise_profiles[mode - 1];
}
uint32_t get_noise_profile_size(NoiseProfile* self) {
return self->noise_profile_size;
}
uint32_t get_noise_profile_blocks_averaged(NoiseProfile* self, int mode) {
if (mode < 1 || mode > 3) {
return 0;
}
return self->noise_profile_blocks_averaged[mode - 1];
}
void set_noise_profile_available(NoiseProfile* self, int mode) {
if (mode >= 1 && mode <= 3) {
self->noise_spectrum_available[mode - 1] = true;
}
}
bool set_noise_profile(NoiseProfile* self, int mode, const float* noise_profile,
const uint32_t noise_profile_size,
const uint32_t noise_profile_blocks_averaged) {
if (!self || mode < 1 || mode > 3 || !noise_profile ||
noise_profile_size != self->noise_profile_size) {
return false;
}
int index = mode - 1;
memcpy(self->noise_profiles[index], noise_profile,
noise_profile_size * sizeof(float));
self->noise_profile_blocks_averaged[index] = noise_profile_blocks_averaged;
self->noise_spectrum_available[index] = true;
return true;
}
bool increment_blocks_averaged(NoiseProfile* self, int mode) {
if (!self || mode < 1 || mode > 3) {
return false;
}
int index = mode - 1;
self->noise_profile_blocks_averaged[index]++;
if (self->noise_profile_blocks_averaged[index] >
MIN_NUMBER_OF_WINDOWS_NOISE_AVERAGED &&
!self->noise_spectrum_available[index]) {
self->noise_spectrum_available[index] = true;
}
return true;
}
bool reset_noise_profile(NoiseProfile* self) {
if (!self) {
return false;
}
for (int i = 0; i < NOISE_PROFILE_MODES; i++) {
(void)initialize_spectrum_with_value(self->noise_profiles[i],
self->noise_profile_size, 0.F);
self->noise_profile_blocks_averaged[i] = 0U;
self->noise_spectrum_available[i] = false;
}
return true;
}
@@ -0,0 +1,44 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef NOISE_PROFILE_H
#define NOISE_PROFILE_H
#include <stdbool.h>
#include <stdint.h>
typedef struct NoiseProfile NoiseProfile;
#define NOISE_PROFILE_MODES \
3 // ROLLING_MEAN, MEDIAN, MAX (no OFF storage needed)
NoiseProfile* noise_profile_initialize(uint32_t size);
void noise_profile_free(NoiseProfile* self);
float* get_noise_profile(NoiseProfile* self, int mode);
uint32_t get_noise_profile_size(NoiseProfile* self);
uint32_t get_noise_profile_blocks_averaged(NoiseProfile* self, int mode);
bool increment_blocks_averaged(NoiseProfile* self, int mode);
bool set_noise_profile(NoiseProfile* self, int mode, const float* noise_profile,
uint32_t noise_profile_size, uint32_t averaged_blocks);
void set_noise_profile_available(NoiseProfile* self, int mode);
bool reset_noise_profile(NoiseProfile* self);
bool is_noise_estimation_available(NoiseProfile* self, int mode);
#endif
@@ -0,0 +1,5 @@
shared_sources += files(
'noise_floor_manager.c',
'spectral_whitening.c',
'postfilter.c',
)
@@ -0,0 +1,100 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "noise_floor_manager.h"
#include "spectral_whitening.h"
#include <stdlib.h>
#include <string.h>
struct NoiseFloorManager {
SpectralWhitening* whitening;
float* whitening_weights;
uint32_t real_spectrum_size;
};
NoiseFloorManager* noise_floor_manager_initialize(const uint32_t fft_size,
const uint32_t sample_rate,
const uint32_t hop) {
NoiseFloorManager* self =
(NoiseFloorManager*)calloc(1U, sizeof(NoiseFloorManager));
if (!self) {
return NULL;
}
self->real_spectrum_size = (fft_size / 2U) + 1U;
self->whitening = spectral_whitening_initialize(fft_size);
if (!self->whitening) {
free(self);
return NULL;
}
self->whitening_weights =
(float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->whitening_weights) {
spectral_whitening_free(self->whitening);
free(self);
return NULL;
}
return self;
}
void noise_floor_manager_free(NoiseFloorManager* self) {
if (!self) {
return;
}
if (self->whitening) {
spectral_whitening_free(self->whitening);
}
if (self->whitening_weights) {
free(self->whitening_weights);
}
free(self);
}
void noise_floor_manager_apply(NoiseFloorManager* self,
uint32_t real_spectrum_size, uint32_t fft_size,
float* gain_spectrum, const float* noise_profile,
float reduction_amount, float whitening_factor) {
if (!self || !gain_spectrum || !noise_profile) {
return;
}
// 1. Calculate whitening weights (including tapering)
spectral_whitening_get_weights(self->whitening, whitening_factor,
noise_profile, self->whitening_weights);
// 2. Apply biasing + frequency-dependent floor
for (uint32_t k = 0U; k < real_spectrum_size; k++) {
float floor = reduction_amount * self->whitening_weights[k];
if (floor > 1.0f) {
floor = 1.0f;
}
float range = 1.0f - floor;
gain_spectrum[k] = floor + (range * gain_spectrum[k]);
}
// 3. Symmetric copy
for (uint32_t k = 1U; k < fft_size - k; k++) {
gain_spectrum[fft_size - k] = gain_spectrum[k];
}
}
@@ -0,0 +1,40 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef NO_FLOOR_MANAGER_H
#define NO_FLOOR_MANAGER_H
#include <stdbool.h>
#include <stdint.h>
typedef struct NoiseFloorManager NoiseFloorManager;
NoiseFloorManager* noise_floor_manager_initialize(uint32_t fft_size,
uint32_t sample_rate,
uint32_t hop);
void noise_floor_manager_free(NoiseFloorManager* self);
void noise_floor_manager_apply(NoiseFloorManager* self,
uint32_t real_spectrum_size, uint32_t fft_size,
float* gain_spectrum, const float* noise_profile,
float reduction_amount, float whitening_factor);
#endif
@@ -0,0 +1,174 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "postfilter.h"
#include "../configurations.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
struct PostFilter {
float* intermediate_gains;
uint32_t fft_size;
uint32_t real_spectrum_size;
bool preserve_minimum;
float default_postfilter_scale;
float min_gain_coefficient;
};
PostFilter* postfilter_initialize(const uint32_t fft_size) {
PostFilter* self = (PostFilter*)calloc(1U, sizeof(PostFilter));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->preserve_minimum = (bool)PRESERVE_MINIMUN_GAIN;
self->default_postfilter_scale = POSTFILTER_SCALE;
self->min_gain_coefficient = powf(10.F, (float)POSTFILTER_MIN_GAIN_DB / 20.F);
self->intermediate_gains =
(float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->intermediate_gains) {
free(self);
return NULL;
}
return self;
}
void postfilter_free(PostFilter* self) {
if (!self) {
return;
}
free(self->intermediate_gains);
free(self);
}
static uint32_t get_adaptive_window_size(const PostFilter* self,
const float* spectrum,
const float snr_threshold,
const float* gain_spectrum) {
float clean_energy = 0.F;
float noisy_energy = 0.F;
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
const float noisy = spectrum[k];
const float clean = noisy * gain_spectrum[k];
clean_energy += clean * clean;
noisy_energy += noisy * noisy;
}
if (noisy_energy <= 1e-12F) {
return 1U;
}
const float zeta = clean_energy / noisy_energy;
const float zeta_t = (zeta >= snr_threshold) ? 1.F : zeta;
if (zeta_t >= 1.F) {
return 1U;
}
const float n = (2.F * roundf(self->default_postfilter_scale *
(1.F - (zeta_t / snr_threshold)))) +
1.F;
return (uint32_t)n;
}
static void moving_average(const float* in, float* out, uint32_t size,
uint32_t n) {
if (n <= 1U || n > size) {
memcpy(out, in, size * sizeof(float));
return;
}
const uint32_t half = n / 2U;
double current_sum = 0.0;
// Initial window sum (boundary handling: use clamping for start)
for (int i = -(int)half; i <= (int)half; i++) {
int idx = i;
if (idx < 0) {
idx = 0;
}
if (idx >= (int)size) {
idx = (int)size - 1;
}
current_sum += (double)in[idx];
}
for (uint32_t i = 0U; i < size; i++) {
out[i] = (float)(current_sum / (double)n);
if (i + 1U < size) {
// Move window: subtract oldest, add newest
int old_idx = (int)i - (int)half;
int new_idx = (int)i + (int)half + 1;
if (old_idx < 0) {
old_idx = 0;
}
if (new_idx >= (int)size) {
new_idx = (int)size - 1;
}
current_sum -= (double)in[old_idx];
current_sum += (double)in[new_idx];
}
}
}
bool postfilter_apply(PostFilter* self, const float* spectrum,
float* gain_spectrum,
const PostFiltersParameters parameters) {
if (!self || !spectrum || !gain_spectrum) {
return false;
}
const uint32_t n = get_adaptive_window_size(
self, spectrum, parameters.snr_threshold, gain_spectrum);
if (n > 1U) {
moving_average(gain_spectrum, self->intermediate_gains,
self->real_spectrum_size, n);
if (self->preserve_minimum) {
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
gain_spectrum[k] = fminf(gain_spectrum[k], self->intermediate_gains[k]);
}
} else {
memcpy(gain_spectrum, self->intermediate_gains,
self->real_spectrum_size * sizeof(float));
}
}
// Apply gain floor
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
if (gain_spectrum[k] < parameters.gain_floor) {
gain_spectrum[k] = parameters.gain_floor;
}
}
return true;
}
@@ -0,0 +1,39 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef POSTFILTER_H
#define POSTFILTER_H
#include <stdbool.h>
#include <stdint.h>
typedef struct PostFilter PostFilter;
typedef struct PostFiltersParameters {
float snr_threshold;
float gain_floor;
} PostFiltersParameters;
PostFilter* postfilter_initialize(uint32_t fft_size);
void postfilter_free(PostFilter* self);
bool postfilter_apply(PostFilter* self, const float* spectrum,
float* gain_spectrum, PostFiltersParameters parameters);
#endif
Binary file not shown.
@@ -0,0 +1,97 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "spectral_whitening.h"
#include "../configurations.h"
#include "../utils/spectral_utils.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
struct SpectralWhitening {
float* tapering_window;
uint32_t fft_size;
uint32_t real_spectrum_size;
};
SpectralWhitening* spectral_whitening_initialize(const uint32_t fft_size) {
SpectralWhitening* self =
(SpectralWhitening*)calloc(1U, sizeof(SpectralWhitening));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->tapering_window =
(float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->tapering_window) {
free(self);
return NULL;
}
// Pre-calculate Right half of Hamming window for HF tapering
uint32_t n_samples = (self->real_spectrum_size * 2U) - 1U;
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
uint32_t n = (k + self->real_spectrum_size) - 1U;
self->tapering_window[k] =
0.54f - (0.46f * cosf((2.0f * (float)M_PI * (float)n) /
(float)(n_samples - 1U)));
}
return self;
}
void spectral_whitening_free(SpectralWhitening* self) {
if (!self) {
return;
}
if (self->tapering_window) {
free(self->tapering_window);
}
free(self);
}
void spectral_whitening_get_weights(SpectralWhitening* self,
float whitening_factor,
const float* noise_profile,
float* weights_out) {
if (!self || !weights_out || !noise_profile) {
return;
}
float noise_peak = 1e-12f;
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
if (noise_profile[k] > noise_peak) {
noise_peak = noise_profile[k];
}
}
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
float weight = 1.0f;
if (whitening_factor > 0.0f && noise_profile[k] > 1e-12f) {
// Power-law valley filling
weight = powf(noise_peak / noise_profile[k], whitening_factor);
}
// Weights include tapering
weights_out[k] = weight * self->tapering_window[k];
}
}
@@ -0,0 +1,37 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECTRAL_WHITENER_H
#define SPECTRAL_WHITENER_H
#include <stdint.h>
typedef struct SpectralWhitening SpectralWhitening;
SpectralWhitening* spectral_whitening_initialize(uint32_t fft_size);
void spectral_whitening_free(SpectralWhitening* self);
void spectral_whitening_get_weights(SpectralWhitening* self,
float whitening_factor,
const float* noise_profile,
float* weights_out);
#endif
@@ -0,0 +1,165 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "absolute_hearing_thresholds.h"
#include "../configurations.h"
#include "../stft/fft_transform.h"
#include "../utils/spectral_utils.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
static void generate_sinewave(AbsoluteHearingThresholds* self);
static void compute_spl_reference_spectrum(AbsoluteHearingThresholds* self);
static void compute_absolute_thresholds(AbsoluteHearingThresholds* self);
struct AbsoluteHearingThresholds {
float* sinewave;
float* window;
float* spl_reference_values;
float* absolute_thresholds;
SpectralFeatures* spectral_features;
FftTransform* fft_transform;
SpectrumType spectrum_type;
uint32_t fft_size;
uint32_t real_spectrum_size;
uint32_t sample_rate;
float sine_wave_amplitude;
float sine_wave_frequency;
float reference_level;
};
AbsoluteHearingThresholds* absolute_hearing_thresholds_initialize(
const uint32_t sample_rate, const uint32_t fft_size,
SpectrumType spectrum_type) {
AbsoluteHearingThresholds* self =
(AbsoluteHearingThresholds*)calloc(1U, sizeof(AbsoluteHearingThresholds));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->sample_rate = sample_rate;
self->spectrum_type = spectrum_type;
self->sine_wave_amplitude = SINE_AMPLITUDE;
self->sine_wave_frequency = REFERENCE_SINE_WAVE_FREQ;
self->reference_level = REFERENCE_LEVEL;
self->fft_transform = fft_transform_initialize_bins(self->fft_size);
self->spl_reference_values =
(float*)calloc(self->real_spectrum_size, sizeof(float));
self->absolute_thresholds =
(float*)calloc(self->real_spectrum_size, sizeof(float));
self->sinewave = (float*)calloc(self->fft_size, sizeof(float));
self->window = (float*)calloc(self->fft_size, sizeof(float));
self->spectral_features =
spectral_features_initialize(self->real_spectrum_size);
if (!self->fft_transform || !self->spl_reference_values ||
!self->absolute_thresholds || !self->sinewave || !self->window ||
!self->spectral_features) {
absolute_hearing_thresholds_free(self);
return NULL;
}
generate_sinewave(self);
(void)get_fft_window(self->window, self->fft_size, VORBIS_WINDOW);
compute_spl_reference_spectrum(self);
compute_absolute_thresholds(self);
return self;
}
void absolute_hearing_thresholds_free(AbsoluteHearingThresholds* self) {
if (!self) {
return;
}
fft_transform_free(self->fft_transform);
spectral_features_free(self->spectral_features);
free(self->sinewave);
free(self->window);
free(self->spl_reference_values);
free(self->absolute_thresholds);
free(self);
}
static void generate_sinewave(AbsoluteHearingThresholds* self) {
for (uint32_t k = 0U; k < self->fft_size; k++) {
self->sinewave[k] =
self->sine_wave_amplitude *
sinf((2.F * M_PIf * (float)k * self->sine_wave_frequency) /
(float)self->sample_rate);
}
}
static void compute_spl_reference_spectrum(AbsoluteHearingThresholds* self) {
for (uint32_t k = 0U; k < self->fft_size; k++) {
get_fft_input_buffer(self->fft_transform)[k] =
self->sinewave[k] * self->window[k];
}
compute_forward_fft(self->fft_transform);
float* reference_spectrum = get_spectral_feature(
self->spectral_features, get_fft_output_buffer(self->fft_transform),
self->fft_size, self->spectrum_type);
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
self->spl_reference_values[k] =
self->reference_level - (10.F * log10f(reference_spectrum[k] + 1e-12F));
}
}
bool apply_thresholds_as_floor(AbsoluteHearingThresholds* self,
float* spectrum) {
if (!self || !spectrum) {
return false;
}
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
const float spl_level =
(10.F * log10f(spectrum[k] + 1e-12F)) + self->spl_reference_values[k];
spectrum[k] =
powf(10.F, fmaxf(spl_level, self->absolute_thresholds[k]) / 10.F);
}
return true;
}
static void compute_absolute_thresholds(AbsoluteHearingThresholds* self) {
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
const float frequency =
fmaxf(fft_bin_to_freq(k, self->sample_rate, self->fft_size), 20.F);
self->absolute_thresholds[k] =
(3.64F * powf((frequency / 1000.F), -0.8F)) -
(6.5F * expf(-0.6F * powf(((frequency / 1000.F) - 3.3F), 2.F))) +
(powf(10.F, -3.F) * powf((frequency / 1000.F), 4.F));
}
}
@@ -0,0 +1,36 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef ABSOLUTE_HEARING_THRESHOLDS_H
#define ABSOLUTE_HEARING_THRESHOLDS_H
#include "../utils/spectral_features.h"
#include <stdbool.h>
#include <stdint.h>
typedef struct AbsoluteHearingThresholds AbsoluteHearingThresholds;
AbsoluteHearingThresholds* absolute_hearing_thresholds_initialize(
uint32_t sample_rate, uint32_t fft_size, SpectrumType spectrum_type);
void absolute_hearing_thresholds_free(AbsoluteHearingThresholds* self);
bool apply_thresholds_as_floor(AbsoluteHearingThresholds* self,
float* spectrum);
#endif
@@ -0,0 +1,204 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "critical_bands.h"
#include "../utils/spectral_utils.h"
#include <stdlib.h>
#include <string.h>
static const float bark_bands[24] = {
100.F, 200.F, 300.F, 400.F, 510.F, 630.F, 770.F, 920.F,
1080.F, 1270.F, 1480.F, 1720.F, 2000.F, 2320.F, 2700.F, 3150.F,
3700.F, 4400.F, 5300.F, 6400.F, 7700.F, 9500.F, 12000.F, 15500.F};
static const float opus_bands[20] = {200.F, 400.F, 600.F, 800.F, 1000.F,
1200.F, 1400.F, 1600.F, 2000.F, 2400.F,
2800.F, 3200.F, 4000.F, 4800.F, 5600.F,
6800.F, 8000.F, 9600.F, 12000.F, 15600.F};
static const float mel_bands[33] = {
250.F, 500.F, 750.F, 1000.F, 1250.F, 1500.F, 1750.F, 2000.F,
2250.F, 2500.F, 2750.F, 3000.F, 3250.F, 3500.F, 3750.F, 4000.F,
4250.F, 4500.F, 4750.F, 5000.F, 5250.F, 5500.F, 5750.F, 6000.F,
6250.F, 6500.F, 6750.F, 7000.F, 7250.F, 7500.F, 7750.F, 8000.F};
static const float octave_bands[10] = {31.5F, 63.F, 125.F, 250.F, 500.F,
1000.F, 2000.F, 4000.F, 8000.F, 16000.F};
void set_number_of_bands(CriticalBands* self);
static void compute_mapping_spectrum(CriticalBands* self);
static void compute_band_indexes(CriticalBands* self);
static uint32_t get_last_valid_band_for_samplerate(CriticalBands* self,
uint32_t number_of_bands);
struct CriticalBands {
uint32_t* band_delimiter_bins;
uint32_t* number_bins_per_band;
float* current_critical_bands;
uint32_t fft_size;
uint32_t real_spectrum_size;
uint32_t sample_rate;
uint32_t number_bands;
CriticalBandType type;
CriticalBandIndexes band_indexes;
};
CriticalBands* critical_bands_initialize(const uint32_t sample_rate,
const uint32_t fft_size,
const CriticalBandType type) {
CriticalBands* self = (CriticalBands*)calloc(1U, sizeof(CriticalBands));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (fft_size / 2U) + 1U;
self->sample_rate = sample_rate;
self->type = type;
compute_mapping_spectrum(self);
self->band_delimiter_bins =
(uint32_t*)calloc(self->number_bands, sizeof(uint32_t));
self->number_bins_per_band =
(uint32_t*)calloc(self->number_bands, sizeof(uint32_t));
if (!self->band_delimiter_bins || !self->number_bins_per_band) {
critical_bands_free(self);
return NULL;
}
compute_band_indexes(self);
return self;
}
void critical_bands_free(CriticalBands* self) {
if (!self) {
return;
}
free(self->band_delimiter_bins);
free(self->number_bins_per_band);
free(self);
}
static void compute_band_indexes(CriticalBands* self) {
for (uint32_t k = 0U; k < self->number_bands; k++) {
const uint32_t bin_index =
freq_to_fft_bin(self->current_critical_bands[k], self->sample_rate,
self->real_spectrum_size);
if (k == 0) {
self->number_bins_per_band[k] = bin_index; // Don't include DC bin
self->band_delimiter_bins[k] = bin_index;
} else if (k == self->number_bands - 1U) {
self->band_delimiter_bins[k] = self->real_spectrum_size;
self->number_bins_per_band[k] =
self->band_delimiter_bins[k] - self->band_delimiter_bins[k - 1];
} else {
self->number_bins_per_band[k] =
bin_index - self->band_delimiter_bins[k - 1];
self->band_delimiter_bins[k] = bin_index;
}
}
}
static void compute_mapping_spectrum(CriticalBands* self) {
switch (self->type) {
case BARK_SCALE: {
self->current_critical_bands = (float*)bark_bands;
uint32_t number_of_bark_bands = sizeof(bark_bands) / sizeof(float);
self->number_bands =
get_last_valid_band_for_samplerate(self, number_of_bark_bands);
break;
}
case MEL_SCALE: {
self->current_critical_bands = (float*)mel_bands;
uint32_t number_of_mel_bands = sizeof(mel_bands) / sizeof(float);
self->number_bands =
get_last_valid_band_for_samplerate(self, number_of_mel_bands);
break;
}
case OPUS_SCALE: {
self->current_critical_bands = (float*)opus_bands;
uint32_t number_of_opus_bands = sizeof(opus_bands) / sizeof(float);
self->number_bands =
get_last_valid_band_for_samplerate(self, number_of_opus_bands);
break;
}
case OCTAVE_SCALE: {
self->current_critical_bands = (float*)octave_bands;
uint32_t number_of_octave_bands = sizeof(opus_bands) / sizeof(float);
self->number_bands =
get_last_valid_band_for_samplerate(self, number_of_octave_bands);
break;
}
default:
break;
}
}
static uint32_t get_last_valid_band_for_samplerate(CriticalBands* self,
uint32_t number_of_bands) {
float nyquist_frequency = (float)self->sample_rate / 2.F;
uint32_t last_valid_band = 0U;
for (uint32_t i = 0; i < number_of_bands; i++) {
if (self->current_critical_bands[i] < nyquist_frequency) {
last_valid_band = i;
}
}
return last_valid_band;
}
bool compute_critical_bands_spectrum(CriticalBands* self, const float* spectrum,
float* critical_bands) {
if (!self || !spectrum || !critical_bands) {
return false;
}
memset(critical_bands, 0, self->number_bands * sizeof(float));
for (uint32_t j = 0U; j < self->number_bands; j++) {
self->band_indexes = get_band_indexes(self, j);
for (uint32_t k = self->band_indexes.start_position;
k < self->band_indexes.end_position; k++) {
critical_bands[j] += spectrum[k];
}
}
return true;
}
CriticalBandIndexes get_band_indexes(CriticalBands* self,
const uint32_t band_number) {
return (CriticalBandIndexes){
.start_position = self->band_delimiter_bins[band_number] -
self->number_bins_per_band[band_number],
.end_position = self->band_delimiter_bins[band_number],
};
}
uint32_t get_number_of_critical_bands(CriticalBands* self) {
return self->number_bands;
}
@@ -0,0 +1,50 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CRITICAL_BANDS_H
#define CRITICAL_BANDS_H
#include <stdbool.h>
#include <stdint.h>
typedef struct CriticalBands CriticalBands;
typedef enum CriticalBandType {
BARK_SCALE = 0,
MEL_SCALE = 1,
OPUS_SCALE = 2,
OCTAVE_SCALE = 3,
} CriticalBandType;
typedef struct CriticalBandIndexes {
uint32_t start_position;
uint32_t end_position;
} CriticalBandIndexes;
CriticalBands* critical_bands_initialize(uint32_t sample_rate,
uint32_t fft_size,
CriticalBandType type);
void critical_bands_free(CriticalBands* self);
bool compute_critical_bands_spectrum(CriticalBands* self, const float* spectrum,
float* critical_bands);
CriticalBandIndexes get_band_indexes(CriticalBands* self, uint32_t band_number);
uint32_t get_number_of_critical_bands(CriticalBands* self);
#endif
@@ -0,0 +1,225 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "masking_estimator.h"
#include "../configurations.h"
#include "../utils/spectral_utils.h"
#include "absolute_hearing_thresholds.h"
#include "critical_bands.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
static void compute_spectral_spreading_function(MaskingEstimator* self);
static float compute_tonality_factor(MaskingEstimator* self,
const float* spectrum, uint32_t band);
struct MaskingEstimator {
uint32_t fft_size;
uint32_t real_spectrum_size;
uint32_t sample_rate;
uint32_t number_critical_bands;
AbsoluteHearingThresholds* reference_spectrum;
CriticalBands* critical_bands;
CriticalBandIndexes band_indexes;
float* spectral_spreading_function;
float* unity_gain_critical_bands_spectrum;
float* spreaded_unity_gain_critical_bands_spectrum;
float* threshold_j;
float* masking_offset;
float* spreaded_spectrum;
float* critical_bands_reference_spectrum;
};
MaskingEstimator* masking_estimation_initialize(const uint32_t fft_size,
const uint32_t sample_rate,
SpectrumType spectrum_type) {
MaskingEstimator* self =
(MaskingEstimator*)calloc(1U, sizeof(MaskingEstimator));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->sample_rate = sample_rate;
self->critical_bands = critical_bands_initialize(
self->sample_rate, self->fft_size, CRITICAL_BANDS_TYPE);
if (!self->critical_bands) {
masking_estimation_free(self);
return NULL;
}
self->number_critical_bands =
get_number_of_critical_bands(self->critical_bands);
self->spectral_spreading_function =
(float*)calloc(((size_t)self->number_critical_bands *
(size_t)self->number_critical_bands),
sizeof(float));
self->unity_gain_critical_bands_spectrum =
(float*)calloc(self->number_critical_bands, sizeof(float));
self->spreaded_unity_gain_critical_bands_spectrum =
(float*)calloc(self->number_critical_bands, sizeof(float));
self->threshold_j =
(float*)calloc(self->number_critical_bands, sizeof(float));
self->masking_offset =
(float*)calloc(self->number_critical_bands, sizeof(float));
self->spreaded_spectrum =
(float*)calloc(self->number_critical_bands, sizeof(float));
self->critical_bands_reference_spectrum =
(float*)calloc(self->number_critical_bands, sizeof(float));
self->reference_spectrum = absolute_hearing_thresholds_initialize(
self->sample_rate, self->fft_size, spectrum_type);
if (!self->spectral_spreading_function ||
!self->unity_gain_critical_bands_spectrum ||
!self->spreaded_unity_gain_critical_bands_spectrum ||
!self->threshold_j || !self->masking_offset || !self->spreaded_spectrum ||
!self->critical_bands_reference_spectrum || !self->reference_spectrum) {
masking_estimation_free(self);
return NULL;
}
compute_spectral_spreading_function(self);
(void)initialize_spectrum_with_value(self->unity_gain_critical_bands_spectrum,
self->number_critical_bands, 1.F);
(void)direct_matrix_to_vector_spectral_convolution(
self->spectral_spreading_function,
self->unity_gain_critical_bands_spectrum,
self->spreaded_unity_gain_critical_bands_spectrum,
self->number_critical_bands);
return self;
}
void masking_estimation_free(MaskingEstimator* self) {
if (!self) {
return;
}
absolute_hearing_thresholds_free(self->reference_spectrum);
critical_bands_free(self->critical_bands);
free(self->spectral_spreading_function);
free(self->unity_gain_critical_bands_spectrum);
free(self->spreaded_unity_gain_critical_bands_spectrum);
free(self->threshold_j);
free(self->masking_offset);
free(self->spreaded_spectrum);
free(self->critical_bands_reference_spectrum);
free(self);
}
bool compute_masking_thresholds(MaskingEstimator* self, const float* spectrum,
float* masking_thresholds) {
if (!self || !spectrum || !masking_thresholds) {
return false;
}
compute_critical_bands_spectrum(self->critical_bands, spectrum,
self->critical_bands_reference_spectrum);
(void)direct_matrix_to_vector_spectral_convolution(
self->spectral_spreading_function,
self->critical_bands_reference_spectrum, self->spreaded_spectrum,
self->number_critical_bands);
for (uint32_t j = 0U; j < self->number_critical_bands; j++) {
const float tonality_factor = compute_tonality_factor(self, spectrum, j);
self->masking_offset[j] = (tonality_factor * (14.5F + (float)(j + 1))) +
(5.5F * (1.F - tonality_factor));
#if BIAS
self->masking_offset[j] = relative_thresholds[j];
if (j > 15) {
self->masking_offset[j] += HIGH_FREQ_BIAS;
}
#endif
self->threshold_j[j] = powf(
10.F, (log10f(self->spreaded_spectrum[j] + 1e-12F) -
(self->masking_offset[j] / 10.F) -
log10f(self->spreaded_unity_gain_critical_bands_spectrum[j] +
1e-12F)));
self->band_indexes = get_band_indexes(self->critical_bands, j);
for (uint32_t k = self->band_indexes.start_position;
k < self->band_indexes.end_position; k++) {
masking_thresholds[k] = self->threshold_j[j];
}
}
apply_thresholds_as_floor(self->reference_spectrum, masking_thresholds);
return true;
}
static void compute_spectral_spreading_function(MaskingEstimator* self) {
for (uint32_t i = 0U; i < self->number_critical_bands; i++) {
for (uint32_t j = 0U; j < self->number_critical_bands; j++) {
const uint32_t y = (i + 1) - (j + 1);
self->spectral_spreading_function[(i * self->number_critical_bands) + j] =
15.81F + (7.5F * ((float)y + 0.474F)) -
(17.5F * sqrtf(1.F + (((float)y + 0.474F) * ((float)y + 0.474F))));
self->spectral_spreading_function[(i * self->number_critical_bands) + j] =
powf(10.F, self->spectral_spreading_function
[(i * self->number_critical_bands) + j] /
10.F);
}
}
}
static float compute_tonality_factor(MaskingEstimator* self,
const float* spectrum, uint32_t band) {
float sum_bins = 0.F;
float sum_log_bins = 0.F;
self->band_indexes = get_band_indexes(self->critical_bands, band);
for (uint32_t k = self->band_indexes.start_position;
k < self->band_indexes.end_position; k++) {
const float val = fmaxf(spectrum[k], 1e-12F);
sum_bins += val;
sum_log_bins += log10f(val);
}
float bins_in_band = (float)self->band_indexes.end_position -
(float)self->band_indexes.start_position;
const float sfm =
(10.F * (sum_log_bins / bins_in_band)) - log10f(sum_bins / bins_in_band);
const float tonality_factor = fminf(sfm / -60.F, 1.F);
return tonality_factor;
}
@@ -0,0 +1,37 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef MASKING_ESTIMATOR_H
#define MASKING_ESTIMATOR_H
#include "../utils/spectral_features.h"
#include <stdbool.h>
#include <stdint.h>
typedef struct MaskingEstimator MaskingEstimator;
MaskingEstimator* masking_estimation_initialize(uint32_t fft_size,
uint32_t sample_rate,
SpectrumType spectrum_type);
void masking_estimation_free(MaskingEstimator* self);
bool compute_masking_thresholds(MaskingEstimator* self, const float* spectrum,
float* masking_thresholds);
#endif
@@ -0,0 +1,8 @@
shared_sources += files(
'absolute_hearing_thresholds.c',
'masking_estimator.c',
'critical_bands.c',
'noise_scaling_criterias.c',
'transient_detector.c',
'spectral_smoother.c',
)
@@ -0,0 +1,277 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "noise_scaling_criterias.h"
#include "../configurations.h"
#include "../utils/spectral_utils.h"
#include "critical_bands.h"
#include "masking_estimator.h"
#include <float.h>
#include <math.h>
#include <stdlib.h>
static void a_posteriori_snr_critical_bands(NoiseScalingCriterias* self,
const float* spectrum,
const float* noise_spectrum,
float* alpha,
NoiseScalingParameters parameters);
static void a_posteriori_snr(NoiseScalingCriterias* self, const float* spectrum,
const float* noise_spectrum, float* alpha,
NoiseScalingParameters parameters);
static void masking_thresholds(NoiseScalingCriterias* self,
const float* spectrum,
const float* noise_spectrum, float* alpha,
float* beta, NoiseScalingParameters parameters);
struct NoiseScalingCriterias {
NoiseScalingType noise_scaling_type;
uint32_t fft_size;
uint32_t real_spectrum_size;
uint32_t sample_rate;
SpectrumType spectrum_type;
uint32_t number_critical_bands;
float lower_snr;
float higher_snr;
float alpha_minimun;
float beta_minimun;
CriticalBandIndexes band_indexes;
CriticalBandType critical_band_type;
float* masking_thresholds;
float* clean_signal_estimation;
float* critical_bands_noise_profile;
float* critical_bands_reference_spectrum;
MaskingEstimator* masking_estimation;
CriticalBands* critical_bands;
};
NoiseScalingCriterias* noise_scaling_criterias_initialize(
const uint32_t fft_size, const CriticalBandType critical_band_type,
const uint32_t sample_rate, SpectrumType spectrum_type) {
NoiseScalingCriterias* self =
(NoiseScalingCriterias*)calloc(1U, sizeof(NoiseScalingCriterias));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->critical_band_type = critical_band_type;
self->sample_rate = sample_rate;
self->spectrum_type = spectrum_type;
self->lower_snr = LOWER_SNR;
self->higher_snr = HIGHER_SNR;
self->alpha_minimun = ALPHA_MIN;
self->beta_minimun = BETA_MIN;
self->critical_bands = critical_bands_initialize(
self->sample_rate, self->fft_size, self->critical_band_type);
self->masking_estimation = masking_estimation_initialize(
self->fft_size, self->sample_rate, self->spectrum_type);
if (!self->critical_bands || !self->masking_estimation) {
noise_scaling_criterias_free(self);
return NULL;
}
self->number_critical_bands =
get_number_of_critical_bands(self->critical_bands);
self->critical_bands_noise_profile =
(float*)calloc(self->number_critical_bands, sizeof(float));
self->critical_bands_reference_spectrum =
(float*)calloc(self->number_critical_bands, sizeof(float));
self->masking_thresholds =
(float*)calloc(self->real_spectrum_size, sizeof(float));
self->clean_signal_estimation =
(float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->critical_bands_noise_profile ||
!self->critical_bands_reference_spectrum || !self->masking_thresholds ||
!self->clean_signal_estimation) {
noise_scaling_criterias_free(self);
return NULL;
}
return self;
}
void noise_scaling_criterias_free(NoiseScalingCriterias* self) {
if (!self) {
return;
}
critical_bands_free(self->critical_bands);
masking_estimation_free(self->masking_estimation);
free(self->clean_signal_estimation);
free(self->masking_thresholds);
free(self->critical_bands_noise_profile);
free(self->critical_bands_reference_spectrum);
free(self);
}
bool apply_noise_scaling_criteria(NoiseScalingCriterias* self,
const float* spectrum,
const float* noise_spectrum, float* alpha,
float* beta,
NoiseScalingParameters parameters) {
if (!spectrum || !noise_spectrum) {
return false;
}
switch ((NoiseScalingType)parameters.scaling_type) {
case A_POSTERIORI_SNR:
a_posteriori_snr(self, spectrum, noise_spectrum, alpha, parameters);
break;
case A_POSTERIORI_SNR_CRITICAL_BANDS:
a_posteriori_snr_critical_bands(self, spectrum, noise_spectrum, alpha,
parameters);
break;
case MASKING_THRESHOLDS:
masking_thresholds(self, spectrum, noise_spectrum, alpha, beta,
parameters);
break;
case NO_SCALING:
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
alpha[k] = self->alpha_minimun;
beta[k] = self->beta_minimun;
}
break;
default:
break;
}
return true;
}
static void a_posteriori_snr_critical_bands(NoiseScalingCriterias* self,
const float* spectrum,
const float* noise_spectrum,
float* alpha,
NoiseScalingParameters parameters) {
compute_critical_bands_spectrum(self->critical_bands, noise_spectrum,
self->critical_bands_noise_profile);
compute_critical_bands_spectrum(self->critical_bands, spectrum,
self->critical_bands_reference_spectrum);
float oversustraction_factor = 1.F;
for (uint32_t j = 0U; j < self->number_critical_bands; j++) {
self->band_indexes = get_band_indexes(self->critical_bands, j);
const float snr_db =
10.F * log10f(self->critical_bands_reference_spectrum[j] /
self->critical_bands_noise_profile[j]);
if (snr_db >= self->lower_snr && snr_db <= self->higher_snr) {
oversustraction_factor = (-0.05F * (snr_db)) + parameters.oversubtraction;
} else if (snr_db < 0.F) {
oversustraction_factor = parameters.oversubtraction;
} else if (snr_db > 20.F) {
oversustraction_factor = 1.F;
}
for (uint32_t k = self->band_indexes.start_position;
k < self->band_indexes.end_position; k++) {
alpha[k] = oversustraction_factor;
}
}
}
static void a_posteriori_snr(NoiseScalingCriterias* self, const float* spectrum,
const float* noise_spectrum, float* alpha,
NoiseScalingParameters parameters) {
float noisy_spectrum_sum = 0.F;
float noise_spectrum_sum = 0.F;
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
noisy_spectrum_sum += spectrum[k];
noise_spectrum_sum += noise_spectrum[k];
}
const float snr_db = 10.F * log10f(noisy_spectrum_sum / noise_spectrum_sum);
float oversustraction_factor;
if (snr_db >= self->lower_snr && snr_db <= self->higher_snr) {
oversustraction_factor = (-0.05F * (snr_db)) + parameters.oversubtraction;
} else if (snr_db < 0.F) {
oversustraction_factor = parameters.oversubtraction;
} else {
oversustraction_factor = 1.F;
}
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
alpha[k] = oversustraction_factor;
}
}
static void masking_thresholds(NoiseScalingCriterias* self,
const float* spectrum,
const float* noise_spectrum, float* alpha,
float* beta, NoiseScalingParameters parameters) {
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
self->clean_signal_estimation[k] =
fmaxf(spectrum[k] - noise_spectrum[k], 0.F);
}
compute_masking_thresholds(self->masking_estimation,
self->clean_signal_estimation,
self->masking_thresholds);
float max_masked_value =
10.F * log10f(max_spectral_value(self->masking_thresholds,
self->real_spectrum_size) +
1e-12F);
float min_masked_value =
10.F * log10f(min_spectral_value(self->masking_thresholds,
self->real_spectrum_size) +
1e-12F);
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
const float current_masked_value =
10.F * log10f(self->masking_thresholds[k] + 1e-12F);
if (current_masked_value >= max_masked_value) {
alpha[k] = self->alpha_minimun;
beta[k] = self->beta_minimun;
} else if (current_masked_value <= min_masked_value) {
alpha[k] = parameters.oversubtraction;
beta[k] = parameters.undersubtraction;
} else {
const float normalized_value = (current_masked_value - min_masked_value) /
(max_masked_value - min_masked_value);
alpha[k] = ((1.F - normalized_value) * parameters.oversubtraction) +
(normalized_value * self->alpha_minimun);
beta[k] = ((1.F - normalized_value) * parameters.undersubtraction) +
(normalized_value * self->beta_minimun);
}
}
}
@@ -0,0 +1,54 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef NOISE_SCALING_CRITERIAS_H
#define NOISE_SCALING_CRITERIAS_H
#include "../utils/spectral_features.h"
#include "critical_bands.h"
#include <stdbool.h>
#include <stdint.h>
typedef enum NoiseScalingType {
A_POSTERIORI_SNR = 0,
A_POSTERIORI_SNR_CRITICAL_BANDS = 1,
MASKING_THRESHOLDS = 2,
NO_SCALING = 3,
} NoiseScalingType;
typedef struct NoiseScalingParameters {
float undersubtraction;
float oversubtraction;
int scaling_type;
} NoiseScalingParameters;
typedef struct NoiseScalingCriterias NoiseScalingCriterias;
NoiseScalingCriterias* noise_scaling_criterias_initialize(
uint32_t fft_size, CriticalBandType critical_band_type,
uint32_t sample_rate, SpectrumType spectrum_type);
void noise_scaling_criterias_free(NoiseScalingCriterias* self);
bool apply_noise_scaling_criteria(NoiseScalingCriterias* self,
const float* spectrum,
const float* noise_spectrum, float* alpha,
float* beta,
NoiseScalingParameters parameters);
#endif
@@ -0,0 +1,146 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "spectral_smoother.h"
#include "transient_detector.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
static void spectrum_time_smoothing(SpectralSmoother* self, float smoothing);
static void spectrum_transient_aware_time_smoothing(SpectralSmoother* self,
float smoothing,
float* spectrum);
struct SpectralSmoother {
uint32_t fft_size;
uint32_t real_spectrum_size;
float adaptive_coefficient;
float previous_adaptive_coefficient;
TimeSmoothingType type;
float* noise_spectrum;
float* smoothed_spectrum;
float* smoothed_spectrum_previous;
TransientDetector* transient_detection;
};
SpectralSmoother* spectral_smoothing_initialize(const uint32_t fft_size,
TimeSmoothingType type) {
SpectralSmoother* self =
(SpectralSmoother*)calloc(1U, sizeof(SpectralSmoother));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->type = type;
self->previous_adaptive_coefficient = 0.F;
self->adaptive_coefficient = 0.F;
self->noise_spectrum =
(float*)calloc(self->real_spectrum_size, sizeof(float));
self->smoothed_spectrum =
(float*)calloc(self->real_spectrum_size, sizeof(float));
self->smoothed_spectrum_previous =
(float*)calloc(self->real_spectrum_size, sizeof(float));
self->transient_detection = transient_detector_initialize(self->fft_size);
if (!self->noise_spectrum || !self->smoothed_spectrum ||
!self->smoothed_spectrum_previous || !self->transient_detection) {
spectral_smoothing_free(self);
return NULL;
}
return self;
}
void spectral_smoothing_free(SpectralSmoother* self) {
if (!self) {
return;
}
transient_detector_free(self->transient_detection);
free(self->noise_spectrum);
free(self->smoothed_spectrum);
free(self->smoothed_spectrum_previous);
free(self);
}
bool spectral_smoothing_run(SpectralSmoother* self,
TimeSmoothingParameters parameters,
float* signal_spectrum) {
if (!self || !signal_spectrum) {
return false;
}
memcpy(self->smoothed_spectrum, signal_spectrum,
sizeof(float) * self->real_spectrum_size);
switch (self->type) {
case FIXED:
spectrum_time_smoothing(self, parameters.smoothing);
break;
case TRANSIENT_AWARE:
spectrum_transient_aware_time_smoothing(self, parameters.smoothing,
signal_spectrum);
break;
default:
break;
}
memcpy(self->smoothed_spectrum_previous, self->smoothed_spectrum,
sizeof(float) * self->real_spectrum_size);
memcpy(signal_spectrum, self->smoothed_spectrum,
sizeof(float) * self->real_spectrum_size);
return true;
}
static void spectrum_transient_aware_time_smoothing(SpectralSmoother* self,
const float smoothing,
float* spectrum) {
if (!transient_detector_run(self->transient_detection, spectrum)) {
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
if (self->smoothed_spectrum[k] > self->smoothed_spectrum_previous[k]) {
self->smoothed_spectrum[k] =
(smoothing * self->smoothed_spectrum_previous[k]) +
((1.F - smoothing) * self->smoothed_spectrum[k]);
}
}
}
}
static void spectrum_time_smoothing(SpectralSmoother* self,
const float smoothing) {
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
if (self->smoothed_spectrum[k] > self->smoothed_spectrum_previous[k]) {
self->smoothed_spectrum[k] =
(smoothing * self->smoothed_spectrum_previous[k]) +
((1.F - smoothing) * self->smoothed_spectrum[k]);
}
}
}
@@ -0,0 +1,46 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECTRAL_SMOOTHER_H
#define SPECTRAL_SMOOTHER_H
#include <stdbool.h>
#include <stdint.h>
typedef enum TimeSmoothingType {
NO_SMOOTHING = 0,
FIXED = 1,
TRANSIENT_AWARE = 2,
} TimeSmoothingType;
typedef struct TimeSmoothingParameters {
float smoothing;
} TimeSmoothingParameters;
typedef struct SpectralSmoother SpectralSmoother;
SpectralSmoother* spectral_smoothing_initialize(uint32_t fft_size,
TimeSmoothingType type);
void spectral_smoothing_free(SpectralSmoother* self);
bool spectral_smoothing_run(SpectralSmoother* self,
TimeSmoothingParameters parameters,
float* signal_spectrum);
#endif
@@ -0,0 +1,97 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "transient_detector.h"
#include "../configurations.h"
#include "../utils/spectral_utils.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
struct TransientDetector {
uint32_t fft_size;
uint32_t real_spectrum_size;
float rolling_mean;
bool transient_present;
uint32_t window_count;
float* previous_spectrum;
};
TransientDetector* transient_detector_initialize(const uint32_t fft_size) {
TransientDetector* self =
(TransientDetector*)calloc(1U, sizeof(TransientDetector));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->previous_spectrum =
(float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->previous_spectrum) {
transient_detector_free(self);
return NULL;
}
self->window_count = 0U;
self->rolling_mean = 0.F;
self->transient_present = false;
return self;
}
void transient_detector_free(TransientDetector* self) {
if (!self) {
return;
}
free(self->previous_spectrum);
free(self);
}
bool transient_detector_run(TransientDetector* self, const float* spectrum) {
const float reduction_function = spectral_flux(
spectrum, self->previous_spectrum, self->real_spectrum_size);
self->window_count += 1U;
if (self->window_count > 1U) {
self->rolling_mean +=
((reduction_function - self->rolling_mean) / (float)self->window_count);
} else {
self->rolling_mean = reduction_function;
}
const float adapted_threshold =
((UPPER_LIMIT - DEFAULT_TRANSIENT_THRESHOLD) * self->rolling_mean) +
1e-6F;
memcpy(self->previous_spectrum, spectrum,
sizeof(float) * self->real_spectrum_size);
if (reduction_function > adapted_threshold) {
return true;
}
return false;
}
@@ -0,0 +1,33 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef TRANSIENT_DETECTOR_H
#define TRANSIENT_DETECTOR_H
#include <stdbool.h>
#include <stdint.h>
typedef struct TransientDetector TransientDetector;
TransientDetector* transient_detector_initialize(uint32_t fft_size);
void transient_detector_free(TransientDetector* self);
bool transient_detector_run(TransientDetector* self, const float* spectrum);
#endif
@@ -0,0 +1,36 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECTRAL_PROCESSOR_H
#define SPECTRAL_PROCESSOR_H
#include <stdbool.h>
// Generic Spectral Processing function over an FFT spectrum. Receives any
// spectral processing module handle (void *) and the FFT of a audio block.
// This is to inject any spectral processor and processing function into the
// STFT transform at runtime
typedef void* SpectralProcessorHandle;
// Processing function which deals with the fft spectrum by mutating the array
// with any DSP that operates with the FFT spectrum (1d FFTW spectrum)
typedef bool (*spectral_processing)(SpectralProcessorHandle spectral_processor,
float* fft_spectrum);
#endif
@@ -0,0 +1,229 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "fft_transform.h"
#include "../utils/general_utils.h"
#include <fftw3.h>
#include <stdlib.h>
#include <string.h>
static uint32_t calculate_fft_size(FftTransform* self);
static bool allocate_fftw(FftTransform* self);
struct FftTransform {
fftwf_plan forward;
fftwf_plan backward;
uint32_t fft_size;
uint32_t frame_size;
uint32_t zeropadding_amount;
uint32_t copy_position;
ZeroPaddingType padding_type;
uint32_t padding_amount;
float* input_fft_buffer;
float* output_fft_buffer;
};
FftTransform* fft_transform_initialize(const uint32_t frame_size,
const ZeroPaddingType padding_type,
const uint32_t zeropadding_amount) {
FftTransform* self = (FftTransform*)calloc(1U, sizeof(FftTransform));
if (!self) {
return NULL;
}
self->padding_type = padding_type;
self->zeropadding_amount = zeropadding_amount;
self->frame_size = frame_size;
self->fft_size = calculate_fft_size(self);
self->copy_position = (self->fft_size / 2U) - (self->frame_size / 2U);
if (!allocate_fftw(self)) {
fft_transform_free(self);
return NULL;
}
return self;
}
FftTransform* fft_transform_initialize_bins(const uint32_t fft_size) {
FftTransform* self = (FftTransform*)calloc(1U, sizeof(FftTransform));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->frame_size = self->fft_size;
if (!allocate_fftw(self)) {
fft_transform_free(self);
return NULL;
}
return self;
}
static bool allocate_fftw(FftTransform* self) {
self->input_fft_buffer = (float*)fftwf_malloc(self->fft_size * sizeof(float));
self->output_fft_buffer =
(float*)fftwf_malloc(self->fft_size * sizeof(float));
if (!self->input_fft_buffer || !self->output_fft_buffer) {
return false;
}
memset(self->input_fft_buffer, 0, self->fft_size * sizeof(float));
memset(self->output_fft_buffer, 0, self->fft_size * sizeof(float));
self->forward =
fftwf_plan_r2r_1d((int)self->fft_size, self->input_fft_buffer,
self->output_fft_buffer, FFTW_R2HC, FFTW_ESTIMATE);
self->backward =
fftwf_plan_r2r_1d((int)self->fft_size, self->output_fft_buffer,
self->input_fft_buffer, FFTW_HC2R, FFTW_ESTIMATE);
return self->forward && self->backward;
}
static uint32_t calculate_fft_size(FftTransform* self) {
switch (self->padding_type) {
case NO_PADDING: {
self->padding_amount = 0;
return get_next_divisible_two((int)self->frame_size);
}
case NEXT_POWER_OF_TWO: {
uint32_t next_power_of_two = get_next_power_two((int)self->frame_size);
self->padding_amount = next_power_of_two - self->frame_size;
return next_power_of_two;
}
case FIXED_AMOUNT: {
self->padding_amount = self->zeropadding_amount;
return get_next_divisible_two(
(int)(self->frame_size + self->padding_amount));
}
default:
return get_next_divisible_two((int)self->frame_size);
}
}
void fft_transform_free(FftTransform* self) {
if (!self) {
return;
}
if (self->input_fft_buffer) {
fftwf_free(self->input_fft_buffer);
}
if (self->output_fft_buffer) {
fftwf_free(self->output_fft_buffer);
}
// FFTW plans can be NULL if initialization failed
if (self->forward) {
fftwf_destroy_plan(self->forward);
}
if (self->backward) {
fftwf_destroy_plan(self->backward);
}
free(self);
}
uint32_t get_fft_size(FftTransform* self) {
if (!self) {
return 0;
}
return self->fft_size;
}
uint32_t get_fft_real_spectrum_size(FftTransform* self) {
if (!self) {
return 0;
}
return (self->fft_size / 2U) + 1U;
}
bool fft_load_input_samples(FftTransform* self, const float* input) {
if (!self || !input) {
return false;
}
// Ensure buffer bounds are safe
if (self->frame_size + self->copy_position > self->fft_size) {
return false;
}
// Copy centered values only
for (uint32_t i = self->copy_position;
i < (self->frame_size + self->copy_position); i++) {
self->input_fft_buffer[i] = input[i - self->copy_position];
}
return true;
}
bool fft_get_output_samples(FftTransform* self, float* output) {
if (!self || !output) {
return false;
}
// Ensure buffer bounds are safe
if (self->frame_size + self->copy_position > self->fft_size) {
return false;
}
// Copy centered values only
for (uint32_t i = self->copy_position;
i < (self->frame_size + self->copy_position); i++) {
output[i - self->copy_position] = self->input_fft_buffer[i];
}
return true;
}
bool compute_forward_fft(FftTransform* self) {
if (!self) {
return false;
}
fftwf_execute(self->forward);
return true;
}
bool compute_backward_fft(FftTransform* self) {
if (!self) {
return false;
}
fftwf_execute(self->backward);
return true;
}
float* get_fft_input_buffer(FftTransform* self) {
return self->input_fft_buffer;
}
float* get_fft_output_buffer(FftTransform* self) {
return self->output_fft_buffer;
}
@@ -0,0 +1,57 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef FFT_TRANSFORM_H
#define FFT_TRANSFORM_H
#include <stdbool.h>
#include <stdint.h>
// C17 enum validation
enum ZeroPaddingType {
NEXT_POWER_OF_TWO = 0,
FIXED_AMOUNT = 1,
NO_PADDING = 2,
};
// Compile-time validation of enum values
_Static_assert(NEXT_POWER_OF_TWO == 0, "NEXT_POWER_OF_TWO must be 0");
_Static_assert(FIXED_AMOUNT == 1, "FIXED_AMOUNT must be 1");
_Static_assert(NO_PADDING == 2, "NO_PADDING must be 2");
typedef enum ZeroPaddingType ZeroPaddingType;
typedef struct FftTransform FftTransform;
FftTransform* fft_transform_initialize(uint32_t frame_size,
ZeroPaddingType padding_type,
uint32_t zeropadding_amount);
FftTransform* fft_transform_initialize_bins(uint32_t fft_size);
void fft_transform_free(FftTransform* self);
bool fft_load_input_samples(FftTransform* self, const float* input);
bool fft_get_output_samples(FftTransform* self, float* output);
uint32_t get_fft_size(FftTransform* self);
uint32_t get_fft_real_spectrum_size(FftTransform* self);
bool compute_forward_fft(FftTransform* self);
bool compute_backward_fft(FftTransform* self);
float* get_fft_input_buffer(FftTransform* self);
float* get_fft_output_buffer(FftTransform* self);
#endif
Binary file not shown.
+6
View File
@@ -0,0 +1,6 @@
shared_sources += files(
'fft_transform.c',
'stft_windows.c',
'stft_buffer.c',
'stft_processor.c',
)
+107
View File
@@ -0,0 +1,107 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "stft_buffer.h"
#include <stdlib.h>
#include <string.h>
struct StftBuffer {
uint32_t read_position;
uint32_t start_position;
uint32_t stft_frame_size;
uint32_t block_step;
float* in_fifo;
float* out_fifo;
};
StftBuffer* stft_buffer_initialize(const uint32_t stft_frame_size,
const uint32_t start_position,
const uint32_t block_step) {
StftBuffer* self = (StftBuffer*)calloc(1U, sizeof(StftBuffer));
if (!self) {
return NULL;
}
self->stft_frame_size = stft_frame_size;
self->start_position = start_position;
self->block_step = block_step;
self->read_position = self->start_position;
self->in_fifo = (float*)calloc(self->stft_frame_size, sizeof(float));
self->out_fifo = (float*)calloc(self->stft_frame_size, sizeof(float));
if (!self->in_fifo || !self->out_fifo) {
stft_buffer_free(self);
return NULL;
}
return self;
}
void stft_buffer_free(StftBuffer* self) {
if (!self) {
return;
}
free(self->in_fifo);
free(self->out_fifo);
free(self);
}
bool is_buffer_full(StftBuffer* self) {
if (self->read_position == self->stft_frame_size) {
return true;
}
return false;
}
float stft_buffer_fill(StftBuffer* self, const float input_sample) {
float sample_value = 0.F;
self->in_fifo[self->read_position] = input_sample;
sample_value = self->out_fifo[self->read_position - self->start_position];
if (self->read_position < self->stft_frame_size) {
self->read_position++; // Advance
}
return sample_value;
}
bool stft_buffer_advance_block(StftBuffer* self,
const float* reconstructed_signal) {
if (!reconstructed_signal) {
return false;
}
self->read_position = self->start_position; // Reset read
memmove(self->in_fifo, &self->in_fifo[self->block_step],
sizeof(float) * self->start_position);
memcpy(self->out_fifo, reconstructed_signal,
sizeof(float) * self->block_step);
return true;
}
float* get_full_buffer_block(StftBuffer* self) {
return self->in_fifo;
}
+38
View File
@@ -0,0 +1,38 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef STFT_BUFFER_H
#define STFT_BUFFER_H
#include <stdbool.h>
#include <stdint.h>
typedef struct StftBuffer StftBuffer;
StftBuffer* stft_buffer_initialize(uint32_t stft_frame_size,
uint32_t start_position,
uint32_t block_step);
void stft_buffer_free(StftBuffer* self);
bool is_buffer_full(StftBuffer* self);
float stft_buffer_fill(StftBuffer* self, float input_sample);
bool stft_buffer_advance_block(StftBuffer* self,
const float* reconstructed_signal);
float* get_full_buffer_block(StftBuffer* self);
#endif
Binary file not shown.
@@ -0,0 +1,196 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "stft_processor.h"
#include "stft_buffer.h"
#include "stft_windows.h"
#include <stdlib.h>
#include <string.h>
struct StftProcessor {
uint32_t input_latency;
uint32_t hop;
uint32_t overlap_factor;
uint32_t fft_size;
uint32_t frame_size;
float* output_accumulator;
float* tmp_buffer;
FftTransform* fft_transform;
StftBuffer* stft_buffer;
StftWindows* stft_windows;
};
StftProcessor* stft_processor_initialize(const uint32_t sample_rate,
const float stft_frame_size,
const uint32_t overlap_factor,
ZeroPaddingType padding_type,
const uint32_t zeropadding_amount,
WindowTypes input_window,
WindowTypes output_window) {
if (sample_rate == 0 || stft_frame_size <= 0.0f || overlap_factor == 0) {
return NULL;
}
StftProcessor* self = (StftProcessor*)calloc(1U, sizeof(StftProcessor));
if (!self) {
return NULL;
}
self->frame_size =
(uint32_t)((stft_frame_size / 1000.F) * (float)sample_rate);
self->fft_transform = fft_transform_initialize(self->frame_size, padding_type,
zeropadding_amount);
if (!self->fft_transform) {
stft_processor_free(self);
return NULL;
}
self->fft_size = get_fft_size(self->fft_transform);
self->overlap_factor = overlap_factor;
self->hop = self->frame_size / self->overlap_factor;
self->input_latency = self->frame_size;
self->output_accumulator =
(float*)calloc(self->frame_size * 2L, sizeof(float));
if (!self->output_accumulator) {
stft_processor_free(self);
return NULL;
}
self->tmp_buffer = (float*)calloc(self->frame_size, sizeof(float));
if (!self->tmp_buffer) {
stft_processor_free(self);
return NULL;
}
self->stft_buffer = stft_buffer_initialize(
self->frame_size, self->input_latency - self->hop, self->hop);
if (!self->stft_buffer) {
stft_processor_free(self);
return NULL;
}
self->stft_windows = stft_window_initialize(
self->fft_size, self->overlap_factor, input_window, output_window);
if (!self->stft_windows) {
stft_processor_free(self);
return NULL;
}
return self;
}
void stft_processor_free(StftProcessor* self) {
if (!self) {
return;
}
if (self->stft_buffer) {
stft_buffer_free(self->stft_buffer);
}
if (self->stft_windows) {
stft_window_free(self->stft_windows);
}
if (self->fft_transform) {
fft_transform_free(self->fft_transform);
}
if (self->output_accumulator) {
free(self->output_accumulator);
}
if (self->tmp_buffer) {
free(self->tmp_buffer);
}
free(self);
}
bool stft_processor_run(StftProcessor* self, const uint32_t number_of_samples,
const float* input, float* output,
spectral_processing spectral_processing,
SpectralProcessorHandle spectral_processor) {
if (!self || !input || !output || number_of_samples == 0U) {
return false;
}
for (uint32_t k = 0U; k < number_of_samples; k++) {
// Start filling buffer sample per sample
output[k] = stft_buffer_fill(self->stft_buffer, input[k]);
if (is_buffer_full(self->stft_buffer)) {
fft_load_input_samples(self->fft_transform,
get_full_buffer_block(self->stft_buffer));
// STFT Analysis
stft_window_apply(self->stft_windows,
get_fft_input_buffer(self->fft_transform),
INPUT_WINDOW);
compute_forward_fft(self->fft_transform);
// Apply processing
spectral_processing(spectral_processor,
get_fft_output_buffer(self->fft_transform));
// STFT Synthesis
compute_backward_fft(self->fft_transform);
stft_window_apply(self->stft_windows,
get_fft_input_buffer(self->fft_transform),
OUTPUT_WINDOW);
fft_get_output_samples(self->fft_transform, self->tmp_buffer);
// STFT Overlap Add
for (uint32_t j = 0U; j < self->frame_size; j++) {
self->output_accumulator[j] += self->tmp_buffer[j];
}
stft_buffer_advance_block(self->stft_buffer, self->output_accumulator);
memmove(self->output_accumulator, &self->output_accumulator[self->hop],
self->frame_size * sizeof(float));
}
}
return true;
}
uint32_t get_stft_latency(StftProcessor* self) {
if (!self) {
return 0;
}
return self->input_latency;
}
uint32_t get_stft_fft_size(StftProcessor* self) {
if (!self) {
return 0;
}
return self->fft_size;
}
uint32_t get_stft_real_spectrum_size(StftProcessor* self) {
if (!self) {
return 0;
}
return get_fft_real_spectrum_size(self->fft_transform);
}
@@ -0,0 +1,50 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef STFT_PROCESSOR_H
#define STFT_PROCESSOR_H
#include "../spectral_processor.h"
#include "../utils/spectral_utils.h"
#include "fft_transform.h"
#include <stdbool.h>
#include <stdint.h>
typedef struct StftProcessor StftProcessor;
StftProcessor* stft_processor_initialize(
uint32_t sample_rate, float stft_frame_size, uint32_t overlap_factor,
ZeroPaddingType padding_type, uint32_t zeropadding_amount,
WindowTypes input_window, WindowTypes output_window);
void stft_processor_free(StftProcessor* self);
uint32_t get_stft_latency(StftProcessor* self);
uint32_t get_stft_fft_size(StftProcessor* self);
uint32_t get_stft_real_spectrum_size(StftProcessor* self);
// Receives an input and output buffer with a a number_of_samples and does the
// STFT transform applying any spectral_processing. It works similar to qsort,
// because it receives a function pointer of any spectral processing that needs
// to be applied in between the analysis and the synthesis
bool stft_processor_run(StftProcessor* self, uint32_t number_of_samples,
const float* input, float* output,
spectral_processing spectral_processing,
SpectralProcessorHandle spectral_processor);
#endif
Binary file not shown.
+106
View File
@@ -0,0 +1,106 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "stft_windows.h"
#include <stdlib.h>
static float get_windows_scale_factor(StftWindows* self,
uint32_t overlap_factor);
struct StftWindows {
float* input_window;
float* output_window;
uint32_t stft_frame_size;
float scale_factor;
};
StftWindows* stft_window_initialize(const uint32_t stft_frame_size,
const uint32_t overlap_factor,
const WindowTypes input_window,
const WindowTypes output_window) {
StftWindows* self = (StftWindows*)calloc(1U, sizeof(StftWindows));
if (!self) {
return NULL;
}
self->stft_frame_size = stft_frame_size;
self->input_window = (float*)calloc(self->stft_frame_size, sizeof(float));
self->output_window = (float*)calloc(self->stft_frame_size, sizeof(float));
if (!self->input_window || !self->output_window) {
stft_window_free(self);
return NULL;
}
(void)get_fft_window(self->input_window, self->stft_frame_size, input_window);
(void)get_fft_window(self->output_window, self->stft_frame_size,
output_window);
self->scale_factor = get_windows_scale_factor(self, overlap_factor);
return self;
}
void stft_window_free(StftWindows* self) {
if (!self) {
return;
}
free(self->input_window);
free(self->output_window);
free(self);
}
static float get_windows_scale_factor(StftWindows* self,
const uint32_t overlap_factor) {
if (overlap_factor < 2) {
return 0.F;
}
float sum = 0.F;
for (uint32_t i = 0U; i < self->stft_frame_size; i++) {
sum += self->input_window[i] * self->output_window[i];
}
return sum * (float)overlap_factor;
}
bool stft_window_apply(StftWindows* self, float* frame,
const WindowPlace place) {
if (!self || !frame) {
return false;
}
for (uint32_t i = 0U; i < self->stft_frame_size; i++) {
switch (place) {
case INPUT_WINDOW:
frame[i] *= self->input_window[i];
break;
case OUTPUT_WINDOW:
frame[i] *= self->output_window[i] / self->scale_factor;
break;
default:
break;
}
}
return true;
}
@@ -0,0 +1,39 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef STFT_WINDOW_H
#define STFT_WINDOW_H
#include "../utils/spectral_utils.h"
#include <stdbool.h>
#include <stdint.h>
typedef struct StftWindows StftWindows;
typedef enum WindowPlace { INPUT_WINDOW = 1, OUTPUT_WINDOW = 2 } WindowPlace;
StftWindows* stft_window_initialize(uint32_t stft_frame_size,
uint32_t overlap_factor,
WindowTypes input_window,
WindowTypes output_window);
void stft_window_free(StftWindows* self);
bool stft_window_apply(StftWindows* self, float* frame, WindowPlace place);
#endif
Binary file not shown.
@@ -0,0 +1,98 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "denoise_mixer.h"
#include <stdlib.h>
#include <string.h>
struct DenoiseMixer {
float* residual_spectrum;
float* denoised_spectrum;
uint32_t fft_size;
uint32_t real_spectrum_size;
uint32_t sample_rate;
uint32_t hop;
};
DenoiseMixer* denoise_mixer_initialize(uint32_t fft_size, uint32_t sample_rate,
uint32_t hop) {
DenoiseMixer* self = (DenoiseMixer*)calloc(1U, sizeof(DenoiseMixer));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->sample_rate = sample_rate;
self->hop = hop;
self->residual_spectrum = (float*)calloc((self->fft_size), sizeof(float));
self->denoised_spectrum = (float*)calloc((self->fft_size), sizeof(float));
if (!self->residual_spectrum || !self->denoised_spectrum) {
denoise_mixer_free(self);
return NULL;
}
return self;
}
void denoise_mixer_free(DenoiseMixer* self) {
if (!self) {
return;
}
free(self->residual_spectrum);
free(self->denoised_spectrum);
free(self);
}
bool denoise_mixer_run(DenoiseMixer* self, float* fft_spectrum,
const float* gain_spectrum,
DenoiseMixerParameters parameters) {
if (!fft_spectrum || !gain_spectrum) {
return false;
}
// Get denoised spectrum - Apply to both real and complex parts
for (uint32_t k = 0U; k < self->fft_size; k++) {
self->denoised_spectrum[k] = fft_spectrum[k] * gain_spectrum[k];
}
// Get residual spectrum - Apply to both real and complex parts
for (uint32_t k = 0U; k < self->fft_size; k++) {
self->residual_spectrum[k] = fft_spectrum[k] - self->denoised_spectrum[k];
}
// Mix denoised and residual - Now a simple toggle
if (parameters.residual_listen) {
for (uint32_t k = 0U; k < self->fft_size; k++) {
fft_spectrum[k] = self->residual_spectrum[k];
}
} else {
for (uint32_t k = 0U; k < self->fft_size; k++) {
fft_spectrum[k] = self->denoised_spectrum[k];
}
}
return true;
}
@@ -0,0 +1,42 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef DENOISE_MIXER_H
#define DENOISE_MIXER_H
#include <stdbool.h>
#include <stdint.h>
typedef struct DenoiseMixerParameters {
float noise_level;
bool residual_listen;
float whitening_amount;
} DenoiseMixerParameters;
typedef struct DenoiseMixer DenoiseMixer;
DenoiseMixer* denoise_mixer_initialize(uint32_t fft_size, uint32_t sample_rate,
uint32_t hop);
void denoise_mixer_free(DenoiseMixer* self);
bool denoise_mixer_run(DenoiseMixer* self, float* fft_spectrum,
const float* gain_spectrum,
DenoiseMixerParameters parameters);
#endif
Binary file not shown.
@@ -0,0 +1,54 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "general_utils.h"
#include <float.h>
#include <math.h>
#include <stdlib.h>
float sanitize_denormal(float value) {
if (!isnormal(value)) {
value = 0.F;
}
return value;
}
float from_db_to_coefficient(const float value_db) {
return expf(value_db / 20.F * logf(10.F));
}
float remap_percentage_log_like_unity(const float value) {
return 1.F - expf(-3.F * (value));
}
int get_next_divisible_two(int number) {
int q = number / 2;
int n1 = 2 * q;
int n2 = (number * 2) > 0 ? (2 * (q + 1)) : (2 * (q - 1));
if (abs(number - n1) < abs(number - n2)) {
return n1;
}
return n2;
}
int get_next_power_two(int number) {
return (int)roundf(powf(2.F, ceilf(log2f((float)number))));
}
@@ -0,0 +1,39 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef GENERAL_UTILS_H
#define GENERAL_UTILS_H
#include <stdbool.h>
#include <stdint.h>
// Compile-time validation
_Static_assert(sizeof(float) >= 4, "float must be at least 32 bits");
_Static_assert(sizeof(double) >= 8, "double must be at least 64 bits");
__attribute__((warn_unused_result)) float sanitize_denormal(float value);
__attribute__((warn_unused_result)) float from_db_to_coefficient(
float value_db);
__attribute__((warn_unused_result)) float remap_percentage_log_like_unity(
float value);
__attribute__((warn_unused_result)) int get_next_divisible_two(int number);
__attribute__((warn_unused_result)) int get_next_power_two(int number);
#endif
Binary file not shown.
@@ -0,0 +1,7 @@
shared_sources += files(
'general_utils.c',
'denoise_mixer.c',
'spectral_features.c',
'spectral_utils.c',
'spectral_trailing_buffer.c',
)
@@ -0,0 +1,189 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "spectral_features.h"
#include <math.h>
#include <stdlib.h>
struct SpectralFeatures {
float* power_spectrum;
float* phase_spectrum;
float* magnitude_spectrum;
uint32_t real_spectrum_size;
};
SpectralFeatures* spectral_features_initialize(
const uint32_t real_spectrum_size) {
SpectralFeatures* self =
(SpectralFeatures*)calloc(1U, sizeof(SpectralFeatures));
if (!self) {
return NULL;
}
self->real_spectrum_size = real_spectrum_size;
self->power_spectrum =
(float*)calloc(self->real_spectrum_size, sizeof(float));
self->phase_spectrum =
(float*)calloc(self->real_spectrum_size, sizeof(float));
self->magnitude_spectrum =
(float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->power_spectrum || !self->phase_spectrum ||
!self->magnitude_spectrum) {
spectral_features_free(self);
return NULL;
}
return self;
}
void spectral_features_free(SpectralFeatures* self) {
if (!self) {
return;
}
free(self->power_spectrum);
free(self->phase_spectrum);
free(self->magnitude_spectrum);
free(self);
}
float* get_power_spectrum(SpectralFeatures* self) {
return self->power_spectrum;
}
float* get_magnitude_spectrum(SpectralFeatures* self) {
return self->magnitude_spectrum;
}
float* get_phase_spectrum(SpectralFeatures* self) {
return self->phase_spectrum;
}
static bool compute_power_spectrum(SpectralFeatures* self,
const float* fft_spectrum,
const uint32_t fft_spectrum_size) {
if (!self || !fft_spectrum || !fft_spectrum_size) {
return false;
}
const uint32_t n = fft_spectrum_size;
const uint32_t n2 = n / 2U;
const bool is_even = (n % 2U == 0);
// DC bin
self->power_spectrum[0] = fft_spectrum[0] * fft_spectrum[0];
// Complex bins
for (uint32_t k = 1U; k < n2; k++) {
float real = fft_spectrum[k];
float imag = fft_spectrum[n - k];
self->power_spectrum[k] = (real * real) + (imag * imag);
}
// Nyquist bin
if (is_even) {
self->power_spectrum[n2] = fft_spectrum[n2] * fft_spectrum[n2];
}
return true;
}
static bool compute_magnitude_spectrum(SpectralFeatures* self,
const float* fft_spectrum,
const uint32_t fft_spectrum_size) {
if (!self || !fft_spectrum || !fft_spectrum_size) {
return false;
}
const uint32_t n = fft_spectrum_size;
const uint32_t n2 = n / 2U;
const bool is_even = (n % 2U == 0);
// DC bin
self->magnitude_spectrum[0] = fabsf(fft_spectrum[0]);
// Complex bins
for (uint32_t k = 1U; k < n2; k++) {
self->magnitude_spectrum[k] = hypotf(fft_spectrum[k], fft_spectrum[n - k]);
}
// Nyquist bin
if (is_even) {
self->magnitude_spectrum[n2] = fabsf(fft_spectrum[n2]);
}
return true;
}
static bool compute_phase_spectrum(SpectralFeatures* self,
const float* fft_spectrum,
const uint32_t fft_spectrum_size) {
if (!self || !fft_spectrum || !fft_spectrum_size) {
return false;
}
const uint32_t n = fft_spectrum_size;
const uint32_t n2 = n / 2U;
const bool is_even = (n % 2U == 0);
// DC bin - purely real
self->phase_spectrum[0] = atan2f(0.F, fft_spectrum[0]);
// Complex bins
for (uint32_t k = 1U; k < n2; k++) {
float real = fft_spectrum[k];
float imag = fft_spectrum[n - k];
self->phase_spectrum[k] = atan2f(imag, real);
}
// Nyquist bin - purely real
if (is_even) {
self->phase_spectrum[n2] = atan2f(0.F, fft_spectrum[n2]);
}
return true;
}
float* get_spectral_feature(SpectralFeatures* self, const float* fft_spectrum,
uint32_t fft_spectrum_size, SpectrumType type) {
if (!self || !fft_spectrum || fft_spectrum_size == 0U) {
return NULL;
}
switch (type) {
case POWER_SPECTRUM:
compute_power_spectrum(self, fft_spectrum, fft_spectrum_size);
return get_power_spectrum(self);
break;
case MAGNITUDE_SPECTRUM:
compute_magnitude_spectrum(self, fft_spectrum, fft_spectrum_size);
return get_magnitude_spectrum(self);
break;
case PHASE_SPECTRUM:
compute_phase_spectrum(self, fft_spectrum, fft_spectrum_size);
return get_phase_spectrum(self);
break;
default:
return NULL;
break;
}
}
@@ -0,0 +1,40 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECTRAL_FEATURES_H
#define SPECTRAL_FEATURES_H
#include <stdbool.h>
#include <stdint.h>
typedef struct SpectralFeatures SpectralFeatures;
typedef enum SpectrumType {
POWER_SPECTRUM = 0,
MAGNITUDE_SPECTRUM = 1,
PHASE_SPECTRUM = 2,
} SpectrumType;
SpectralFeatures* spectral_features_initialize(uint32_t real_spectrum_size);
void spectral_features_free(SpectralFeatures* self);
float* get_spectral_feature(SpectralFeatures* self, const float* fft_spectrum,
uint32_t fft_spectrum_size, SpectrumType type);
#endif
Binary file not shown.
@@ -0,0 +1,90 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "spectral_trailing_buffer.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
struct SpectralTrailingBuffer {
uint32_t real_spectrum_size;
uint32_t buffer_size;
float* buffer;
};
SpectralTrailingBuffer* spectral_trailing_buffer_initialize(
const uint32_t real_spectrum_size, const uint32_t buffer_size) {
SpectralTrailingBuffer* self =
(SpectralTrailingBuffer*)calloc(1U, sizeof(SpectralTrailingBuffer));
if (!self) {
return NULL;
}
self->real_spectrum_size = real_spectrum_size;
self->buffer_size = buffer_size;
self->buffer = (float*)calloc(
((size_t)self->real_spectrum_size * (size_t)self->buffer_size),
sizeof(float));
if (!self->buffer) {
spectral_trailing_buffer_free(self);
return NULL;
}
return self;
}
void spectral_trailing_buffer_free(SpectralTrailingBuffer* self) {
if (!self) {
return;
}
free(self->buffer);
free(self);
}
bool spectral_trailing_buffer_push_back(SpectralTrailingBuffer* self,
const float* input_spectrum) {
if (!input_spectrum) {
return false;
}
memmove(self->buffer, &self->buffer[self->real_spectrum_size],
sizeof(float) * self->real_spectrum_size * (self->buffer_size - 1U));
memcpy(&self->buffer[(size_t)self->real_spectrum_size *
(size_t)(self->buffer_size - 1U)],
input_spectrum, sizeof(float) * self->real_spectrum_size);
return true;
}
float* get_trailing_spectral_buffer(SpectralTrailingBuffer* self) {
return self->buffer;
}
uint32_t get_spectrum_buffer_size(SpectralTrailingBuffer* self) {
return self->buffer_size;
}
uint32_t get_spectrum_size(SpectralTrailingBuffer* self) {
return self->real_spectrum_size;
}
@@ -0,0 +1,38 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECTRAL_TRAILING_BUFFER_H
#define SPECTRAL_TRAILING_BUFFER_H
#include <stdbool.h>
#include <stdint.h>
typedef struct SpectralTrailingBuffer SpectralTrailingBuffer;
SpectralTrailingBuffer* spectral_trailing_buffer_initialize(
uint32_t real_spectrum_size, uint32_t buffer_size);
void spectral_trailing_buffer_free(SpectralTrailingBuffer* self);
bool spectral_trailing_buffer_push_back(SpectralTrailingBuffer* self,
const float* input_spectrum);
float* get_trailing_spectral_buffer(SpectralTrailingBuffer* self);
uint32_t get_spectrum_buffer_size(SpectralTrailingBuffer* self);
uint32_t get_spectrum_size(SpectralTrailingBuffer* self);
#endif
@@ -0,0 +1,280 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "spectral_utils.h"
#include "../configurations.h"
#include "general_utils.h"
#include <float.h>
#include <math.h>
#include <stdlib.h>
static float blackman(const uint32_t bin_index, const uint32_t fft_size) {
const float p = ((float)(bin_index)) / ((float)(fft_size));
return sanitize_denormal(0.42F - (0.5F * cosf(2.F * M_PIf * p)) +
(0.08F * cosf(4.F * M_PIf * p)));
}
static float hanning(const uint32_t bin_index, const uint32_t fft_size) {
const float p = ((float)(bin_index)) / ((float)(fft_size));
return sanitize_denormal(0.5F - (0.5F * cosf(2.F * M_PIf * p)));
}
static float hamming(const uint32_t bin_index, const uint32_t fft_size) {
const float p = ((float)(bin_index)) / ((float)(fft_size));
return sanitize_denormal(0.54F - (0.46F * cosf(2.F * M_PIf * p)));
}
static float vorbis(const uint32_t bin_index, const uint32_t fft_size) {
const float p = ((float)(bin_index)) / ((float)(fft_size));
return sanitize_denormal(sinf(M_PIf / 2.F * powf(sinf(M_PIf * p), 2.F)));
}
bool get_fft_window(float* window, const uint32_t fft_size,
const WindowTypes window_type) {
if (!window || !fft_size) {
return false;
}
for (uint32_t k = 0; k < fft_size; k++) {
switch (window_type) {
case HANN_WINDOW:
window[k] = hanning(k, fft_size);
break;
case HAMMING_WINDOW:
window[k] = hamming(k, fft_size);
break;
case BLACKMAN_WINDOW:
window[k] = blackman(k, fft_size);
break;
case VORBIS_WINDOW:
window[k] = vorbis(k, fft_size);
break;
default:
break;
}
}
return true;
}
bool initialize_spectrum_with_value(float* spectrum, uint32_t spectrum_size,
const float value) {
if (!spectrum || spectrum_size == 0U) {
return false;
}
for (uint32_t i = 0U; i < spectrum_size; i++) {
spectrum[i] = value;
}
return true;
}
float max_spectral_value(const float* spectrum,
const uint32_t real_spectrum_size) {
if (!spectrum || real_spectrum_size == 0U) {
return 0.F;
}
float max = spectrum[0];
for (uint32_t k = 1U; k < real_spectrum_size; k++) {
max = fmaxf(spectrum[k], max);
}
return max;
}
float min_spectral_value(const float* spectrum,
const uint32_t real_spectrum_size) {
if (!spectrum || real_spectrum_size == 0U) {
return 0.F;
}
float min = spectrum[0];
for (uint32_t k = 1U; k < real_spectrum_size; k++) {
min = fminf(spectrum[k], min);
}
return min;
}
bool min_spectrum_float(float* spectrum_one, const float* spectrum_two,
const uint32_t spectrum_size) {
if (!spectrum_one || !spectrum_two || spectrum_size == 0U) {
return false;
}
for (uint32_t k = 0; k < spectrum_size; k++) {
spectrum_one[k] = fminf(spectrum_one[k], spectrum_two[k]);
}
return true;
}
bool max_spectrum_float(float* spectrum_one, const float* spectrum_two,
const uint32_t spectrum_size) {
if (!spectrum_one || !spectrum_two || spectrum_size == 0U) {
return false;
}
for (uint32_t k = 0; k < spectrum_size; k++) {
spectrum_one[k] = fmaxf(spectrum_one[k], spectrum_two[k]);
}
return true;
}
bool min_spectrum_double(double* spectrum_one, const double* spectrum_two,
const uint32_t spectrum_size) {
if (!spectrum_one || !spectrum_two || spectrum_size == 0U) {
return false;
}
for (uint32_t k = 0; k < spectrum_size; k++) {
spectrum_one[k] = fmin(spectrum_one[k], spectrum_two[k]);
}
return true;
}
bool max_spectrum_double(double* spectrum_one, const double* spectrum_two,
const uint32_t spectrum_size) {
if (!spectrum_one || !spectrum_two || spectrum_size == 0U) {
return false;
}
for (uint32_t k = 0; k < spectrum_size; k++) {
spectrum_one[k] = fmax(spectrum_one[k], spectrum_two[k]);
}
return true;
}
bool direct_matrix_to_vector_spectral_convolution(const float* matrix_spectum,
const float* spectrum,
float* out_spectrum,
uint32_t spectrum_size) {
if (!matrix_spectum || !spectrum || !out_spectrum || spectrum_size == 0U) {
return false;
}
for (uint32_t i = 0U; i < spectrum_size; i++) {
out_spectrum[i] = 0.F;
for (uint32_t j = 0U; j < spectrum_size; j++) {
out_spectrum[i] +=
(matrix_spectum[(i * spectrum_size) + j] * spectrum[j]);
}
}
return true;
}
float fft_bin_to_freq(const uint32_t bin_index, const uint32_t sample_rate,
const uint32_t fft_size) {
return (float)bin_index * ((float)sample_rate / (float)fft_size);
}
uint32_t freq_to_fft_bin(const float freq, const uint32_t sample_rate,
const uint32_t fft_size) {
return (uint32_t)((freq / ((float)sample_rate / (float)fft_size)) + 0.5f);
}
float spectral_flux(const float* spectrum, const float* previous_spectrum,
const uint32_t spectrum_size) {
if (!spectrum || !previous_spectrum || spectrum_size == 0U) {
return 0.F;
}
float spectral_flux = 0.F;
for (uint32_t i = 0U; i < spectrum_size; i++) {
const float temp = sqrtf(spectrum[i]) - sqrtf(previous_spectrum[i]);
spectral_flux += (temp + fabsf(temp)) / 2.F;
}
return spectral_flux;
}
bool get_rolling_mean_spectrum(float* averaged_spectrum,
const float* current_spectrum,
const uint32_t number_of_blocks,
const uint32_t spectrum_size) {
if (!averaged_spectrum || !current_spectrum || spectrum_size == 0U) {
return false;
}
for (uint32_t k = 0U; k < spectrum_size; k++) {
if (number_of_blocks <= 1U) {
averaged_spectrum[k] = current_spectrum[k];
} else {
averaged_spectrum[k] += (current_spectrum[k] - averaged_spectrum[k]) /
(float)number_of_blocks;
}
}
return true;
}
static int min_max_comparator(const void* a, const void* b) {
float x = *(const float*)a;
float y = *(const float*)b;
return x >= y ? 1 : -1;
}
static float find_median(const float* array, uint32_t array_size) {
float median = 0.F;
if (array_size % 2 == 0) {
// if number of elements are even
median = (array[(array_size - 1U) / 2U] + array[array_size / 2U]) / 2.F;
} else {
// if number of elements are odd
median = array[array_size / 2U];
}
return median;
}
bool get_rolling_median_spectrum(float* median_spectrum,
const float* current_spectrum_buffer,
const uint32_t number_of_blocks,
const uint32_t spectrum_size) {
if (!median_spectrum || !current_spectrum_buffer || spectrum_size == 0U) {
return false;
}
float tmp_buffer[number_of_blocks];
for (uint32_t i = 0U; i < spectrum_size; i++) {
for (uint32_t j = 0U; j < number_of_blocks; j++) {
tmp_buffer[j] = current_spectrum_buffer[(j * spectrum_size) + i];
}
// Sorting array
qsort(tmp_buffer, number_of_blocks, sizeof(float), min_max_comparator);
float median_of_buffer = find_median(tmp_buffer, number_of_blocks);
// Taking the max of the median
if (median_of_buffer > median_spectrum[i]) {
median_spectrum[i] = median_of_buffer;
}
}
return true;
}
@@ -0,0 +1,78 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECTRAL_UTILS_H
#define SPECTRAL_UTILS_H
#include <stdbool.h>
#include <stdint.h>
typedef enum WindowTypes {
HANN_WINDOW = 0,
HAMMING_WINDOW = 1,
BLACKMAN_WINDOW = 2,
VORBIS_WINDOW = 3
} WindowTypes;
bool get_fft_window(float* window, uint32_t fft_size, WindowTypes window_type);
bool initialize_spectrum_with_value(float* spectrum, uint32_t spectrum_size,
float value);
bool direct_matrix_to_vector_spectral_convolution(const float* matrix_spectum,
const float* spectrum,
float* out_spectrum,
uint32_t spectrum_size);
float max_spectral_value(const float* spectrum, uint32_t real_spectrum_size);
float min_spectral_value(const float* spectrum, uint32_t real_spectrum_size);
#define min_spectrum(spectrum_one, spectrum_two, spectrum_size) \
_Generic((spectrum_one), \
float*: min_spectrum_float, \
double*: min_spectrum_double, \
default: min_spectrum_float)(spectrum_one, spectrum_two, spectrum_size)
#define max_spectrum(spectrum_one, spectrum_two, spectrum_size) \
_Generic((spectrum_one), \
float*: max_spectrum_float, \
double*: max_spectrum_double, \
default: max_spectrum_float)(spectrum_one, spectrum_two, spectrum_size)
bool min_spectrum_float(float* spectrum_one, const float* spectrum_two,
uint32_t spectrum_size);
bool max_spectrum_float(float* spectrum_one, const float* spectrum_two,
uint32_t spectrum_size);
bool min_spectrum_double(double* spectrum_one, const double* spectrum_two,
uint32_t spectrum_size);
bool max_spectrum_double(double* spectrum_one, const double* spectrum_two,
uint32_t spectrum_size);
float fft_bin_to_freq(uint32_t bin_index, uint32_t sample_rate,
uint32_t fft_size);
uint32_t freq_to_fft_bin(float freq, uint32_t sample_rate, uint32_t fft_size);
float spectral_flux(const float* spectrum, const float* previous_spectrum,
uint32_t spectrum_size);
bool get_rolling_mean_spectrum(float* averaged_spectrum,
const float* current_spectrum,
uint32_t number_of_blocks,
uint32_t spectrum_size);
bool get_rolling_median_spectrum(float* median_spectrum,
const float* current_spectrum_buffer,
uint32_t number_of_blocks,
uint32_t spectrum_size);
#endif
Binary file not shown.