Update audio example?

Hi!

I'm interested in picking up C++ programming, and your fpl seemed like fun thing to start tinkering with :)

I got as far as trying to compile your Simple Audio Playback example here:

https://libfpl.org/docs/page_example_simple_audio.html

However that seems to be for an earlier version of fpl. I managed to patch it up so it at least compiles with v0.9.4 beta, but I couldn't get it to actually play audio (g++, cdt, Ubuntu 20.04). I included my the updated example code below, in case you can use it for something :)

If anyone has any ideas on why I don't get sound, I'll put my debugging attempts below.

The issue seems to be that thread #4 segfaults. Stepping into this line in the example:

1
if (fplPlayAudio() == fplAudioResultType_Success) {


I get to here:

1
fpl__WaitForAudioEvent(&audioState->startEvent);


...where if I "step in", thread #4 to segfaults at this line in fplMemoryClear:

1
FPL__MEMORY_CLEAR(uint64_t, mem, size, FPL__MEM_SHIFT_64, FPL__MEM_MASK_64);


and if I try to "step in" the program just exits with code 0.

I have several audio devices available if that matters, but just changing device doesn't seem to help.

As you can see in my updated example code below, I added a few lines (under "Find audio device", now commented out) that print out what audio devices are available. For me that gives:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Multiple audio devices available:
0: Playback/recording through the PulseAudio sound server
1: PulseAudio Sound Server
2: HDA Intel PCH, ALC3266 Analog
2.1 Surround output to Front and Subwoofer speakers
3: HDA Intel PCH, ALC3266 Analog
4.0 Surround output to Front and Rear speakers
4: HDA Intel PCH, ALC3266 Analog
4.1 Surround output to Front, Rear and Subwoofer speakers
5: HDA Intel PCH, ALC3266 Analog
5.0 Surround output to Front, Center and Rear speakers
6: HDA Intel PCH, ALC3266 Analog
5.1 Surround output to Front, Center, Rear and Subwoofer speakers
7: HDA Intel PCH, ALC3266 Analog
7.1 Surround output to Front, Center, Side, Rear and Woofer speakers
8: HDA Intel PCH, HDMI 0
HDMI Audio Output
9: HDA Intel PCH, HDMI 1
HDMI Audio Output
10: HDA Intel PCH, HDMI 2
HDMI Audio Output
11: HDA Intel PCH, HDMI 3
HDMI Audio Output
12: HDA Intel PCH, HDMI 4
HDMI Audio Output
13: HDA Intel PCH, HDMI 0
Direct sample mixing device
14: HDA Intel PCH, HDMI 1
Direct sample mixing device
15: HDA Intel PCH, HDMI 2
Direct sample mixing device
Using audio device: Playback/recording through the PulseAudio sound server


Here's the my updated example code:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
#define FPL_IMPLEMENTATION
#define FPL_NO_WINDOW
#include "final_platform_layer.h"
#include <math.h> // sinf

struct AudioTest {
    uint32_t toneHz;
    uint32_t toneVolume;
    uint32_t runningSampleIndex;
    uint32_t wavePeriod;
    bool useSquareWave;
};

static const float PI32 = 3.14159265359f;

static uint32_t FillAudioBuffer(const fplAudioDeviceFormat *nativeFormat, const uint32_t frameCount, void *outputSamples, void *userData) {
    AudioTest *audioTest = (AudioTest *)userData;
    fplAssert(audioTest != nullptr);
    fplAssert(nativeFormat->type == fplAudioFormatType_S16);
    uint32_t result = 0;
    int16_t *outSamples = (int16_t *)outputSamples;
    uint32_t halfWavePeriod = audioTest->wavePeriod / 2;
    for (uint32_t frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
        int16_t sampleValue;
        if (audioTest->useSquareWave) {
            sampleValue = ((audioTest->runningSampleIndex++ / halfWavePeriod) % 2) ? (int16_t)audioTest->toneVolume : -(int16_t)audioTest->toneVolume;
        } else {
            float t = 2.0f * PI32 * (float)audioTest->runningSampleIndex++ / (float)audioTest->wavePeriod;
            sampleValue = (int16_t)(sinf(t) * audioTest->toneVolume);
        }
        for (uint32_t channelIndex = 0; channelIndex < nativeFormat->channels; ++channelIndex) {
             *outSamples++ = sampleValue;
            ++result;
        }
    }
    return result;
}

int main(int argc, char **argv) {
    int result = -1;

    // Initialize to default settings which is 48 KHz and 2 Channels
    fplSettings settings;
    fplSetDefaultSettings(&settings);

    // Optionally overwrite audio settings if needed

    // Setup some state for the sine/square wave generation
    AudioTest audioTest = {};
    audioTest.toneHz = 256;
    audioTest.toneVolume = 1000;
    audioTest.wavePeriod = settings.audio.targetFormat.sampleRate / audioTest.toneHz;
    audioTest.useSquareWave = false;

    // Provide client read callback and optionally user data
    settings.audio.clientReadCallback = FillAudioBuffer;
    settings.audio.userData = &audioTest;
    settings.audio.targetFormat.type = fplAudioFormatType_S16;
    settings.audio.targetFormat.channels = 2;
    settings.audio.targetFormat.sampleRate = 48000;

    // Disable auto start/stop of audio playback
    settings.audio.startAuto = false;
    settings.audio.stopAuto = false;

    // Find audio device
    if (fplPlatformInit(fplInitFlags_Audio, &settings)) {
        fplAudioDeviceInfo audioDevices[16] = {};
        uint32_t deviceCount = fplGetAudioDevices(audioDevices, fplArrayCount(audioDevices));
        if (deviceCount > 0) {
        	//fplConsoleFormatOut("Multiple audio devices available:\n");
        	//for (int i = 0; i < deviceCount; i++) {
        	//	fplConsoleFormatOut("%i: %s\n", i, audioDevices[i].name);
        	//}
            settings.audio.targetDevice = audioDevices[0];
            fplConsoleFormatOut("Using audio device: %s\n", settings.audio.targetDevice.name);
        }
        fplPlatformRelease();
    }

    // Initialize the platform with audio enabled and the settings
    if (fplPlatformInit(fplInitFlags_Audio, &settings)) {
        // You can overwrite the client read callback and user data if you want to
        fplSetAudioClientReadCallback(FillAudioBuffer, &audioTest);
        // Start audio playback (This will start calling clientReadCallback regulary)
        if (fplPlayAudio() == fplAudioResultType_Success) {
        	fplConsoleFormatOut("Success!\n");
            // Print out the native audio format
            fplAudioDeviceFormat nativeFormat;
            fplGetAudioHardwareFormat(&nativeFormat);
            fplConsoleFormatOut("Audio with %lu KHz and %lu channels is playing, press any key to stop playback...\n", nativeFormat.sampleRate, nativeFormat.channels);
            // Wait for any key presses
            fplConsoleWaitForCharInput();
            // Stop audio playback
            fplStopAudio();
        }
        // Release the platform
        fplPlatformRelease();
        result = 0;
    }
    return(result);
}

Edited by Joel Hedlund on
Sorry for the late answer, but i didn´t seen your post :-(

Did you tried to compile and run the "FPL_Audio" from the demos folder?
Embarrassingly i haven´t updated the examples in the documentations for a very long time, so this may be totally broken.

But the issues regarding audio seems to be much more deeper and i wanted to investigate that more in detail.
On which CPU architecture have you tried to compile the example? Which distro are you using? Did you double checked the resulting buffer sizes and formats? Please set a breakpoint in fpl__AudioInitAlsa()

Also please checkout the "develop" branch, which have improved support for linux audio.

Looking forward to hear from you.

Greetings,
Final