Skip to content

Commit

Permalink
Merge remote-tracking branch 'lkoenig/dev/src_linear' into tryagain
Browse files Browse the repository at this point in the history
  • Loading branch information
qnebra committed Jul 29, 2024
2 parents 4858fb0 + 8e62284 commit 4d25956
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 47 deletions.
87 changes: 40 additions & 47 deletions src/src_linear.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ static void linear_close (SRC_STATE *state) ;

typedef struct
{ int linear_magic_marker ;
bool dirty ;
long in_count, in_used ;
long out_count, out_gen ;
bool initialized ;
float *last_value ;
} LINEAR_DATA ;

Expand All @@ -54,7 +52,6 @@ static SRC_ERROR
linear_vari_process (SRC_STATE *state, SRC_DATA *data)
{ LINEAR_DATA *priv ;
double src_ratio, input_index, rem ;
int ch ;

if (data->input_frames <= 0)
return SRC_ERR_NO_ERROR ;
Expand All @@ -64,16 +61,14 @@ linear_vari_process (SRC_STATE *state, SRC_DATA *data)

priv = (LINEAR_DATA*) state->private_data ;

if (!priv->dirty)
if (!priv->initialized)
{ /* If we have just been reset, set the last_value data. */
for (ch = 0 ; ch < state->channels ; ch++)
for (int ch = 0 ; ch < state->channels ; ch++)
priv->last_value [ch] = data->data_in [ch] ;
priv->dirty = true ;
priv->initialized = true ;
} ;

priv->in_count = data->input_frames * state->channels ;
priv->out_count = data->output_frames * state->channels ;
priv->in_used = priv->out_gen = 0 ;
data->input_frames_used = data->output_frames_gen = 0 ;

src_ratio = state->last_ratio ;

Expand All @@ -83,72 +78,70 @@ linear_vari_process (SRC_STATE *state, SRC_DATA *data)
input_index = state->last_position ;

/* Calculate samples before first sample in input array. */
while (input_index < 1.0 && priv->out_gen < priv->out_count)
float* current_out = data->data_out;
while (input_index < 1.0 && data->output_frames_gen < data->output_frames)
{
if (priv->in_used + state->channels * (1.0 + input_index) >= priv->in_count)
break ;
if (data->output_frames > 0 && fabs (state->last_ratio - data->src_ratio) > SRC_MIN_RATIO_DIFF)
src_ratio = state->last_ratio + data->output_frames_gen * (data->src_ratio - state->last_ratio) / data->output_frames ;

if (priv->out_count > 0 && fabs (state->last_ratio - data->src_ratio) > SRC_MIN_RATIO_DIFF)
src_ratio = state->last_ratio + priv->out_gen * (data->src_ratio - state->last_ratio) / priv->out_count ;

for (ch = 0 ; ch < state->channels ; ch++)
{ data->data_out [priv->out_gen] = (float) (priv->last_value [ch] + input_index *
for (int ch = 0 ; ch < state->channels ; ch++)
{ *current_out++ = (float) (priv->last_value [ch] + input_index *
((double) data->data_in [ch] - priv->last_value [ch])) ;
priv->out_gen ++ ;
} ;
data->output_frames_gen ++ ;

/* Figure out the next index. */
input_index += 1.0 / src_ratio ;
} ;

rem = fmod_one (input_index) ;
priv->in_used += state->channels * psf_lrint (input_index - rem) ;
data->input_frames_used += psf_lrint (input_index - rem) ;
input_index = rem ;

/* Main processing loop. */
while (priv->out_gen < priv->out_count && priv->in_used + state->channels * input_index < priv->in_count)
{
if (priv->out_count > 0 && fabs (state->last_ratio - data->src_ratio) > SRC_MIN_RATIO_DIFF)
src_ratio = state->last_ratio + priv->out_gen * (data->src_ratio - state->last_ratio) / priv->out_count ;

#if SRC_DEBUG
if (priv->in_used < state->channels && input_index < 1.0)
{ printf ("Whoops!!!! in_used : %ld channels : %d input_index : %f\n", priv->in_used, state->channels, input_index) ;
exit (1) ;
} ;
assert(data->output_frames_gen >= data->output_frames || data->input_frames_used > 0);
#endif
while (data->output_frames_gen < data->output_frames && data->input_frames_used + input_index < data->input_frames)
{
if (data->output_frames > 0 && fabs (state->last_ratio - data->src_ratio) > SRC_MIN_RATIO_DIFF)
src_ratio = state->last_ratio + data->output_frames_gen * (data->src_ratio - state->last_ratio) / data->output_frames ;

for (ch = 0 ; ch < state->channels ; ch++)
{ data->data_out [priv->out_gen] = (float) (data->data_in [priv->in_used - state->channels + ch] + input_index *
((double) data->data_in [priv->in_used + ch] - data->data_in [priv->in_used - state->channels + ch])) ;
priv->out_gen ++ ;
} ;
const float* current_in = data->data_in + data->input_frames_used * state->channels;
const float* prev_in = current_in - state->channels;
for (int ch = 0 ; ch < state->channels ; ch++)
{
*current_out++ = (float) (prev_in[ch] + input_index *
((double) current_in[ch] - prev_in[ch])) ;
}
data->output_frames_gen ++ ;

/* Figure out the next index. */
input_index += 1.0 / src_ratio ;
rem = fmod_one (input_index) ;

priv->in_used += state->channels * psf_lrint (input_index - rem) ;
const int num_frame_used = psf_lrint (input_index - rem);
data->input_frames_used += num_frame_used ;
input_index = rem ;
} ;
}

if (priv->in_used > priv->in_count)
{ input_index += (priv->in_used - priv->in_count) / state->channels ;
priv->in_used = priv->in_count ;
} ;
if (data->input_frames_used > data->input_frames)
{
input_index += (data->input_frames_used - data->input_frames) ;
data->input_frames_used = data->input_frames ;
}

state->last_position = input_index ;

if (priv->in_used > 0)
for (ch = 0 ; ch < state->channels ; ch++)
priv->last_value [ch] = data->data_in [priv->in_used - state->channels + ch] ;
if (data->input_frames_used > 0) {
const float *last_value = data->data_in + (data->input_frames_used - 1) * state->channels;
for (int ch = 0 ; ch < state->channels ; ch++)
priv->last_value [ch] = last_value[ch];
}

/* Save current ratio rather then target ratio. */
state->last_ratio = src_ratio ;

data->input_frames_used = priv->in_used / state->channels ;
data->output_frames_gen = priv->out_gen / state->channels ;

return SRC_ERR_NO_ERROR ;
} /* linear_vari_process */

Expand Down Expand Up @@ -237,7 +230,7 @@ linear_reset (SRC_STATE *state)
if (priv == NULL)
return ;

priv->dirty = false ;
priv->initialized = false ;
memset (priv->last_value, 0, sizeof (priv->last_value [0]) * state->channels) ;

return ;
Expand Down
8 changes: 8 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ add_executable(misc_test misc_test.c util.c util.h)
target_link_libraries(misc_test PRIVATE samplerate)
add_test(NAME misc_test COMMAND misc_test WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/src)

add_executable(streaming_test streaming_test.c util.c util.h)
target_link_libraries(streaming_test PRIVATE samplerate)
add_test(NAME streaming_test COMMAND streaming_test WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/src)

add_executable(vari_streaming_test vari_streaming_test.c util.c util.h)
target_link_libraries(vari_streaming_test PRIVATE samplerate)
add_test(NAME vari_streaming_test COMMAND vari_streaming_test WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/src)

add_executable(termination_test termination_test.c util.c util.h)
target_link_libraries(termination_test PRIVATE samplerate)
add_test(NAME termination_test COMMAND termination_test WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/src)
Expand Down
160 changes: 160 additions & 0 deletions tests/vari_streaming_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
** Copyright (c) 2002-2016, Erik de Castro Lopo <erikd@mega-nerd.com>
** All rights reserved.
**
** This code is released under 2-clause BSD license. Please see the
** file at : https://github.com/libsndfile/libsamplerate/blob/master/COPYING
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <math.h>
#include <samplerate.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "util.h"

#define BUFFER_LEN (1 << 15)
#define INPUT_BUFFER_LEN (BUFFER_LEN)
#define OUTPUT_BUFFER_LEN (BUFFER_LEN)
#define BLOCK_LEN 192

static void vari_stream_test(int converter, double ratio);

int main(void) {
static double src_ratios[] = {0.3, 0.9, 1.1, 3.0};

int k;

puts("\n Zero Order Hold interpolator:");
for (k = 0; k < ARRAY_LEN(src_ratios); k++)
vari_stream_test(SRC_ZERO_ORDER_HOLD, src_ratios[k]);

puts("\n Linear interpolator:");
for (k = 0; k < ARRAY_LEN(src_ratios); k++)
vari_stream_test(SRC_LINEAR, src_ratios[k]);

puts("\n Sinc interpolator:");
for (k = 0; k < ARRAY_LEN(src_ratios); k++)
vari_stream_test(SRC_SINC_FASTEST, src_ratios[k]);

puts("");

return 0;
} /* main */

struct generate_waveform_state_s {
float phase;
float phase_increment;
};

static void generate_waveform(struct generate_waveform_state_s* state, float* output, size_t len) {
for(size_t n = 0; n < len; ++n) {
output[n] = state->phase;
state->phase += state->phase_increment;
if (state->phase > 1.0 || state->phase < -1.0)
state->phase = state->phase > 0 ? -1.0 : 1.0;
}
}

static void vari_stream_test(int converter, double src_ratio) {
float input[INPUT_BUFFER_LEN], output[OUTPUT_BUFFER_LEN];

SRC_STATE *src_state;

int error, terminate;

printf("\tvari_streaming_test (SRC ratio = %6.4f) ........... ", src_ratio);
fflush(stdout);

/* Perform sample rate conversion. */
if ((src_state = src_new(converter, 1, &error)) == NULL) {
printf("\n\nLine %d : src_new() failed : %s\n\n", __LINE__,
src_strerror(error));
exit(1);
};

struct generate_waveform_state_s generator = {
.phase = 0.0,
.phase_increment = 2 / 48000, // 1Hz at 48000kHz samplerate.
};

double resampled_time = 0;
const size_t input_period_sizes[] = {
BLOCK_LEN,
BLOCK_LEN,
BLOCK_LEN,
1,
BLOCK_LEN,
};

int num_input_frame_used = 0;
int num_frames_outputed = 0;
int total_num_input_frames = 0;

int current_num_frames = 0;
for (size_t n = 0; n < ARRAY_LEN(input_period_sizes); ++n) {
generate_waveform(&generator, input, input_period_sizes[n]);
total_num_input_frames += input_period_sizes[n];
current_num_frames += input_period_sizes[n];
SRC_DATA src_data = {
.data_in = input,
.data_out = output,
.input_frames = input_period_sizes[n],
.output_frames = OUTPUT_BUFFER_LEN,
.input_frames_used = 0,
.output_frames_gen = 0,
.end_of_input = n == ARRAY_LEN(input_period_sizes) - 1,
.src_ratio = src_ratio,
};
// printf("eoi: %d\n", src_data.end_of_input);
if ((error = src_process(src_state, &src_data))) {
printf("\n\nLine %d : %s\n\n", __LINE__, src_strerror(error));

printf("src_data.input_frames : %ld\n", src_data.input_frames);
printf("src_data.output_frames : %ld\n", src_data.output_frames);

exit(1);
};


num_input_frame_used += src_data.input_frames_used;
num_frames_outputed += src_data.output_frames_gen;

memmove(
input,
input + src_data.input_frames_used,
(INPUT_BUFFER_LEN - src_data.input_frames_used) * sizeof(input[0])
);
current_num_frames -= src_data.input_frames_used;
if (src_data.end_of_input) break;
};

src_state = src_delete(src_state);

terminate = (int)ceil((src_ratio >= 1.0) ? src_ratio : 1.0 / src_ratio);

if (fabs(num_frames_outputed - src_ratio * total_num_input_frames) > 2 * terminate) {
printf("\n\nLine %d : bad output data length %d should be %d.\n", __LINE__,
num_frames_outputed, (int)floor(src_ratio * total_num_input_frames));
printf("\tsrc_ratio : %.4f\n", src_ratio);
printf("\tinput_len : %d\n\toutput_len : %d\n\n", total_num_input_frames, num_frames_outputed);
exit(1);
};

if (num_input_frame_used != total_num_input_frames) {
printf("\n\nLine %d : unused %d input frames.\n", __LINE__, current_num_frames);
printf("\tinput_len : %d\n", total_num_input_frames);
printf("\tinput_frames_used : %d\n\n", num_input_frame_used);
exit(1);
};

puts("ok");

return;
} /* stream_test */

0 comments on commit 4d25956

Please sign in to comment.