mirror of
https://github.com/mudler/LocalAI.git
synced 2026-07-31 10:28:43 -04:00
f32_to_s16le relied on std::min argument order to keep a NaN sample away from std::lround, whose result is unspecified for NaN. That was too subtle to rest on a comment, and the comment was itself wrong: it warned against a spelling that the outer std::max already catches, while three real spellings leak, including std::clamp, which is the idiomatic C++17 way to write the same clamp and so the likeliest future edit. Divert NaN before the clamp and encode it as 0. A NaN sample rendered as a full-scale click is worse audio than a dropped one, and this unit converts audio that may have originated off the wire. Pin it with an exact-value check rather than a range check, since all three outcomes the plausible spellings produce are finite and inside full scale, plus an invalid-operation check that fails unless the NaN is diverted before any ordered comparison. That second check is what catches modernizing the clamp and dropping the guard together. Also bound the seconds round-trip comment, which claimed unconditionally what holds only below roughly 2^23 samples, and document NaN, saturation and that bound in the header. Assisted-by: Claude:claude-opus-5 [Claude Code] Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
39 lines
1.6 KiB
C++
39 lines
1.6 KiB
C++
#pragma once
|
|
|
|
// Time and sample-format conversion between audio.cpp's runtime types (sample
|
|
// indices, float PCM) and LocalAI's proto types. Standard library only.
|
|
//
|
|
// LocalAI uses three different time units:
|
|
// TranscriptSegment / TranscriptWord start,end : int64 nanoseconds
|
|
// VADSegment start,end : float seconds
|
|
// DiarizeSegment start,end : float seconds
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace audiocpp_backend {
|
|
|
|
// Returns 0 when sample_rate is not positive rather than dividing by zero.
|
|
// Uses integer arithmetic so 44.1 kHz does not lose precision.
|
|
std::int64_t samples_to_nanoseconds(std::int64_t samples, int sample_rate);
|
|
|
|
float samples_to_seconds(std::int64_t samples, int sample_rate);
|
|
|
|
// Rounds to nearest. Negative seconds and NaN both yield 0, and a value too
|
|
// large to convert saturates at INT64_MAX rather than overflowing. Round trips
|
|
// with samples_to_seconds only below roughly 2^23 samples, past which the float
|
|
// seconds can no longer resolve adjacent sample indices.
|
|
std::int64_t seconds_to_samples(double seconds, int sample_rate);
|
|
|
|
// Decodes little-endian signed 16-bit PCM. A trailing odd byte is dropped.
|
|
std::vector<float> s16le_to_f32(const std::string &bytes);
|
|
|
|
// Encodes to little-endian signed 16-bit PCM, clamping to [-1, 1] first so an
|
|
// overshooting sample saturates instead of wrapping to the opposite sign.
|
|
// A NaN sample encodes to 0, on the grounds that silence beats a full-scale
|
|
// click.
|
|
std::string f32_to_s16le(const std::vector<float> &samples);
|
|
|
|
} // namespace audiocpp_backend
|