diff --git a/lib/ft2/ft2.f90 b/lib/ft2/ft2.f90 new file mode 100644 index 000000000..204a8a78f --- /dev/null +++ b/lib/ft2/ft2.f90 @@ -0,0 +1,154 @@ +program ft2 + + use packjt77 + include 'gcom1.f90' + integer ft2audio,ptt + logical allok + character*20 pttport + character*8 arg + integer*2 iwave2(30000) + + allok=.true. +! Get home-station details + open(10,file='ft2.ini',status='old',err=1) + go to 2 +1 print*,'Cannot open ft2.ini' + allok=.false. +2 read(10,*,err=3) mycall,mygrid,ndevin,ndevout,pttport,exch + go to 4 +3 print*,'Error reading ft2.ini' + allok=.false. +4 if(index(pttport,'/').lt.1) read(pttport,*) nport + hiscall=' ' + hiscall_next=' ' + idevin=ndevin + idevout=ndevout + call padevsub(idevin,idevout) + if(idevin.ne.ndevin .or. idevout.ne.ndevout) allok=.false. + i1=ptt(nport,1,1,iptt) + if(i1.lt.0 .and. nport.ne.0) allok=.false. + if(.not.allok) then + write(*,"('Please fix setup error(s) and restart.')") + go to 999 + endif + + nright=1 + iwrite=0 + iwave=0 + nwave=NTZ + nfsample=12000 + ngo=1 + npabuf=1280 + ntxok=0 + ntransmitting=0 + tx_once=.false. + snrdb=99.0 + txmsg='CQ K1JT FN20' + + nargs=iargc() + if(nargs.eq.3) then + call getarg(1,txmsg) + call getarg(2,arg) + read(arg,*) f0 + call getarg(3,arg) + read(arg,*) snrdb + nTxOK=1 + tx_once=.true. + call ft2_iwave(txmsg,f0,snrdb,iwave) + endif + + iwave2(1:23040)=iwave + iwave2(23041:30000)=0 + nutc=0 + nfqso=nint(f0) + + call ft2_decode(nutc,nfqso,iwave2) + + ierr=ft2audio(idevin,idevout,npabuf,nright,y1,y2,NRING,iwrite,itx, & + iwave,nwave,nfsample,nTxOK,nTransmitting,ngo) + if(ierr.ne.0) then + print*,'Error',ierr,' in JTaudio, you will only be able to work offline.' + else + write(*,1006) +1006 format('Audio streams terminated normally.') + endif + +999 end program ft2 + +subroutine update(total_time,ic1,ic2) + + real*8 total_time + integer ptt + logical transmitted + integer*2 id(30000),id2(30000) + include 'gcom1.f90' + data nt0/-1/,transmitted/.false./,snr/0.0/ + save nt0,transmitted,snr + + if(ic1.ne.0 .or. ic2.ne.0) then + if(ic1.eq.27 .and. ic2.eq.0) ngo=0 !ESC + if(nTxOK.eq.0 .and. ntransmitting.eq.0) then + nd=0 + if(ic1.eq.0 .and. ic2.eq.59) nd=7 !F1 + if(ic1.eq.0 .and. ic2.eq.60) nd=6 !F2 + if(ic1.eq.0 .and. ic2.eq.61) nd=5 !F3 + if(ic1.eq.0 .and. ic2.eq.62) nd=4 !F4 + if(ic1.eq.0 .and. ic2.eq.63) nd=3 !F5 + if(nd.gt.0) then + i1=ptt(nport,1,1,iptt) + ntxok=1 + n=1000 + nwave=NTZ + do i=1,nwave/nd + ib=i*nd + ia=ib-nd+1 + iwave(ia:ib)=n + n=-n + enddo + endif + endif + if(ic1.eq.13 .and. ic2.eq.0) hiscall=hiscall_next + endif + + if(ntransmitting.eq.1) transmitted=.true. + if(ntransmitting.eq.0) then + if(iptt.eq.1 .and. nport.gt.0) i1=ptt(nport,0,1,iptt) + if(tx_once .and. transmitted) stop + endif + + nt=2*total_time + if(nt.gt.nt0 .or. ic1.ne.0 .or. ic2.ne.0) then + k=iwrite-6000 + if(k.lt.1) k=k+NRING + sq=0. + do i=1,6000 + k=k+1 + if(k.gt.NRING) k=k-NRING + x=y1(k) + sq=sq + x*x + enddo + sigdb=0. + if(sq.gt.0.0) sigdb=db(sq/6000.0) + k=iwrite-30000 + if(k.lt.1) k=k+NRING + do i=1,30000 + k=k+1 + if(k.gt.NRING) k=k-NRING + id(i)=y1(k) + enddo + nutc=0 + nfqso=1500 + call ft2_iwave(txmsg,1500.0,snr,id2) !### + snr=snr-1.0 + call ft2_decode(nutc,nfqso,id2) !### +!### call ft2_decode(nutc,nfqso,id) + + write(*,1010) nt,total_time,iwrite,itx,ntxok,ntransmitting,sigdb,snr +1010 format(i6,f9.3,4i6,f6.1,f6.0) + nt0=nt + max1=0 + max2=0 + endif + + return +end subroutine update diff --git a/lib/ft2/ft2.ini b/lib/ft2/ft2.ini new file mode 100644 index 000000000..d66770d17 --- /dev/null +++ b/lib/ft2/ft2.ini @@ -0,0 +1,2 @@ +K1JT FN20 1 5 0 NJ +MyCall MyGrid AudioIn AudioOut PTTport Exch diff --git a/lib/ft2/ft2_decode.f90 b/lib/ft2/ft2_decode.f90 new file mode 100644 index 000000000..5647c8d02 --- /dev/null +++ b/lib/ft2/ft2_decode.f90 @@ -0,0 +1,277 @@ +subroutine ft2_decode(nutc,nfqso,iwave) + + use crc + use packjt77 + include 'ft2_params.f90' + character message*37,c77*77 + character*37 decodes(100) + character*120 data_dir + complex c2(0:NMAX/16-1) !Complex waveform + complex cb(0:NMAX/16-1) + complex cd(0:144*10-1) !Complex waveform + complex c1(0:9),c0(0:9) + complex ccor(0:1,144) + complex csum,cterm,cc0,cc1,csync1 + real*8 fMHz + + real a(5) + real rxdata(128),llr(128) !Soft symbols + real llr2(128) + real sbits(144),sbits1(144),sbits3(144) + real ps(0:8191),psbest(0:8191) + real candidate(3,100) + real savg(NH1) + integer*2 iwave(NMAX) !Generated full-length waveform + integer*1 message77(77),apmask(128),cw(128) + integer*1 hbits(144),hbits1(144),hbits3(144) + integer*1 s16(16) + logical unpk77_success + data s16/0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0/ + + fs=12000.0/NDOWN !Sample rate + dt=1/fs !Sample interval after downsample (s) + tt=NSPS*dt !Duration of "itone" symbols (s) + baud=1.0/tt !Keying rate for "itone" symbols (baud) + txt=NZ*dt !Transmission length (s) + twopi=8.0*atan(1.0) + h=0.8 !h=0.8 seems to be optimum for AWGN sensitivity (not for fading) + + dphi=twopi/2*baud*h*dt*16 ! dt*16 is samp interval after downsample + dphi0=-1*dphi + dphi1=+1*dphi + phi0=0.0 + phi1=0.0 + do i=0,9 + c1(i)=cmplx(cos(phi1),sin(phi1)) + c0(i)=cmplx(cos(phi0),sin(phi0)) + phi1=mod(phi1+dphi1,twopi) + phi0=mod(phi0+dphi0,twopi) + enddo + the=twopi*h/2.0 + cc1=cmplx(cos(the),-sin(the)) + cc0=cmplx(cos(the),sin(the)) + + data_dir="." + fMHz=7.074 + ncoh=1 + candidate=0.0 + ncand=0 + fa=375.0 + fb=3000.0 + syncmin=0.2 + maxcand=100 + nfqso=-1 + call getcandidates2(iwave,fa,fb,maxcand,savg,candidate,ncand) + ndecodes=0 + do icand=1,ncand + f0=candidate(1,icand) +! print*,'A',ncand,f0 + xsnr=1.0 + if( f0.le.375.0 .or. f0.ge.(5000.0-375.0) ) cycle + call ft2_downsample(iwave,f0,c2) ! downsample from 160s/Symbol to 10s/Symbol +! 750 samples/second here + ibest=-1 + sybest=-99. + dfbest=-1. +!### do if=-15,+15 + do if=-30,30 + df=if + a=0. + a(1)=-df + call twkfreq1(c2,NMAX/16,fs,a,cb) + do is=0,374 + csync1=0. + cterm=1 + do ib=1,16 + i1=(ib-1)*10+is + i2=i1+136*10 + if(s16(ib).eq.1) then + csync1=csync1+sum(cb(i1:i1+9)*conjg(c1(0:9)))*cterm + cterm=cterm*cc1 + else + csync1=csync1+sum(cb(i1:i1+9)*conjg(c0(0:9)))*cterm + cterm=cterm*cc0 + endif + enddo +! write(60,3001) if,is,abs(csync1) +!3001 format(2i6,f10.3) + if(abs(csync1).gt.sybest) then + ibest=is + sybest=abs(csync1) + dfbest=df + endif + enddo + enddo + +!dfbest=0.0 +!ibest=187 + a=0. + a(1)=-dfbest + call twkfreq1(c2,NMAX/16,fs,a,cb) + ib=ibest + cd=cb(ib:ib+144*10-1) + s2=sum(real(cd*conjg(cd)))/(10*144) + cd=cd/sqrt(s2) + do nseq=1,5 + if( nseq.eq.1 ) then ! noncoherent single-symbol detection + sbits1=0.0 + do ibit=1,144 + ib=(ibit-1)*10 + ccor(1,ibit)=sum(cd(ib:ib+9)*conjg(c1(0:9))) + ccor(0,ibit)=sum(cd(ib:ib+9)*conjg(c0(0:9))) + sbits1(ibit)=abs(ccor(1,ibit))-abs(ccor(0,ibit)) + hbits1(ibit)=0 + if(sbits1(ibit).gt.0) hbits1(ibit)=1 + enddo + sbits=sbits1 + hbits=hbits1 + sbits3=sbits1 + hbits3=hbits1 + elseif( nseq.ge.2 ) then + nbit=2*nseq-1 + numseq=2**(nbit) + ps=0 + do ibit=nbit/2+1,144-nbit/2 + ps=0.0 + pmax=0.0 + do iseq=0,numseq-1 + csum=0.0 + cterm=1.0 + k=1 + do i=nbit-1,0,-1 + ibb=iand(iseq/(2**i),1) + csum=csum+ccor(ibb,ibit-(nbit/2+1)+k)*cterm + if(ibb.eq.0) cterm=cterm*cc0 + if(ibb.eq.1) cterm=cterm*cc1 + k=k+1 + enddo + ps(iseq)=abs(csum) + if( ps(iseq) .gt. pmax ) then + pmax=ps(iseq) + ibflag=1 + endif + enddo + if( ibflag .eq. 1 ) then + psbest=ps + ibflag=0 + endif + call getbitmetric(2**(nbit/2),psbest,numseq,sbits3(ibit)) + hbits3(ibit)=0 + if(sbits3(ibit).gt.0) hbits3(ibit)=1 + enddo + sbits=sbits3 + hbits=hbits3 + endif + nsync_qual=count(hbits(1:16).eq.s16) + if(nsync_qual.lt.10) exit + rxdata=sbits(17:144) + rxav=sum(rxdata(1:128))/128.0 + rx2av=sum(rxdata(1:128)*rxdata(1:128))/128.0 + rxsig=sqrt(rx2av-rxav*rxav) + rxdata=rxdata/rxsig + sigma=0.80 + llr(1:128)=2*rxdata/(sigma*sigma) + apmask=0 + max_iterations=40 + do ibias=0,0 + llr2=llr + if(ibias.eq.1) llr2=llr+0.4 + if(ibias.eq.2) llr2=llr-0.4 + call bpdecode128_90(llr2,apmask,max_iterations,message77,cw,nharderror,niterations) + if(nharderror.ge.0) exit + enddo + nhardmin=-1 + if(sum(message77).eq.0) cycle + if( nharderror.ge.0 ) then + write(c77,'(77i1)') message77(1:77) + call unpack77(c77,message,unpk77_success) + idupe=0 + do i=1,ndecodes + if(decodes(i).eq.message) idupe=1 + enddo + if(idupe.eq.1) goto 888 + ndecodes=ndecodes+1 + decodes(ndecodes)=message + nsnr=nint(xsnr) + freq=f0+dfbest + write(*,1212) nutc,nsnr,ibest/750.0,nint(freq),message, & + nseq,nharderror,nhardmin +1212 format(i4.4,i4,f6.2,i6,2x,a37,3i5) + goto 888 + endif + enddo ! nseq +888 continue + enddo !candidate list + +end subroutine ft2_decode + +subroutine getbitmetric(ib,ps,ns,xmet) + real ps(0:ns-1) + xm1=0 + xm0=0 + do i=0,ns-1 + if( iand(i/ib,1) .eq. 1 .and. ps(i) .gt. xm1 ) xm1=ps(i) + if( iand(i/ib,1) .eq. 0 .and. ps(i) .gt. xm0 ) xm0=ps(i) + enddo + xmet=xm1-xm0 + return +end subroutine getbitmetric + +subroutine downsample2(ci,f0,co) + parameter(NI=144*160,NH=NI/2,NO=NI/16) ! downsample from 200 samples per symbol to 10 + complex ci(0:NI-1),ct(0:NI-1) + complex co(0:NO-1) + fs=12000.0 + df=fs/NI + ct=ci + call four2a(ct,NI,1,-1,1) !c2c FFT to freq domain + i0=nint(f0/df) + ct=cshift(ct,i0) + co=0.0 + co(0)=ct(0) + b=8.0 + do i=1,NO/2 + arg=(i*df/b)**2 + filt=exp(-arg) + co(i)=ct(i)*filt + co(NO-i)=ct(NI-i)*filt + enddo + co=co/NO + call four2a(co,NO,1,1,1) !c2c FFT back to time domain + return +end subroutine downsample2 + +subroutine ft2_downsample(iwave,f0,c) + +! Input: i*2 data in iwave() at sample rate 12000 Hz +! Output: Complex data in c(), sampled at 1200 Hz + + include 'ft2_params.f90' + parameter (NFFT2=NMAX/16) + integer*2 iwave(NMAX) + complex c(0:NMAX/16-1) + complex c1(0:NFFT2-1) + complex cx(0:NMAX/2) + real x(NMAX) + equivalence (x,cx) + + BW=4.0*75 + df=12000.0/NMAX + x=iwave + call four2a(x,NMAX,1,-1,0) !r2c FFT to freq domain + ibw=nint(BW/df) + i0=nint(f0/df) + c1=0. + c1(0)=cx(i0) + do i=1,NFFT2/2 + arg=(i-1)*df/bw + win=exp(-arg*arg) + c1(i)=cx(i0+i)*win + c1(NFFT2-i)=cx(i0-i)*win + enddo + c1=c1/NFFT2 + call four2a(c1,NFFT2,1,1,1) !c2c FFT back to time domain + c=c1(0:NMAX/16-1) + return +end subroutine ft2_downsample + diff --git a/lib/ft2/ft2_iwave.f90 b/lib/ft2/ft2_iwave.f90 new file mode 100644 index 000000000..94445545d --- /dev/null +++ b/lib/ft2/ft2_iwave.f90 @@ -0,0 +1,66 @@ +subroutine ft2_iwave(msg37,f0,snrdb,iwave) + +! Generate waveform for experimental "FT2" mode + + use packjt77 + include 'ft2_params.f90' !Set various constants + parameter (NWAVE=NN*NSPS) + character msg37*37,msgsent37*37 + real wave(NMAX) + integer itone(NN) + integer*2 iwave(NMAX) !Generated full-length waveform + + twopi=8.0*atan(1.0) + fs=12000.0 !Sample rate (Hz) + dt=1.0/fs !Sample interval (s) + hmod=0.8 !Modulation index (MSK=0.5, FSK=1.0) + tt=NSPS*dt !Duration of symbols (s) + baud=1.0/tt !Keying rate (baud) + bw=1.5*baud !Occupied bandwidth (Hz) + txt=NZ*dt !Transmission length (s) + bandwidth_ratio=2500.0/(fs/2.0) + sig=sqrt(2*bandwidth_ratio) * 10.0**(0.05*snrdb) + if(snrdb.gt.90.0) sig=1.0 + txt=NN*NSPS/12000.0 + +! Source-encode, then get itone(): + itype=1 + call genft2(msg37,0,msgsent37,itone,itype) + + k=0 + phi=0.0 + do j=1,NN !Generate real waveform + dphi=twopi*(f0*dt+(hmod/2.0)*(2*itone(j)-1)/real(NSPS)) + do i=1,NSPS + k=k+1 + wave(k)=sig*sin(phi) + phi=mod(phi+dphi,twopi) + enddo + enddo + kz=k + + peak=maxval(abs(wave(1:kz))) +! nslots=1 +! if(width.gt.0.0) call filt8(f0,nslots,width,wave) + + if(snrdb.lt.90) then + do i=1,NMAX !Add gaussian noise at specified SNR + xnoise=gran() + wave(i)=wave(i) + xnoise + enddo + endif + + gain=1.0 + if(snrdb.lt.90.0) then + wave=gain*wave + else + datpk=maxval(abs(wave)) + fac=32767.0/datpk + wave=fac*wave + endif + + if(any(abs(wave).gt.32767.0)) print*,"Warning - data will be clipped." + iwave(1:kz)=nint(wave(1:kz)) + + return +end subroutine ft2_iwave diff --git a/lib/ft2/ft2_params.f90 b/lib/ft2/ft2_params.f90 new file mode 100644 index 000000000..4751e47e4 --- /dev/null +++ b/lib/ft2/ft2_params.f90 @@ -0,0 +1,12 @@ +! LDPC (128,90) code +parameter (KK=90) !Information bits (77 + CRC13) +parameter (ND=128) !Data symbols +parameter (NS=16) !Sync symbols (2x8) +parameter (NN=NS+ND) !Total channel symbols (144) +parameter (NSPS=160) !Samples per symbol at 12000 S/s +parameter (NZ=NSPS*NN) !Samples in full 1.92 s waveform (23040) +parameter (NMAX=30000) !Samples in iwave (2.5*12000) +parameter (NFFT1=400, NH1=NFFT1/2) !Length of FFTs for symbol spectra +parameter (NSTEP=NSPS/4) !Rough time-sync step size +parameter (NHSYM=NMAX/NSTEP-3) !Number of symbol spectra (1/4-sym steps) +parameter (NDOWN=16) !Downsample factor diff --git a/lib/ft2/ft2audio.c b/lib/ft2/ft2audio.c new file mode 100644 index 000000000..9d0589d36 --- /dev/null +++ b/lib/ft2/ft2audio.c @@ -0,0 +1,330 @@ +#include +#include "portaudio.h" +#include + +int iaa; +int icc; +double total_time=0.0; + +// Definition of structure pointing to the audio data +typedef struct +{ + int *iwrite; + int *itx; + int *TxOK; + int *Transmitting; + int *nwave; + int *nright; + int nbuflen; + int nfs; + short *y1; + short *y2; + short *iwave; +} paTestData; + +// Input callback routine: +static int +SoundIn( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) +{ + paTestData *data = (paTestData*)userData; + short *in = (short*)inputBuffer; + unsigned int i; + static int ia=0; + + if(*data->Transmitting) return 0; + + if(statusFlags!=0) printf("Status flags %d\n",(int)statusFlags); + + if((statusFlags&1) == 0) { + //increment buffer pointers only if data available + ia=*data->iwrite; + if(*data->nright==0) { //Use left channel for input + for(i=0; iy1[ia] = (*in++); + data->y2[ia] = (*in++); + ia++; + } + } else { //Use right channel + for(i=0; iy2[ia] = (*in++); + data->y1[ia] = (*in++); + ia++; + } + } + } + + if(ia >= data->nbuflen) ia=0; //Wrap buffer pointer if necessary + *data->iwrite = ia; //Save buffer pointer + iaa=ia; + total_time += (double)framesPerBuffer/12000.0; + return 0; +} + +// Output callback routine: +static int +SoundOut( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) +{ + paTestData *data = (paTestData*)userData; + short *wptr = (short*)outputBuffer; + unsigned int i,n; + static short int n2; + static int ic=0; + static int TxOKz=0; + + // printf("txOK: %d %d\n",TxOKz,*data->TxOK); + + if(*data->TxOK && (!TxOKz)) ic=0; + TxOKz=*data->TxOK; + *data->Transmitting=*data->TxOK; + + if(*data->TxOK) { + for(i=0 ; i < framesPerBuffer; i++ ) { + n2=data->iwave[ic]; + *wptr++ = n2; //left + *wptr++ = n2; //right + ic++; + + if(ic >= *data->nwave) { + *data->TxOK = 0; + *data->Transmitting = 0; + *data->iwrite = 0; //Reset Rx buffer pointer to 0 + ic=0; + break; + } + } + } else { + memset((void*)outputBuffer, 0, 2*sizeof(short)*framesPerBuffer); + } + *data->itx = icc; //Save buffer pointer + icc=ic; + return 0; +} + +/*******************************************************************/ +int ft2audio_(int *ndevin, int *ndevout, int *npabuf, int *nright, + short y1[], short y2[], int *nbuflen, int *iwrite, + int *itx, short iwave[], int *nwave, int *nfsample, + int *TxOK, int *Transmitting, int *ngo) + +{ + paTestData data; + PaStream *instream, *outstream; + PaStreamParameters inputParameters, outputParameters; + // PaStreamInfo *streamInfo; + + int nfpb = *npabuf; + int nSampleRate = *nfsample; + int ndevice_in = *ndevin; + int ndevice_out = *ndevout; + double dSampleRate = (double) *nfsample; + PaError err_init, err_open_in, err_open_out, err_start_in, err_start_out; + PaError err = 0; + + data.iwrite = iwrite; + data.itx = itx; + data.TxOK = TxOK; + data.Transmitting = Transmitting; + data.y1 = y1; + data.y2 = y2; + data.nbuflen = *nbuflen; + data.nright = nright; + data.nwave = nwave; + data.iwave = iwave; + data.nfs = nSampleRate; + + err_init = Pa_Initialize(); // Initialize PortAudio + + if(err_init) { + printf("Error initializing PortAudio.\n"); + printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_init), + err_init); + Pa_Terminate(); // I don't think we need this but... + return(-1); + } + + // printf("Opening device %d for input, %d for output...\n",ndevice_in,ndevice_out); + + inputParameters.device = ndevice_in; + inputParameters.channelCount = 2; + inputParameters.sampleFormat = paInt16; + inputParameters.suggestedLatency = 0.2; + inputParameters.hostApiSpecificStreamInfo = NULL; + +// Test if this configuration actually works, so we do not run into an ugly assertion + err_open_in = Pa_IsFormatSupported(&inputParameters, NULL, dSampleRate); + + if (err_open_in == 0) { + err_open_in = Pa_OpenStream( + &instream, //address of stream + &inputParameters, + NULL, + dSampleRate, //Sample rate + nfpb, //Frames per buffer + paNoFlag, + (PaStreamCallback *)SoundIn, //Callback routine + (void *)&data); //address of data structure + + if(err_open_in) { // We should have no error here usually + printf("Error opening input audio stream:\n"); + printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_open_in), err_open_in); + + err = 1; + } else { + // printf("Successfully opened audio input.\n"); + } + } else { + printf("Error opening input audio stream.\n"); + printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_open_in), err_open_in); + + err = 1; + } + + outputParameters.device = ndevice_out; + outputParameters.channelCount = 2; + outputParameters.sampleFormat = paInt16; + outputParameters.suggestedLatency = 0.2; + outputParameters.hostApiSpecificStreamInfo = NULL; + +// Test if this configuration actually works, so we do not run into an ugly assertion + err_open_out = Pa_IsFormatSupported(NULL, &outputParameters, dSampleRate); + + if (err_open_out == 0) { + err_open_out = Pa_OpenStream( + &outstream, //address of stream + NULL, + &outputParameters, + dSampleRate, //Sample rate + nfpb, //Frames per buffer + paNoFlag, + (PaStreamCallback *)SoundOut, //Callback routine + (void *)&data); //address of data structure + + if(err_open_out) { // We should have no error here usually + printf("Error opening output audio stream!\n"); + printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_open_out), err_open_out); + + err += 2; + } else { + // printf("Successfully opened audio output.\n"); + } + } else { + printf("Error opening output audio stream.\n"); + printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_open_out), err_open_out); + + err += 2; + } + + // if there was no error in opening both streams start them + if (err == 0) { + err_start_in = Pa_StartStream(instream); //Start input stream + + if(err_start_in) { + printf("Error starting input audio stream!\n"); + printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_start_in), err_start_in); + + err += 4; + } + + err_start_out = Pa_StartStream(outstream); //Start output stream + + if(err_start_out) { + printf("Error starting output audio stream!\n"); + printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_start_out), err_start_out); + + err += 8; + } + } + + if (err == 0) printf("Audio streams running normally.\n******************************************************************\n"); + + while( Pa_IsStreamActive(instream) && (*ngo != 0) && (err == 0) ) { + int ic1=0; + int ic2=0; + if(_kbhit()) ic1 = _getch(); + if(_kbhit()) ic2 = _getch(); + // if(ic1!=0 || ic2!=0) printf("%d %d %d\n",iaa,ic1,ic2); + update_(&total_time,&ic1,&ic2); + Pa_Sleep(100); + } + + Pa_AbortStream(instream); // Abort stream + Pa_CloseStream(instream); // Close stream, we're done. + Pa_AbortStream(outstream); // Abort stream + Pa_CloseStream(outstream); // Close stream, we're done. + + Pa_Terminate(); + + return(err); +} + + +int padevsub_(int *idevin, int *idevout) +{ + int numdev,ndefin,ndefout; + int nchin[101], nchout[101]; + int i, devIdx; + int numDevices; + const PaDeviceInfo *pdi; + PaError err; + + Pa_Initialize(); + numDevices = Pa_GetDeviceCount(); + numdev = numDevices; + + if( numDevices < 0 ) { + err = numDevices; + Pa_Terminate(); + return err; + } + + if ((devIdx = Pa_GetDefaultInputDevice()) > 0) { + ndefin = devIdx; + } else { + ndefin = 0; + } + + if ((devIdx = Pa_GetDefaultOutputDevice()) > 0) { + ndefout = devIdx; + } else { + ndefout = 0; + } + + printf("\nAudio Input Output Device Name\n"); + printf("Device Channels Channels\n"); + printf("------------------------------------------------------------------\n"); + + for( i=0; i < numDevices; i++ ) { + pdi = Pa_GetDeviceInfo(i); +// if(i == Pa_GetDefaultInputDevice()) ndefin = i; +// if(i == Pa_GetDefaultOutputDevice()) ndefout = i; + nchin[i]=pdi->maxInputChannels; + nchout[i]=pdi->maxOutputChannels; + printf(" %2d %2d %2d %s\n",i,nchin[i],nchout[i],pdi->name); + } + + printf("\nUser requested devices: Input = %2d Output = %2d\n", + *idevin,*idevout); + printf("Default devices: Input = %2d Output = %2d\n", + ndefin,ndefout); + if((*idevin<0) || (*idevin>=numdev)) *idevin=ndefin; + if((*idevout<0) || (*idevout>=numdev)) *idevout=ndefout; + if((*idevin==0) && (*idevout==0)) { + *idevin=ndefin; + *idevout=ndefout; + } + printf("Will open devices: Input = %2d Output = %2d\n", + *idevin,*idevout); + + Pa_Terminate(); + + return 0; +} + diff --git a/lib/ft2/g4.cmd b/lib/ft2/g4.cmd new file mode 100644 index 000000000..d6d9134e4 --- /dev/null +++ b/lib/ft2/g4.cmd @@ -0,0 +1,6 @@ +gcc -c ft2audio.c +gcc -c ptt.c +gfortran -c ../77bit/packjt77.f90 +gfortran -c ../crc.f90 +gfortran -o ft2 -fbounds-check -fno-second-underscore -Wall -Wno-conversion -Wno-character-truncation ft2.f90 ft2_iwave.f90 ft2_decode.f90 getcandidates2.f90 ft2audio.o ptt.o libwsjt_fort.a libwsjt_cxx.a libportaudio.a ../libfftw3f_win.a -lwinmm +rm *.o *.mod diff --git a/lib/ft2/gcom1.f90 b/lib/ft2/gcom1.f90 new file mode 100644 index 000000000..fa2262127 --- /dev/null +++ b/lib/ft2/gcom1.f90 @@ -0,0 +1,30 @@ +! Variable Purpose +!--------------------------------------------------------------------------- +integer NRING !Length of Rx ring buffer +integer NTZ !Length of Tx waveform in samples +parameter(NRING=32768) !About 2.7 s at 12000 sam/sec +parameter(NTZ=23040) !144*160 +parameter(NMAX=30000) !2.5*12000 +real snrdb +integer ndevin !Device# for audio input +integer ndevout !Device# for audio output +integer iwrite !Pointer to Rx ring buffer +integer itx !Pointer to Tx buffer +integer ngo !Set to 0 to terminate audio streams +integer nTransmitting !Actually transmitting? +integer nTxOK !OK to transmit? +integer nport !COM port for PTT +logical tx_once !Transmit one message, then exit +integer*2 y1 !Ring buffer for audio channel 0 +integer*2 y2 !Ring buffer for audio channel 1 +integer*2 iwave !Data for Tx audio +character*6 mycall +character*6 hiscall +character*6 hiscall_next +character*4 mygrid +character*3 exch +character*37 txmsg + +common/gcom1/snrdb,ndevin,ndevout,iwrite,itx,ngo,nTransmitting,nTxOK,nport, & + tx_once, y1(NRING),y2(NRING),iwave(NTZ),mycall,hiscall, & + hiscall_next,mygrid,exch,txmsg diff --git a/lib/ft2/genft2.f90 b/lib/ft2/genft2.f90 new file mode 100644 index 000000000..f416c3616 --- /dev/null +++ b/lib/ft2/genft2.f90 @@ -0,0 +1,86 @@ +subroutine genft2(msg0,ichk,msgsent,i4tone,itype) +! s8 + 48bits + s8 + 80 bits = 144 bits (72ms message duration) +! +! Encode an MSK144 message +! Input: +! - msg0 requested message to be transmitted +! - ichk if ichk=1, return only msgsent +! if ichk.ge.10000, set imsg=ichk-10000 for short msg +! - msgsent message as it will be decoded +! - i4tone array of audio tone values, 0 or 1 +! - itype message type +! 1 = 77 bit message +! 7 = 16 bit message " Rpt" + + use iso_c_binding, only: c_loc,c_size_t + use packjt77 + character*37 msg0 + character*37 message !Message to be generated + character*37 msgsent !Message as it will be received + character*77 c77 + integer*4 i4tone(144) + integer*1 codeword(128) + integer*1 msgbits(77) + integer*1 bitseq(144) !Tone #s, data and sync (values 0-1) + integer*1 s16(16) + real*8 xi(864),xq(864),pi,twopi + data s16/0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0/ + equivalence (ihash,i1hash) + logical unpk77_success + + nsym=128 + pi=4.0*atan(1.0) + twopi=8.*atan(1.0) + + message(1:37)=' ' + itype=1 + if(msg0(1:1).eq.'@') then !Generate a fixed tone + read(msg0(2:5),*,end=1,err=1) nfreq !at specified frequency + go to 2 +1 nfreq=1000 +2 i4tone(1)=nfreq + else + message=msg0 + + do i=1, 37 + if(ichar(message(i:i)).eq.0) then + message(i:37)=' ' + exit + endif + enddo + do i=1,37 !Strip leading blanks + if(message(1:1).ne.' ') exit + message=message(i+1:) + enddo + + if(message(1:1).eq.'<') then + i2=index(message,'>') + i1=0 + if(i2.gt.0) i1=index(message(1:i2),' ') + if(i1.gt.0) then + call genmsk40(message,msgsent,ichk,i4tone,itype) + if(itype.lt.0) go to 999 + i4tone(41)=-40 + go to 999 + endif + endif + + i3=-1 + n3=-1 + call pack77(message,i3,n3,c77) + call unpack77(c77,msgsent,unpk77_success) !Unpack to get msgsent + + if(ichk.eq.1) go to 999 + read(c77,"(77i1)") msgbits + call encode_128_90(msgbits,codeword) + +!Create 144-bit channel vector: + bitseq=0 + bitseq(1:16)=s16 + bitseq(17:144)=codeword + + i4tone=bitseq + endif + +999 return +end subroutine genft2 diff --git a/lib/ft2/getcandidates2.f90 b/lib/ft2/getcandidates2.f90 new file mode 100644 index 000000000..d7e75097c --- /dev/null +++ b/lib/ft2/getcandidates2.f90 @@ -0,0 +1,64 @@ +subroutine getcandidates2(id,fa,fb,maxcand,savg,candidate,ncand) + +! For now, hardwired to find the largest peak in the average spectrum + + include 'ft2_params.f90' + real s(NH1,NHSYM) + real savg(NH1),savsm(NH1) + real x(NFFT1) + complex cx(0:NH1) + real candidate(3,100) + integer*2 id(NMAX) + integer*1 s8(8) + integer indx(NH1) + data s8/0,1,1,1,0,0,1,0/ + equivalence (x,cx) + +! Compute symbol spectra, stepping by NSTEP steps. + savg=0. + tstep=NSTEP/12000.0 + df=12000.0/NFFT1 !3.125 Hz + fac=1.0/300.0 + do j=1,NHSYM + ia=(j-1)*NSTEP + 1 + ib=ia+NSPS-1 + x(1:NSPS)=fac*id(ia:ib) + x(NSPS+1:)=0. + call four2a(x,NFFT1,1,-1,0) !r2c FFT + do i=1,NH1 + s(i,j)=real(cx(i))**2 + aimag(cx(i))**2 + enddo + savg=savg + s(1:NH1,j) !Average spectrum + enddo + savsm=0. + do i=2,NH1-1 + savsm(i)=sum(savg(i-1:i+1))/3. + enddo + savsm(1)=savg(1) + savsm(NH1)=savg(NH1) + + nfa=nint(fa/df) + nfb=nint(fb/df) + np=nfb-nfa+1 + indx=0 + call indexx(savsm(nfa:nfb),np,indx) + xn=savsm(nfa+indx(nint(0.3*np))) + savsm=savsm/xn + imax=-1 + xmax=-99. + do i=2,NH1-1 + if(savsm(i).gt.savsm(i-1).and. & + savsm(i).gt.savsm(i+1).and. & + savsm(i).gt.xmax) then + xmax=savsm(i) + imax=i + endif + enddo + f0=imax*df + if(xmax.gt.1.2) then + if(ncand.lt.maxcand) ncand=ncand+1 + candidate(1,ncand)=f0 + endif + +return +end subroutine getcandidates2 diff --git a/lib/ft2/libportaudio.a b/lib/ft2/libportaudio.a new file mode 100644 index 000000000..f20f02c16 Binary files /dev/null and b/lib/ft2/libportaudio.a differ diff --git a/lib/ft2/libwsjt_cxx.a b/lib/ft2/libwsjt_cxx.a new file mode 100644 index 000000000..c7dde28c8 Binary files /dev/null and b/lib/ft2/libwsjt_cxx.a differ diff --git a/lib/ft2/libwsjt_fort.a b/lib/ft2/libwsjt_fort.a new file mode 100644 index 000000000..84c1c6f09 Binary files /dev/null and b/lib/ft2/libwsjt_fort.a differ diff --git a/lib/ft2/portaudio.h b/lib/ft2/portaudio.h new file mode 100644 index 000000000..250fba021 --- /dev/null +++ b/lib/ft2/portaudio.h @@ -0,0 +1,1123 @@ + +#ifndef PORTAUDIO_H +#define PORTAUDIO_H +/* + * $Id: portaudio.h,v 1.1 2005/11/29 21:27:24 joe Exp $ + * PortAudio Portable Real-Time Audio Library + * PortAudio API Header File + * Latest version available at: http://www.portaudio.com/ + * + * Copyright (c) 1999-2002 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** @file + @brief The PortAudio API. +*/ + + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +/** Retrieve the release number of the currently running PortAudio build, + eg 1900. +*/ +int Pa_GetVersion( void ); + + +/** Retrieve a textual description of the current PortAudio build, + eg "PortAudio V19-devel 13 October 2002". +*/ +const char* Pa_GetVersionText( void ); + + +/** Error codes returned by PortAudio functions. + Note that with the exception of paNoError, all PaErrorCodes are negative. +*/ + +typedef int PaError; +typedef enum PaErrorCode +{ + paNoError = 0, + + paNotInitialized = -10000, + paUnanticipatedHostError, + paInvalidChannelCount, + paInvalidSampleRate, + paInvalidDevice, + paInvalidFlag, + paSampleFormatNotSupported, + paBadIODeviceCombination, + paInsufficientMemory, + paBufferTooBig, + paBufferTooSmall, + paNullCallback, + paBadStreamPtr, + paTimedOut, + paInternalError, + paDeviceUnavailable, + paIncompatibleHostApiSpecificStreamInfo, + paStreamIsStopped, + paStreamIsNotStopped, + paInputOverflowed, + paOutputUnderflowed, + paHostApiNotFound, + paInvalidHostApi, + paCanNotReadFromACallbackStream, /**< @todo review error code name */ + paCanNotWriteToACallbackStream, /**< @todo review error code name */ + paCanNotReadFromAnOutputOnlyStream, /**< @todo review error code name */ + paCanNotWriteToAnInputOnlyStream, /**< @todo review error code name */ + paIncompatibleStreamHostApi +} PaErrorCode; + + +/** Translate the supplied PortAudio error code into a human readable + message. +*/ +const char *Pa_GetErrorText( PaError errorCode ); + + +/** Library initialization function - call this before using PortAudio. + This function initialises internal data structures and prepares underlying + host APIs for use. This function MUST be called before using any other + PortAudio API functions. + + If Pa_Initialize() is called multiple times, each successful + call must be matched with a corresponding call to Pa_Terminate(). + Pairs of calls to Pa_Initialize()/Pa_Terminate() may overlap, and are not + required to be fully nested. + + Note that if Pa_Initialize() returns an error code, Pa_Terminate() should + NOT be called. + + @return paNoError if successful, otherwise an error code indicating the cause + of failure. + + @see Pa_Terminate +*/ +PaError Pa_Initialize( void ); + + +/** Library termination function - call this when finished using PortAudio. + This function deallocates all resources allocated by PortAudio since it was + initializied by a call to Pa_Initialize(). In cases where Pa_Initialise() has + been called multiple times, each call must be matched with a corresponding call + to Pa_Terminate(). The final matching call to Pa_Terminate() will automatically + close any PortAudio streams that are still open. + + Pa_Terminate() MUST be called before exiting a program which uses PortAudio. + Failure to do so may result in serious resource leaks, such as audio devices + not being available until the next reboot. + + @return paNoError if successful, otherwise an error code indicating the cause + of failure. + + @see Pa_Initialize +*/ +PaError Pa_Terminate( void ); + + + +/** The type used to refer to audio devices. Values of this type usually + range from 0 to (Pa_DeviceCount-1), and may also take on the PaNoDevice + and paUseHostApiSpecificDeviceSpecification values. + + @see Pa_DeviceCount, paNoDevice, paUseHostApiSpecificDeviceSpecification +*/ +typedef int PaDeviceIndex; + + +/** A special PaDeviceIndex value indicating that no device is available, + or should be used. + + @see PaDeviceIndex +*/ +#define paNoDevice ((PaDeviceIndex)-1) + + +/** A special PaDeviceIndex value indicating that the device(s) to be used + are specified in the host api specific stream info structure. + + @see PaDeviceIndex +*/ +#define paUseHostApiSpecificDeviceSpecification ((PaDeviceIndex)-2) + + +/* Host API enumeration mechanism */ + +/** The type used to enumerate to host APIs at runtime. Values of this type + range from 0 to (Pa_GetHostApiCount()-1). + + @see Pa_GetHostApiCount +*/ +typedef int PaHostApiIndex; + + +/** Retrieve the number of available host APIs. Even if a host API is + available it may have no devices available. + + @return A non-negative value indicating the number of available host APIs + or, a PaErrorCode (which are always negative) if PortAudio is not initialized + or an error is encountered. + + @see PaHostApiIndex +*/ +PaHostApiIndex Pa_GetHostApiCount( void ); + + +/** Retrieve the index of the default host API. The default host API will be + the lowest common denominator host API on the current platform and is + unlikely to provide the best performance. + + @return A non-negative value ranging from 0 to (Pa_GetHostApiCount()-1) + indicating the default host API index or, a PaErrorCode (which are always + negative) if PortAudio is not initialized or an error is encountered. +*/ +PaHostApiIndex Pa_GetDefaultHostApi( void ); + + +/** Unchanging unique identifiers for each supported host API. This type + is used in the PaHostApiInfo structure. The values are guaranteed to be + unique and to never change, thus allowing code to be written that + conditionally uses host API specific extensions. + + New type ids will be allocated when support for a host API reaches + "public alpha" status, prior to that developers should use the + paInDevelopment type id. + + @see PaHostApiInfo +*/ +typedef enum PaHostApiTypeId +{ + paInDevelopment=0, /* use while developing support for a new host API */ + paDirectSound=1, + paMME=2, + paASIO=3, + paSoundManager=4, + paCoreAudio=5, + paOSS=7, + paALSA=8, + paAL=9, + paBeOS=10, + paWDMKS=11, + paJACK=12 +} PaHostApiTypeId; + + +/** A structure containing information about a particular host API. */ + +typedef struct PaHostApiInfo +{ + /** this is struct version 1 */ + int structVersion; + /** The well known unique identifier of this host API @see PaHostApiTypeId */ + PaHostApiTypeId type; + /** A textual description of the host API for display on user interfaces. */ + const char *name; + + /** The number of devices belonging to this host API. This field may be + used in conjunction with Pa_HostApiDeviceIndexToDeviceIndex() to enumerate + all devices for this host API. + @see Pa_HostApiDeviceIndexToDeviceIndex + */ + int deviceCount; + + /** The the default input device for this host API. The value will be a + device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice + if no default input device is available. + */ + PaDeviceIndex defaultInputDevice; + + /** The the default output device for this host API. The value will be a + device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice + if no default output device is available. + */ + PaDeviceIndex defaultOutputDevice; + +} PaHostApiInfo; + + +/** Retrieve a pointer to a structure containing information about a specific + host Api. + + @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1) + + @return A pointer to an immutable PaHostApiInfo structure describing + a specific host API. If the hostApi parameter is out of range or an error + is encountered, the function returns NULL. + + The returned structure is owned by the PortAudio implementation and must not + be manipulated or freed. The pointer is only guaranteed to be valid between + calls to Pa_Initialize() and Pa_Terminate(). +*/ +const PaHostApiInfo * Pa_GetHostApiInfo( PaHostApiIndex hostApi ); + + +/** Convert a static host API unique identifier, into a runtime + host API index. + + @param type A unique host API identifier belonging to the PaHostApiTypeId + enumeration. + + @return A valid PaHostApiIndex ranging from 0 to (Pa_GetHostApiCount()-1) or, + a PaErrorCode (which are always negative) if PortAudio is not initialized + or an error is encountered. + + The paHostApiNotFound error code indicates that the host API specified by the + type parameter is not available. + + @see PaHostApiTypeId +*/ +PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type ); + + +/** Convert a host-API-specific device index to standard PortAudio device index. + This function may be used in conjunction with the deviceCount field of + PaHostApiInfo to enumerate all devices for the specified host API. + + @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1) + + @param hostApiDeviceIndex A valid per-host device index in the range + 0 to (Pa_GetHostApiInfo(hostApi)->deviceCount-1) + + @return A non-negative PaDeviceIndex ranging from 0 to (Pa_GetDeviceCount()-1) + or, a PaErrorCode (which are always negative) if PortAudio is not initialized + or an error is encountered. + + A paInvalidHostApi error code indicates that the host API index specified by + the hostApi parameter is out of range. + + A paInvalidDevice error code indicates that the hostApiDeviceIndex parameter + is out of range. + + @see PaHostApiInfo +*/ +PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi, + int hostApiDeviceIndex ); + + + +/** Structure used to return information about a host error condition. +*/ +typedef struct PaHostErrorInfo{ + PaHostApiTypeId hostApiType; /**< the host API which returned the error code */ + long errorCode; /**< the error code returned */ + const char *errorText; /**< a textual description of the error if available, otherwise a zero-length string */ +}PaHostErrorInfo; + + +/** Return information about the last host error encountered. The error + information returned by Pa_GetLastHostErrorInfo() will never be modified + asyncronously by errors occurring in other PortAudio owned threads + (such as the thread that manages the stream callback.) + + This function is provided as a last resort, primarily to enhance debugging + by providing clients with access to all available error information. + + @return A pointer to an immutable structure constaining information about + the host error. The values in this structure will only be valid if a + PortAudio function has previously returned the paUnanticipatedHostError + error code. +*/ +const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void ); + + + +/* Device enumeration and capabilities */ + +/** Retrieve the number of available devices. The number of available devices + may be zero. + + @return A non-negative value indicating the number of available devices or, + a PaErrorCode (which are always negative) if PortAudio is not initialized + or an error is encountered. +*/ +PaDeviceIndex Pa_GetDeviceCount( void ); + + +/** Retrieve the index of the default input device. The result can be + used in the inputDevice parameter to Pa_OpenStream(). + + @return The default input device index for the default host API, or paNoDevice + if no default input device is available or an error was encountered. +*/ +PaDeviceIndex Pa_GetDefaultInputDevice( void ); + + +/** Retrieve the index of the default output device. The result can be + used in the outputDevice parameter to Pa_OpenStream(). + + @return The default output device index for the defualt host API, or paNoDevice + if no default output device is available or an error was encountered. + + @note + On the PC, the user can specify a default device by + setting an environment variable. For example, to use device #1. +
+ set PA_RECOMMENDED_OUTPUT_DEVICE=1
+
+ The user should first determine the available device ids by using + the supplied application "pa_devs". +*/ +PaDeviceIndex Pa_GetDefaultOutputDevice( void ); + + +/** The type used to represent monotonic time in seconds that can be used + for syncronisation. The type is used for the outTime argument to the + PaStreamCallback and as the result of Pa_GetStreamTime(). + + @see PaStreamCallback, Pa_GetStreamTime +*/ +typedef double PaTime; + + +/** A type used to specify one or more sample formats. Each value indicates + a possible format for sound data passed to and from the stream callback, + Pa_ReadStream and Pa_WriteStream. + + The standard formats paFloat32, paInt16, paInt32, paInt24, paInt8 + and aUInt8 are usually implemented by all implementations. + + The floating point representation (paFloat32) uses +1.0 and -1.0 as the + maximum and minimum respectively. + + paUInt8 is an unsigned 8 bit format where 128 is considered "ground" + + The paNonInterleaved flag indicates that a multichannel buffer is passed + as a set of non-interleaved pointers. + + @see Pa_OpenStream, Pa_OpenDefaultStream, PaDeviceInfo + @see paFloat32, paInt16, paInt32, paInt24, paInt8 + @see paUInt8, paCustomFormat, paNonInterleaved +*/ +typedef unsigned long PaSampleFormat; + + +#define paFloat32 ((PaSampleFormat) 0x00000001) /**< @see PaSampleFormat */ +#define paInt32 ((PaSampleFormat) 0x00000002) /**< @see PaSampleFormat */ +#define paInt24 ((PaSampleFormat) 0x00000004) /**< Packed 24 bit format. @see PaSampleFormat */ +#define paInt16 ((PaSampleFormat) 0x00000008) /**< @see PaSampleFormat */ +#define paInt8 ((PaSampleFormat) 0x00000010) /**< @see PaSampleFormat */ +#define paUInt8 ((PaSampleFormat) 0x00000020) /**< @see PaSampleFormat */ +#define paCustomFormat ((PaSampleFormat) 0x00010000)/**< @see PaSampleFormat */ + +#define paNonInterleaved ((PaSampleFormat) 0x80000000) + +/** A structure providing information and capabilities of PortAudio devices. + Devices may support input, output or both input and output. +*/ +typedef struct PaDeviceInfo +{ + int structVersion; /* this is struct version 2 */ + const char *name; + PaHostApiIndex hostApi; /* note this is a host API index, not a type id*/ + + int maxInputChannels; + int maxOutputChannels; + + /* Default latency values for interactive performance. */ + PaTime defaultLowInputLatency; + PaTime defaultLowOutputLatency; + /* Default latency values for robust non-interactive applications (eg. playing sound files). */ + PaTime defaultHighInputLatency; + PaTime defaultHighOutputLatency; + + double defaultSampleRate; +} PaDeviceInfo; + + +/** Retrieve a pointer to a PaDeviceInfo structure containing information + about the specified device. + @return A pointer to an immutable PaDeviceInfo structure. If the device + parameter is out of range the function returns NULL. + + @param device A valid device index in the range 0 to (Pa_GetDeviceCount()-1) + + @note PortAudio manages the memory referenced by the returned pointer, + the client must not manipulate or free the memory. The pointer is only + guaranteed to be valid between calls to Pa_Initialize() and Pa_Terminate(). + + @see PaDeviceInfo, PaDeviceIndex +*/ +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device ); + + +/** Parameters for one direction (input or output) of a stream. +*/ +typedef struct PaStreamParameters +{ + /** A valid device index in the range 0 to (Pa_GetDeviceCount()-1) + specifying the device to be used or the special constant + paUseHostApiSpecificDeviceSpecification which indicates that the actual + device(s) to use are specified in hostApiSpecificStreamInfo. + This field must not be set to paNoDevice. + */ + PaDeviceIndex device; + + /** The number of channels of sound to be delivered to the + stream callback or accessed by Pa_ReadStream() or Pa_WriteStream(). + It can range from 1 to the value of maxInputChannels in the + PaDeviceInfo record for the device specified by the device parameter. + */ + int channelCount; + + /** The sample format of the buffer provided to the stream callback, + a_ReadStream() or Pa_WriteStream(). It may be any of the formats described + by the PaSampleFormat enumeration. + */ + PaSampleFormat sampleFormat; + + /** The desired latency in seconds. Where practical, implementations should + configure their latency based on these parameters, otherwise they may + choose the closest viable latency instead. Unless the suggested latency + is greater than the absolute upper limit for the device implementations + shouldround the suggestedLatency up to the next practial value - ie to + provide an equal or higher latency than suggestedLatency whereever possibe. + Actual latency values for an open stream may be retrieved using the + inputLatency and outputLatency fields of the PaStreamInfo structure + returned by Pa_GetStreamInfo(). + @see default*Latency in PaDeviceInfo, *Latency in PaStreamInfo + */ + PaTime suggestedLatency; + + /** An optional pointer to a host api specific data structure + containing additional information for device setup and/or stream processing. + hostApiSpecificStreamInfo is never required for correct operation, + if not used it should be set to NULL. + */ + void *hostApiSpecificStreamInfo; + +} PaStreamParameters; + + +/** Return code for Pa_IsFormatSupported indicating success. */ +#define paFormatIsSupported (0) + +/** Determine whether it would be possible to open a stream with the specified + parameters. + + @param inputParameters A structure that describes the input parameters used to + open a stream. The suggestedLatency field is ignored. See PaStreamParameters + for a description of these parameters. inputParameters must be NULL for + output-only streams. + + @param outputParameters A structure that describes the output parameters used + to open a stream. The suggestedLatency field is ignored. See PaStreamParameters + for a description of these parameters. outputParameters must be NULL for + input-only streams. + + @param sampleRate The required sampleRate. For full-duplex streams it is the + sample rate for both input and output + + @return Returns 0 if the format is supported, and an error code indicating why + the format is not supported otherwise. The constant paFormatIsSupported is + provided to compare with the return value for success. + + @see paFormatIsSupported, PaStreamParameters +*/ +PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ); + + + +/* Streaming types and functions */ + + +/** + A single PaStream can provide multiple channels of real-time + streaming audio input and output to a client application. A stream + provides access to audio hardware represented by one or more + PaDevices. Depending on the underlying Host API, it may be possible + to open multiple streams using the same device, however this behavior + is implementation defined. Portable applications should assume that + a PaDevice may be simultaneously used by at most one PaStream. + + Pointers to PaStream objects are passed between PortAudio functions that + operate on streams. + + @see Pa_OpenStream, Pa_OpenDefaultStream, Pa_OpenDefaultStream, Pa_CloseStream, + Pa_StartStream, Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive, + Pa_GetStreamTime, Pa_GetStreamCpuLoad + +*/ +typedef void PaStream; + + +/** Can be passed as the framesPerBuffer parameter to Pa_OpenStream() + or Pa_OpenDefaultStream() to indicate that the stream callback will + accept buffers of any size. +*/ +#define paFramesPerBufferUnspecified (0) + + +/** Flags used to control the behavior of a stream. They are passed as + parameters to Pa_OpenStream or Pa_OpenDefaultStream. Multiple flags may be + ORed together. + + @see Pa_OpenStream, Pa_OpenDefaultStream + @see paNoFlag, paClipOff, paDitherOff, paNeverDropInput, + paPrimeOutputBuffersUsingStreamCallback, paPlatformSpecificFlags +*/ +typedef unsigned long PaStreamFlags; + +/** @see PaStreamFlags */ +#define paNoFlag ((PaStreamFlags) 0) + +/** Disable default clipping of out of range samples. + @see PaStreamFlags +*/ +#define paClipOff ((PaStreamFlags) 0x00000001) + +/** Disable default dithering. + @see PaStreamFlags +*/ +#define paDitherOff ((PaStreamFlags) 0x00000002) + +/** Flag requests that where possible a full duplex stream will not discard + overflowed input samples without calling the stream callback. This flag is + only valid for full duplex callback streams and only when used in combination + with the paFramesPerBufferUnspecified (0) framesPerBuffer parameter. Using + this flag incorrectly results in a paInvalidFlag error being returned from + Pa_OpenStream and Pa_OpenDefaultStream. + + @see PaStreamFlags, paFramesPerBufferUnspecified +*/ +#define paNeverDropInput ((PaStreamFlags) 0x00000004) + +/** Call the stream callback to fill initial output buffers, rather than the + default behavior of priming the buffers with zeros (silence). This flag has + no effect for input-only and blocking read/write streams. + + @see PaStreamFlags +*/ +#define paPrimeOutputBuffersUsingStreamCallback ((PaStreamFlags) 0x00000008) + +/** A mask specifying the platform specific bits. + @see PaStreamFlags +*/ +#define paPlatformSpecificFlags ((PaStreamFlags)0xFFFF0000) + +/** + Timing information for the buffers passed to the stream callback. +*/ +typedef struct PaStreamCallbackTimeInfo{ + PaTime inputBufferAdcTime; + PaTime currentTime; + PaTime outputBufferDacTime; +} PaStreamCallbackTimeInfo; + + +/** + Flag bit constants for the statusFlags to PaStreamCallback. + + @see paInputUnderflow, paInputOverflow, paOutputUnderflow, paOutputOverflow, + paPrimingOutput +*/ +typedef unsigned long PaStreamCallbackFlags; + +/** In a stream opened with paFramesPerBufferUnspecified, indicates that + input data is all silence (zeros) because no real data is available. In a + stream opened without paFramesPerBufferUnspecified, it indicates that one or + more zero samples have been inserted into the input buffer to compensate + for an input underflow. + @see PaStreamCallbackFlags +*/ +#define paInputUnderflow ((PaStreamCallbackFlags) 0x00000001) + +/** In a stream opened with paFramesPerBufferUnspecified, indicates that data + prior to the first sample of the input buffer was discarded due to an + overflow, possibly because the stream callback is using too much CPU time. + Otherwise indicates that data prior to one or more samples in the + input buffer was discarded. + @see PaStreamCallbackFlags +*/ +#define paInputOverflow ((PaStreamCallbackFlags) 0x00000002) + +/** Indicates that output data (or a gap) was inserted, possibly because the + stream callback is using too much CPU time. + @see PaStreamCallbackFlags +*/ +#define paOutputUnderflow ((PaStreamCallbackFlags) 0x00000004) + +/** Indicates that output data will be discarded because no room is available. + @see PaStreamCallbackFlags +*/ +#define paOutputOverflow ((PaStreamCallbackFlags) 0x00000008) + +/** Some of all of the output data will be used to prime the stream, input + data may be zero. + @see PaStreamCallbackFlags +*/ +#define paPrimingOutput ((PaStreamCallbackFlags) 0x00000010) + +/** + Allowable return values for the PaStreamCallback. + @see PaStreamCallback +*/ +typedef enum PaStreamCallbackResult +{ + paContinue=0, + paComplete=1, + paAbort=2 +} PaStreamCallbackResult; + + +/** + Functions of type PaStreamCallback are implemented by PortAudio clients. + They consume, process or generate audio in response to requests from an + active PortAudio stream. + + @param input and @param output are arrays of interleaved samples, + the format, packing and number of channels used by the buffers are + determined by parameters to Pa_OpenStream(). + + @param frameCount The number of sample frames to be processed by + the stream callback. + + @param timeInfo The time in seconds when the first sample of the input + buffer was received at the audio input, the time in seconds when the first + sample of the output buffer will begin being played at the audio output, and + the time in seconds when the stream callback was called. + See also Pa_GetStreamTime() + + @param statusFlags Flags indicating whether input and/or output buffers + have been inserted or will be dropped to overcome underflow or overflow + conditions. + + @param userData The value of a user supplied pointer passed to + Pa_OpenStream() intended for storing synthesis data etc. + + @return + The stream callback should return one of the values in the + PaStreamCallbackResult enumeration. To ensure that the callback continues + to be called, it should return paContinue (0). Either paComplete or paAbort + can be returned to finish stream processing, after either of these values is + returned the callback will not be called again. If paAbort is returned the + stream will finish as soon as possible. If paComplete is returned, the stream + will continue until all buffers generated by the callback have been played. + This may be useful in applications such as soundfile players where a specific + duration of output is required. However, it is not necessary to utilise this + mechanism as Pa_StopStream(), Pa_AbortStream() or Pa_CloseStream() can also + be used to stop the stream. The callback must always fill the entire output + buffer irrespective of its return value. + + @see Pa_OpenStream, Pa_OpenDefaultStream + + @note With the exception of Pa_GetStreamCpuLoad() it is not permissable to call + PortAudio API functions from within the stream callback. +*/ +typedef int PaStreamCallback( + const void *input, void *output, + unsigned long frameCount, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ); + + +/** Opens a stream for either input, output or both. + + @param stream The address of a PaStream pointer which will receive + a pointer to the newly opened stream. + + @param inputParameters A structure that describes the input parameters used by + the opened stream. See PaStreamParameters for a description of these parameters. + inputParameters must be NULL for output-only streams. + + @param outputParameters A structure that describes the output parameters used by + the opened stream. See PaStreamParameters for a description of these parameters. + outputParameters must be NULL for input-only streams. + + @param sampleRate The desired sampleRate. For full-duplex streams it is the + sample rate for both input and output + + @param framesPerBuffer The number of frames passed to the stream callback + function, or the preferred block granularity for a blocking read/write stream. + The special value paFramesPerBufferUnspecified (0) may be used to request that + the stream callback will recieve an optimal (and possibly varying) number of + frames based on host requirements and the requested latency settings. + Note: With some host APIs, the use of non-zero framesPerBuffer for a callback + stream may introduce an additional layer of buffering which could introduce + additional latency. PortAudio guarantees that the additional latency + will be kept to the theoretical minimum however, it is strongly recommended + that a non-zero framesPerBuffer value only be used when your algorithm + requires a fixed number of frames per stream callback. + + @param streamFlags Flags which modify the behaviour of the streaming process. + This parameter may contain a combination of flags ORed together. Some flags may + only be relevant to certain buffer formats. + + @param streamCallback A pointer to a client supplied function that is responsible + for processing and filling input and output buffers. If this parameter is NULL + the stream will be opened in 'blocking read/write' mode. In blocking mode, + the client can receive sample data using Pa_ReadStream and write sample data + using Pa_WriteStream, the number of samples that may be read or written + without blocking is returned by Pa_GetStreamReadAvailable and + Pa_GetStreamWriteAvailable respectively. + + @param userData A client supplied pointer which is passed to the stream callback + function. It could for example, contain a pointer to instance data necessary + for processing the audio buffers. This parameter is ignored if streamCallback + is NULL. + + @return + Upon success Pa_OpenStream() returns paNoError and places a pointer to a + valid PaStream in the stream argument. The stream is inactive (stopped). + If a call to Pa_OpenStream() fails, a non-zero error code is returned (see + PaError for possible error codes) and the value of stream is invalid. + + @see PaStreamParameters, PaStreamCallback, Pa_ReadStream, Pa_WriteStream, + Pa_GetStreamReadAvailable, Pa_GetStreamWriteAvailable +*/ +PaError Pa_OpenStream( PaStream** stream, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ); + + +/** A simplified version of Pa_OpenStream() that opens the default input + and/or output devices. + + @param stream The address of a PaStream pointer which will receive + a pointer to the newly opened stream. + + @param numInputChannels The number of channels of sound that will be supplied + to the stream callback or returned by Pa_ReadStream. It can range from 1 to + the value of maxInputChannels in the PaDeviceInfo record for the default input + device. If 0 the stream is opened as an output-only stream. + + @param numOutputChannels The number of channels of sound to be delivered to the + stream callback or passed to Pa_WriteStream. It can range from 1 to the value + of maxOutputChannels in the PaDeviceInfo record for the default output dvice. + If 0 the stream is opened as an output-only stream. + + @param sampleFormat The sample format of both the input and output buffers + provided to the callback or passed to and from Pa_ReadStream and Pa_WriteStream. + sampleFormat may be any of the formats described by the PaSampleFormat + enumeration. + + @param sampleRate Same as Pa_OpenStream parameter of the same name. + @param framesPerBuffer Same as Pa_OpenStream parameter of the same name. + @param streamCallback Same as Pa_OpenStream parameter of the same name. + @param userData Same as Pa_OpenStream parameter of the same name. + + @return As for Pa_OpenStream + + @see Pa_OpenStream, PaStreamCallback +*/ +PaError Pa_OpenDefaultStream( PaStream** stream, + int numInputChannels, + int numOutputChannels, + PaSampleFormat sampleFormat, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamCallback *streamCallback, + void *userData ); + + +/** Closes an audio stream. If the audio stream is active it + discards any pending buffers as if Pa_AbortStream() had been called. +*/ +PaError Pa_CloseStream( PaStream *stream ); + + +/** Functions of type PaStreamFinishedCallback are implemented by PortAudio + clients. They can be registered with a stream using the Pa_SetStreamFinishedCallback + function. Once registered they are called when the stream becomes inactive + (ie once a call to Pa_StopStream() will not block). + A stream will become inactive after the stream callback returns non-zero, + or when Pa_StopStream or Pa_AbortStream is called. For a stream providing audio + output, if the stream callback returns paComplete, or Pa_StopStream is called, + the stream finished callback will not be called until all generated sample data + has been played. + + @param userData The userData parameter supplied to Pa_OpenStream() + + @see Pa_SetStreamFinishedCallback +*/ +typedef void PaStreamFinishedCallback( void *userData ); + + +/** Register a stream finished callback function which will be called when the + stream becomes inactive. See the description of PaStreamFinishedCallback for + further details about when the callback will be called. + + @param stream a pointer to a PaStream that is in the stopped state - if the + stream is not stopped, the stream's finished callback will remain unchanged + and an error code will be returned. + + @param streamFinishedCallback a pointer to a function with the same signature + as PaStreamFinishedCallback, that will be called when the stream becomes + inactive. Passing NULL for this parameter will un-register a previously + registered stream finished callback function. + + @return on success returns paNoError, otherwise an error code indicating the cause + of the error. + + @see PaStreamFinishedCallback +*/ +PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback ); + + +/** Commences audio processing. +*/ +PaError Pa_StartStream( PaStream *stream ); + + +/** Terminates audio processing. It waits until all pending + audio buffers have been played before it returns. +*/ +PaError Pa_StopStream( PaStream *stream ); + + +/** Terminates audio processing immediately without waiting for pending + buffers to complete. +*/ +PaError Pa_AbortStream( PaStream *stream ); + + +/** Determine whether the stream is stopped. + A stream is considered to be stopped prior to a successful call to + Pa_StartStream and after a successful call to Pa_StopStream or Pa_AbortStream. + If a stream callback returns a value other than paContinue the stream is NOT + considered to be stopped. + + @return Returns one (1) when the stream is stopped, zero (0) when + the stream is running or, a PaErrorCode (which are always negative) if + PortAudio is not initialized or an error is encountered. + + @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive +*/ +PaError Pa_IsStreamStopped( PaStream *stream ); + + +/** Determine whether the stream is active. + A stream is active after a successful call to Pa_StartStream(), until it + becomes inactive either as a result of a call to Pa_StopStream() or + Pa_AbortStream(), or as a result of a return value other than paContinue from + the stream callback. In the latter case, the stream is considered inactive + after the last buffer has finished playing. + + @return Returns one (1) when the stream is active (ie playing or recording + audio), zero (0) when not playing or, a PaErrorCode (which are always negative) + if PortAudio is not initialized or an error is encountered. + + @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamStopped +*/ +PaError Pa_IsStreamActive( PaStream *stream ); + + + +/** A structure containing unchanging information about an open stream. + @see Pa_GetStreamInfo +*/ + +typedef struct PaStreamInfo +{ + /** this is struct version 1 */ + int structVersion; + + /** The input latency of the stream in seconds. This value provides the most + accurate estimate of input latency available to the implementation. It may + differ significantly from the suggestedLatency value passed to Pa_OpenStream(). + The value of this field will be zero (0.) for output-only streams. + @see PaTime + */ + PaTime inputLatency; + + /** The output latency of the stream in seconds. This value provides the most + accurate estimate of output latency available to the implementation. It may + differ significantly from the suggestedLatency value passed to Pa_OpenStream(). + The value of this field will be zero (0.) for input-only streams. + @see PaTime + */ + PaTime outputLatency; + + /** The sample rate of the stream in Hertz (samples per second). In cases + where the hardware sample rate is inaccurate and PortAudio is aware of it, + the value of this field may be different from the sampleRate parameter + passed to Pa_OpenStream(). If information about the actual hardware sample + rate is not available, this field will have the same value as the sampleRate + parameter passed to Pa_OpenStream(). + */ + double sampleRate; + +} PaStreamInfo; + + +/** Retrieve a pointer to a PaStreamInfo structure containing information + about the specified stream. + @return A pointer to an immutable PaStreamInfo structure. If the stream + parameter invalid, or an error is encountered, the function returns NULL. + + @param stream A pointer to an open stream previously created with Pa_OpenStream. + + @note PortAudio manages the memory referenced by the returned pointer, + the client must not manipulate or free the memory. The pointer is only + guaranteed to be valid until the specified stream is closed. + + @see PaStreamInfo +*/ +const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream ); + + +/** Determine the current time for the stream according to the same clock used + to generate buffer timestamps. This time may be used for syncronising other + events to the audio stream, for example synchronizing audio to MIDI. + + @return The stream's current time in seconds, or 0 if an error occurred. + + @see PaTime, PaStreamCallback +*/ +PaTime Pa_GetStreamTime( PaStream *stream ); + + +/** Retrieve CPU usage information for the specified stream. + The "CPU Load" is a fraction of total CPU time consumed by a callback stream's + audio processing routines including, but not limited to the client supplied + stream callback. This function does not work with blocking read/write streams. + + This function may be called from the stream callback function or the + application. + + @return + A floating point value, typically between 0.0 and 1.0, where 1.0 indicates + that the stream callback is consuming the maximum number of CPU cycles possible + to maintain real-time operation. A value of 0.5 would imply that PortAudio and + the stream callback was consuming roughly 50% of the available CPU time. The + return value may exceed 1.0. A value of 0.0 will always be returned for a + blocking read/write stream, or if an error occurrs. +*/ +double Pa_GetStreamCpuLoad( PaStream* stream ); + + +/** Read samples from an input stream. The function doesn't return until + the entire buffer has been filled - this may involve waiting for the operating + system to supply the data. + + @param stream A pointer to an open stream previously created with Pa_OpenStream. + + @param buffer A pointer to a buffer of sample frames. The buffer contains + samples in the format specified by the inputParameters->sampleFormat field + used to open the stream, and the number of channels specified by + inputParameters->numChannels. If non-interleaved samples were requested, + buffer is a pointer to the first element of an array of non-interleaved + buffer pointers, one for each channel. + + @param frames The number of frames to be read into buffer. This parameter + is not constrained to a specific range, however high performance applications + will want to match this parameter to the framesPerBuffer parameter used + when opening the stream. + + @return On success PaNoError will be returned, or PaInputOverflowed if input + data was discarded by PortAudio after the previous call and before this call. +*/ +PaError Pa_ReadStream( PaStream* stream, + void *buffer, + unsigned long frames ); + + +/** Write samples to an output stream. This function doesn't return until the + entire buffer has been consumed - this may involve waiting for the operating + system to consume the data. + + @param stream A pointer to an open stream previously created with Pa_OpenStream. + + @param buffer A pointer to a buffer of sample frames. The buffer contains + samples in the format specified by the outputParameters->sampleFormat field + used to open the stream, and the number of channels specified by + outputParameters->numChannels. If non-interleaved samples were requested, + buffer is a pointer to the first element of an array of non-interleaved + buffer pointers, one for each channel. + + @param frames The number of frames to be written from buffer. This parameter + is not constrained to a specific range, however high performance applications + will want to match this parameter to the framesPerBuffer parameter used + when opening the stream. + + @return On success PaNoError will be returned, or paOutputUnderflowed if + additional output data was inserted after the previous call and before this + call. +*/ +PaError Pa_WriteStream( PaStream* stream, + const void *buffer, + unsigned long frames ); + + +/** Retrieve the number of frames that can be read from the stream without + waiting. + + @return Returns a non-negative value representing the maximum number of frames + that can be read from the stream without blocking or busy waiting or, a + PaErrorCode (which are always negative) if PortAudio is not initialized or an + error is encountered. +*/ +signed long Pa_GetStreamReadAvailable( PaStream* stream ); + + +/** Retrieve the number of frames that can be written to the stream without + waiting. + + @return Returns a non-negative value representing the maximum number of frames + that can be written to the stream without blocking or busy waiting or, a + PaErrorCode (which are always negative) if PortAudio is not initialized or an + error is encountered. +*/ +signed long Pa_GetStreamWriteAvailable( PaStream* stream ); + + +/* Miscellaneous utilities */ + + +/** Retrieve the size of a given sample format in bytes. + + @return The size in bytes of a single sample in the specified format, + or paSampleFormatNotSupported if the format is not supported. +*/ +PaError Pa_GetSampleSize( PaSampleFormat format ); + + +/** Put the caller to sleep for at least 'msec' milliseconds. This function is + provided only as a convenience for authors of portable code (such as the tests + and examples in the PortAudio distribution.) + + The function may sleep longer than requested so don't rely on this for accurate + musical timing. +*/ +void Pa_Sleep( long msec ); + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PORTAUDIO_H */ diff --git a/lib/ft2/ptt.c b/lib/ft2/ptt.c new file mode 100644 index 000000000..fdda4ff97 --- /dev/null +++ b/lib/ft2/ptt.c @@ -0,0 +1,58 @@ +#include +#include + +int ptt_(int *nport, int *ntx, int *ndtr, int *iptt) +{ + static HANDLE hFile; + static int open=0, nhold=0; + char s[10]; + int i3,i4,i5,i6,i9,i00; + + if(*nport==0) { + *iptt=*ntx; + return(0); + } + + nhold=0; + if(*nport>100) nhold=1; + + if(*ntx && (!open)) { + sprintf(s,"\\\\.\\COM%d",*nport%100); + hFile=CreateFile( + TEXT(s), + GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + if(hFile==INVALID_HANDLE_VALUE) { + printf("PTT: Cannot open COM port %d.\n",*nport%100); + return(-1); + } + open=1; + } + + if(*ntx && open) { + if(*ndtr) + EscapeCommFunction(hFile,5); //set DTR + else + EscapeCommFunction(hFile,3); //set RTS + *iptt=1; + } + + else { + if(*ndtr) + EscapeCommFunction(hFile,6); //clear DTR + else + EscapeCommFunction(hFile,4); //clear RTS + EscapeCommFunction(hFile,9); //clear BREAK + if(nhold==0) { + i00=CloseHandle(hFile); + open=0; + } + *iptt=0; + } + return(0); +} diff --git a/lib/ft2/ptt_unix.c b/lib/ft2/ptt_unix.c new file mode 100644 index 000000000..b16380608 --- /dev/null +++ b/lib/ft2/ptt_unix.c @@ -0,0 +1,341 @@ +#include +#include +#include +#include +#include +#include +//#include +//#include +//#include +//#include +//#include + +int lp_reset (int fd); +int lp_ptt (int fd, int onoff); + +#ifdef HAVE_SYS_STAT_H +# include +#endif +#if (defined(__unix__) || defined(unix)) && !defined(USG) +# include +#endif + +#include +/* parport functions */ + +int dev_is_parport(int fd); +int ptt_parallel(int fd, int *ntx, int *iptt); +int ptt_serial(int fd, int *ntx, int *iptt); + +int fd=-1; /* Used for both serial and parallel */ + +/* + * ptt_ + * + * generic unix PTT routine called from Fortran + * + * Inputs + * unused Unused, to satisfy old windows calling convention + * ptt_port device name serial or parallel + * ntx pointer to fortran command on or off + * iptt pointer to fortran command status on or off + * Returns - non 0 if error +*/ + +/* Tiny state machine */ +#define STATE_PORT_CLOSED 0 +#define STATE_PORT_OPEN_PARALLEL 1 +#define STATE_PORT_OPEN_SERIAL 2 + +int +ptt_(int *unused, char *ptt_port, int *ntx, int *ndtr, int *iptt) +{ + static int state=0; + char *p; + + /* In the very unlikely event of a NULL pointer, just return. + * Yes, I realise this should not be possible in WSJT. + */ + if (ptt_port == NULL) { + *iptt = *ntx; + return (0); + } + + switch (state) { + case STATE_PORT_CLOSED: + + /* Remove trailing ' ' */ + if ((p = strchr(ptt_port, ' ')) != NULL) + *p = '\0'; + + /* If all that is left is a '\0' then also just return */ + if (*ptt_port == '\0') { + *iptt = *ntx; + return(0); + } + + if ((fd = open(ptt_port, O_RDWR|O_NONBLOCK)) < 0) { + fprintf(stderr, "Can't open %s.\n", ptt_port); + return (1); + } + + if (dev_is_parport(fd)) { + state = STATE_PORT_OPEN_PARALLEL; + lp_reset(fd); + ptt_parallel(fd, ntx, iptt); + } else { + state = STATE_PORT_OPEN_SERIAL; + ptt_serial(fd, ntx, iptt); + } + break; + + case STATE_PORT_OPEN_PARALLEL: + ptt_parallel(fd, ntx, iptt); + break; + + case STATE_PORT_OPEN_SERIAL: + ptt_serial(fd, ntx, iptt); + break; + + default: + close(fd); + fd = -1; + state = STATE_PORT_CLOSED; + break; + } + return(0); +} + +/* + * ptt_serial + * + * generic serial unix PTT routine called indirectly from Fortran + * + * fd - already opened file descriptor + * ntx - pointer to fortran command on or off + * iptt - pointer to fortran command status on or off + */ + +int +ptt_serial(int fd, int *ntx, int *iptt) +{ + int control = TIOCM_RTS | TIOCM_DTR; + + if(*ntx) { + ioctl(fd, TIOCMBIS, &control); /* Set DTR and RTS */ + *iptt = 1; + } else { + ioctl(fd, TIOCMBIC, &control); + *iptt = 0; + } + return(0); +} + + +/* parport functions */ + +/* + * dev_is_parport(fd): + * + * inputs - Already open fd + * output - 1 if parallel port, 0 if not + * side effects - Unfortunately, this is platform specific. + */ + +#if defined(HAVE_LINUX_PPDEV_H) /* Linux (ppdev) */ + +int +dev_is_parport(int fd) +{ + struct stat st; + int m; + + if ((fstat(fd, &st) == -1) || + ((st.st_mode & S_IFMT) != S_IFCHR) || + (ioctl(fd, PPGETMODE, &m) == -1)) + return(0); + + return(1); +} + +#elif defined(HAVE_DEV_PPBUS_PPI_H) /* FreeBSD (ppbus/ppi) */ + +int +dev_is_parport(int fd) +{ + struct stat st; + unsigned char c; + + if ((fstat(fd, &st) == -1) || + ((st.st_mode & S_IFMT) != S_IFCHR) || + (ioctl(fd, PPISSTATUS, &c) == -1)) + return(0); + + return(1); +} + +#else /* Fallback (nothing) */ + +int +dev_is_parport(int fd) +{ + return(0); +} + +#endif +/* Linux wrapper around PPFCONTROL */ +#ifdef HAVE_LINUX_PPDEV_H +static void +parport_control (int fd, unsigned char controlbits, int values) +{ + struct ppdev_frob_struct frob; + frob.mask = controlbits; + frob.val = values; + + if (ioctl (fd, PPFCONTROL, &frob) == -1) + { + fprintf(stderr, "Parallel port PPFCONTROL"); + exit (1); + } +} +#endif + +/* FreeBSD wrapper around PPISCTRL */ +#ifdef HAVE_DEV_PPBUS_PPI_H +static void +parport_control (int fd, unsigned char controlbits, int values) +{ + unsigned char val; + + if (ioctl (fd, PPIGCTRL, &val) == -1) + { + fprintf(stderr, "Parallel port PPIGCTRL"); + exit (1); + } + + val &= ~controlbits; + val |= values; + + if (ioctl (fd, PPISCTRL, &val) == -1) + { + fprintf(stderr, "Parallel port PPISCTRL"); + exit (1); + } +} +#endif + +/* Initialise a parallel port, given open fd */ +int +lp_init (int fd) +{ +#ifdef HAVE_LINUX_PPDEV_H + int mode; +#endif + +#ifdef HAVE_LINUX_PPDEV_H + mode = PARPORT_MODE_PCSPP; + + if (ioctl (fd, PPSETMODE, &mode) == -1) + { + fprintf(stderr, "Setting parallel port mode"); + close (fd); + return(-1); + } + + if (ioctl (fd, PPEXCL, NULL) == -1) + { + fprintf(stderr, "Parallel port is already in use.\n"); + close (fd); + return(-1); + } + if (ioctl (fd, PPCLAIM, NULL) == -1) + { + fprintf(stderr, "Claiming parallel port.\n"); + fprintf(stderr, "HINT: did you unload the lp kernel module?"); + close (fd); + return(-1); + } + + /* Enable CW & PTT - /STROBE bit (pin 1) */ + parport_control (fd, PARPORT_CONTROL_STROBE, PARPORT_CONTROL_STROBE); +#endif +#ifdef HAVE_DEV_PPBUS_PPI_H + parport_control (fd, STROBE, STROBE); +#endif + lp_reset (fd); + return(0); +} + +/* release ppdev and close port */ +int +lp_free (int fd) +{ +#ifdef HAVE_LINUX_PPDEV_H + lp_reset (fd); + + /* Disable CW & PTT - /STROBE bit (pin 1) */ + parport_control (fd, PARPORT_CONTROL_STROBE, 0); + + ioctl (fd, PPRELEASE); +#endif +#ifdef HAVE_DEV_PPBUS_PPI_H + /* Disable CW & PTT - /STROBE bit (pin 1) */ + parport_control (fd, STROBE, 0); +#endif + close (fd); + return(0); +} + +/* set to a known state */ +int +lp_reset (int fd) +{ +#if defined (HAVE_LINUX_PPDEV_H) || defined (HAVE_DEV_PPBUS_PPI_H) + lp_ptt (fd, 0); +#endif + return(0); +} + +/* SSB PTT keying - /INIT bit (pin 16) (inverted) */ +int +lp_ptt (int fd, int onoff) +{ +#ifdef HAVE_LINUX_PPDEV_H + if (onoff == 1) + parport_control (fd, PARPORT_CONTROL_INIT, + PARPORT_CONTROL_INIT); + else + parport_control (fd, PARPORT_CONTROL_INIT, 0); +#endif +#ifdef HAVE_DEV_PPBUS_PPI_H + if (onoff == 1) + parport_control (fd, nINIT, + nINIT); + else + parport_control (fd, nINIT, 0); +#endif + return(0); +} + +/* + * ptt_parallel + * + * generic parallel unix PTT routine called indirectly from Fortran + * + * fd - already opened file descriptor + * ntx - pointer to fortran command on or off + * iptt - pointer to fortran command status on or off + */ + +int +ptt_parallel(int fd, int *ntx, int *iptt) +{ + if(*ntx) { + lp_ptt(fd, 1); + *iptt=1; + } else { + lp_ptt(fd, 0); + *iptt=0; + } + return(0); +}