9

I live next to a big road. Having the window open at night is blessedly cool and, intermittently, very loud. How could I adjust the volume automatically, based on the built-in microphone input? If I set the volume so that I can hear speech in a movie while a car passes, it will be very loud at other times, and it feels very obnoxious towards the people nearby (outside and neighbors).

My system is Debian Buster, though I can probably get a generic solution to work. If no package is available that does this, a command to extract the loudness from the default microphone would already be helpful to script this.

2
  • 1
    You can use sox to calculate average loudness (IIRC there are other question on stackexchange about this), and pacmd to change the volume for Pulseaudio.
    – dirkt
    Jul 20, 2017 at 21:57
  • 1
    @dirkt Thanks for the pointers! I found this question, I'll try to implement it tomorrow and report back (perhaps I can answer my own question): superuser.com/questions/306701/…
    – Luc
    Jul 20, 2017 at 23:35

1 Answer 1

3

I've made a Python script to do the job. A remaining problem is that my laptop's microphone will, of course, also pick up its own speakers. I think 'echo cancellation' might be what I'm looking for, but I'd have no idea how to implement that myself. Using an external microphone might work though.

It is python 2 due to the python-alsaaudio dependency, unfortunately.

#!/usr/bin/env python

''' For noise cancellation:
$ pactl load-module module-echo-cancel
$ PULSE_PROP="filter.want=echo-cancel" ./this-script.py
'''

''' SETTINGS (you might want to keep presets for music and speech) '''
smoothing = 15 # Over how many samples should we compute?
step_size = 1 # maximum volume adjustment in percent points
# scale_xxx = (n, level) # At mic level n, scale to level% audio volume
scale_min = (4, 39)
scale_max = (19, 53)

''' CREDITS
https://stackoverflow.com/a/1937058
How get sound input from microphone in python, and process it on the fly?
Answer by jbochi

https://stackoverflow.com/a/10739764
How to programmatically change volume in Ubuntu
Answer by mata
'''

import alsaaudio, audioop, sys, os

bucket = [None for i in range(smoothing)]

inp = alsaaudio.PCM(alsaaudio.PCM_CAPTURE)

inp.setchannels(1)
inp.setrate(8000)
inp.setformat(alsaaudio.PCM_FORMAT_S16_LE)

inp.setperiodsize(200)

print('Setting volume to minimum ({}%)'.format(scale_min[1]))
os.system('pactl set-sink-volume 0 {}%'.format(scale_min[1]))

i = 1
last_volume = scale_min[1]
while True:
    l, data = inp.read()
    if l:
        val = audioop.max(data, 2)
        bucket[i % smoothing] = val

        if i % smoothing == 0:
            m = min(bucket)
            miclvl = float(m) / 50.0

            if miclvl < scale_min[0]:
                scale = scale_min[1]
            elif miclvl > scale_max[0]:
                scale = scale_max[1]
            else:
                miclvl_range = scale_max[0] - scale_min[0]
                level_range = scale_max[1] - scale_min[1]
                scale = (miclvl - scale_min[0]) / miclvl_range * level_range + scale_min[1]

            scale = int(round(scale))
            step = max(min(scale - last_volume, step_size), -step_size)

            if step != 0:
                last_volume += step
                step = '+' + str(step) if step > 0 else str(step)
                os.system('pactl set-sink-volume 0 {}%'.format(step))

            miclvl = round(miclvl, 1)
            miclvlpacing = ' ' * (4 - len(str(miclvl)))
            stepspacing = ' ' * (2 - len(str(step)))
            sys.stdout.write('mic lvl {}{}  ideal scale {}%  adjust {}{}  now {}  '.format(
                miclvl, miclvlpacing, str(scale), step, stepspacing, last_volume))
            print(int(round(last_volume - scale_min[1])) * 'x')

        i += 1
2
  • 1
    Pulseaudio can do echo cancellation to some degree, see e.g. here and here, google for more. If you want to do it yourself, you have to subtract the output signal from the input signal, time-delayed and with the right amplitude. Finding those two parameters automatically is the fun part.:-) (Google "correlation")
    – dirkt
    Jul 21, 2017 at 9:20
  • @dirkt Thanks again! Pulseaudio's doesn't seem to work very well, though; especially at higher volume levels (>50%) it just breaks down, detects itself and keeps going up. I'll just have to go and grab a mic, it's not like they're expensive anyway :)
    – Luc
    Jul 22, 2017 at 11:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.