From df6ba94ee6be4bc7675e3a50c01acbe12e22ae50 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 26 Jul 2020 23:01:47 +0200 Subject: [PATCH] add files sndprint, vsjack --- src/frontend/Makefile.am | 2 + src/frontend/sndprint.c | 316 ++++++++++++++++++++++++++ src/frontend/sndprint.h | 11 + src/spicelib/devices/vsrc/Makefile.am | 4 +- src/spicelib/devices/vsrc/vsjack.c | 218 ++++++++++++++++++ src/spicelib/devices/vsrc/vsjack.h | 4 + visualc/vngspice.vcxproj | 2 + 7 files changed, 556 insertions(+), 1 deletion(-) create mode 100644 src/frontend/sndprint.c create mode 100644 src/frontend/sndprint.h create mode 100644 src/spicelib/devices/vsrc/vsjack.c create mode 100644 src/spicelib/devices/vsrc/vsjack.h diff --git a/src/frontend/Makefile.am b/src/frontend/Makefile.am index 8781b8610..868502b24 100644 --- a/src/frontend/Makefile.am +++ b/src/frontend/Makefile.am @@ -179,6 +179,8 @@ libfte_la_SOURCES = \ shyu.h \ signal_handler.c \ signal_handler.h \ + sndprint.c \ + sndprint.h \ spec.c \ spec.h \ spiceif.c \ diff --git a/src/frontend/sndprint.c b/src/frontend/sndprint.c new file mode 100644 index 000000000..5851d21a2 --- /dev/null +++ b/src/frontend/sndprint.c @@ -0,0 +1,316 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "sndprint.h" + + +int o_samplerate = 48000; +int o_sndfmt = (SF_FORMAT_WAV | SF_FORMAT_PCM_24); +float o_mult = 1.0; +float o_off = 0.0; + +////////////////////////////////// aliki ////////////////////////////////// + +#define HDRSIZE 256 + +void * my_open_aliki(char *fn, int nchannel) { + char p[HDRSIZE]; + FILE *aldfile; + if ((aldfile = fopen (fn, "w")) == 0) { + printf ("Error: Not able to open output file '%s'\n", fn); + exit (1); + } + + strcpy (p, "aliki"); + p [6] = p [7] = 0; + *(uint32_t *)(p + 8) = 2; //_vers + *(uint32_t *)(p + 12) = nchannel; // _type; + *(uint32_t *)(p + 16) = o_samplerate; //_rate_n; + *(uint32_t *)(p + 20) = 1; //_rate_d; + *(uint32_t *)(p + 24) = 486239; //_n_fram; + *(uint32_t *)(p + 28) = 1; // _n_sect; + *(uint32_t *)(p + 32) = 0; // _tref_i; + *(uint32_t *)(p + 36) = 0; // _tref_n; + *(uint32_t *)(p + 40) = 1; // _tref_d; + *(uint32_t *)(p + 44) = 0; // _bits; + + memset (p + 48, 0, HDRSIZE - 48); + if (fwrite (p, 1, HDRSIZE, aldfile) != HDRSIZE) { + printf ("Error: Not able to write aliki header to '%s'\n", fn); + fclose (aldfile); + exit(1); + } + return ((void*) aldfile); +} + +int my_write_aliki(void *d, float val) { + return(fwrite(&val, sizeof (float), 1, (FILE*) d)); +} + +void my_close_aliki(void *d) { + fclose((FILE*) d); +} + + +////////////////////////////////// sndfile ////////////////////////////////// + +typedef struct { + SNDFILE *outfile ; + int sf_channels; + int sf_bptr; + float *sf_buf; +} SSFILE; + +void * my_open_sf(char *fn, int nchannel) { + + SSFILE *d = calloc(1,sizeof(SSFILE)); + SF_INFO sfinfo ; + + sfinfo.samplerate = o_samplerate; + sfinfo.channels = nchannel; + sfinfo.frames = 0; + sfinfo.format = o_sndfmt; + + d->sf_channels = nchannel; + d->sf_bptr = 0; + d->sf_buf=calloc(nchannel, sizeof(float)); + + if ((d->outfile = sf_open (fn, SFM_WRITE, &sfinfo)) == NULL) { + printf ("Error: Not able to open output file '%s'\n", fn); + exit (1) ; + } + +#if 1 + sf_command (d->outfile, SFC_SET_UPDATE_HEADER_AUTO, NULL, SF_TRUE) ; + sf_command (d->outfile, SFC_SET_CLIPPING, NULL, SF_TRUE) ; +#endif + + return ((void*)d); +} + +int my_write_sf(void *d, float val) { + SSFILE *p= (SSFILE*) d; + p->sf_buf[p->sf_bptr++] =val; + if (p->sf_bptr >= p->sf_channels) { + sf_writef_float (p->outfile, p->sf_buf, 1); + p->sf_bptr=0; + } + return (1); +} + +void my_close_sf(void *d) { + sf_close(((SSFILE*)d)->outfile); + free(((SSFILE*)d)->sf_buf); + free((SSFILE*)d); +} + + + + + +////////////////////////////////// spice ////////////////////////////////// + +typedef struct SP_BUF { + double tme; + double *val; +} SP_BUF; + +void (*p_close)(void*); +void *(*p_open)(char*, int); +int (*p_write)(void*, float); +void * outfile; +uint32_t sample; +int sp_nchannel; +#define SP_MAX (2) +SP_BUF *sp_buf; +char *filename = NULL; + +#define HAVE_SRC + +#ifndef HAVE_SRC +#define OVERSAMPLING (1.0) +#else +#include +#define OBUFSIZE 256 +int oversampling = 64; +#define OVERSAMPLING ((double) oversampling) +SRC_STATE *rabbit; +int rabbit_err; +float *interleaved; +float *resampled; +int iptr = 0; + +int resample_wrapper (void *d, float val) { + interleaved[iptr++] = val; + size_t ibufsize = sp_nchannel * OBUFSIZE * OVERSAMPLING; + size_t obufsize = sp_nchannel * OBUFSIZE ; + if (iptr == ibufsize) { + + SRC_DATA src_data; + src_data.data_in = interleaved; + src_data.data_out = resampled; + src_data.input_frames = iptr/sp_nchannel; + src_data.output_frames = OBUFSIZE; + src_data.end_of_input = 0; + src_data.src_ratio = 1.0/OVERSAMPLING; + src_data.input_frames_used = 0; + src_data.output_frames_gen = 0; + + //printf ("the rabbit says: %s\n", src_strerror( + src_process(rabbit, &src_data); + //)); + + if (src_data.output_frames_gen *sp_nchannel != obufsize) { + printf ("resample warning: out %li != %i\n", src_data.output_frames_gen*sp_nchannel, obufsize); + } + + if (src_data.input_frames_used *sp_nchannel != iptr) { + printf ("resample warning: in: %li != %i\n", src_data.input_frames_used*sp_nchannel, iptr); + } + + int i; + for (i=0; i< src_data.output_frames_gen*sp_nchannel; i++) + p_write (d, resampled[i]); + + iptr=0; + return (src_data.output_frames_gen*sp_nchannel); + } + return (0); +} + +#endif + +void snd_configure(char *fn, int srate, int fmt, double mult, double off, int os ){ + if (filename) free(filename); + filename=strdup(fn); + + o_samplerate = srate; + o_mult = mult; + o_off = off; + oversampling = os; + if (fmt!=0) { + p_close = &my_close_sf; + p_open = &my_open_sf; + p_write = &my_write_sf; + o_sndfmt=(fmt>0)?fmt:(SF_FORMAT_WAV | SF_FORMAT_PCM_24); + printf("info: opened snd file '%s'\n",filename); + } else { + p_close = &my_close_aliki; + p_open = &my_open_aliki; + p_write = &my_write_aliki; + printf("info: opened aliki file '%s'\n",filename); + } +} + +int snd_format(char *fmt) { + int f = atoi(fmt); + if (!strcmp(fmt, "wav")) f= (SF_FORMAT_WAV | SF_FORMAT_PCM_24); + if (!strcmp(fmt, "wav16")) f= (SF_FORMAT_WAV | SF_FORMAT_PCM_16); + if (!strcmp(fmt, "wav24")) f= (SF_FORMAT_WAV | SF_FORMAT_PCM_24); + if (!strcmp(fmt, "wav32")) f= (SF_FORMAT_WAV | SF_FORMAT_PCM_32); + if (!strcmp(fmt, "aiff")) f= (SF_FORMAT_AIFF | SF_FORMAT_PCM_16); + if (!strcmp(fmt, "aliki")) f= 0; + return (f); +} + +void snd_init(int nchannel) { + int i; + if (!filename) snd_configure("spice.wav", 48000.0, o_sndfmt, o_mult, o_off, oversampling); + outfile = p_open (filename, nchannel); + sp_nchannel = nchannel; + sp_buf = calloc(nchannel, sizeof(SP_BUF)); + for (i=0; i< SP_MAX; i++){ + sp_buf[i].tme=0.0; + sp_buf[i].val = calloc(nchannel, sizeof(float)); + } + sample=0; +#ifdef HAVE_SRC + interleaved=calloc(nchannel*OBUFSIZE*OVERSAMPLING, sizeof(float)); + resampled=calloc(nchannel*OBUFSIZE, sizeof(float)); + rabbit=src_new(SRC_SINC_BEST_QUALITY, nchannel, &rabbit_err); + src_set_ratio(rabbit, 1.0/OVERSAMPLING); + src_reset(rabbit); +#endif +} + +int snd_send(double tme, int c, double out) { + int i; + int rv =0; + if (c==0) for (i=SP_MAX-1; i>0; i--) { + memcpy(&(sp_buf[i]), &(sp_buf[i-1]), sizeof(SP_BUF)); + } + sp_buf[0].tme=tme * OVERSAMPLING; + sp_buf[0].val[c]=out; +#ifdef SND_DEBUG + printf ("INFO : c:%i tme:%f fsmp:%i val:%f\n", c, tme, sample,out); +#endif + + if (sample == 0) { + if (c==(sp_nchannel-1)) + sample = ceil(tme * OVERSAMPLING); + return (0); + } + + if ( (sample) < ceil(tme * OVERSAMPLING) ) { + if (!(sp_buf[0].tme > sample)) printf ("error 1 %f !> %i\n",sp_buf[0].tme,sample); + if ( (sp_buf[1].tme > sample)) printf ("error 2 %f !< %i\n",sp_buf[1].tme,sample); +#if 1 // DEBUG + if ((sp_buf[0].tme - sample) > 1.0) printf ("error 3 large timestep: dv/dt=%e dt:%f dv:%e\n", + (sp_buf[0].val[c] - sp_buf[1].val[c]) / (sp_buf[0].tme - sample) , + (sp_buf[0].tme - sample), ( sp_buf[0].val[c] - sp_buf[1].val[c]) ); +#endif + + // linear + double p = (sp_buf[0].tme - sample ) / (sp_buf[0].tme - sp_buf[1].tme); + double val = sp_buf[0].val[c] - p * ( sp_buf[0].val[c] - sp_buf[1].val[c] ); +#ifdef SND_DEBUG + printf ("DEBUG: writing c:%i p:%f*[%f - %f] v:%f\n",c,p,sp_buf[0].val[c], sp_buf[1].val[c], val); +#endif + +#ifdef HAVE_SRC + rv = resample_wrapper (outfile, o_off + val * o_mult); +#else + p_write(outfile, o_off + val * o_mult); + if (c==(sp_nchannel-1)) rv =1; +#endif + if (c==(sp_nchannel-1)) sample ++; + + } else { +#ifdef SND_DEBUG + printf(" ^^^^^^^^^ SKIPPED ^^^^^^^^^\n"); +#endif + } + return (rv); +} + +void snd_close(void) { +#ifdef HAVE_SRC + while (!resample_wrapper(outfile, 0.0)); // flush buffer. +#endif + p_close(outfile); + free(filename); filename=NULL; +#ifdef HAVE_SRC + free (interleaved); + free (resampled); +#endif + /* + int i; + for (i=0; i< SP_MAX; i){ + free (sp_buf[i].val); + sp_buf[i].val=NULL; + } + */ + free (sp_buf); +} + +double snd_get_samplerate(void) { + return ((double) o_samplerate); +} + +/* vi:set ts=8 sts=2 sw=2: */ + diff --git a/src/frontend/sndprint.h b/src/frontend/sndprint.h new file mode 100644 index 000000000..b9bfa3af3 --- /dev/null +++ b/src/frontend/sndprint.h @@ -0,0 +1,11 @@ +#ifndef _SPICE_SNDFILE_H +#define _SPICE_SNDFILE_H + +void snd_configure(char*, int, int, double, double, int); +void snd_init(int); +void snd_close(void); +int snd_send(double, int, double); +int snd_format(char*); +double snd_get_samplerate(void); + +#endif diff --git a/src/spicelib/devices/vsrc/Makefile.am b/src/spicelib/devices/vsrc/Makefile.am index a7800ee4a..823b3758c 100644 --- a/src/spicelib/devices/vsrc/Makefile.am +++ b/src/spicelib/devices/vsrc/Makefile.am @@ -19,7 +19,9 @@ libvsrc_la_SOURCES = \ vsrcpzld.c \ vsrcpzs.c \ vsrcset.c \ - vsrctemp.c + vsrctemp.c \ + vsjack.c \ + vsjack.h if KLU_WANTED diff --git a/src/spicelib/devices/vsrc/vsjack.c b/src/spicelib/devices/vsrc/vsjack.c new file mode 100644 index 000000000..dd6f8eb4e --- /dev/null +++ b/src/spicelib/devices/vsrc/vsjack.c @@ -0,0 +1,218 @@ +#include +#include +#include + +/////// SNDFILE /////// +#include +#include +#include +#include +#define VS_BUFSIZ 1024 + +#define MAX_D 6 +char *(sources[MAX_D]); + +SNDFILE * m_sndfile[MAX_D]; +int m_channels[MAX_D]; //< number of channles in src-file +uint32_t m_samplerate[MAX_D]; //< samplerate of source +uint32_t m_frames[MAX_D]; //< duration of source in frames +float *(interleaved[MAX_D]); //< internal soundfile buffer +uint32_t ilb_start[MAX_D]; //< first sample in buffer +uint32_t ilb_end[MAX_D]; //< last sample in buffer + +#define HAVE_SRC + +#ifdef HAVE_SRC +#include +double src_ratio=64.0; +#define SRC_RATIO src_ratio +SRC_STATE *rabbit[MAX_D]; +int rabbit_err[MAX_D]; +float *(resampled[MAX_D]); //< internal soundfile buffer +#endif + +void vsjack_initialize(void) { + int d; + for (d=0;d0) { + ilb_end[d] = ilb_start[d] + nframes; + } else { + ilb_end[d] = ilb_start[d]; + printf ("Decoder error.\n"); + } +#ifdef HAVE_SRC + SRC_DATA src_data; + src_data.data_in = interleaved[d]; + src_data.data_out = resampled[d]; + src_data.input_frames = VS_BUFSIZ; + src_data.output_frames = VS_BUFSIZ*SRC_RATIO; + src_data.end_of_input = ((ilb_end[d]-ilb_start[d]) < VS_BUFSIZ); + src_data.src_ratio = SRC_RATIO; + src_data.input_frames_used = 0; + src_data.output_frames_gen = 0; + + src_process(rabbit[d], &src_data); +#endif +} + +double get_value(int d, double time, int channel) { + uint32_t sample = (uint32_t) floor(time*((double)m_samplerate[d])); + + // TODO: print EOF warning (once). FIXME move to load_buffer + if (sample > m_frames[d] ) return (0.0); + + if (sample < ilb_start[d] || sample >= ilb_end[d]) + load_buffer(d, sample); + + if (sample < ilb_start[d] || sample >= ilb_end[d]) { + printf ("no such value buffered for file:%i.\n",d); + return (0.0); // nan ? + } + +#ifdef HAVE_SRC + int offset = floor((sample - ilb_start[d])*SRC_RATIO); + if (offset > VS_BUFSIZ*SRC_RATIO || offset < 0) { + printf ("value not in buffer:%i.\n",d); + return (0.0); // nan ? + } + float val = ((float *)(resampled[d]))[m_channels[d]*offset+channel]; +# if 0 // DEBUG +# define SQUARE(A) ((A)*(A)) + static double stride =0; + static double last =0; + static double deviation =0; + static int dev_cnt = 0; + if (channel == 0) { + stride += (SRC_RATIO*time*((double)m_samplerate[d])) - last; + last = (SRC_RATIO*time*((double)m_samplerate[d])); + deviation += SQUARE((SRC_RATIO*time*((double)m_samplerate[d])) - floor(SRC_RATIO*time*((double)m_samplerate[d]))); + dev_cnt ++; + if ((dev_cnt%(12000))==0) + printf ("read time dev= %f - stride= %f\n", sqrt(deviation/(double)dev_cnt), stride/(double)dev_cnt); + } +# endif +# if 0 // zero order hold. + return((double)val); +# else + // linear interpolation + float val1 = ((float *)(resampled[d]))[(m_channels[d]*(offset+1))+channel]; + double diff = (SRC_RATIO*time*((double)m_samplerate[d])) - + floor(SRC_RATIO*time*((double)m_samplerate[d])); + double rv = ((double)val)*(1.0-diff) + ((double)val1)*diff; + return(rv); +# endif + +#else // no upsampling. + + int offset = sample - ilb_start[d]; + if (offset > VS_BUFSIZ || offset < 0) { + printf ("value not in buffer:%i.\n",d); + return (0.0); // nan ? + } + return((double)(((float *)(interleaved[d]))[m_channels[d]*offset+channel])) +#endif + } + + +/* + * "public" functions + */ + +double vsjack_get_value (int d, double time, double time_offset, int channel, double oversampling) { + assert (d>=0 && d< MAX_D ); + if (m_sndfile[d] == NULL) return (0.0); // FIXME + if (oversampling > 0) src_ratio = oversampling; + + double value = get_value(d, time+time_offset, channel); + return (value); +} + +void vsjack_set_file (int d , char *fn) { + 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; + if (!initialized) { + initialized=1; + vsjack_initialize(); + } + if (d==-1) return -1;// initialize only + assert (d>=0 && d< MAX_D ); + assert (sources[d]!=NULL); + if (openfile_sf(d, sources[d])) { + printf ("could not open '%s'\n", sources[d]); + exit (1); + } + return (d); +} + +/* vi:set ts=8 sts=4 sw=4: */ diff --git a/src/spicelib/devices/vsrc/vsjack.h b/src/spicelib/devices/vsrc/vsjack.h new file mode 100644 index 000000000..fe151a648 --- /dev/null +++ b/src/spicelib/devices/vsrc/vsjack.h @@ -0,0 +1,4 @@ + +double vsjack_get_value (int, double, double, int, double); +int vsjack_open (int); +void vsjack_set_file (int, char*); diff --git a/visualc/vngspice.vcxproj b/visualc/vngspice.vcxproj index 8e80be4e2..8da95202a 100644 --- a/visualc/vngspice.vcxproj +++ b/visualc/vngspice.vcxproj @@ -949,6 +949,7 @@ + @@ -1581,6 +1582,7 @@ +