you C Coders might find this interesting, a virtually ZERO DELAY CW audio filter that totally cleans up a harsh CPO square wave click'N sidetone while shaping its edges

24 views
Skip to first unread message

Chuck Vaughn

unread,
Jul 25, 2024, 6:56:13 AMJul 25
to iCW - internet CW
Here is the LIVE DEMO video of the CW AUDIO BANDpass filter in action

chatGPT wrote its c CODE:
(see attached txt file)

COMPILEd by this command:
gcc -o bandpassfilter3 bandpassfilter3.c -ljack -lm

runs by command line   
./bandpassfilter3 192000 1024 800 5
arguments are
   sample rate of sound card
   buffer frames of the jack audio connection kit
   CW pitch
   "Q" of the bandpass filter

c code  looks like this:
#include <stdio.h>
#include <stdlib.h>
#include <jack/jack.h>
#include <math.h>
#include <signal.h>
#include <unistd.h>

#define PI 3.14159265358979323846

jack_port_t *input_port, *output_port;
jack_client_t *client;

typedef struct {
    double x1, x2, y1, y2;
} BiquadState;

typedef struct {
    double b0, b1, b2;
    double a0, a1, a2;
    BiquadState state;
} BiquadCoeffs;

BiquadCoeffs g_coeffs[2]; // Filter coefficients for a cascaded biquad filter

// Global variables for sample rate and running flag
int SAMPLE_RATE;
int running = 1; // Flag to control the main loop

// Signal handler for graceful termination
void signal_handler(int signum) {
    if (signum == SIGINT) {
        running = 0; // Set the flag to stop processing
    }
}

// Calculate filter coefficients
void calculate_coefficients(double f0, double Q) {
    double omega0 = 2.0 * PI * f0 / SAMPLE_RATE;
    double alpha = sin(omega0) / (2.0 * Q);
    double cos_omega0 = cos(omega0);

    // Low-pass filter coefficients (First biquad section)
    g_coeffs[0].b0 = (1 - cos_omega0) / 2;
    g_coeffs[0].b1 = 1 - cos_omega0;
    g_coeffs[0].b2 = (1 - cos_omega0) / 2;
    g_coeffs[0].a0 = 1 + alpha;
    g_coeffs[0].a1 = -2 * cos_omega0;
    g_coeffs[0].a2 = 1 - alpha;

    // High-pass filter coefficients (Second biquad section)
    g_coeffs[1].b0 = (1 + cos_omega0) / 2;
    g_coeffs[1].b1 = -(1 + cos_omega0);
    g_coeffs[1].b2 = (1 + cos_omega0) / 2;
    g_coeffs[1].a0 = 1 + alpha;
    g_coeffs[1].a1 = -2 * cos_omega0;
    g_coeffs[1].a2 = 1 - alpha;

    // Initialize state variables
    g_coeffs[0].state.x1 = g_coeffs[0].state.x2 = g_coeffs[0].state.y1 = g_coeffs[0].state.y2 = 0.0;
    g_coeffs[1].state.x1 = g_coeffs[1].state.x2 = g_coeffs[1].state.y1 = g_coeffs[1].state.y2 = 0.0;

    // Debugging: Print coefficients
    printf("Coefficients for filter:\n");
    printf("First Biquad Section (Low-pass):\n");
    printf("b0: %f, b1: %f, b2: %f\n", g_coeffs[0].b0, g_coeffs[0].b1, g_coeffs[0].b2);
    printf("a0: %f, a1: %f, a2: %f\n", g_coeffs[0].a0, g_coeffs[0].a1, g_coeffs[0].a2);
    printf("Second Biquad Section (High-pass):\n");
    printf("b0: %f, b1: %f, b2: %f\n", g_coeffs[1].b0, g_coeffs[1].b1, g_coeffs[1].b2);
    printf("a0: %f, a1: %f, a2: %f\n", g_coeffs[1].a0, g_coeffs[1].a1, g_coeffs[1].a2);
}

