Purpose
After completing this exercise, you will understand how to configure and add the Audio Decoder Library (for decoding audio files in WAV format) to the project developed in the previous lab (Lab 3: SD card reader support to load audio files) with the MPLAB® Harmony 3 Configurator (MHC). You will also learn how to integrate these modules into an MPLAB Harmony project. This section will really start to show you the power of Harmony!
Overview
In this exercise, we extend the previous lab, Lab 3: SD card reader support to load audio files and add new functionality to it. This application reads a WAV audio file from the SD card on the PIC32 Multimedia Expansion Board (MEB) II and streams it over the PIC32 I²S interface through the audio CODEC. Here, we implement the functionality of an audio player (for WAV files only). The application switches to the next track when the current track playing is completed (assuming that there is more than one track on the SD card). This application also demonstrates the configuration and integration of the additional modules using MHC, thereby extending its functionality.
The naming of functions and variables used in this exercise have been changed from APP_TONE_TEXTFILE_SDCARD_ to APP_SDCARD_AUDIO_. Otherwise, the basic application flow remains the same.
The application sub-tasks for this lab are shown in this figure.
Figure 4.2: APP_SDCARD_AUDIO_Tasks() |
The initial states in the APP_SDCARD_AUDIO_Tasks() are the same as those found in the previous lab:
- Open the CODEC to obtain a handle to the CODEC buffer.
- Register a CODEC buffer event handler function (called when the buffer transfer was completed).
- Mount the file system for the SD card.
- Set the current drive path for the SD card.
You will add a new state for this lab, STATE RUNNING. This state searches all directories on the SD card and saves a list of WAV audio files into a table.
Figure 4.3: Audio Player State Machine |
The audio player state machine then starts to run. The audio player states perform the task of reading the audio data from the opened WAV audio file, decoding the WAV raw data through the decoder, and then passes the decoded buffer to the audio CODEC for playing. The audio player states also handle track change events and waits for the current audio buffer completion event from the audio CODEC. You will see these states described in more detail in Step 4.
Lab Source Files and Solutions:
If you haven't already, download all lab source files and solutions for the SD card audio player labs.
This project has been verified to work with the following versions of software tools:
MPLAB X IDE v5.25, MPLAB XC32 Compiler v2.30, MPLAB Harmony MHC v3.3.2, DEV_PACKS v3.5.0, CORE v3.5.0, CSP v3.5.0, BSP v3.5.0, AUDIO v3.4.0, and USB v3.3.0
As the tools are regularly updated, there may be occasional issues while using newer versions. If that is the case, we recommend using the same version as specified in the project.
The archived versions of our tools can be found below:
MPLAB Harmony
MPLAB X IDE and XC32 Compiler
Note that multiple versions of all these tools can co-exist on the same computer.
Procedure
This lab builds off the work you performed in the previous lab. If you did not complete SD card Audio Player Lab 3, please start Lab 4 using the Lab 3 solution project (found under the firmware folder). Verify it works as expected before continuing with this lab.
All steps must be completed before you will be ready to build, download, and run the application.
Lab Index
Step 1: Copy Source Files and Rename Project for Lab 4
Create a new folder audio_player_lab4 under apps/training/solutions/dev/audio_player.
At this point, we suggest you close any open projects from previous labs. This will avoid confusion as you will be opening the project with the same name as the previous lab before renaming it for this lab.
Copy the firmware folder from
apps/training/solutions/dev/audio_player/audio_player_lab3 to the newly created folder, audio_player_lab4.
Rename the project from audio_player_lab3 to audio_player_lab4.
- Open audio_player_lab3.
- Under Projects, right click on audio_player_lab3 and select Rename….
- In the pop up window, rename the project from audio_player_lab3 to audio_player_lab4.
- MPLAB X IDE then renames the project. It closes the old project audio_player_lab3 and opens the renamed project audio_player_lab4.
Verify that the project builds properly after renaming. Click the
Clean and Build Project button
.
Click Tools > Embedded > MPLAB Harmony 3 Configurator to open MHC.
Step 2: Add WAV Decoder Functionality
Select the MHC Project Graph tab. Remember, if you ever lose the MHC tool, you can find it here: Tools > Embedded > MPLAB Harmony 3 Configurator.
Add the Audio Decoder Libraries into your project from the Available Components window.
Expand the tree Audio> Decoder> Audio Decoder and select Audio Decoder Libraries.
Your Project Graph should now resemble the screen shown below. Keep the decoder's default configuration options.
Click on the Audio Decoder Libraries component box to view the Configuration Options. When you add the Audio Decoder Libraries, by default it enables the WAV and ADPCM decoders. Uncheck the Enable ADPCM Decoder box because this format is not required by the application.
Save the MHC configuration by clicking the save button.
Step 3: Generate Harmony Code
Expand the source file folders in the Projects pane. Notice that these are the files from Lab 3 that you copied to this directory.
Click on the Generate Code button as shown in the following figure. Notice the Merging Strategy option.
Note the new decoder folders added to the lab.
Save all files and build the code. To do this, click on the
Clean and Build icon
and verify the project builds successfully.
At this point, you should be able to debug and step through the application. Effectively, you have a running MPLAB Harmony system. However, it is not yet ready to do anything. Next, you will develop your application state machine logic and make sure the system does what you want it to do. You’re ready to start implementing the application now.
Step 4: Include Application-specific Source Files, Add Required Code, and Build the Project
Copy source files into your project's source files folder:
- app_sdcard_audio_task.c
- app_sdcard_audio_task.h
- decoder.c
- decoder.h
Copy them from this folder:
apps/training/solutions/audio_player/audio_player_lab4/dev_files
to this one:
apps/training/solutions/dev/audio_player/audio_player_lab4/firmware/src
This snapshot has the audio_player_lab3 application source and header files (app_tone_textfile_sdcard.c and app_tone_textfile_sdcard.h) already removed from the apps/training/solutions/dev/audio_player/audio_player_lab4/firmware/src folder to avoid any confusion. You will likely see those files in this folder when you are executing this step.
Add the copied source files to your project.
- Add app_sdcard_audio_task.c and decoder.c to Source Files\app by right clicking and selecting Add Existing Items…
- Add app_sdcard_audio_task.h and decoder.h to Header Files\app by right clicking and selecting Add Existing Items…
Remove app_tone_textfile_sdcard.c and app_tone_textfile_sdcard.h from the folder.
- Right click on the app_tone_textfile_sdcard.h file and select Remove From Project.
- Right click on the app_tone_textfile_sdcard.c file and select Remove From Project.
After performing this step, the files under the project should look like this:
Next, we need to tell the compiler about the path of the decoders and include files.
- To include this path, right click on the project name audio_player_lab4 in the MPLAB X Projects pane and select Properties.
- In the Categories pane, select Conf:[default] > XC32 (Global Options) > xc32-gcc.
- Now, click on the Option categories dropdown menu and select the Preprocessing and Messages option.
- Click on the box beside Include directories.
Browse to the following path:
..firmware\src\config\default\audio\decoder\audio_decoders.
Select OK.
As we did with the previous exercise, we will modify the app.c and app.h files from the previous exercise in order to change function names to match the function names in the newly included app_sdcard_audio_task.c file. Open the file app.c and find the APP_Initialize function. Change the function named:
APP_TONE_TEXTFILE_SDCARD_Initialize()
to:
APP_SDCARD_AUDIO_Initialize() as shown below.
Find the APP_Tasks function and change the function named:
APP_TONE_TEXTFILE_SDCARD_Tasks()
to:
APP_SDCARD_AUDIO_Tasks() as shown below.
At the top of app.c, change the name of the prtotypes as shown below:
Next, open app_button_press_task.c and change the name of the prototype/function from:
APP_TONE_TEXTFILE_SDCARD_SuspendStreaming
to:
APP_SDCARD_AUDIO_SuspendStreaming.
Also, change the name of the prototype/function from:
APP_TONE_TEXTFILE_SDCARD_Initialize
to:
APP_SDCARD_AUDIO_Initialize, as shown below.
Save all files and build the code. Click on the
Clean and Build icon
and verify that the project builds successfully.
Step 5: Review the Application code
Application File: app_sdcard_audio_task.h
Open the file app_sdcard_audio_task.h. This file defines application states, data, and Application Peripheal Interfaces (APIs).
Application states corresponding to the state machine described in the Overview section are as follows. (Note the newly added states are indicated by a red box.)
The audio player states are shown below.
All of the these enumerations should be self-explanatory.
The application data structure is shown below:
The variables state and codec are retained from the previous lab. They hold the state of application and codec related variables respectively.
The audio player data structure is shown below.
The functionality of some of the important structure members are explained below:
- fileHandle: holds "handle" to the audio file currently being played.
- fileStatus: holds the file status for the current audio file, returned by a call to SYS_FS_FileStat().
- state: holds the state of the audio player state machine.
- buffer: holds the buffer related information that is used by the decoder and the audio CODEC. The buffer members are:
- buffer: holds the decoded audio data.
- inUse: a buffer is marked to be in use when the buffer is queued for playing with the CODEC.
- decoded: a buffer is marked as decoded when the buffer contains the decoded audio data (output of decoder).
- writeHandler: holds the buffer handle returned by a call to DRV_CODEC_BufferAddWrite().
- bufferSize: indicates the number of decoded audio data elements available in the buffer.
- currentSongIdx, nextSongIdx, and previousSongIdx: point to index of the current song, next song, and previous song respectively in the global buffer that contains the path information of all the audio files.
- totalAudioFiles: holds the total number of WAV audio files found on the SD card.
- nBytesRead: holds the actual number of bytes read from the file; returned by a call to SYS_FS_FileRead().
- currentFilesize: holds the total size of the current audio file pointed by the fileHandle.
- readIdx and wrriteIdx: are used to index into the buffer containing audio data for the audio CODEC. writeIdx points to the index until the decoded audio data is available in the buffer. readIdx points to the index until the audio data is queued to the audio CODEC.
- decodeDataSize: holds the number of data bytes decoded by the decoder; returned by a call to DECODER_Decode().
APP_SDCARD_AUDIO_CARD_EVENT enumerates the events passed to the SD card event handler.
The enum APP_SDCARD_AUDIO_CARD_EVENT is passed to the SD card event handler, APP_SDCARD_AUDIO_Card_EventHandler(), allowing the application to take various actions based on the SD card events.
Application File: app_sdcard_audio_task.c
Open the file app_sdcard_audio_task.c. This file contains the application state machine and implements the APIs.
The implementation of function APP_SDCARD_AUDIO_Initialize sets up the default state of APP_SDCARD_AUDIO_Tasks and initializes the variables for the CODEC driver and player state machine along with other variables.
The function APP_SDCARD_AUDIO_Tasks implements the task’s state machine as shown in the figure in Overview section. As in the previous lab, SD Card Audio Player Lab 3, it starts in the APP_SDCARD_AUDIO_STATE_CODEC_OPEN state and remains in it until it opens the CODEC driver and gets a valid driver handle. After this, it registers a CODEC buffer handler in the APP_SDCARD_AUDIO_STATE_CODEC_SET_BUFFER_HANDLER state, mounts the file system in the APP_SDCARD_AUDIO_CARD_MOUNT state, and sets the current drive in the APP_SDCARD_AUDIO_CARD_CURRENT_DRIVE_SET state.
The application then enters the APP_SDCARD_AUDIO_STATE_RUNNING state where it calls the APP_SDCARD_AUDIO_Card_Tasks function that traverses through all the directories on the SD card and saves the path of all WAV files found to the AppSdCardAudioCardFilesTable. It also selects the first WAV file from the AppSdCardAudioCardFilesTable and a call to the APP_SDCARD_AUDIO_Card_OpenTrack function opens it for playing.
The audio player state machine then starts executing. The audio player state APP_SDCARD_AUDIO_PLAYER_STATE_RUNNING checks if any buffer is available and, if yes, reads the current audio file by a call to the APP_SDCARD_AUDIO_Card_FillBuffer function and passes the read data to the APP_SDCARD_AUDIO_Player_Decode function, which decodes the WAV formatted data read from the file. Finally, if the decoded buffer is not in use, it queues the decoded buffer to the CODEC driver for playing by sending a call to DRV_CODEC_BufferAddWrite function and marks it as in use.
The player state remains in the APP_SDCARD_AUDIO_PLAYER_STATE_RUNNING state until the entire audio file has been read out.
The CODEC driver calls the buffer event handler APP_SDCARD_AUDIO_BufferEventHandler (registered with the CODEC by a call to the DRV_CODEC_BufferEventHandlerSet function), upon completion of each queued buffer to the audio CODEC. The application checks if a decoded buffer is ready and, if yes, queues the decoded buffer for playing with the audio CODEC by sending a call to the DRV_CODEC_BufferAddWrite function.
Once the audio file is completely read out, or if the decoder output is zero (i.e., there is nothing to decode), the player state is changed to APP_SDCARD_AUDIO_PLAYER_STATE_TRACK_CHANGE, where it first waits for the already queued audio buffers to the CODEC driver to complete; after which it selects the next audio file from the AppSdCardAudioCardFilesTable, and opens it for playing. The player state machine then changes back to APP_SDCARD_AUDIO_PLAYER_STATE_RUNNING. This process continues and the audio files are continuously played in a circular manner from the AppSdCardAudioCardFilesTable, one after the other.
Several other APIs are developed that provide information on the current state of the audio player to other state machines or tasks.
Step 6: Debug your Application
Congratulations! You’re done! You are ready to debug
your application!
Before we program and run our application, we need to program the micro SD card with the WAV audio files. Go to the dev_files folder of this exercise and copy a few sample WAV files to the micro SD card.
Insert the micro SD card into the micro SD card slot (J8) on the MEB II.
Before you start the debugger, you may want to set a breakpoint in the application file in app_sdcard_audio_task.c to verify the important stages of the application (like the opening of the CODEC driver and mounting of the file system on the SD card) were executed successfully.
Put a breakpoint in the APP_SDCARD_AUDIO_PLAYER_STATE_TRACK_CHANGE state which will indicate that an audio track is complete and the next track will be selected for playing.
Debug your application! Click the
Debug Main Project icon.
Single step (by clicking F7) and observe the flow of control in the application state machine.
Put a breakpoint in the buffer event handler. Hitting the breakpoint in the event handler indicates that the last submitted buffer is transferred to the CODEC.
Remove all break points and click F5 to allow the application to run.
Connect a headphone to the HP Out connector on the MEB II and you should hear the audio track being played.
You may load more WAV audio files by switching to the USB SD card reader mode (by pressing the switch S1 on the MEB II). After loading, switch back to the player mode and you should hear the newly added audio tracks to the SD card.
Results
You should be able to hear audio tracks on your headphones connected to the MEB II. If you did not configure the decoder correctly or copied audio files with a file format other than WAV, you may not hear any audio at all or may hear distorted audio.
Analysis
In this exercise, we successfully played an audio track stored in the form of a WAV file on an SD card. We extended the previous lab, SD Card Audio Player Lab 3, by adding audio decoder functionality using MHC. The decoder parsed the WAV files and extracted information including the audio sampling rate, bits per sample, and ultimately provided the application with the raw audio data samples. We also extended the application by adding a separate state machine that traversed through the SD card folder structure and saved the WAV audio files in a table. The application configured a CODEC driver based on the sampling rate information provided by the decoder and passed the audio samples read from the decoder to the CODEC for playing.
Conclusions
With this exercise, we have all the major pieces of a basic audio player glued together. We have the SD card driver and a file system to hold the audio files, decoder functionality to decode audio files of the WAV file format, and a CODEC driver for digital to analog conversion of audio. In effect, we have a full-fledged audio player. As a next step, you may extend the application by adding decoder support for other audio file formats like MP3 or WMA using MHC.