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
/* 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@0:bbf6cf0eab95, 2014-03-27 (annotated)
- Committer:
- bw
- Date:
- Thu Mar 27 21:27:04 2014 +0000
- Revision:
- 0:bbf6cf0eab95
Initial.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
bw | 0:bbf6cf0eab95 | 1 | /******************************************************************************* |
bw | 0:bbf6cf0eab95 | 2 | * Module processes 16-bit audio samples to produces delay-based sound effects. |
bw | 0:bbf6cf0eab95 | 3 | * Bryan Wade |
bw | 0:bbf6cf0eab95 | 4 | * 27 MAR 2014 |
bw | 0:bbf6cf0eab95 | 5 | ******************************************************************************/ |
bw | 0:bbf6cf0eab95 | 6 | #include "mbed.h" |
bw | 0:bbf6cf0eab95 | 7 | #include "effects.h" |
bw | 0:bbf6cf0eab95 | 8 | #include "delay.h" |
bw | 0:bbf6cf0eab95 | 9 | |
bw | 0:bbf6cf0eab95 | 10 | // Delay based effects require significant RAM, so we must |
bw | 0:bbf6cf0eab95 | 11 | // scavange two banks of RAM that mbed has reserved for ethernet: |
bw | 0:bbf6cf0eab95 | 12 | // AHBSRAM0: 0x2007C000 - 0x2007FFFF (16KB) |
bw | 0:bbf6cf0eab95 | 13 | // AHBSRAM1: 0x20080000 - 0x20083FFF (16KB) |
bw | 0:bbf6cf0eab95 | 14 | // To keep the linker happy we allocate the RAM with a 16KB array in each bank, |
bw | 0:bbf6cf0eab95 | 15 | // but since they are contiguous, we can treat them as one 32KB block. |
bw | 0:bbf6cf0eab95 | 16 | static const int RAM_BANK0_SIZE = 16 * 1024; |
bw | 0:bbf6cf0eab95 | 17 | static const int RAM_BANK1_SIZE = 16 * 1024; |
bw | 0:bbf6cf0eab95 | 18 | __attribute((section("AHBSRAM0"),aligned)) char ramBank0[RAM_BANK0_SIZE]; |
bw | 0:bbf6cf0eab95 | 19 | __attribute((section("AHBSRAM1"),aligned)) char ramBank1[RAM_BANK1_SIZE]; |
bw | 0:bbf6cf0eab95 | 20 | |
bw | 0:bbf6cf0eab95 | 21 | static effect_mode_t effectMode; |
bw | 0:bbf6cf0eab95 | 22 | static uint16_t effectGain; |
bw | 0:bbf6cf0eab95 | 23 | static const int MAX_DELAYS = 7; |
bw | 0:bbf6cf0eab95 | 24 | static delay_t *delay[MAX_DELAYS]; |
bw | 0:bbf6cf0eab95 | 25 | |
bw | 0:bbf6cf0eab95 | 26 | static void initializeEcho(void); |
bw | 0:bbf6cf0eab95 | 27 | static void initializeReverb(void); |
bw | 0:bbf6cf0eab95 | 28 | |
bw | 0:bbf6cf0eab95 | 29 | /* |
bw | 0:bbf6cf0eab95 | 30 | * Initialize module. |
bw | 0:bbf6cf0eab95 | 31 | */ |
bw | 0:bbf6cf0eab95 | 32 | void Effects_Initialize(void) |
bw | 0:bbf6cf0eab95 | 33 | { |
bw | 0:bbf6cf0eab95 | 34 | effectGain = 0; |
bw | 0:bbf6cf0eab95 | 35 | |
bw | 0:bbf6cf0eab95 | 36 | // Create all the delay objects. They will be initialized and |
bw | 0:bbf6cf0eab95 | 37 | // re-initialized as needed each time the mode changes, but the |
bw | 0:bbf6cf0eab95 | 38 | // total number of delay objects is fixed. |
bw | 0:bbf6cf0eab95 | 39 | for (int i = 0; i < MAX_DELAYS; i++) |
bw | 0:bbf6cf0eab95 | 40 | { |
bw | 0:bbf6cf0eab95 | 41 | delay[i] = Delay_Create(); |
bw | 0:bbf6cf0eab95 | 42 | } |
bw | 0:bbf6cf0eab95 | 43 | Effects_SetMode(EFFECT_STRAIGHT); |
bw | 0:bbf6cf0eab95 | 44 | } |
bw | 0:bbf6cf0eab95 | 45 | |
bw | 0:bbf6cf0eab95 | 46 | /* |
bw | 0:bbf6cf0eab95 | 47 | * Apply current effect to sample stream. |
bw | 0:bbf6cf0eab95 | 48 | * @return Processed sample. |
bw | 0:bbf6cf0eab95 | 49 | */ |
bw | 0:bbf6cf0eab95 | 50 | int16_t Effects_ProcessSample(int16_t dataIn) |
bw | 0:bbf6cf0eab95 | 51 | { |
bw | 0:bbf6cf0eab95 | 52 | int16_t dataOut; |
bw | 0:bbf6cf0eab95 | 53 | |
bw | 0:bbf6cf0eab95 | 54 | switch (effectMode) |
bw | 0:bbf6cf0eab95 | 55 | { |
bw | 0:bbf6cf0eab95 | 56 | case EFFECT_ECHO: |
bw | 0:bbf6cf0eab95 | 57 | dataOut = Delay_WriteWithFeedback(delay[0], dataIn, effectGain); |
bw | 0:bbf6cf0eab95 | 58 | break; |
bw | 0:bbf6cf0eab95 | 59 | |
bw | 0:bbf6cf0eab95 | 60 | case EFFECT_REVERB: |
bw | 0:bbf6cf0eab95 | 61 | dataOut = (Delay_WriteWithFeedback(delay[0], dataIn, effectGain) + |
bw | 0:bbf6cf0eab95 | 62 | Delay_WriteWithFeedback(delay[1], dataIn, effectGain) + |
bw | 0:bbf6cf0eab95 | 63 | Delay_WriteWithFeedback(delay[2], dataIn, effectGain) + |
bw | 0:bbf6cf0eab95 | 64 | Delay_WriteWithFeedback(delay[3], dataIn, effectGain) + |
bw | 0:bbf6cf0eab95 | 65 | Delay_WriteWithFeedback(delay[4], dataIn, effectGain) + |
bw | 0:bbf6cf0eab95 | 66 | Delay_WriteWithFeedback(delay[5], dataIn, effectGain) + |
bw | 0:bbf6cf0eab95 | 67 | Delay_WriteWithFeedback(delay[6], dataIn, effectGain)) / 7; |
bw | 0:bbf6cf0eab95 | 68 | break; |
bw | 0:bbf6cf0eab95 | 69 | |
bw | 0:bbf6cf0eab95 | 70 | case EFFECT_STRAIGHT: |
bw | 0:bbf6cf0eab95 | 71 | default: |
bw | 0:bbf6cf0eab95 | 72 | dataOut = dataIn; |
bw | 0:bbf6cf0eab95 | 73 | } |
bw | 0:bbf6cf0eab95 | 74 | |
bw | 0:bbf6cf0eab95 | 75 | return dataOut; |
bw | 0:bbf6cf0eab95 | 76 | } |
bw | 0:bbf6cf0eab95 | 77 | |
bw | 0:bbf6cf0eab95 | 78 | /* |
bw | 0:bbf6cf0eab95 | 79 | * Getter/setters. |
bw | 0:bbf6cf0eab95 | 80 | */ |
bw | 0:bbf6cf0eab95 | 81 | void Effects_SetMode(effect_mode_t mode) |
bw | 0:bbf6cf0eab95 | 82 | { |
bw | 0:bbf6cf0eab95 | 83 | // Ignore if already in desired mode. |
bw | 0:bbf6cf0eab95 | 84 | if (effectMode == mode) return; |
bw | 0:bbf6cf0eab95 | 85 | |
bw | 0:bbf6cf0eab95 | 86 | // Effects_Process() will continue to be called by an ISR while |
bw | 0:bbf6cf0eab95 | 87 | // changing modes, so first change to straight mode since this |
bw | 0:bbf6cf0eab95 | 88 | // can safely play at any time. Then change to the desired mode |
bw | 0:bbf6cf0eab95 | 89 | // once it is fully configured. |
bw | 0:bbf6cf0eab95 | 90 | effectMode = EFFECT_STRAIGHT; |
bw | 0:bbf6cf0eab95 | 91 | |
bw | 0:bbf6cf0eab95 | 92 | switch (mode) |
bw | 0:bbf6cf0eab95 | 93 | { |
bw | 0:bbf6cf0eab95 | 94 | case EFFECT_ECHO: |
bw | 0:bbf6cf0eab95 | 95 | initializeEcho(); |
bw | 0:bbf6cf0eab95 | 96 | effectMode = mode; |
bw | 0:bbf6cf0eab95 | 97 | break; |
bw | 0:bbf6cf0eab95 | 98 | |
bw | 0:bbf6cf0eab95 | 99 | case EFFECT_REVERB: |
bw | 0:bbf6cf0eab95 | 100 | initializeReverb(); |
bw | 0:bbf6cf0eab95 | 101 | effectMode = mode; |
bw | 0:bbf6cf0eab95 | 102 | break; |
bw | 0:bbf6cf0eab95 | 103 | |
bw | 0:bbf6cf0eab95 | 104 | case EFFECT_STRAIGHT: |
bw | 0:bbf6cf0eab95 | 105 | default: |
bw | 0:bbf6cf0eab95 | 106 | effectMode = mode; |
bw | 0:bbf6cf0eab95 | 107 | } |
bw | 0:bbf6cf0eab95 | 108 | } |
bw | 0:bbf6cf0eab95 | 109 | |
bw | 0:bbf6cf0eab95 | 110 | void Effects_SetGain(uint16_t gain) { effectGain = gain; } |
bw | 0:bbf6cf0eab95 | 111 | |
bw | 0:bbf6cf0eab95 | 112 | /* |
bw | 0:bbf6cf0eab95 | 113 | * Configure one large delay for echo. |
bw | 0:bbf6cf0eab95 | 114 | */ |
bw | 0:bbf6cf0eab95 | 115 | void initializeEcho(void) |
bw | 0:bbf6cf0eab95 | 116 | { |
bw | 0:bbf6cf0eab95 | 117 | // The maximum echo delay is 16K samples since each sample is 2 bytes |
bw | 0:bbf6cf0eab95 | 118 | // and we have 32KB available. |
bw | 0:bbf6cf0eab95 | 119 | static const int ECHO_DELAY_SAMPLES = 12000; |
bw | 0:bbf6cf0eab95 | 120 | Delay_Configure(delay[0], (void *)ramBank0, ECHO_DELAY_SAMPLES * sizeof(int16_t)); |
bw | 0:bbf6cf0eab95 | 121 | } |
bw | 0:bbf6cf0eab95 | 122 | |
bw | 0:bbf6cf0eab95 | 123 | /* |
bw | 0:bbf6cf0eab95 | 124 | * Configure all delays for reverb. |
bw | 0:bbf6cf0eab95 | 125 | */ |
bw | 0:bbf6cf0eab95 | 126 | void initializeReverb(void) |
bw | 0:bbf6cf0eab95 | 127 | { |
bw | 0:bbf6cf0eab95 | 128 | // Delay lengths are chosen as a base length times prime numbers to prevent |
bw | 0:bbf6cf0eab95 | 129 | // interfernce patterns. Total size of all delay lines must fit within 32KB. |
bw | 0:bbf6cf0eab95 | 130 | static const int REVERB_BASE_DELAY_SAMPLES = 256; |
bw | 0:bbf6cf0eab95 | 131 | static const int PRIMES[MAX_DELAYS] = {2, 3, 5, 7, 11, 13, 17}; |
bw | 0:bbf6cf0eab95 | 132 | |
bw | 0:bbf6cf0eab95 | 133 | char *ram = ramBank0; // Location for first buffer. |
bw | 0:bbf6cf0eab95 | 134 | |
bw | 0:bbf6cf0eab95 | 135 | for (int i = 0; i < MAX_DELAYS; i++) |
bw | 0:bbf6cf0eab95 | 136 | { |
bw | 0:bbf6cf0eab95 | 137 | // Configure each delay with proper buffer size and location |
bw | 0:bbf6cf0eab95 | 138 | int size = PRIMES[i] * REVERB_BASE_DELAY_SAMPLES * sizeof(int16_t); |
bw | 0:bbf6cf0eab95 | 139 | Delay_Configure(delay[i], (void *)ram, size); |
bw | 0:bbf6cf0eab95 | 140 | |
bw | 0:bbf6cf0eab95 | 141 | // The next buffer location immediately follows this one. |
bw | 0:bbf6cf0eab95 | 142 | ram += size; |
bw | 0:bbf6cf0eab95 | 143 | } |
bw | 0:bbf6cf0eab95 | 144 | } |
bw | 0:bbf6cf0eab95 | 145 | |
bw | 0:bbf6cf0eab95 | 146 | |
bw | 0:bbf6cf0eab95 | 147 |