Search code examples
caudiosdlmicrophone

SDL Audio capture does not detect any audio when recording with a microphone


I'm trying to record audio from my microphone and immediately play it back through my speakers. I fail to do just that; no audio seems to be detected by SDL.

Here's my code:

#include <SDL2/SDL.h>

#include <stdio.h>
#include <stdlib.h>

static SDL_AudioDeviceID input_dev;
static SDL_AudioDeviceID output_dev;

static Uint8* buffer = 0;
static int in_pos = 0;
static int out_pos = 0;

void cb_in(void *userdata, Uint8 *stream, int len) {
    // If len < 4, the printf below will probably segfault

    SDL_memcpy(stream, buffer + in_pos, len);
    in_pos += len;
    printf("IN:  %d\t%d %d %d %d\n", in_pos, stream[0], stream[1], stream[2], stream[3]);
}

void cb_out(void *userdata, Uint8 *stream, int len) {
    // If len < 4, the printf below will probably segfault

    if (out_pos >= in_pos) {
        // Output is way ahead of input; fill with emptiness
        memset(buffer + out_pos, 0, len * sizeof(Uint8));
        printf("OUT: %d\t(Empty)\n", out_pos);
    } else if (out_pos + len > in_pos) {
        // Output is reaching input; read until reaching input, and leave the rest empty
        memset(buffer + out_pos, 0, len * sizeof(Uint8));
        SDL_memcpy(buffer + out_pos, stream, in_pos - out_pos);
        out_pos = in_pos;
        printf("OUT: %d\t%d %d %d %d (Partial)\n", out_pos, stream[0], stream[1], stream[2], stream[3]);
    } else {
        // Input is way ahead of output; read as much as requested
        SDL_memcpy(buffer + out_pos, stream, len);
        out_pos += len;
        printf("OUT: %d\t%d %d %d %d\n", out_pos, stream[0], stream[1], stream[2], stream[3]);
    }

    // This is to make sure the output device works
    //for (int i = 0; i < len; i++)
    //    stream[i] = (Uint8) random();
}

int main() {
    SDL_Init(SDL_INIT_AUDIO);

    // 16Mb should be enough; the test lasts 5 seconds
    buffer = malloc(16777215);

    SDL_AudioSpec want_in, want_out, have_in, have_out;

    SDL_zero(want_out);
    want_out.freq = 44100;
    want_out.format = AUDIO_F32;
    want_out.channels = 1;
    want_out.samples = 1024;
    want_out.callback = cb_out;

    output_dev = SDL_OpenAudioDevice(NULL, 0, &want_out, &have_out, SDL_AUDIO_ALLOW_ANY_CHANGE);
    if (output_dev == 0) {
        SDL_Log("Failed to open output: %s", SDL_GetError());
        return 1;
    }

    SDL_zero(want_in);
    want_in.freq = 44100;
    want_in.format = AUDIO_F32;
    want_in.channels = 1;
    want_in.samples = 1024;
    want_in.callback = cb_in;

    input_dev = SDL_OpenAudioDevice(NULL, 1, &want_in, &have_in, SDL_AUDIO_ALLOW_ANY_CHANGE);
    if (input_dev == 0) {
        SDL_Log("Failed to open input: %s", SDL_GetError());
        return 1;
    }

    SDL_PauseAudioDevice(input_dev, 0);
    SDL_PauseAudioDevice(output_dev, 0);

    SDL_Delay(5000);

    SDL_CloseAudioDevice(output_dev);
    SDL_CloseAudioDevice(input_dev);
    free(buffer);
}

(Note: For the sake of shortness and readability, I did not put checks for every possible overflow.)

Upon compiling this code with gcc main.c -lSDL2 -I/usr/include/SDL2/ and executing it, the program runs fine; however, no audio seems to be picked up from my microphone. The printf's will show only zeroes.

I've already checked for these:

  1. It connects to the appropriate microphone. I listed all available devices with the snippet here (archive) after replacing the 0's with 1's (to detect capturing devices), and manually made the program pick that device specifically using SDL_GetAudioDeviceName(<number>, 1)
  2. My microphone is working well and is recording loud; I tested it with multiple other applications and no other have issues.

I'm using Ubuntu 20.04 with Pulseaudio 13.99.1.


Solution

  • SDL_memcpy is memcpy, meaning first argument is destination, and second argument is source. In both ouf your callbacks that order is wrong, your input callback just erases your stream data with what is initially stored in buffer (zeroes), and output callback don't output anything but instead copies garbage to buffer.

    Similarly your if (out_pos >= in_pos) branch should erase stream, not buffer.

    Note that your requested audio format is F32 (not guaranteed as you allow format changes), but your debug printfs interpret it as uint8. That will not affect audio playback, only how relevant output is.