Allow loading the entire audio file at vsjack_open() time,

get the actual oversampling, to allocate the sample space.
This commit is contained in:
Linus Torvalds 2025-07-13 15:03:07 +02:00 committed by Holger Vogt
parent dbb81ae1e7
commit 6e9e5f7d80
4 changed files with 74 additions and 72 deletions

View File

@ -13,13 +13,14 @@
#define VS_RESAMPLING_CHUNK 1024 #define VS_RESAMPLING_CHUNK 1024
#include "ngspice/ngspice.h" #include "ngspice/ngspice.h"
#include "vsjack.h"
extern char* inp_pathresolve(const char* name); extern char* inp_pathresolve(const char* name);
#define MAX_D 6 #define MAX_D 6
static char* (sources[MAX_D]);
static SNDFILE* m_sndfile[MAX_D]; static SNDFILE* m_sndfile[MAX_D];
static int m_channel[MAX_D]; //< channel to be used in src-file
static int m_channels[MAX_D]; //< number of channles in src-file static int m_channels[MAX_D]; //< number of channles in src-file
static uint32_t m_samplerate[MAX_D]; //< samplerate of source static uint32_t m_samplerate[MAX_D]; //< samplerate of source
static uint32_t m_frames[MAX_D]; //< duration of source in frames static uint32_t m_frames[MAX_D]; //< duration of source in frames
@ -29,8 +30,7 @@ static float* (interleaved[MAX_D]); //< internal soundfile buffer
#ifdef HAVE_SRC #ifdef HAVE_SRC
#include <samplerate.h> #include <samplerate.h>
static double src_ratio = 64.0; static double src_ratio[MAX_D];
#define SRC_RATIO (src_ratio)
static SRC_STATE* rabbit[MAX_D]; static SRC_STATE* rabbit[MAX_D];
static int rabbit_err[MAX_D]; static int rabbit_err[MAX_D];
static float* (resampled[MAX_D]); //< internal soundfile buffer static float* (resampled[MAX_D]); //< internal soundfile buffer
@ -38,7 +38,7 @@ static uint32_t input_frames_used[MAX_D];
static uint32_t output_frames_generated[MAX_D]; static uint32_t output_frames_generated[MAX_D];
#endif #endif
void vsjack_initialize(void) { static void vsjack_initialize(void) {
int d; int d;
for (d = 0; d < MAX_D; d++) { for (d = 0; d < MAX_D; d++) {
m_sndfile[d] = NULL; m_sndfile[d] = NULL;
@ -46,19 +46,16 @@ void vsjack_initialize(void) {
#ifdef HAVE_SRC #ifdef HAVE_SRC
resampled[d] = NULL; resampled[d] = NULL;
#endif #endif
sources[d] = NULL;
} }
sources[0] = strdup("/tmp/test.wav");
sources[1] = strdup("/tmp/test1.wav");
} }
void realloc_sf(int d, uint32_t buffersize) { static void realloc_sf(int d, uint32_t buffersize) {
if (interleaved[d]) free(interleaved[d]); if (interleaved[d]) free(interleaved[d]);
interleaved[d] = (float*)calloc(m_channels[d] * buffersize, sizeof(float)); interleaved[d] = (float*)calloc(m_channels[d] * buffersize, sizeof(float));
} }
#ifdef HAVE_SRC #ifdef HAVE_SRC
void realloc_src(int d, uint32_t buffersize) { static void realloc_src(int d, uint32_t buffersize) {
if (resampled[d]) free(resampled[d]); if (resampled[d]) free(resampled[d]);
resampled[d] = (float*)calloc(m_channels[d] * buffersize, sizeof(float)); resampled[d] = (float*)calloc(m_channels[d] * buffersize, sizeof(float));
} }
@ -75,8 +72,8 @@ void closefile_sf(int d) {
} }
#endif #endif
int openfile_sf(int d, char* filename) { static int openfile_sf(int d, char* filename, uint32_t channel, double oversampling) {
uint32_t nframes; int nframes;
SF_INFO sfinfo; SF_INFO sfinfo;
if (!m_sndfile[d]) if (!m_sndfile[d])
sf_close(m_sndfile[d]); sf_close(m_sndfile[d]);
@ -102,17 +99,22 @@ int openfile_sf(int d, char* filename) {
return (-1); return (-1);
} }
nframes = sfinfo.frames; nframes = sfinfo.frames;
if (channel >= sfinfo.channels) {
fprintf(stderr, "Error: Audio file does not have channel %d (0-%d)\n", channel, sfinfo.channels-1);
return (-1);
}
m_channel[d] = channel;
m_channels[d] = sfinfo.channels; m_channels[d] = sfinfo.channels;
m_samplerate[d] = sfinfo.samplerate; m_samplerate[d] = sfinfo.samplerate;
m_frames[d] = nframes; m_frames[d] = nframes;
realloc_sf(d, nframes); realloc_sf(d, nframes);
#ifdef HAVE_SRC #ifdef HAVE_SRC
SRC_DATA src_data;
realloc_src(d, nframes * SRC_RATIO); src_ratio[d] = oversampling;
realloc_src(d, nframes * oversampling);
rabbit[d] = src_new(SRC_SINC_BEST_QUALITY, m_channels[d], &(rabbit_err[d])); rabbit[d] = src_new(SRC_SINC_BEST_QUALITY, m_channels[d], &(rabbit_err[d]));
src_set_ratio(rabbit[d], SRC_RATIO); src_set_ratio(rabbit[d], oversampling);
src_reset(rabbit[d]); src_reset(rabbit[d]);
output_frames_generated[d] = 0; output_frames_generated[d] = 0;
input_frames_used[d] = 0; input_frames_used[d] = 0;
@ -120,14 +122,15 @@ int openfile_sf(int d, char* filename) {
#endif #endif
nframes = sf_readf_float(m_sndfile[d], (interleaved[d]), nframes); nframes = sf_readf_float(m_sndfile[d], (interleaved[d]), nframes);
if (nframes < 0) { if (nframes < 0) {
fprintf(stderr, "Error: Failed to read audio frames\n"); fprintf(stderr, "Error: Failed to read audio frames\n");
return (-1); return (-1);
} }
m_frames[d] = nframes; m_frames[d] = nframes;
return (0); return (0);
} }
double get_value(int d, double time, int channel) { static double get_value(int d, double time) {
uint32_t channel = m_channel[d];
uint32_t nframes = m_frames[d]; uint32_t nframes = m_frames[d];
double sample_fp = time * ((double)m_samplerate[d]); double sample_fp = time * ((double)m_samplerate[d]);
uint32_t sample = (uint32_t)floor(sample_fp); uint32_t sample = (uint32_t)floor(sample_fp);
@ -135,54 +138,55 @@ double get_value(int d, double time, int channel) {
if (sample >= nframes) return (0.0); if (sample >= nframes) return (0.0);
#ifdef HAVE_SRC #ifdef HAVE_SRC
double SRC_RATIO = src_ratio[d];
sample_fp *= SRC_RATIO; sample_fp *= SRC_RATIO;
sample = (uint32_t)floor(sample_fp); sample = (uint32_t)floor(sample_fp);
// Do we need to generate more output frames? // Do we need to generate more output frames?
while (sample >= output_frames_generated[d]) { while (sample >= output_frames_generated[d]) {
SRC_DATA src_data; SRC_DATA src_data;
uint32_t output_generated = output_frames_generated[d]; uint32_t output_generated = output_frames_generated[d];
uint32_t input_used = input_frames_used[d]; uint32_t input_used = input_frames_used[d];
uint32_t input_frames_left = nframes - input_used; uint32_t input_frames_left = nframes - input_used;
// Not enough output frames, and nothing more to input? // Not enough output frames, and nothing more to input?
// Give up. // Give up.
if (!input_frames_left) if (!input_frames_left)
return (0.0); return (0.0);
// Do the resampling in smaller chunks // Do the resampling in smaller chunks
src_data.end_of_input = 1; src_data.end_of_input = 1;
if (input_frames_left > VS_RESAMPLING_CHUNK) { if (input_frames_left > VS_RESAMPLING_CHUNK) {
input_frames_left = VS_RESAMPLING_CHUNK; input_frames_left = VS_RESAMPLING_CHUNK;
src_data.end_of_input = 0; src_data.end_of_input = 0;
} }
src_data.data_in = interleaved[d] + m_channels[d] * input_used; src_data.data_in = interleaved[d] + m_channels[d] * input_used;
src_data.data_out = resampled[d] + m_channels[d] * output_generated; src_data.data_out = resampled[d] + m_channels[d] * output_generated;
src_data.input_frames = input_frames_left; src_data.input_frames = input_frames_left;
src_data.output_frames = nframes * SRC_RATIO - output_generated; src_data.output_frames = nframes * SRC_RATIO - output_generated;
src_data.src_ratio = SRC_RATIO; src_data.src_ratio = SRC_RATIO;
src_data.output_frames_gen = 0; src_data.output_frames_gen = 0;
src_data.input_frames_used = 0; src_data.input_frames_used = 0;
if (src_process(rabbit[d], &src_data)) { if (src_process(rabbit[d], &src_data)) {
fprintf(stderr, "src_process() failed on sound file"); fprintf(stderr, "src_process() failed on sound file");
return -1; return -1;
} }
output_frames_generated[d] += src_data.output_frames_gen; output_frames_generated[d] += src_data.output_frames_gen;
input_frames_used[d] += src_data.input_frames_used; input_frames_used[d] += src_data.input_frames_used;
if (src_data.end_of_input) if (src_data.end_of_input)
break; break;
} }
// Are we past all the generated samples? // Are we past all the generated samples?
if (sample >= output_frames_generated[d]) if (sample >= output_frames_generated[d])
return (0.0); return (0.0);
float val = ((float*)(resampled[d]))[m_channels[d] * sample + channel]; float val = ((float*)(resampled[d]))[m_channels[d] * sample + channel];
// Are we the last sample? // Are we the last sample?
if (sample + 1 == output_frames_generated[d]) if (sample + 1 == output_frames_generated[d])
return val; return val;
// linear interpolation between samples // linear interpolation between samples
double diff = sample_fp - sample; double diff = sample_fp - sample;
@ -201,32 +205,24 @@ double get_value(int d, double time, int channel) {
* "public" functions * "public" functions
*/ */
double vsjack_get_value(int d, double time, double time_offset, int channel, double oversampling) { double vsjack_get_value(int d, double time, double time_offset) {
assert(d >= 0 && d < MAX_D); assert(d >= 0 && d < MAX_D);
if (m_sndfile[d] == NULL) return (0.0); // FIXME if (m_sndfile[d] == NULL) return (0.0); // FIXME
if (oversampling > 0) src_ratio = oversampling;
double value = get_value(d, time + time_offset, channel); double value = get_value(d, time + time_offset);
return (value); return (value);
} }
void vsjack_set_file(int d, char* fn) { int vsjack_open(int d, char *file, int channel, double oversampling) {
assert(d >= 0 && d < MAX_D);
if (sources[d] != NULL) free(sources[d]);
sources[d] = strdup(fn);
}
int vsjack_open(int d) {
static int initialized = 0; static int initialized = 0;
if (!initialized) { if (!initialized) {
initialized = 1; initialized = 1;
vsjack_initialize(); vsjack_initialize();
} }
if (d == -1) return -1;// initialize only
assert(d >= 0 && d < MAX_D); assert(d >= 0 && d < MAX_D);
assert(sources[d] != NULL);
if (openfile_sf(d, sources[d])) { if (openfile_sf(d, file, channel, oversampling)) {
fprintf(stderr, "Error: Could not open or read '%s'\n", sources[d]); fprintf(stderr, "Error: Could not open or read '%s'\n", file);
controlled_exit(1); controlled_exit(1);
} }
return (d); return (d);

View File

@ -1,4 +1,3 @@
double vsjack_get_value (int, double, double, int, double); double vsjack_get_value (int, double time, double time_offset);
int vsjack_open (int); int vsjack_open (int, char *file, int channel, double oversampling);
void vsjack_set_file (int, char*);

View File

@ -425,7 +425,7 @@ VNoi3 3 0 DC 0 TRNOISE(0 0 0 0 15m 22u 50u) : generate RTS noise
break; break;
case SOUND: { case SOUND: {
value = here->VSRCcoeffs[2] * vsjack_get_value((int)here->VSRCcoeffs[0], time, here->VSRCcoeffs[3], (int)rint(here->VSRCcoeffs[4]), rint(here->VSRCcoeffs[5])); value = here->VSRCcoeffs[2] * vsjack_get_value((int)here->VSRCcoeffs[0], time, here->VSRCcoeffs[3]);
value += here->VSRCcoeffs[1]; value += here->VSRCcoeffs[1];
} }
break; break;

View File

@ -295,18 +295,25 @@ VSRCparam(int param, IFvalue *value, GENinstance *inst, IFvalue *select)
break; break;
case VSRC_SOUND: { case VSRC_SOUND: {
int id, channel;
double oversampling;
here->VSRCfunctionType = SOUND; here->VSRCfunctionType = SOUND;
here->VSRCfuncTGiven = TRUE; here->VSRCfuncTGiven = TRUE;
copy_coeffs(here, value); copy_coeffs(here, value);
here->VSRCcoeffsGiven = TRUE; here->VSRCcoeffsGiven = TRUE;
vsjack_open(-1); // initialize if (!jfile) {
if (jfile) { fprintf(stderr, "Warning! Need filename for sound input");
vsjack_set_file((int)rint(here->VSRCcoeffs[0]), jfile); return(E_BADPARM);
tfree(jfile);
} }
if (value->v.numValue != 6) if (value->v.numValue != 6) {
fprintf(stderr, "Warning! invalid jack args: %i\nFormat: jack(id v_off v_mult t_off channel oversampling)", value->v.numValue); fprintf(stderr, "Warning! invalid jack args: %i\nFormat: jack(id v_off v_mult t_off channel oversampling)", value->v.numValue);
vsjack_open((int)rint(here->VSRCcoeffs[0])); return (E_BADPARM);
}
id = (int)rint(here->VSRCcoeffs[0]);
channel = (int)rint(here->VSRCcoeffs[4]);
oversampling = here->VSRCcoeffs[5];
vsjack_open(id, jfile, channel, oversampling);
tfree(jfile);
} }
break; break;