Audio Processing with The QuadMic 4-Microphone Array on the Raspberry Pi

quadmic_w_raspberrypi_2.JPG

“As an Amazon Associates Program member, clicking on links may result in Maker Portal receiving a small commission that helps support future projects.”

The QuadMic Array is a 4-microphone array based around the AC108 quad-channel analog-to-digital converter (ADC) with Inter-IC Sound (I2S) audio output capable of interfacing with the Raspberry Pi. The QuadMic can be used for applications in voice detection and recognition, acoustic localization, noise control, and other applications in audio and acoustic analysis. The QuadMic will be connected to the header of a Raspberry Pi 4 and used to record simultaneous audio data from all four microphones. Some signal processing routines will be developed as part of an acoustic analysis with the four microphones. Algorithms will be introduced that approximate acoustic source directivity, which can help with understanding and characterizing noise sources, room and spatial geometries, and other aspects of acoustic systems. Python is also used for the analysis. Additionally, visualizations will aid in the understanding of the measurements and subsequent analyses conducts in this tutorial.


The QuadMic and Raspberry Pi computer are sed for the analysis, along with an audio frequency generator developed for smartphones. The Raspberry Pi 4 is used to take advantage of the faster processor (1.5GHz Quad-Core Cortex-A72 (ARM v8) 64-bit SoC - Broadcom BCM2711). The parts used specifically for the tutorial are given below with links:

The QuadMic occupies the entire Raspberry Pi header, thus, there is no room for other devices to be connected to the RPi. Additionally, it is important to state that the QuadMic must be wired to all 40 pins and aligned correctly. Misaligning the QuadMic with the RPi’s 40-pin header can result in damage of either or both the QuadMic or/and Raspberry Pi.

 

NOTE: The QuadMic should be attached to the Raspberry Pi -ONLY- when the RPi is powered off


 
quadmic_ac108_annotation.JPG

The QuadMic installation procedure closely follows the one outlined in the Seeed Studio documentation for the “ReSpeaker 4-Mic Array for Raspberry Pi.” And while our tutorial uses the QuadMic instead of the ReSpeaker, both use the AC108 I2S analog-to-digital converter and have nearly identical form factor. Thus, a lot of the use cases are similar, particularly in the library preparation on the Raspberry Pi. Python will exclusively used for the analysis here, whereas the Seeed Studio documentation uses some proprietary software.

First, update and upgrade any Raspberry Pi packages by typing the following into the terminal command line:

pi@raspberrypi:~ $ sudo apt-get update
pi@raspberrypi:~ $ sudo apt-get upgrade

Next, the audio card emulation for the QuadMic needs to be installed by cloning into the Seeed voice card repository for the ReSpeaker:

pi@raspberrypi:~ $ git clone https://github.com/respeaker/seeed-voicecard.git
pi@raspberrypi:~ $ cd seeed-voicecard
pi@raspberrypi:~ $ sudo ./install.sh

The installation will likely 1-2 minutes on the Raspberry Pi 4 (possibly longer on previous models). If any errors arise, make sure to update and upgrade, reboot, and make sure the RPi is connected to the internet - then try again. Finally, reboot the RPi:

pi@raspberrypi:~ $ sudo reboot

Before verifying the QuadMic on the RPi, we need to install the Python pyaudio libraries:

pi@raspberrypi:~ $ sudo apt-get install libportaudio0 libportaudio2 libportaudiocpp0 portaudio19-dev
pi@raspberrypi:~ $ sudo pip3 install pyaudio

Now, the QuadMic can be verified by opening Python and using the ‘pyaudio’ package to list all available audio devices and their properties. The code below uses pyaudio to do this and tells the user whether a QuadMic is detected or not:

##############################################
# QuadMic Device Finder
##############################################
#
import pyaudio
audio = pyaudio.PyAudio() # start pyaudio device
for indx in range(audio.get_device_count()):
    dev = audio.get_device_info_by_index(indx) # get device
    if dev['maxInputChannels']==4 and \
       len([ii for ii in dev['name'].split('-') if ii=='4mic'])>=1:
        print('-'*30)
        print('Found QuadMic!')
        print('Device Index: {}'.format(indx)) # device index
        print('Device Name: {}'.format(dev['name'])) # device name
        print('Device Input Channels: {}'.format(dev['maxInputChannels'])) # channels

