Difference between revisions of "EBC Exercise 17 Using ALSA for Audio Processing"

From eLinux.org
Jump to: navigation, search
(Added Part b)
Line 137: Line 137:
 
* Either Delete the new ERR() statement, or switch it back to DBG().
 
* Either Delete the new ERR() statement, or switch it back to DBG().
 
We don’t really need this statement, so feel free to remove it. On the other hand, if you want to leave the new debugging statement, we recommend that you, at the very least, change it back to a DBG() statement.
 
We don’t really need this statement, so feel free to remove it. On the other hand, if you want to leave the new debugging statement, we recommend that you, at the very least, change it back to a DBG() statement.
 +
 +
== Part b - Audio Playback ==
 +
 +
The goal of this part is to analyze a recorded file to the driver.
 +
 +
* Copy the AudioThru files from [http://www.rose-hulman.edu/~yoder/Beagle/exercises/ here] to your Beagle.
 +
* Change directories to AudioThru/lab06b_audio_playback
 +
 +
* Use a text editor to examine '''audio_thread.c'''.
 +
Only audio_thread.c has changed from the lab06a_audio_record application. The other files are unchanged. Let’s look at some of the differences:
 +
** The ‘create’ part of the audio_thread_fxn() utilizes to:
 +
*** Use the fopen() function call to open a file for playback.
 +
*** audio_io_setup() and configure the audio output driver.
 +
*** The malloc() function allocates a RAM buffer to store the audio data from the input file before it is written to the audio driver.
 +
** Inside the while() loop:
 +
*** fread() method is used to read audio data from the input file (/tmp/audio.raw)
 +
*** snd_pcm_writei() method is used to write the data to the ALSA driver.
 +
*** When the envPtr->quit variable is set to true, the loop exits. (This occurs when the user presses Ctrl-C in the terminal.)
 +
** Finally, review the “cleanup” phase, which runs right before exiting. (This basically undo’s the steps in the create/setup phase).
 +
 +
=== Build and Run the Application ===
 +
 +
* Build and install the application using make.
 +
<pre>
 +
make debug
 +
</pre>
 +
or
 +
<pre>
 +
make
 +
</pre>
 +
 +
 +
* Make sure that audio.raw was created properly.
 +
Llist the contents of /tmp with the “-lsa” flags setting to verify that /tmp/audio.raw exists and has a greater than zero filesize.
 +
 +
The application is hard coded (using a #define statement in audio_thread.c) to read data from the file /tmp/audio.raw.
 +
 +
----
 +
 +
'''Note:''' If the Beagle is reset or powered-off after running the lab6a_audio_record application, the /tmp/audio.raw file will be erased.
 +
 +
If this happens, run “make” again from the lab06a_audio_record directory to reinstall
 +
the audio recorder. Then run it once in either debug or release mode to re-record
 +
the /tmp/audio.raw file
 +
 +
Finally, you can return to lab06b_audio_playback and run make to re-install the
 +
playback utility.
 +
 +
----
 +
 +
* Execute the ./app_DEBUG.Beagle application.
 +
The application should play back the audio that was recorded in lab06a_audio_record and then exit. If you do not wish to hear all of the audio, press Ctrl-C to exit.
 +
 +
=== Questions about: audio_thread.c ===
 +
 +
# Which C structure (variable type and variable name) is used to set the audio input driver to stereo, 48000 kHz using the ALSA driver?
 +
# Which function call is used in the while() loop to read audio data from the line input via the ALSA driver?
 +
# In the while( ) loop there is an fread() function (similar to fwrite() function, lab06a). For the file read (or write) function, a FILE pointer is the last parameter passed. What is the purpose of the FILE pointer, and where does it come from? (In other words, what function is used to generate valid FILE pointers from which read and write operations can be made?)

Revision as of 14:14, 22 August 2011


This lab has three parts (a, b and c) we inspect the first two parts (recorder and playback) and then stitch the input driver to the output driver to create the loopthru application.

This lab demonstrate the Linux ALSA driver as well as basic file I/O. Parts a and b are inspection labs. While c requires you to combine the previous two parts.

  • Part a analyze the function calls necessary to record audio from line input to a file.
  • Part b examines the function calls necessary to playback audio from a recorded audio file.
  • Part c combines a and b into a single application that loops the audio from input to output (i.e. audio loop-thru) without recording to a file. For an extra challenge, advanced users may want to try to build c without referring to the procedure.

Much of this lab was adapted for the Beagle from OMAP™/DaVinci™ System Integration using Linux Workshop.

Part a - Audio Record

The goal of this part is to analyze the function calls necessary to record audio from the Beagle's a line input to a file. Note: The 1/8 inch audio in jack on the Beagle is a line in jack, not a microphone in. It doesn't not have the extra amplification needed for a microphone.

  • Copy the AudioThru files from here to your Beagle.
  • Change directories to AudioThru/lab06a_audio_record
  • List the files used to build this application:
  1. __________________________________________________________________________
  2. __________________________________________________________________________
  3. __________________________________________________________________________
  4. __________________________________________________________________________

File Inspection

  • Use a text editor to examine the new files in this application.
$ gedit main.c

Other popular editors are emacs and gvim.

main.c

This is the entry point for the application. main() does the following:

  1. Creates a signal handler to trap the Ctrl-C signal (also called SIGINT, the interrupt signal). When this signal is sent to the application, the audioEnv.quit global variable is set to true to signal the audio thread to exit its main loop and begin cleanup.
  2. Calls the audio_thread_fxn() function to enter into the audio function.
  3. Upon completion of this function, the main routine checks – and reports – success or failure returned from the audio function.

audio_thread.c

audio_thread_fxn()

audio_thread_fxn( ) encapsulates the code required to run the audio recorder. The lab06a_audio_recorder application is single-threaded, so the motivation for encapsulation in this manner may not be initially obvious. We will see in later labs – when combining audio and video in a multi-threaded program – why declaring this function (as opposed to running everything from main) is useful.

audio_thread_fxn utilizes the following:

audio_io_setup
Opens and configures the audio input driver. See audio_input_output.c for details.
while()
will execute until the envPtr->quit global variable is set to true.
    • Inside the while() loop, snd_pcm_readi() is used to read data from the audio input driver (ALSA) and fwrite() is used to write the data into a file.
    • When the envPtr->quit variable is set to true (occurs when the user presses Ctrl-C in the terminal) this capture (record) process exits and the application proceeds to the cleanup phase before exiting.
initMask

It goes without saying, writing robust code – and debugging it – can be a tedious chore; it is further exasperated when using printf() statements as the primary means of providing debug information back to the programmer. To this end, we have employed an initMask to help keep track of resources opened (and closed) during the program.

The audio_thread_fxn() uses an initialization mask (initMask) to keep track of how many resources have been opened and initialized. Each bit in the mask corresponds to a resource; the bit positions in the initMask variable are #defined towards the top of the file.

/* The levels of initialization for initMask */
#define ALSA_INITIALIZED 0x1
#define INPUT_BUFFER_ALLOCATED 0x2
#define OUTPUT_FILE_OPENED 0x4
/* Only used to cleanup items that were initialized */
unsigned int initMask = 0x0;
    • When you OR the initMask with a #define'd value, the associated bit will get set in the initMask variable. For example,

InitMask.png

This is useful so that if an error occurs, the application will not attempt to close or free resources that were never opened or allocated. If you look down at the cleanup part of our audio_thread.c, you’ll see how we used the initMask variable to accomplish this.

Build and Run the application

  • Build and install the application using make, i.e. “make all install”.
  • Test your audio connection:
arecord –f cd | aplay –f cd

This command uses the arecord (“ALSA recorder”) utility to capture audio and, instead of sending to a file, uses a Linux process pipe to send the data to the aplay (“ALSA player”) application. This will loop audio through the board. If you have a working audio input and the board is connected to a speaker, you should hear the audio play over the speakers. Press ctrl-c to quit.

On arecord and aplay, the –f option lets you change the format:

Quality # Channels Bits Rate
default mono 8-bit 8 KHZ
cd stereo 16-bit 44.1 KHz
  • Execute the ./app_DEBUG.Beagle application.

The application is hard-coded (using a #define statement in audio_thread.c) to save the audio data to the file /tmp/audio.raw. Execute the application.

  • Press Ctrl-C to exit the application.

After a suitable amount of time press Ctrl-C in the terminal to exit from the application. You can list the /tmp/audio.raw file with the –lsa options to see the size of the file and verify that it has recorded properly:

ls –lsa /tmp/audio.raw

Recall that a signal handler was placed in main.c to trap the SIGINT (Ctrl-C) signal. When Ctrl-C is placed, this signal handler will execute, signaling the audio thread to exit its main loop, proceed to cleanup, and then exit.

  • Use the Linux aplay utility to confirm successful recording.

Because this application saves the audio as a raw stream you may also check that the record has operated properly using the aplay utility.

$ aplay –c 2 –f S16_LE –r 44100 /tmp/audio.raw

DBG vs ERR

Let’s explore the debugging features we’re using in our lab files. We are using two macros defined in the file debug.h. They are DBG() and ERR() – essentially, they are wrapper functions around an fprintf() function.

  • Add a new debug statement to your file.

In main.c, immediately after the signal handler function, add a DBG() statement:

// Set the signal callback for Ctrl-C
pSigPrev = signal( SIGINT, signal_handler );
DBG( "Registered SIGINT signal handler.\n" );
  • Build (using “make all”) and run both debug and release profiles on the

Beagle, comparing their outputs.

Does your new statement show up in the terminal when you execute the program?

    • Debug profile:  Yes  No ___________________________________________
    • Release profile:  Yes  No ___________________________________________
  • Switch from DBG() to ERR(), then once again, build, run and compare both profiles.
    • What is the difference between DBG and ERR? ___________________________________

__________________________________________________________________________

  • Either Delete the new ERR() statement, or switch it back to DBG().

We don’t really need this statement, so feel free to remove it. On the other hand, if you want to leave the new debugging statement, we recommend that you, at the very least, change it back to a DBG() statement.

Part b - Audio Playback

The goal of this part is to analyze a recorded file to the driver.

  • Copy the AudioThru files from here to your Beagle.
  • Change directories to AudioThru/lab06b_audio_playback
  • Use a text editor to examine audio_thread.c.

Only audio_thread.c has changed from the lab06a_audio_record application. The other files are unchanged. Let’s look at some of the differences:

    • The ‘create’ part of the audio_thread_fxn() utilizes to:
      • Use the fopen() function call to open a file for playback.
      • audio_io_setup() and configure the audio output driver.
      • The malloc() function allocates a RAM buffer to store the audio data from the input file before it is written to the audio driver.
    • Inside the while() loop:
      • fread() method is used to read audio data from the input file (/tmp/audio.raw)
      • snd_pcm_writei() method is used to write the data to the ALSA driver.
      • When the envPtr->quit variable is set to true, the loop exits. (This occurs when the user presses Ctrl-C in the terminal.)
    • Finally, review the “cleanup” phase, which runs right before exiting. (This basically undo’s the steps in the create/setup phase).

Build and Run the Application

  • Build and install the application using make.
make debug

or

make


  • Make sure that audio.raw was created properly.

Llist the contents of /tmp with the “-lsa” flags setting to verify that /tmp/audio.raw exists and has a greater than zero filesize.

The application is hard coded (using a #define statement in audio_thread.c) to read data from the file /tmp/audio.raw.


Note: If the Beagle is reset or powered-off after running the lab6a_audio_record application, the /tmp/audio.raw file will be erased.

If this happens, run “make” again from the lab06a_audio_record directory to reinstall the audio recorder. Then run it once in either debug or release mode to re-record the /tmp/audio.raw file

Finally, you can return to lab06b_audio_playback and run make to re-install the playback utility.


  • Execute the ./app_DEBUG.Beagle application.

The application should play back the audio that was recorded in lab06a_audio_record and then exit. If you do not wish to hear all of the audio, press Ctrl-C to exit.

Questions about: audio_thread.c

  1. Which C structure (variable type and variable name) is used to set the audio input driver to stereo, 48000 kHz using the ALSA driver?
  2. Which function call is used in the while() loop to read audio data from the line input via the ALSA driver?
  3. In the while( ) loop there is an fread() function (similar to fwrite() function, lab06a). For the file read (or write) function, a FILE pointer is the last parameter passed. What is the purpose of the FILE pointer, and where does it come from? (In other words, what function is used to generate valid FILE pointers from which read and write operations can be made?)