pythonaudioalsapyalsaaudio

Calculating decibel values from live microphone audio using alsaaudio


I have this Python script that I'm running on my Ubuntu laptop to get the live ambient noise levels from the microphone on the laptop. I don't think this script works well though and I can't figure out why. I suspected it might just be a long period size but I'm not sure if that's it. Can someone help me understand what's wrong with this script? Thank you!

import alsaaudio
import audioop
import time
import math

# Set the audio parameters
device = 'default'
channels = 1  # mono
sample_rate = 44100  # 44.1 kHz
format = alsaaudio.PCM_FORMAT_S16_LE

# Open the audio input stream
inp = alsaaudio.PCM(alsaaudio.PCM_CAPTURE, alsaaudio.PCM_NORMAL, device)
inp.setchannels(channels)
inp.setrate(sample_rate)
inp.setformat(format)
inp.setperiodsize(256)

# Main loop to continuously print audio levels
try:
    while True:
        # Read audio data from the microphone
        _, data = inp.read()

        # Calculate the peak audio level in decibels
        rms = audioop.rms(data, 2)  # 2 for sample width 16-bit

        # Print the decibel level without dynamic range adjustment
        decibels = 20 * math.log10(rms)
        print(f"Decibels: {decibels:.2f} dB")

        time.sleep(0.01)  # Adjust the sleep duration as needed

except KeyboardInterrupt:
    print("Stopping the script.")

I expect this script to be sensitive and show an immediate jump in the decibel values if I make a sound. That unfortunately is not the case right now and I'm not sure why. I used the sounddevice library and it seems to work well but I can't get the same result with pyalsaaudio.

EDIT: Based on comments by @fdcpp, I have modified my script and I get negative values and don't know why

import alsaaudio
import audioop
import math
import struct

# Set the audio parameters
device = 'default'
channels = 1  # mono
sample_rate = 44100  # 44.1 kHz
format = alsaaudio.PCM_FORMAT_S16_LE

# Open the audio input stream
inp = alsaaudio.PCM(alsaaudio.PCM_CAPTURE, alsaaudio.PCM_NORMAL, device, channels=channels, rate=sample_rate, format=format, periodsize=1024)

# Main loop to continuously print audio levels
try:
    while True:
        # Read audio data from the microphone
        _, data = inp.read()

        # Convert byte data to 16-bit integer format. Have tried with and without this and get the same result.
        samples = struct.unpack('<' + ('h' * (len(data) // 2)), data)

        # Calculate the peak audio level in decibels
        peak_amplitude = max(map(abs, samples))

        # Convert amplitude to decibels
        decibels = 20 * math.log10(peak_amplitude / 32767)  # Assuming 16-bit audio
        print(f"Decibels: {decibels:.2f} dB")

except KeyboardInterrupt:
    print("Stopping the script.")


Solution

  • Based on @fdcpp's comments, this seems to work well.

    import alsaaudio
    import audioop
    import math
    
    # Set the audio parameters
    device = 'default'
    sample_rate = 44100  # 44.1 kHz
    
    # Open the audio input stream
    inp = alsaaudio.PCM(alsaaudio.PCM_CAPTURE, alsaaudio.PCM_NORMAL, device, channels=1, rate=sample_rate, format=alsaaudio.PCM_FORMAT_S16_LE, periodsize=1024)
    
    
    # Main loop to continuously print audio levels
    try:
        while True:
            # Read audio data from the microphone
            _, data = inp.read()
    
            # Calculate the peak audio level in decibels
            peak_amplitude = audioop.max(data, 2)
    
            # Convert amplitude to decibels
            decibels = 20 * math.log10(peak_amplitude)  # Assuming 16-bit audio
            print(f"Decibels: {decibels:.2f} dB")
    
    except KeyboardInterrupt:
        print("Stopping the script.")