Decibel Visual Indicator
I wanted to create a prototype that would indicate whether the volume of nearby sound was within a dangerous range of dB. It will flash to the music and indicate safe ranges with cool colors, and dangerous readings with warm colors. This prototype would eventually be sized down with the proper hardware to be the size of a watch, or pendant.
Supplies
Raspberry Pi 4 with most recent OS, and provided case.
ReSpeaker 4 mic array. We will be repurposing this device and its onboard soundcard. The ReSpeaker is intended for projects with voice activated commands.
Keyboard, Mouse, Monitor.
Speaker for sound testing.
Attach ReSpeaker to Pi
Seat the ReSpeaker onto the GPIO. It will fit snugly over all the GPIO pins.
Attach Pi to the Case
Seat the Pi into the case until you hear it click in and seat firmly. This will give the respeaker some stability. The top of the case wont fit, but thats okay.
Attach Peripherals
Attach Keyboard, Mouse, and Monitor with mini HDMI. This must be done after the pi is seated in the case.
Boot Pi
Boot the Pi, and launch the command line. Using CTRL+ALT+T.
Update Drivers
Run the following code in the terminal. To install the drivers for the ReSpeaker.
sudo apt-get update
git clone https://github.com/HinTak/seeed-voicecard.git
cd seeed-voicecard
sudo ./install.sh
sudo reboot now
The Pi will reboot.
Verify the ReSpeaker Is the Default Audio Device
Once the Pi fully reboots, open the terminal.
Run the command in the terminal to check the Audio Driver settings.
arecord -L
The output should look like this. Make sure it says "seed4micvoicecard" to activate the microphone inputs on the Respeaker.
null
Discard all samples (playback) or generate zero samples (capture)
jack
JACK Audio Connection Kit
pulse
PulseAudio Sound Server
default
playback
ac108
sysdefault:CARD=seeed4micvoicec
seeed-4mic-voicecard, # <<<<
Audio Device
dmix:CARD=seeed4micvoicec,DEV=0
seeed-4mic-voicecard, # <<<<
Direct sample mixing device
dsnoop:CARD=seeed4micvoicec,DEV=0
seeed-4mic-voicecard, # <<<<
Direct sample snooping device
hw:CARD=seeed4micvoicec,DEV=0
seeed-4mic-voicecard, # <<<<
Direct hardware device without any conversions
plughw:CARD=seeed4micvoicec,DEV=0
seeed-4mic-voicecard, # <<<<
Hardware device with all software conversions
usbstream:CARD=seeed4micvoicec
seeed-4mic-voicecard # <<<<
USB Stream Output
usbstream:CARD=ALSA
bcm2835 ALSA
USB Stream Output
Update the Libraries.
In the terminal, run the following code to update all the libraries needed for the project.
pip install pixel_ring gpiozero pyaudio numpy math time
sudo apt update
sudo apt upgrade
Program the Decibel Meter
I will be using Thonny as an IDE for the Python code.
Enter the following code to load the ReSpeaker with the decibel meter function.
#!/usr/bin/env python3
import time
import math
import pyaudio
import numpy as np
from pixel_ring import pixel_ring
from gpiozero import LED
# Activate LEDS
if __name__ == '__main__':
power = LED(5)
power.on()
def apply_a_weighting(frequency, amplitude):
# A-weighting filter coefficients (adjust as needed)
a_weighting_coefficients = {
20: 39.7,
25: 32.7,
# ... (remaining coefficients)
}
if frequency in a_weighting_coefficients:
return amplitude # Do not subtract the A-weighting coefficients
else:
return amplitude
def measure_decibels(audio_signal, sample_rate):
# Assuming audio_signal is a list of amplitude values representing the audio signal
# Calculate the root mean square (RMS) value of the audio signal
rms = math.sqrt(sum(x ** 2 for x in audio_signal) / len(audio_signal))
# Apply A-weighting to the RMS value
a_weighted_rms = apply_a_weighting(sample_rate / 6, rms)
# Calculate the decibel level in dB SPL (Sound Pressure Level)
decibels = 10 * math.log10(a_weighted_rms / 20e-6) # 20e-6 Pa is the reference sound pressure
return decibels
def set_led_color(decibel):
# Set LED color based on decibel level
if 0 <= decibel <= 71:
pixel_ring.set_color(r=10, g=255, b=50) # Lime Green
elif 72 <= decibel <= 73:
pixel_ring.set_color(r=36, g=100, b=185) # Forest Green
elif 74 <= decibel <= 75:
pixel_ring.set_color(r=0, g=15, b=255) # Blue
elif 76 <= decibel <= 77:
pixel_ring.set_color(r=100, g=52, b=7) # Forest Green
elif 78 <= decibel <= 79:
pixel_ring.set_color(r=10, g=15, b=250) # Purple
elif 80 <= decibel <= 82:
pixel_ring.set_color(r=255, g=15, b=250) # Pink
elif 83 <= decibel <= 84:
pixel_ring.set_color(r=255, g=100, b=0) # Orange
elif 85 <= decibel <= 86:
pixel_ring.set_color(r=200, g=0, b=175) # Red
elif 85 <= decibel <= 86:
pixel_ring.set_color(r=255, g=15, b=100) # Deep Red
elif 87 <= decibel <= 88:
pixel_ring.set_color(r=255, g=255, b=255) # Bright White
elif 89 <= decibel <= 90:
pixel_ring.set_color(r=255, g=0, b=0) # Pure Red
elif 91 <= decibel <= 92:
pixel_ring.set_color(r=255, g=15, b=100) # Deep Red
elif 93 <= decibel <= 94:
pixel_ring.set_color(r=255, g=255, b=255) # Bright White
elif 95 <= decibel <= 96:
pixel_ring.set_color(r=255, g=0, b=0) # Pure Red
elif decibel >= 97:
pixel_ring.set_color(r=200, g=200, b=200) # White
else:
# Handle other cases or set a default color
pass
def main():
# PyAudio configuration
chunk_size = 1400 # Adjust the chunk size according to your requirements
sample_format = pyaudio.paInt16
channels = 1 # Adjust the number of channels based on your ReSpeaker array
sample_rate = 44100 # Adjust the sample rate based on your ReSpeaker array
p = pyaudio.PyAudio()
pixel_ring.set_brightness(20) # Adjust the brightness as needed
stream = p.open(format=sample_format,
channels=channels,
rate=sample_rate,
input=True,
frames_per_buffer=chunk_size)
print("Listening...")
try:
while True:
# Read audio data
data = np.frombuffer(stream.read(
chunk_size), dtype=np.int16)
# Calculate decibel level
decibel = measure_decibels(data, sample_rate)
print(f"Decibel Level: {decibel:.3f} dB")
# Set LED color based on decibel level
set_led_color(decibel)
except KeyboardInterrupt:
print("Stopping...")
if __name__ == "__main__":
main()
Adjust the RGB Values
In these piece of code you can adjust the RGB Values of the LEDS.
I used this RGB calculator from W3 .
def set_led_color(decibel):
# Set LED color based on decibel level
if 0 <= decibel <= 71:
pixel_ring.set_color(r=10, g=255, b=50) # Lime Green
elif 72 <= decibel <= 73:
pixel_ring.set_color(r=36, g=100, b=185) # Forest Green
elif 74 <= decibel <= 75:
pixel_ring.set_color(r=0, g=15, b=255) # Blue
elif 76 <= decibel <= 77:
pixel_ring.set_color(r=100, g=52, b=7) # Forest Green
elif 78 <= decibel <= 79:
pixel_ring.set_color(r=10, g=15, b=250) # Purple
elif 80 <= decibel <= 82:
pixel_ring.set_color(r=255, g=15, b=250) # Pink
elif 83 <= decibel <= 84:
pixel_ring.set_color(r=255, g=100, b=0) # Orange
elif 85 <= decibel <= 86:
pixel_ring.set_color(r=200, g=0, b=175) # Red
elif 85 <= decibel <= 86:
pixel_ring.set_color(r=255, g=15, b=100) # Deep Red
elif 87 <= decibel <= 88:
pixel_ring.set_color(r=255, g=255, b=255) # Bright White
elif 89 <= decibel <= 90:
pixel_ring.set_color(r=255, g=0, b=0) # Pure Red
elif 91 <= decibel <= 92:
pixel_ring.set_color(r=255, g=15, b=100) # Deep Red
elif 93 <= decibel <= 94:
pixel_ring.set_color(r=255, g=255, b=255) # Bright White
elif 95 <= decibel <= 96:
pixel_ring.set_color(r=255, g=0, b=0) # Pure Red
elif decibel >= 97:
pixel_ring.set_color(r=200, g=200, b=200) # White
else:
# Handle other cases or set a default color
pass
Sampling Settings
Adjust the chunk, channels, and sample rate to your specific piece of hardware.
def main():
# PyAudio configuration
chunk_size = 1400 # Adjust the chunk size according to your requirements
sample_format = pyaudio.paInt16
channels = 1 # Adjust the number of channels based on your ReSpeaker array
sample_rate = 44100 # Adjust the sample rate based on your ReSpeaker array
Adjust Brightness
Increase/Decrease the value of "pixel_ring.set_brightness" to adjust the brightness.
Lower than 3 can be nearly imperceptible with lights on.
p = pyaudio.PyAudio()
pixel_ring.set_brightness(20) # Adjust the brightness as needed
Adjust Mic Sensitivity
Increase or decrease the value of "decibels =10" to change the sensitivity. Increments of .5 work best.
# Calculate the decibel level in dB SPL (Sound Pressure Level)
decibels = 10 * math.log10(a_weighted_rms / 20e-6) # 20e-6 Pa is the reference sound pressure
Make the Script a Service
This will make the python script a service, this way it will launch on boot without the peripherals attached. This will launch the file you need to edit in the Nano text editor. I named the service runstart.service . Name your service anything you want as you use .service as the file type.
sudo nano /etc/systemd/system/runstart.service
In the Nano text editor change the file to match below.
[Unit]
Description=My Script Service
After=multi-user.target
[Service]
ExecStart=/usr/bin/python3 /path/to/your/script.py
WorkingDirectory=/path/to/your/script/directory
StandardOutput=inherit
StandardError=inherit
Restart=always
User=pi
[Install]
WantedBy=multi-user.target
Make sure to replace /path/to/your/script.py and /path/to/your/script/directory with the actual paths to your Python script and its directory. Also, adjust the User parameter to the user that should run the script (in this example, it's set to pi).
Test With a Speaker
Verify the code is working properly. It should launch on its own. It will take roughly 30 seconds to launch. The LEDs should output Blue and Green in low noise environments. When music is playing at an increased volume, there will be more purple and orange. When the LEDS are outputting Red and White, the decibel meter has moved into a dangerous volume level. You can test this by using a speaker or radio. Play music and move the decibel meter away from the source of sound, and verify it displays cool colors. Then move your pi close to the speaker and monitor for warm colors.
If there seems to be a delay, or the LEDS respond slowly, return to step #10. Attempt to adjust the chunk, and try or 2 channels. The chunk size can be as low as 1000 and as high as 20,000. I would keep the sample rate at 44100.