From ae3e466c8b1516d26a3ac80a5f6ea5dd86ee6576 Mon Sep 17 00:00:00 2001 From: drowe67 Date: Mon, 15 Oct 2012 03:59:03 +0000 Subject: [PATCH] added a fifo buffer to interface between framesPerBufferof portaudio and the buffer size we need for modem processing. Basic audio loopback working now git-svn-id: https://svn.code.sf.net/p/freetel/code@752 01035d8c-6547-0410-b346-abe4f91aad63 --- fdmdv2/src/Makefile.linux | 2 +- fdmdv2/src/fdmdv2_defines.h | 30 ++---- fdmdv2/src/fdmdv2_main.cpp | 201 ++++++++++++++++++++++++------------ fdmdv2/src/fdmdv2_main.h | 3 + 4 files changed, 149 insertions(+), 87 deletions(-) diff --git a/fdmdv2/src/Makefile.linux b/fdmdv2/src/Makefile.linux index 9b0da6ea..56fbbbb8 100644 --- a/fdmdv2/src/Makefile.linux +++ b/fdmdv2/src/Makefile.linux @@ -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 \ diff --git a/fdmdv2/src/fdmdv2_defines.h b/fdmdv2/src/fdmdv2_defines.h index e2004f5c..f5e1939e 100644 --- a/fdmdv2/src/fdmdv2_defines.h +++ b/fdmdv2/src/fdmdv2_defines.h @@ -24,25 +24,13 @@ //========================================================================== #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) @@ -74,10 +66,6 @@ #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, diff --git a/fdmdv2/src/fdmdv2_main.cpp b/fdmdv2/src/fdmdv2_main.cpp index b6d2ce18..88a31c60 100644 --- a/fdmdv2/src/fdmdv2_main.cpp +++ b/fdmdv2/src/fdmdv2_main.cpp @@ -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; iinfifo, indata, framesPerBuffer); + + /* while we have enough samples available ... */ + while (fifo_read(cbData->infifo, in48k_short, N48) == 0) { + + /* convert to float */ + + for(i=0; ipWFPanel->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= 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