How do I make a machine "blank screen" for a period of time (as a penalty) if certain noise levels are reached?

My kids (4 and 5) yell a lot when playing games on the computer. I found an effective cure for this. When I hear loud noises, I ssh into the game computer and do:

chvt 3;  sleep 15;  chvt 7 

This will turn off the screen for 15 seconds on Linux. I've told them that the computer doesn't like loud noises. They totally believe this and beg the computer for forgiveness. They became much quieter, but not to the level that I would be happy, and so I need to continue this educational process. However, I am not always around to do this manually.

Is it possible to automate this? A microphone is attached to the box. If the level of loudness passes some threshold then I want to run a command.


Solution 1:

Use sox from SoX to analyze a short audio sample:

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

With -t .wav we specify we process the wav type, "|arecord -d 2" executes the arecord program for two seconds, -n outputs to the null file and with stat we specify we want statistics.

The output of this command, on my system with some background speech, is:

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

The maximum amplitude can then be extracted via:

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

We grep for the line we want, use tr to trim away the space characters and then cut it by the : character and take the second part which gives us 0.068383 in this example. As suggested by comments, RMS is a better measure of energy than maximum amplitude.

You can finally use bc on the result to compare floating-point values from the command-line:

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

If you build a loop (see Bash examples) that calls sleep for 1 minute, tests the volume, and then repeats, you can leave it running in the background. The last step is to add it to the init scripts or service files (depending on your OS / distro), such that you do not even have to launch it manually.

Solution 2:

Here's how it can be done with Pure Data:

Kid yell prevention using Pure Data

Metro is a metronome, and "metro 100" keeps banging each 100 ms.

The audio is coming from adc~, the volume is calculated by env~. "pd dsp 0" turns off the DSP when banged, "pd dsp 1" turns it on. "shell" executes the passed command in a shell, I use the Linux xrandr API to set the brightness to X, you need to adapt this for Wayland.

As you can see, grace period and locking takes up way more space than audio code does.

Making a solution with ring buffers and/or moving averages should be way easier than doing it with sox. So I don't think it's a bad idea to use Pure Data for this. But the screen blanking itself and the locking doesn't fit with the dataflow paradigm.

The PD file is at gist.github.com: ysangkok - kidsyell.pd.

Solution 3:

Check "How to detect the presence of sound/audio" by Thomer M. Gil.

Basically it records the sound every 5 seconds, than checks for the sound amplitude, using sox, and decides if trigger a script or not. I think you can easily adapt the ruby script for your children! Or you can choose to hack away on the Python script (using PyAudio) that he has provided as well.

Solution 4:

You can get information from the microphone by doing something like:

arecord -d1 /dev/null -vvv

You might have to play with the settings a little, such as:

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

From there on out, it's a simple matter of parsing the output.

Solution 5:

This is one of the more fun questions that I've seen. I would like to thank tucuxi for such a fine answer; that I have set as a bash script

#!/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