// Filter a single sample
double filter_sample(double sample) {
    // Apply the first biquad section (Low-pass)
    double y0 = (g_coeffs[0].b0 * sample + g_coeffs[0].b1 * g_coeffs[0].state.x1 + g_coeffs[0].b2 * g_coeffs[0].state.x2
                - g_coeffs[0].a1 * g_coeffs[0].state.y1 - g_coeffs[0].a2 * g_coeffs[0].state.y2) / g_coeffs[0].a0;

    // Update state variables for the first biquad section
    g_coeffs[0].state.x2 = g_coeffs[0].state.x1;
    g_coeffs[0].state.x1 = sample;
    g_coeffs[0].state.y2 = g_coeffs[0].state.y1;
    g_coeffs[0].state.y1 = y0;

    // Apply the second biquad section (High-pass)
    double y1 = (g_coeffs[1].b0 * y0 + g_coeffs[1].b1 * g_coeffs[1].state.x1 + g_coeffs[1].b2 * g_coeffs[1].state.x2
                - g_coeffs[1].a1 * g_coeffs[1].state.y1 - g_coeffs[1].a2 * g_coeffs[1].state.y2) / g_coeffs[1].a0;

    // Update state variables for the second biquad section
    g_coeffs[1].state.x2 = g_coeffs[1].state.x1;
    g_coeffs[1].state.x1 = y0;
    g_coeffs[1].state.y2 = g_coeffs[1].state.y1;
    g_coeffs[1].state.y1 = y1;

    return y1;
}

// JACK process callback function
int process(jack_nframes_t nframes, void *arg) {
    jack_default_audio_sample_t *in, *out;

    in = (jack_default_audio_sample_t *)jack_port_get_buffer(input_port, nframes);
    out = (jack_default_audio_sample_t *)jack_port_get_buffer(output_port, nframes);

    for (int i = 0; i < nframes; i++) {
        out[i] = filter_sample(in[i]);
    }

    return 0;
}

int main(int argc, char *argv[]) {
    if (argc < 4 || argc > 5) {
        fprintf(stderr, "Usage: %s <sample_rate> <buffer_size> <center_freq> [Q]\n", argv[0]);
        return 1;
    }

    SAMPLE_RATE = atoi(argv[1]);
    int buffer_size = atoi(argv[2]);
    double f0 = atof(argv[3]); // Center frequency
    double Q = (argc == 5) ? atof(argv[4]) : 8.0; // Quality factor, default to 8.0 if not provided

    if (SAMPLE_RATE <= 0 || buffer_size <= 0 || f0 <= 0 || Q <= 0) {
        fprintf(stderr, "Invalid arguments. Sample rate, buffer size, center frequency, and Q must be positive numbers.\n");
        return 1;
    }

    jack_status_t status;

    client = jack_client_open("bandpass_filter", JackNoStartServer, &status);
    if (client == NULL) {
        fprintf(stderr, "Failed to open JACK client\n");
        return 1;
    }

    if (status & JackServerStarted) {
        fprintf(stderr, "JACK server started\n");
    }

    if (status & JackNameNotUnique) {
        const char *client_name = jack_get_client_name(client);
        fprintf(stderr, "Unique name `%s' assigned\n", client_name);
    }

    input_port = jack_port_register(client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
    output_port = jack_port_register(client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
    if (input_port == NULL || output_port == NULL) {
        fprintf(stderr, "Failed to register JACK ports\n");
        jack_client_close(client);
        return 1;
    }

    calculate_coefficients(f0, Q);

    jack_set_process_callback(client, process, 0);

    if (jack_activate(client)) {
        fprintf(stderr, "Failed to activate JACK client\n");
        jack_client_close(client);
        return 1;
    }

    // Set up signal handler for graceful termination
    signal(SIGINT, signal_handler);

    printf("Press Ctrl+C to quit...\n");

    while (running) {
        // Main loop can be filled with additional logic if needed
        // For now, it just waits for the signal to terminate
        sleep(1);
    }

    // Attempt to close the JACK client gracefully
    if (jack_client_close(client)) {
        fprintf(stderr, "Error closing JACK client\n");
        return 1;
    }

    return 0;
}
bandpassfilter3.c

df7t...@gmail.com

unread,
Jul 28, 2024, 9:48:07 PMJul 28
to iCW - internet CW
Hello Chuck,

No C Coder here, but to take care, especially of hams using a paddle at high speeds, who need a near-zero latency of the monitor tone, is great!


TNX, 73
Tom

aa0hw

unread,
11:38 AM (1 hour ago) 11:38 AM
to iCW - internet CW
Here is some python code that does an even better job 
since it produces RAISED COSINE EDGES, (instead of R/C exponential from the earlier C CODE version)
and still takes less than 1ms to go from input to output
LIVE VIDEO DEMO HERE:


Reply all
Reply to author
Forward
0 new messages