Assuming the Seeed voice card library was installed correctly, the above should output the following to the Python shell:

quadmic_device_found.png

QuadMic Device Output After Seeed Voice Card Installation

The QuadMic device finder routine above will be used to specify the index to be read by the audio streaming function in pyaudio. If the above code snippet doesn’t print out information about a device - then it is likely that one or multiple issues are a result:

  • The Raspberry Pi needs to be restarted

  • The Seeed voice card was not properly installed

  • The QuadMic is improperly wired/aligned to the Raspberry Pi

  • The QuadMic is damaged

  • The Raspberry Pi Version and/or operating system is not compatible with the Seeed voice card

Under the assumption that the QuadMic has been found by pyaudio on the Raspberry Pi, the next section will test the four microphones and introduce methods for verifying the functionality of each.


The QuadMic can be tested by taking sample measurements while tapping each microphone. This will allow the user to ensure that each microphone is correlated correctly with the respective measurement and parsing of each data vector. The code below plots all four microphone responses in real time at a sample rate of 16kHz in chunks of 4000 points. This equates to roughly 250ms recordings for all four microphones simultaneously. The code (implemented and tested in Python 3.7 on a Raspberry Pi 4) is given below:

##############################################
# QuadMic Test for all 4-Microphones
# ---- this code plots the time series for all
# ---- four MEMS microphones on the QuadMic
# ---- attached to the Raspberry Pi
#
# -- by Josh Hrisko, Principal Engineer
#       Maker Portal LLC 2021
#
import pyaudio,sys,time
import matplotlib
matplotlib.use('TkAgg')
import numpy as np
import matplotlib.pyplot as plt
#
##############################################
# Finding QuadMic Device 
##############################################
#
def indx_getter():
    quadmic_indx = []
    for indx in range(audio.get_device_count()):
        dev = audio.get_device_info_by_index(indx) # get device
        if dev['maxInputChannels']==4 and \
           len([ii for ii in dev['name'].split('-') if ii=='4mic'])>=1:
            print('-'*30)
            print('Found QuadMic!')
            print('Device Index: {}'.format(indx)) # device index
            print('Device Name: {}'.format(dev['name'])) # device name
            print('Device Input Channels: {}'.format(dev['maxInputChannels'])) # channels
            quadmic_indx = int(indx)
            channels = dev['maxInputChannels']
    if quadmic_indx == []:
        print('No QuadMic Found')
        sys.exit() # exit the script if no QuadMic found
    return quadmic_indx,channels # return index, if found
#
##############################################
# pyaudio Streaming Object
##############################################
#
def audio_dev_formatter():
    stream = audio.open(format=pyaudio_format,rate=samp_rate,
                        channels=chans,input_device_index=quadmic_indx,
                        input=True,frames_per_buffer=CHUNK) # audio stream
    stream.stop_stream() # stop streaming to prevent overloa
    return stream
#
##############################################
# Grabbing Data from Buffer
##############################################
#
def data_grabber():
    stream.start_stream() # start data stream
    channel_data = [[]]*chans # data array
    [stream.read(CHUNK,exception_on_overflow=False) for ii in range(0,1)] # clears buffer
    for frame in range(0,int(np.ceil((samp_rate*record_length)/CHUNK))):
        if frame==0:
            print('Recording Started...')
        # grab data frames from buffer
        stream_data = stream.read(CHUNK,exception_on_overflow=False)
        data = np.frombuffer(stream_data,dtype=buffer_format) # grab data from buffer
        for chan in range(chans): # loop through all channels
            channel_data[chan] = np.append(channel_data[chan],
                                        data[chan::chans]) # separate channels
    print('Recording Stopped')
    return channel_data
