processChunk method
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);
}