#define _POSIX_C_SOURCE 200809L #include "wave_internal.h" #include #include #include #include struct xtc_recorder { float *ring; size_t capacity; uint64_t write_position, read_position, written; unsigned int error, rate; FILE *file; char *path, *temporary; int finished; }; xtc_result xtc_recorder_create(const char *path, unsigned int rate, size_t capacity, xtc_recorder **out) { xtc_recorder *r; size_t length = 0; int fd; if (!out) return XTC_ERR_INVALID_ARGUMENT; *out = NULL; if (!path || !rate || rate > 768000 || !capacity || capacity > 2097152U) return XTC_ERR_INVALID_ARGUMENT; while (length < 4096 && path[length]) ++length; if (!length || length == 4096) return XTC_ERR_INVALID_ARGUMENT; if (sizeof(float) != 4 || FLT_RADIX != 2 || FLT_MANT_DIG != 24 || FLT_MAX_EXP != 128) return XTC_ERR_UNSUPPORTED; if (!__atomic_always_lock_free(sizeof(uint64_t), 0) || !__atomic_always_lock_free(sizeof(unsigned int), 0)) return XTC_ERR_UNSUPPORTED; r = calloc(1, sizeof(*r)); if (!r) return XTC_ERR_OUT_OF_MEMORY; r->capacity = capacity; r->rate = rate; r->ring = malloc(capacity * sizeof(float)); r->path = malloc(length + 1); r->temporary = calloc(length + 12, 1); if (!r->ring || !r->path || !r->temporary) { xtc_recorder_destroy(r); return XTC_ERR_OUT_OF_MEMORY; } memcpy(r->path, path, length + 1); memcpy(r->temporary, path, length); memcpy(r->temporary + length, ".tmp.XXXXXX", 12); fd = mkstemp(r->temporary); if (fd < 0) { xtc_recorder_destroy(r); return XTC_ERR_IO; } r->file = fdopen(fd, "w+b"); if (!r->file) { close(fd); xtc_recorder_destroy(r); return XTC_ERR_IO; } if (!xtc_wave_header(r->file, rate, 0)) { xtc_recorder_destroy(r); return XTC_ERR_IO; } *out = r; return XTC_OK; } void xtc_recorder_push(xtc_recorder *r, const float *input, size_t frames) { uint64_t write, read; size_t i; if (!r || !frames || __atomic_load_n(&r->error, __ATOMIC_ACQUIRE)) return; write = __atomic_load_n(&r->write_position, __ATOMIC_RELAXED); read = __atomic_load_n(&r->read_position, __ATOMIC_ACQUIRE); if (!input || frames > r->capacity || frames > r->capacity - (size_t)(write - read) || write > UINT64_C(1073741814) - frames) { __atomic_store_n(&r->error, (unsigned int)(input ? XTC_ERR_CAPACITY : XTC_ERR_IO), __ATOMIC_RELEASE); return; } for (i = 0; i < frames; ++i) r->ring[(size_t)((write + i) % r->capacity)] = input[i]; __atomic_store_n(&r->write_position, write + frames, __ATOMIC_RELEASE); } xtc_result xtc_recorder_drain(xtc_recorder *r) { uint64_t read, write; if (!r || !r->file || r->finished) return XTC_ERR_INVALID_STATE; read = __atomic_load_n(&r->read_position, __ATOMIC_RELAXED); write = __atomic_load_n(&r->write_position, __ATOMIC_ACQUIRE); while (read < write) { size_t offset = (size_t)(read % r->capacity); size_t count = r->capacity - offset; if (count > write - read) count = (size_t)(write - read); if (!xtc_wave_write(r->file, r->ring + offset, count)) { __atomic_store_n(&r->error, (unsigned int)XTC_ERR_IO, __ATOMIC_RELEASE); return XTC_ERR_IO; } read += count; r->written += count; __atomic_store_n(&r->read_position, read, __ATOMIC_RELEASE); } return (xtc_result)__atomic_load_n(&r->error, __ATOMIC_ACQUIRE); } xtc_result xtc_recorder_status(const xtc_recorder *r, uint64_t *frames) { if (!r || !frames) return XTC_ERR_INVALID_ARGUMENT; *frames = __atomic_load_n(&r->write_position, __ATOMIC_ACQUIRE); return (xtc_result)__atomic_load_n(&r->error, __ATOMIC_ACQUIRE); } xtc_result xtc_recorder_finish(xtc_recorder *r) { xtc_result result; int failed = 0; if (!r || !r->file || r->finished) return XTC_ERR_INVALID_STATE; result = xtc_recorder_drain(r); if (result == XTC_ERR_IO) failed = 1; if (fseek(r->file, 0, SEEK_SET) || !xtc_wave_header(r->file, r->rate, (uint32_t)r->written) || fflush(r->file) || fsync(fileno(r->file))) failed = 1; if (fclose(r->file)) failed = 1; r->file = NULL; if (!failed && rename(r->temporary, r->path)) failed = 1; r->finished = 1; return failed ? XTC_ERR_IO : result; } void xtc_recorder_destroy(xtc_recorder *r) { if (!r) return; if (r->file) fclose(r->file); if (r->temporary) unlink(r->temporary); free(r->temporary); free(r->path); free(r->ring); free(r); }