#
##############################################
# functions for plotting data
##############################################
#
def plotter():
    ##########################################
    # ---- time series for all mics
    plt.style.use('ggplot') # plot formatting
    fig,ax = plt.subplots(figsize=(12,8)) # create figure
    ax.set_ylabel('Amplitude',fontsize=16) # amplitude label
    ax.set_ylim([-2**15,2**15]) # set 16-bit limits
    fig.canvas.draw() # draw initial plot
    ax_bgnd = fig.canvas.copy_from_bbox(ax.bbox) # get background
    lines = [] # line array for updating
    for chan in range(chans): # loop through channels
        chan_line, = ax.plot(data_chunks[chan],
                label='Microphone {0:1d}'.format(chan+1)) # initial channel plot
        lines.append(chan_line) # channel plot array
    ax.legend(loc='upper center',
              bbox_to_anchor=(0.5,-0.05),ncol=chans) # legend for mic labels
    fig.show() # show plot
    return fig,ax,ax_bgnd,lines

def plot_updater():
    ##########################################
    # ---- time series and full-period FFT
    fig.canvas.restore_region(ax_bgnd) # restore background (for speed)
    for chan in range(chans):
        lines[chan].set_ydata(data_chunks[chan]) # set channel data
        ax.draw_artist(lines[chan]) # draw line
    fig.canvas.blit(ax.bbox) # blitting (for speed)
    fig.canvas.flush_events() # required for blitting
    return lines
#
##############################################
# Main Loop
##############################################
#
if __name__=="__main__":
    #########################
    # Audio Formatting
    #########################
    #
    samp_rate      = 16000 # audio sample rate
    CHUNK          = 4000 # frames per buffer reading
    buffer_format  = np.int16 # 16-bit for buffer
    pyaudio_format = pyaudio.paInt16 # bit depth of audio encoding
    
    audio = pyaudio.PyAudio() # start pyaudio device
    quadmic_indx,chans = indx_getter() # get QuadMic device index and channels
    
    stream = audio_dev_formatter() # audio stream

    record_length = 0.1 # seconds to record
    data_chunks = data_grabber() # grab the data    
    fig,ax,ax_bgnd,lines = plotter() # establish initial plot

    while True:
        data_chunks = data_grabber() # grab the data    
        lines = plot_updater() # update plot with new data

The code above should output a visualization similar to the one given below, which registers the taps on each of the four microphones:

quad_tap_test.gif

One thing to note in the tap test is the peaking of the microphone signal. The microphones peak at 2 raised to the 15th power, both negatively and positively. Anytime the microphone is peaking it can cause damage, and thus, it shouldn’t be done often. Therefore, the tap test should serve as a starting method of ensuring that the channels are being parsed correctly. Going forward, only acoustic inputs will be used to force the microphones into a response.


The QuadMic 4-microphone array was introduced for the Raspberry Pi as a way to record four microphones simultaneously. Using Python, the QuadMic was read and all four microphones were tested and identified. The parsing of the four microphones was done and a real-time plot showed that each microphone corresponded correctly to the hardware label for each MEMS mic. This tutorial was meant as an introduction and getting started routine for setting up and testing the QuadMic for audio processing and acoustic analysis with multiple microphones, using the Raspberry Pi. In the next tutorial in the QuadMic series, a real-world example will be explored where algorithms for determining direction of arrival (DoA) or acoustic localization will be introduced. The sample rate limitations and frequency response of the QuadMic will also be covered in the next tutorial for the QuadMic analyses on the RPi. Use of the onboard RGB LEDs will also be covered, where the direction of arrival can be approximated with one of the 12 LEDs - this is similar to an Amazon Alexa or Echo. The goal of this tutorial series is to introduce the QuadMic as a viable piece of hardware for advanced acoustic analysis on the Raspberry Pi. Examples include: acoustic source directivity, noise reduction, room acoustic characterization, and multi-microphone frequency analysis.

quadmic_zoom_mic3_vertical.JPG
 
Citation for This Page:
 

See More in Raspberry Pi and Acoustics: