| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566 |
- """
- Tape Processor — Simulates tape saturation and applies safety filters.
- """
- import numpy as np
- from .eq_engine import EQBand, EQEngine
- from .calibrator import TAPE_PROFILES
- class TapeProcessor:
- def __init__(self, sample_rate: int = 44100):
- self.sample_rate = sample_rate
-
- self.tape_type = "Type I (Ferric)"
- self.enable_highpass = True
- self.enable_lowpass = True
- self.enable_saturation = True
- self.saturation_threshold = 0.95
-
- # Internal filter engine for safety filters
- self._filter_engine = EQEngine(sample_rate)
- self._update_filters()
-
- def _update_filters(self):
- profile = TAPE_PROFILES.get(self.tape_type, TAPE_PROFILES["Type I (Ferric)"])
-
- bands = []
- if self.enable_highpass:
- bands.append(EQBand(freq=profile["low_cut"], gain=0.0, q=0.707, filter_type='highpass'))
- if self.enable_lowpass:
- bands.append(EQBand(freq=profile["high_cut"], gain=0.0, q=0.707, filter_type='lowpass'))
-
- self._filter_engine.bands = bands
- self._filter_engine.update_filters()
- def set_tape_type(self, tape_type: str):
- if tape_type != self.tape_type:
- self.tape_type = tape_type
- self._update_filters()
-
- def set_sample_rate(self, sr: int):
- self.sample_rate = sr
- self._filter_engine.set_sample_rate(sr)
- def process_pre_eq(self, chunk: np.ndarray) -> np.ndarray:
- if chunk.size == 0:
- return chunk
-
- # 1. Apply safety HP/LP filters
- if self.enable_highpass or self.enable_lowpass:
- chunk = self._filter_engine.process(chunk)
-
- return chunk
-
- def process_post_eq(self, chunk: np.ndarray) -> np.ndarray:
- if chunk.size == 0:
- return chunk
-
- # 2. Soft Saturation
- if self.enable_saturation:
- # Scale so threshold maps to where tanh starts rounding off
- scale = 1.0 / max(0.1, self.saturation_threshold)
- chunk = np.tanh(chunk * scale) / scale
-
- return chunk
-
- def reset_state(self):
- self._filter_engine.reset_state()
|