diff --git a/docs/guides/medialink.md b/docs/guides/medialink.md index fdba3332..f3f6394d 100644 --- a/docs/guides/medialink.md +++ b/docs/guides/medialink.md @@ -150,7 +150,9 @@ intercom = medialink.Intercom(link, mic, speaker, voice=voice) `audiofeatures`, which ignores a fan or road noise that stays on. Elsewhere, CircuitPython among them, it uses a level gate: the room's quiet sets a floor, and sound 10 dB over it counts. `voice.detector` says which you got. -The gate can't tell a voice from a door slamming. +The gate can't tell a voice from a door slamming. Both learn the room's quiet from +the first sound they hear, and pass over digital silence (frames in which +every sample is the same), which a microphone can send while it starts. When both sides speak at once, one gets the floor and the other's words are lost until the turn ends, as with two buttons. While the peer talks, diff --git a/lib/medialink/voice.py b/lib/medialink/voice.py index 92b1e61f..bb630a99 100644 --- a/lib/medialink/voice.py +++ b/lib/medialink/voice.py @@ -15,6 +15,12 @@ Either way a frame counts for ``hold_ms`` after the last one that held speech, so the pauses between words don't end a turn. + +Both learn the room's quiet from the first frames they hear, so a frame in +which every sample is the same is passed over: that is a microphone not yet +running, not a room. An ES7210 on a P4 board sends 100 to 280 ms of it the +first time it opens after power-up, and a detector that learned the quiet +from that took the room's own hiss for 3.4 s of speech. """ import struct @@ -62,6 +68,10 @@ def speaking(self): def feed(self, pcm): """Take a frame of signed 16-bit PCM. True while it is worth sending.""" self.level = _level(pcm, self.channels) + step = 2 * self.channels + if bytes(pcm[step:]) == bytes(pcm[:-step]): + # Digital silence, from a microphone still starting: not the room. + return self.speaking if self._frontend is not None: voiced = self._voiced_frontend(pcm) else: diff --git a/tests/test_medialink_voice.py b/tests/test_medialink_voice.py index 6ae5cf14..fc4dae01 100644 --- a/tests/test_medialink_voice.py +++ b/tests/test_medialink_voice.py @@ -53,6 +53,23 @@ def test_a_steady_noise_becomes_the_floor(self): heard = [voice.feed(f) for f in frames(loud)] self.assertFalse(any(heard[-50:])) + def a_microphone_starting_up(self, detector): + """A microphone's first open after power-up, as the P4 board's ES7210 + gives it: 160 ms of one repeated sample, then 120 ms of zeros, then + the room's hiss.""" + voice = medialink.VoiceActivity(detector=detector, hold_ms=1000) + start = frames(struct.pack("