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 (Lab3: SD card reader support to load audio files) with the MPLAB® Harmony 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, Lab3: 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.1: APP_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 had 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.2: APP_SDCARD_AUDIO_Tasks()
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 passing the decoded buffer to the audio CODEC for playing. The audio player states also handle track change events and waiting for the current audio buffer completion event from the audio CODEC. You will see these states described in more detail in Step 4.
Figure 4.3: Audio Player State Machine
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 v3.26, MPLAB XC32 Compiler v1.40, MPLAB Harmony v1.08
Because we regularly update our tools, occasionally you may discover an issue while using newer versions. If you suspect that to be the case, we recommend that you double check using the same versions that the project was tested with.
Archived versions of our tools can be found on the following Microchip websites:
MPLAB Harmony (See the Archived Downloads tab)
MPLAB X IDE and XC32 Compiler (See the Downloads Archive tab)
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 perform SD card Audio Player Lab3, please start Lab4 using the Lab3 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 Lab4
Create a new folder audio_player_lab4 under apps/training/middleware/dev/audio_player.
At this point, we suggest you close any open projects from previous Labs. This will avoid confusion since 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/middleware/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 Configurator to open MHC.
Step 2: Add WAV decoder functionality
Under the Harmony Framework Configuration tree, expand Decoder > Decoder > Audio Decoders tree.
- Check the Use Audio Decoder Libraries? box.
- Check the Use WAV AUDIO stream box.
Yes, this is all that is needed in order to add decoder functionality!
Step 3: Generate Harmony code
Generate and save the configuration as shown below :
- Make sure that the Merging Strategy selected is Prompt Merge For All User Changes.
Resolve code generation merge conflicts.
When you click the Generate button, MHC will modify and generate source files based on the options selected in MHC. If MHC generates code that will change a pre-existing file, it will show you the code it generated, and allow you to confirm if you want to add it to your source code. The following screenshots will guide you on which changes need to be merged (i.e., accepted).
Click the Next button (highlighted in blue) to keep the #define statement shown in the following image and move to the next set of merge conflicts.
Click the arrow highlighted in green and merge the code from the generated code to the current file as shown in the following image.
Let’s examine what was done after MHC generated the code:
The app folder contains files related to your specific application. In the following steps, you will add your own code to the existing app.c and app.h files. You will also add/remove other application-specific source files.
The framework folder under the app folder contains customized Harmony Framework files. These files have been generated by MHC in response to your specific MHC selections. As you can see, this lab doesn't add new files to those that existed in the previous lab.
Also, note the framework folder under the Source Files folder contains standard Harmony Framework files that have been added to your project by MHC (based on your MHC selections). To be clear, unlike the framework files found under the app folder, these files have not been modified by MHC. They are some of the standard Harmony Framework files included in the Harmony Framework download.
As you can see, the decoder folder was added. Note also that appropriate implementation only for the WAV decoder is added. For other decoders, the files added (aac.c, adpcm.c, mp3.c, opus_support.c, speex.c and wma.c) are just stubs.
The driver and system folders under the framework folder have remained the same as in the previous lab.
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/middleware/audio_player/audio_player_lab4/dev_files
to this one:
apps/training/middleware/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/middleware/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:
MHC provides Decoder functionality as part of the audio app, universal_audio_decoders. These can be found under this folder:
..\microchip\harmony\v1_08\apps\audio\universal_audio_decoders\firmware\src\audio_decoder
This may change in the future and the decoders may be incorporated as part of the MHC framework, thereby not requiring this step.
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:[pic32mz_ef_sk_meb2] > 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:
..\microchip\harmony\v1_08\apps\audio\universal_audio_decoders\firmware\src\audio_decoder.
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.
An additional step is required here. We must include the app_sdcard_audio_task.h file in app.h. This is required because the decoder file wav.c depends on definitions present in other files and expects app.h to include those files. Including app_sdcard_audio_task.h in app.h will include all the relevant files needed by the decoder.
From apps/training/middleware/dev/audio_player/audio_player_lab4/firmware/src, open app.h and add the line as shown below:
Next, open app_button_press_task.c and change the name of the function from:
APP_TONE_TEXTFILE_SDCARD_SuspendStreaming
to:
APP_SDCARD_AUDIO_SuspendStreaming.
Also, change the name of the function from:
APP_TONE_TEXTFILE_SDCARD_Initialize
to:
APP_SDCARD_AUDIO_Initialize, as shown below.
Finally, we need to add application specific definitions in the system_config.h file. Open system_config.h.
Note you need to change the names of these macro definitions from the previous lab as shown in the following image.
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 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 Lab3, 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 none of the decoded buffer is in use, it queues the decoded buffer to the CODEC driver for playing by 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 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.
System Configuration File: system_config.h
This file defines the application-specific configuration macros.
The macro APP_SDCARD_AUDIO_CODEC_WRITE_QUEUE_SIZE contains the size of the CODEC driver buffer queue.
#define APP_SDCARD_AUDIO_CODEC_WRITE_QUEUE_SIZE QUEUE_SIZE_TX_IDX0
The macro APP_SDCARD_AUDIO_CARD_MAX_DIRS defines the maximum number of folders that will be scanned for the audio files.
#define APP_SDCARD_AUDIO_CARD_MAX_DIRS 20
The macro APP_SDCARD_AUDIO_CARD_MAX_FILES defines the maximum number of files that will be saved.
#define APP_SDCARD_AUDIO_CARD_MAX_FILES 500
The macros DECODER_MAX_INPUT_BUFFER_SIZE and DECODER_MAX_OUTPUT_BUFFER_SIZE define the size of input and output buffers to the decoder respectively.
#define DECODER_MAX_OUTPUT_BUFFER_SIZE 1024 * 12
#define DECODER_MAX_INPUT_BUFFER_SIZE 1024 * 20
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 that 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 Lab3, 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.