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.