Streams USB audio with sound effects applied. Sound effect selected by joystick and intensity altered by tilting the mbed. Output to the mbed-application-board phono jack.

Dependencies:   C12832_lcd MMA7660 USBDevice mbed

/media/uploads/bw/img_1293.jpg

/* Uses the mbed LPC1768 and mbed-application-board to create a USB audio device
 * that streams audio from a host computer to headphones or powered speakers. 
 * A couple different sound effects can be applied to the stream in real-time,
 * and tilting the mbed alters intensity of the effect.
 *
 *                                               ECHO
 *       The joystick selects )                   |
 *       one of three effect  )       STRAIGHT -  o  - STRAIGHT
 *       modes.               )                   |
 *                                              REVERB   
 * 
 *
 *
 *                                               \\           ||    
 *       Tilting the mbed     )      ======       \\          ||
 *       determines intensity )                    \\         ||
 *       of the effect.       )
 *                                     0%         50%         100%  
 *
 * The LCD display shows the current effect mode, intesity and buffer level.
*/

Files at this revision

API Documentation at this revision

Comitter:
bw
Date:
Thu Mar 27 21:27:04 2014 +0000
Child:
1:d2250586c044
Commit message:
Initial.

Changed in this revision

C12832_lcd.lib Show annotated file Show diff for this revision Revisions of this file
MMA7660.lib Show annotated file Show diff for this revision Revisions of this file
USBDevice.lib Show annotated file Show diff for this revision Revisions of this file
audio.cpp Show annotated file Show diff for this revision Revisions of this file
audio.h Show annotated file Show diff for this revision Revisions of this file
buffer.cpp Show annotated file Show diff for this revision Revisions of this file
buffer.h Show annotated file Show diff for this revision Revisions of this file
delay.cpp Show annotated file Show diff for this revision Revisions of this file
delay.h Show annotated file Show diff for this revision Revisions of this file
effects.cpp Show annotated file Show diff for this revision Revisions of this file
effects.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
user_interface.cpp Show annotated file Show diff for this revision Revisions of this file
user_interface.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/C12832_lcd.lib	Thu Mar 27 21:27:04 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/dreschpe/code/C12832_lcd/#8f86576007d6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MMA7660.lib	Thu Mar 27 21:27:04 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/Sissors/code/MMA7660/#a8e20db7901e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBDevice.lib	Thu Mar 27 21:27:04 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/USBDevice/#8133879e4fb4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audio.cpp	Thu Mar 27 21:27:04 2014 +0000
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Manages all aspects of audio playback with sound effects.
+ * Bryan Wade
+ * 27 MAR 2014
+ ******************************************************************************/
+#include "mbed.h"
+#include "USBAudio.h"
+#include "buffer.h"
+#include "effects.h"
+
+static const int SAMPLE_FREQ = 50000;
+static const int NUM_CHANNELS = 1;
+static const int PACKET_FREQ = 1000;
+static const int PACKET_LENGTH = SAMPLE_FREQ / PACKET_FREQ;
+static const int PLAYBACK_BUFFER_SIZE = 8 * 1024;
+
+static int16_t packetBuffer[PACKET_LENGTH];
+static char playbackBufferRAM[PLAYBACK_BUFFER_SIZE]; 
+static buffer_t *playbackBuffer;
+static bool playbackReady = false;
+
+AnalogOut speaker(p18);
+USBAudio audio(SAMPLE_FREQ, NUM_CHANNELS, 8000, 1, 0x7180, 0x7500);
+Ticker playbackTic;
+DigitalOut bufferLED(LED1);   // On when buffer is ready for playback.
+DigitalOut playbackLED(LED2); // On when playing audio.
+DigitalOut sampleLED(LED3);   // Toggles on each audio sample played.
+DigitalOut packetLED(LED4);   // Toggles on each audio packet received.
+
+/*
+ * Transfers packet to the playback buffer each time a packet is received.
+ */
+void handlePacket(void) {
+    Buffer_WriteBlock(playbackBuffer, packetBuffer, PACKET_LENGTH);
+    packetLED = !packetLED; 
+}
+
+/*
+ * Pulls a sample from the playback buffer, adds effects and sends to speaker.
+ */
+void handlePlayback() 
+{
+    int16_t sample; 
+    
+    // Get a sample from playback buffer
+    if (playbackReady) {
+        if (Buffer_Read(playbackBuffer, &sample))
+        {
+            playbackLED = 1; // Playing 
+        } else {
+            sample = 0;         // Shouldn't get hear if buffer limits set properly.
+            playbackLED = 0; 
+        }
+    } else {
+        sample = 0;             // Nothing to play
+        playbackLED = 0;
+    }
+
+    // Add sound effects
+    int32_t out = Effects_ProcessSample(sample);
+            
+    // Write the sample to the uni-polar A/D, which requires offseting the signed 16-bit sample.         
+    speaker.write_u16(out + 32767);
+            
+    sampleLED = !sampleLED;   
+}
+
+/*
+ * Initialize audio module.
+ */
+void Audio_Initialize(void)
+{
+    Effects_Initialize();
+    
+    // Create a buffer that will incomming audio packets and provide a consistent supply
+    // for playback at the desired sample rate.
+    playbackBuffer = Buffer_Create(playbackBufferRAM, sizeof(playbackBufferRAM));
+         
+    // Start the playback timer interrupt that will call the playback handler at the desired
+    // sample rate.     
+    playbackTic.attach_us(handlePlayback, 1000000.0/(float)(SAMPLE_FREQ));
+
+    // Attach the handler that will buffer packets from incomming USB audio. 
+    // Perform the first read to set the buffer location, then all subsequent packets
+    // will go to this buffer, and the handler pulls them from this small buffer
+    // and places them in the large playback buffer.
+    audio.attachReadCallback(handlePacket);
+    audio.readNB((uint8_t *)packetBuffer);
+    
+    // Reduce USB IRQ priority so that audio playback is not interrupted by USB activity.
+    static const int LOW_PRIORITY = 255; // Larger number is lower priority.
+    NVIC_SetPriority(USB_IRQn, LOW_PRIORITY);   
+}
+
+/*
+ * Checks buffer level and updates playbackReady flag.
+ * @return Buffer level.
+ */
+int32_t Audio_CheckPlaybackBufferLevel(void)
+{
+    static int32_t PLAYBACK_START_LEVEL = 4 * PACKET_LENGTH;
+    static int32_t PLAYBACK_STOP_LEVEL = 2 * PACKET_LENGTH;
+    
+    int32_t level = Buffer_GetLevel(playbackBuffer);
+    if (level > PLAYBACK_START_LEVEL) {
+        playbackReady = true;
+    } else if (level < PLAYBACK_STOP_LEVEL) {
+        playbackReady = false;
+    }
+    
+    bufferLED = playbackReady;
+    
+    return level;   
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audio.h	Thu Mar 27 21:27:04 2014 +0000
@@ -0,0 +1,19 @@
+/*******************************************************************************
+ * Manages all aspects of audio playback with sound effects.
+ * Bryan Wade
+ * 27 MAR 2014
+ ******************************************************************************/
+#ifndef AUDIO_H
+#define AUDIO_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+// Initialize the module.
+void Audio_Initialize(void);
+
+// Periodically call to stop/start playback according to the 
+// current buffer level. Return level for diagnostics.
+int32_t Audio_CheckPlaybackBufferLevel(void);
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/buffer.cpp	Thu Mar 27 21:27:04 2014 +0000
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Implements FIFO buffer for glitch-free audio playback.
+ * Bryan Wade
+ * 27 MAR 2014
+ ******************************************************************************/
+#include "buffer.h"
+#include "string.h"
+
+struct buffer_t { // Implementation of opaque type.
+    int16_t *base;
+    int16_t *head;
+    int16_t *tail;
+    size_t len;
+};
+
+/*
+ * Create a buffer object given a ptr and size of available RAM.
+ * @return Ptr to the new buffer.
+ */
+buffer_t *Buffer_Create(void *ram, size_t size)
+{
+    buffer_t *buffer = (buffer_t *)malloc(sizeof(buffer_t));
+    if (buffer) {
+        buffer->base = (int16_t *)ram;
+        buffer->len = size / sizeof(int16_t);
+        memset(ram, 0, size);
+        buffer->head = buffer->tail = buffer->base;
+    }
+    return buffer;
+}
+
+/*
+ * Read a single sample from the buffer.
+ * @return True if successful (no underflow).
+ */
+bool Buffer_Read(buffer_t *buffer, int16_t *pDataOut)
+{
+    if (buffer->head == buffer->tail)
+        return false;
+
+    *pDataOut = *buffer->tail;
+
+    if (++buffer->tail >= buffer->base + buffer->len) {
+        buffer->tail = buffer->base;
+    }
+
+    return true;
+}
+
+/*
+ * Write a single sample to the buffer.
+ */
+void Buffer_Write(buffer_t *buffer, int16_t dataIn)
+{
+    if (Buffer_GetLevel(buffer) >= buffer->len - 1)
+        return;
+    
+    *buffer->head = dataIn;
+
+    if (++buffer->head >= buffer->base + buffer->len)
+        buffer->head = buffer->base;
+}
+
+/*
+ * Write a block of data to the buffer.
+ */
+void Buffer_WriteBlock(buffer_t *buffer, const int16_t *pDataIn, uint32_t length)
+{
+    int i;
+    for (i = 0; i < length; i++) {
+        Buffer_Write(buffer, pDataIn[i]);
+    }
+}
+
+/*
+ * Get buffer level
+ */
+int32_t Buffer_GetLevel(buffer_t *buffer) 
+{
+    __disable_irq(); /* Begin critical section */
+    
+    int32_t level = (buffer->head >= buffer->tail) ?
+                    buffer->head - buffer->tail :
+                    buffer->len + buffer->head - buffer->tail;
+                    
+    __enable_irq(); /* End critical section */
+    
+    return level;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/buffer.h	Thu Mar 27 21:27:04 2014 +0000
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Implements FIFO buffer for glitch-free audio playback.
+ * Bryan Wade
+ * 27 MAR 2014
+ ******************************************************************************/
+#ifndef BUFFER_H
+#define BUFFER_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef struct buffer_t buffer_t; // Opaque type declaration.
+
+// Get a ptr to a new buffer.
+buffer_t *Buffer_Create(void *ram, size_t size);
+
+// Read one sample from buffer.
+bool Buffer_Read(buffer_t *buffer, int16_t *pDataOut);
+
+// Write one sample to buffer.
+void Buffer_Write(buffer_t *buffer, int16_t DataIn);
+
+// Write a block of data to buffer.
+void Buffer_WriteBlock(buffer_t *buffer, const int16_t *pDataIn, uint32_t length);
+
+// Get the current number of samples buffered.
+int32_t Buffer_GetLevel(buffer_t *buffer);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/delay.cpp	Thu Mar 27 21:27:04 2014 +0000
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Implements delay lines for audio samples.
+ * Bryan Wade
+ * 27 MAR 2014
+ ******************************************************************************/
+#include "delay.h"
+#include "string.h"
+
+struct delay_t { // Implementation of opaque type.
+    int16_t *base;
+    int16_t *head;
+    size_t len;
+};
+
+/*
+ * Create a delay object given a ptr and size of available RAM.
+ * This is only allocating memory for the metadata. The location of 
+ * the buffer memory is passed in.
+ * @return Ptr to the new delay.
+ */
+delay_t *Delay_Create(void)
+{
+    return (delay_t *)malloc(sizeof(delay_t));
+}
+
+/*
+ * Configure delay buffer location and size.
+ * @return True if successful.
+ */
+bool Delay_Configure(delay_t *delay, void *ram, size_t size)
+{
+    if (delay) {
+        delay->base = delay->head = (int16_t *)ram;
+        delay->len = size / sizeof(int16_t);
+        memset(ram, 0, size);
+        return true;
+    }
+    
+    return false;
+}
+
+/*
+ * Push a new sample with feedback into the delay while simulateously retieving the output sample.
+ * @return Output data.
+ */
+int16_t Delay_WriteWithFeedback(delay_t *delay, int16_t dataIn, uint16_t gain)
+{
+    int16_t dataOut = *delay->head;
+
+    // Feedback gain is fixed-point Q16 format.
+    *delay->head = dataIn + ((gain * dataOut ) >> 16);
+    
+    if (++delay->head >= delay->base + delay->len)
+        delay->head = delay->base;
+        
+    return dataOut;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/delay.h	Thu Mar 27 21:27:04 2014 +0000
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Implements delay lines for audio samples.
+ * Bryan Wade
+ * 27 MAR 2014
+ ******************************************************************************/
+#ifndef DELAY_H
+#define DELAY_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef struct delay_t delay_t; // Opaque type declaration.
+
+// Get a ptr to a new delay struct
+delay_t *Delay_Create(void);
+
+// Configure the delay length and ram location
+bool Delay_Configure(delay_t *delay, void *ram, size_t size);
+
+// Write one sample with feedback to the delay and get one sample out.
+int16_t Delay_WriteWithFeedback(delay_t *delay, int16_t dataIn, uint16_t gain);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/effects.cpp	Thu Mar 27 21:27:04 2014 +0000
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * Module processes 16-bit audio samples to produces delay-based sound effects.
+ * Bryan Wade
+ * 27 MAR 2014
+ ******************************************************************************/
+#include "mbed.h"
+#include "effects.h"
+#include "delay.h"
+
+// Delay based effects require significant RAM, so we must
+// scavange two banks of RAM that mbed has reserved for ethernet:
+//   AHBSRAM0: 0x2007C000 - 0x2007FFFF (16KB)
+//   AHBSRAM1: 0x20080000 - 0x20083FFF (16KB)                            
+// To keep the linker happy we allocate the RAM with a 16KB array in each bank, 
+// but since they are contiguous, we can treat them as one 32KB block.
+static const int RAM_BANK0_SIZE = 16 * 1024;
+static const int RAM_BANK1_SIZE = 16 * 1024; 
+__attribute((section("AHBSRAM0"),aligned)) char ramBank0[RAM_BANK0_SIZE];
+__attribute((section("AHBSRAM1"),aligned)) char ramBank1[RAM_BANK1_SIZE];
+
+static effect_mode_t effectMode;
+static uint16_t effectGain;
+static const int MAX_DELAYS = 7;
+static delay_t *delay[MAX_DELAYS];
+
+static void initializeEcho(void);
+static void initializeReverb(void);
+
+/*
+ * Initialize module.
+ */
+void Effects_Initialize(void) 
+{
+    effectGain = 0;
+    
+    // Create all the delay objects. They will be initialized and
+    // re-initialized as needed each time the mode changes, but the
+    // total number of delay objects is fixed.
+    for (int i = 0; i < MAX_DELAYS; i++) 
+    {
+        delay[i] = Delay_Create();  
+    }
+    Effects_SetMode(EFFECT_STRAIGHT);
+}
+
+/*
+ * Apply current effect to sample stream.
+ * @return Processed sample.
+ */
+int16_t Effects_ProcessSample(int16_t dataIn)
+{
+    int16_t dataOut;
+    
+    switch (effectMode) 
+    {
+    case EFFECT_ECHO:
+        dataOut = Delay_WriteWithFeedback(delay[0], dataIn, effectGain);
+        break;
+    
+    case EFFECT_REVERB:
+        dataOut = (Delay_WriteWithFeedback(delay[0], dataIn, effectGain) + 
+                   Delay_WriteWithFeedback(delay[1], dataIn, effectGain) +
+                   Delay_WriteWithFeedback(delay[2], dataIn, effectGain) +
+                   Delay_WriteWithFeedback(delay[3], dataIn, effectGain) +
+                   Delay_WriteWithFeedback(delay[4], dataIn, effectGain) +
+                   Delay_WriteWithFeedback(delay[5], dataIn, effectGain) +
+                   Delay_WriteWithFeedback(delay[6], dataIn, effectGain)) / 7;    
+        break;
+    
+    case EFFECT_STRAIGHT:
+    default:
+        dataOut = dataIn;
+    }
+    
+    return dataOut;
+}
+
+/*
+ * Getter/setters.
+ */
+void Effects_SetMode(effect_mode_t mode)
+{
+    // Ignore if already in desired mode.
+    if (effectMode == mode) return;
+        
+    // Effects_Process() will continue to be called by an ISR while
+    // changing modes, so first change to straight mode since this 
+    // can safely play at any time. Then change to the desired mode
+    // once it is fully configured.
+    effectMode = EFFECT_STRAIGHT;
+    
+    switch (mode)
+    {
+    case EFFECT_ECHO:
+        initializeEcho();
+        effectMode = mode;
+        break;
+    
+    case EFFECT_REVERB:
+        initializeReverb();  
+        effectMode = mode;
+        break;
+    
+    case EFFECT_STRAIGHT:
+    default:
+        effectMode = mode;
+    }
+}
+
+void Effects_SetGain(uint16_t gain) { effectGain = gain; }
+
+/*
+ * Configure one large delay for echo.
+ */
+void initializeEcho(void)
+{
+    // The maximum echo delay is 16K samples since each sample is 2 bytes
+    // and we have 32KB available. 
+    static const int ECHO_DELAY_SAMPLES = 12000; 
+    Delay_Configure(delay[0], (void *)ramBank0, ECHO_DELAY_SAMPLES * sizeof(int16_t));  
+}
+
+/*
+ * Configure all delays for reverb.
+ */
+void initializeReverb(void)
+{
+    // Delay lengths are chosen as a base length times prime numbers to prevent 
+    // interfernce patterns. Total size of all delay lines must fit within 32KB.
+    static const int REVERB_BASE_DELAY_SAMPLES = 256;
+    static const int PRIMES[MAX_DELAYS] = {2, 3, 5, 7, 11, 13, 17};   
+    
+    char *ram = ramBank0; // Location for first buffer.
+    
+    for (int i = 0; i < MAX_DELAYS; i++) 
+    {
+        // Configure each delay with proper buffer size and location
+        int size = PRIMES[i] * REVERB_BASE_DELAY_SAMPLES * sizeof(int16_t);              
+        Delay_Configure(delay[i], (void *)ram, size);      
+        
+        // The next buffer location immediately follows this one.
+        ram += size; 
+    }  
+}
+
+
+    
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/effects.h	Thu Mar 27 21:27:04 2014 +0000
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Module processes 16-bit audio samples to produces delay-based sound effects.
+ * Bryan Wade
+ * 27 MAR 2014
+ ******************************************************************************/
+#ifndef EFFECTS_H
+#define EFFECTS_H
+
+#include <stdint.h>
+
+// Practical gain limit for decent sound.
+#define MAX_EFFECT_GAIN (58982) 
+
+// Available sound effect modes
+typedef enum effect_mode_t {
+    EFFECT_STRAIGHT,
+    EFFECT_ECHO,
+    EFFECT_REVERB
+} effect_mode_t;
+
+
+// Initialize module.
+void Effects_Initialize(void);
+
+// Process one audio sample.
+int16_t Effects_ProcessSample(int16_t dataIn);
+
+void Effects_SetMode(effect_mode_t mode);
+
+void Effects_SetGain(uint16_t gain);
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Thu Mar 27 21:27:04 2014 +0000
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Project: USB Sound Effects.
+ *
+ * Uses the mbed LPC1768 and mbed-application-board to create a USB audio device
+ * that streams audio from a host computer to headphones or powered speakers. 
+ * A couple different sound effects can be applied to the stream in real-time,
+ * tilting the mbed alters the effects.
+ *
+ *                                               ECHO
+ *       The joystick selects )                   |
+ *       one of three effect  )       STRAIGHT -  o  - STRAIGHT
+ *       modes.               )                   |
+ *                                              REVERB   
+ * 
+ *
+ *
+ *                                               \\           ||    
+ *       Tilting the mbed     )      ======       \\          ||
+ *       determines intensity )                    \\         ||
+ *       of the effect.       )
+ *                                     0%         50%         100%  
+ *
+ * The LCD display shows the current effect mode, intesity and buffer level.
+ *
+ * For better audio quality, packets are buffered before playbeck. Playback is
+ * halted when the buffer drops below two packets and resumes when it is greater
+ * than four packets. This creates a lag of a few milliseconds, which is not 
+ * detectable by a listener using straight playback mode.
+ * 
+ * The echo effect produces echos with a delay of about 250 milliseconds. Tilting
+ * the mbed alters the persistence of echo.
+ *
+ * The reverb effect utilizes multiple delay lines of length varying from  
+ * about 25 to 200 milliseconds. Again, tilting the mbed alters the 
+ * persistence. 
+ *
+ * Implemenation Notes:
+ * Audio playback is implement with an mbed ticker, and it is critical that this interrupt
+ * is not delayed to acheive decent audio quality. USB audio packet handling also uses
+ * interrupts, but the priority is set lower to ensure proper playback. The USBAudio class
+ * was modified to use a callback to transfer the received packet from the packet buffer to 
+ * the larger playback buffer. The user interface is handled entirely in the user-context
+ * since it has only soft timing requirements.
+ *
+ * Future work:
+ * 1. Clocks within the host computer and mbed will differ slightly, so the 
+ * average sample rate from the computer will not exactly match the average mbed
+ * playback rate. Over time the playback buffer level with creep until it becomes 
+ * totaly empty or full. The current implementation addresses this by simply 
+ * increasing the buffer size and selecting a sample rate that the mbed 
+ * can replicate most accurately (mulitple of 1 us to use mbed Ticker). A better 
+ * solution may be to slightly alter the playback rate in response to the buffer level.
+ * For example use 20us rate when the buffer level is high and 19us when the buffer 
+ * level is low. Of course this would distort the playback frequecy by 5%, so it would
+ * be even better to make a custom timer interrupt with better resolution than the 
+ * mbed ticker.
+ *
+ * 2. It would be interesting to add stereo audio, but the mbed-application-board 
+ * audio jack is mono so this would require hardware customization.
+ *
+ * Bryan Wade
+ * 27 MAR 2014
+ ******************************************************************************/
+
+#include "mbed.h"
+#include "audio.h"
+#include "effects.h"
+#include "user_interface.h"
+
+int main() 
+{
+    UI_Initialize();
+    Audio_Initialize(); // Start audio playback.
+    
+    while (true) 
+    {   
+        //       
+        int32_t level = Audio_CheckPlaybackBufferLevel();
+        UI_Update(level);          
+
+        Effects_SetGain(UI_GetEffectGain());
+        Effects_SetMode(UI_GetEffectMode());
+
+        // 50 milliseconds provides good UI responsivness
+        wait(.05);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Thu Mar 27 21:27:04 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/3d0ef94e36ec
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/user_interface.cpp	Thu Mar 27 21:27:04 2014 +0000
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Mangages LCD display, joystick and accelerometer for user interface.
+ * Bryan Wade
+ * 27 MAR 2014
+ ******************************************************************************/
+#include "mbed.h"
+#include "C12832_lcd.h"
+#include "MMA7660.h"
+#include "user_interface.h"
+#include "effects.h"
+
+C12832_LCD lcd;
+MMA7660 accel(p28, p27, true);
+BusIn joystick(p15, p12, p13, p16);
+AnalogIn pot(p19);
+
+static const int STICK_IDLE  = 0;
+static const int STICK_UP    = 1;
+static const int STICK_DOWN  = 2;
+static const int STICK_LEFT  = 4;
+static const int STICK_RIGHT = 8;
+
+static uint8_t stickPrevious;
+static effect_mode_t selectedMode;
+static char *selectedModeName;
+static uint16_t effectGain;
+
+/*
+ * Update the display with mode, gain and buffer level.
+ */
+void updateDisplay(int32_t level)
+{
+    lcd.locate(0,0);
+    lcd.printf("Effect: %s      ", selectedModeName);
+       
+    lcd.locate(0,10);
+    lcd.printf("Intensity: %d    ", effectGain / (MAX_EFFECT_GAIN / 100));
+    
+    lcd.locate(0,20);
+    lcd.printf("Buffer: %d    ", level);
+}
+
+/*
+ * Get effects mode selected by joystick.
+ */
+void serviceJoystick(void)
+{
+    uint8_t stick = joystick;
+    
+    if (stick != stickPrevious) {
+        stickPrevious = stick;    
+    
+        switch (stick)
+        {
+        case STICK_UP:
+            selectedMode = EFFECT_ECHO;
+            selectedModeName = "Echo";
+            break;
+            
+        case STICK_DOWN:
+            selectedMode = EFFECT_REVERB;
+            selectedModeName = "Reverb";
+            break;
+            
+        case STICK_LEFT:
+        case STICK_RIGHT:
+            selectedMode = EFFECT_STRAIGHT;
+            selectedModeName = "Straight";
+            break;
+        }    
+    }
+}
+
+/*
+ * Get effects gain from accelerometer tilt.
+ */
+ void serviceAccel(void)
+ {
+     // Tilt sensitivity selected to produce max effect gain 
+     // at about 80 deg tilt.
+     static const int32_t TILT_SENSITIVITY = 2750; 
+     int a[3];
+     accel.readData(a);
+     //Convert x-axis raw accelerometer data to the effect gain.
+     int32_t x = a[0] * TILT_SENSITIVITY;
+     if (x < 0) x = -x;
+     if (x > MAX_EFFECT_GAIN) x = MAX_EFFECT_GAIN;
+     effectGain = (uint16_t)x;    
+ }
+ 
+/*
+ * Initialize the joystick and display.
+ */
+void UI_Initialize(void)
+{
+    lcd.cls();
+    stickPrevious = STICK_IDLE;
+    selectedMode = EFFECT_STRAIGHT;
+    selectedModeName = "Straight";
+    effectGain = 0;
+    // Set the accelerometer to sample slightly
+    // faster than we poll the user interface.
+    accel.setSampleRate(32);
+}
+
+/*
+ * Updates display, joystick and accelerometer
+ */
+void UI_Update(int32_t bufferLevel)
+{
+    serviceJoystick();
+    serviceAccel();
+    updateDisplay(bufferLevel);    
+}
+ 
+
+effect_mode_t UI_GetEffectMode(void) { return selectedMode; }
+
+uint16_t UI_GetEffectGain(void) { return effectGain; }
+
+
+    
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/user_interface.h	Thu Mar 27 21:27:04 2014 +0000
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Mangages LCD display, joystick and accelerometer for user interface.
+ * Bryan Wade
+ * 27 MAR 2014
+ ******************************************************************************/
+#ifndef USER_INTERFACE_H
+#define USER_INTERFACE_H
+
+#include "effects.h"
+
+// Initialize module.
+void UI_Initialize(void);
+
+// Update the display and poll all input devices.
+void UI_Update(int32_t bufferLevel);
+
+// Get the effect mode selected by joystick.
+effect_mode_t UI_GetEffectMode(void);
+
+// Get the effect gain selected by accelerometer tilt.
+uint16_t UI_GetEffectGain(void);
+
+#endif
\ No newline at end of file