added a fifo buffer to interface between framesPerBufferof portaudio and the buffer...
authordrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Mon, 15 Oct 2012 03:59:03 +0000 (03:59 +0000)
committerdrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Mon, 15 Oct 2012 03:59:03 +0000 (03:59 +0000)
git-svn-id: https://svn.code.sf.net/p/freetel/code@752 01035d8c-6547-0410-b346-abe4f91aad63

fdmdv2/src/Makefile.linux
fdmdv2/src/fdmdv2_defines.h
fdmdv2/src/fdmdv2_main.cpp
fdmdv2/src/fdmdv2_main.h

index 9b0da6eaf0bd1a1dc975fe036ac1d46555a1c9c5..56fbbbb8b92796438c828783f12edb2aceb8e1b1 100644 (file)
@@ -15,7 +15,7 @@ CODEC2_INC=-I$(CODEC2_PATH)/src
 CODEC2_LIB=$(CODEC2_PATH)/src/.libs/libcodec2.a
 
 CPP_FLAGS = $(WX_CPPFLAGS) $(CODEC2_INC) -I../extern/include -g -Wall
-LIBS = $(WX_LIBS) $(CODEC2_LIB) -lm /usr/lib/libportaudiocpp.so.0 -lpthread 
+LIBS = $(WX_LIBS) $(CODEC2_LIB) -lm -lportaudiocpp -lpthread 
 
 OBJS = topFrame.o \
 fdmdv2_main.o \
index e2004f5c7bbc52ee0bed0446c1d65c8215be8d2f..f5e1939e5d3c5806e4c0c83376ff071190a271cb 100644 (file)
 //==========================================================================
 #ifndef __FDMDV2_DEFINES__
 #define __FDMDV2_DEFINES__
+
 #include "wx/wx.h"
-/* FFT points */
-#define FDMDV_NSPEC         512
+#include "fdmdv.h"
 
-#define MIN_DB              -40.0
-#define MAX_DB              0.0
-#define BETA                0.1     // constant for time averageing spectrum data
-#define MIN_HZ              0
-#define MAX_HZ              4000
-#define WATERFALL_SECS_Y    5       // number of seconds respresented by y axis of waterfall
-#define DT                  0.02    // time between samples
-#define FS                  8000    // FDMDV modem sample rate
+// Spectrogram and Waterfall
 
-#define FDMDV_BITS_PER_FRAME          28  /* 20ms frames, 1400 bit/s                                        */
-#define FDMDV_NOM_SAMPLES_PER_FRAME  160  /* modulator output samples/frame and nominal demod samples/frame */
-                                          /* at 8000 Hz sample rate                                         */
-#define FDMDV_MAX_SAMPLES_PER_FRAME  200  /* max demod samples/frame, use this to allocate storage          */
-#define FDMDV_SCALE                 1000  /* suggested scaling for 16 bit shorts                            */
-#define FDMDV_NSYM                    15
+#define FDMDV_NSPEC         512
 
 #define MIN_DB              -40.0
 #define MAX_DB              0.0
@@ -53,6 +41,8 @@
 #define DT                  0.02    // time between samples
 #define FS                  8000    // FDMDV modem sample rate
 
+// Scatter diagram 
+
 #define SCATTER_MEM         (FDMDV_NSYM)*50
 #define SCATTER_X_MAX       3.0
 #define SCATTER_Y_MAX       3.0
@@ -64,7 +54,9 @@
 #define H2                  (H/2)
 #define SP                  20
 
-// sound card
+// sample rate I/O & conversion constants
+#define MAX_FPB             2048                           // maximum value of framesPerBuffer
+#define PA_FPB              512                            // nominal value of framesPerBuffer
 #define SAMPLE_RATE         48000                          // 48 kHz sampling rate rec. as we can trust accuracy of sound card
 #define N8                  FDMDV_NOM_SAMPLES_PER_FRAME    // processing buffer size at 8 kHz
 #define MEM8                (FDMDV_OS_TAPS/FDMDV_OS)
 #define BITS_PER_CODEC_FRAME  (2 * FDMDV_BITS_PER_FRAME)
 #define BYTES_PER_CODEC_FRAME (BITS_PER_CODEC_FRAME / 8)
 
