Lab4: Play a WAV Audio File from an SD Card with Harmony v2

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 theMPLAB® 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 thePIC32 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 fromAPP_TONE_TEXTFILE_SDCARD_ toAPP_SDCARD_AUDIO_. Otherwise, the basic application flow remains the same.


The application sub-tasks for this lab are shown in this figure.

Fig_3_1_3.png
Figure 4.1:APP_Tasks()

The initial states in theAPP_SDCARD_AUDIO_Tasks() are the same as those found in the previous lab:

  1. Open the CODEC to obtain a handle to the CODEC buffer.
  2. Register a CODEC buffer event handler function (called when the buffer transfer had completed).
  3. Mount the file system for the SD card.
  4. 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.

Fig_3_2.png
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 inStep 4.

Fig_3_3.png
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 theArchived Downloads tab)
MPLAB X IDE and XC32 Compiler (See theDownloads 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 performSD card Audio Player Lab3, please start Lab4 using the Lab3 solution project (found under thefirmware 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

1

Create a new folderaudio_player_lab4 underapps/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.

2

Copy thefirmware folder from
apps/training/middleware/dev/audio_player/audio_player_lab3 to the newly created folder,audio_player_lab4.

3

Rename the project fromaudio_player_lab3 toaudio_player_lab4.

  • Openaudio_player_lab3.
  • UnderProjects, right click onaudio_player_lab3 and selectRename….
Lab2_project_name_change.png
  • In the pop up window, rename the project fromaudio_player_lab3 toaudio_player_lab4.
Lab3_rename.png
  • MPLAB X IDE then renames the project. It closes the old projectaudio_player_lab3 and opens the renamed projectaudio_player_lab4.
Lab3_renamed.png

4

Verify that the project builds properly after renaming. Click theClean and Build Project button,Main_Rebuild_Project.png.

5

ClickTools > Embedded > MPLAB Harmony Configurator to open MHC.

Step 2: Add WAV decoder functionality

Under theHarmony Framework Configuration tree, expandDecoder > Decoder > Audio Decoders tree.

  • Check theUse Audio Decoder Libraries? box.
  • Check theUse WAV AUDIO stream box.
Step1_Configure_the_Decoder.png

Yes, this is all that is needed in order to add decoder functionality!

Step 3: Generate Harmony code

1

Generate and save the configuration as shown below :

  • Make sure that the Merging Strategy selected isPrompt Merge For All User Changes.
Step2_Generate_Harmony_Code_1.png

2

Resolve code generation merge conflicts.

When you click theGenerate 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 theNext button (highlighted in blue) to keep the #define statement shown in the following image and move to the next set of merge conflicts.

Step2_Generate_Harmony_Code_2.png

Click the arrow highlighted in green and merge the code from the generated code to the current file as shown in the following image.

Step2_Generate_Harmony_Code_3.png

Let’s examine what was done after MHC generated the code:

Theapp folder contains files related to your specific application. In the following steps, you will add your own code to the existingapp.c andapp.h files. You will also add/remove other application-specific source files.

Theframework folder under theapp folder containscustomized 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.

Step2_Generate_Harmony_Code_4_1.png

Also, note theframework folder under theSource Files folder containsstandard 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 theapp 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, thedecoder 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.

Thedriver andsystem folders under theframework folder have remained the same as in the previous lab.

Step2_Generate_Harmony_Code_5_1.png

Step 4: Include application-specific source files, add required code and build the project

1

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

Step3_1.png

This snapshot has the audio_player_lab3 application source and header files (app_tone_textfile_sdcard.c andapp_tone_textfile_sdcard.h) already removed from theapps/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.

2

Add the copied source files to your project.

  • Addapp_sdcard_audio_task.c anddecoder.c toSource Files\app by right clicking and selectingAdd Existing Items…
Step3_4_1.png
Step3_5.png
Step3_6.png
  • Addapp_sdcard_audio_task.h anddecoder.h toHeader Files\app by right clicking and selectingAdd Existing Items…
Step3_7_1.png
Step3_8.png
Step3_9.png

3

Removeapp_tone_textfile_sdcard.c andapp_tone_textfile_sdcard.h from the folder.

  • Right click on theapp_tone_textfile_sdcard.h file and selectRemove From Project.
Step3_2_1.png
  • Right click on theapp_tone_textfile_sdcard.c file and selectRemove From Project.
Step3_3_1.png

After performing this step, the files under the project should look like this:

Step3_10_1.png

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.

4

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 nameaudio_player_lab4 in the MPLAB XProjects pane and selectProperties.
  • In theCategories pane, selectConf:[pic32mz_ef_sk_meb2] > XC32 (Global Options) > xc32-gcc.
  • Now, click on theOption categories dropdown menu and select thePreprocessing and Messages option.
Step3_11.png
  • Click on the box besideInclude directories.
Step3_12.png

Browse to the following path:
..\microchip\harmony\v1_08\apps\audio\universal_audio_decoders\firmware\src\audio_decoder.
SelectOK.

Step3_13.png
Step3_14.png

5

As we did with the previous exercise, we will modify theapp.c andapp.h files from the previous exercise in order to change function names to match the function names in the newly includedapp_sdcard_audio_task.c file. Open the fileapp.c and find theAPP_Initialize function. Change the function named:
APP_TONE_TEXTFILE_SDCARD_Initialize()
to:
APP_SDCARD_AUDIO_Initialize() as shown below.

Step3_15.png

6

Find theAPP_Tasks function and change the function named:
APP_TONE_TEXTFILE_SDCARD_Tasks()
to:
APP_SDCARD_AUDIO_Tasks() as shown below.

Step3_16.png

7

An additional step is required here. We must include theapp_sdcard_audio_task.h file inapp.h. This is required because the decoder filewav.c depends on definitions present in other files and expectsapp.h to include those files. Includingapp_sdcard_audio_task.h inapp.h will include all the relevant files needed by the decoder.
Fromapps/training/middleware/dev/audio_player/audio_player_lab4/firmware/src, openapp.h and add the line as shown below:

Step3_17.png

8

Next, openapp_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.

button_task_change.png
button_task_change.png

9

Finally, we need to add application specific definitions in thesystem_config.h file. Opensystem_config.h.

Note you need to change the names of these macro definitions from the previous lab as shown in the following image.

Step3_18.png

10

Save all files and build the code. Click on theClean and Build iconMain_Rebuild_Project.png and verify that the project builds successfully.

Step 5: Review the Application code

Application File:app_sdcard_audio_task.h

1

Open the fileapp_sdcard_audio_task.h. This file defines application states, data, and APIs.

2

Application states corresponding to the state machine described in theOverview section are as follows. (Note the newly added states are indicated by a red box.)

Step3_19.png

3

The audio player states are shown below.

Step3_20.png

All of the these enumerations should be self-explanatory.

4

The application data structure is shown below:

Step3_21.png

The variablesstate andcodec are retained from the previous lab. They hold the state of application and codec related variables respectively.

5

The audio player data structure is shown below.

Step3_22_23.png

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 toSYS_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. Thebuffer members are:
Step3_24.png
  • 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 toDRV_CODEC_BufferAddWrite().
  • bufferSize indicates the number of decoded audio data elements available in thebuffer.
  • currentSongIdx,nextSongIdx andpreviousSongIdx 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 toSYS_FS_FileRead().
  • currentFilesize holds the total size of the current audio file pointed by thefileHandle.
  • readIdx andwrriteIdx are used to index into thebuffer containing audio data for the audio CODEC.writeIdx points to the index until the decoded audio data is available in thebuffer.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 toDECODER_Decode().

6

APP_SDCARD_AUDIO_CARD_EVENT enumerates the events passed to the SD card event handler.

Step3_25.png

The enumAPP_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

7

Open the fileapp_sdcard_audio_task.c. This file contains the application state machine and implements the APIs.

8

The implementation of functionAPP_SDCARD_AUDIO_Initialize sets up the default state ofAPP_SDCARD_AUDIO_Tasks and initializes the variables for the CODEC driver and player state machine along with other variables.

9

The functionAPP_SDCARD_AUDIO_Tasks implements the task’s state machine as shown in the figure inOverview section. As in the previous lab,SD Card Audio Player Lab3, it starts in theAPP_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 theAPP_SDCARD_AUDIO_STATE_CODEC_SET_BUFFER_HANDLER state, mounts the file system in theAPP_SDCARD_AUDIO_CARD_MOUNT state, and sets the current drive in theAPP_SDCARD_AUDIO_CARD_CURRENT_DRIVE_SET state.

10

The application then enters theAPP_SDCARD_AUDIO_STATE_RUNNING state where it calls theAPP_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 theAppSdCardAudioCardFilesTable. It also selects the first WAV file from theAppSdCardAudioCardFilesTable and a call to theAPP_SDCARD_AUDIO_Card_OpenTrack function opens it for playing.

11

The audio player state machine then starts executing. The audio player stateAPP_SDCARD_AUDIO_PLAYER_STATE_RUNNING checks if any buffer is available and, if yes, reads the current audio file by a call to theAPP_SDCARD_AUDIO_Card_FillBuffer function and passes the read data to theAPP_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 toDRV_CODEC_BufferAddWrite function and marks it as in use.
The player state remains in theAPP_SDCARD_AUDIO_PLAYER_STATE_RUNNING state until the entire audio file has been read out.

12

The CODEC driver calls the buffer event handlerAPP_SDCARD_AUDIO_BufferEventHandler (registered with the CODEC by a call to theDRV_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 theDRV_CODEC_BufferAddWrite function.

13

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 toAPP_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 theAppSdCardAudioCardFilesTable, and opens it for playing. The player state machine then changes back toAPP_SDCARD_AUDIO_PLAYER_STATE_RUNNING. This process continues and the audio files are continuously played in a circular manner from theAppSdCardAudioCardFilesTable, one after the other.

14

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.

15

The macroAPP_SDCARD_AUDIO_CODEC_WRITE_QUEUE_SIZE contains the size of the CODEC driver buffer queue.

#defineAPP_SDCARD_AUDIO_CODEC_WRITE_QUEUE_SIZEQUEUE_SIZE_TX_IDX0

16

The macroAPP_SDCARD_AUDIO_CARD_MAX_DIRS defines the maximum number of folders that will be scanned for the audio files.

#defineAPP_SDCARD_AUDIO_CARD_MAX_DIRS20

17

The macroAPP_SDCARD_AUDIO_CARD_MAX_FILES defines the maximum number of files that will be saved.

#defineAPP_SDCARD_AUDIO_CARD_MAX_FILES500

18

The macrosDECODER_MAX_INPUT_BUFFER_SIZE andDECODER_MAX_OUTPUT_BUFFER_SIZE define the size of input and output buffers to the decoder respectively.

#defineDECODER_MAX_OUTPUT_BUFFER_SIZE1024 *12#defineDECODER_MAX_INPUT_BUFFER_SIZE1024 *20

Step 6: Debug your Application

Congratulations! You’re done! You are ready to debugDebug%20Button%20Symbol.png your application!

1

Before we program and run our application, we need to program the micro SD card with the WAV audio files. Go to thedev_files folder of this exercise and copy a few sample WAV files to the micro SD card.

2

Insert the micro SD card into the micro SD card slot (J8) on the MEB II.

3

Before you start the debugger, you may want to set a breakpoint in the application file inapp_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.

4

Put a breakpoint in theAPP_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.

Step3_26.png

5

Debug your application! Click theDebug Main ProjectDebug%20Button%20Symbol.png icon.

6

Single step (by clickingF7) and observe the flow of control in the application state machine.

7

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.

Step3_27.png

8

Remove all break points and clickF5 to allow the application to run.

9

Connect a headphone to the HP Out connector on the MEB II and you should hear the audio track being played.

10

You may load more WAV audio files by switching to the USB SD card reader mode (by pressing the switchS1 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.