A Java demonstration project showcasing frequency domain convolution using the overlap-save method with sample-accurate kernel switching.
This project implements a convolution system that can dynamically switch between different filter kernels during signal processing. This capability is essential for adaptive audio systems like noise-cancelling headphones, where filter characteristics must change in real-time as the acoustic environment evolves.
Traditional convolution libraries process signals with a fixed kernel throughout the entire operation. When you need to change filter characteristics mid-stream, you typically must:
- Stop processing
- Restart with the new kernel
- Lose any state or introduce discontinuities
This approach doesn't work for real-time adaptive systems that need smooth, sample-accurate transitions between filter responses.
This implementation extends the overlap-save method to support:
- Sample-accurate kernel switching at user-defined intervals
- Mathematically correct convolution even across kernel boundaries
- Efficient frequency-domain processing for larger kernels
- Cyclic kernel patterns for repeating filter sequences
- Implements both single-kernel and multi-kernel convolution
- Handles kernels of varying lengths (automatically padded to match)
- Preserves mathematical correctness at kernel transition boundaries
- Optimized FFT-based processing using Apache Commons Math
- Comprehensive test suite demonstrating various scenarios
- Audio file I/O support for testing with real audio signals
- Java 24
- Maven 3.6+
# Clone the repository
git clone https://github.com/LiveNathan/convolution-kernel-switching-demo.git
cd convolution-kernel-switching-demo
# Run tests to see the functionality in action
mvn test
# Run the Spring Boot application
mvn spring-boot:run
// Single kernel convolution
Convolution convolution = new OverlapSaveAdapter();
double[] signal = {1.0, 2.0, 3.0, 4.0};
double[] kernel = {0.5, 0.25};
double[] result = convolution.with(signal, kernel);
// Multi-kernel convolution with switching
double[] lowpass = {0.25, 0.5, 0.25};
double[] highpass = {-0.25, 0.5, -0.25};
List<double[]> kernels = List.of(lowpass, highpass);
// Switch kernels every 1000 samples
int periodSamples = 1000;
double[] switchingResult = convolution.with(signal, kernels, periodSamples);
The project follows clean architecture principles with clear separation of concerns:
Convolution
- Main convolution interface supporting both single and multi-kernel operations
OverlapSaveAdapter
- Implements overlap-save algorithm with kernel switching logicSignalTransformer
- Handles FFT operations and signal processing utilitiesWavFileReader
/WavFileWriter
- Audio file I/O for testing with real signals
Block-based Processing: Kernel switches occur at block boundaries, preserving convolution mathematics while enabling sample-accurate timing.
FFT Optimization: Uses frequency-domain processing for efficiency, with automatic FFT size optimization based on signal and kernel characteristics.
Padding Strategy: Automatically handles kernels of different lengths by padding to a common size.
The project includes comprehensive tests covering:
- Basic convolution properties (commutativity, identity)
- Kernel switching at various boundaries
- Edge cases (empty kernels, mismatched lengths)
- Real audio file processing scenarios
Run the audio tests to hear kernel switching in action:
mvn test -Dtest=OverlapSaveAdapterTest#testConvolutionWithAudioFiles
This generates audio files in target/test-outputs/
demonstrating:
- Single kernel convolution
- Multi-kernel convolution with switching every 2 seconds
The overlap-save method processes signals in blocks:
- Pad signal blocks to prevent circular convolution artifacts
- Transform signal and kernel to frequency domain (FFT)
- Multiply frequency responses
- Transform back to time domain (IFFT)
- Extract valid samples, discarding overlap regions
For multi-kernel convolution:
- Divide signal into periods based on
periodSamples
- Select appropriate kernel for each period using modular arithmetic
- Process each block with its corresponding kernel transform
- Combine results while maintaining proper sample alignment
- Pre-computes FFTs for all kernels to avoid redundant calculations
- Uses power-of-2 FFT sizes for optimal performance
- Automatically selects FFT block sizes based on signal/kernel characteristics
- Reuses FFT instances via ThreadLocal caching
Read the detailed blog post about the implementation: Sample-Accurate Kernel Switching with Overlap-Save Convolution
- Spring Boot 3.5.3 - Application framework
- Apache Commons Math 4.0-beta1 - FFT operations
- Apache Arrow 18.3.0 - Utility functions
- JSpecify 1.0.0 - Null safety annotations
This project is available under the MIT License.
Contributions are welcome! Please ensure all tests pass and follow the existing code style:
mvn test
The project uses ErrorProne and NullAway for static analysis to maintain code quality.