├── sysconfig └── noised ├── .gitignore ├── README.md ├── init.d └── noised └── noise_detection.rb /sysconfig/noised: -------------------------------------------------------------------------------- 1 | SOUND_CARD=0 2 | DEST_MAIL=ilmorna@gmail.com 3 | LOG_FILE=/var/log/noise_detector.log 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | coverage 6 | InstalledFiles 7 | lib/bundler/man 8 | pkg 9 | rdoc 10 | spec/reports 11 | test/tmp 12 | test/version_tmp 13 | tmp 14 | 15 | # YARD artifacts 16 | .yardoc 17 | _yardoc 18 | doc/ 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ruby-noise-detection 2 | ==================== 3 | 4 | Noise Detection Ruby Script 5 | 6 | Installation 7 | ------------ 8 | The noise dector ruby script uses two linux tool to record and analyse de 9 | output: **sox** and **arecord**. 10 | So you need to install these packages on your raspberrypi (or other linux 11 | system): 12 | 13 | ```bash 14 | sudo apt-get install sox alsa-utils 15 | ``` 16 | 17 | Then, in the current version, the script does not accept parameters to change 18 | file location (log, tmp recording and pid file), so you need to allow the script 19 | execution user (**pi** for example) the write access to these files and folders: 20 | 21 | ``` 22 | RECORD_FILENAME='/tmp/noise.wav' 23 | LOG_FILE='/var/log/noise_detector.log' 24 | PID_FILE='/etc/noised/noised.pid' 25 | ``` 26 | 27 | or you can edit the script changin these variables values with what you prefer 28 | for your system. 29 | 30 | Detect Audio Card 31 | ----------------- 32 | 33 | ```bash 34 | pi@raspberrypi ~ $ noise_detection.rb -d 35 | Detecting your soundcard... 36 | 0 [ALSA ]: BRCM bcm2835 ALSbcm2835 ALSA - bcm2835 ALSA 37 | bcm2835 ALSA 38 | 1 [U0x46d0x8d7 ]: USB-Audio - USB Device 0x46d:0x8d7 39 | USB Device 0x46d:0x8d7 at usb-bcm2708_usb-1.2, full speed 40 | ``` 41 | 42 | Test Audio Card Record 43 | ---------------------- 44 | ```bash 45 | pi@raspberrypi ~ $ noise_detection.rb -t 1 46 | Testing soundcard... 47 | Samples read: 40000 48 | Length (seconds): 5.000000 49 | Scaled by: 2147483647.0 50 | Maximum amplitude: 0.013245 51 | Minimum amplitude: -0.041565 52 | Midline amplitude: -0.014160 53 | Mean norm: 0.013523 54 | Mean amplitude: -0.013466 55 | RMS amplitude: 0.014662 56 | Maximum delta: 0.023560 57 | Minimum delta: 0.000000 58 | Mean delta: 0.003664 59 | RMS delta: 0.004679 60 | Rough frequency: 406 61 | Volume adjustment: 24.059 62 | ``` 63 | 64 | Starting Noise Detection with Alert 65 | ----------------------------------- 66 | ```bash 67 | noise_detection.rb -m 1 -n 0.30 -e me@server.com -v 68 | ``` 69 | The script will be started in background 70 | 71 | Terminating Noise Detection 72 | --------------------------- 73 | ```bash 74 | noise_detection.rb -k 75 | ``` 76 | -------------------------------------------------------------------------------- /init.d/noised: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # noised Noise detection and alert 4 | # 5 | # chkconfig: - 24 76 6 | # 7 | # description: This script allow you to detect noise using your computer 8 | # microphone and send an email alert to you. 9 | # 10 | ### BEGIN INIT INFO 11 | # Provides: noised 12 | # Required-Start: $remote_fs 13 | # Required-Stop: $remote_fs 14 | # Short-Description: Start daemon at boot time 15 | # Description: Enable service provided by daemon. 16 | ### END INIT INFO 17 | 18 | noisefile="/usr/sbin/noise_detection.rb" 19 | pidfile="/var/run/noise_detection.pid" 20 | if [ -d /var/lock/subsys ]; then 21 | # RedHat/CentOS/etc who use subsys 22 | lockfile="/var/lock/subsys/noise_detection" 23 | else 24 | # The rest of them 25 | lockfile="/var/lock/noise_detection" 26 | fi 27 | 28 | # Check that binary exists 29 | if ! [ -f $noisefile ]; then 30 | echo "noise_detection binary not found" 31 | exit 5 32 | fi 33 | 34 | # Source function library. 35 | . /etc/init.d/functions 36 | 37 | if [ -f /etc/sysconfig/noised ]; then 38 | . /etc/sysconfig/noised 39 | fi 40 | 41 | echo $SOUND_CARD 42 | 43 | # Determine if we can use the -p option to daemon, killproc, and status. 44 | # RHEL < 5 can't. 45 | if status | grep -q -- '-p' 2>/dev/null; then 46 | daemonopts="--pidfile $pidfile" 47 | pidopts="-p $pidfile" 48 | fi 49 | 50 | start() { 51 | echo -n "Starting noise detector: " 52 | # Only try to start if not already started 53 | if ! rh_status_q; then 54 | daemon ${daemonopts} ${noisefile} -m $SOUND_CARD 55 | fi 56 | # This will be 0 if script is already running 57 | RETVAL=$? 58 | echo 59 | [ $RETVAL -eq 0 ] && touch ${lockfile} 60 | return $RETVAL 61 | } 62 | 63 | stop() { 64 | echo -n "Shutting down noise detector: " 65 | # If running, try to stop it 66 | if rh_status_q; then 67 | killproc ${pidopts} -d 10 ${noisefile} 68 | else 69 | # Non-zero status either means lockfile and pidfile need cleanup (1 and 2) 70 | # or the process is already stopped (3), so we can just call true to 71 | # trigger the cleanup that happens below. 72 | true 73 | fi 74 | RETVAL=$? 75 | echo 76 | [ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile} 77 | return $RETVAL 78 | } 79 | 80 | restart() { 81 | stop 82 | start 83 | } 84 | 85 | rh_status() { 86 | status ${pidopts} ${noisefile} 87 | RETVAL=$? 88 | return $RETVAL 89 | } 90 | 91 | # See how we were called. 92 | case "$1" in 93 | start) 94 | start 95 | ;; 96 | stop) 97 | stop 98 | ;; 99 | restart) 100 | restart 101 | ;; 102 | status) 103 | rh_status 104 | ;; 105 | *) 106 | echo "Usage: noised {start|stop|restart|status}" 107 | RETVAL=2 108 | ;; 109 | esac 110 | exit $RETVAL 111 | -------------------------------------------------------------------------------- /noise_detection.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby -w 2 | # 3 | # Copyright (C) 2013 Marco Mornati [http://www.mornati.net] 4 | # Based on Thomer M. Gil First [http://thomer.com/] version template 5 | # 6 | # Oct 05, 2012: Initial version 7 | # 8 | # This program is free software. You may distribute it under the terms of 9 | # the GNU General Public License as published by the Free Software 10 | # Foundation, version 3. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 15 | # Public License for more details. 16 | # 17 | # This program detects the presence of sound and invokes a program. 18 | # 19 | 20 | require 'optparse' 21 | require 'net/smtp' 22 | require 'logger' 23 | require 'date' 24 | 25 | 26 | HW_DETECTION_CMD = "cat /proc/asound/cards" 27 | # You need to replace MICROPHONE with the name of your microphone, as reported 28 | # by /proc/asound/cards 29 | SAMPLE_DURATION = 5 # seconds 30 | FORMAT = 'S16_LE' # this is the format that my USB microphone generates 31 | THRESHOLD = 0.05 32 | RECORD_FILENAME='/tmp/noise.wav' 33 | LOG_FILE='/var/log/noise_detector.log' 34 | PID_FILE='/etc/noised/noised.pid' 35 | 36 | logger = Logger.new(LOG_FILE) 37 | logger.level = Logger::DEBUG 38 | 39 | logger.info("Noise detector started @ #{DateTime.now.strftime('%d/%m/%Y %H:%M:%S')}") 40 | 41 | 42 | def self.check_required() 43 | if !File.exists?('/usr/bin/arecord') 44 | warn "/usr/bin/arecord not found; install package alsa-utils" 45 | exit 1 46 | end 47 | 48 | if !File.exists?('/usr/bin/sox') 49 | warn "/usr/bin/sox not found; install package sox" 50 | exit 1 51 | end 52 | 53 | if !File.exists?('/proc/asound/cards') 54 | warn "/proc/asound/cards not found" 55 | exit 1 56 | end 57 | 58 | end 59 | 60 | # Parsing script parameters 61 | options = {} 62 | optparse = OptionParser.new do |opts| 63 | opts.banner = "Usage: noise_detection.rb -m ID [options]" 64 | 65 | opts.on("-m", "--microphone SOUND_CARD_ID", "REQUIRED: Set microphone id") do |m| 66 | options[:microphone] = m 67 | end 68 | opts.on("-s", "--sample SECONDS", "Sample duration") do |s| 69 | options[:sample] = s 70 | end 71 | opts.on("-n", "--threshold NOISE_THRESHOLD", "Set Activation noise Threshold. EX. 0.1") do |n| 72 | options[:threshold] = n 73 | end 74 | opts.on("-e", "--email DEST_EMAIL", "Alert destination email") do |e| 75 | options[:email] = e 76 | end 77 | opts.on("-v", "--[no-]verbose", "Run verbosely") do |v| 78 | options[:verbose] = v 79 | end 80 | opts.on("-d", "--detect", "Detect your sound cards") do |d| 81 | options[:detection] = d 82 | end 83 | opts.on("-t", "--test SOUND_CARD_ID", "Test soundcard with the given id") do |t| 84 | options[:test] = t 85 | end 86 | opts.on("-k", "--kill", "Terminating background script") do |k| 87 | options[:kill] = k 88 | end 89 | end.parse! 90 | 91 | if options[:kill] 92 | logger.info("Terminating script"); 93 | logger.debug("Looking for pid file in #{PID_FILE}") 94 | begin 95 | pidfile = File.open(PID_FILE, "r") 96 | storedpid = pidfile.read 97 | Process.kill("TERM", Integer(storedpid)) 98 | rescue Exception => e 99 | logger.error("Cannot read pid file: " + e.message) 100 | exit 1 101 | end 102 | exit 0 103 | end 104 | 105 | if options[:detection] 106 | puts "Detecting your soundcard..." 107 | puts `#{HW_DETECTION_CMD}` 108 | exit 0 109 | end 110 | 111 | #Check required binaries 112 | check_required() 113 | 114 | if options[:sample] 115 | SAMPLE_DURATION = options[:sample] 116 | end 117 | 118 | if options[:threshold] 119 | THRESHOLD = options[:threshold].to_f 120 | end 121 | 122 | if options[:test] 123 | puts "Testing soundcard..." 124 | puts `/usr/bin/arecord -D plughw:#{options[:test]},0 -d #{SAMPLE_DURATION} -f #{FORMAT} 2>/dev/null | /usr/bin/sox -t .wav - -n stat 2>&1` 125 | exit 0 126 | end 127 | 128 | optparse.parse! 129 | 130 | #Now raise an exception if we have not found a host option 131 | raise OptionParser::MissingArgument if options[:microphone].nil? 132 | raise OptionParser::MissingArgument if options[:email].nil? 133 | 134 | if options[:verbose] 135 | logger.debug("Script parameters configurations:") 136 | logger.debug("SoundCard ID: #{options[:microphone]}") 137 | logger.debug("Sample Duration: #{SAMPLE_DURATION}") 138 | logger.debug("Output Format: #{FORMAT}") 139 | logger.debug("Noise Threshold: #{THRESHOLD}") 140 | logger.debug("Record filename (overwritten): #{RECORD_FILENAME}") 141 | logger.debug("Destination email: #{options[:email]}") 142 | end 143 | 144 | #Starting script part 145 | pid = fork do 146 | stop_process = false 147 | Signal.trap("USR1") do 148 | logger.debug("Running...") 149 | end 150 | Signal.trap("TERM") do 151 | logger.info("Terminating...") 152 | File.delete(PID_FILE) 153 | stop_process = true 154 | end 155 | 156 | loop do 157 | if (stop_process) 158 | logger.info("Noise detector stopped @ #{DateTime.now.strftime('%d/%m/%Y %H:%M:%S')}") 159 | break 160 | end 161 | rec_out = `/usr/bin/arecord -D plughw:#{options[:microphone]},0 -d #{SAMPLE_DURATION} -f #{FORMAT} -t wav #{RECORD_FILENAME} 2>/dev/null` 162 | out = `/usr/bin/sox -t .wav #{RECORD_FILENAME} -n stat 2>&1` 163 | out.match(/Maximum amplitude:\s+(.*)/m) 164 | amplitude = $1.to_f 165 | logger.debug("Detected amplitude: #{amplitude}") if options[:verbose] 166 | if amplitude > THRESHOLD 167 | logger.info("Sound detected!!!") 168 | 169 | # Read a file 170 | filecontent = File.open(RECORD_FILENAME ,"rb") {|io| io.read} 171 | 172 | encoded = [filecontent].pack("m") # base64 econding 173 | puts value = %x[/usr/sbin/sendmail #{options[:email]} << EOF 174 | subject: WARNING: Noise Detected 175 | from: home@mornati.net 176 | Content-Description: "noise.wav" 177 | Content-Type: audio/x-wav; name="noise.wav" 178 | Content-Transfer-Encoding:base64 179 | Content-Disposition: attachment; filename="noise.wav" 180 | #{encoded} 181 | EOF] 182 | else 183 | logger.debug("No sound detected...") 184 | end 185 | end 186 | end 187 | 188 | Process.detach(pid) 189 | logger.debug("Started... (#{pid})") 190 | File.open(PID_FILE, "w") { |file| file.write(pid) } 191 | --------------------------------------------------------------------------------