processChunk method

  1. @override
void processChunk(
  1. Int16List pcm16Data
)
override

Feed raw PCM16 audio data for processing. Data should be mono, signed 16-bit integers.

Implementation

@override
void processChunk(Int16List pcm16Data) {
  if (!_initialized || !_settings.isEnabled) return;

  final now = DateTime.now();

  // 1. Apply band-pass filter
  final filtered = _applyBandpass(pcm16Data);

  // 2. Calculate RMS amplitude (normalized 0-1)
  final rms = _calculateRms(filtered);
  _currentRms = rms;

  // During calibration, just collect RMS values
  if (_isCalibrating) {
    _calibrationRmsValues.add(rms);
    return;
  }

  // 3. Convert RMS to dB for gate comparison
  final rmsDb = rms > 0 ? 20.0 * log(rms) / ln10 : -60.0;
  final isAboveGate = rmsDb > _settings.gateThresholdDb;

  // 4. Calculate peak amplitude
  double peak = 0.0;
  for (final sample in filtered) {
    final abs = sample.abs();
    if (abs > peak) peak = abs;
  }
  final normalizedPeak = peak / 32768.0;

  // 5. Envelope detection: track attack time and sustain
  double attackMs = 0.0;
  double sustainMs = 0.0;

  if (isAboveGate && !_gateWasOpen) {
    // Gate just opened — sound onset
    _soundOnsetTime = now;
    _gateOpenTime = now;
    _currentPeakInWindow = normalizedPeak;
  } else if (isAboveGate && _gateWasOpen) {
    // Gate still open — track peak and sustain
    if (normalizedPeak > _currentPeakInWindow) {
      _currentPeakInWindow = normalizedPeak;
      // Attack time = time from onset to current peak
      if (_soundOnsetTime != null) {
        attackMs = now.difference(_soundOnsetTime!).inMicroseconds / 1000.0;
      }
    }
    if (_gateOpenTime != null) {
      sustainMs = now.difference(_gateOpenTime!).inMicroseconds / 1000.0;
    }
  } else if (!isAboveGate && _gateWasOpen) {
    // Gate just closed — finalize envelope
    if (_soundOnsetTime != null) {
      attackMs = _currentAttackMs; // Keep last measured attack
    }
    if (_gateOpenTime != null) {
      sustainMs = now.difference(_gateOpenTime!).inMicroseconds / 1000.0;
    }
    _soundOnsetTime = null;
    _gateOpenTime = null;
    _currentPeakInWindow = 0.0;
  }

  _gateWasOpen = isAboveGate;
  if (attackMs > 0) _currentAttackMs = attackMs;

  // 6. Emit frame
  final frame = AudioDspFrame(
    rmsAmplitude: rms,
    peakAmplitude: normalizedPeak,
    envelopeAttackMs: _currentAttackMs,
    envelopeSustainMs: sustainMs,
    isAboveGate: isAboveGate,
    timestamp: now,
  );

  _frameController.add(frame);
}