-/* 8 to 48 kHz sample rate conversion */
-#define FDMDV_OS                 6         /* oversampling rate           */
-#define FDMDV_OS_TAPS           48         /* number of OS filter taps    */
-
 enum
 {
     ID_ROTATE_LEFT = wxID_HIGHEST + 1,
index b6d2ce183c7c038e97b046c709cc2f9069d946ba..88a31c60c278d685cc8b781273a97cb4b0b8b30c 100644 (file)
@@ -780,7 +780,8 @@ void MainFrame::OnTogBtnOnOff(wxCommandEvent& event)
 #endif // _USE_TIMER
         startRxStream();
 //        startTxStream();
-        m_togBtnOnOff->SetLabel(wxT("Stop"));
+        if (m_RxRunning)
+           m_togBtnOnOff->SetLabel(wxT("Stop"));
     }
     else
     {
@@ -862,7 +863,9 @@ void MainFrame::startRxStream()
         if(m_rxDevIn == paNoDevice)
         {
             wxMessageBox(wxT("Rx Error: No default input device."), wxT("Error"), wxOK);
-            return;
+           delete m_rxPa;
+           m_RxRunning = false;
+           return;
         }
         m_rxErr = m_rxPa->setInputDevice(m_rxDevIn);
         m_rxErr = m_rxPa->setInputChannelCount(2);                          // stereo input
@@ -874,6 +877,8 @@ void MainFrame::startRxStream()
         if (m_rxDevOut == paNoDevice)
         {
             wxMessageBox(wxT("Rx Error: No default output device."), wxT("Error"), wxOK);
+           delete m_rxPa;
+           m_RxRunning = false;            
             return;
         }
         m_rxErr = m_rxPa->setOutputDevice(m_rxDevOut);
@@ -883,7 +888,7 @@ void MainFrame::startRxStream()
         m_rxErr = m_rxPa->setOutputLatency(m_rxPa->getOutputDefaultLowLatency());
         m_rxPa->setOutputHostApiStreamInfo(NULL);
 
-        m_rxErr = m_rxPa->setFramesPerBuffer(FRAMES_PER_BUFFER);
+       m_rxErr = m_rxPa->setFramesPerBuffer(PA_FPB);
         m_rxErr = m_rxPa->setSampleRate(SAMPLE_RATE);
         m_rxErr = m_rxPa->setStreamFlags(0);
 
@@ -900,6 +905,9 @@ void MainFrame::startRxStream()
             m_rxUserdata->in48k[i] = 0.0;
         }
 
+       m_rxUserdata->infifo = fifo_create(2*N48);
+       m_rxUserdata->outfifo = fifo_create(2*N48);
+
         m_rxPa->setUserData(m_rxUserdata);
         m_rxErr = m_rxPa->setCallback(rxCallback);
         m_rxErr = m_rxPa->streamOpen();
@@ -907,13 +915,21 @@ void MainFrame::startRxStream()
         if(m_rxErr != paNoError)
         {
             wxMessageBox(wxT("Rx Stream Open/Setup error."), wxT("Error"), wxOK);
-            return;
+           delete m_rxPa;
+           fifo_destroy(m_rxUserdata->infifo);
+           fifo_destroy(m_rxUserdata->outfifo);
+           m_RxRunning = false;            
+           return;
         }
         m_rxErr = m_rxPa->streamStart();
         if(m_rxErr != paNoError)
         {
             wxMessageBox(wxT("Rx Stream Start Error."), wxT("Error"), wxOK);
-            return;
+           delete m_rxPa;
+           fifo_destroy(m_rxUserdata->infifo);
+           fifo_destroy(m_rxUserdata->outfifo);
+           m_RxRunning = false;            
+           return;
         }
     }
 }
@@ -931,6 +947,8 @@ void MainFrame::stopRxStream()
         fdmdv_destroy(g_pFDMDV);
         codec2_destroy(g_pCodec2);
 //        delete g_RxInBuf;
