/*
PROGRAM to execute an asrc between a master spdif input and a master Teensy i2s output
(frequency measurement)
|
|
(128 sample input)spdif3---->(128 inblock samples)--->Srcblock---->(128+-x outblock samples)---->(512-sample FIFO)----->i2s(128 sample output)
The asrc sinc filter continuously varies its group delay. The coefficients are re-calculated for every new output sample. A 3rd-order polynomial is used. It
requires the equivalent of 4 convolutions per sample (4 mono, 3 stereo, 1.6 for 6 channels), but furnishes a THD+N measurement of -130dB on 32-bit samples
using only 2672 32-bit integer coefs instead of 20481 32-bit floating point coefs
------------------ useful formulas ---------------------------------
M is the oversampling ratio of the polyphase asrc sinc filter (15)
fsout=fsin*(1+ppm/1e6)
Tin=1/fsin
Tout=1/fsout
dt=(M)*(Tout-Tin)/Tin
or dt=(M)*(fsin/fsout-1)
or dt=(M)*(nbconsumed/nbgen-1)
or dt=-(M)*ppm/(1e6+ppm)
fsout/fsin=M/(M+dt)
-------------------------------------------------------------------*/
#include "srconvfunctsT.h"
void Fillinblock(int *inblock,int nbytes,FILE *inputfile); //fill SRC inblock with BLOCKLEN samples from spdif
void FILLFIFOblock(int *outblock,int nbout,int *FIFO,int *FIFOlvl); //MT SRC outblock (BLOCKLEN+-N samples) to FIFO
void MTFIFOblock(int *FIFO,int *FIFOlvl,int nbytes,FILE *outputfile,int *pos); //MT BLOCKLEN samples from FIFO to i2s
#define FIFOSIZ 4*BLOCKLEN
#define FIFOlvlMAX BLOCKLEN-8
#define FIFOlvlMIN 3*BLOCKLEN+8
#define XTRASIZ 22 //take into account for upsampling from 44.1->48kHz, need at least 12 XTRASIZ samples in the Srcblock outblock buffer
//#define DEBUG
int main(int argc, char *argv[])
{
double dt,DT,ddt,fsin,fsout,ppm,Tin,Tout,alpha=0.0,dalpha;
int j,nch,nbits,intfsin,intfsout,datavalid,pos=0,nbinsmpls,nbytes,nbytestot,nbgenerated,nbconsumed,doi2s=0;
#ifdef DEBUG
int totalnbconsumed=0,totalnbgenerated=0;
double time=0.;
FILE *fres;
#endif
FILE *insig,*outsig; //insig contains master spdif input samples, outsig contains master i2s output samples
TeensySRCcontext *SRCctxt;//for Teensy Srcblock variables
int outblock[BLOCKLEN+XTRASIZ],inblock[BLOCKLEN],blocksiz=BLOCKLEN;//Srcblock output & input buffers
int FIFO[FIFOSIZ]; //FIFO buffers storing the Srcblock output while samples are waiting for next i2s update
int FIFOlvl=(int)((double)FIFOSIZ/2.0); //FIFO avg target level
for(j=0;j<FIFOSIZ;j++) FIFO[j]=0; //initialize to 0
if (argc<2)
{
printf(" use: srconv ppm \n");
printf(" (ppm = -81250 for converting 48000 to 44100 Hz)\n");
printf(" input file: sinein48long.wav output file: sineoutT.wav\n");
exit(1);
}
ppm=atof(argv[1]);//ppm=(fsout-fsin)/fsin this value will be determined by measurement of fs offset between input spdif sample frequency and Teensy sample frequency
if ((insig=fopen("sinein48long.wav","rb"))==0)
{
printf("\n\n********** BAD INPUT FILE NAME **********\n\n");
exit(1);
}
outsig=fopen("sineoutT.wav","wb"); //samples from spdif input
#ifdef DEBUG
fres=fopen("res.txt","wt"); //current FIFOlvl containing resampled samples
#endif
if (readhead(insig,&nch,&nbits,&intfsin,&nbytestot,&datavalid)!=0) //getting input 48kHz wav file parameters
{
printf("not a wav file\n");
exit(1);
}
fsin=(double)intfsin;
fsout=fsin*(1.+ppm/1.e6); //ppm is -81250 to resample at 44100Hz
intfsout=(int)(fsout+.5);
Tin=1/fsin;
Tout=1/fsout;
dt=OVERSAMPLING*(Tout-Tin)/Tin; //dt determines the sinc filter group delay variation per output sample
ddt=.0001;//small adjustment DT=dt+-ddt to reach correct avg FIFOlvl at FIFOSIZ/2 and to compensate small errors in measured frequency offsets
DT=dt; //initial value
dalpha=(Tout-Tin)/Tin; //dalpha simulates ratio between spdif isr and i2s update events
inithead(nch,nbits,intfsout,outsig); //prepare output wav file
nbinsmpls=nbytestot/(nbits/8);
nbytes=nbits/8;
//----------------------------------------------for Teensy Srcblock variables -----------------------------------------------------
SRCctxt=malloc(sizeof(TeensySRCcontext));
SRCctxt->pint=malloc(((NTAPS*OVERSAMPLING+1)*(POLYORD+1)/2)*sizeof(int32_t)); //allocate sinc filter fixed point polynomial values
SRCctxt->tempbuf=malloc((WORKSIZ+2*OVERSAMPLING*NTAPS)*sizeof(double)); //allocate temporary buffer for calculating polynomial values
InitSrc(SRCctxt,fsin,fsout,1); //calculate Srcblock fixed variables according to initial fsin,fsout
free(SRCctxt->tempbuf); //no longer needed unless relatively large modifications of fsin, fsout
//-------------------------------------------------------------------------------------------------------------
while(1) //infinite loop
{
if (alpha<-.5)
{
alpha+=1.0;
doi2s+=1; //another additional i2s update has occured (upsampling)
}
else if (alpha>.5)
{
alpha-=1.0;
doi2s-=1; //another additional spdif isr has occured (downsampling)
}
if (doi2s>0)
{
//MT BLOCKLEN FIFO samples, i2s output is ready
MTFIFOblock(FIFO,&FIFOlvl,nbytes,outsig,&pos);
alpha+=dalpha;
doi2s--;
}
else
{
//Spdif isr is ready. Src executes BLOCKLEN input samples and fills the FIFO with BLOCKLEN+-N samples
Fillinblock(inblock,nbytes,insig);
Srcblock(1,1,blocksiz,inblock,&nbconsumed,outblock,&nbgenerated,&DT,SRCctxt); //resample BLOCKLEN input samples to BLOCKLEN+-N output samples
FILLFIFOblock(outblock,nbgenerated,FIFO,&FIFOlvl); //get SRC nbout samples and put them in FIFO
#ifdef DEBUG
totalnbconsumed+=BLOCKLEN;
totalnbgenerated+=nbgenerated;
#endif
nbinsmpls-=BLOCKLEN;
doi2s++;
}
if (FIFOlvl<FIFOlvlMAX) DT=dt+ddt; //slowdown src, FIFO is too full
else if (FIFOlvl>FIFOlvlMIN) DT=dt-ddt; //speedup src, FIFO is too MT
#ifdef DEBUG
fprintf(fres,"%d\n",FIFOlvl);
#endif
if (nbinsmpls<BLOCKLEN) break; //end of input wav file
} //while(1)
fclose(insig);
closeheader(pos,outsig);
fclose(outsig);
#ifdef DEBUG
fclose(fres);
#endif
//----for Teensy-------
free(SRCctxt->pint);
free(SRCctxt);
//---------------------
#ifdef DEBUG
time=(double)totalnbconsumed/fsin;
printf("totalconsumed: %d totalgenerated: %d approximatePPM: %f fsout: %f\n",totalnbconsumed,totalnbgenerated,1e6*(((double)totalnbgenerated/(double)totalnbconsumed)-1.0),(double)totalnbgenerated/time);
printf("memory SRCctxt: %d p: %d pint: %d tempbuf: %d\n",sizeof(TeensySRCcontext),((NTAPS*OVERSAMPLING+1)*(POLYORD+1)/2)*sizeof(double),((NTAPS*OVERSAMPLING+1)*(POLYORD+1)/2)*sizeof(int32_t),(WORKSIZ+2*OVERSAMPLING*NTAPS)*sizeof(double));
#endif
return(0);
}
void Fillinblock(int *inblock,int nbytes,FILE *inputfile) //fill SRC inblock with BLOCKLEN samples from spdif
{
int i,j=0;
unsigned int itemp;
unsigned char cbuf;
while (j<BLOCKLEN) //fill the buffer with new samples
{
itemp=0;
for (i=0;i<nbytes;i++)
{
cbuf=fgetc(inputfile);
itemp+=cbuf<<((i+4-nbytes)*8);
}
inblock[j++]=(int)itemp;
}
}
void FILLFIFOblock(int *outblock,int nbout,int *FIFO,int *FIFOlvl) //MT SRC outblock (BLOCKLEN+-N samples) to FIFO
{
int ibuf;
while (nbout>0)
{
ibuf=*outblock++;
FIFO[(*FIFOlvl)--]=ibuf;
nbout--;
}
}
void MTFIFOblock(int *FIFO,int *FIFOlvl,int nbytes,FILE *outputfile,int *pos) //MT BLOCKLEN samples from FIFO to i2s
{
int outptr=FIFOSIZ-1;
int i,j,ibuf;
unsigned char cbuf;
//MT BLOCKLEN samples from FIFO to file
for (j=0;j<BLOCKLEN;j++)
{
(*FIFOlvl)++;
ibuf=FIFO[outptr--];
for (i=0;i<nbytes;i++)
{
cbuf=ibuf>>((i+4-nbytes)*8);
fputc(cbuf,outputfile);
(*pos)++;
}
}
//move down remains of the FIFO to the bottom
j=outptr;
outptr=FIFOSIZ-1;
while (j>0)
{
FIFO[outptr--]=FIFO[j--];
}
}