Source browser

examples/shaders/signal_frequency_mask.cpp

Read the complete file below or download its original bytes. Line numbers provide stable links to a declaration or processing step.

File fingerprint
1// Apply a symmetric low-pass, high-pass, band-pass, or band-stop FFT response.
2#ifdef __cplusplus // Include the runtime only in the generated C++ build.
3#include "shdr.h" // Use the common vec2 buffer representation.
4#endif // Finish header inclusion for the generated C++ build.
5layout(local_size_x = 64) in; // Each invocation scales one spectrum bin.
6layout(std430, binding = 0) readonly buffer Input { // Read an unshifted FFT spectrum.
7    vec2 input_values[]; // Read an unshifted FFT spectrum.
8}; // Read an unshifted FFT spectrum.
9layout(std430, binding = 1) writeonly buffer Output { // Preserve the input for other consumers.
10    vec2 output_values[]; // Preserve the input for other consumers.
11}; // Preserve the input for other consumers.
12uniform uint block_length; // Number of bins per channel.
13uniform uint channels; // Number of independent channel spectra.
14uniform uint mode; // Select low-pass 0, high-pass 1, band-pass 2, or band-stop 3.
15uniform float sample_rate; // Convert bin indices into hertz.
16uniform float lower_hz; // Set the single cutoff or the lower edge of a band.
17uniform float upper_hz; // Set the upper edge for a band filter.
18uniform float transition_hz; // Use zero for hard edges or a positive cosine transition.
19float low_response(float frequency, float cutoff) { // Describe the gain below one cutoff.
20    if (transition_hz == 0.0) // Include the cutoff bin in a hard low-pass response.
21        return frequency <= cutoff ? 1.0 // Include the cutoff bin in a hard low-pass response.
22                                   : 0.0; // Include the cutoff bin in a hard low-pass response.
23    float begin = cutoff - transition_hz * 0.5; // Place half the transition below the cutoff.
24    float end = cutoff + transition_hz * 0.5; // Place the other half above the cutoff.
25    if (frequency <= begin) // Keep bins inside the pass band.
26        return 1.0; // Keep bins inside the pass band.
27    if (frequency >= end) // Remove bins inside the stop band.
28        return 0.0; // Remove bins inside the stop band.
29    float phase = // Map the transition to one half cosine period.
30        (frequency - begin) / transition_hz; // Map the transition to one half cosine period.
31    return 0.5 * // Join both plateaus with a smooth gain.
32           (1.0 + cos(3.14159265358979323846 * phase)); // Join both plateaus with a smooth gain.
33} // The same response is used for positive and negative frequencies.
34void main() { // Filtering stays on the selected compute backend.
35    uint index = gl_GlobalInvocationID.x; // Select one channel and bin.
36    if (index >= block_length * channels) // Ignore dispatch padding.
37        return; // Ignore dispatch padding.
38    uint bin = index % block_length; // Find this bin within its channel.
39    float frequency = float(min(bin, block_length - bin)) * sample_rate / // Fold negative frequencies onto their magnitude.
40                      float(block_length); // Fold negative frequencies onto their magnitude.
41    float lower = low_response(frequency, lower_hz); // Evaluate the first cutoff.
42    float gain = lower; // Start with a low-pass response.
43    if (mode == 1u) // Spectral inversion produces a high-pass response.
44        gain = 1.0 - lower; // Spectral inversion produces a high-pass response.
45    if (mode >= 2u) { // A band filter needs two cutoff responses.
46        float band = (1.0 - lower) * // Keep the frequencies between both edges.
47                     low_response(frequency, upper_hz); // Keep the frequencies between both edges.
48        gain = mode == 2u ? band : 1.0 - band; // Invert the band to create a band-stop response.
49    } // The gain remains real so real-input conjugate symmetry is preserved.
50    output_values[index] = input_values[index] * gain; // Keep phase and scale only the amplitude.
51} // The inverse FFT converts this spectrum back into a signal block.