+       fifo_destroy(m_rxUserdata->infifo);
+       fifo_destroy(m_rxUserdata->outfifo);
         delete m_rxUserdata;
     }
 /*
@@ -1096,80 +1114,133 @@ int MainFrame::rxCallback(
     float           out8k[N8];
     float           out48k[N48];
     short           out48k_short[N48];
+    short           in48k_short[N48];
+    short           indata[MAX_FPB];
+    short           outdata[MAX_FPB];
 
     (void) timeInfo;
     (void) statusFlags;
 
     assert(inputBuffer != NULL);
+    assert(outputBuffer != NULL);
+
+    /* 
+       framesPerBuffer is portaudio-speak for number of samples we
+       actually get from the record side and need to provide to the
+       play side. On Linux (at least) it was found that
+       framesPerBuffer may not always be what we ask for in the
+       framesPerBuffer field of Pa_OpenStream.  For example a request
+       for 960 sample buffers lead to framesPerBuffer = 1024.
+
+       To perform the 48 to 8 kHz conversion we need an integer
+       multiple of FDMDV_OS samples to support the interpolation and
+       decimation.  As we can't guarantee the size of framesPerBuffer
+       we do a little FIFO buffering.
+    */
+
+    /* assemble a mono buffer (just use left channel) and write to FIFO */
+
+    assert(framesPerBuffer < MAX_FPB);
+    for(i=0; i<framesPerBuffer; i++,rptr+=2)
+       indata[i] = *rptr;
+    fifo_write(cbData->infifo, indata, framesPerBuffer);
+
+    /* while we have enough samples available ... */
 
+    while (fifo_read(cbData->infifo, in48k_short, N48) == 0) {
+
+       /* convert to float */
+
+       for(i=0; i<N48; i++)
+           in48k[FDMDV_OS_TAPS + i] = in48k_short[i];
+
+       // downsample and update filter memory
+       fdmdv_48_to_8(out8k, &in48k[FDMDV_OS_TAPS], N8);
+       for(i = 0; i < FDMDV_OS_TAPS; i++)
+       {
+           in48k[i] = in48k[i + N48];
+       }
 #ifdef RX_CB
-    // Convert input model samples from 48 to 8 kHz
-    // just use left channel
-    for(i = 0; i < framesPerBuffer; i++, rptr += 2)
-    {
-        in48k[i + FDMDV_OS_TAPS] = *rptr;
-    }
-    // downsample and update filter memory
-    fdmdv_48_to_8(out8k, &in48k[FDMDV_OS_TAPS], N8);
-    for(i = 0; i < FDMDV_OS_TAPS; i++)
-    {
-        in48k[i] = in48k[i + framesPerBuffer];
-    }
-    // run demod, decoder and update GUI info
-    unsigned int j = N8;
-    //for(i = 0; i < N8; i++)
-    for(i = 0; i < j; i++)
-    {
-        g_RxInBuf[g_nInputBuf + i] = (short)out8k[i];
-    }
-    g_nInputBuf += FDMDV_NOM_SAMPLES_PER_FRAME;
-    per_frame_rx_processing(g_pRxOutBuf, &g_nInputBuf, g_CodecBits, g_RxInBuf, &g_nOutputBuf, &g_nRxIn, &g_State, g_pCodec2);
-    cbData->pWFPanel->m_newdata = true;
-    cbData->pSPPanel->m_newdata = true;
-    // if demod out of sync copy input audio from A/D to aid in tuning
-    if (g_nOutputBuf >= N8)
-    {
-        if(g_State == 0)
+       // run demod, decoder and update GUI info
+       for(i = 0; i < N8; i++)
+       {
+           g_RxInBuf[g_nInputBuf + i] = (short)out8k[i];
+       }
+       g_nInputBuf += FDMDV_NOM_SAMPLES_PER_FRAME;
+       per_frame_rx_processing(g_pRxOutBuf, &g_nInputBuf, g_CodecBits, g_RxInBuf, &g_nOutputBuf, &g_nRxIn, &g_State, g_pCodec2);
+    
+       cbData->pWFPanel->m_newdata = true;
+       cbData->pSPPanel->m_newdata = true;
+    
+       // if demod out of sync copy input audio from A/D to aid in tuning
+       if (g_nOutputBuf >= N8)
         {
-            for(i = 0; i < N8; i++)
+           if(g_State == 0)
             {
-                in8k[MEM8 + i] = out8k[i];       // A/D signal
-            }
-        }
-        else
+               for(i = 0; i < N8; i++)
+                {
+                   in8k[MEM8 + i] = out8k[i];       // A/D signal
+               }
+           }
+           else
+           {
+               for(i = 0; i < N8; i++)
+                {
+                   in8k[MEM8+i] = g_pRxOutBuf[i];   // decoded spech
+               }
+           }
+           g_nOutputBuf -= N8;
+       }
+       assert(g_nOutputBuf >= 0);
+       // shift speech samples in output buffer
+       for(i = 0; i < (unsigned int)g_nOutputBuf; i++)
         {
-            for(i = 0; i < N8; i++)
-            {
-                in8k[MEM8+i] = g_pRxOutBuf[i];   // decoded spech
-            }
+           g_pRxOutBuf[i] = g_pRxOutBuf[i + N8];
+       }
+ #endif
+       /* test: echo input to output */
+       for(i=0; i<N8; i++)
+           in8k[MEM8+i] = out8k[i];
+
+       // Convert output speech to 48 kHz sample rate
+       // upsample and update filter memory
+       fdmdv_8_to_48(out48k, &in8k[MEM8], N8);
+       for(i = 0; i < MEM8; i++)
+        {
+           in8k[i] = in8k[i + N8];
         }
