Chuyển đổi tín hiệu từ miền thời gian sang miền tần số
Hàm này thực hiện biến đổi Fourier rời rạc (FFT) để phân tích tín hiệu âm thanh thành các thành phần tần số riêng lẻ.
// Đầu vào:
// * |time_data| là tín hiệu trong miền thời gian.
// * |time_data_length| là độ dài của bộ đệm phân tích.
// * |magnitude_length| là độ dài của phổ biên độ, bằng độ dài của |real| và |imag| (time_data_length / 2 + 1).
// Đầu ra:
// * |time_data| là tín hiệu trong miền tần số.
// * |real| là phần thực trong miền tần số.
// * |imag| là phần ảo trong miền tần số.
// * |magn| là biên độ đã tính toán của tín hiệu trong miền tần số.
static void FFT(NoiseSuppressionC *self,
float *time_data,
size_t time_data_length,
size_t magnitude_length,
float *real,
float *imag,
float *magn, float *lmagn, int prev_calc, float *signalEnergy, float *sumMagn) {
size_t i;
// Đảm bảo độ dài biên độ chính xác.
assert(magnitude_length == time_data_length / 2 + 1);
// Thực hiện biến đổi FFT số thực.
WebRtc_rdft(time_data_length, 1, time_data, self->ip, self->wfft);
// Khởi tạo thành phần tần số đầu tiên và cuối cùng trong miền tần số.
imag[0] = 0;
real[0] = time_data[0];
magn[0] = fabsf(real[0]);
imag[magnitude_length - 1] = 0;
real[magnitude_length - 1] = time_data[1];
magn[magnitude_length - 1] = fabsf(real[magnitude_length - 1]);
// Con trỏ đến dữ liệu miền thời gian, bắt đầu xử lý.
float *time_data_ptr = time_data + 2;
// Nếu đã tính toán trước đó, thực thi logic bên dưới.
if (prev_calc == 1) {
// Tính năng lượng của thành phần tần số đầu tiên và cuối cùng.
float first = real[0] * real[0] + imag[0] * imag[0];
float last = real[magnitude_length - 1] * real[magnitude_length - 1] +
imag[magnitude_length - 1] * imag[magnitude_length - 1];
*signalEnergy = first + last;
*sumMagn = sqrtf(first + epsilon_squ) + 2.f + sqrtf(last + epsilon_squ);
lmagn[0] = log1pf(magn[0]);
lmagn[magnitude_length - 1] = log1pf(magn[magnitude_length - 1]);
// Xử lý các thành phần tần số còn lại (trừ đầu và cuối).
for (i = 1; i < magnitude_length - 1; ++i) {
real[i] = time_data_ptr[0];
imag[i] = time_data_ptr[1];
// Tính năng lượng.
const float energy = real[i] * real[i] + imag[i] * imag[i];
*signalEnergy += energy;
// Tính phổ biên độ.
magn[i] = sqrtf(energy + epsilon_squ);
*sumMagn += magn[i];
lmagn[i] = log1pf(magn[i]);
time_data_ptr += 2;
}
}
else {
// Nếu chưa tính toán trước đó, chỉ tính phổ biên độ.
for (i = 1; i < magnitude_length - 1; ++i) {
real[i] = time_data_ptr[0];
imag[i] = time_data_ptr[1];
// Tính phổ biên độ.
magn[i] = sqrtf(real[i] * real[i] + imag[i] * imag[i] + epsilon_squ);
time_data_ptr += 2;
}
}
}
Hàm IFFT tĩnh để chuyển đổi tín hiệu từ miền tần số về miền thời gian
Hàm này thực hiện biến đổi Fourier ngược (IFFT) để tái tạo tín hiệu âm thanh trong miền thời gian từ các thành phần tần số đã được xử lý.
// Tham số đầu vào:
// * |real| là phần thực trong miền tần số.
// * |imag| là phần ảo trong miền tần số.
// * |magnitude_length| là độ dài của phổ biên độ, bằng độ dài của |real| và |imag|.
// * |time_data_length| là độ dài của bộ đệm phân tích (2 * (magnitude_length - 1)).
// Tham số đầu ra:
// * |time_data| là tín hiệu trong miền thời gian.
static void IFFT(NoiseSuppressionC *self,
const float *real,
const float *imag,
size_t magnitude_length,
size_t time_data_length,
float *time_data) {
size_t i;
// Xác nhận độ dài dữ liệu thời gian chính xác.
assert(time_data_length == 2 * (magnitude_length - 1));
// Khởi tạo hai phần tử đầu tiên của dữ liệu thời gian.
time_data[0] = real[0];
time_data[1] = real[magnitude_length - 1];
// Con trỏ tới mảng dữ liệu thời gian, bắt đầu từ phần tử thứ ba.
float *time_data_ptr = time_data + 2;
// Duyệt dữ liệu tần số, chuyển đổi sang dữ liệu thời gian, bỏ qua phần tử đầu và cuối vì đã được gán.
for (i = 1; i < magnitude_length - 1; ++i) {
time_data_ptr[0] = real[i];
time_data_ptr[1] = imag[i];
time_data_ptr += 2; // Di chuyển đến vị trí dữ liệu thời gian tiếp theo.
}
// Gọi hàm WebRtc_rdft để thực hiện biến đổi Fourier rời rạc ngược.
WebRtc_rdft(time_data_length, -1, time_data, self->ip, self->wfft);
// Hệ số chuẩn hóa cho phép FFT.
float norm = 2.f / time_data_length;
// Chuẩn hóa dữ liệu thời gian.
for (i = 0; i < time_data_length; ++i) {
time_data[i] *= norm; // Scale FFT.
}
}
Hàm tĩnh tính năng lượng của tín hiệu sau khi áp dụng cửa sổ
Hàm này áp dụng một hàm cửa sổ (ví dụ: cửa sổ Hanning) vào dữ liệu âm thanh và tính toán năng lượng của kết quả.
// Tham số đầu vào:
// * |window| là hàm cửa sổ.
// * |data| là dữ liệu gốc.
// * |length| là độ dài dữ liệu.
// * |data_windowed| là dữ liệu sau khi áp dụng hàm cửa sổ.
// Giá trị trả về:
// * Năng lượng của dữ liệu sau khi áp dụng cửa sổ.
static float WindowingEnergy(const float *window,
const float *data,
size_t length,
float *data_windowed) {
size_t i;
float energy = 0.f;
// Duyệt dữ liệu, áp dụng cửa sổ và tính năng lượng.
for (i = 0; i < length; ++i) {
data_windowed[i] = window[i] * data[i]; // Áp dụng cửa sổ.
energy += data_windowed[i] * data_windowed[i]; // Cộng dồn năng lượng.
}
return energy; // Trả về năng lượng đã tính.
}
Tính năng lượng của một bộ đệm
Hàm đơn giản này tính tổng bình phương của tất cả các mẫu trong bộ đệm âm thanh.
// Tham số đầu vào:
// * |buffer| là bộ đệm cần tính năng lượng.
// * |length| là độ dài của bộ đệm.
// Trả về năng lượng đã tính.
static float Energy(const float *buffer, size_t length) {
size_t i;
float energy = 0.f;
// Duyệt từng phần tử của bộ đệm, cộng dồn bình phương vào năng lượng.
for (i = 0; i < length; ++i) {
energy += buffer[i] * buffer[i];
}
return energy; // Trả về giá trị năng lượng đã cộng dồn.
}
Ước tính tỷ lệ tín hiệu trên nhiễu (SNR) trước đó dựa trên quyết định (Decision-Directed - DD) và tạo bộ lọc Wiener
Đây là lõi của thuật toán giảm nhiễu. Nó ước tính SNR hiện tại dựa trên ước tính trước đó và SNR hiện tại, sau đó tạo ra một bộ lọc Wiener để giảm nhiễu.
// Tham số đầu vào:
// * |magn| là ước tính phổ biên độ của tín hiệu.
// Tham số đầu ra:
// * |theFilter| là đáp ứng tần số của bộ lọc Wiener đã được tính.
static void ComputeDdBasedWienerFilter(const NoiseSuppressionC *self,
const float *magn,
float *theFilter) {
size_t i;
float snrPrior, previousEstimateStsa, currentEstimateStsa;
// Duyệt tất cả các điểm tần số.
for (i = 0; i < self->magnLen; i++) {
// Ước tính của khung trước: dựa trên khung trước và bộ lọc khuếch đại.
// Sử dụng tỷ lệ tín hiệu/nhiễu đã được làm mịn, epsilon để tránh chia cho 0.
previousEstimateStsa = self->magnPrevProcess[i] * self->smooth[i] / (self->noisePrev[i] + epsilon);
// SNR hậu nghiệm và tiên nghiệm.
currentEstimateStsa = 0.f;
// Nếu biên độ hiện tại lớn hơn mức nhiễu, tính ước tính STSA hiện tại (tỷ lệ biên độ phổ ngắn hạn).
if (magn[i] > self->noise[i]) {
currentEstimateStsa = (magn[i] - self->noise[i]) / (self->noise[i] + epsilon);
}
// Ước tính DD là tổng của hai số hạng: ước tính hiện tại và ước tính trước đó.
// Cập nhật SNR tiên nghiệm theo hướng quyết định.
snrPrior = DD_PR_SNR * previousEstimateStsa +
(1.f - DD_PR_SNR) * currentEstimateStsa;
// Tính bộ lọc khuếch đại (gain filter), đây là bộ lọc Wiener dựa trên SNR đã ước tính.
theFilter[i] = snrPrior / (self->overdrive + snrPrior);
} // Kết thúc vòng lặp tần số.
}
Thay đổi mức độ quyết liệt của phương pháp giảm nhiễu
Hàm này cho phép người dùng điều chỉnh cường độ giảm nhiễu, từ nhẹ đến rất mạnh, bằng cách thay đổi các tham số nội bộ.
// |mode| = 0 là nhẹ (6dB), |mode| = 1 là trung bình (10dB), |mode| = 2 là
// mạnh (15dB), |mode| = 3 là rất mạnh.
// Trả về 0 nếu thành công, -1 nếu thất bại.
int WebRtcNs_set_policy_core(NoiseSuppressionC *self, int mode) {
// Các chế độ cho phép: 0, 1, 2, 3.
if (mode == 0) {
self->overdrive = 1.f; // Hệ số overdrive
self->denoiseBound = 0.5f; // Giới hạn giảm nhiễu
self->gainmap = 0; // Ánh xạ khuếch đại
}
else if (mode == 1) {
// self->overdrive = 1.25f; // Giá trị overdrive cũ
self->overdrive = 1.f; // Giá trị overdrive đã sửa
self->denoiseBound = 0.25f; // Giới hạn giảm nhiễu
self->gainmap = 1; // Ánh xạ khuếch đại
}
else if (mode == 2) {
// self->overdrive = 1.25f; // Giá trị overdrive cũ
self->overdrive = 1.1f; // Giá trị overdrive đã sửa
self->denoiseBound = 0.125f; // Giới hạn giảm nhiễu
self->gainmap = 1; // Ánh xạ khuếch đại
}
else if (mode == 3) {
// self->overdrive = 1.3f; // Giá trị overdrive cũ
self->overdrive = 1.25f; // Giá trị overdrive đã sửa
self->denoiseBound = 0.09f; // Giới hạn giảm nhiễu
self->gainmap = 1; // Ánh xạ khuếch đại
}
else {
return -1; // Nếu mode không phải 0, 1, 2, hoặc 3, trả về -1
}
self->aggrMode = mode; // Thiết lập chế độ quyết liệt
return 0; // Trả về 0 thành công
}