13#include <ascii-chat/session/audio.h>
14#include <ascii-chat/common.h>
15#include <ascii-chat/audio/audio.h>
16#include <ascii-chat/asciichat_errno.h>
27#define SESSION_AUDIO_MAX_SOURCES 32
76 session_audio_ctx_t *ctx = SAFE_CALLOC(1,
sizeof(session_audio_ctx_t), session_audio_ctx_t *);
78 ctx->is_host = is_host;
82 asciichat_error_t result =
audio_init(&ctx->audio_ctx);
83 if (result != ASCIICHAT_OK) {
84 log_error(
"Failed to initialize audio context: %d", result);
92 ctx->mix_buffer_size = AUDIO_BUFFER_SIZE * 4;
93 ctx->mix_buffer = SAFE_CALLOC(ctx->mix_buffer_size,
sizeof(
float),
float *);
97 ctx->sources[i].source_id = 0;
98 ctx->sources[i].active =
false;
99 ctx->sources[i].buffer = NULL;
101 ctx->source_count = 0;
104 ctx->initialized =
true;
122 if (ctx->sources[i].buffer) {
124 ctx->sources[i].buffer = NULL;
129 if (ctx->mix_buffer) {
130 SAFE_FREE(ctx->mix_buffer);
137 ctx->initialized =
false;
146 if (!ctx || !ctx->initialized) {
147 return SET_ERRNO(ERROR_INVALID_PARAM,
"session_audio_start_capture: invalid context");
155 if (!ctx || !ctx->initialized) {
156 return SET_ERRNO(ERROR_INVALID_PARAM,
"session_audio_start_playback: invalid context");
164 if (!ctx || !ctx->initialized) {
165 return SET_ERRNO(ERROR_INVALID_PARAM,
"session_audio_start_duplex: invalid context");
173 if (result == ASCIICHAT_OK) {
181 if (!ctx || !ctx->initialized || !ctx->running) {
186 ctx->running =
false;
190 if (!ctx || !ctx->initialized) {
201 if (!ctx || !ctx->initialized || !buffer || num_samples == 0) {
205 asciichat_error_t result =
audio_read_samples(&ctx->audio_ctx, buffer, (
int)num_samples);
206 if (result != ASCIICHAT_OK) {
214 if (!ctx || !ctx->initialized || !buffer) {
215 return SET_ERRNO(ERROR_INVALID_PARAM,
"session_audio_write_playback: invalid parameter");
226 if (!ctx || !ctx->initialized) {
227 return SET_ERRNO(ERROR_INVALID_PARAM,
"session_audio_add_source: invalid context");
231 return SET_ERRNO(ERROR_INVALID_STATE,
"session_audio_add_source: not a host context");
236 if (ctx->sources[i].active && ctx->sources[i].source_id == source_id) {
243 if (!ctx->sources[i].active) {
244 ctx->sources[i].source_id = source_id;
245 ctx->sources[i].active =
true;
247 if (!ctx->sources[i].buffer) {
248 ctx->sources[i].active =
false;
249 return SET_ERRNO(ERROR_MEMORY,
"Failed to create audio buffer for source");
256 return SET_ERRNO(ERROR_RESOURCE_EXHAUSTED,
"Maximum audio sources reached");
260 if (!ctx || !ctx->initialized || !ctx->is_host) {
265 if (ctx->sources[i].active && ctx->sources[i].source_id == source_id) {
266 if (ctx->sources[i].buffer) {
268 ctx->sources[i].buffer = NULL;
270 ctx->sources[i].active =
false;
271 ctx->sources[i].source_id = 0;
279 size_t num_samples) {
280 if (!ctx || !ctx->initialized || !samples) {
281 return SET_ERRNO(ERROR_INVALID_PARAM,
"session_audio_write_source: invalid parameter");
285 return SET_ERRNO(ERROR_INVALID_STATE,
"session_audio_write_source: not a host context");
290 if (ctx->sources[i].active && ctx->sources[i].source_id == source_id) {
291 if (ctx->sources[i].buffer) {
294 return SET_ERRNO(ERROR_INVALID_STATE,
"Source buffer not initialized");
298 return SET_ERRNO(ERROR_NOT_FOUND,
"Audio source not found: %u", source_id);
302 if (!ctx || !ctx->initialized || !output || num_samples == 0) {
311 memset(output, 0, num_samples *
sizeof(
float));
314 if (!ctx->mix_buffer || num_samples > ctx->mix_buffer_size) {
318 int sources_mixed = 0;
322 if (!ctx->sources[i].active || ctx->sources[i].source_id == exclude_id) {
326 if (!ctx->sources[i].buffer) {
333 if (samples_read == 0) {
338 for (
size_t j = 0; j < samples_read; j++) {
339 output[j] += ctx->mix_buffer[j];
346 if (sources_mixed > 1) {
347 float scale = 1.0f / (float)sources_mixed;
348 for (
size_t i = 0; i < num_samples; i++) {
351 if (output[i] > 1.0f) {
353 }
else if (output[i] < -1.0f) {
size_t audio_ring_buffer_read(audio_ring_buffer_t *rb, float *data, size_t samples)
asciichat_error_t audio_ring_buffer_write(audio_ring_buffer_t *rb, const float *data, int samples)
void audio_ring_buffer_destroy(audio_ring_buffer_t *rb)
asciichat_error_t audio_init(audio_context_t *ctx)
audio_ring_buffer_t * audio_ring_buffer_create(void)
asciichat_error_t audio_start_duplex(audio_context_t *ctx)
void audio_destroy(audio_context_t *ctx)
asciichat_error_t audio_stop_duplex(audio_context_t *ctx)
asciichat_error_t audio_write_samples(audio_context_t *ctx, const float *buffer, int num_samples)
asciichat_error_t audio_read_samples(audio_context_t *ctx, float *buffer, int num_samples)
asciichat_error_t session_audio_write_playback(session_audio_ctx_t *ctx, const float *buffer, size_t num_samples)
size_t session_audio_read_captured(session_audio_ctx_t *ctx, float *buffer, size_t num_samples)
asciichat_error_t session_audio_start_capture(session_audio_ctx_t *ctx)
void session_audio_destroy(session_audio_ctx_t *ctx)
asciichat_error_t session_audio_start_playback(session_audio_ctx_t *ctx)
bool session_audio_is_running(session_audio_ctx_t *ctx)
asciichat_error_t session_audio_write_source(session_audio_ctx_t *ctx, uint32_t source_id, const float *samples, size_t num_samples)
asciichat_error_t session_audio_start_duplex(session_audio_ctx_t *ctx)
void session_audio_stop(session_audio_ctx_t *ctx)
asciichat_error_t session_audio_add_source(session_audio_ctx_t *ctx, uint32_t source_id)
void session_audio_remove_source(session_audio_ctx_t *ctx, uint32_t source_id)
session_audio_ctx_t * session_audio_create(bool is_host)
#define SESSION_AUDIO_MAX_SOURCES
Maximum number of audio sources for mixing.
size_t session_audio_mix_excluding(session_audio_ctx_t *ctx, uint32_t exclude_id, float *output, size_t num_samples)
Internal session audio context structure.
int source_count
Number of active sources (host only)
size_t mix_buffer_size
Size of mix buffer in samples.
session_audio_source_t sources[32]
Audio sources for mixing (host only)
bool initialized
Context is fully initialized.
bool is_host
True if this is a host context (has mixing capabilities)
audio_context_t audio_ctx
Underlying audio context from lib/audio.
bool running
Audio streams are currently running.
float * mix_buffer
Temporary buffer for mixing operations.
Per-source audio buffer info for mixing.
audio_ring_buffer_t * buffer