Skip to content

File syn_audio.c

File List > audio > syn_audio.c

Go to the documentation of this file

#if __has_include("syn_config.h")
#include "syn_config.h"
#endif

#if !defined(SYN_USE_AUDIO) || SYN_USE_AUDIO

#include "../util/syn_assert.h"
#include "syn_audio.h"

#include <string.h>

SYN_Status syn_audio_init(SYN_Audio *audio, const SYN_Audio_Config *cfg)
{
    if (audio == NULL || cfg == NULL) {
        return SYN_INVALID_PARAM;
    }
    if (cfg->buf == NULL || cfg->half_size == 0U || cfg->buf_capacity < (2U * cfg->half_size)) {
        return SYN_INVALID_PARAM;
    }

    memset(audio, 0, sizeof(SYN_Audio));
    audio->cfg = *cfg;
    audio->active_half = 0U;
    audio->fill_pos = 0U;
    audio->running = false;
    audio->underrun_cnt = 0U;
    audio->frames_played = 0U;

    memset(cfg->buf, 0, cfg->buf_capacity * sizeof(int16_t));

    return SYN_OK;
}

SYN_Status syn_audio_start(SYN_Audio *audio)
{
    if (audio == NULL || audio->cfg.buf == NULL) {
        return SYN_INVALID_PARAM;
    }

    audio->running = true;
    audio->active_half = 0U;
    audio->fill_pos = 0U;

    if (audio->cfg.out_fn != NULL) {
        audio->cfg.out_fn(&audio->cfg.buf[0], audio->cfg.half_size, audio->cfg.ctx);
    }

    return SYN_OK;
}

SYN_Status syn_audio_stop(SYN_Audio *audio)
{
    if (audio == NULL) {
        return SYN_INVALID_PARAM;
    }

    audio->running = false;
    return SYN_OK;
}

size_t syn_audio_feed(SYN_Audio *audio, const int16_t *samples, size_t count)
{
    if (audio == NULL || samples == NULL || count == 0U) {
        return 0U;
    }

    size_t written = 0U;
    size_t half_len = audio->cfg.half_size;
    int16_t *half_buf = &audio->cfg.buf[audio->active_half * half_len];

    while (written < count) {
        if (audio->fill_pos < half_len) {
            half_buf[audio->fill_pos++] = samples[written++];
        } else {
            /* Current half buffer full — switch to alternate half */
            audio->active_half = (uint8_t)(1U - audio->active_half);
            half_buf = &audio->cfg.buf[audio->active_half * half_len];
            audio->fill_pos = 0U;

            if (audio->running && audio->cfg.out_fn != NULL) {
                audio->cfg.out_fn(half_buf, half_len, audio->cfg.ctx);
            }
        }
    }

    return written;
}

void syn_audio_isr_half(SYN_Audio *audio)
{
    if (audio == NULL || !audio->running) {
        return;
    }

    audio->frames_played++;
    if (audio->cfg.out_fn != NULL) {
        audio->cfg.out_fn(&audio->cfg.buf[0], audio->cfg.half_size, audio->cfg.ctx);
    }
}

void syn_audio_isr_complete(SYN_Audio *audio)
{
    if (audio == NULL || !audio->running) {
        return;
    }

    audio->frames_played++;
    if (audio->cfg.out_fn != NULL) {
        audio->cfg.out_fn(&audio->cfg.buf[audio->cfg.half_size], audio->cfg.half_size,
                          audio->cfg.ctx);
    }
}

#endif /* SYN_USE_AUDIO */