¿Cómo hago que una máquina se quede en "pantalla en blanco" durante un período de tiempo (como penalización) si se alcanzan ciertos niveles de ruido?

1549

Mis hijos (4 y 5) gritan mucho cuando juegan en la computadora. Encontré una cura efectiva para esto. Cuando escucho ruidos fuertes, me meto en la computadora del juego y hago:

chvt 3;  sleep 15;  chvt 7 

Esto apagará la pantalla durante 15 segundos en Linux. Les dije que a la computadora no le gustan los ruidos fuertes. Creen totalmente en esto y le piden perdón a la computadora. Se volvieron mucho más silenciosos, pero no al nivel en que sería feliz, por lo que necesito continuar este proceso educativo. Sin embargo, no siempre estoy cerca para hacer esto manualmente.

¿Es posible automatizar esto? Se adjunta un micrófono a la caja. Si el nivel de volumen pasa algún umbral, entonces quiero ejecutar un comando.

Leonid Volnitsky
fuente
3
Hasta que aprendan a presionar CTRL + ALT + F7
Suici Doga
1
@SuiciDoga Hola; ¡No saben lo que está pasando!
wizzwizz4
Felicidades por una solución técnica. Pero creo que es importante decir siempre la verdad a los niños.
Peter

Respuestas:

645

Use soxdesde SoX para analizar una muestra de audio corta:

sox -t .wav "|arecord -d 2" -n stat

Con -t .wavespecificamos que procesamos el tipo de wav, "|arecord -d 2"ejecuta el arecord programa durante dos segundos, da -nsalida al archivo nulo y con statque especificamos queremos estadísticas.

El resultado de este comando, en mi sistema con algunas palabras de fondo, es:

Recording WAVE 'stdin' : Unsigned 8 bit, Rate 8000 Hz, Mono
Samples read:             16000
Length (seconds):      2.000000
Scaled by:         2147483647.0
Maximum amplitude:     0.312500
Minimum amplitude:    -0.421875
Midline amplitude:    -0.054688
Mean    norm:          0.046831
Mean    amplitude:    -0.000044
RMS     amplitude:     0.068383
Maximum delta:         0.414063
Minimum delta:         0.000000
Mean    delta:         0.021912
RMS     delta:         0.036752
Rough   frequency:          684
Volume adjustment:        2.370

La amplitud máxima se puede extraer a través de:

grep -e "RMS.*amplitude" | tr -d ' ' | cut -d ':' -f 2

Nosotros greppara la línea que queremos, usamos trpara recortar los caracteres de espacio y luego cutpor el :carácter y tomar la segunda parte que nos da 0.068383en este ejemplo. Como sugieren los comentarios, RMS es una mejor medida de energía que la amplitud máxima.

Finalmente puede usar bcel resultado para comparar valores de punto flotante desde la línea de comandos:

if (( $(echo "$value > $threshold" | bc -l) )) ; # ... 

Si crea un bucle (vea los ejemplos de Bash ) que llama a dormir durante 1 minuto, prueba el volumen y luego se repite, puede dejarlo ejecutándose en segundo plano. El último paso es agregarlo a los scripts de inicio o archivos de servicio (dependiendo de su sistema operativo / distribución), de modo que ni siquiera tenga que iniciarlo manualmente.

tucuxi
fuente
282
No recomendaría tomar la máxima amplitud. No es bueno para los niños cuando su pantalla se pone en blanco solo porque alguien aplaudió o algo similar. El promedio parece más apropiado.
orlp 01 de
34
Solo una aclaración, por "promedio" te refieres a la amplitud RMS ¿verdad? La amplitud media estará cerca de 0 si el ruido es de un volumen constante durante los 2 segundos (las mitades positiva y negativa se cancelarán entre sí).
Lucas
66
Un simple detector de "energía" para una serie de muestras es simplemente agregar el valor de todos los picos juntos. Ni siquiera tendría que promediarlo si no quisiera. Un pico es cualquier punto en el sample[n]>sample[n-1]&&sample[n]>sample[n+1]que he usado esto como un mecanismo rudimentario para medir la energía de una canción y funciona bastante bien. Simplemente busque un número mágico en el que esté satisfecho con el nivel de volumen.
Kaslai
3
Me gustaría ver una salida de muestra de su primer comando cuando realmente se trata de un niño gritando, como referencia.
Alvin Wong
3
Para el uso descrito (iniciar automáticamente + ejecutar cada pocos minutos) un trabajo cron es la herramienta adecuada para usar. Mucho más simple de configurar y más robusto que usar init script + bash loop + sleep.
m000
131

Así es como se puede hacer con Pure Data :

Prevención de gritos de niños usando Pure Data

Metro es un metrónomo, y "metro 100" sigue golpeando cada 100 ms.

