├── .gitignore ├── README.md ├── LICENSE └── slm.py /.gitignore: -------------------------------------------------------------------------------- 1 | .ropeproject/ 2 | *.pyc 3 | *.txt 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RPI_SLM 2 | 3 | ## Basic sound level meter in raspberry using pyfilterbank library 4 | 5 | As we use pyfilterbank library you need first to install it. 6 | 7 | 1. Clone repository 8 | 9 | `git clone https://github.com/SiggiGue/pyfilterbank.git` 10 | 11 | 2. If you have numpy => 1.13.3 in your raspberry as being pointed out by @spors you 12 | need to replace a line in butterworth.py 13 | 14 | Erase 15 | - L2 = L / 2.0 16 | 17 | Replace with 18 | + L2 = L// 2 19 | 20 | 3. Compile C functions 21 | 22 | `gcc -c -std=c99 -O3 sosfilt.c` 23 | `gcc -shared -o sosfilt.so sosfilt.o` 24 | 25 | 4. Build 26 | 27 | `python setup.py build` 28 | 29 | 5. Install 30 | 31 | `python setup.py install` 32 | 33 | ## Basic Usage 34 | 35 | with ` python slm.py` will run with a default calibration constant and a slow weighting. 36 | 37 | You can specify a calibration constant and F or S time weighting. 38 | 39 | `python slm.py -c -t ` 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /slm.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | ### Filtros de Octavas y tercios con pyfilterbank segun IEC 61260 5 | 6 | import argparse 7 | import numpy as np 8 | from pyfilterbank.octbank import FractionalOctaveFilterbank 9 | from pyfilterbank.splweighting import a_weighting_coeffs_design 10 | from pyfilterbank.octbank import frequencies_fractional_octaves 11 | import pyaudio 12 | from datetime import datetime 13 | from scipy.signal import lfilter 14 | import time 15 | 16 | 17 | # Argumentos del script 18 | 19 | parser = argparse.ArgumentParser(description='Basic SLM with linear weighting, default sampling rate is 44100') 20 | parser.add_argument("-c", "--constant", help="Calibration constant", type=float) 21 | parser.add_argument("-t", "--time", help="type -t F for fast or -t S for Slow", type=str) 22 | args = parser.parse_args() 23 | 24 | if args.constant: 25 | C = args.constant 26 | else: 27 | print "Calibration constant set to default -54.2" 28 | C = -54.2 29 | 30 | if args.time == 'F': 31 | T = 0.125 32 | frames = 5513 33 | elif args.time == 'S': 34 | T = 1 35 | frames = 44100 36 | else: 37 | print "Time weigthing set to Slow" 38 | T = 1 39 | frames = 44100 40 | 41 | # parametros Stream de audio 42 | CHANNELS = 1 43 | fs = 44100 44 | RATE = fs 45 | 46 | 47 | # Construir banco de filtros de tercio y octava 48 | third_oct = FractionalOctaveFilterbank( 49 | sample_rate=fs, 50 | order=4, 51 | nth_oct=3.0, 52 | norm_freq=1000.0, 53 | start_band=-19, 54 | end_band=13, 55 | edge_correction_percent=0.01, 56 | filterfun='cffi') 57 | 58 | octave = FractionalOctaveFilterbank( 59 | sample_rate=fs, 60 | order=4, 61 | nth_oct=1.0, 62 | norm_freq=1000.0, 63 | start_band=-6, 64 | end_band=4, 65 | edge_correction_percent=0.01, 66 | filterfun='cffi') 67 | 68 | # Filtro tipo A 69 | b, a = a_weighting_coeffs_design(fs) 70 | 71 | freqs, foo = frequencies_fractional_octaves(-6,4,1000,1) 72 | 73 | p = pyaudio.PyAudio() 74 | levels = [] 75 | date = datetime.now() 76 | filename = str(date.year) + str(date.month) + str(date.day) + '_' + str(date.hour) + str(date.minute) + str(date.second) + '.txt' 77 | f = open(filename, 'ab') 78 | # Formato de la tabla en el .txt 79 | f.write('Time\t\t') 80 | for freq in freqs: 81 | f.write('{0:.0f}Hz\t'.format(freq)) 82 | f.write('LA\t') 83 | f.write('LZ\t\n') 84 | f.write((13*8)*'-') 85 | f.write(2*'\n') 86 | 87 | def db_level(pa, T, C): 88 | po = 0.000002 89 | level = 10*np.log10(np.nansum((pa/po)**2)/T) + C 90 | return level 91 | 92 | def leq(levels): 93 | e_sum = (np.sum(np.power(10, np.multiply(0.1, levels))))/len(levels) 94 | eq_level = 10*np.log10(e_sum) 95 | return eq_level 96 | 97 | def callback(in_data, frame_count, time_info, status): 98 | audio_data = np.fromstring(in_data, dtype=np.float32) 99 | y = lfilter(b, a, audio_data) 100 | y_oct, states = octave.filter(y) 101 | i = 0 102 | oct_level = [] 103 | f.write('{:%H:%M:%S}\t\t'.format(datetime.now())) 104 | for e in y_oct.T: 105 | oct_level.append(db_level(e,T,C)) 106 | print('{0:.2f} Hz -- {1:.2f} dBA'.format(freqs[i],oct_level[i])) 107 | f.write('{0:.2f}\t'.format(oct_level[i])) 108 | i += 1 109 | La = db_level(y,T,C) 110 | L = db_level(audio_data,T,C) 111 | f.write('{0:.2f}\t'.format(La)) 112 | f.write('{0:.2f}\t'.format(L)) 113 | f.write('\n') 114 | levels.append(La) 115 | print('{0:.2f} dBA'.format(La)) 116 | print('{0:.2f} dBZ'.format(L)) 117 | print('Leq {0:.2f} dBA'.format(leq(levels))) 118 | return (in_data, pyaudio.paContinue) 119 | 120 | stream = p.open(format=pyaudio.paFloat32, 121 | channels=CHANNELS, 122 | rate=RATE, 123 | frames_per_buffer=frames, 124 | input=True, 125 | output=False, 126 | stream_callback=callback) 127 | 128 | stream.start_stream() 129 | 130 | record = True 131 | 132 | 133 | while record: 134 | time.sleep(0.1) 135 | 136 | 137 | stream.stop_stream() 138 | stream.close() 139 | p.terminate() 140 | 141 | 142 | 143 | --------------------------------------------------------------------------------