diff options
| author | Doan Luu Duc Tai <doan@luuduct.ai> | 2026-09-08 14:46:47 -0500 |
|---|---|---|
| committer | Doan Luu Duc Tai <doan@luuduct.ai> | 2026-09-08 14:46:47 -0500 |
| commit | f34fb5042d40c186583f9e3009d8a1dd71d07d43 (patch) | |
| tree | 3f494f3723ebe9f173d0702a97509794f0ef85a5 /src/audio.c | |
Initial commitmain
Diffstat (limited to 'src/audio.c')
| -rw-r--r-- | src/audio.c | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/src/audio.c b/src/audio.c new file mode 100644 index 0000000..3db0d70 --- /dev/null +++ b/src/audio.c @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * vfetch - neofetch but the ascii part is a video + * + * Copyright (C) 2026 Doan Luu Duc Tai + * + */ + +#include "audio.h" +#include <stdatomic.h> +#include <stdlib.h> +#include <string.h> +#include "audio_defs.h" +#include "miniaudio.h" +#include "pl_mpeg.h" + +/* This warning is suppressed because this function is a callback that + * implements a specific signature required by the miniaudio library + * (ma_data_callback). The library itself dictates the order of parameters + * (pOutput, pInput), and we cannot change it. Since this function is not called + * directly by our own code but by the audio library, the risk of a developer + * accidentally swapping the parameters is nonexistent. */ +static void +data_callback(ma_device *pDevice, + /* NOLINTNEXTLINE(bugprone-easily-swappable-parameters) */ + void *pOutput, + const void *pInput, + ma_uint32 frame_count) { + (void)pInput; + AudioState *audio = (AudioState *)pDevice->pUserData; + float *out = (float *)pOutput; + ma_uint32 total_frames_written = 0; + + while (total_frames_written < frame_count) { + if (audio->buffer_index >= audio->buffer_count) { + plm_samples_t *samples = plm_decode_audio(audio->plm); + + if (!samples) { + (void)memset(out + ((size_t)total_frames_written * 2), + 0, + (frame_count - total_frames_written) * sizeof(float) * 2); + return; + } + + (void)memcpy(audio->sample_buffer, + samples->interleaved, + sizeof(float) * (size_t)samples->count * 2); + audio->buffer_index = 0; + audio->buffer_count = samples->count; + } + + ma_uint32 frames_to_copy = frame_count - total_frames_written; + ma_uint32 frames_available = audio->buffer_count - audio->buffer_index; + if (frames_to_copy > frames_available) { + frames_to_copy = frames_available; + } + + (void)memcpy(out + ((size_t)total_frames_written * 2), + audio->sample_buffer + ((size_t)audio->buffer_index * 2), + sizeof(float) * (size_t)frames_to_copy * 2); + + audio->buffer_index += frames_to_copy; + total_frames_written += frames_to_copy; + } + + atomic_fetch_add(&audio->frames_consumed, frame_count); +} + +#define K_MPG_SAMPLES_PER_FRAME 1152 +VfetchError initialize_audio(AppState *state, const char *audio_source_file) { + AudioState *audio = &state->audio_state; + audio->state = state; + audio->buffer_index = 0; + audio->buffer_count = 0; + audio->sample_buffer = NULL; + + audio->plm = plm_create_with_filename(audio_source_file); + if (!audio->plm || !plm_get_num_audio_streams(audio->plm)) { + return VFETCH_ERROR_MA_INIT; + } + + ma_device_config config = ma_device_config_init(ma_device_type_playback); + config.playback.format = ma_format_f32; + config.playback.channels = 2; + config.sampleRate = (ma_uint32)plm_get_samplerate(audio->plm); + config.dataCallback = data_callback; + config.pUserData = audio; + + if (ma_device_init(NULL, &config, &audio->device) != MA_SUCCESS) { + plm_destroy(audio->plm); + return VFETCH_ERROR_MA_INIT; + } + + audio->sample_buffer = malloc(sizeof(float) * K_MPG_SAMPLES_PER_FRAME * 2); + if (!audio->sample_buffer) { + return VFETCH_ERROR_MEMORY_ALLOCATION; + } + + ma_uint32 buffered_frames = + audio->device.playback.internalPeriodSizeInFrames * + audio->device.playback.internalPeriods; + if (audio->device.sampleRate > 0) { + audio->output_latency_seconds = + (double)buffered_frames / (double)audio->device.sampleRate; + } else { + audio->output_latency_seconds = 0.0; + } + + if (ma_device_start(&audio->device) != MA_SUCCESS) { + shutdown_audio(state); + return VFETCH_ERROR_MA_INIT; + } + + return VFETCH_SUCCESS; +} +#undef K_MPG_SAMPLES_PER_FRAME + +void shutdown_audio(AppState *state) { + ma_device_uninit(&state->audio_state.device); + if (state->audio_state.plm) { + plm_destroy(state->audio_state.plm); + } + if (state->audio_state.sample_buffer) { + free(state->audio_state.sample_buffer); + } +} |
