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.
*/

effects.cpp

Committer:
bw
Date:
2014-03-27
Revision:
2:9429f84ea165
Parent:
0:bbf6cf0eab95

File content as of revision 2:9429f84ea165:

/*******************************************************************************
 * 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; 
    }  
}