El audio proviene de adc ~, el volumen se calcula por env ~. "pd dsp 0" apaga el DSP cuando se golpea, "pd dsp 1" lo enciende. "shell" ejecuta el comando pasado en un shell, uso la API xrandr de Linux para establecer el brillo en X, necesita adaptar esto para Wayland.

Como puede ver, el período de gracia y el bloqueo ocupan mucho más espacio que el código de audio.

Hacer una solución con amortiguadores de anillo y / o promedios móviles debería ser mucho más fácil que hacerlo sox. Así que no creo que sea una mala idea usar Pure Data para esto. Pero la pantalla se queda en blanco y el bloqueo no se ajusta al paradigma del flujo de datos.

El archivo PD está en gist.github.com: ysangkok - kidsyell.pd .

Janus Troelsen
fuente
11
¡muy agradable! Puede hacer que esto sea bastante receptivo utilizando esta técnica: rastree el nivel de sonido promedio durante un minuto, luego úselo como línea de base, de modo que cuando los niños superen los 20 dB por encima de la línea de fondo, se dispare. Luego se ajustará automáticamente al nivel de sonido ambiental.
Hans-Christoph Steiner
1
Sí, eso tiene sentido @ Hans-ChristophSteiner. Pero, en cierto modo, ¿el nivel de ruido ambiental en realidad no requeriría que los niños gritaran más alto, ya que representarían una proporción menor del ruido general? Eso, por supuesto, solo se aplicaría si el ruido existente es blanco o rosa o si se ignora.
Janus Troelsen
44
si fuera más tranquilo de lo normal, como una mañana de fin de semana, entonces lo haría más sensible, ya que siempre estaría 20 dB por encima del nivel ambiental
Hans-Christoph Steiner
Esta es la PD extendida?
nullpotent
@iccthedral: usé pd-extended para hacerlo, pero no sé si usé alguna construcción específica de pd-extended.
Janus Troelsen
103

Consulte "Cómo detectar la presencia de sonido / audio" de Thomer M. Gil .

Básicamente, graba el sonido cada 5 segundos, luego comprueba la amplitud del sonido, usa soxy decide si dispara un guión o no. ¡Creo que puede adaptar fácilmente el rubyguión para sus hijos! O puede optar por hackear el script de Python (usando PyAudio) que también ha proporcionado.

Atropo
fuente
55
¿Qué pasa con esos arranques de menos de 5 segundos que evitan la detección?
RhysW
53

Puede obtener información del micrófono haciendo algo como:

arecord -d1 /dev/null -vvv

Puede que tenga que jugar un poco con la configuración, como:

arecord -d1 -Dhw:0 -c2 -fS16_LE /dev/null -vvv

De ahí en adelante, es una simple cuestión de analizar la salida.

cha0site
fuente
44

Esta es una de las preguntas más divertidas que he visto. Me gustaría agradecer a tucuxi por tan buena respuesta; que configuré como script bash

#!/bin/bash

threshold=0.001
# we should check that sox and arecord are installed
if [ $1 ]; then threshold=$1; fi
while [ 1 -gt 0 ]; do
 if(( $(echo "$(sox -t .wav '|arecord -d 2' -n stat 2>&1|grep -e 'RMS.*amplitude'|tr -d ' '|cut -d ':' -f 2 ) > $threshold"|bc -l) ))
 then
  chvt 3; sleep 5; chvt 7;
 fi
done
Alexx Roche
fuente
77
Si comienza a correr agregando una línea a /etc/rc4.d/S99rc.local y luego cambia el micrófono de entrada de no amplificado al 100%, usted también puede terminar siendo arrojado a tty3 (puede saltar hacia atrás antes de que el sueño se apague) con Ctrl + Alt + F7), y si su teclado es demasiado fuerte para abrir una terminal, para ejecutar sudo killall too_loud luego Ctrl + Alt + F1 e inicie sesión allí.)
Alexx Roche
41

Mis 2 centavos para la solución C o C ++: tal vez no sea el enfoque más efectivo, pero en Linux, puede usar la API ALSA (biblioteca de manejo de audio incorporada de Linux) y usar alguna técnica numérica (por ejemplo, calcular el sonido promedio nivelar cada segundo) para obtener el nivel de ruido.

Luego puede verificarlo en un bucle infinito, y si es mayor que un umbral preestablecido, puede usar la biblioteca X11 para apagar la pantalla durante algunos segundos, o alternativamente (menos elegante, pero funciona) invocar el chvtcomando usando system("chvt 3; sleep 15; chvt 7 ");.

H2CO3
fuente
2
Si usa el comando, consideraría algo diferente entonces chvt. ArchWiki tiene buenos ejemplos.
AD