-        g_nOutputBuf -= N8;
-    }
-    assert(g_nOutputBuf >= 0);
-    // shift speech samples in output buffer
-    for(i = 0; i < (unsigned int)g_nOutputBuf; i++)
-    {
-        g_pRxOutBuf[i] = g_pRxOutBuf[i + N8];
-    }
-    // Convert output speech to 48 kHz sample rate
-    // upsample and update filter memory
-    fdmdv_8_to_48(out48k, &in8k[MEM8], N8);
-    for(i = 0; i < MEM8; i++)
-    {
-        in8k[i] = in8k[i + N8];
+       assert(outputBuffer != NULL);
+
+       // write signal to fifo
+       for(i = 0; i < N48; i++)
+        {
+           out48k_short[i] = (short)out48k[i];
+       }
+
+       fifo_write(cbData->outfifo, out48k_short, N48);
     }
-    assert(outputBuffer != NULL);
-    // write signal to both channels
-    for(i = 0; i < N48; i++)
-    {
-        out48k_short[i] = (short)out48k[i];
+
+   /* OK now set up output samples */
+
+    if (fifo_read(cbData->outfifo, outdata, framesPerBuffer) == 0) {
+
+       /* write signal to both channels */
+
+       for(i=0; i<framesPerBuffer; i++,wptr+=2) {
+           wptr[0] = outdata[i]; 
+           wptr[1] = outdata[i]; 
+       }
     }
-    for(i = 0; i < framesPerBuffer; i++, wptr += 2)
-    {
-        wptr[0] = out48k_short[i];
-        wptr[1] = out48k_short[i];
+    else {
+       //printf("no data\n");
+       /* zero output if no data available */
+       for(i=0; i<framesPerBuffer; i++,wptr+=2) {
+           wptr[0] = 0; 
+           wptr[1] = 0; 
+       }
     }
-#endif
+
     return paContinue;
 }
 
index e0476564d0f83b80d003ce7bc32f7d235cb8dd50..cad24dedc70620887943f2b07c6223975636d820 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "codec2.h"
 #include "fdmdv.h"
+#include "fifo.h"
 
 #include "topFrame.h"
 #include "dlg_about.h"
@@ -119,6 +120,8 @@ typedef struct
 //    float           *mag_dB;
     float           in48k[FDMDV_OS_TAPS + N48];
     float           in8k[MEM8 + N8];
+    struct FIFO    *infifo;
+    struct FIFO    *outfifo;
 } paCallBackData;
 
 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=