28

I'm trying to read, in near-realtime, the volume coming from the audio of a USB microphone in Python.

I have the pieces, but can't figure out how to put it together.

If I already have a .wav file, I can pretty simply read it using wavefile:

from wavefile import WaveReader

with WaveReader("/Users/rmartin/audio.wav") as r:
    for data in r.read_iter(size=512):
        left_channel = data[0]
        volume = np.linalg.norm(left_channel)
        print(volume)

This works great, but I want to process the audio from the microphone in real-time, not from a file.

So my thought was to use something like ffmpeg to PIPE the real-time output into WaveReader, but my Byte knowledge is somewhat lacking.

import subprocess
import numpy as np

command = ["/usr/local/bin/ffmpeg",
            '-f', 'avfoundation',
            '-i', ':2',
            '-t', '5',
            '-ar', '11025',
            '-ac', '1',
            '-acodec','aac', '-']

pipe = subprocess.Popen(command, stdout=subprocess.PIPE, bufsize=10**8)
stdout_data = pipe.stdout.read()
audio_array = np.fromstring(stdout_data, dtype="int16")

print audio_array

That looks pretty, but it doesn't do much. It fails with a [NULL @ 0x7ff640016600] Unable to find a suitable output format for 'pipe:' error.

I assume this is a fairly simple thing to do given that I only need to check the audio for volume levels.

Anyone know how to accomplish this simply? FFMPEG isn't a requirement, but it does need to work on OSX & Linux.

Brodie
  • 5
  • 5
Ryan Martin
  • 1,613
  • 3
  • 24
  • 36
  • I've used [pyaudio](https://people.csail.mit.edu/hubert/pyaudio/); it is available on [pypi](https://pypi.python.org/pypi/PyAudio). – Warren Weckesser Oct 19 '16 at 17:43
  • 1
    For piping to WAV, drop `-acodec aac` and insert `-f wav` before `-` – Gyan Oct 19 '16 at 18:39
  • 3
    You could use the [sounddevice](http://python-sounddevice.readthedocs.io/) module. There is an [example for plotting the microphone signal in realtime](http://python-sounddevice.readthedocs.io/en/latest/examples.html#plot-microphone-signal-s-in-real-time) which could easily be changed to calculate the sound level instead of plotting. – Matthias Oct 20 '16 at 08:59
  • Thanks for the feedback Warren, Mulvya & Matthias. Mulvya - excellent - your suggestion showed me why my FFMPEG stream wasn't working. Matthias - sounddevice is awesome and exactly what I'm looking for. I posted an example below for anyone else looking. – Ryan Martin Oct 20 '16 at 14:30
  • Nice, but there is now way of getting volume information from outside of the callback. – X Builder Feb 25 '22 at 04:46

3 Answers3

43

Thanks to @Matthias for the suggestion to use the sounddevice module. It's exactly what I need.

For posterity, here is a working example that prints real-time audio levels to the shell:

# Print out realtime audio volume as ascii bars

import sounddevice as sd
import numpy as np

def print_sound(indata, outdata, frames, time, status):
    volume_norm = np.linalg.norm(indata)*10
    print ("|" * int(volume_norm))

with sd.Stream(callback=print_sound):
    sd.sleep(10000)

enter image description here

Community
  • 1
  • 1
Ryan Martin
  • 1,613
  • 3
  • 24
  • 36
  • 1
    That works nicely, but only if you add `import numpy as np`. BTW, you could use `InputStream` because you don't need `outdata`. But it works either way ... – Matthias Oct 21 '16 at 15:14
  • 1
    Getting `ValueError: Input and output device must have the same samplerate` =/ – Alexander Matveev Dec 16 '20 at 19:41
13

Python 3 user here
I had few problems to make that work so I used: https://python-sounddevice.readthedocs.io/en/0.3.3/examples.html#plot-microphone-signal-s-in-real-time
And I need to install sudo apt-get install python3-tk for python 3.6 look Tkinter module not found on Ubuntu
Then I modified script:

#!/usr/bin/env python3
import numpy as np
import sounddevice as sd

duration = 10 #in seconds

def audio_callback(indata, frames, time, status):
   volume_norm = np.linalg.norm(indata) * 10
   print("|" * int(volume_norm))


stream = sd.InputStream(callback=audio_callback)
with stream:
   sd.sleep(duration * 1000)

And yes it working :)

pbaranski
  • 22,778
  • 19
  • 100
  • 117
1

The fastest, minimum working example is the following:

import sounddevice as sd
import numpy as np

def print_volume(indata, frames, time, status):
    volume_norm = np.linalg.norm(indata)*10
    print(f'Microphone Volume: {volume_norm:.2f}')

with sd.InputStream(callback=print_volume):
    sd.sleep(10000)
  • I need to get Output Speaker Volume at current time. Basically i need to detect if output speaker is playing some audio or not. I tried various approaches but they did not worked. – Pavan Yogi Jun 27 '23 at 13:19