Decibel Visual Indicator

by johnmpierce in Circuits > Audio

649 Views, 5 Favorites, 0 Comments

Decibel Visual Indicator

20231209_123959.jpg

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

pibox.jpg
20231209_133539.jpg

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

connect1.jpg
connect2.jpg
20231209_134608.jpg

Seat the ReSpeaker onto the GPIO. It will fit snugly over all the GPIO pins.

Attach Pi to the Case

20231209_134641.jpg
20231209_134711.jpg

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

20231209_134500.jpg

Attach Keyboard, Mouse, and Monitor with mini HDMI. This must be done after the pi is seated in the case.

Boot Pi

20231209_14h04m54s_grim.png

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.