feed method

VadStep feed(
  1. Uint8List chunk, {
  2. bool speaking = false,
})

Dá um pedaço de som. Devolve o que fazer a seguir.

Implementation

VadStep feed(Uint8List chunk, {bool speaking = false}) {
  final n = chunk.lengthInBytes ~/ 2;
  if (n == 0) return const VadStep.idle();
  final rms = rmsOf(chunk);
  final ms = (n * 1000) ~/ sampleRate;
  frames++;

  // Primeiros pedaços: aprender o silêncio deste sítio, sem nunca passar de
  // [maxBase], e sem deitar fora o som (se já estava a falar, conta).
  if (frames <= 3 && !speaking) {
    base = base == 0 ? math.min(rms, maxBase) : math.min(base, rms);
  }
  // ⚠️ AS DUAS FASQUIAS, e são diferentes de propósito.
  //
  // A CORTAR (ela a falar): manda o chão medido, não o ruído da sala. Estava
  // a multiplicar o ruído por 8, e numa sala com fundo 0,036 a fasquia ia a
  // 0,29: nem uma voz a 0,16 a calava, apesar de o chão dizer 0,09. Duas
  // vezes o ruído chega para acompanhar uma sala barulhenta.
  //
  // A OUVIR (ela calada): acompanha o ruído, mas COM TETO. Sem teto, num
  // café a fasquia chegava a 0,38 e o dono tinha de gritar. A voz de quem
  // fala ao pé do telemóvel dá 0,12 a 0,25; o teto fica em 0,10.
  final speakThr = speaking
      ? math.max(base * 2.0, bargeFloor)
      : math.min(math.max(base * 3.2, 0.014), tetoOuvir);
  final quietThr = math.max(base * 1.8, 0.008);

  // O ruído da sala acompanha-se: desce depressa (ficou tudo calado) e sobe
  // devagar (ligaram o ar condicionado, entraram no carro). Nunca aprende
  // com som alto o bastante para ser voz, senão passava a ignorar-te.
  if (!capturing && !speaking) {
    if (rms < base) {
      base = base * 0.8 + rms * 0.2;
    } else if (rms < speakThr) {
      base = base * 0.98 + rms * 0.02;
    }
  }

  if (speaking) {
    final thr = speakThr;
    if (perfil.length < 40) perfil.add(rms);
    // Só conta como interrupção o que for VOZ. Um estalo de tecla é alto,
    // mas não tem tom nenhum.
    final alto = rms > thr;
    if (rms > maxEnquantoFala) maxEnquantoFala = rms;
    final tom = alto ? periodicidade(chunk) : 0.0;
    if (tom > tomDaInterrupcao) tomDaInterrupcao = tom;
    if (tom >= bargeVozMin) {
      _vozHaMs = 0;
    } else {
      _vozHaMs += ms;
    }
    final ehVoz = alto && _vozHaMs <= vozValeMs;
    if (ehVoz) {
      speechMs += ms;
      quietMs = 0;
      _corrida += ms;
      if (_corrida > _maiorCorrida) _maiorCorrida = _corrida;
      // Tempo que chegue E um bocado seguido que só a voz consegue fazer.
      if (speechMs >= bargeMs && _maiorCorrida >= bargeRunMs) return const VadStep.bargeIn();
    } else {
      _corrida = 0;
      // Uma pausa curta não apaga a conta: entre sílabas há silêncio. Só uma
      // pausa a sério é que diz que aquilo foi um barulho e não alguém a
      // falar connosco.
      quietMs += ms;
      if (quietMs > bargeGapMs) {
        speechMs = 0;
        quietMs = 0;
        _maiorCorrida = 0;
      }
    }
    return const VadStep.idle();
  }

  if (rms > speakThr) {
    capturing = true;
    speechMs += ms;
    quietMs = 0;
    if (rms > picoDaFrase) picoDaFrase = rms;
    final tf = periodicidade(chunk);
    if (tf > tomDaFrase) tomDaFrase = tf;
  } else if (capturing) {
    if (rms < quietThr) quietMs += ms;
  }
  if (capturing) {
    _sum += rms;
    if (rms < _min) _min = rms;
    if (rms > _max) _max = rms;
    _n++;
  }

  if (!capturing) return const VadStep.idle();

  // Barulho de fundo apanhado cedo: alguns segundos sem uma única pausa E
  // sempre ao mesmo nível. Ninguém fala assim; uma ventoinha faz exatamente
  // isto. Não se espera pelos 15 segundos para perceber.
  final plano = _min > 0 && _max < _min * flatRatio;
  if (speechMs > noiseAfterMs && quietMs == 0 && plano) return _noise();

  if (quietMs <= quietToCloseMs && speechMs <= maxSpeechMs) return const VadStep.capturing();

  final spoke = speechMs;
  final nivel = _n > 0 ? _sum / _n : 0.0;

  // Quinze segundos sem UMA pausa não é ninguém a falar: é ruído constante
  // (uma ventoinha, o motor do carro, uma sala cheia). Ninguém fala assim.
  // Sobe-se o silêncio de referência para esse nível e deixa de se confundir.
  if (spoke > maxSpeechMs && quietMs < 200) return _noise();

  // Fim de frase. Só vale a pena enviar se houver mesmo fala.
  return VadStep.done(spokeMs: spoke, enough: spoke >= minSpeechMs, level: nivel);
}