first commit
This commit is contained in:
+354
@@ -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;
|
||||
}
|
||||
+54
@@ -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
|
||||
BIN
Binary file not shown.
@@ -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
|
||||
@@ -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.
@@ -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
|
||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
shared_sources += files(
|
||||
'gain_estimators.c',
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
shared_sources = []
|
||||
subdir('gain_estimation')
|
||||
subdir('noise_estimation')
|
||||
subdir('post_estimation')
|
||||
subdir('pre_estimation')
|
||||
subdir('stft')
|
||||
subdir('utils')
|
||||
+401
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+48
@@ -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
|
||||
BIN
Binary file not shown.
@@ -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
|
||||
Binary file not shown.
@@ -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
|
||||
Binary file not shown.
+165
@@ -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));
|
||||
}
|
||||
}
|
||||
+36
@@ -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
|
||||
BIN
Binary file not shown.
@@ -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
|
||||
Binary file not shown.
@@ -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
|
||||
Binary file not shown.
@@ -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',
|
||||
)
|
||||
+277
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+54
@@ -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
|
||||
BIN
Binary file not shown.
@@ -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
|
||||
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 "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
|
||||
Binary file not shown.
@@ -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.
@@ -0,0 +1,6 @@
|
||||
shared_sources += files(
|
||||
'fft_transform.c',
|
||||
'stft_windows.c',
|
||||
'stft_buffer.c',
|
||||
'stft_processor.c',
|
||||
)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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.
@@ -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.
Reference in New Issue
Block a user