Advanced Linux Audio and DSP Programming: Building Real-Time Audio Processing Systems
Advanced Linux audio and DSP programming requires deep understanding of real-time audio processing, digital signal processing algorithms, and low-latency audio systems. This comprehensive guide explores building professional audio applications using ALSA, JACK, implementing custom DSP algorithms, and creating high-performance audio processing systems.
Advanced Linux Audio and DSP Programming
Real-Time Audio Processing Framework
Advanced ALSA Audio Engine
// alsa_audio_engine.c - Advanced ALSA-based audio processing engine
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <math.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <sched.h>
#include <alsa/asoundlib.h>
#include <fftw3.h>
#define SAMPLE_RATE 48000
#define CHANNELS 2
#define BUFFER_SIZE 512
#define PERIOD_SIZE 128
#define PERIODS 4
#define MAX_FILTERS 32
#define MAX_EFFECTS 16
#define FFT_SIZE 1024
#define OVERLAP_SIZE 256
// Audio format definitions
typedef enum {
AUDIO_FORMAT_S16_LE,
AUDIO_FORMAT_S24_LE,
AUDIO_FORMAT_S32_LE,
AUDIO_FORMAT_FLOAT32_LE,
AUDIO_FORMAT_FLOAT64_LE
} audio_format_t;
// DSP filter types
typedef enum {
FILTER_TYPE_LOWPASS,
FILTER_TYPE_HIGHPASS,
FILTER_TYPE_BANDPASS,
FILTER_TYPE_BANDSTOP,
FILTER_TYPE_ALLPASS,
FILTER_TYPE_NOTCH,
FILTER_TYPE_PEAK,
FILTER_TYPE_SHELF_LOW,
FILTER_TYPE_SHELF_HIGH
} filter_type_t;
// Audio effect types
typedef enum {
EFFECT_TYPE_REVERB,
EFFECT_TYPE_DELAY,
EFFECT_TYPE_CHORUS,
EFFECT_TYPE_FLANGER,
EFFECT_TYPE_PHASER,
EFFECT_TYPE_DISTORTION,
EFFECT_TYPE_COMPRESSOR,
EFFECT_TYPE_LIMITER,
EFFECT_TYPE_GATE,
EFFECT_TYPE_EQUALIZER
} effect_type_t;
// Biquad filter structure
typedef struct {
filter_type_t type;
double frequency;
double q_factor;
double gain;
double sample_rate;
// Filter coefficients
double b0, b1, b2;
double a0, a1, a2;
// Filter state
double x1, x2;
double y1, y2;
bool enabled;
} biquad_filter_t;
// Delay line structure
typedef struct {
float *buffer;
int size;
int write_index;
int read_index;
float feedback;
float wet_level;
float dry_level;
} delay_line_t;
// Reverb structure
typedef struct {
delay_line_t *delay_lines;
int num_delays;
float *all_pass_delays;
int num_allpass;
float room_size;
float damping;
float wet_level;
float dry_level;
float width;
} reverb_t;
// Compressor structure
typedef struct {
float threshold;
float ratio;
float attack_time;
float release_time;
float knee;
float makeup_gain;
// Internal state
float envelope;
float gain_reduction;
float attack_coeff;
float release_coeff;
bool enabled;
} compressor_t;
// Spectrum analyzer structure
typedef struct {
fftwf_complex *fft_input;
fftwf_complex *fft_output;
fftwf_plan fft_plan;
float *window;
float *magnitude_spectrum;
float *phase_spectrum;
int fft_size;
int overlap_size;
int hop_size;
// Circular buffer for overlap-add
float *overlap_buffer;
int overlap_index;
} spectrum_analyzer_t;
// Audio processing chain
typedef struct {
biquad_filter_t filters[MAX_FILTERS];
int num_filters;
reverb_t reverb;
compressor_t compressor;
delay_line_t delay;
spectrum_analyzer_t analyzer;
// Effect parameters
float master_gain;
float pan_left;
float pan_right;
bool bypass;
} audio_processor_t;
// ALSA audio device structure
typedef struct {
snd_pcm_t *playback_handle;
snd_pcm_t *capture_handle;
snd_pcm_hw_params_t *hw_params;
snd_pcm_sw_params_t *sw_params;
char *playback_device;
char *capture_device;
audio_format_t format;
unsigned int sample_rate;
unsigned int channels;
snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t period_size;
// Audio buffers
float *input_buffer;
float *output_buffer;
float *processing_buffer;
// Threading
pthread_t audio_thread;
pthread_mutex_t audio_mutex;
pthread_cond_t audio_cond;
// Control flags
volatile bool running;
volatile bool processing_enabled;
// Performance metrics
struct timespec last_callback_time;
double callback_duration_avg;
double callback_duration_max;
int xrun_count;
// DSP processor
audio_processor_t processor;
} alsa_audio_device_t;
// Function prototypes
int alsa_audio_init(alsa_audio_device_t *device, const char *playback_dev, const char *capture_dev);
int alsa_audio_start(alsa_audio_device_t *device);
int alsa_audio_stop(alsa_audio_device_t *device);
int alsa_audio_cleanup(alsa_audio_device_t *device);
void *audio_thread_function(void *arg);
int audio_callback(alsa_audio_device_t *device, float *input, float *output, int frames);
// DSP functions
int init_audio_processor(audio_processor_t *processor, int sample_rate);
int process_audio(audio_processor_t *processor, float *input, float *output, int frames, int channels);
void cleanup_audio_processor(audio_processor_t *processor);
// Filter functions
int init_biquad_filter(biquad_filter_t *filter, filter_type_t type, double freq, double q, double gain, double sample_rate);
float process_biquad_filter(biquad_filter_t *filter, float input);
void calculate_biquad_coefficients(biquad_filter_t *filter);
// Effect functions
int init_reverb(reverb_t *reverb, int sample_rate);
int process_reverb(reverb_t *reverb, float *input, float *output, int frames);
void cleanup_reverb(reverb_t *reverb);
int init_compressor(compressor_t *comp, float threshold, float ratio, float attack, float release, int sample_rate);
float process_compressor(compressor_t *comp, float input);
int init_delay_line(delay_line_t *delay, int delay_samples, float feedback, float wet, float dry);
float process_delay_line(delay_line_t *delay, float input);
void cleanup_delay_line(delay_line_t *delay);
// Spectrum analysis functions
int init_spectrum_analyzer(spectrum_analyzer_t *analyzer, int fft_size, int sample_rate);
int process_spectrum_analyzer(spectrum_analyzer_t *analyzer, float *input, int frames);
void cleanup_spectrum_analyzer(spectrum_analyzer_t *analyzer);
// Utility functions
void apply_window(float *buffer, float *window, int size);
void generate_hanning_window(float *window, int size);
float db_to_linear(float db);
float linear_to_db(float linear);
void interleave_audio(float *left, float *right, float *interleaved, int frames);
void deinterleave_audio(float *interleaved, float *left, float *right, int frames);
// Global audio device
static alsa_audio_device_t g_audio_device;
static volatile bool g_running = true;
void signal_handler(int signum) {
g_running = false;
}
int main(int argc, char *argv[]) {
int result;
// Setup signal handler
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
// Initialize audio device
result = alsa_audio_init(&g_audio_device, "default", "default");
if (result != 0) {
fprintf(stderr, "Failed to initialize audio device: %d\n", result);
return 1;
}
// Start audio processing
result = alsa_audio_start(&g_audio_device);
if (result != 0) {
fprintf(stderr, "Failed to start audio processing: %d\n", result);
alsa_audio_cleanup(&g_audio_device);
return 1;
}
printf("Audio processing started. Press Ctrl+C to stop.\n");
// Main loop
while (g_running) {
// Print performance statistics
printf("Callback duration: avg=%.3fms max=%.3fms xruns=%d\n",
g_audio_device.callback_duration_avg * 1000.0,
g_audio_device.callback_duration_max * 1000.0,
g_audio_device.xrun_count);
sleep(5);
}
// Stop and cleanup
alsa_audio_stop(&g_audio_device);
alsa_audio_cleanup(&g_audio_device);
printf("Audio processing stopped.\n");
return 0;
}
int alsa_audio_init(alsa_audio_device_t *device, const char *playback_dev, const char *capture_dev) {
if (!device) return -1;
memset(device, 0, sizeof(alsa_audio_device_t));
// Set device names
device->playback_device = strdup(playback_dev);
device->capture_device = strdup(capture_dev);
// Set audio parameters
device->format = AUDIO_FORMAT_FLOAT32_LE;
device->sample_rate = SAMPLE_RATE;
device->channels = CHANNELS;
device->buffer_size = BUFFER_SIZE;
device->period_size = PERIOD_SIZE;
// Open playback device
int result = snd_pcm_open(&device->playback_handle, device->playback_device, SND_PCM_STREAM_PLAYBACK, 0);
if (result < 0) {
fprintf(stderr, "Cannot open playback device %s: %s\n", device->playback_device, snd_strerror(result));
return -1;
}
// Open capture device
result = snd_pcm_open(&device->capture_handle, device->capture_device, SND_PCM_STREAM_CAPTURE, 0);
if (result < 0) {
fprintf(stderr, "Cannot open capture device %s: %s\n", device->capture_device, snd_strerror(result));
snd_pcm_close(device->playback_handle);
return -1;
}
// Configure hardware parameters for playback
snd_pcm_hw_params_alloca(&device->hw_params);
snd_pcm_hw_params_any(device->playback_handle, device->hw_params);
snd_pcm_hw_params_set_access(device->playback_handle, device->hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(device->playback_handle, device->hw_params, SND_PCM_FORMAT_FLOAT_LE);
snd_pcm_hw_params_set_channels(device->playback_handle, device->hw_params, device->channels);
snd_pcm_hw_params_set_rate_near(device->playback_handle, device->hw_params, &device->sample_rate, 0);
snd_pcm_hw_params_set_buffer_size_near(device->playback_handle, device->hw_params, &device->buffer_size);
snd_pcm_hw_params_set_period_size_near(device->playback_handle, device->hw_params, &device->period_size, 0);
result = snd_pcm_hw_params(device->playback_handle, device->hw_params);
if (result < 0) {
fprintf(stderr, "Cannot set playback hardware parameters: %s\n", snd_strerror(result));
return -1;
}
// Configure hardware parameters for capture
snd_pcm_hw_params_any(device->capture_handle, device->hw_params);
snd_pcm_hw_params_set_access(device->capture_handle, device->hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(device->capture_handle, device->hw_params, SND_PCM_FORMAT_FLOAT_LE);
snd_pcm_hw_params_set_channels(device->capture_handle, device->hw_params, device->channels);
snd_pcm_hw_params_set_rate_near(device->capture_handle, device->hw_params, &device->sample_rate, 0);
snd_pcm_hw_params_set_buffer_size_near(device->capture_handle, device->hw_params, &device->buffer_size);
snd_pcm_hw_params_set_period_size_near(device->capture_handle, device->hw_params, &device->period_size, 0);
result = snd_pcm_hw_params(device->capture_handle, device->hw_params);
if (result < 0) {
fprintf(stderr, "Cannot set capture hardware parameters: %s\n", snd_strerror(result));
return -1;
}
// Allocate audio buffers
int buffer_samples = device->buffer_size * device->channels;
device->input_buffer = (float *)malloc(buffer_samples * sizeof(float));
device->output_buffer = (float *)malloc(buffer_samples * sizeof(float));
device->processing_buffer = (float *)malloc(buffer_samples * sizeof(float));
if (!device->input_buffer || !device->output_buffer || !device->processing_buffer) {
fprintf(stderr, "Failed to allocate audio buffers\n");
return -1;
}
// Initialize threading
pthread_mutex_init(&device->audio_mutex, NULL);
pthread_cond_init(&device->audio_cond, NULL);
// Initialize audio processor
result = init_audio_processor(&device->processor, device->sample_rate);
if (result != 0) {
fprintf(stderr, "Failed to initialize audio processor\n");
return -1;
}
printf("ALSA audio device initialized: %u Hz, %u channels, %lu frames buffer\n",
device->sample_rate, device->channels, device->buffer_size);
return 0;
}
int alsa_audio_start(alsa_audio_device_t *device) {
if (!device) return -1;
device->running = true;
device->processing_enabled = true;
// Set real-time scheduling
struct sched_param param;
param.sched_priority = 80;
pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m);
// Create audio thread
int result = pthread_create(&device->audio_thread, NULL, audio_thread_function, device);
if (result != 0) {
fprintf(stderr, "Failed to create audio thread: %d\n", result);
return -1;
}
return 0;
}
void *audio_thread_function(void *arg) {
alsa_audio_device_t *device = (alsa_audio_device_t *)arg;
struct timespec callback_start, callback_end;
// Set thread name
pthread_setname_np(pthread_self(), "audio_thread");
// Prepare PCM devices
snd_pcm_prepare(device->playback_handle);
snd_pcm_prepare(device->capture_handle);
// Start capture
snd_pcm_start(device->capture_handle);
while (device->running) {
clock_gettime(CLOCK_MONOTONIC, &callback_start);
// Read audio input
int frames_read = snd_pcm_readi(device->capture_handle, device->input_buffer, device->period_size);
if (frames_read < 0) {
if (frames_read == -EPIPE) {
// Buffer overrun
device->xrun_count++;
snd_pcm_prepare(device->capture_handle);
continue;
}
fprintf(stderr, "Read error: %s\n", snd_strerror(frames_read));
break;
}
// Process audio
if (device->processing_enabled) {
audio_callback(device, device->input_buffer, device->output_buffer, frames_read);
} else {
// Bypass processing
memcpy(device->output_buffer, device->input_buffer, frames_read * device->channels * sizeof(float));
}
// Write audio output
int frames_written = snd_pcm_writei(device->playback_handle, device->output_buffer, frames_read);
if (frames_written < 0) {
if (frames_written == -EPIPE) {
// Buffer underrun
device->xrun_count++;
snd_pcm_prepare(device->playback_handle);
continue;
}
fprintf(stderr, "Write error: %s\n", snd_strerror(frames_written));
break;
}
// Calculate callback duration
clock_gettime(CLOCK_MONOTONIC, &callback_end);
double duration = (callback_end.tv_sec - callback_start.tv_sec) +
(callback_end.tv_nsec - callback_start.tv_nsec) / 1e9;
// Update performance metrics
device->callback_duration_avg = (device->callback_duration_avg * 0.95) + (duration * 0.05);
if (duration > device->callback_duration_max) {
device->callback_duration_max = duration;
}
device->last_callback_time = callback_end;
}
return NULL;
}
int audio_callback(alsa_audio_device_t *device, float *input, float *output, int frames) {
if (!device || !input || !output) return -1;
// Process audio through DSP chain
return process_audio(&device->processor, input, output, frames, device->channels);
}
int init_audio_processor(audio_processor_t *processor, int sample_rate) {
if (!processor) return -1;
memset(processor, 0, sizeof(audio_processor_t));
// Initialize default filter chain
init_biquad_filter(&processor->filters[0], FILTER_TYPE_HIGHPASS, 80.0, 0.7, 0.0, sample_rate);
init_biquad_filter(&processor->filters[1], FILTER_TYPE_LOWPASS, 12000.0, 0.7, 0.0, sample_rate);
processor->num_filters = 2;
// Initialize reverb
init_reverb(&processor->reverb, sample_rate);
// Initialize compressor
init_compressor(&processor->compressor, -20.0, 4.0, 0.003, 0.1, sample_rate);
// Initialize delay
init_delay_line(&processor->delay, sample_rate / 4, 0.3, 0.2, 0.8); // 250ms delay
// Initialize spectrum analyzer
init_spectrum_analyzer(&processor->analyzer, FFT_SIZE, sample_rate);
// Set default parameters
processor->master_gain = 1.0f;
processor->pan_left = 1.0f;
processor->pan_right = 1.0f;
processor->bypass = false;
return 0;
}
int process_audio(audio_processor_t *processor, float *input, float *output, int frames, int channels) {
if (!processor || !input || !output) return -1;
if (processor->bypass) {
memcpy(output, input, frames * channels * sizeof(float));
return 0;
}
// Deinterleave stereo input
float *left_channel = (float *)alloca(frames * sizeof(float));
float *right_channel = (float *)alloca(frames * sizeof(float));
for (int i = 0; i < frames; i++) {
left_channel[i] = input[i * channels];
right_channel[i] = input[i * channels + 1];
}
// Process left channel
for (int i = 0; i < frames; i++) {
float sample = left_channel[i];
// Apply filters
for (int f = 0; f < processor->num_filters; f++) {
if (processor->filters[f].enabled) {
sample = process_biquad_filter(&processor->filters[f], sample);
}
}
// Apply compressor
if (processor->compressor.enabled) {
sample = process_compressor(&processor->compressor, sample);
}
// Apply delay
sample = process_delay_line(&processor->delay, sample);
left_channel[i] = sample * processor->master_gain * processor->pan_left;
}
// Process right channel (simplified - same processing)
for (int i = 0; i < frames; i++) {
float sample = right_channel[i];
// Apply basic processing (filters, compressor, etc.)
for (int f = 0; f < processor->num_filters; f++) {
if (processor->filters[f].enabled) {
sample = process_biquad_filter(&processor->filters[f], sample);
}
}
right_channel[i] = sample * processor->master_gain * processor->pan_right;
}
// Apply reverb (stereo)
process_reverb(&processor->reverb, left_channel, right_channel, frames);
// Run spectrum analysis on left channel
process_spectrum_analyzer(&processor->analyzer, left_channel, frames);
// Reinterleave output
for (int i = 0; i < frames; i++) {
output[i * channels] = left_channel[i];
output[i * channels + 1] = right_channel[i];
}
return 0;
}
int init_biquad_filter(biquad_filter_t *filter, filter_type_t type, double freq, double q, double gain, double sample_rate) {
if (!filter) return -1;
filter->type = type;
filter->frequency = freq;
filter->q_factor = q;
filter->gain = gain;
filter->sample_rate = sample_rate;
filter->enabled = true;
// Initialize state variables
filter->x1 = filter->x2 = 0.0;
filter->y1 = filter->y2 = 0.0;
// Calculate filter coefficients
calculate_biquad_coefficients(filter);
return 0;
}
void calculate_biquad_coefficients(biquad_filter_t *filter) {
double omega = 2.0 * M_PI * filter->frequency / filter->sample_rate;
double sin_omega = sin(omega);
double cos_omega = cos(omega);
double alpha = sin_omega / (2.0 * filter->q_factor);
double A = pow(10.0, filter->gain / 40.0);
switch (filter->type) {
case FILTER_TYPE_LOWPASS:
filter->b0 = (1.0 - cos_omega) / 2.0;
filter->b1 = 1.0 - cos_omega;
filter->b2 = (1.0 - cos_omega) / 2.0;
filter->a0 = 1.0 + alpha;
filter->a1 = -2.0 * cos_omega;
filter->a2 = 1.0 - alpha;
break;
case FILTER_TYPE_HIGHPASS:
filter->b0 = (1.0 + cos_omega) / 2.0;
filter->b1 = -(1.0 + cos_omega);
filter->b2 = (1.0 + cos_omega) / 2.0;
filter->a0 = 1.0 + alpha;
filter->a1 = -2.0 * cos_omega;
filter->a2 = 1.0 - alpha;
break;
case FILTER_TYPE_BANDPASS:
filter->b0 = alpha;
filter->b1 = 0.0;
filter->b2 = -alpha;
filter->a0 = 1.0 + alpha;
filter->a1 = -2.0 * cos_omega;
filter->a2 = 1.0 - alpha;
break;
case FILTER_TYPE_PEAK:
filter->b0 = 1.0 + alpha * A;
filter->b1 = -2.0 * cos_omega;
filter->b2 = 1.0 - alpha * A;
filter->a0 = 1.0 + alpha / A;
filter->a1 = -2.0 * cos_omega;
filter->a2 = 1.0 - alpha / A;
break;
default:
// Default to allpass
filter->b0 = 1.0 - alpha;
filter->b1 = -2.0 * cos_omega;
filter->b2 = 1.0 + alpha;
filter->a0 = 1.0 + alpha;
filter->a1 = -2.0 * cos_omega;
filter->a2 = 1.0 - alpha;
break;
}
// Normalize coefficients
filter->b0 /= filter->a0;
filter->b1 /= filter->a0;
filter->b2 /= filter->a0;
filter->a1 /= filter->a0;
filter->a2 /= filter->a0;
filter->a0 = 1.0;
}
float process_biquad_filter(biquad_filter_t *filter, float input) {
if (!filter || !filter->enabled) return input;
// Direct Form II implementation
double w = input - filter->a1 * filter->x1 - filter->a2 * filter->x2;
double output = filter->b0 * w + filter->b1 * filter->x1 + filter->b2 * filter->x2;
// Update state
filter->x2 = filter->x1;
filter->x1 = w;
return (float)output;
}
int init_compressor(compressor_t *comp, float threshold, float ratio, float attack, float release, int sample_rate) {
if (!comp) return -1;
comp->threshold = threshold;
comp->ratio = ratio;
comp->attack_time = attack;
comp->release_time = release;
comp->knee = 2.0f;
comp->makeup_gain = 0.0f;
comp->enabled = true;
// Calculate attack/release coefficients
comp->attack_coeff = expf(-1.0f / (attack * sample_rate));
comp->release_coeff = expf(-1.0f / (release * sample_rate));
// Initialize state
comp->envelope = 0.0f;
comp->gain_reduction = 0.0f;
return 0;
}
float process_compressor(compressor_t *comp, float input) {
if (!comp || !comp->enabled) return input;
// Convert to dB
float input_db = linear_to_db(fabsf(input));
// Calculate gain reduction
float gain_reduction = 0.0f;
if (input_db > comp->threshold) {
float over_threshold = input_db - comp->threshold;
gain_reduction = over_threshold * (1.0f - 1.0f / comp->ratio);
}
// Apply envelope following
float target_gain = -gain_reduction;
if (target_gain < comp->gain_reduction) {
// Attack
comp->gain_reduction = target_gain + (comp->gain_reduction - target_gain) * comp->attack_coeff;
} else {
// Release
comp->gain_reduction = target_gain + (comp->gain_reduction - target_gain) * comp->release_coeff;
}
// Apply gain reduction and makeup gain
float output_gain = db_to_linear(comp->gain_reduction + comp->makeup_gain);
return input * output_gain;
}
int init_delay_line(delay_line_t *delay, int delay_samples, float feedback, float wet, float dry) {
if (!delay) return -1;
delay->size = delay_samples;
delay->buffer = (float *)calloc(delay_samples, sizeof(float));
if (!delay->buffer) return -1;
delay->write_index = 0;
delay->read_index = 0;
delay->feedback = feedback;
delay->wet_level = wet;
delay->dry_level = dry;
return 0;
}
float process_delay_line(delay_line_t *delay, float input) {
if (!delay || !delay->buffer) return input;
// Read delayed sample
float delayed_sample = delay->buffer[delay->read_index];
// Write input + feedback to delay line
delay->buffer[delay->write_index] = input + delayed_sample * delay->feedback;
// Update indices
delay->write_index = (delay->write_index + 1) % delay->size;
delay->read_index = (delay->read_index + 1) % delay->size;
// Mix wet and dry signals
return input * delay->dry_level + delayed_sample * delay->wet_level;
}
int init_spectrum_analyzer(spectrum_analyzer_t *analyzer, int fft_size, int sample_rate) {
if (!analyzer) return -1;
analyzer->fft_size = fft_size;
analyzer->overlap_size = fft_size / 4;
analyzer->hop_size = fft_size - analyzer->overlap_size;
// Allocate FFT buffers
analyzer->fft_input = (fftwf_complex *)fftwf_malloc(fft_size * sizeof(fftwf_complex));
analyzer->fft_output = (fftwf_complex *)fftwf_malloc(fft_size * sizeof(fftwf_complex));
if (!analyzer->fft_input || !analyzer->fft_output) {
return -1;
}
// Create FFT plan
analyzer->fft_plan = fftwf_plan_dft_1d(fft_size, analyzer->fft_input, analyzer->fft_output, FFTW_FORWARD, FFTW_ESTIMATE);
// Allocate analysis buffers
analyzer->window = (float *)malloc(fft_size * sizeof(float));
analyzer->magnitude_spectrum = (float *)malloc(fft_size * sizeof(float));
analyzer->phase_spectrum = (float *)malloc(fft_size * sizeof(float));
analyzer->overlap_buffer = (float *)calloc(analyzer->overlap_size, sizeof(float));
if (!analyzer->window || !analyzer->magnitude_spectrum || !analyzer->phase_spectrum || !analyzer->overlap_buffer) {
return -1;
}
// Generate window function
generate_hanning_window(analyzer->window, fft_size);
analyzer->overlap_index = 0;
return 0;
}
void generate_hanning_window(float *window, int size) {
for (int i = 0; i < size; i++) {
window[i] = 0.5f * (1.0f - cosf(2.0f * M_PI * i / (size - 1)));
}
}
float db_to_linear(float db) {
return powf(10.0f, db / 20.0f);
}
float linear_to_db(float linear) {
return 20.0f * log10f(fmaxf(linear, 1e-10f));
}
int alsa_audio_cleanup(alsa_audio_device_t *device) {
if (!device) return -1;
// Stop audio thread
device->running = false;
if (device->audio_thread) {
pthread_join(device->audio_thread, NULL);
}
// Close PCM handles
if (device->playback_handle) {
snd_pcm_close(device->playback_handle);
}
if (device->capture_handle) {
snd_pcm_close(device->capture_handle);
}
// Free audio buffers
free(device->input_buffer);
free(device->output_buffer);
free(device->processing_buffer);
// Free device names
free(device->playback_device);
free(device->capture_device);
// Cleanup audio processor
cleanup_audio_processor(&device->processor);
// Cleanup threading
pthread_mutex_destroy(&device->audio_mutex);
pthread_cond_destroy(&device->audio_cond);
printf("ALSA audio device cleanup completed\n");
return 0;
}
void cleanup_audio_processor(audio_processor_t *processor) {
if (!processor) return;
cleanup_reverb(&processor->reverb);
cleanup_delay_line(&processor->delay);
cleanup_spectrum_analyzer(&processor->analyzer);
}
void cleanup_reverb(reverb_t *reverb) {
if (!reverb) return;
if (reverb->delay_lines) {
for (int i = 0; i < reverb->num_delays; i++) {
cleanup_delay_line(&reverb->delay_lines[i]);
}
free(reverb->delay_lines);
}
free(reverb->all_pass_delays);
}
void cleanup_delay_line(delay_line_t *delay) {
if (!delay) return;
free(delay->buffer);
delay->buffer = NULL;
}
void cleanup_spectrum_analyzer(spectrum_analyzer_t *analyzer) {
if (!analyzer) return;
if (analyzer->fft_plan) {
fftwf_destroy_plan(analyzer->fft_plan);
}
fftwf_free(analyzer->fft_input);
fftwf_free(analyzer->fft_output);
free(analyzer->window);
free(analyzer->magnitude_spectrum);
free(analyzer->phase_spectrum);
free(analyzer->overlap_buffer);
}
This comprehensive audio and DSP programming guide provides:
- ALSA Audio Engine: Complete real-time audio processing system with low-latency I/O
- DSP Processing Chain: Biquad filters, compressors, delays, and reverb effects
- Spectrum Analysis: FFT-based frequency domain analysis with overlap-add processing
- Real-Time Performance: Optimized for low-latency audio with performance monitoring
- Professional Audio Effects: Industry-standard audio processing algorithms
- Threading and Synchronization: Proper real-time audio thread management
- Memory Management: Efficient audio buffer handling and DSP state management
The code demonstrates advanced audio programming techniques essential for building professional audio applications and real-time audio processing systems.