This tutorial targets ALSA capture and SoX file writing. In this tutorial we will introduce the bare minimum requirements for ALSA capture and we will write the audio out to file so that we can test it worked. You can use this template to insert your own signal processing algorithms if you like.
Before we start, you have to have compiled and installed gtkIOStream. For certain components of gtkIOStream its compilation and installation isn't necessary, but for certain parts of audio processing it is necessary. Tutorial 0 and contents guides you through how to install and test the installation of gtkIOStream. Before you start this tutorial, you must have it installed first.
Our first step is to open the standard lightweight Raspberry Pi IDE with a blank file for us to code up in ... like so :
Code: Select all
geany ALSACapture.C
Code: Select all
#include <ALSA/ALSA.H>
#include <iostream>
using namespace std;
using namespace ALSA;
#include <Sox.H>
int main(int argc, char *argv[]) {
if (argc<2){
cout<<"Usage:\n"<<argv[0]<<" audioFileName"<<endl;
return -1;
}
const float duration=10.; // time to record in seconds
const string deviceName="hw:0"; // the alsa device to open
Capture capture(deviceName.c_str()); // open the capture device
cout<<"opened the device "<<capture.getDeviceName()<<endl;
cout<<"the device is in the "<<capture.getStateName()<<" state"<<endl;
snd_pcm_format_t format;
capture.getFormat(format);
cout<<"format "<<capture.getFormatName(format)<<endl;
float fs=capture.getSampleRate();
cout<<"sample rate is "<<fs<<" Hz"<<endl;
cout<<"channels "<<capture.getChannels()<<endl;
// Open the audio file for writing
Sox<short int> sox;
int res=sox.openWrite(argv[1], fs, capture.getChannels(), pow(2.,(double)(snd_pcm_format_width(format)-1)));
if (res<0)
return SoxDebug().evaluateError(res);
capture.setParams();
if (!capture.prepared()){
cout<<"should be prepared, but isn't"<<endl;
return -1;
}
snd_pcm_uframes_t pSize;
capture.getPeriodSize(&pSize);
cout<<"period size "<<pSize<<endl;
// Create our temporary buffer which we will read audio into before writing it out from
Eigen::Array<short int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> buffer(pSize,capture.getChannels());
if ((res=capture.start())<0) // start the device capturing
return ALSADebug().evaluateError(res);
int N=(int)floor(duration*fs); // we will stop after roughly duration s
while (N>0){
capture>>buffer; // capture the audio data
// do your signal processing here
sox.write(buffer);
N-=buffer.rows();
}
capture.drop(); // halt the ALSA capture device
sox.closeWrite();
return 0;
}
Code: Select all
int main(int argc, char *argv[]) {
if (argc<2){
cout<<"Usage:\n"<<argv[0]<<" audioFileName"<<endl;
return -1;
}
Next we setup some constants the first constant is the duration to record for (roughly 10 seconds) :
Code: Select all
const float duration=10.;
const string deviceName="hw:0";
Next we have included the ALSA header and the Sox headers, including the iostream header for convenient message printing :
Code: Select all
#include <ALSA/ALSA.H>
#include <iostream>
using namespace std;
using namespace ALSA;
#include <Sox.H>
Now that we have access to the gtkIOStream ALSA classes we open the capture device and print the name to screen and we also find out what state the capture device is in :
Code: Select all
Capture capture(deviceName.c_str()); // open the capture device
cout<<"opened the device "<<capture.getDeviceName()<<endl;
cout<<"the device is in the "<<capture.getStateName()<<" state"<<endl;
We will print out the format, sample rate and channels next :
Code: Select all
snd_pcm_format_t format;
capture.getFormat(format);
cout<<"format "<<capture.getFormatName(format)<<endl;
float fs=capture.getSampleRate();
cout<<"sample rate is "<<fs<<" Hz"<<endl;
cout<<"channels "<<capture.getChannels()<<endl;
We want to open the SoX file, which we have seen in the last tutorial 1 :
Code: Select all
Sox<short int> sox;
int res=sox.openWrite(argv[1], fs, capture.getChannels(), pow(2.,(double)(snd_pcm_format_width(format)-1)));
if (res<0)
return SoxDebug().evaluateError(res);
Before we can start playing we want to know the period size and also we want to put the ALSA device into the prepared state, which we do like so :
Code: Select all
capture.setParams();
if (!capture.prepared()){
cout<<"should be prepared, but isn't"<<endl;
return -1;
}
Great - so now the capture device is open and in the prepared state ... all we need to do is get some memory for audio reading/writing, start the device and loop writing the captured audio to file.
First we print out the period size :
Code: Select all
snd_pcm_uframes_t pSize;
capture.getPeriodSize(&pSize);
cout<<"period size "<<pSize<<endl;
Code: Select all
Eigen::Array<short int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> buffer(pSize,capture.getChannels());
Then we start the ALSA device to capture audio :
Code: Select all
if ((res=capture.start())<0) // start the device capturing
return ALSADebug().evaluateError(res);
Finally we do our capture write loop where first we work out how many frames (samples per channel) we need to grab (N) which is the number of samples per second (fs which is the sample rate in Hz) multiplied by the duration in seconds (SI units right !) :
Code: Select all
int N=(int)floor(duration*fs);
while (N>0){
capture>>buffer; // capture the audio data
// do your signal processing here
sox.write(buffer);
N-=buffer.rows();
}
So you see it is pretty trivial at this point, simply capture to a buffer, write to file or do some signal processing first before writing.
Finally, we drop and stop the ALSA capture device so there are no over runs (a term used for not reading a real time buffer quickly enough and it "over flowing"). We also close the audio file and return 0 on exit.
Code: Select all
capture.drop(); // halt the ALSA capture device
sox.closeWrite();
return 0;
}
We compile it with the following command :
Code: Select all
g++ `pkg-config --cflags --libs gtkIOStream` -o ALSACapture ALSACapture.C
We run like so - capturing a wav file in this instance (it could be almost any other audio file format) and this is what you should see :
Code: Select all
$ ./ALSACapture /tmp/test.wav
opening the device hw:0
opening the device hw:0
func: fillParams
func: setAccess
func: setFormat
func: setChannels
func: setSampleRate
opened the device func: getDeviceName
hw:0
the device is in the func: getStateName
OPEN state
format S16_LE
sample rate is 48000 Hz
channels 2
func: setHWParams
func: getSWParams
func: setSWThreshold
func: setAvailMin
func: setSWParams
period size 256
func: drop
func: running
func: close
func: drop
func: running
PCM::drop can't drop, not running
We test by listening to it using aplay
Code: Select all
aplay /tmp/test.wav
and your playback page should look like this :