Files
2026-08-24 10:03:24 +08:00

463 lines
14 KiB
C

/*
* BlueALSA - resampler.c
* SPDX-FileCopyrightText: 2025 BlueALSA developers
* SPDX-License-Identifier: MIT
*/
#include "resampler.h"
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <endian.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/time.h>
#include <alsa/asoundlib.h>
#include <samplerate.h>
#include "shared/log.h"
#include "shared/ffb.h"
#include "shared/rt.h"
/* How many milliseconds to allow the delay to change before adjusting the
* resampling rate. This value must allow the delay to vary due to timer
* jitter without triggering a rate change. */
# define RESAMPLER_TOLERANCE_MS 3
/* How many milliseconds to wait for the delay value to stabilize after a
* reset. */
#define RESAMPLER_STABILIZE_MS 5000
/* Step size of rate adjustment. */
#define RESAMPLER_STEP_SIZE 0.000004
/* Limit how many increment steps can be made when adjusting rate ratio. */
#define RESAMPLER_MAX_STEPS 100
/* Ignore rapid changes in delay since such changes can only result from
* stream discontinuities, not timer drift. */
#define RESAMPLER_MAX_CHANGE_MS 10
/* Minimum time in milliseconds between rate ratio adjustments. */
#define RESAMPLER_PERIOD_MS 100
/* Number of samples to process in one go in case where the format
* conversion is required. */
#define RESAMPLER_BUFFER_SIZE 4096
static const struct timespec ts_stabilize = {
.tv_sec = RESAMPLER_STABILIZE_MS / 1000,
.tv_nsec = (RESAMPLER_STABILIZE_MS % 1000) * 1000000,
};
/**
* Check whether audio format is supported as input by the resampler. */
bool resampler_is_input_format_supported(snd_pcm_format_t format) {
return format == SND_PCM_FORMAT_S16_LE ||
format == SND_PCM_FORMAT_S32_LE ||
format == SND_PCM_FORMAT_S24_LE;
}
/**
* Check whether audio format is supported as output by the resampler. */
bool resampler_is_output_format_supported(snd_pcm_format_t format) {
return format == SND_PCM_FORMAT_S16 ||
format == SND_PCM_FORMAT_S32 ||
format == SND_PCM_FORMAT_FLOAT;
}
/**
* Initialize the resampler structure.
*
* @param resampler The resampler structure to initialize.
* @param type The type of the converter to use.
* @param channels The number of channels in the stream.
* @param in_format The sample format of the incoming stream.
* @param in_rate The nominal sample rate of the incoming stream.
* @param out_format The required output sample format.
* @param out_rate The nominal sample rate of the output stream.
* @param min_target The minimum target delay.
* @param max_target The maximum target delay.
* @return On success this function returns 0. Otherwise -1 is returned. */
int resampler_init(
struct resampler *resampler,
enum resampler_converter_type type,
unsigned int channels,
snd_pcm_format_t in_format,
unsigned int in_rate,
snd_pcm_format_t out_format,
unsigned int out_rate,
snd_pcm_uframes_t min_target,
snd_pcm_uframes_t max_target) {
debug("Initializing resampler: min-delay=%#.1f max-delay=%#.1f",
1000.0 * min_target / in_rate, 1000.0 * max_target / in_rate);
/* Check whether formats can be resampled. */
if (!resampler_is_input_format_supported(in_format) ||
!resampler_is_output_format_supported(out_format)) {
return errno = EINVAL, -1;
}
int err;
if ((resampler->src_state = src_new(type, channels, &err)) == NULL) {
debug("Couldn't create converter: %s", src_strerror(err));
return errno = EINVAL, -1;
}
resampler->in_buffer = NULL;
if (in_format != SND_PCM_FORMAT_FLOAT) {
if ((resampler->in_buffer = malloc(RESAMPLER_BUFFER_SIZE * sizeof(float))) == NULL)
goto fail;
}
resampler->out_buffer = NULL;
if (out_format != SND_PCM_FORMAT_FLOAT) {
if ((resampler->out_buffer = malloc(RESAMPLER_BUFFER_SIZE * sizeof(float))) == NULL)
goto fail;
}
resampler->channels = channels;
resampler->in_format = in_format;
resampler->out_format = out_format;
resampler->min_target = min_target;
resampler->max_target = max_target;
resampler->max_delay_diff = RESAMPLER_MAX_CHANGE_MS * in_rate / 1000;
resampler->rate_ratio_step_count = 0;
resampler->delay_tolerance = RESAMPLER_TOLERANCE_MS * in_rate / 1000;
resampler->nominal_rate_ratio = (double)out_rate / (double)in_rate;
resampler->steady_rate_ratio_step_count = 0;
resampler->src_data.src_ratio = resampler->nominal_rate_ratio;
resampler->input_frames = 0;
resampler->last_input_frames = 0;
resampler->period = RESAMPLER_PERIOD_MS * in_rate / 1000;
resampler->in_rate = in_rate;
return 0;
fail:
resampler_free(resampler);
return -1;
}
/**
* Free resources allocated for the resampler. */
void resampler_free(struct resampler *resampler) {
if (resampler->src_state != NULL) {
src_delete(resampler->src_state);
resampler->src_state = NULL;
}
free(resampler->in_buffer);
resampler->in_buffer = NULL;
free(resampler->out_buffer);
resampler->out_buffer = NULL;
}
/**
* Resample as many frames as possible from the buffer in to the buffer out. */
int resampler_process(struct resampler *resampler, ffb_t *in, ffb_t *out) {
const unsigned int channels = resampler->channels;
const size_t in_samples_total = ffb_len_out(in);
SRC_DATA *src_data = &resampler->src_data;
size_t samples_used = 0;
/* If the input sample format is FLOAT we can process them directly
* without any intermediate conversion. Otherwise, we need to use our
* internal buffer to convert the samples to FLOAT before processing. */
if (resampler->in_format != SND_PCM_FORMAT_FLOAT)
src_data->data_in = resampler->in_buffer;
else
src_data->data_in = in->data;
/* The same case applies to the output sample format. */
if (resampler->out_format != SND_PCM_FORMAT_FLOAT)
src_data->data_out = resampler->out_buffer;
else
src_data->data_out = out->tail;
while (true) {
size_t in_samples = in_samples_total - samples_used;
size_t out_samples = ffb_len_in(out);
/* Convert input samples to FLOAT format if necessary. */
if (resampler->in_format != SND_PCM_FORMAT_FLOAT) {
in_samples = MIN(in_samples, RESAMPLER_BUFFER_SIZE);
if (resampler->in_format == SND_PCM_FORMAT_S16_LE) {
const int16_t *in_data = in->data;
src_short_to_float_array(in_data + samples_used, resampler->in_buffer, in_samples);
}
else if (resampler->in_format == SND_PCM_FORMAT_S32_LE) {
const int32_t *in_data = in->data;
src_int_to_float_array(in_data + samples_used, resampler->in_buffer, in_samples);
}
}
if (resampler->out_format != SND_PCM_FORMAT_FLOAT)
out_samples = MIN(out_samples, RESAMPLER_BUFFER_SIZE);
src_data->input_frames = in_samples / channels;
src_data->output_frames = out_samples / channels;
int err;
if ((err = src_process(resampler->src_state, src_data)) != 0) {
error("Resampling failed: %s", src_strerror(err));
return err;
}
if (src_data->output_frames_gen == 0)
break;
const size_t samples_gen = src_data->output_frames_gen * channels;
samples_used += src_data->input_frames_used * channels;
/* Convert output samples to integer format if necessary. */
if (resampler->out_format == SND_PCM_FORMAT_S16)
src_float_to_short_array(resampler->out_buffer, out->tail, samples_gen);
else if (resampler->out_format == SND_PCM_FORMAT_S32)
src_float_to_int_array(resampler->out_buffer, out->tail, samples_gen);
ffb_seek(out, samples_gen);
}
ffb_shift(in, samples_used);
return 0;
}
/**
* Reset the resampling ratio to its nominal rate.
*
* This function should be called after any discontinuity in the stream. */
void resampler_reset(struct resampler *resampler) {
resampler->src_data.src_ratio = resampler->nominal_rate_ratio;
resampler->rate_ratio_step_count = 0;
resampler->steady_rate_ratio_step_count = 0;
/* Disable adaptive resampling until the delay has had time to settle. */
resampler->target_delay = 0;
gettimestamp(&resampler->reset_ts);
}
double resampler_current_rate_ratio(const struct resampler *resampler) {
return resampler->src_data.src_ratio;
}
/**
* Change the rate ratio applied by the resampler to adjust for the given new
* delay value, always trying to move the delay back towards the target value.
*
* @return Returns true if the rate ratio was changed. */
bool resampler_update_rate_ratio(
struct resampler *resampler,
snd_pcm_uframes_t frames_read,
snd_pcm_uframes_t delay) {
bool ret = false;
/* Update the rate ratio only if at least one period
* has passed since the last update. */
if (frames_read > 0) {
resampler->input_frames += frames_read;
/* Prevent the possibility of integer overflow. */
resampler->input_frames %= INTMAX_MAX;
if (resampler->input_frames - resampler->last_input_frames < resampler->period)
return false;
resampler->last_input_frames = resampler->input_frames;
}
if (resampler->target_delay == 0 && !is_timespec_zero(&resampler->reset_ts)) {
/* Do not restart adaptive resampling until the delay has had time to
* settle to a new value. */
struct timespec ts_wait;
struct timespec ts_now;
gettimestamp(&ts_now);
timespecadd(&resampler->reset_ts, &ts_stabilize, &ts_wait);
if (difftimespec(&ts_now, &ts_wait, &ts_wait) < 0) {
/* Do not allow the target to be outside the configured range. If
* the actual delay is outside that range then try to move it back
* as quickly as possible. */
if (delay > resampler->max_target) {
resampler->target_delay = resampler->max_target;
resampler->src_data.src_ratio =
resampler->nominal_rate_ratio - RESAMPLER_STEP_SIZE * RESAMPLER_MAX_STEPS;
resampler->rate_ratio_step_count = -RESAMPLER_MAX_STEPS;
}
else if (delay < resampler->min_target) {
resampler->target_delay = resampler->min_target;
resampler->src_data.src_ratio =
resampler->nominal_rate_ratio + RESAMPLER_STEP_SIZE * RESAMPLER_MAX_STEPS;
resampler->rate_ratio_step_count = RESAMPLER_MAX_STEPS;
}
else {
/* Once the delay has returned within the permitted target
* range, zero the reset timestamp to indicate that normal
* adaptive resampling has re-started. */
resampler->reset_ts.tv_sec = 0;
resampler->reset_ts.tv_nsec = 0;
resampler->target_delay = delay;
}
resampler->delay_diff = delay - resampler->target_delay;
debug("Adaptive resampling target delay: %#.1f ms",
1000.0 * resampler->target_delay / resampler->in_rate);
return true;
}
return false;
}
snd_pcm_sframes_t delay_diff = delay - resampler->target_delay;
snd_pcm_uframes_t delay_diff_abs = labs(delay_diff);
/* Reset the resampler whenever the delay exceeds the limit. */
if (delay_diff_abs > resampler->max_delay_diff) {
/* Reset the resampler if not already done. */
if (is_timespec_zero(&resampler->reset_ts)) {
#if DEBUG
if (resampler->target_delay != 0)
/* Omit the log message if the target delay is not yet set. In such
* case we are just initializing the resampler, not resetting it. */
debug("Resetting resampler: Delay difference limit exceeded: %zu > %zu",
delay_diff_abs, resampler->max_delay_diff);
#endif
resampler_reset(resampler);
return true;
}
}
if (delay_diff_abs > resampler->delay_tolerance) {
/* When the delay is not already moving back towards tolerance,
* step the size of the rate adjustment in the appropriate
* direction. */
if (delay_diff > 0 && delay_diff > resampler->delay_diff) {
if (resampler->rate_ratio_step_count > -RESAMPLER_MAX_STEPS) {
resampler->src_data.src_ratio -= RESAMPLER_STEP_SIZE;
resampler->rate_ratio_step_count--;
ret = true;
}
}
else if (delay_diff < 0 && delay_diff < resampler->delay_diff) {
if (resampler->rate_ratio_step_count < RESAMPLER_MAX_STEPS) {
resampler->src_data.src_ratio += RESAMPLER_STEP_SIZE;
resampler->rate_ratio_step_count++;
ret = true;
}
}
}
else if (labs(resampler->delay_diff) > resampler->delay_tolerance) {
/* When the delay has returned to tolerance, step the size of the steady
* rate ratio in the appropriate direction and set the rate ratio to
* the revised steady rate ratio. */
if (resampler->delay_diff > 0) {
if (resampler->steady_rate_ratio_step_count > -RESAMPLER_MAX_STEPS) {
resampler->steady_rate_ratio_step_count--;
ret = true;
}
}
else {
if (resampler->steady_rate_ratio_step_count > RESAMPLER_MAX_STEPS) {
resampler->steady_rate_ratio_step_count++;
ret = true;
}
}
if (ret) {
resampler->rate_ratio_step_count = resampler->steady_rate_ratio_step_count;
resampler->src_data.src_ratio =
resampler->nominal_rate_ratio + RESAMPLER_STEP_SIZE * resampler->rate_ratio_step_count;
}
}
resampler->delay_diff = delay_diff;
return ret;
}
/**
* Convert a buffer of PCM samples to the equivalent native-endian format. The
* samples are modified in place. Also pad 24bit samples (packed into 32bits)
* to convert them to valid 32-bit samples.
*
* @param buffer The buffer of samples to convert.
* @param len The number of samples in the buffer.
* @param format The original format of the samples. */
void resampler_convert_to_native_endian_format(
void *buffer, size_t len, snd_pcm_format_t format) {
#if __BYTE_ORDER == __BIG_ENDIAN
switch (format) {
case SND_PCM_FORMAT_S16_LE: {
uint16_t *data = buffer;
for (size_t n = 0; n < len; n++)
le16toh(data[n]);
} break;
case SND_PCM_FORMAT_S24_LE: {
uint32_t *data = buffer;
for (size_t n = 0; n < len; n++) {
le32toh(data[n]);
/* convert to S32 */
data[n] <<= 8;
}
} break;
case SND_PCM_FORMAT_S32_LE: {
uint32_t *data = buffer;
for (size_t n = 0; n < len; n++)
le32toh(data[n]);
} break;
default:
return;
}
#else
switch (format) {
case SND_PCM_FORMAT_S24_LE: {
uint32_t *data = buffer;
for (size_t n = 0; n < len; n++) {
/* convert to S32 */
data[n] <<= 8;
}
} break;
default:
return;
}
#endif
}
/**
* Return the equivalent native-endian format for the given format.
* For unsupported formats the given source format value is returned. */
snd_pcm_format_t resampler_native_endian_format(snd_pcm_format_t format) {
#if __BYTE_ORDER == __BIG_ENDIAN
switch (format) {
case SND_PCM_FORMAT_S16_LE:
return SND_PCM_FORMAT_S16;
case SND_PCM_FORMAT_S24_LE:
/* 24-bit samples must be converted to 32-bit
* before passing to the resampler. */
return SND_PCM_FORMAT_S32;
case SND_PCM_FORMAT_S32_LE:
return SND_PCM_FORMAT_S32;
default:
return format;
}
#else
if (format == SND_PCM_FORMAT_S24_LE)
/* 24-bit samples must be converted to 32-bit
* before passing to the resampler. */
return SND_PCM_FORMAT_S32;
return format;
#endif
}
snd_pcm_format_t resampler_preferred_output_format(void) {
return SND_PCM_FORMAT_FLOAT;
}