from abc import ABC, abstractmethod
import numpy as np

class BaseProcessorModule(ABC):
    """Abstract base class for all processing modules."""
    
    def __init__(self, sample_rate: int):
        self.sample_rate = sample_rate

    @abstractmethod
    def process(self, audio: np.ndarray, **kwargs) -> np.ndarray:
        """Process the audio array."""
        pass
    
    def _calculate_rms_envelope(self, audio: np.ndarray, window_size: int) -> np.ndarray:
        """Helper: Calculate RMS envelope."""
        padded = np.pad(audio ** 2, (window_size // 2, window_size // 2), mode='edge')
        window = np.ones(window_size) / window_size
        envelope = np.sqrt(np.convolve(padded, window, mode='same'))
        return envelope[window_size // 2: -window_size // 2 or None][:len(audio)]

    def _apply_attack_release(self, envelope: np.ndarray, attack_samples: int, release_samples: int) -> np.ndarray:
        """Helper: Apply attack and release smoothing."""
        from scipy.signal import lfilter
        # Simple 1-pole filter approximation for smoothing
        # Note: A true compressor envelope is more complex, but this suffices for sidechaining
        alpha_att = np.exp(-1.0 / (attack_samples + 1e-9))
        alpha_rel = np.exp(-1.0 / (release_samples + 1e-9))
        
        # This is a simplification; for production, we often use a custom C/Cython loop or Numba
        # Here we'll use a simple IIR filter which approximates the smoothing
        # Using the average coefficient as a compromise for a single-pass filter
        alpha = (alpha_att + alpha_rel) / 2
        return lfilter([1.0 - alpha], [1.0, -alpha], envelope)
