The Sounds of Raspberry

July 12, 2013

I’ve been talking about using Linux-based systems for embedded use lately. One very popular system is the Raspberry Pi.

I’ve been talking about using Linux-based systems for embedded use lately. One very popular system has been the Raspberry Pi. I wanted to do a small project with the Pi, but was disappointed to see that it only has audio out and no audio in capabilities.

On the other hand, it does have USB ports. I rummaged a bit and found an old Labtec USB mic based on an AK5370 A/D converter. As expected, the Pi found it and suddenly had audio input capabilities.

There are many ways to work with audio hardware under Linux, but one of the best is the PortAudio library. One of the great things about this library is that is very portable to different platforms and it is also easy to use. The portability means you could easily develop your code on a desktop Linux machine and then move it. You could even do some development on a Windows machine or a Mac, although porting the rest of the code might not be as easy.

I have longer term plans, but for now I just wanted to get something working, so I dug through some old code I’d used before with PortAudio to refresh my memory. The library is easy to use. You open each input or output device you want to use as a stream. When you open the stream, you provide a callback function. When PortAudio needs more data for an output device, or has a block of data from an input device, it calls your callback function.

You can find the entire program in the online listings. However, here’s the callback function:

// Audio data comes in through this callback
static int paCallback(const void *in, void *out, unsigned long framesPerBuffer,
		      const PaStreamCallbackTimeInfo *timeinfo,
		      PaStreamCallbackFlags statusFlags,
		      void *userdata)
{
  unsigned i;
  uint32_t *buf=(uint32_t *)in;
  audiostats *stats=(audiostats *)userdata;
  for (i=0;i<framesPerBuffer;i++)
    {
      // update stats
      if (stats->minin>buf[i]) stats->minin=buf[i];
      if (stats->maxin<buf[i]) stats->maxin=buf[i];
      stats->total+=buf[i];
    }
  stats->count+=framesPerBuffer;
  return 0;
}

In this case, the callback just gathers some statistics that the main program will print out. However, eventually you’ll want to do something more interesting (perhaps a fast Fourier transform or a Goertzel on the incoming data). Keep in mind that the callback (at least on some systems) will be time critical, so you don’t want to do too much here. I’m planning on just copying the buffer over and letting another part of the code do the processing.

If you dig into the online listing, you’ll see the setup is pretty much what you’d expect:

  • Initialize the PortAudio library (Pa_Initialize)
  • Query the library about the default input stream (Pa_GetDefaultInputDevice, Pa_GetDeviceInfo)
  • Open the default input stream (Pa_OpenDefaultStream)
  • Start the stream (Pa_StartStream)
  • Wait (Pa_Sleep; although there were other ways to do this)
  • Stop the stream (Pa_StopStream)
  • Close the stream (Pa_CloseStream)
  • Shut the library down (Pa_Terminate)

You can find the documentation for the calls on the PortAudio web site.

This is a great example of how using Linux gives you a lot of leverage. I have no idea how the microphone delivers audio data over USB. I don’t have to deal with raw audio data from a device file. The code is even fairly portable to other operating systems. What’s not to like?

I’ll be looking more at PortAudio and other Linux tools over the next few weeks. Although I’m using the Raspberry Pi, the techniques would apply to any Linux system like a Beagle Bone, or even a small form factor PC running Linux.