This is an old revision of the document!
Table of Contents
Neural-inspired Tinnitus Synthesizer
Objective
This project implements a neural-inspired tinnitus synthesizer in Python. The synthesizer generates audio based on current neurological models of musical tinnitus, focusing on the relationship between brainstem-generated noise and cortical filtering. The project aims to replicate the complex harmonic structures reported in musical tinnitus cases (myself, actually) while providing insights into the underlying neural mechanisms.
Theoretical Background
Neural Basis
Musical tinnitus is believed (I believe, this is 100% personal experience) to originate from two primary mechanisms:
- Noise Generation: The brainstem or acoustic nerve nuclei (8th cranial nerve) generate white noise, which serves as the raw material for tinnitus perception
- Cortical Filtering: The brain applies complex filtering mechanisms to this noise, creating harmonic-rich, musical percepts
- Pattern Recognition: The filtered signals often match familiar musical patterns stored in auditory memory
Signal Processing Model
The synthesis process follows these key stages:
- Source Signal: White noise generation, mimicking neural noise
- Frequency Shaping: Multiple resonant bandpass filters create harmonic structures
- Amplitude Modulation: 8 Hz tremolo, matching common tinnitus characteristics
- Harmonic Processing: Material-specific scaling of harmonics to create different timbres
Implementation Details
Core Components
- Filename: `tinnitus_synthesizer.py`
- Dependencies: `numpy`, `scipy`, `sounddevice`
- Features:
- Neural Noise Generation: Simulates brainstem-generated noise
- Dynamic Filtering: Implements cortical filtering mechanisms
- Frequency Interpolation: Smooth transitions between musical notes
- Material Simulation: Different harmonic profiles for various timbres
Signal Processing Architecture
- White Noise Generation: Base signal representing neural noise
- Bandpass Filtering: Multiple parallel filters creating resonant peaks
- Harmonic Scaling: Material-specific attenuation of odd/even harmonics
- Amplitude Modulation: Tremolo effect at physiologically relevant frequencies
Synthesis Parameters
- Carrier Frequencies: Musical notes (e.g., D4, F4, E4)
- Harmonic Structure: Up to 32 harmonics with material-specific scaling
- Modulation: 8 Hz tremolo with adjustable depth
- Filter Characteristics: Variable bandwidth and Q-factor
Implementation
- tinnitus_synthesizer.py
import numpy as np from scipy import signal import sounddevice as sd def smooth_interpolate(freqs, t, duration, transition_time=0.33): """ Create smooth frequency transitions using exponential interpolation """ samples = len(t) freq_signal = np.zeros(samples) segments = len(freqs) segment_duration = duration / segments for i in range(segments): start_idx = int(i * samples / segments) end_idx = int((i + 1) * samples / segments) transition_samples = int(transition_time * samples / duration) # Get current and next frequency curr_freq = freqs[i] next_freq = freqs[(i + 1) % segments] # Create segment time array segment_t = np.linspace(0, 1, end_idx - start_idx) # Create exponential transition tau = 0.1 # Time constant for exponential transition = curr_freq + (next_freq - curr_freq) * (1 - np.exp(-segment_t / tau)) freq_signal[start_idx:end_idx] = transition return freq_signal def get_harmonic_scaling(harmonic_number, style='glass'): """ Calculate harmonic scaling based on different organ pipe materials/styles """ is_even = harmonic_number % 2 == 0 if style == 'glass': # Glass pipes: strong even harmonics, quick decay of odds if is_even: return 1.0 / (harmonic_number ** 0.3) # Slower decay for even harmonics else: return 1.0 / (harmonic_number ** 1.5) # Quick decay for odd harmonics elif style == 'metal': # Metal pipes: strong upper harmonics, slight odd/even difference if is_even: return 1.0 / (harmonic_number ** 0.4) else: return 1.0 / (harmonic_number ** 0.6) elif style == 'crystal': # Crystal-like: very strong even harmonics, almost no odds if is_even: return 1.0 / (harmonic_number ** 0.25) # Very strong even harmonics else: return 1.0 / (harmonic_number ** 2.0) # Very weak odd harmonics return 1.0 / harmonic_number # Default scaling def generate_filtered_tinnitus(duration=20, sample_rate=44100, frequencies=None, style='glass'): if frequencies is None: frequencies = [294, 349, 330, 165] t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False) white_noise = np.random.normal(0, 2, len(t)) def create_resonant_filter(center_freq, bandwidth=30): low_freq = center_freq - bandwidth/2 high_freq = center_freq + bandwidth/2 low_freq = max(1, low_freq) high_freq = min(sample_rate/2 - 1, high_freq) b, a = signal.butter(2, [low_freq, high_freq], btype='bandpass', fs=sample_rate) return b, a current_freq = smooth_interpolate(frequencies, t, duration) n_harmonics = 32 output_signal = np.zeros_like(white_noise) chunk_size = sample_rate n_chunks = len(t) // chunk_size + 1 for chunk in range(n_chunks): start_idx = chunk * chunk_size end_idx = min((chunk + 1) * chunk_size, len(t)) if start_idx >= len(t): break chunk_output = np.zeros(end_idx - start_idx) chunk_noise = white_noise[start_idx:end_idx] chunk_freq = current_freq[start_idx:end_idx] for harmonic in range(1, n_harmonics + 1): harmonic_freq = chunk_freq * harmonic if np.max(harmonic_freq) < sample_rate / 2: avg_freq = np.mean(harmonic_freq) # Tighter bandwidth for even harmonics bandwidth = 40 if harmonic % 2 == 0 else 60 b, a = create_resonant_filter(avg_freq, bandwidth) # Apply slight random detuning detuning_factor = 1 + 0.005 * (np.random.random() - 0.5) harmonic_signal = signal.lfilter(b, a, chunk_noise) * detuning_factor # Apply material-specific harmonic scaling scaling = get_harmonic_scaling(harmonic, style) * 2.0 # Amplified for more presence chunk_output += harmonic_signal * scaling output_signal[start_idx:end_idx] = chunk_output # Subtle tremolo mod_freq = 8 mod_depth = 0.25 # Reduced tremolo for more stable tone tremolo = 1 + mod_depth * np.sin(2 * np.pi * mod_freq * t) modulated_signal = output_signal * tremolo # Less noise in the final mix final_signal = 0.99 * modulated_signal + 0.01 * white_noise # Normalize and apply gain final_signal = final_signal / np.max(np.abs(final_signal)) final_signal *= 0.8 return final_signal, sample_rate # Generate and play with different styles frequencies = [588, 698, 660, 588, 698, 660, 330] # D5, F5, E5, D5, F5, E5, E4 # Try different styles: 'glass', 'metal', or 'crystal' tinnitus_sound, sample_rate = generate_filtered_tinnitus( duration=20, frequencies=frequencies, style='glass' # Try changing this to 'metal' or 'crystal' ) sd.play(tinnitus_sound, samplerate=sample_rate) sd.wait()
Limitations
1. Computational Efficiency:
- High computational load due to multiple parallel filters
- Memory intensive for long durations
- Chunk-based processing required for longer sequences
2. Physiological Accuracy:
- Simplified model of actual neural processes
- Does not account for individual variations in tinnitus perception
- Limited to musical tinnitus simulation only
3. Sound Design:
- Fixed tremolo frequency
- Limited material presets
- No real-time parameter modification
Future Improvements
1. Enhanced Physiological Modeling:
- Implementation of more complex neural filtering mechanisms
- Addition of frequency-dependent phase relationships
- Integration of adaptation and fatigue effects
2. Performance Optimization:
- GPU acceleration for filter processing
- More efficient filter implementation
- Real-time parameter control
3. Extended Features:
- Additional material presets
- Interactive parameter adjustment
- Visual representation of frequency content
References
- Research papers on musical tinnitus
- DSP techniques for audio synthesis
- Neurological models of tinnitus
Final Note
This neural-inspired synthesizer provides a platform for experimenting with tinnitus-like sound generation based on current neurological models. Its modular design allows for easy extension and modification of various parameters, making it useful for both research and educational purposes.
