Microphone Array
HAL Example
Device Compatibility
Overview
The microphone array interface supports:
- Accepting input from individual microphones
- Accepting input from beamformed microphone
Device Pinouts:
Code Examples
Below are examples of how to interface with the microphone array in MATRIX HAL.
Microphone array function references can be found here.
The command below will compile each example. Be sure to pass in your C++ file and desired output file.
g++ -o YOUR_OUTPUT_FILE YOUR_CPP_FILE -std=c++11 -lmatrix_creator_hal -lgflags
Microphone Array Record to File
The following section shows how to record data from the microphone array to a file. You can download this example here.
Beamformed microphone is channel 8
To convert the .raw
files outputted by this example to playable .wav
files run these commands, replacing 16000
with selected sampling rate.
sudo apt-get install sox alsa-utils sox -r 16000 -c 1 -e signed -c 1 -e signed -b 16 mic_16000_s16le_channel_0.raw channel_0.wav sox -r 16000 -c 1 -e signed -c 1 -e signed -b 16 mic_16000_s16le_channel_1.raw channel_1.wav sox -r 16000 -c 1 -e signed -c 1 -e signed -b 16 mic_16000_s16le_channel_2.raw channel_2.wav sox -r 16000 -c 1 -e signed -c 1 -e signed -b 16 mic_16000_s16le_channel_3.raw channel_3.wav sox -r 16000 -c 1 -e signed -c 1 -e signed -b 16 mic_16000_s16le_channel_4.raw channel_4.wav sox -r 16000 -c 1 -e signed -c 1 -e signed -b 16 mic_16000_s16le_channel_5.raw channel_5.wav sox -r 16000 -c 1 -e signed -c 1 -e signed -b 16 mic_16000_s16le_channel_6.raw channel_6.wav sox -r 16000 -c 1 -e signed -c 1 -e signed -b 16 mic_16000_s16le_channel_7.raw channel_7.wav sox -r 16000 -c 1 -e signed -c 1 -e signed -b 16 mic_16000_s16le_channel_8.raw channel_8.wav
Include Statements
To begin working with the Microphone Array you need to include these header files.
// Google gflags parser #include <gflags/gflags.h> // Communicating with Pi GPIO #include <wiringPi.h> // Input/output stream class to operate on files #include <fstream> // Input/output streams and functions #include <iostream> // Use strings #include <string> // Arrays for math operations #include <valarray> // Communicates with MATRIX device #include "matrix_hal/matrixio_bus.h" // Interfaces with microphone array #include "matrix_hal/microphone_array.h" // Enables using FIR filter with microphone array #include "matrix_hal/microphone_core.h"
Initial Variables
These initial variables are used in the example.
// Defines variables from user arguments using gflags utility // (https://gflags.github.io/gflags/) // Grabs sampling frequency input from user DEFINE_int32(sampling_frequency, 16000, "Sampling Frequency"); // Argument example: "--sampling_frequency 48000" // Grabs duration input from user DEFINE_int32(duration, 5, "Interrupt after N seconds"); // Argument example: "--duration 10" // Grabs gain input from user DEFINE_int32(gain, -1, "Microphone Gain"); // Argument example: "--gain 5"
Initial Setup
You'll then need to setup MatrixIOBus
in order to communicate with the hardware on your MATRIX device. Also, parse command line flags and set user flags as variables.
int main(int argc, char *agrv[]) { // Parse command line flags with gflags utility // (https://gflags.github.io/gflags/) google::ParseCommandLineFlags(&argc, &agrv, true); // Create MatrixIOBus object for hardware communication matrix_hal::MatrixIOBus bus; // Initialize bus and exit program if error occurs if (!bus.Init()) return false; // Set user flags from gflags as variables int sampling_rate = FLAGS_sampling_frequency; int seconds_to_record = FLAGS_duration; int gain = FLAGS_gain;
Main Setup
Now we will create our MicrophoneArray
object and use it to interface with the microphone array.
// The following code is part of main() // Create MicrophoneArray object matrix_hal::MicrophoneArray microphone_array; // Set microphone_array to use MatrixIOBus bus microphone_array.Setup(&bus); // Set microphone sampling rate microphone_array.SetSamplingRate(sampling_rate); // If gain is positive, set the gain if (gain > 0) microphone_array.SetGain(gain); // Log gain_ and sampling_frequency_ variables microphone_array.ShowConfiguration(); // Log recording duration variable std::cout << "Duration : " << seconds_to_record << "s" << std::endl; // Calculate and set up beamforming delays for beamforming microphone_array.CalculateDelays(0, 0, 1000, 320 * 1000); // These are default values
Fir Filter Setup
Now we will create our MicrophoneCore
object and use it to enable the FIR filter.
// The following code is part of main() // Create MicrophoneCore object matrix_hal::MicrophoneCore microphone_core(microphone_array); // Set microphone_core to use MatrixIOBus bus microphone_core.Setup(&bus);
Microphone Input
Now we will read microphone array data, send to a buffer, and write to file.
// The following code is part of main() // Create a buffer array for microphone input int16_t buffer[microphone_array.Channels() + 1] [microphone_array.SamplingRate() + microphone_array.NumberOfSamples()]; // Create an array of streams to write microphone data to files std::ofstream os[microphone_array.Channels() + 1]; // For each microphone channel (+1 for beamforming), make a file and open it for (uint16_t c = 0; c < microphone_array.Channels() + 1; c++) { // Set filename for microphone output std::string filename = "mic_" + std::to_string(microphone_array.SamplingRate()) + "_s16le_channel_" + std::to_string(c) + ".raw"; // Create and open file os[c].open(filename, std::ofstream::binary); } // Counter variable for tracking recording time uint32_t samples = 0; // For recording duration for (int s = 0; s < seconds_to_record; s++) { // Endless loop while (true) { // Read microphone stream data microphone_array.Read(); // For number of samples for (uint32_t s = 0; s < microphone_array.NumberOfSamples(); s++) { // For each microphone for (uint16_t c = 0; c < microphone_array.Channels(); c++) { // Send microphone data to buffer buffer[c][samples] = microphone_array.At(s, c); } // Writes beamformed microphone data into buffer buffer[microphone_array.Channels()][samples] = microphone_array.Beam(s); // Increment samples for buffer write samples++; } // Once number of samples is >= sampling rate if (samples >= microphone_array.SamplingRate()) { // For each microphone channel for (uint16_t c = 0; c < microphone_array.Channels() + 1; c++) { // Write to recording file os[c].write((const char *)buffer[c], samples * sizeof(int16_t)); } // Set samples to zero for loop to fill buffer samples = 0; break; } } } return 0; }
Microphone Array Record to Pipe
The following section shows how to record data from the microphone array to a linux FIFO pipe. You can download this example here.
When beamformed microphone (channel 8) is read from a FIFO pipe distortion may occur.
The following commands copy a modified asound.conf
file into /etc/
, which allows arecord
to record from the pipe.
wget https://github.com/matrix-io/matrix-hal-examples/blob/master/microphone_array/asound.conf sudo mv -f /etc/asound.conf /etc/asound.conf_old sudo mv -f ./asound.conf /etc/
To record from microphone channel 0 for 5 seconds at 16KHz using arecord
, run these commands.
rm -rf /tmp/matrix_micarray_channel_* ./mic_record_pipe --sampling_frequency 16000 & arecord channel0.wav -f S16_LE -r 16000 -d 5 --device=mic_channel0
To stop the example from running, run this command.
killall mic_record_pipe
Include Statements
To begin working with the Microphone Array you need to include these header files.
// Imports FIFO pipe support (https://en.wikipedia.org/wiki/Named_pipe) #include <sys/stat.h> // Linux file control options #include <fcntl.h> // System calls #include <unistd.h> // Google gflags parser #include <gflags/gflags.h> // Communicating with Pi GPIO #include <wiringPi.h> // Input/output stream class to operate on files #include <fstream> // Input/output streams and functions #include <iostream> // Use strings #include <string> // Arrays for math operations #include <valarray> // Communicates with MATRIX device #include "matrix_hal/matrixio_bus.h" // Interfaces with microphone array #include "matrix_hal/microphone_array.h" // Enables using FIR filter with microphone array #include "matrix_hal/microphone_core.h"
Initial Variables
These initial variables are used in the example.
// Defines variables from user arguments using gflags utility // (https://gflags.github.io/gflags/) // Grabs sampling frequency input from user DEFINE_int32(sampling_frequency, 16000, "Sampling Frequency"); // Argument example: "--sampling_frequency 48000" // Grabs gain input from user DEFINE_int32(gain, -1, "Microphone Gain"); // Argument example: "--gain 5"
Initial Setup
You'll then need to setup MatrixIOBus
in order to communicate with the hardware on your MATRIX device. Also, parse command line flags and set user flags as variables.
int main(int argc, char *agrv[]) { // Parse command line flags with gflags utility // (https://gflags.github.io/gflags/) google::ParseCommandLineFlags(&argc, &agrv, true); // Create MatrixIOBus object for hardware communication matrix_hal::MatrixIOBus bus; // Initialize bus and exit program if error occurs if (!bus.Init()) return false; // Set user flags from gflags as variables int sampling_rate = FLAGS_sampling_frequency; int gain = FLAGS_gain;
Main Setup
Now we will create our MicrophoneArray
object and use it to interface with the microphone array.
// The following code is part of main() // Create MicrophoneArray object matrix_hal::MicrophoneArray microphone_array; // Set microphone_array to use MatrixIOBus bus microphone_array.Setup(&bus); // Set microphone sampling rate microphone_array.SetSamplingRate(sampling_rate); // If gain is positive, set the gain if (gain > 0) microphone_array.SetGain(gain); // Log gain_ and sampling_frequency_ variables microphone_array.ShowConfiguration(); // Log recording duration variable std::cout << "Duration : " << seconds_to_record << "s" << std::endl; // Calculate and set up beamforming delays for beamforming microphone_array.CalculateDelays(0, 0, 1000, 320 * 1000); // These are default values
Fir Filter Setup
Now we will create our MicrophoneCore
object and use it to enable the FIR filter.
// The following code is part of main() // Create MicrophoneCore object matrix_hal::MicrophoneCore microphone_core(microphone_array); // Set microphone_core to use MatrixIOBus bus microphone_core.Setup(&bus);
Microphone Input
Now we will read microphone array data, send to a buffer, and write to a FIFO pipe.
// The following code is part of main() // Create a buffer array for microphone input int16_t buffer[microphone_array.Channels() + 1] [microphone_array.SamplingRate() + microphone_array.NumberOfSamples()]; // For each channel plus the beamforming channel for (uint16_t c = 0; c < microphone_array.Channels() + 1; c++) { // Name for the FIFO pipe std::string name = "/tmp/matrix_micarray_channel_" + std::to_string(c); // Create the FIFO pipe if (mkfifo(name.c_str(), 0666) != 0) { // Output error if mkfifo fails std::cerr << "unable to create " << name << " FIFO." << std::endl; } } // For pipe operations int named_pipe_handle; // Endless loop while (true) { // Read microphone stream data microphone_array.Read(); // Bool to flag when beamformed written bool beam_write = false; // For each microphone for (uint16_t c = 0; c < microphone_array.Channels() + 1; c++) { // Open pipe std::string name = "/tmp/matrix_micarray_channel_" + std::to_string(c); named_pipe_handle = open(name.c_str(), O_WRONLY | O_NONBLOCK); // For number of samples for (uint32_t s = 0; s < microphone_array.NumberOfSamples(); s++) { buffer[c][s] = microphone_array.At(s, c); // If beamformed data was not sent to buffer, send it if (!beam_write) { // Send beamformed data to buffer buffer[microphone_array.Channels()][s] = microphone_array.Beam(s); } } // Flag that beamforming data is in buffer beam_write = true; // Write to each pipe write(named_pipe_handle, &buffer[c][0], sizeof(int16_t) * microphone_array.NumberOfSamples()); // Close pipe after write close(named_pipe_handle); } } return 0; }