Como faço para a máquina "tela em branco" por um período de tempo (como penalidade) se determinados níveis de ruído são atingidos?

1549

Meus filhos (4 e 5) gritam muito quando jogam no computador. Eu encontrei uma cura eficaz para isso. Quando ouço barulhos altos, ssh no computador do jogo e faço:

chvt 3;  sleep 15;  chvt 7 

Isso desligará a tela por 15 segundos no Linux. Eu disse a eles que o computador não gosta de barulhos altos. Eles acreditam totalmente nisso e pedem perdão ao computador. Eles ficaram muito mais calmos, mas não no nível em que eu seria feliz e, portanto, preciso continuar esse processo educacional. No entanto, nem sempre estou por perto para fazer isso manualmente.

É possível automatizar isso? Um microfone está conectado à caixa. Se o nível de volume ultrapassar algum limite, desejo executar um comando.

Leonid Volnitsky
fonte
3
Até que aprender a pressionar CTRL + ALT + F7
Suici Doga
1
@SuiciDoga Hey; eles não sabem o que está acontecendo!
Wizzwizz4
Parabéns por uma solução técnica. Mas acho que é importante dizer sempre a verdade para as crianças.
peterh 30/01

Respostas:

645

Use soxdo SoX para analisar uma pequena amostra de áudio:

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

Com -t .wavespecificamos nós processamos o tipo wav, "|arecord -d 2"executa o arecord programa por dois segundos, -nsaídas para o arquivo nulo e com statnós especificamos que queremos estatísticas.

A saída deste comando, no meu sistema com algum discurso em segundo plano, é:

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

A amplitude máxima pode então ser extraída através de:

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

Nós greppara a linha que queremos, usar trpara aparar os caracteres de espaço e, em seguida, cutpelo :caráter e tomar a segunda parte, que nos dá 0.068383neste exemplo. Conforme sugerido pelos comentários, o RMS é uma melhor medida de energia que a amplitude máxima.

Você pode finalmente usar bcno resultado para comparar valores de ponto flutuante na linha de comando:

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

Se você criar um loop (consulte os exemplos do Bash ) que chama suspensão por 1 minuto, testa o volume e depois repete, você pode deixá-lo em execução em segundo plano. O último passo é adicioná-lo aos scripts init ou aos arquivos de serviço (dependendo do sistema operacional / distribuição), de forma que você nem precise iniciá-lo manualmente.

tucuxi
fonte
282
Eu não recomendaria tomar a amplitude máxima. Não é legal para as crianças quando a tela fica em branco só porque alguém bateu palmas ou algo parecido. Média parece mais apropriada.
orlp
34
Apenas um esclarecimento, por "média" você quer dizer RMS Amplitude, certo? A amplitude média será próxima de 0 se o ruído for de um volume consistente durante os 2 segundos (as metades positiva e negativa serão canceladas uma à outra).
Lucas
6
Um simples detector de "energia" para uma série de amostras é apenas adicionar o valor de todos os picos juntos. Você não precisaria calcular a média se não quisesse. Um pico é qualquer ponto em sample[n]>sample[n-1]&&sample[n]>sample[n+1]que eu usei isso como um mecanismo rudimentar para medir a energia de uma música e funciona muito bem. Basta procurar um número mágico no qual você esteja satisfeito com o nível de volume.
Kaslai
3
Gostaria de ver um exemplo de saída do seu primeiro comando quando se trata realmente de uma criança gritando, para referência.
Alvin Wong
3
Para o uso descrito (iniciar automaticamente + executar a cada poucos minutos), um trabalho cron é a ferramenta certa a ser usada. Muito mais simples de configurar e mais robusto do que usar script init + loop bash + suspensão.
m000 24/03
131

Veja como isso pode ser feito com o Pure Data :

Prevenção de gritos de crianças usando Pure Data

O metrô é um metrônomo e o "metrô 100" continua batendo a cada 100 ms.

O áudio é proveniente de adc ~, o volume é calculado por env ~. "pd dsp 0" desliga o DSP quando batido, "pd dsp 1" o liga. "shell" executa o comando passado em um shell, eu uso a API do Linux xrandr para definir o brilho como X, você precisa adaptar isso para Wayland.

Como você pode ver, o período de cortesia e o bloqueio ocupam muito mais espaço do que o código de áudio.

Fazer uma solução com buffers de anel e / ou médias móveis deve ser bem mais fácil do que fazê-lo sox. Portanto, não acho uma má idéia usar o Pure Data para isso. Mas a tela fica em branco e o bloqueio não se encaixa no paradigma do fluxo de dados.

O arquivo PD está em gist.github.com: ysangkok - kidsyell.pd .

Janus Troelsen
fonte
11
muito agradável! Você pode fazer com que isso seja bastante responsivo usando esta técnica: rastreie o nível médio de som em um minuto e use-o como linha de base, para que, quando as crianças ultrapassem 20 dB acima da linha de base, seja acionado. Em seguida, ele se ajustará automaticamente ao nível do som ambiente.
Hans-Christoph Steiner
1
Sim, isso faz sentido @ Hans-ChristophSteiner. Mas, de certa forma, o nível de ruído ambiente realmente não exigia que as crianças gritassem mais alto, uma vez que representariam uma proporção menor do ruído geral? É claro que isso só se aplicaria se o ruído existente for branco ou rosa ou de outra forma ignorado.
Janus Troelsen
4
se foi mais tranqüilo do que o habitual, como uma manhã de fim de semana, então seria torná-lo mais sensível, uma vez que sempre seria 20 dB acima do nível do ambiente
Hans-Christoph Steiner
Este é o PD estendido?
Nulopotent
@iccthedral: usei o pd-extended para fazer isso, mas não sei se usei alguma construção específica do pd-extended.
Janus Troelsen
103

Marque "Como detectar a presença de som / áudio" por Thomer M. Gil .

Basicamente, ele grava o som a cada 5 segundos, em seguida, verifica a amplitude do som, usando soxe decide se aciona ou não um script. Eu acho que você pode facilmente adaptar o rubyscript para seus filhos! Ou você pode optar por cortar o script Python (usando PyAudio) que ele também forneceu.

Atropo
fonte
5
E aquelas explosões de menos de 5 segundos que evitam a detecção?
RhysW
53

Você pode obter informações do microfone fazendo algo como:

arecord -d1 /dev/null -vvv

Pode ser necessário jogar um pouco com as configurações, como:

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

A partir daí, é uma simples questão de analisar a saída.

cha0site
fonte
44

Essa é uma das perguntas mais divertidas que eu já vi. Eu gostaria de agradecer ao tucuxi por uma resposta tão boa; que defini como um 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
fonte
7
Se você iniciar essa execução adicionando uma linha ao /etc/rc4.d/S99rc.local e depois mudar o microfone de entrada de não amplificado para 100%, você também poderá acabar sendo lançado para tty3 (você pode voltar antes que o sono seja interrompido) mais com Ctrl + Alt + F7), e se o seu teclado é muito alto para abrir um terminal, para executar sudo killall too_loud seguida, Ctrl + Alt + F1 e entrar lá).
Alexx Roche
41

Meus 2 centavos para a solução C ou C ++: talvez não seja a abordagem mais eficaz, mas no Linux, você pode usar a API ALSA (biblioteca de manipulação de áudio integrada do Linux) e usar alguma técnica numérica (por exemplo, calcular o som médio cada segundo) para obter o nível de ruído.

Em seguida, você pode verificá-lo em um loop infinito e, se for maior que um limite predefinido, poderá usar a biblioteca X11 para desligar a tela por alguns segundos ou, alternativamente (menos elegante, mas funciona), invocar o chvtcomando system("chvt 3; sleep 15; chvt 7 ");.

H2CO3
fonte
2
Se usando o comando eu consideraria algo diferente então chvt. O ArchWiki tem bons exemplos.
AD