diff options
| author | Yuval Adam <yuv.adm@gmail.com> | 2014-06-29 12:34:32 +0300 |
|---|---|---|
| committer | Yuval Adam <yuv.adm@gmail.com> | 2014-06-29 12:34:32 +0300 |
| commit | c3e4c9a25c2910d2d66d52215b3406b13d5b23d5 (patch) | |
| tree | added370d1e356901f8579f076e3263fb0464db7 /boards/ek-lm4f232/usb_host_audio/usb_host_audio.c | |
| parent | 990090a4cc9070837d31e66b58d40f0c3d038741 (diff) | |
Add more board models
Diffstat (limited to 'boards/ek-lm4f232/usb_host_audio/usb_host_audio.c')
| -rw-r--r-- | boards/ek-lm4f232/usb_host_audio/usb_host_audio.c | 1987 |
1 files changed, 1987 insertions, 0 deletions
diff --git a/boards/ek-lm4f232/usb_host_audio/usb_host_audio.c b/boards/ek-lm4f232/usb_host_audio/usb_host_audio.c new file mode 100644 index 0000000..a54ad8c --- /dev/null +++ b/boards/ek-lm4f232/usb_host_audio/usb_host_audio.c @@ -0,0 +1,1987 @@ +//*****************************************************************************
+//
+// usb_host_audio.c - Main routine for the USB host audio example.
+//
+// Copyright (c) 2012-2014 Texas Instruments Incorporated. All rights reserved.
+// Software License Agreement
+//
+// Texas Instruments (TI) is supplying this software for use solely and
+// exclusively on TI's microcontroller products. The software is owned by
+// TI and/or its suppliers, and is protected under applicable copyright
+// laws. You may not combine this software with "viral" open-source
+// software in order to form a larger program.
+//
+// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
+// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
+// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
+// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+// DAMAGES, FOR ANY REASON WHATSOEVER.
+//
+// This is part of revision 2.1.0.12573 of the EK-LM4F232 Firmware Package.
+//
+//*****************************************************************************
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include "inc/hw_memmap.h"
+#include "inc/hw_types.h"
+#include "inc/hw_ints.h"
+#include "driverlib/flash.h"
+#include "driverlib/gpio.h"
+#include "driverlib/interrupt.h"
+#include "driverlib/rom.h"
+#include "driverlib/sysctl.h"
+#include "driverlib/udma.h"
+#include "driverlib/systick.h"
+#include "driverlib/pin_map.h"
+#include "grlib/grlib.h"
+#include "grlib/widget.h"
+#include "grlib/canvas.h"
+#include "grlib/slider.h"
+#include "grlib/listbox.h"
+#include "grlib/pushbutton.h"
+#include "utils/ustdlib.h"
+#include "third_party/fatfs/src/ff.h"
+#include "third_party/fatfs/src/diskio.h"
+#include "drivers/usb_sound.h"
+#include "utils/wavfile.h"
+#include "drivers/cfal96x64x16.h"
+#include "drivers/buttons.h"
+#include "drivers/slidemenuwidget.h"
+#include "usblib/usblib.h"
+
+//*****************************************************************************
+//
+//! \addtogroup example_list
+//! <h1>USB host audio example application using SD Card FAT file system
+//! (usb_host_audio)</h1>
+//!
+//! This example application demonstrates playing .wav files from an SD card
+//! that is formatted with a FAT file system using USB host audio class. The
+//! application will only look in the root directory of the SD card and display
+//! all files that are found. Files can be selected to show their format and
+//! then played if the application determines that they are a valid .wav file.
+//! Only PCM format (uncompressed) files may be played.
+//!
+//! For additional details about FatFs, see the following site:
+//! http://elm-chan.org/fsw/ff/00index_e.html
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Our running system tick counter and a global used to determine the time
+// elapsed since last call to GetTickms().
+//
+//*****************************************************************************
+static uint32_t g_ui32SysTickCount;
+static uint32_t g_ui32LastTick;
+static uint32_t GetTickms(void);
+
+//*****************************************************************************
+//
+// The following are data structures used by FatFs.
+//
+//*****************************************************************************
+static FATFS g_sFatFs;
+static DIR g_sDirObject;
+static FILINFO g_sFileInfo;
+
+//*****************************************************************************
+//
+// A structure that holds a mapping between an FRESULT numerical code,
+// and a string representation. FRESULT codes are returned from the FatFs
+// FAT file system driver.
+//
+//*****************************************************************************
+typedef struct
+{
+ FRESULT fresult;
+ char *pcResultStr;
+}
+tFresultString;
+
+//*****************************************************************************
+//
+// A macro to make it easy to add result codes to the table.
+//
+//*****************************************************************************
+#define FRESULT_ENTRY(f) { (f), (#f) }
+
+//*****************************************************************************
+//
+// A table that holds a mapping between the numerical FRESULT code and
+// it's name as a string. This is used for looking up error codes and
+// providing a human-readable string.
+//
+//*****************************************************************************
+tFresultString g_sFresultStrings[] =
+{
+ FRESULT_ENTRY(FR_OK),
+ FRESULT_ENTRY(FR_DISK_ERR),
+ FRESULT_ENTRY(FR_INT_ERR),
+ FRESULT_ENTRY(FR_NOT_READY),
+ FRESULT_ENTRY(FR_NO_FILE),
+ FRESULT_ENTRY(FR_NO_PATH),
+ FRESULT_ENTRY(FR_INVALID_NAME),
+ FRESULT_ENTRY(FR_DENIED),
+ FRESULT_ENTRY(FR_EXIST),
+ FRESULT_ENTRY(FR_INVALID_OBJECT),
+ FRESULT_ENTRY(FR_WRITE_PROTECTED),
+ FRESULT_ENTRY(FR_INVALID_DRIVE),
+ FRESULT_ENTRY(FR_NOT_ENABLED),
+ FRESULT_ENTRY(FR_NO_FILESYSTEM),
+ FRESULT_ENTRY(FR_MKFS_ABORTED),
+ FRESULT_ENTRY(FR_TIMEOUT),
+ FRESULT_ENTRY(FR_LOCKED),
+ FRESULT_ENTRY(FR_NOT_ENOUGH_CORE),
+ FRESULT_ENTRY(FR_TOO_MANY_OPEN_FILES),
+ FRESULT_ENTRY(FR_INVALID_PARAMETER),
+};
+
+//*****************************************************************************
+//
+// A macro that holds the number of result codes.
+//
+//*****************************************************************************
+#define NUM_FRESULT_CODES (sizeof(g_sFresultStrings) / sizeof(tFresultString))
+
+//*****************************************************************************
+//
+// Error reasons returned by ChangeToDirectory().
+//
+//*****************************************************************************
+#define NAME_TOO_LONG_ERROR 1
+#define OPENDIR_ERROR 2
+
+//*****************************************************************************
+//
+// Define a pair of buffers that are used for holding path information.
+// The buffer size must be large enough to hold the longest expected
+// full path name, including the file name, and a trailing null character.
+// The initial path is set to root "/".
+//
+//*****************************************************************************
+#define PATH_BUF_SIZE 80
+static char g_pcCwdBuf[PATH_BUF_SIZE] = "/";
+static char g_pcTmpBuf[PATH_BUF_SIZE];
+
+//*****************************************************************************
+//
+// A set of string pointers that are used for showing status on the display.
+// Five lines of text are accommodated, which is the reasonable limit for
+// this display.
+//
+//*****************************************************************************
+static char *g_pcStatusLines[5];
+
+//*****************************************************************************
+//
+// A variable to track the current level in the directory tree. The root
+// level is level 0.
+//
+//*****************************************************************************
+static uint32_t g_ui32Level;
+
+//*****************************************************************************
+//
+// A variable to track the current level in the directory tree. The root
+// level is level 0.
+//
+//*****************************************************************************
+static uint32_t g_ui32Level;
+
+//*****************************************************************************
+//
+// Declare a pair of off-screen buffers and associated display structures.
+// These are used by the slide menu widget for animated menu effects.
+//
+//*****************************************************************************
+#define OFFSCREEN_BUF_SIZE GrOffScreen4BPPSize(96, 40)
+static uint8_t g_pui8OffscreenBufA[OFFSCREEN_BUF_SIZE];
+static uint8_t g_pui8OffscreenBufB[OFFSCREEN_BUF_SIZE];
+static tDisplay g_sOffscreenDisplayA;
+static tDisplay g_sOffscreenDisplayB;
+
+//*****************************************************************************
+//
+// Create a palette that is used by the on-screen menus and anything else that
+// uses the (above) off-screen buffers. This palette should contain any
+// colors that are used by any widget using the offscreen buffers. There can
+// be up to 16 colors in this palette.
+//
+//*****************************************************************************
+static uint32_t g_pui32Palette[] =
+{
+ ClrBlack,
+ ClrWhite,
+ ClrDarkBlue,
+ ClrLightBlue,
+ ClrRed,
+ ClrDarkGreen,
+ ClrYellow,
+ ClrBlue
+};
+#define NUM_PALETTE_ENTRIES (sizeof(g_pui32Palette) / sizeof(uint32_t))
+
+//*****************************************************************************
+//
+// The number of SysTick ticks per second.
+//
+//*****************************************************************************
+#define TICKS_PER_SECOND 100
+#define MS_PER_SYSTICK (1000 / TICKS_PER_SECOND)
+
+//*****************************************************************************
+//
+// Audio buffering definitions, these are optimized to deal with USB audio.
+// AUDIO_TRANSFER_SIZE define one frame of audio at 48000 Stereo 16 bit
+// and AUDIO_BUFFERS declares 16 frames(16ms) of audio buffering.
+//
+//*****************************************************************************
+#define AUDIO_TRANSFER_SIZE (192)
+#define AUDIO_BUFFERS (16)
+#define AUDIO_BUFFER_SIZE (AUDIO_TRANSFER_SIZE * AUDIO_BUFFERS)
+uint32_t g_ui32TransferSize;
+uint32_t g_ui32BufferSize;
+
+//*****************************************************************************
+//
+// The main audio buffer and it's pointers.
+//
+//*****************************************************************************
+uint8_t g_pui8AudioBuffer[AUDIO_BUFFER_SIZE];
+uint8_t *g_pui8Read;
+uint8_t *g_pui8Write;
+uint32_t g_ui32ValidBytes;
+
+//*****************************************************************************
+//
+// Holds global flags for the system.
+//
+//*****************************************************************************
+uint32_t g_ui32Flags = 0;
+
+//
+// Currently streaming audio to the USB device.
+//
+#define FLAGS_PLAYING 1
+
+//
+// The last transfer has competed so a new one can be started.
+//
+#define FLAGS_TX_COMPLETE 2
+
+//
+// New audio device present.
+//
+#define FLAGS_DEVICE_CONNECT 3
+
+//
+// New audio device present.
+//
+#define FLAGS_DEVICE_READY 4
+
+//
+// Play Screen is being displayed
+//
+#define FLAGS_PLAY_SCREEN 5
+
+//*****************************************************************************
+//
+// These are the global .wav file states used by the application.
+//
+//*****************************************************************************
+tWavFile g_sWavFile;
+tWavHeader g_sWavHeader;
+
+//*****************************************************************************
+//
+// Define the maximum number of files that can appear at any directory level.
+// This is used for allocating space for holding the file information.
+// Define the maximum depth of subdirectories, also used to allocating space
+// for directory structures.
+// Define the maximum number of characters allowed to be stored for a file
+// name.
+//
+//*****************************************************************************
+#define MAX_FILES_PER_MENU 64
+#define MAX_SUBDIR_DEPTH 32
+#define MAX_FILENAME_STRING_LEN 16
+
+//*****************************************************************************
+//
+// Widget definitions
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Declare a set of menu items and matching strings that are used to hold
+// file information. There are two alternating sets. Two are needed because
+// the file information must be retained for the current directory, and the
+// new directory (up or down the tree).
+//
+//*****************************************************************************
+static char g_pcFileNames[2][MAX_FILES_PER_MENU][MAX_FILENAME_STRING_LEN];
+static tSlideMenuItem g_psFileMenuItems[2][MAX_FILES_PER_MENU];
+
+//*****************************************************************************
+//
+// Declare a set of menus, one for each level of directory.
+//
+//*****************************************************************************
+static tSlideMenu g_psFileMenus[MAX_SUBDIR_DEPTH];
+
+//*****************************************************************************
+//
+// Define the slide menu widget. This is the wigdet that is used for
+// displaying the file information.
+//
+//*****************************************************************************
+SlideMenu(g_sFileMenuWidget, WIDGET_ROOT, 0, 0, &g_sCFAL96x64x16, 0, 12, 96, 40,
+ &g_sOffscreenDisplayA, &g_sOffscreenDisplayB, 16,
+ ClrWhite, ClrRed, ClrBlack, &g_sFontFixed6x8, &g_psFileMenus[0],
+ 0);
+
+//*****************************************************************************
+//
+// The canvas widgets for the wav file information.
+//
+//*****************************************************************************
+extern tCanvasWidget g_sWaveInfoBackground;
+
+char g_pcVolume[16] = "";
+Canvas(g_sWaveInfoVolume, &g_sWaveInfoBackground, 0, 0,
+ &g_sCFAL96x64x16, 0, 42, 96, 10,
+ CANVAS_STYLE_FILL | CANVAS_STYLE_TEXT |
+ CANVAS_STYLE_TEXT_OPAQUE, ClrRed, ClrWhite, ClrWhite, g_psFontFixed6x8,
+ g_pcVolume, 0, 0);
+
+char g_pcTime[16] = "";
+Canvas(g_sWaveInfoTime, &g_sWaveInfoBackground, &g_sWaveInfoVolume, 0,
+ &g_sCFAL96x64x16, 0, 32, 96, 10,
+ CANVAS_STYLE_FILL | CANVAS_STYLE_TEXT |
+ CANVAS_STYLE_TEXT_OPAQUE, ClrRed, ClrWhite, ClrWhite, g_psFontFixed6x8,
+ g_pcTime, 0, 0);
+
+char g_pcFormat[16] = "";
+Canvas(g_sWaveInfoSample, &g_sWaveInfoBackground, &g_sWaveInfoTime, 0,
+ &g_sCFAL96x64x16, 0, 22, 96, 10,
+ CANVAS_STYLE_FILL | CANVAS_STYLE_TEXT |
+ CANVAS_STYLE_TEXT_OPAQUE, ClrRed, ClrWhite, ClrWhite, g_psFontFixed6x8,
+ g_pcFormat, 0, 0);
+
+char g_pcFileName[16] = "";
+Canvas(g_sWaveInfoFileName, &g_sWaveInfoBackground, &g_sWaveInfoSample, 0,
+ &g_sCFAL96x64x16, 0, 12, 96, 10,
+ CANVAS_STYLE_FILL | CANVAS_STYLE_TEXT | CANVAS_STYLE_TEXT_OPAQUE,
+ ClrRed, ClrWhite, ClrWhite, g_psFontFixed6x8,
+ g_pcFileName, 0, 0);
+
+#define INITIAL_VOLUME_PERCENT 20
+uint32_t g_ui32CurrentVolume;
+
+//*****************************************************************************
+//
+// The canvas widget acting as the background for the wav file information.
+//
+//*****************************************************************************
+Canvas(g_sWaveInfoBackground, WIDGET_ROOT, 0,
+ &g_sWaveInfoFileName, &g_sCFAL96x64x16, 0, 12, 96, 40,
+ CANVAS_STYLE_OUTLINE | CANVAS_STYLE_FILL, ClrBlack, ClrWhite, ClrWhite,
+ g_psFontFixed6x8, 0, 0, 0);
+
+//*****************************************************************************
+//
+// The status line.
+//
+//*****************************************************************************
+#define STATUS_TEXT_SIZE 40
+char g_pcStatusText[STATUS_TEXT_SIZE] = "";
+Canvas(g_sStatus, WIDGET_ROOT, 0, 0,
+ &g_sCFAL96x64x16, 0, 64 - 12, 96, 12,
+ (CANVAS_STYLE_FILL | CANVAS_STYLE_OUTLINE | CANVAS_STYLE_TEXT),
+ ClrDarkBlue, ClrWhite, ClrWhite, g_psFontFixed6x8, g_pcStatusText, 0, 0);
+
+//*****************************************************************************
+//
+// The heading containing the application title.
+//
+//*****************************************************************************
+Canvas(g_sHeading, WIDGET_ROOT, 0, 0,
+ &g_sCFAL96x64x16, 0, 0, 96, 12,
+ (CANVAS_STYLE_FILL | CANVAS_STYLE_OUTLINE | CANVAS_STYLE_TEXT),
+ ClrDarkBlue, ClrWhite, ClrWhite, g_psFontFixed6x8, "usb-host-audio", 0, 0);
+
+//*****************************************************************************
+//
+// State information for keep track of time.
+//
+//*****************************************************************************
+static uint32_t g_ui32BytesPlayed;
+static uint32_t g_ui32NextUpdate;
+
+//*****************************************************************************
+//
+// Globals used to track play back position.
+//
+//*****************************************************************************
+static uint16_t g_ui16Minutes;
+static uint16_t g_ui16Seconds;
+
+//*****************************************************************************
+//
+// Fill the audio buffer with data from the open file.
+//
+//*****************************************************************************
+void
+FillAudioBuffer(void)
+{
+ uint32_t ui32Count, ui32Size;
+ uint8_t *pui8Read;
+
+ //
+ // If already full return.
+ //
+ if(g_ui32ValidBytes == g_ui32BufferSize)
+ {
+ return;
+ }
+
+ //
+ // Snap shot the read pointer since it might change while we are filling
+ // the buffer. If the read pointer moved due to an interrupt we will
+ // get the update on the next call to this function.
+ //
+ pui8Read = g_pui8Read;
+
+ //
+ // If write is ahead of read then fill to the end of the buffer if
+ // possible.
+ //
+ if(pui8Read <= g_pui8Write)
+ {
+ //
+ // Calculate the amount of space we have.
+ //
+ ui32Size = g_ui32BufferSize -
+ (uint32_t)(g_pui8Write - g_pui8AudioBuffer);
+ ui32Count = WavRead(&g_sWavFile, g_pui8Write, ui32Size);
+
+ g_pui8Write += ui32Count;
+ g_ui32ValidBytes += ui32Count;
+
+ if(g_pui8Write == (g_pui8AudioBuffer + g_ui32BufferSize))
+ {
+ g_pui8Write = g_pui8AudioBuffer;
+ }
+ }
+
+ //
+ // If read is now ahead of write then fill to the read pointer.
+ //
+ if(pui8Read > g_pui8Write)
+ {
+ //
+ // Calculate the amount of space we have.
+ //
+ ui32Size = (uint32_t)(pui8Read - g_pui8Write);
+ ui32Count = WavRead(&g_sWavFile, g_pui8Write, ui32Size);
+
+ g_pui8Write += ui32Count;
+ g_ui32ValidBytes += ui32Count;
+ }
+}
+
+//*****************************************************************************
+//
+// This function handles the callback from the USB audio device when a buffer
+// has been played or a new buffer has been received.
+//
+//*****************************************************************************
+static void
+USBAudioOutCallback(void *pvBuffer, uint32_t ui32Event)
+{
+ //
+ // If a buffer has been played then schedule a new one to play.
+ //
+ if((ui32Event == USB_EVENT_TX_COMPLETE) &&
+ (HWREGBITW(&g_ui32Flags, FLAGS_PLAYING)))
+ {
+ //
+ // Indicate that a transfer was complete so that the non-interrupt
+ // code can read in more data from the file.
+ //
+ HWREGBITW(&g_ui32Flags, FLAGS_TX_COMPLETE) = 1;
+
+ //
+ // Increment the read pointer.
+ //
+ g_pui8Read += g_ui32TransferSize;
+
+ //
+ // Remove the specified number of bytes from the buffer.
+ //
+ if(g_ui32ValidBytes > g_ui32TransferSize)
+ {
+ g_ui32ValidBytes -= g_ui32TransferSize;
+ }
+ else
+ {
+ g_ui32ValidBytes = 0;
+ }
+
+ //
+ // Wrap the read pointer if necessary.
+ //
+ if(g_pui8Read >= (g_pui8AudioBuffer + g_ui32BufferSize))
+ {
+ g_pui8Read = g_pui8AudioBuffer;
+ }
+
+ //
+ // Increment the number of bytes that have been played.
+ //
+ g_ui32BytesPlayed += g_ui32TransferSize;
+
+ //
+ // Schedule a new USB audio buffer to be transmitted to the USB
+ // audio device.
+ //
+ USBSoundBufferOut(g_pui8Read, g_ui32TransferSize, USBAudioOutCallback);
+ }
+}
+
+//*****************************************************************************
+//
+// This function is used to update the current volume being using for play
+// back.
+//
+//*****************************************************************************
+static void
+DisplayVolume(void)
+{
+ //
+ // Print the volume string in the format Volume dd%
+ //
+ usprintf(g_pcVolume, "Volume %0d%%", g_ui32CurrentVolume);
+
+ //
+ // Display the updated time on the screen.
+ //
+ WidgetPaint((tWidget *)&g_sWaveInfoVolume);
+}
+
+//*****************************************************************************
+//
+// This function is used to tell when to update the play back times for a file.
+// It will only update the screen at 1 second intervals but can be called more
+// often with no result.
+//
+//*****************************************************************************
+static void
+DisplayTime(uint32_t ui32ForceUpdate)
+{
+ uint32_t ui32Seconds;
+ uint32_t ui32Minutes;
+
+ //
+ // Only display on the screen once per second.
+ //
+ if((g_ui32BytesPlayed >= g_ui32NextUpdate) || (ui32ForceUpdate != 0))
+ {
+ //
+ // Set the next update time to one second later.
+ //
+ g_ui32NextUpdate = g_ui32BytesPlayed + g_sWavHeader.ui32AvgByteRate;
+
+ //
+ // Calculate the integer number of minutes and seconds.
+ //
+ ui32Seconds = g_ui32BytesPlayed / g_sWavHeader.ui32AvgByteRate;
+ ui32Minutes = ui32Seconds / 60;
+ ui32Seconds -= ui32Minutes * 60;
+
+ //
+ // Print the time string in the format mm.ss/mm.ss
+ //
+ usprintf(g_pcTime, "%2d:%02d/%d:%02d", ui32Minutes,ui32Seconds,
+ g_ui16Minutes, g_ui16Seconds);
+
+ //
+ // Display the updated time on the screen.
+ //
+ WidgetPaint((tWidget *)&g_sWaveInfoTime);
+ }
+}
+
+//*****************************************************************************
+//
+// This function will handle stopping the play back of audio. It will not do
+// this immediately but will defer stopping audio at a later time. This allows
+// this function to be called from an interrupt handler.
+//
+//*****************************************************************************
+static void
+WaveStop(void)
+{
+ uint32_t ui32Idx;
+ uint32_t *pui32Buffer;
+
+ //
+ // Stop playing audio.
+ //
+ HWREGBITW(&g_ui32Flags, FLAGS_PLAYING) = 0;
+
+ //
+ // Do unsigned long accesses to clear out the buffer.
+ //
+ pui32Buffer = (uint32_t *)g_pui8AudioBuffer;
+
+ //
+ // Zero out the buffer.
+ //
+ for(ui32Idx = 0; ui32Idx < (AUDIO_BUFFER_SIZE >> 2); ui32Idx++)
+ {
+ pui32Buffer[ui32Idx] = 0;
+ }
+
+ //
+ // Reset the number of bytes played and for a time update on the screen.
+ //
+ g_ui32BytesPlayed = 0;
+ DisplayTime(1);
+
+ //
+ // Change the status text on the button to Stopped.
+ //
+ strcpy(g_pcStatusText, "Stopped");
+ WidgetPaint((tWidget *)&g_sStatus);
+}
+
+//*****************************************************************************
+//
+// This is the handler for this SysTick interrupt. FatFs requires a
+// timer tick every 10ms for internal timing purposes.
+//
+//*****************************************************************************
+void
+SysTickHandler(void)
+{
+ //
+ // Increment the system tick count.
+ //
+ g_ui32SysTickCount++;
+
+ //
+ // Call the FatFs tick timer.
+ //
+ disk_timerproc();
+}
+
+//*****************************************************************************
+//
+// This function returns the number of ticks since the last time this function
+// was called.
+//
+//*****************************************************************************
+static uint32_t
+GetTickms(void)
+{
+ uint32_t ui32RetVal;
+ uint32_t ui32Saved;
+
+ ui32RetVal = g_ui32SysTickCount;
+ ui32Saved = ui32RetVal;
+
+ if(ui32Saved > g_ui32LastTick)
+ {
+ ui32RetVal = ui32Saved - g_ui32LastTick;
+ }
+ else
+ {
+ ui32RetVal = g_ui32LastTick - ui32Saved;
+ }
+
+ //
+ // This could miss a few milliseconds but the timings here are on a
+ // much larger scale.
+ //
+ g_ui32LastTick = ui32Saved;
+
+ //
+ // Return the number of milliseconds since the last time this was called.
+ //
+ return(ui32RetVal * MS_PER_SYSTICK);
+}
+
+//*****************************************************************************
+//
+// This function returns a string representation of an error code
+// that was returned from a function call to FatFs. It can be used
+// for printing human readable error messages.
+//
+//*****************************************************************************
+static const char *
+StringFromFresult(FRESULT fresult)
+{
+ uint32_t ui32Idx;
+
+ //
+ // Enter a loop to search the error code table for a matching
+ // error code.
+ //
+ for(ui32Idx = 0; ui32Idx < NUM_FRESULT_CODES; ui32Idx++)
+ {
+ //
+ // If a match is found, then return the string name of the
+ // error code.
+ //
+ if(g_sFresultStrings[ui32Idx].fresult == fresult)
+ {
+ return(g_sFresultStrings[ui32Idx].pcResultStr);
+ }
+ }
+
+ //
+ // At this point no matching code was found, so return a
+ // string indicating unknown error.
+ //
+ return("UNKNOWN ERR");
+}
+
+//*****************************************************************************
+//
+// This function shows a status screen. It draws a banner at the top of the
+// screen with the name of the application, and then up to 5 lines of text
+// in the remaining screen area. The caller specifies a pointer to a set of
+// strings, and the count of number of lines of text (up to 5). This
+// function attempts to vertically center the strings on the display.
+//
+//*****************************************************************************
+static void
+ShowStatusScreen(char *pcStatus[], uint32_t ui32Count)
+{
+ tContext sContext;
+ tRectangle sRect;
+ uint32_t ui32Idx;
+ int32_t i32Y;
+
+ //
+ // Initialize the graphics context.
+ //
+ GrContextInit(&sContext, &g_sCFAL96x64x16);
+
+ //
+ // Fill the rest of the display with black, to clear whatever was there
+ // before.
+ //
+ sRect.i16XMin = 0;
+ sRect.i16XMax = GrContextDpyWidthGet(&sContext) - 1;
+ sRect.i16YMin = 12;
+ sRect.i16YMax = 63 - 12;
+ GrContextForegroundSet(&sContext, ClrBlack);
+ GrRectFill(&sContext, &sRect);
+
+ //
+ // Change foreground for white text.
+ //
+ GrContextForegroundSet(&sContext, ClrWhite);
+
+ //
+ // Cap the max number of status lines to 4
+ //
+ ui32Count = (ui32Count > 4) ? 4 : ui32Count;
+
+ //
+ // Compute the starting Y coordinate based on the number of lines.
+ //
+ i32Y = 36 - (ui32Count * 5);
+
+ //
+ // Display the status lines
+ //
+ GrContextFontSet(&sContext, g_psFontFixed6x8);
+ for(ui32Idx = 0; ui32Idx < ui32Count; ui32Idx++)
+ {
+ GrStringDrawCentered(&sContext, pcStatus[ui32Idx], -1,
+ GrContextDpyWidthGet(&sContext) / 2, i32Y, 0);
+ i32Y += 10;
+ }
+}
+
+//*****************************************************************************
+//
+// Initializes the file system module.
+//
+// \param None.
+//
+// This function initializes the third party FAT implementation.
+//
+// \return Returns \e true on success or \e false on failure.
+//
+//*****************************************************************************
+static bool
+FileInit(void)
+{
+ //
+ // Mount the file system, using logical disk 0.
+ //
+ if(f_mount(0, &g_sFatFs) != FR_OK)
+ {
+ return(false);
+ }
+ return(true);
+}
+
+//*****************************************************************************
+//
+// This function handled global level events for the USB host audio. This
+// function was passed into the USBSoundInit() function.
+//
+//*****************************************************************************
+static void
+AudioEvent(uint32_t ui32Event, uint32_t ui32Param)
+{
+ switch(ui32Event)
+ {
+ case SOUND_EVENT_READY:
+ {
+ //
+ // Flag that a new audio device is present.
+ //
+ HWREGBITW(&g_ui32Flags, FLAGS_DEVICE_CONNECT) = 1;
+
+ //
+ // Change the text to reflect the change.
+ //
+ strcpy(g_pcStatusText, "Ready");
+ WidgetPaint((tWidget *)&g_sStatus);
+
+ break;
+ }
+ case SOUND_EVENT_DISCONNECT:
+ {
+ //
+ // Device is no longer present.
+ //
+ HWREGBITW(&g_ui32Flags, FLAGS_DEVICE_READY) = 0;
+ HWREGBITW(&g_ui32Flags, FLAGS_DEVICE_CONNECT) = 0;
+ HWREGBITW(&g_ui32Flags, FLAGS_PLAYING) = 0;
+ HWREGBITW(&g_ui32Flags, FLAGS_PLAY_SCREEN) = 0;
+
+ //
+ // Change the text to reflect the change.
+ //
+ strcpy(g_pcStatusText, "No Device");
+ WidgetPaint((tWidget *)&g_sStatus);
+
+ //
+ // Display the SD card found message again. This
+ // should replace the slide menu.
+ //
+ g_pcStatusLines[0] = "SD Card Found";
+ ShowStatusScreen(g_pcStatusLines, 1);
+
+ break;
+ }
+ case SOUND_EVENT_UNKNOWN_DEV:
+ {
+ if(ui32Param == 1)
+ {
+ //
+ // Unknown device connected.
+ //
+ strcpy(g_pcStatusText, "Unknown Device");
+ WidgetPaint((tWidget *)&g_sStatus);
+ }
+ else
+ {
+ //
+ // Unknown device disconnected.
+ //
+ strcpy(g_pcStatusText, "No Device");
+ WidgetPaint((tWidget *)&g_sStatus);
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+//*****************************************************************************
+//
+// The error routine that is called if the driver library encounters an error.
+//
+//*****************************************************************************
+#ifdef DEBUG
+void
+__error__(char *pcFilename, uint32_t ui32Line)
+{
+}
+
+#endif
+
+//*****************************************************************************
+//
+// This function is called to read the contents of the current directory from
+// the USB stick and populate a set of menu items, one for each file in the
+// directory. A subdirectory within the directory counts as a file item.
+//
+// This function returns the number of file items that were found, or 0 if
+// there is any error detected.
+//
+//*****************************************************************************
+static uint32_t
+PopulateFileList(uint32_t ui32Level)
+{
+ uint32_t ui32ItemCount;
+ FRESULT fresult;
+
+ //
+ // Open the current directory for access.
+ //
+ fresult = f_opendir(&g_sDirObject, g_pcCwdBuf);
+
+ //
+ // Check for error and return if there is a problem.
+ //
+ if(fresult != FR_OK)
+ {
+ //
+ // Ensure that the error is reported.
+ //
+ g_pcStatusLines[0] = "Error from";
+ g_pcStatusLines[1] = "SD Card";
+ g_pcStatusLines[2] = (char *)StringFromFresult(fresult);
+ ShowStatusScreen(g_pcStatusLines, 3);
+ return(0);
+ }
+
+ //
+ // Initialize the count of files in this directory
+ //
+ ui32ItemCount = 0;
+
+ //
+ // Enter loop to enumerate through all directory entries.
+ //
+ for(;;)
+ {
+ //
+ // Read an entry from the directory.
+ //
+ fresult = f_readdir(&g_sDirObject, &g_sFileInfo);
+
+ //
+ // Check for error and return if there is a problem.
+ //
+ if(fresult != FR_OK)
+ {
+ g_pcStatusLines[0] = "Error from";
+ g_pcStatusLines[1] = "USB disk";
+ g_pcStatusLines[2] = (char *)StringFromFresult(fresult);
+ ShowStatusScreen(g_pcStatusLines, 3);
+ return(0);
+ }
+
+ //
+ // If the file name is blank, then this is the end of the
+ // listing.
+ //
+ if(!g_sFileInfo.fname[0])
+ {
+ break;
+ }
+
+ //
+ // Add the information to a menu item
+ //
+ if(ui32ItemCount < MAX_FILES_PER_MENU)
+ {
+ tSlideMenuItem *psMenuItem;
+
+ //
+ // Get a pointer to the current menu item. Use the directory
+ // level to determine which of the two sets of menu items to use.
+ // (ui32Level & 1]. This lets us alternate between the current
+ // set of menu items and the new set (up or down the tree).
+ //
+ psMenuItem = &g_psFileMenuItems[ui32Level & 1][ui32ItemCount];
+
+ //
+ // Add the file name to the menu item
+ //
+ usnprintf(g_pcFileNames[ui32Level & 1][ui32ItemCount],
+ MAX_FILENAME_STRING_LEN, "%s", g_sFileInfo.fname);
+ psMenuItem->pcText = g_pcFileNames[ui32Level & 1][ui32ItemCount];
+
+ //
+ // If this is a directory, then add the next level menu so that
+ // when displayed it will be showed with a submenu option
+ // (next level down in directory tree). Otherwise it is a file
+ // so clear the child menu so that there is no submenu option
+ // shown.
+ //
+ psMenuItem->psChildMenu = (g_sFileInfo.fattrib & AM_DIR) ?
+ &g_psFileMenus[ui32Level + 1] : 0;
+
+ //
+ // Move to the next entry in the item array we use to populate the
+ // list box.
+ //
+ ui32ItemCount++;
+ }
+ }
+
+ //
+ // Made it to here, return the count of files that were populated.
+ //
+ return(ui32ItemCount);
+}
+
+//*****************************************************************************
+//
+// This function is used to change to a new directory in the file system.
+// It takes a parameter that specifies the directory to make the current
+// working directory.
+// Path separators must use a forward slash "/". The directory parameter
+// can be one of the following:
+// * root ("/")
+// * a fully specified path ("/my/path/to/mydir")
+// * a single directory name that is in the current directory ("mydir")
+// * parent directory ("..")
+//
+// It does not understand relative paths, so dont try something like this:
+// ("../my/new/path")
+//
+// Once the new directory is specified, it attempts to open the directory
+// to make sure it exists. If the new path is opened successfully, then
+// the current working directory (cwd) is changed to the new path.
+//
+// In cases of error, the pui32Reason parameter will be written with one of
+// the following values:
+//
+// NAME_TOO_LONG_ERROR - combination of paths are too long for the buffer
+// OPENDIR_ERROR - there is some problem opening the new directory
+//
+//*****************************************************************************
+static FRESULT
+ChangeToDirectory(char *pcDirectory, uint32_t *pui32Reason)
+{
+ uint32_t ui32Idx;
+ FRESULT fresult;
+
+ //
+ // Copy the current working path into a temporary buffer so
+ // it can be manipulated.
+ //
+ strcpy(g_pcTmpBuf, g_pcCwdBuf);
+
+ //
+ // If the first character is /, then this is a fully specified
+ // path, and it should just be used as-is.
+ //
+ if(pcDirectory[0] == '/')
+ {
+ //
+ // Make sure the new path is not bigger than the cwd buffer.
+ //
+ if(strlen(pcDirectory) + 1 > sizeof(g_pcCwdBuf))
+ {
+ *pui32Reason = NAME_TOO_LONG_ERROR;
+ return(FR_OK);
+ }
+
+ //
+ // If the new path name (in argv[1]) is not too long, then
+ // copy it into the temporary buffer so it can be checked.
+ //
+ else
+ {
+ strncpy(g_pcTmpBuf, pcDirectory, sizeof(g_pcTmpBuf));
+ }
+ }
+
+ //
+ // If the argument is .. then attempt to remove the lowest level
+ // on the CWD.
+ //
+ else if(!strcmp(pcDirectory, ".."))
+ {
+ //
+ // Get the index to the last character in the current path.
+ //
+ ui32Idx = strlen(g_pcTmpBuf) - 1;
+
+ //
+ // Back up from the end of the path name until a separator (/)
+ // is found, or until we bump up to the start of the path.
+ //
+ while((g_pcTmpBuf[ui32Idx] != '/') && (ui32Idx > 1))
+ {
+ //
+ // Back up one character.
+ //
+ ui32Idx--;
+ }
+
+ //
+ // Now we are either at the lowest level separator in the
+ // current path, or at the beginning of the string (root).
+ // So set the new end of string here, effectively removing
+ // that last part of the path.
+ //
+ g_pcTmpBuf[ui32Idx] = 0;
+ }
+
+ //
+ // Otherwise this is just a normal path name from the current
+ // directory, and it needs to be appended to the current path.
+ //
+ else
+ {
+ //
+ // Test to make sure that when the new additional path is
+ // added on to the current path, there is room in the buffer
+ // for the full new path. It needs to include a new separator,
+ // and a trailing null character.
+ //
+ if(strlen(g_pcTmpBuf) + strlen(pcDirectory) + 1 + 1 > sizeof(g_pcCwdBuf))
+ {
+ *pui32Reason = NAME_TOO_LONG_ERROR;
+ return(FR_INVALID_OBJECT);
+ }
+
+ //
+ // The new path is okay, so add the separator and then append
+ // the new directory to the path.
+ //
+ else
+ {
+ //
+ // If not already at the root level, then append a /
+ //
+ if(strcmp(g_pcTmpBuf, "/"))
+ {
+ strcat(g_pcTmpBuf, "/");
+ }
+
+ //
+ // Append the new directory to the path.
+ //
+ strcat(g_pcTmpBuf, pcDirectory);
+ }
+ }
+
+ //
+ // At this point, a candidate new directory path is in g_pcTmpBuf.
+ // Try to open it to make sure it is valid.
+ //
+ fresult = f_opendir(&g_sDirObject, g_pcTmpBuf);
+
+ //
+ // If it can't be opened, then it is a bad path. Return an error.
+ //
+ if(fresult != FR_OK)
+ {
+ *pui32Reason = OPENDIR_ERROR;
+ return(fresult);
+ }
+
+ //
+ // Otherwise, it is a valid new path, so copy it into the CWD.
+ //
+ else
+ {
+ strncpy(g_pcCwdBuf, g_pcTmpBuf, sizeof(g_pcCwdBuf));
+ }
+
+ //
+ // Return success.
+ //
+ return(FR_OK);
+}
+
+//*****************************************************************************
+//
+// Sends a button/key press message to the slide menu widget that is showing
+// files.
+//
+//*****************************************************************************
+static void
+SendWidgetKeyMessage(uint32_t ui32Msg)
+{
+ WidgetMessageQueueAdd(WIDGET_ROOT,
+ ui32Msg, (uint32_t)&g_sFileMenuWidget, 0, 1, 1);
+}
+
+//*****************************************************************************
+//
+// This function performs actions that are common whenever the directory
+// level is changed up or down. It populates the correct menu structure with
+// the list of files in the directory.
+//
+//*****************************************************************************
+static bool
+ProcessDirChange(char *pcDir, uint32_t ui32Level)
+{
+ FRESULT fresult;
+ uint32_t ui32Reason;
+ uint32_t ui32FileCount;
+
+ //
+ // Attempt to change to the new directory.
+ //
+ fresult = ChangeToDirectory(pcDir, &ui32Reason);
+
+ //
+ // If the directory change was successful, populate the
+ // list of files for the new subdirectory.
+ //
+ if((fresult == FR_OK) && (ui32Level < MAX_SUBDIR_DEPTH))
+ {
+ //
+ // Get a pointer to the current menu for this CWD.
+ //
+ tSlideMenu *psMenu = &g_psFileMenus[ui32Level];
+
+ //
+ // Populate the menu items with the file list for the new CWD.
+ //
+ ui32FileCount = PopulateFileList(ui32Level);
+
+ //
+ // Initialize the file menu with the list of menu items,
+ // which are just files and dirs in the root directory
+ //
+ psMenu->psSlideMenuItems = g_psFileMenuItems[ui32Level & 1];
+ psMenu->ui32Items = ui32FileCount;
+
+ //
+ // Set the parent directory, if there is one. If at level 0
+ // (CWD is root), then there is no parent directory.
+ //
+ if(ui32Level)
+ {
+ psMenu->psParent = &g_psFileMenus[ui32Level - 1];
+ }
+ else
+ {
+ psMenu->psParent = 0;
+ }
+
+ //
+ // If we are descending into a new subdir, then initialize the other
+ // menu item fields to default values.
+ //
+ if(ui32Level > g_ui32Level)
+ {
+ psMenu->ui32CenterIndex = 0;
+ psMenu->ui32FocusIndex = 0;
+ psMenu->bMultiSelectable = 0;
+ }
+
+ //
+ // Return a success indication
+ //
+ return(true);
+ }
+
+ //
+ // Directory change was not successful
+ //
+ else
+ {
+ //
+ // Return failure indication
+ //
+ return(false);
+ }
+}
+
+//*****************************************************************************
+//
+// The program main function. It performs initialization, then handles wav
+// file playback.
+//
+//*****************************************************************************
+int
+main(void)
+{
+ uint32_t ui32Temp;
+ uint32_t ui32Retcode;
+
+ //
+ // Enable lazy stacking for interrupt handlers. This allows floating-point
+ // instructions to be used within interrupt handlers, but at the expense of
+ // extra stack usage.
+ //
+ ROM_FPULazyStackingEnable();
+
+ //
+ // Set the system clock to run at 50MHz from the PLL.
+ //
+ ROM_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
+ SYSCTL_XTAL_16MHZ);
+
+ //
+ // Configure the required pins for USB operation.
+ //
+ ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
+ ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);
+ ROM_GPIOPinConfigure(GPIO_PG4_USB0EPEN);
+ ROM_GPIOPinTypeUSBDigital(GPIO_PORTG_BASE, GPIO_PIN_4);
+ ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOL);
+ ROM_GPIOPinTypeUSBAnalog(GPIO_PORTL_BASE, GPIO_PIN_6 | GPIO_PIN_7);
+ ROM_GPIOPinTypeUSBAnalog(GPIO_PORTB_BASE, GPIO_PIN_0 | GPIO_PIN_1);
+
+ //
+ // Configure SysTick for a 100Hz interrupt.
+ //
+ ROM_SysTickPeriodSet(SysCtlClockGet() / TICKS_PER_SECOND);
+ ROM_SysTickEnable();
+ ROM_SysTickIntEnable();
+
+ //
+ // Enable Interrupts
+ //
+ ROM_IntMasterEnable();
+
+ //
+ // Initialize the display driver.
+ //
+ CFAL96x64x16Init();
+
+ //
+ // Initialize the buttons driver.
+ //
+ ButtonsInit();
+
+ //
+ // Initialize two offscreen displays and assign the palette. These
+ // buffers are used by the slide menu widget to allow animation effects.
+ //
+ GrOffScreen4BPPInit(&g_sOffscreenDisplayA, g_pui8OffscreenBufA, 96, 40);
+ GrOffScreen4BPPPaletteSet(&g_sOffscreenDisplayA, g_pui32Palette, 0,
+ NUM_PALETTE_ENTRIES);
+ GrOffScreen4BPPInit(&g_sOffscreenDisplayB, g_pui8OffscreenBufB, 96, 40);
+ GrOffScreen4BPPPaletteSet(&g_sOffscreenDisplayB, g_pui32Palette, 0,
+ NUM_PALETTE_ENTRIES);
+
+ //
+ // Add the compile-time defined widgets to the widget tree.
+ //
+ WidgetAdd(WIDGET_ROOT, (tWidget *)&g_sHeading);
+ WidgetAdd(WIDGET_ROOT, (tWidget *)&g_sStatus);
+ WidgetAdd(WIDGET_ROOT, (tWidget *)&g_sFileMenuWidget);
+
+ //
+ // Initialize the status text.
+ //
+ strcpy(g_pcStatusText, "No Device");
+
+ //
+ // Issue the initial paint request to the widgets then immediately call
+ // the widget manager to process the paint message. This ensures that the
+ // display is drawn as quickly as possible and saves the delay we would
+ // otherwise experience if we processed the paint message after mounting
+ // and reading the SD card.
+ //
+ WidgetPaint(WIDGET_ROOT);
+ WidgetMessageQueueProcess();
+
+ //
+ // Determine whether or not an SD Card is installed. If not, print a
+ // warning and have the user install one and restart.
+ //
+ ui32Retcode = disk_initialize(0);
+
+ if(ui32Retcode != RES_OK)
+ {
+ g_pcStatusLines[0] = "No SD Card Found";
+ g_pcStatusLines[1] = "Please insert";
+ g_pcStatusLines[2] = "a card and";
+ g_pcStatusLines[3] = "reset the board.";
+ ShowStatusScreen(g_pcStatusLines, 4);
+ return (1);
+ }
+ else
+ {
+ g_pcStatusLines[0] = "SD Card Found";
+ ShowStatusScreen(g_pcStatusLines, 1);
+
+ //
+ // Mount the file system, using logical disk 0.
+ //
+ f_mount(0, &g_sFatFs);
+ if(!FileInit())
+ {
+ return(1);
+ }
+ }
+
+ //
+ // Not playing anything right now.
+ //
+ g_ui32Flags = 0;
+ g_ui32SysTickCount = 0;
+ g_ui32LastTick = 0;
+ g_ui32CurrentVolume = INITIAL_VOLUME_PERCENT;
+
+ //
+ // Configure the USB host output.
+ //
+ USBSoundInit(0, AudioEvent);
+
+ //
+ // Enter an (almost) infinite loop for reading and processing commands from
+ // the user.
+ //
+ while(1)
+ {
+ uint32_t ui32LastTickCount = 0;
+
+ //
+ // On connect change the device state to ready.
+ //
+ if(HWREGBITW(&g_ui32Flags, FLAGS_DEVICE_CONNECT))
+ {
+ HWREGBITW(&g_ui32Flags, FLAGS_DEVICE_CONNECT) = 0;
+
+ //
+ // Getting here means the device is ready.
+ // Reset the CWD to the root directory.
+ //
+ g_pcCwdBuf[0] = '/';
+ g_pcCwdBuf[1] = 0;
+
+ //
+ // Set the initial directory level to the root
+ //
+ g_ui32Level = 0;
+
+ //
+ // We need to reset the indexes of the root menu to 0, so that
+ // it will start at the top of the file list, and reset the
+ // slide menu widget to start with the root menu.
+ //
+ g_psFileMenus[g_ui32Level].ui32CenterIndex = 0;
+ g_psFileMenus[g_ui32Level].ui32FocusIndex = 0;
+ SlideMenuMenuSet(&g_sFileMenuWidget, &g_psFileMenus[g_ui32Level]);
+
+ //
+ // Initiate a directory change to the root. This will
+ // populate a menu structure representing the root directory.
+ //
+ if(ProcessDirChange("/", g_ui32Level))
+ {
+ //
+ // Request a repaint so the file menu will be shown
+ //
+ WidgetPaint(WIDGET_ROOT);
+ }
+ else
+ {
+ g_pcStatusLines[0] = "ERROR";
+ g_pcStatusLines[1] = "Unable to change";
+ g_pcStatusLines[2] = "directory.";
+ ShowStatusScreen(g_pcStatusLines, 3);
+ return(1);
+ }
+
+ //
+ // Attempt to set the audio format to 44100 16 bit stereo by
+ // default otherwise try 48000 16 bit stereo.
+ //
+ if(USBSoundOutputFormatSet(44100, 16, 2) == 0)
+ {
+ ui32Temp = 44100;
+ }
+ else if(USBSoundOutputFormatSet(48000, 16, 2) == 0)
+ {
+ ui32Temp = 48000;
+ }
+ else
+ {
+ ui32Temp = 0;
+ }
+
+ //
+ // If the audio device was support put the sample rate in the
+ // status line.
+ //
+ if(ui32Temp != 0)
+ {
+ //
+ // Calculate the number of bytes per USB frame.
+ //
+ g_ui32TransferSize = (ui32Temp * 4) / 1000;
+
+ //
+ // Calculate the size of the audio buffer.
+ //
+ g_ui32BufferSize = AUDIO_BUFFERS * g_ui32TransferSize;
+
+ //
+ // Print the time string in the format mm.ss/mm.ss
+ //
+
+ if(ui32Temp == 44100)
+ {
+ usprintf(g_pcStatusText, "44.1 kHz Ready");
+ }
+ else if(ui32Temp == 48000)
+ {
+ usprintf(g_pcStatusText, "48 kHz Ready");
+ }
+
+ HWREGBITW(&g_ui32Flags, FLAGS_DEVICE_READY) = 1;
+ }
+ else
+ {
+ strcpy(g_pcStatusText, "Not Supported");
+ return (1);
+ }
+
+ //
+ // Set initial volume.
+ //
+ USBSoundVolumeSet(g_ui32CurrentVolume);
+
+ //
+ // Update the status line.
+ //
+ WidgetPaint((tWidget *)&g_sStatus);
+ }
+
+ //
+ // Process occurrence of timer tick. Check for user input
+ // once each tick.
+ //
+ if((g_ui32SysTickCount != ui32LastTickCount) &&
+ HWREGBITW(&g_ui32Flags, FLAGS_DEVICE_READY) &&
+ (HWREGBITW(&g_ui32Flags, FLAGS_PLAY_SCREEN) == 0))
+ {
+ uint8_t ui8ButtonState;
+ uint8_t ui8ButtonChanged;
+
+ ui32LastTickCount = g_ui32SysTickCount;
+
+ //
+ // Get the current debounced state of the buttons.
+ //
+ ui8ButtonState = ButtonsPoll(&ui8ButtonChanged, 0);
+
+ //
+ // If select button or right button is pressed, then we
+ // are trying to descend into another directory
+ //
+ if(BUTTON_PRESSED(SELECT_BUTTON,
+ ui8ButtonState, ui8ButtonChanged) ||
+ BUTTON_PRESSED(RIGHT_BUTTON,
+ ui8ButtonState, ui8ButtonChanged))
+ {
+ uint32_t ui32NewLevel;
+ uint32_t ui32ItemIdx;
+ char *pcItemName;
+
+ //
+ // Get a pointer to the current menu for this CWD.
+ //
+ tSlideMenu *psMenu = &g_psFileMenus[g_ui32Level];
+
+ //
+ // Get the highlighted index in the current file list.
+ // This is the currently highlighted file or dir
+ // on the display. Then get the name of the file at
+ // this index.
+ //
+ ui32ItemIdx = SlideMenuFocusItemGet(psMenu);
+ pcItemName = psMenu->psSlideMenuItems[ui32ItemIdx].pcText;
+
+ //
+ // Make sure we are not yet past the maximum tree
+ // depth.
+ //
+ if(g_ui32Level < MAX_SUBDIR_DEPTH)
+ {
+ //
+ // Potential new level is one greater than the
+ // current level.
+ //
+ ui32NewLevel = g_ui32Level + 1;
+
+ //
+ // Process the directory change to the new
+ // directory. This function will populate a menu
+ // structure with the files and subdirs in the new
+ // directory.
+ //
+ if(ProcessDirChange(pcItemName, ui32NewLevel))
+ {
+ //
+ // If the change was successful, then update
+ // the level.
+ //
+ g_ui32Level = ui32NewLevel;
+
+ //
+ // Now that all the prep is done, send the
+ // KEY_RIGHT message to the widget and it will
+ // "slide" from the previous file list to the
+ // new file list of the CWD.
+ //
+ SendWidgetKeyMessage(WIDGET_MSG_KEY_RIGHT);
+
+ }
+
+ //
+ // We have selected a file to play. Display
+ // the file information and if it is a valid
+ // wav file, allow playback.
+ //
+ else
+ {
+
+ //
+ // Update the file name information.
+ //
+ strncpy(g_pcFileName, pcItemName, 16);
+
+ if(WavOpen(g_pcFileName, &g_sWavFile) == 0)
+ {
+ //
+ // Read the .wav file format.
+ //
+ WavGetFormat(&g_sWavFile, &g_sWavHeader);
+
+ //
+ // Print the formatted string so that it can be
+ // displayed.
+ //
+ usprintf(g_pcFormat, "%d Hz %d bit ",
+ g_sWavHeader.ui32SampleRate / 1000,
+ g_sWavHeader.ui16BitsPerSample);
+
+ //
+ // Concatenate the number of channels.
+ //
+ if(g_sWavHeader.ui16NumChannels == 1)
+ {
+ strcat(g_pcFormat, "Mo");
+ }
+ else
+ {
+ strcat(g_pcFormat, "St");
+ }
+
+ //
+ // Calculate the minutes and seconds in the file.
+ //
+ g_ui16Seconds = g_sWavHeader.ui32DataSize /
+ g_sWavHeader.ui32AvgByteRate;
+ g_ui16Minutes = g_ui16Seconds / 60;
+ g_ui16Seconds -= g_ui16Minutes * 60;
+
+ //
+ // Close the file, it will be re-opened on play.
+ //
+ WavClose(&g_sWavFile);
+
+ //
+ // Update the file time information.
+ //
+ DisplayTime(1);
+
+ //
+ // Update the volume information
+ //
+ DisplayVolume();
+ }
+ else
+ {
+ //
+ // Set the time and volume strings to null strings.
+ //
+ g_pcTime[0] = 0;
+ g_pcVolume[0] = 0;
+
+ //
+ // Print message about invalid wav format
+ //
+ usprintf(g_pcFormat, "Invalid Wav");
+ }
+
+ //
+ // Update the file name line.
+ //
+ WidgetPaint((tWidget *)&g_sWaveInfoBackground);
+
+ //
+ // Set a flag to change the button functions.
+ //
+ HWREGBITW(&g_ui32Flags, FLAGS_PLAY_SCREEN) = 1;
+ }
+ }
+ }
+
+ //
+ // If the UP button is pressed, just pass it to the widget
+ // which will handle scrolling the list of files.
+ //
+ if(BUTTON_PRESSED(UP_BUTTON, ui8ButtonState, ui8ButtonChanged))
+ {
+ SendWidgetKeyMessage(WIDGET_MSG_KEY_UP);
+ }
+
+ //
+ // If the DOWN button is pressed, just pass it to the widget
+ // which will handle scrolling the list of files.
+ //
+ if(BUTTON_PRESSED(DOWN_BUTTON, ui8ButtonState, ui8ButtonChanged))
+ {
+ SendWidgetKeyMessage(WIDGET_MSG_KEY_DOWN);
+ }
+
+ //
+ // If the LEFT button is pressed, then we are attempting
+ // to go up a level in the file system.
+ //
+ if(BUTTON_PRESSED(LEFT_BUTTON, ui8ButtonState, ui8ButtonChanged))
+ {
+ uint32_t ui32NewLevel;
+
+ //
+ // Make sure we are not already at the top of the
+ // directory tree (at root).
+ //
+ if(g_ui32Level)
+ {
+ //
+ // Potential new level is one less than the
+ // current level.
+ //
+ ui32NewLevel = g_ui32Level - 1;
+
+ //
+ // Process the directory change to the new
+ // directory. This function will populate a menu
+ // structure with the files and subdirs in the new
+ // directory.
+ //
+ if(ProcessDirChange("..", ui32NewLevel))
+ {
+ //
+ // If the change was successful, then update
+ // the level.
+ //
+ g_ui32Level = ui32NewLevel;
+
+ //
+ // Now that all the prep is done, send the
+ // KEY_LEFT message to the widget and it will
+ // "slide" from the previous file list to the
+ // new file list of the CWD.
+ //
+ SendWidgetKeyMessage(WIDGET_MSG_KEY_LEFT);
+ }
+ }
+ }
+ }
+
+ //
+ // If we are in the play back screen, change the function of the
+ // buttons.
+ //
+ if((g_ui32SysTickCount != ui32LastTickCount) &&
+ HWREGBITW(&g_ui32Flags, FLAGS_DEVICE_READY) &&
+ (HWREGBITW(&g_ui32Flags, FLAGS_PLAY_SCREEN)))
+ {
+ uint8_t ui8ButtonState;
+ uint8_t ui8ButtonChanged;
+
+ ui32LastTickCount = g_ui32SysTickCount;
+
+ //
+ // Get the current debounced state of the buttons.
+ //
+ ui8ButtonState = ButtonsPoll(&ui8ButtonChanged, 0);
+
+ //
+ // If the left button is pressed, we need to return to
+ // the file menu.
+ //
+ if(BUTTON_PRESSED(LEFT_BUTTON, ui8ButtonState, ui8ButtonChanged))
+ {
+ //
+ // It is possible we have already started playback.
+ //
+ WaveStop();
+
+ //
+ // Redraw the menu over the play screen.
+ //
+ HWREGBITW(&g_ui32Flags, FLAGS_PLAY_SCREEN) = 0;
+ WidgetPaint(WIDGET_ROOT);
+ }
+
+ //
+ // If the select or right button is pressed, we will
+ // start play back of the wav file.
+ //
+ if(BUTTON_PRESSED(SELECT_BUTTON,
+ ui8ButtonState, ui8ButtonChanged) ||
+ BUTTON_PRESSED(RIGHT_BUTTON,
+ ui8ButtonState, ui8ButtonChanged))
+ {
+ //
+ // If we are stopped, then start playing.
+ //
+ if(HWREGBITW(&g_ui32Flags, FLAGS_PLAYING) == 0)
+ {
+ HWREGBITW(&g_ui32Flags, FLAGS_PLAYING) = 1;
+ //
+ // Don't play anything but 16 bit audio since most USB
+ // devices do not support 8 bit formats.
+ //
+ if(g_sWavHeader.ui16BitsPerSample != 16)
+ {
+ return(1);
+ }
+
+ //
+ // See if this is a valid .wav file that can be opened.
+ //
+ if(WavOpen(g_pcFileName, &g_sWavFile) == 0)
+ {
+ //
+ // Change the text on the button to playing.
+ //
+ strcpy(g_pcStatusText, "Playing");
+ WidgetPaint((tWidget *)&g_sStatus);
+
+ //
+ // Indicate that wave play back should start.
+ //
+ HWREGBITW(&g_ui32Flags, FLAGS_PLAYING) = 1;
+ }
+ else
+ {
+ HWREGBITW(&g_ui32Flags, FLAGS_PLAYING) = 0;
+ return(1);
+ }
+
+ //
+ // Initialize the read and write pointers.
+ //
+ g_pui8Read = g_pui8AudioBuffer;
+ g_pui8Write = g_pui8AudioBuffer;
+ g_ui32ValidBytes = 0;
+
+ //
+ // Fill the audio buffer from the file.
+ //
+ FillAudioBuffer();
+
+ //
+ // Start the audio playback.
+ //
+ USBSoundBufferOut(g_pui8Read, g_ui32TransferSize,
+ USBAudioOutCallback);
+ }
+
+ //
+ // Stop play back if we are playing.
+ //
+ else
+ {
+ WaveStop();
+ }
+ }
+
+ //
+ // If the UP button is pressed, increase the volume by 5%.
+ //
+ if(BUTTON_PRESSED(UP_BUTTON, ui8ButtonState, ui8ButtonChanged))
+ {
+ if(g_ui32CurrentVolume < 100)
+ {
+ g_ui32CurrentVolume += 5;
+ USBSoundVolumeSet(g_ui32CurrentVolume);
+ DisplayVolume();
+ }
+ }
+
+ //
+ // If the DOWN button is pressed, decrease the volume by 5%.
+ //
+ if(BUTTON_PRESSED(DOWN_BUTTON, ui8ButtonState, ui8ButtonChanged))
+ {
+ if((g_ui32CurrentVolume <= 100) && (g_ui32CurrentVolume > 0))
+ {
+ g_ui32CurrentVolume -= 5;
+ USBSoundVolumeSet(g_ui32CurrentVolume);
+ DisplayVolume();
+ }
+ }
+ }
+
+ //
+ // Handle the case when the wave file is playing.
+ //
+ if(HWREGBITW(&g_ui32Flags, FLAGS_PLAYING))
+ {
+ //
+ // Handle the transmit complete event.
+ //
+ if(HWREGBITW(&g_ui32Flags, FLAGS_TX_COMPLETE))
+ {
+ //
+ // Clear the transmit complete flag.
+ //
+ HWREGBITW(&g_ui32Flags, FLAGS_TX_COMPLETE) = 0;
+
+ //
+ // If there is space available then fill the buffer.
+ //
+ FillAudioBuffer();
+
+ //
+ // If we run out of valid bytes then stop the playback.
+ //
+ if(g_ui32ValidBytes == 0)
+ {
+ //
+ // No more data or error so stop playing.
+ //
+ WaveStop();
+ }
+
+ //
+ // Update the real display time.
+ //
+ DisplayTime(0);
+ }
+ }
+
+ //
+ // Need to periodically call the USBMain() routine so that
+ // non-interrupt gets a chance to run.
+ //
+ USBMain(GetTickms());
+
+ //
+ // Process any messages in the widget message queue.
+ //
+ WidgetMessageQueueProcess();
+ }
+}
+
|
