Part Number: AM2754-Q1
I want to do some simple audio processing using c7x in my application. I need a base template which I can modify to add my own audio processing algorithm. Is there any base example available for this?
Part Number: AM2754-Q1
I want to do some simple audio processing using c7x in my application. I need a base template which I can modify to add my own audio processing algorithm. Is there any base example available for this?
===============================================================================
This guide explains how to implement real-time audio processing on AM275x devices
using the C7x DSP core. The implementation captures audio from a microphone/aux input,
processes it in real-time, and outputs the processed audio to speakers.
The basic flow is:
Microphone/AUX input -> ADC -> McASP RX -> Buffer -> C7x Processing -> Buffer -> McASP TX -> DAC -> Speaker
===============================================================================
STEP 1: CREATE AUDIO PROCESSING TASK
What it does:
Creates a FreeRTOS task that will handle the audio processing in real-time.
Why it's needed:
Audio processing must run at the highest priority to meet real-time deadlines.
If the processing is delayed, you get audio dropouts and glitches.
How to do it:
Create the task with maximum priority and large stack size:
gAudioProcessingTask = xTaskCreateStatic(
audio_processing, // Task function
"audio_processing", // Task name
AUDIO_PROCESSING_TASK_SIZE, // Stack size (64KB)
NULL, // Parameters
configMAX_PRIORITIES-1, // Highest priority
gAudioProcessingTaskStack, // Stack memory
&gAudioProcessingTaskObj // Task object
);
Key points:
- Use highest priority (configMAX_PRIORITIES-1) so nothing preempts audio processing
- Allocate large stack (64KB) to handle complex DSP algorithms
- Use static allocation for deterministic memory usage
===============================================================================
STEP 2: WAIT FOR SEMAPHORE IN THE TASK
What it does:
The audio processing task waits for a signal that new audio data is available.
Why it's needed:
The task should only process audio when fresh data arrives from the microphone.
Waiting on a semaphore puts the task to sleep, saving CPU cycles until needed.
How to do it:
In your audio processing task function:
void audio_processing(void *args)
{
while(1) {
// Wait forever until new audio data arrives
SemaphoreP_pend(&gMcaspTxStartSem, SystemP_WAIT_FOREVER);
// Audio processing happens after this...
}
}
Key points:
- SystemP_WAIT_FOREVER means the task will sleep until signaled
- This is efficient - no CPU wasted polling for data
- The task wakes up immediately when new audio arrives
===============================================================================
STEP 3: RELEASE SEMAPHORE IN RX CALLBACK
What it does:
When the McASP hardware finishes receiving a buffer of audio data, it calls
a callback function that signals the processing task.
Why it's needed:
This creates an event-driven system. As soon as new audio data is captured,
the processing task is notified and can immediately start working on it.
How to do it:
The RX callback function:
void mcasp_rxcb(MCASP_Handle handle, MCASP_Transaction *transaction)
{
current_transaction = transaction; // Store the buffer pointer
SemaphoreP_post(&gMcaspTxStartSem); // Wake up processing task
}
Key points:
- This runs in interrupt context - keep it minimal and fast
- Store the buffer pointer so the task knows which buffer to process
- SemaphoreP_post() wakes up the waiting audio processing task immediately
===============================================================================
STEP 4: INVALIDATE CACHE BEFORE PROCESSING
What it does:
Tells the CPU that the data in the buffer may have changed and to read fresh
data from memory instead of using cached copies.
Why it's needed:
The audio data was written to memory by the DMA controller, not the CPU.
The CPU cache may contain old data, so we must invalidate it to see the
fresh audio samples.
How to do it:
CacheP_inv(current_transaction->buf, APP_MCASP_AUDIO_BUFF_SIZE, CacheP_TYPE_ALL);
Key points:
- Always do this before reading DMA data
- Ensures you process fresh audio samples, not stale cached data
- Critical for data correctness in DMA-based systems
===============================================================================
STEP 5: PROCESS AUDIO SAMPLES
What it does:
This is where your actual audio processing algorithm runs - filtering, effects,
gain control, etc.
Why it's needed:
This is the core functionality - modifying the audio in real-time according
to your requirements.
How to do it:
Cast the buffer to 32-bit samples and process:
(Here a simple left channel mute is done as audio processing. You can add you processing algorithm here)
uint32_t* buffer = (uint32_t*)(current_transaction->buf);
uint32_t cnt = current_transaction->count; // Number of samples
// Example: Mute left channel (even indices are left, odd are right)
for (uint32_t i = 0; i < cnt; i += 2) {
buffer[i] = 0; // Mute left channel
// buffer[i+1] unchanged (right channel passes through)
}
Key points:
- Samples are 32-bit signed integers
- Stereo audio: even indices = left channel, odd indices = right channel
- Keep processing fast to meet real-time deadlines
- Buffer contains 512 samples (256 left/right pairs)
===============================================================================
STEP 6: WRITE BACK CACHE AFTER PROCESSING
What it does:
Tells the CPU to write any cached data back to memory so the DMA controller
can see the processed audio samples.
Why it's needed:
After processing, the modified audio data may only exist in CPU cache.
The DMA controller needs the data in memory to send it to the speakers.
How to do it:
CacheP_wb(current_transaction->buf, APP_MCASP_AUDIO_BUFF_SIZE, CacheP_TYPE_ALL);
Key points:
- Always do this after modifying DMA data
- Ensures the DMA controller sees your processed audio samples
- Critical for correct audio output
===============================================================================
STEP 7: SUBMIT PROCESSED BUFFER FOR OUTPUT
What it does:
Gives the processed audio buffer back to the McASP driver for transmission
to the speakers.
Why it's needed:
This completes the audio processing cycle - the processed audio gets sent
to the DAC and out to the speakers.
How to do it:
MCASP_submitTx(mcaspHandle, current_transaction);
Key points:
- This queues the buffer for audio output
- The same buffer that contained input audio now contains processed output audio
- The cycle repeats when the TX completes and the buffer is reused for RX
===============================================================================
STEP 8: SET UP BUFFER CIRCULATION (TX CALLBACK)
What it does:
When the McASP finishes transmitting audio to the speakers, it calls a callback
that resubmits the buffer for capturing new audio.
Why it's needed:
This creates continuous audio flow - buffers circulate between RX (capture)
and TX (playback) to maintain uninterrupted audio streaming.
How to do it:
void mcasp_txcb(MCASP_Handle handle, MCASP_Transaction *transaction)
{
MCASP_submitRx(handle, transaction); // Reuse buffer for next capture
}
Key points:
- This runs in interrupt context - keep it minimal
- Reuses the same buffer that just finished playing audio
- Maintains the circular buffer system for continuous operation
===============================================================================
COMPLETE AUDIO PROCESSING FLOW
1. System starts, codecs and McASP are configured
2. Audio processing task is created and waits on semaphore
3. McASP starts capturing audio from microphone
4. When RX buffer is full, mcasp_rxcb() wakes up processing task
5. Processing task invalidates cache and processes audio samples
6. Task writes back cache and submits buffer for TX
7. McASP transmits processed audio to speakers
8. When TX completes, mcasp_txcb() resubmits buffer for next RX
9. Cycle repeats continuously for real-time audio processing
===============================================================================
TIMING CONSIDERATIONS
Buffer size: 2048 bytes = 512 samples = 256 stereo pairs
At 48kHz: 256/48000 = 5.33ms of audio per buffer
Total system latency: ~21ms (4 buffers × 5.33ms)
===============================================================================
COMMON AUDIO PROCESSING EXAMPLES
1. Channel Muting:
Set buffer[i] = 0 for specific channels
2. Gain Control:
Multiply samples: buffer[i] = buffer[i] * gain_factor
3. Simple Filter:
Use previous samples: output = alpha*input + (1-alpha)*previous_output
4. Stereo Effects:
Mix left/right channels or add delays between channels
===============================================================================
TROUBLESHOOTING
Problem: Audio dropouts/glitches
Solution: Ensure audio task has highest priority, optimize processing code
Problem: No audio output
Solution: Check codec I2C configuration, verify McASP pin connections
Problem: Distorted audio
Solution: Check for overflow in processing, verify gain levels
Problem: System crashes
Solution: Check buffer alignment, verify cache management calls
===============================================================================
FULL CODE EXAMPLE
Adding the mcasp_playback.c file for reference. To try this, just replace this file inside the mcasp_stereo_playback example.
/*
* Copyright (C) 2024 Texas Instruments Incorporated
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <kernel/dpl/DebugP.h>
#include <kernel/dpl/AddrTranslateP.h>
#include <kernel/dpl/ClockP.h>
#include <kernel/dpl/DebugP.h>
#include <drivers/i2c.h>
#include <drivers/gpio.h>
#include <drivers/mcasp.h>
#include <board/ioexp/ioexp_tca6424.h>
#include "ti_drivers_config.h"
#include "ti_drivers_open_close.h"
#include "ti_board_open_close.h"
#include <drivers/pinmux.h>
#include "FreeRTOS.h"
#include "task.h"
/* ========================================================================== */
/* Macros & Typedefs */
/* ========================================================================== */
/* Audio buffer settings */
#define APP_MCASP_AUDIO_BUFF_COUNT (4U)
#define APP_MCASP_AUDIO_BUFF_SIZE (2048U)
#define AUDIO_PROCESSING_TASK_PRI (configMAX_PRIORITIES-1)
#define AUDIO_PROCESSING_TASK_SIZE (65536U/sizeof(configSTACK_DEPTH_TYPE))
/* ========================================================================== */
/* Global Variables */
/* ========================================================================== */
/* Create buffers for transmit and Receive */
uint8_t gMcaspAudioBufferTx[APP_MCASP_AUDIO_BUFF_COUNT][APP_MCASP_AUDIO_BUFF_SIZE] __attribute__((aligned(256)));
uint8_t gMcaspAudioBufferRx[APP_MCASP_AUDIO_BUFF_COUNT][APP_MCASP_AUDIO_BUFF_SIZE] __attribute__((aligned(256)));
/* Create transaction objects for transmit and Receive */
MCASP_Transaction gMcaspAudioTxnTx[APP_MCASP_AUDIO_BUFF_COUNT] = {0};
MCASP_Transaction gMcaspAudioTxnRx[APP_MCASP_AUDIO_BUFF_COUNT] = {0};
StackType_t gAudioProcessingTaskStack[AUDIO_PROCESSING_TASK_SIZE] __attribute__((aligned(32)));
StaticTask_t gAudioProcessingTaskObj;
TaskHandle_t gAudioProcessingTask;
SemaphoreP_Object gMcaspTxStartSem;
MCASP_Transaction *current_transaction;
/* ========================================================================== */
/* Extern Function Declaration */
/* ========================================================================== */
int32_t Board_codecConfig(void);
/**
* @brief Audio Processing Task - Left Channel Muting Example
*
* This function demonstrates real-time stereo audio processing by muting the left channel.
*
* Audio Processing Flow:
* 1. Wait for RX buffer completion (new audio data available)
* 2. Invalidate cache to ensure fresh data from DMA
* 3. Process audio samples (mute left channel in this example)
* 4. Write back cache to make processed data visible to DMA
* 5. Submit processed buffer for TX (audio output)
*
* Stereo Sample Layout in Buffer:
* - buffer[0], buffer[2], buffer[4]... = Left channel samples (even indices)
* - buffer[1], buffer[3], buffer[5]... = Right channel samples (odd indices)
*
* @param args Task parameters (unused)
*/
void audio_processing(void *args)
{
MCASP_Handle mcaspHandle;
mcaspHandle = MCASP_getHandle(CONFIG_MCASP0);
while(1){
/* Wait for RX completion signal from interrupt callback */
SemaphoreP_pend(&gMcaspTxStartSem, SystemP_WAIT_FOREVER);
/* Cache invalidate: Ensure CPU sees fresh DMA data */
CacheP_inv(current_transaction->buf, APP_MCASP_AUDIO_BUFF_SIZE, CacheP_TYPE_ALL);
/* Cast buffer to 32-bit samples and get sample count */
uint32_t* buffer =(uint32_t*)(current_transaction->buf);
uint32_t cnt = current_transaction->count; /* Number of 32-bit samples */
uint32_t inc = 0;
/* Process stereo audio: Mute left channel (even indices) */
for (inc = 0; inc < cnt; inc+=2)
{
buffer[inc] = 0; /* Mute left channel */
/* buffer[inc+1] unchanged - right channel passes through */
}
/* Cache writeback: Make processed data visible to DMA */
CacheP_wb(current_transaction->buf, APP_MCASP_AUDIO_BUFF_SIZE, CacheP_TYPE_ALL);
/* Submit processed buffer for audio output */
MCASP_submitTx(mcaspHandle, current_transaction);
}
}
void mcasp_playback_main(void *args)
{
int32_t status = SystemP_SUCCESS;
uint32_t i;
MCASP_Handle mcaspHandle;
char valueChar;
#if defined (SOC_AM275X)
Pinmux_PerCfg_t i2cPinmuxConfig[] =
{
{
PIN_GPIO1_72,
( PIN_MODE(1) | PIN_INPUT_ENABLE | PIN_PULL_DIRECTION )
},
{PINMUX_END, 0U}
};
Pinmux_config(i2cPinmuxConfig, PINMUX_DOMAIN_ID_MAIN);
#endif
/* Configure codec */
status = Board_codecConfig();
DebugP_assert(status == SystemP_SUCCESS);
DebugP_log("[MCASP] Audio playback example started.\r\n");
mcaspHandle = MCASP_getHandle(CONFIG_MCASP0);
/* Construct Semaphore */
SemaphoreP_constructBinary(&gMcaspTxStartSem, 0);
/* Prepare and submit audio transaction transmit objects */
for (i = 0U; i < APP_MCASP_AUDIO_BUFF_COUNT; i++)
{
gMcaspAudioTxnTx[i].buf = (void*) &gMcaspAudioBufferTx[i][0];
gMcaspAudioTxnTx[i].count = APP_MCASP_AUDIO_BUFF_SIZE/4;
gMcaspAudioTxnTx[i].timeout = 0xFFFFFF;
MCASP_submitTx(mcaspHandle, &gMcaspAudioTxnTx[i]);
}
/* Prepare and submit audio transaction receive objects */
for (i = 0U; i < APP_MCASP_AUDIO_BUFF_COUNT; i++)
{
gMcaspAudioTxnRx[i].buf = (void*) &gMcaspAudioBufferRx[i][0];
gMcaspAudioTxnRx[i].count = APP_MCASP_AUDIO_BUFF_SIZE/4;
gMcaspAudioTxnRx[i].timeout = 0xFFFFFF;
MCASP_submitRx(mcaspHandle, &gMcaspAudioTxnRx[i]);
}
/* Trigger McASP receive operation */
status = MCASP_startTransferRx(mcaspHandle);
DebugP_assert(status == SystemP_SUCCESS);
/* Trigger McASP transmit operation */
status = MCASP_startTransferTx(mcaspHandle);
DebugP_assert(status == SystemP_SUCCESS);
/* This task is created at highest priority, it should create more tasks and then delete itself */
gAudioProcessingTask = xTaskCreateStatic( audio_processing, /* Pointer to the function that implements the task. */
"audio_processing", /* Text name for the task. This is to facilitate debugging only. */
AUDIO_PROCESSING_TASK_SIZE, /* Stack depth in units of StackType_t typically uint32_t on 32b CPUs */
NULL, /* We are not using the task parameter. */
AUDIO_PROCESSING_TASK_PRI, /* task priority, 0 is lowest priority, configMAX_PRIORITIES-1 is highest */
gAudioProcessingTaskStack, /* pointer to stack base */
&gAudioProcessingTaskObj ); /* pointer to statically allocated task object memory */
configASSERT(gAudioProcessingTask != NULL);
DebugP_log("Enter your response on UART terminal");
do
{
DebugP_log("\r\nStop the demo? (y/n) : ");
status = DebugP_scanf("%c", &valueChar);
DebugP_assert(status == SystemP_SUCCESS);
} while (valueChar != 'y');
vTaskDelete(gAudioProcessingTask);
MCASP_stopTransferTx(mcaspHandle);
MCASP_stopTransferRx(mcaspHandle);
DebugP_log("Exiting demo\r\n");
}
/**
* @brief McASP TX Completion Callback
*
* Called automatically by the McASP driver when a transmit (playback) buffer
* has been completely sent to the DAC.
*
* Function: Maintains continuous audio flow by resubmitting the completed
* TX buffer as an RX buffer for the next audio capture cycle.
*
* @param handle McASP driver handle
* @param transaction Completed TX transaction to reuse for RX
*/
void mcasp_txcb(MCASP_Handle handle,
MCASP_Transaction *transaction)
{
/* Resubmit completed TX buffer for next RX cycle */
MCASP_submitRx(handle, transaction);
}
/**
* @brief McASP RX Completion Callback
*
* Called automatically by the McASP driver when a receive (capture) buffer
* has been filled with fresh audio data from the ADC.
*
* Function: Signals the audio processing task that new audio data is available
* for processing. This is the trigger that starts each audio processing cycle.
*
* @param handle McASP driver handle
* @param transaction Completed RX transaction containing fresh audio data
*/
void mcasp_rxcb(MCASP_Handle handle,
MCASP_Transaction *transaction)
{
/* Store pointer to received buffer for processing task */
current_transaction = transaction;
/* Signal audio processing task that new data is ready */
SemaphoreP_post(&gMcaspTxStartSem);
}