CurrentFilePath :=\r
CurrentFileFullPath :=\r
User :=OFA-Staff\r
-Date :=9/4/2012\r
+Date :=9/9/2012\r
CodeLitePath :="C:\bin\CodeLite"\r
LinkerName :=g++\r
SharedObjectLinkerName :=g++ -shared -fPIC\r
WXCFG:=gcc_dll\mswu\r
UNIT_TEST_PP_SRC_DIR:=C:\bin\UnitTest++-1.3\r
Objects=$(IntermediateDirectory)/src_dlg_about$(ObjectSuffix) $(IntermediateDirectory)/src_dlg_audio$(ObjectSuffix) $(IntermediateDirectory)/src_dlg_comports$(ObjectSuffix) $(IntermediateDirectory)/src_dlg_options$(ObjectSuffix) $(IntermediateDirectory)/src_fdmdv2_main$(ObjectSuffix) $(IntermediateDirectory)/src_fdmdv2_plot$(ObjectSuffix) $(IntermediateDirectory)/src_topFrame$(ObjectSuffix) $(IntermediateDirectory)/src_fdmdv2_plot_scatter$(ObjectSuffix) $(IntermediateDirectory)/src_fdmdv2_plot_waterfall$(ObjectSuffix) $(IntermediateDirectory)/src_fdmdv2_plot_spectrum$(ObjectSuffix) \\r
- $(IntermediateDirectory)/src_fdmdv2_pa_wrapper$(ObjectSuffix) $(IntermediateDirectory)/src_fdmdv2_process_audio$(ObjectSuffix) $(IntermediateDirectory)/src_fdmdv2_plot_scalar$(ObjectSuffix) \r
+ $(IntermediateDirectory)/src_fdmdv2_pa_wrapper$(ObjectSuffix) $(IntermediateDirectory)/src_fdmdv2_plot_scalar$(ObjectSuffix) \r
\r
##\r
## Main Build Targets \r
$(IntermediateDirectory)/src_fdmdv2_pa_wrapper$(PreprocessSuffix): ../src/fdmdv2_pa_wrapper.cpp\r
@$(CXX) $(CXXFLAGS) $(IncludePCH) $(IncludePath) $(PreprocessOnlySwitch) $(OutputSwitch) $(IntermediateDirectory)/src_fdmdv2_pa_wrapper$(PreprocessSuffix) "C:/bin/Projects/Radio/fdmdv2/src/fdmdv2_pa_wrapper.cpp"\r
\r
-$(IntermediateDirectory)/src_fdmdv2_process_audio$(ObjectSuffix): ../src/fdmdv2_process_audio.cpp $(IntermediateDirectory)/src_fdmdv2_process_audio$(DependSuffix)\r
- $(CXX) $(IncludePCH) $(SourceSwitch) "C:/bin/Projects/Radio/fdmdv2/src/fdmdv2_process_audio.cpp" $(CXXFLAGS) $(ObjectSwitch)$(IntermediateDirectory)/src_fdmdv2_process_audio$(ObjectSuffix) $(IncludePath)\r
-$(IntermediateDirectory)/src_fdmdv2_process_audio$(DependSuffix): ../src/fdmdv2_process_audio.cpp\r
- @$(CXX) $(CXXFLAGS) $(IncludePCH) $(IncludePath) -MG -MP -MT$(IntermediateDirectory)/src_fdmdv2_process_audio$(ObjectSuffix) -MF$(IntermediateDirectory)/src_fdmdv2_process_audio$(DependSuffix) -MM "C:/bin/Projects/Radio/fdmdv2/src/fdmdv2_process_audio.cpp"\r
-\r
-$(IntermediateDirectory)/src_fdmdv2_process_audio$(PreprocessSuffix): ../src/fdmdv2_process_audio.cpp\r
- @$(CXX) $(CXXFLAGS) $(IncludePCH) $(IncludePath) $(PreprocessOnlySwitch) $(OutputSwitch) $(IntermediateDirectory)/src_fdmdv2_process_audio$(PreprocessSuffix) "C:/bin/Projects/Radio/fdmdv2/src/fdmdv2_process_audio.cpp"\r
-\r
$(IntermediateDirectory)/src_fdmdv2_plot_scalar$(ObjectSuffix): ../src/fdmdv2_plot_scalar.cpp $(IntermediateDirectory)/src_fdmdv2_plot_scalar$(DependSuffix)\r
$(CXX) $(IncludePCH) $(SourceSwitch) "C:/bin/Projects/Radio/fdmdv2/src/fdmdv2_plot_scalar.cpp" $(CXXFLAGS) $(ObjectSwitch)$(IntermediateDirectory)/src_fdmdv2_plot_scalar$(ObjectSuffix) $(IncludePath)\r
$(IntermediateDirectory)/src_fdmdv2_plot_scalar$(DependSuffix): ../src/fdmdv2_plot_scalar.cpp\r
$(RM) $(IntermediateDirectory)/src_fdmdv2_pa_wrapper$(ObjectSuffix)\r
$(RM) $(IntermediateDirectory)/src_fdmdv2_pa_wrapper$(DependSuffix)\r
$(RM) $(IntermediateDirectory)/src_fdmdv2_pa_wrapper$(PreprocessSuffix)\r
- $(RM) $(IntermediateDirectory)/src_fdmdv2_process_audio$(ObjectSuffix)\r
- $(RM) $(IntermediateDirectory)/src_fdmdv2_process_audio$(DependSuffix)\r
- $(RM) $(IntermediateDirectory)/src_fdmdv2_process_audio$(PreprocessSuffix)\r
$(RM) $(IntermediateDirectory)/src_fdmdv2_plot_scalar$(ObjectSuffix)\r
$(RM) $(IntermediateDirectory)/src_fdmdv2_plot_scalar$(DependSuffix)\r
$(RM) $(IntermediateDirectory)/src_fdmdv2_plot_scalar$(PreprocessSuffix)\r
<File Name="../src/fdmdv2_plot_waterfall.cpp"/>
<File Name="../src/fdmdv2_plot_spectrum.cpp"/>
<File Name="../src/fdmdv2_pa_wrapper.cpp"/>
- <File Name="../src/fdmdv2_process_audio.cpp"/>
<File Name="../src/fdmdv2_plot_scalar.cpp"/>
</VirtualDirectory>
<VirtualDirectory Name="include">
-./Release/src_dlg_about.o ./Release/src_dlg_audio.o ./Release/src_dlg_comports.o ./Release/src_dlg_options.o ./Release/src_fdmdv2_main.o ./Release/src_fdmdv2_plot.o ./Release/src_topFrame.o ./Release/src_fdmdv2_plot_scatter.o ./Release/src_fdmdv2_plot_waterfall.o ./Release/src_fdmdv2_plot_spectrum.o ./Release/src_fdmdv2_pa_wrapper.o ./Release/src_fdmdv2_process_audio.o ./Release/src_fdmdv2_plot_scalar.o \r
+./Release/src_dlg_about.o ./Release/src_dlg_audio.o ./Release/src_dlg_comports.o ./Release/src_dlg_options.o ./Release/src_fdmdv2_main.o ./Release/src_fdmdv2_plot.o ./Release/src_topFrame.o ./Release/src_fdmdv2_plot_scatter.o ./Release/src_fdmdv2_plot_waterfall.o ./Release/src_fdmdv2_plot_spectrum.o ./Release/src_fdmdv2_pa_wrapper.o ./Release/src_fdmdv2_plot_scalar.o \r
//==========================================================================\r
#ifndef __FDMDV2_DEFINES__
#define __FDMDV2_DEFINES__
-
+#include "wx/wx.h"
/* FFT points */
#define FDMDV_NSPEC 512
\r
#define N48 (N8*FDMDV_OS) // processing buffer size at 48 kHz
#define NUM_CHANNELS 2 // I think most sound cards prefer stereo we will convert to mono
-#define BITS_PER_CODEC_FRAME (2*FDMDV_BITS_PER_FRAME)
-#define BYTES_PER_CODEC_FRAME (BITS_PER_CODEC_FRAME/8)
+#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 wxUSE_PCX 1\r
#define wxUSE_LIBTIFF 1\r
\r
-static int rxCallback(\r
- const void *inBuffer,\r
- void *outBuffer,\r
- unsigned long framesPerBuffer,\r
- const PaStreamCallbackTimeInfo *outTime,\r
- PaStreamCallbackFlags statusFlags,\r
- void *userData\r
- );\r
-static int txCallback(\r
- const void *inBuffer,\r
- void *outBuffer,\r
- unsigned long framesPerBuffer,\r
- const PaStreamCallbackTimeInfo *outTime,\r
- PaStreamCallbackFlags statusFlags,\r
- void *userData\r
- );\r
-\r
-float av_mag[FDMDV_NSPEC]; // shared between a few classes
+//float av_mag[FDMDV_NSPEC]; // shared between a few classes
\r
// initialize the application\r
IMPLEMENT_APP(MainApp);\r
// Create the main application window\r
MainFrame *frame = new MainFrame(NULL);\r
\r
- frame->m_panelDefaultA = new PlotPanel((wxFrame*) frame->m_auiNbookCtrl );\r
- frame->m_auiNbookCtrl->AddPage(frame->m_panelDefaultA, _("Test A"), true, wxNullBitmap );\r
-\r
-// frame->m_panelDefaultB = new PlotPanel((wxFrame*) frame->m_auiNbookCtrl );\r
-// frame->m_auiNbookCtrl->AddPage(frame->m_panelDefaultB, _("Test B"), true, wxNullBitmap );\r
-\r
- frame->m_panelSpectrum = new PlotSpectrum((wxFrame*) frame->m_auiNbookCtrl );\r
- frame->m_auiNbookCtrl->AddPage(frame->m_panelSpectrum, _("Spectrum"), true, wxNullBitmap );\r
-\r
- frame->m_panelWaterfall = new PlotWaterfall((wxFrame*) frame->m_auiNbookCtrl );\r
- frame->m_auiNbookCtrl->AddPage(frame->m_panelWaterfall, _("Waterfall"), true, wxNullBitmap );\r
-\r
-// frame->m_panelScatter = new PlotScatter((wxFrame*) frame->m_auiNbookCtrl );\r
-// frame->m_auiNbookCtrl->AddPage(frame->m_panelWaterfall, _("Scatter"), true, wxNullBitmap );\r
-\r
-// frame->m_panelScalar = new PlotScalar((wxFrame*) frame->m_auiNbookCtrl, 500, 500);\r
-// frame->m_auiNbookCtrl->AddPage(frame->m_panelWaterfall, _("Scalar"), true, wxNullBitmap );\r
+ SetTopWindow(frame);\r
\r
+ // Should guarantee that the first plot tab defined is the one\r
+ // displayed. But it doesn't when built from command line. Why?\r
frame->m_auiNbookCtrl->ChangeSelection(0);\r
\r
- SetTopWindow(frame);\r
frame->Layout();\r
frame->Show();\r
+\r
return true;\r
}\r
\r
{\r
wxMessageBox(wxT("Port Audio failed to initialize"), wxT("Pa_Initialize"), wxOK);\r
}\r
+ // Add Waterfall Plot window\r
+ m_panelWaterfall = new PlotWaterfall((wxFrame*) m_auiNbookCtrl );\r
+ m_auiNbookCtrl->AddPage(m_panelWaterfall, _("Waterfall"), true, wxNullBitmap );\r
+\r
+ // Add Spectrum Plot window\r
+ m_panelSpectrum = new PlotSpectrum((wxFrame*) m_auiNbookCtrl );\r
+ m_auiNbookCtrl->AddPage(m_panelSpectrum, _("Spectrum"), true, wxNullBitmap );\r
+\r
+// m_panelScatter = new PlotScatter((wxFrame*) m_auiNbookCtrl );\r
+// m_auiNbookCtrl->AddPage(m_panelWaterfall, _("Scatter"), true, wxNullBitmap );\r
+\r
+// m_panelScalar = new PlotScalar((wxFrame*) m_auiNbookCtrl, 500, 500);\r
+// m_auiNbookCtrl->AddPage(m_panelWaterfall, _("Scalar"), true, wxNullBitmap );\r
+\r
+ // Add generic plot window\r
+ m_panelDefaultA = new PlotPanel((wxFrame*) m_auiNbookCtrl );\r
+ m_auiNbookCtrl->AddPage(m_panelDefaultA, _("Test A"), true, wxNullBitmap );\r
}\r
\r
//-------------------------------------------------------------------------\r
}\r
dc.SetUserScale(m_zoom, m_zoom);\r
const wxSize size = GetClientSize();\r
- dc.DrawBitmap(m_bitmap, dc.DeviceToLogicalX((size.x - m_zoom * m_bitmap.GetWidth()) / 2), dc.DeviceToLogicalY((size.y - m_zoom * m_bitmap.GetHeight()) / 2), true);\r
+// dc.DrawBitmap(m_bitmap, dc.DeviceToLogicalX((size.x - m_zoom * m_bitmap.GetWidth()) / 2), dc.DeviceToLogicalY((size.y - m_zoom * m_bitmap.GetHeight()) / 2), true);\r
}\r
\r
//-------------------------------------------------------------------------\r
wxMessageBox(wxT("Got Click!"), wxT("OnTogBtnALCClick"), wxOK);\r
event.Skip();\r
}\r
+/*\r
\r
//-------------------------------------------------------------------------\r
// rxCallback()\r
//-------------------------------------------------------------------------\r
-static int rxCallback(\r
+int MainFrame::rxCallback(\r
const void *inBuffer,\r
void *outBuffer,\r
unsigned long framesPerBuffer,\r
*out++ = 0.5f * (leftIn + rightIn); // mixing\r
}\r
return paContinue; // 0;\r
-}
-\r
+}\r
+ * */
+/*\r
//-------------------------------------------------------------------------\r
// txCallback()\r
//-------------------------------------------------------------------------\r
-static int txCallback(\r
+int MainFrame::txCallback(\r
const void *inBuffer,\r
void *outBuffer,\r
unsigned long framesPerBuffer,\r
}\r
return paContinue; // 0;\r
}
+*/\r
\r
//-------------------------------------------------------------------------\r
// OnTogBtnOnOff()\r
{\r
if(m_sound != NULL)\r
{\r
- //if(m_sound->IsOk()IsPlaying())\r
- //{\r
+ if(m_sound->IsOk())\r
+ {\r
m_sound->Stop();\r
m_sound = NULL;\r
- //}\r
+ }\r
}\r
-// wxMessageBox("Got Click!", "OnClose", wxOK);\r
-// event.Skip();\r
}\r
\r
//-------------------------------------------------------------------------\r
DECLARE_APP(MainApp)
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=\r
-// Class MainApp\r
+// Class MainFrame\r
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=\r
class MainFrame : public TopFrame
{
PaError m_rxErr;
PaError m_txErr;
wxSound *m_sound;
- float av_mag[FDMDV_NSPEC];\r
-
- void DoStartThread();\r
- void DoPauseThread();\r
+// static int (*rxCallback(void const*, void*, unsigned long, PaStreamCallbackTimeInfo const*, unsigned long, void*));
+/*\r
+ static int rxCallback(\r
+ const void *inBuffer,\r
+ void *outBuffer,\r
+ unsigned long framesPerBuffer,\r
+ const PaStreamCallbackTimeInfo *outTime,\r
+ PaStreamCallbackFlags statusFlags,\r
+ void *userData\r
+ );\r
+ static int txCallback(\r
+ const void *inBuffer,\r
+ void *outBuffer,\r
+ unsigned long framesPerBuffer,\r
+ const PaStreamCallbackTimeInfo *outTime,\r
+ PaStreamCallbackFlags statusFlags,\r
+ void *userData\r
+ );\r
+*/\r
+// void DoStartThread();\r
+// void DoPauseThread();\r
\r
//void DoResumeThread() { ... }\r
\r
- void OnThreadUpdate(wxThreadEvent&);\r
- void OnThreadCompletion(wxThreadEvent&);\r
+// void OnThreadUpdate(wxThreadEvent&);\r
+// void OnThreadCompletion(wxThreadEvent&);\r
\r
protected:\r
// Fdmdv2ThreadAudio *m_pThread;\r
bool m_useMemory;
wxTextCtrl* m_tc;
- wxBitmap m_bitmap;
- wxBitmap m_alphaBitmap;
+// wxBitmap m_bitmap;
+// wxBitmap m_alphaBitmap;
int m_zoom;
// any class wishing to process wxWidgets events must use this macro
// DECLARE_EVENT_TABLE()
};
-
+/*\r
+typedef int (ClassName::*CallbackType)(float);\r
+\r
+void DoWork(CallbackType callback)\r
+{\r
+ //Class instance to invoke it through\r
+ ClassName instance;\r
+\r
+ //Invocation\r
+ int result = instance->*callback(1.0f);\r
+}\r
+\r
+int main(int argc, char ** argv)\r
+{\r
+ //Pass in SomeCallback to the DoWork\r
+ DoWork(&ClassName::Method);\r
+}\r
+*/
#endif //__FDMDV2_MAIN__
statusFlags = 0;\r
streamCallback = NULL;\r
streamFinishedCallback = NULL;\r
- timeInfo = 0;
+ timeInfo = 0;\r
+ loadData();\r
}
PortAudioWrap::~PortAudioWrap()
if(isOpen())
{
PaError rv = Pa_CloseStream(stream);
-// stream = NULL;
return rv;
}
else
PaError PortAudioWrap::setInputDevice(PaDeviceIndex index)\r
{
-// inputDevice = index;
inputBuffer.device = index;
return paNoError;
}
PaError PortAudioWrap::setOutputDevice(PaDeviceIndex index)\r
{
-// outputDevice = index;
outputBuffer.device = index;
return paNoError;
}
streamCallback = callback;
return paNoError;
}
+\r
+typedef struct
+{
+ float in48k[FDMDV_OS_TAPS + N48];
+ float in8k[MEM8 + N8];
+} paCallBackData;
+\r
+
+//----------------------------------------------------------------
+// per_frame_rx_processing()
+//----------------------------------------------------------------
+void PortAudioWrap::per_frame_rx_processing(
+ short output_buf[], /* output buf of decoded speech samples */
+ int *n_output_buf, /* how many samples currently in output_buf[] */
+ int codec_bits[], /* current frame of bits for decoder */
+ short input_buf[], /* input buf of modem samples input to demod */
+ int *n_input_buf, /* how many samples currently in input_buf[] */
+ int *nin, /* amount of samples demod needs for next call */
+ int *state, /* used to collect codec_bits[] halves */
+ struct CODEC2 *c2 /* Codec 2 states */
+ )
+{
+ struct FDMDV_STATS stats;
+ int sync_bit;
+ float rx_fdm[FDMDV_MAX_SAMPLES_PER_FRAME];
+ int rx_bits[FDMDV_BITS_PER_FRAME];
+ unsigned char packed_bits[BYTES_PER_CODEC_FRAME];
+ float rx_spec[FDMDV_NSPEC];
+ int i;\r
+ int nin_prev;\r
+ int bit;\r
+ int byte;
+ int next_state;
+
+ assert(*n_input_buf <= (2 * FDMDV_NOM_SAMPLES_PER_FRAME));
+
+ /*
+ This while loop will run the demod 0, 1 (nominal) or 2 times:
+
+ 0: when tx sample clock runs faster than rx, occasionally we
+ will run out of samples
+
+ 1: normal, run decoder once, every 2nd frame output a frame of
+ speech samples to D/A
+
+ 2: when tx sample clock runs slower than rx, occasionally we will
+ have enough samples to run demod twice.
+
+ With a +/- 10 Hz sample clock difference at FS=8000Hz (+/- 1250
+ ppm), case 0 or 1 occured about once every 30 seconds. This is
+ no problem for the decoded audio.
+ */
+ while(*n_input_buf >= *nin)
+ {
+ // demod per frame processing
+ for(i = 0; i < *nin; i++)
+ {
+ rx_fdm[i] = (float)input_buf[i]/FDMDV_SCALE;
+ }
+ nin_prev = *nin;
+ fdmdv_demod(fdmdv_state, rx_bits, &sync_bit, rx_fdm, nin);
+ *n_input_buf -= nin_prev;
+ assert(*n_input_buf >= 0);
+
+ // shift input buffer
+ for(i=0; i<*n_input_buf; i++)
+ {
+ input_buf[i] = input_buf[i+nin_prev];
+ }
+
+ // compute rx spectrum & get demod stats, and update GUI plot data
+ fdmdv_get_rx_spectrum(fdmdv_state, rx_spec, rx_fdm, nin_prev);
+ fdmdv_get_demod_stats(fdmdv_state, &stats);
+ averageData(rx_spec);
+// aScatter->add_new_samples(stats.rx_symbols);
+// aTimingEst->add_new_sample(stats.rx_timing);
+// aFreqEst->add_new_sample(stats.foff);
+// aSNR->add_new_sample(stats.snr_est);
+ /*
+ State machine to:
+
+ + Mute decoded audio when out of sync. The demod is synced
+ when we are using the fine freq estimate and SNR is above
+ a thresh.
+
+ + Decode codec bits only if we have a 0,1 sync bit
+ sequence. Collects two frames of demod bits to decode
+ one frame of codec bits.
+ */
+ next_state = *state;
+ switch(*state)
+ {
+ case 0:
+ // mute output audio when out of sync
+ if(*n_output_buf < 2 * codec2_samples_per_frame(c2) - N8)
+ {
+ for(i=0; i<N8; i++)\r
+ {
+ output_buf[*n_output_buf + i] = 0;
+ }
+ *n_output_buf += N8;
+ }
+ assert(*n_output_buf <= (2 * codec2_samples_per_frame(c2)));
+ if((stats.fest_coarse_fine == 1) && (stats.snr_est > 3.0))
+ {
+ next_state = 1;
+ }
+ break;
+
+ case 1:
+ if(sync_bit == 0)
+ {
+ next_state = 2;
+ // first half of frame of codec bits
+ memcpy(codec_bits, rx_bits, FDMDV_BITS_PER_FRAME * sizeof(int));
+ }
+ else
+ {
+ next_state = 1;
+ }
+ if(stats.fest_coarse_fine == 0)
+ {
+ next_state = 0;
+ }
+ break;
+
+ case 2:
+ next_state = 1;
+ if(stats.fest_coarse_fine == 0)
+ {
+ next_state = 0;
+ }
+ if(sync_bit == 1)
+ {
+ // second half of frame of codec bits
+ memcpy(&codec_bits[FDMDV_BITS_PER_FRAME], rx_bits, FDMDV_BITS_PER_FRAME*sizeof(int));
+ // pack bits, MSB received first
+ bit = 7;
+ byte = 0;
+ memset(packed_bits, 0, BYTES_PER_CODEC_FRAME);
+ for(i = 0; i < BITS_PER_CODEC_FRAME; i++)
+ {
+ packed_bits[byte] |= (codec_bits[i] << bit);
+ bit--;
+ if(bit < 0)
+ {
+ bit = 7;
+ byte++;
+ }
+ }
+ assert(byte == BYTES_PER_CODEC_FRAME);
+ // add decoded speech to end of output buffer
+ if(*n_output_buf <= codec2_samples_per_frame(c2))
+ {
+ codec2_decode(c2, &output_buf[*n_output_buf], packed_bits);
+ *n_output_buf += codec2_samples_per_frame(c2);
+ }
+ assert(*n_output_buf <= (2 * codec2_samples_per_frame(c2)));
+ }
+ break;
+ }
+ *state = next_state;
+ }
+}\r
+\r
+//----------------------------------------------------------------
+// update average of each spectrum point
+//----------------------------------------------------------------
+void PortAudioWrap::averageData(float mag_dB[])
+{
+ int i;
+
+ for(i = 0; i < FDMDV_NSPEC; i++)
+ {
+ m_av_mag[i] = (1.0 - BETA) * m_av_mag[i] + BETA * mag_dB[i];
+ }
+}\r
+\r
+//----------------------------------------------------------------
+// create Dummy Data
+//----------------------------------------------------------------
+void PortAudioWrap::loadData()\r
+{\r
+ int i;\r
+ for(i = 0; i < FDMDV_NSPEC; i++)\r
+ {\r
+ m_av_mag[i] = i;\r
+ }\r
+}
//==========================================================================\r
// Name: fdmdv2_pa_wrapper.h\r
// Purpose: Defines a wrapper class around PortAudio\r
-// Created: August 12, 2012
+// Created: August 12, 2012\r
// Initial author: David Witten\r
// License:\r
//\r
-// Copyright (C) 2012 David Witten
-//
-// All rights reserved.
-//
+// Copyright (C) 2012 David Witten\r
+//\r
+// All rights reserved.\r
+//\r
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License version 2.1,
// as published by the Free Software Foundation. This program is
// You should have received a copy of the GNU Lesser General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
//
-//==========================================================================\r
+//==========================================================================
#include <stdio.h>
#include <math.h>
+#include "fdmdv2_defines.h"
+#include "fdmdv.h"
+#include "codec2.h"
#include "portaudio.h"
-\r
+
// Note that many of the older ISA sound cards on PCs do NOT support
// full duplex audio (simultaneous record and playback).
// And some only support full duplex at lower sample rates.
//#define SAMPLE_RATE (44100)
+//
#define PA_SAMPLE_TYPE paFloat32 //paInt16
#define FRAMES_PER_BUFFER (64)
typedef float SAMPLE;
-float CubicAmplifier(float input);
-
-class PortAudioWrap\r
-{\r
- public:\r
- PortAudioWrap();\r
+class PortAudioWrap
+{
+ public:
+ PortAudioWrap();
~PortAudioWrap();\r
+
+ float m_av_mag[FDMDV_NSPEC];
\r
- private:\r
- PaStream *stream;\r
+ private:
+ PaStream *stream;
PaStreamParameters inputBuffer;
PaStreamParameters outputBuffer;
- void *userData;\r
- int samplerate;\r
+ void *userData;
+ int samplerate;
unsigned long framesPerBuffer;
- PaStreamCallbackFlags statusFlags;\r
- PaStreamCallback *streamCallback;\r
- PaStreamFinishedCallback *streamFinishedCallback;\r
+ PaStreamCallbackFlags statusFlags;
+ PaStreamCallback *streamCallback;
+ PaStreamFinishedCallback *streamFinishedCallback;
const PaStreamCallbackTimeInfo *timeInfo;
-// PaDeviceIndex inputDevice;
-// PaDeviceIndex outputDevice;
+ struct FDMDV *fdmdv_state;\r
\r
public:\r
-
- PaError streamOpen();\r
+ void per_frame_rx_processing(\r
+ short output_buf[], /* output buf of decoded speech samples */\r
+ int *n_output_buf, /* how many samples currently in output_buf[] */\r
+ int codec_bits[], /* current frame of bits for decoder */\r
+ short input_buf[], /* input buf of modem samples input to demod */\r
+ int *n_input_buf, /* how many samples currently in input_buf[] */\r
+ int *nin, /* amount of samples demod needs for next call */\r
+ int *state, /* used to collect codec_bits[] halves */\r
+ struct CODEC2 *c2 /* Codec 2 states */\r
+ );\r
\r
- PaDeviceIndex getDefaultInputDevice();
- PaDeviceIndex getDefaultOutputDevice();\r
+ /*\r
+ static int Callback(\r
+ const void *inBuffer,\r
+ void *outBuffer,\r
+ unsigned long framesPerBuffer,\r
+ const PaStreamCallbackTimeInfo *outTime,\r
+ PaStreamCallbackFlags statusFlags,\r
+ void *userData\r
+ );\r
+ static int txCallback(\r
+ const void *inBuffer,\r
+ void *outBuffer,\r
+ unsigned long framesPerBuffer,\r
+ const PaStreamCallbackTimeInfo *outTime,\r
+ PaStreamCallbackFlags statusFlags,\r
+ void *userData\r
+ );\r
+ */\r
\r
- PaStreamParameters *getDeviceInfo(PaDeviceIndex idx);\r
- PaError setFramesPerBuffer(unsigned long size);\r
- PaError setSampleRate(unsigned long size);\r
- PaError setStreamFlags(PaStreamFlags flags);\r
- PaError setCallback(PaStreamCallback *streamCallback);\r
- PaError setStreamCallback(PaStream *stream, PaStreamCallback* callback ) { streamCallback = callback; return 0;}
- PaError setStreamFinishedCallback(PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback );
-
- PaError setInputDevice(PaDeviceIndex dev);\r
- PaError setInputChannelCount(int count);
- int getInputChannelCount();
- PaError setInputSampleFormat(PaSampleFormat format);
- PaError setInputSampleRate(PaSampleFormat format);
- PaError setInputLatency(PaTime latency);\r
- void setInputHostApiStreamInfo(void *info = NULL);\r
- PaTime getInputDefaultLowLatency();
- const char *getDeviceName(PaDeviceIndex dev);
-
- PaError setOutputDevice(PaDeviceIndex dev);\r
- PaError setOutputChannelCount(int count);
- const int getOutputChannelCount();
- PaError setOutputSampleFormat(PaSampleFormat format);
- PaError setOutputLatency(PaTime latency);
- void setOutputHostApiStreamInfo(void *info = NULL);
- PaTime getOutputDefaultLowLatency();
+ PaDeviceIndex getDefaultInputDevice();\r
+ PaDeviceIndex getDefaultOutputDevice();\r
+ PaStreamParameters *getDeviceInfo(PaDeviceIndex idx);\r
+\r
+ PaError setFramesPerBuffer(unsigned long size);\r
+ PaError setSampleRate(unsigned long size);\r
+ PaError setStreamFlags(PaStreamFlags flags);\r
+ PaError setCallback(PaStreamCallback *streamCallback);\r
+ PaError setStreamCallback(PaStream *stream, PaStreamCallback* callback) { streamCallback = callback; return 0;}\r
+ PaError setStreamFinishedCallback(PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback);\r
+ PaError streamOpen();\r
+\r
+ PaError setInputDevice(PaDeviceIndex dev);\r
+ PaError setInputChannelCount(int count);\r
+ int getInputChannelCount();\r
+ PaError setInputSampleFormat(PaSampleFormat format);\r
+ PaError setInputSampleRate(PaSampleFormat format);\r
+ PaError setInputLatency(PaTime latency);\r
+ void setInputHostApiStreamInfo(void *info = NULL);\r
+ PaTime getInputDefaultLowLatency();\r
+ const char *getDeviceName(PaDeviceIndex dev);\r
+\r
+ PaError setOutputDevice(PaDeviceIndex dev);\r
+ PaError setOutputChannelCount(int count);\r
+ const int getOutputChannelCount();\r
+ PaError setOutputSampleFormat(PaSampleFormat format);\r
+ PaError setOutputLatency(PaTime latency);\r
+ PaError streamStart();\r
+ PaError streamClose();\r
+ void setOutputHostApiStreamInfo(void *info = NULL);\r
+ PaTime getOutputDefaultLowLatency();\r
+ void averageData(float mag_dB[]);\r
+\r
+ void setFdmdvState(FDMDV* fdmdv_state) {this->fdmdv_state = fdmdv_state;}
+// void setFramesPerBuffer(unsigned long framesPerBuffer) {this->framesPerBuffer = framesPerBuffer;}
+ void setInputBuffer(const PaStreamParameters& inputBuffer) {this->inputBuffer = inputBuffer;}
+// void setAvMag(float av_mag) {this->m_av_mag = av_mag;}
+ void setOutputBuffer(const PaStreamParameters& outputBuffer) {this->outputBuffer = outputBuffer;}
+ void setSamplerate(int samplerate) {this->samplerate = samplerate;}
+ void setStatusFlags(const PaStreamCallbackFlags& statusFlags) {this->statusFlags = statusFlags;}
+ void setStream(PaStream* stream) {this->stream = stream;}
+// void setStreamCallback(PaStreamCallback* streamCallback) {this->streamCallback = streamCallback;}
+ void setStreamFinishedCallback(PaStreamFinishedCallback* streamFinishedCallback) {this->streamFinishedCallback = streamFinishedCallback;}\r
+ void setTimeInfo(PaStreamCallbackTimeInfo* timeInfo) {this->timeInfo = timeInfo;}\r
+ void setUserData(void* userData) {this->userData = userData;}\r
+ FDMDV* getFdmdvState() {return fdmdv_state;}\r
+ unsigned long getFramesPerBuffer() const {return framesPerBuffer;}\r
+ const PaStreamParameters& getInputBuffer() const {return inputBuffer;}\r
+// const float *getAvMag(int idx) const {return (float *)&m_av_mag[idx];}\r
+ const PaStreamParameters& getOutputBuffer() const {return outputBuffer;}\r
+ int getSamplerate() const {return samplerate;}\r
+ const PaStreamCallbackFlags& getStatusFlags() const {return statusFlags;}\r
+ PaStream* getStream() {return stream;}\r
+// PaStreamCallback* getStreamCallback() {return streamCallback;}\r
+// PaStreamFinishedCallback* getStreamFinishedCallback() {return streamFinishedCallback;}\r
+// PaStreamCallbackTimeInfo* getTimeInfo() {return timeInfo;}\r
+ void *getUserData() {return userData;}\r
\r
-// PaError init();\r
- PaError streamStart();\r
- PaError streamClose();\r
- void terminate();\r
- bool isOpen() const;\r
- void stop();
- void abort();
- bool isStopped() const;
- bool isActive() const;
+ void terminate();\r
+ void stop();\r
+ void abort();\r
+ bool isOpen() const;\r
+ bool isStopped() const;\r
+ bool isActive() const;\r
+ void loadData();\r
};\r
// components of this project)\r
//==========================================================================\r
#include <string.h>\r
-#include "wx/wx.h"\r
-#include "fdmdv2_main.h"\r
#include "fdmdv2_plot.h"\r
\r
BEGIN_EVENT_TABLE(PlotPanel, wxPanel)\r
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=\r
PlotPanel::PlotPanel(wxFrame* parent) : wxPanel(parent)\r
{\r
+ m_pNoteBook = (wxAuiNotebook *) parent;\r
+ m_pTopFrame = (MainFrame *)m_pNoteBook->GetParent();\r
+ m_bmp = new wxBitmap(MAX_BMP_X, MAX_BMP_Y, wxBITMAP_SCREEN_DEPTH);
+ wxNativePixelData m_pBmp(*m_bmp);\r
+ if ( !m_pBmp )\r
+ {\r
+ // ... raw access to bitmap data unavailable, do something else ...\r
+ return;\r
+ }\r
+\r
+ if ( m_pBmp.GetWidth() < 20 || m_pBmp.GetHeight() < 20 )\r
+ {\r
+ // ... complain: the bitmap it too small ...\r
+ return;\r
+ }\r
+\r
+ m_zoomFactor = 1.0;\r
m_clip = false;\r
m_use_bitmap = true;\r
- m_zoomFactor = 1.0;\r
+ m_newdata = false;\r
m_rubberBand = false;\r
m_mouseDown = false;\r
m_penShortDash = wxPen(wxColor(0xA0, 0xA0, 0xA0), 1, wxPENSTYLE_SHORT_DASH);\r
m_penDotDash = wxPen(wxColor(0xD0, 0xD0, 0xD0), 1, wxPENSTYLE_DOT_DASH);\r
+ m_penSolid = wxPen(wxColor(0x00, 0x00, 0x00), 1, wxPENSTYLE_SOLID);\r
// m_gridLeftOffset = 20;\r
// m_gridRightOffset = 5;\r
// m_gridTopOffset = 5;\r
SetLabelSize(10.0);\r
}\r
\r
+//-------------------------------------------------------------------------\r
+// ~PlotPanel()\r
+//-------------------------------------------------------------------------\r
+PlotPanel::~PlotPanel()\r
+{\r
+ if(!m_bmp->IsNull())\r
+ {\r
+ delete m_bmp;\r
+ }
+}\r
+\r
//-------------------------------------------------------------------------\r
// GetLabelSize()\r
//-------------------------------------------------------------------------\r
\r
// Vertical gridlines\r
dc.SetPen(m_penShortDash);\r
- for(p = (PLOT_BORDER + XLEFT_OFFSET + GRID_INCREMENT); p < m_w; p += GRID_INCREMENT)\r
+ for(p = (PLOT_BORDER + XLEFT_OFFSET + GRID_INCREMENT); p < ((m_w - XLEFT_OFFSET) + GRID_INCREMENT); p += GRID_INCREMENT)\r
{\r
dc.DrawLine(p, (m_h + PLOT_BORDER), p, PLOT_BORDER);\r
}\r
dc.SetBrush(ltBlueBrush);\r
dc.SetPen(wxPen(BLACK_COLOR, 1));\r
dc.DrawRectangle(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_w, m_h);\r
-\r
drawGraticule(dc);\r
}\r
\r
// License: BSD License (other licenses may apply to other\r
// components of this project)\r
//==========================================================================\r
+//#include "fdmdv2_main.h"\r
#ifndef __FDMDV2_PLOT__
#define __FDMDV2_PLOT__
-//#include "codec2.h"
-//#include "fdmdv.h"\r
+#include <wx/wx.h>\r
+#include <wx/aui/auibook.h>\r
#include <wx/rawbmp.h>\r
#include <wx/image.h>\r
#include <wx/dcbuffer.h>\r
\r
+#define MAX_ZOOM 7\r
+#define MAX_BMP_X (400 * MAX_ZOOM)\r
+#define MAX_BMP_Y (400 * MAX_ZOOM)\r
+\r
#define wxUSE_FILEDLG 1\r
#define wxUSE_LIBPNG 1\r
#define wxUSE_LIBJPEG 1\r
\r
#define PLOT_BORDER 3\r
#define XLEFT_OFFSET 30\r
-#define XLEFT_TEXT_OFFSET 7\r
+#define XLEFT_TEXT_OFFSET 8\r
#define YBOTTOM_OFFSET 25\r
#define GRID_INCREMENT 50\r
#define GREY_COLOR wxColor(0x80, 0x80, 0x80)\r
wxWHITE_BRUSH\r
*/\r
\r
+class MainFrame;\r
+\r
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=\r
// Class PlotPanel\r
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=\r
{\r
public:\r
PlotPanel(wxFrame* parent);\r
- void paintEvent(wxPaintEvent & evt);\r
- void draw(wxAutoBufferedPaintDC& dc);\r
- void drawGraticule(wxAutoBufferedPaintDC& dc);\r
+ ~PlotPanel();\r
wxPen m_penShortDash;\r
wxPen m_penDotDash;\r
+ wxPen m_penSolid;\r
wxRect m_rectCtrl;\r
wxRect m_rectGrid;\r
wxRect m_rectPlot;\r
-// int m_gridLeftOffset;\r
-// int m_gridRightOffset;\r
-// int m_gridTopOffset;\r
-// int m_gridBottomOffset;\r
+ MainFrame *m_pTopFrame;\r
+ wxAuiNotebook *m_pNoteBook;\r
double m_label_size;\r
wxSize m_Bufsz;\r
+ bool m_newdata;\r
wxBitmap *m_bmp;\r
- wxImagePixelData *m_pBmp;\r
- //wxNativePixelData *m_pBmp;\r
- //wxAlphaPixelData *m_pBmp;\r
+ wxNativePixelData *m_pBmp;\r
\r
// some useful events\r
- void OnMouseMove(wxMouseEvent& event);\r
- void OnMouseDown(wxMouseEvent& event);\r
- void OnMouseUp(wxMouseEvent& event);\r
- void OnMouseWheelMoved(wxMouseEvent& event);\r
- void OnClose(wxCloseEvent& event ){ event.Skip(); }
- void OnSize( wxSizeEvent& event );\r
- void OnErase(wxEraseEvent& event);\r
- void OnPaint(wxPaintEvent& event);\r
-
- double SetZoomFactor(double zf);\r
- double GetZoomFactor(double zf);\r
+ void OnMouseMove(wxMouseEvent& event);\r
+ void OnMouseDown(wxMouseEvent& event);\r
+ void OnMouseUp(wxMouseEvent& event);\r
+ void OnMouseWheelMoved(wxMouseEvent& event);\r
+ void OnClose(wxCloseEvent& event ){ event.Skip(); }
+ void OnSize( wxSizeEvent& event );\r
+ void OnErase(wxEraseEvent& event);\r
+ void OnPaint(wxPaintEvent& event);\r
//void OnUpdateUI( wxUpdateUIEvent& event ){ event.Skip(); }\r
- void OnShow(wxShowEvent& event);\r
- double GetLabelSize();\r
- void SetLabelSize(double size);\r
+
+ void paintEvent(wxPaintEvent & evt);\r
+ virtual void draw(wxAutoBufferedPaintDC& dc);\r
+ virtual void drawGraticule(wxAutoBufferedPaintDC& dc);\r
+ virtual double SetZoomFactor(double zf);\r
+ virtual double GetZoomFactor(double zf);\r
+ virtual void OnShow(wxShowEvent& event);\r
+ virtual double GetLabelSize();\r
+ virtual void SetLabelSize(double size);\r
protected:\r
- int m_x;
- int m_y;
- int m_w;
- int m_h;
- int m_prev_w;
- int m_prev_h;
- int m_prev_x;
- int m_prev_y;
- bool m_use_bitmap;\r
- bool m_clip;\r
- bool m_rubberBand;\r
- bool m_mouseDown;\r
- double m_zoomFactor;\r
+ int m_x;
+ int m_y;
+ int m_w;
+ int m_h;\r
+ int m_left;\r
+ int m_top;
+ int m_prev_w;
+ int m_prev_h;
+ int m_prev_x;
+ int m_prev_y;
+ bool m_use_bitmap;\r
+ bool m_clip;\r
+ bool m_rubberBand;\r
+ bool m_mouseDown;\r
+ double m_zoomFactor;\r
+// int m_gridLeftOffset;\r
+// int m_gridRightOffset;\r
+// int m_gridTopOffset;\r
+// int m_gridBottomOffset;\r
DECLARE_EVENT_TABLE()\r
};\r
#endif //__FDMDV2_PLOT__\r
//fl_pop_clip();\r
}
\r
-// // update average of each spectrum point\r
-// void new_data(float mag_dB[])\r
-// {\r
-// int i;\r
-//\r
-// for(i=0; i<FDMDV_NSPEC; i++)
-// {\r
-// av_mag[i] = (1.0 - BETA)*av_mag[i] + BETA*mag_dB[i];
-// }\r
-// }
-\r
//----------------------------------------------------------------\r
// OnPaint()\r
//----------------------------------------------------------------\r
DECLARE_EVENT_TABLE()\r
};\r
\r
-//// update average of each spectrum point\r
-//void new_data(float mag_dB[])\r
-//{\r
-// int i;\r
-//\r
-// for(i=0; i<FDMDV_NSPEC; i++)\r
-// {\r
-// av_mag[i] = (1.0 - BETA)*av_mag[i] + BETA*mag_dB[i];\r
-// }\r
-//}\r
-\r
#endif // __FDMDV2_PLOT_SCALAR__
\r
SetLabelSize(10.0);\r
}\r
\r
+#define TEXT_BASELINE_OFFSET_Y -5\r
+\r
//-------------------------------------------------------------------------\r
// drawGraticule()\r
//-------------------------------------------------------------------------\r
int p;\r
char buf[15];\r
wxString s;\r
+ //int h_mod_inc = 0;\r
\r
// Vertical gridlines\r
dc.SetPen(m_penShortDash);\r
- for(p = (PLOT_BORDER + XLEFT_OFFSET + GRID_INCREMENT); p < m_w; p += GRID_INCREMENT)\r
+ for(p = (PLOT_BORDER + XLEFT_OFFSET + GRID_INCREMENT); p < ((m_w - XLEFT_OFFSET) + GRID_INCREMENT); p += GRID_INCREMENT)\r
{\r
dc.DrawLine(p, (m_h + PLOT_BORDER), p, PLOT_BORDER);\r
}\r
+\r
+ int y_zero = (m_h - m_top) / 2 ;\r
+ dc.SetPen(m_penSolid);\r
+ dc.DrawLine(PLOT_BORDER + XLEFT_OFFSET, y_zero, (m_w + PLOT_BORDER + XLEFT_OFFSET), y_zero);\r
+ sprintf(buf, "%6.0f", 0.0);\r
+ dc.DrawText(buf, XLEFT_TEXT_OFFSET, y_zero + TEXT_BASELINE_OFFSET_Y);\r
+\r
// Horizontal gridlines\r
dc.SetPen(m_penDotDash);\r
- for(p = (m_h - GRID_INCREMENT); p > PLOT_BORDER; p -= GRID_INCREMENT)\r
+ for(p = 0; (y_zero + p) < m_h ; p += GRID_INCREMENT)\r
{\r
- dc.DrawLine(PLOT_BORDER + XLEFT_OFFSET, (p + PLOT_BORDER), (m_w + PLOT_BORDER + XLEFT_OFFSET), (p + PLOT_BORDER));\r
+ if(p > 0)\r
+ {\r
+ dc.DrawLine(PLOT_BORDER + XLEFT_OFFSET, (y_zero + p), (m_w + PLOT_BORDER + XLEFT_OFFSET), (y_zero + p));\r
+ sprintf(buf, "%6.0f", (double)(p) * -10);\r
+ dc.DrawText(buf, XLEFT_TEXT_OFFSET, (y_zero + p + TEXT_BASELINE_OFFSET_Y));\r
+\r
+ dc.DrawLine(PLOT_BORDER + XLEFT_OFFSET, (y_zero - p), (m_w + PLOT_BORDER + XLEFT_OFFSET), (y_zero - p));\r
+ sprintf(buf, "%6.0f", (double)(p) * 10);\r
+ dc.DrawText(buf, XLEFT_TEXT_OFFSET, (y_zero - p + TEXT_BASELINE_OFFSET_Y));\r
+ }\r
}\r
+\r
// Label the X-Axis\r
dc.SetPen(wxPen(GREY_COLOR, 1));\r
for(p = GRID_INCREMENT; p < (m_w - YBOTTOM_OFFSET); p += GRID_INCREMENT)\r
sprintf(buf, "%1.1f Hz",(double)(p / 10));\r
dc.DrawText(buf, p - PLOT_BORDER + XLEFT_OFFSET, m_h + YBOTTOM_OFFSET/2);\r
}\r
- // Label the Y-Axis\r
- for(p = (m_h - GRID_INCREMENT); p > PLOT_BORDER; p -= GRID_INCREMENT)\r
- {\r
- sprintf(buf, "%1.0f", (double)((m_h - p) * -10));\r
- dc.DrawText(buf, XLEFT_TEXT_OFFSET, p);\r
- }\r
}\r
\r
//----------------------------------------------------------------\r
// draw()\r
//----------------------------------------------------------------\r
void PlotSpectrum::draw(wxAutoBufferedPaintDC& dc)
-{
+{\r
+/*
float x_px_per_point = 0.0;
float y_px_per_dB = 0.0;
int i;
float mag1, mag2;
char label[20];
float px_per_hz;\r
-\r
+*/\r
m_rectCtrl = GetClientRect();\r
m_rectGrid = m_rectCtrl;\r
\r
dc.Clear();\r
\r
// Draw a filled rectangle with aborder\r
- wxBrush ltBlueBrush = wxBrush(LIGHT_YELLOW_COLOR);\r
- dc.SetBrush(ltBlueBrush);\r
- dc.SetPen(wxPen(BLACK_COLOR, 1));\r
- dc.DrawRectangle(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_w, m_h);\r
+ wxBrush ltYellowBrush = wxBrush(LIGHT_YELLOW_COLOR);\r
+ dc.SetBrush(ltYellowBrush);\r
+// dc.SetBrush(BLACK_COLOR);\r
+// dc.SetPen(wxPen(BLACK_COLOR, 1));\r
+ m_top = PLOT_BORDER;\r
+ m_left = PLOT_BORDER + XLEFT_OFFSET;\r
+ dc.DrawRectangle(m_left, m_top, m_w, m_h);\r
\r
drawGraticule(dc);\r
/*
//==========================================================================\r
#include <string.h>\r
#include "wx/wx.h"\r
+#include "fdmdv2_main.h"\r
#include "fdmdv2_plot_waterfall.h"
\r
-extern float *av_mag;\r
+//extern float *av_mag;\r
+\r
/*\r
\r
Notes:\r
{
int i;
\r
- m_bmp = new wxBitmap(MAX_BMP_X, MAX_BMP_Y, wxBITMAP_SCREEN_DEPTH);
+// m_bmp = new wxBitmap(MAX_BMP_X, MAX_BMP_Y, wxBITMAP_SCREEN_DEPTH);
for(i = 0; i < 255; i++)
{
m_heatmap_lut[i] = heatmap((float)i, 0.0, 255.0);
}
m_greyscale = 0;
SetLabelSize(10.0);\r
- m_Bufsz = GetMaxClientSize();
+ m_Bufsz = GetMaxClientSize();\r
+// m_newdata = true;\r
+ m_newdata = false;\r
};
//----------------------------------------------------------------\r
//----------------------------------------------------------------\r
PlotWaterfall::~PlotWaterfall()
{
- if(!m_bmp->IsNull())\r
- {\r
- delete m_bmp;\r
- }
}
\r
/*\r
delete m_bmp;\r
//m_Bufsz = h * w;\r
\r
- //m_img = new wxImage(sz, pdata, alpha,false);\r
+ //m_img = new wxImage(sz, pdata, alpha, false);\r
m_bmp = new wxBitmap(w, h, wxBITMAP_SCREEN_DEPTH);\r
//m_pBmp = m_bmp->GetBitmapData();\r
}
\r
// Vertical gridlines\r
dc.SetPen(m_penShortDash);\r
- for(p = (PLOT_BORDER + XLEFT_OFFSET + GRID_INCREMENT); p < m_w; p += GRID_INCREMENT)\r
+ for(p = (PLOT_BORDER + XLEFT_OFFSET + GRID_INCREMENT); p < ((m_w - XLEFT_OFFSET) + GRID_INCREMENT); p += GRID_INCREMENT)\r
{\r
dc.DrawLine(p, (m_h + PLOT_BORDER), p, PLOT_BORDER);\r
}\r
m_rectGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2)));\r
m_rectGrid.Offset(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER);\r
\r
-// m_rectGrid.Deflate(PLOT_BORDER, (PLOT_BORDER + (YBOTTOM_OFFSET/2)));\r
-// m_rectGrid.Offset(PLOT_BORDER, PLOT_BORDER);\r
-\r
m_h = m_rectGrid.GetHeight();\r
m_w = m_rectGrid.GetWidth();\r
\r
\r
drawGraticule(dc);\r
//plotData(dc);\r
+ if(m_newdata)\r
+ {\r
+ m_newdata = false;\r
+ plotPixelData(dc);\r
+ }\r
}\r
\r
//----------------------------------------------------------------\r
unsigned *last_row;\r
unsigned *pdest;\r
unsigned *psrc;
+// float *av_mag = ((MainFrame *)GetParent())->m_av_mag;\r
/* detect resizing of window */
/*\r
if ((m_h != m_prev_h) || (m_w != m_prev_w))
bytes_in_row_of_blocks = dy * m_w * sizeof(unsigned);
for(b = 0; b < dy_blocks - 1; b++)
{
- pdest = m_pixel_buf + b * m_w * dy;
- psrc = m_pixel_buf + (b + 1) * m_w * dy;
+ pdest = (unsigned int *)m_pBmp + b * m_w * dy;
+ psrc = (unsigned int *)m_pBmp + (b + 1) * m_w * dy;
memcpy(pdest, psrc, bytes_in_row_of_blocks);
}
// create a new row of blocks at bottom
spec_index_per_px = (float)FDMDV_NSPEC / (float) m_w;
intensity_per_dB = (float)256 /(MAX_DB - MIN_DB);
- last_row = m_pixel_buf + dy *(dy_blocks - 1)* m_w;
+ last_row = (unsigned int *)m_pBmp + dy *(dy_blocks - 1)* m_w;
for(px = 0; px < m_w; px++)
{
index = px * spec_index_per_px;
- intensity = intensity_per_dB * (av_mag[index] - MIN_DB);
- if (intensity > 255)
+ intensity = intensity_per_dB * (m_pTopFrame->m_rxPa->m_av_mag[index] - MIN_DB);
+ //intensity = intensity_per_dB * m_pTopFrame->m_rxPa->getAvMag(index) - MIN_DB);\r
+
+ if(intensity > 255)
{
intensity = 255;
}
{
intensity = 0;
}
- if (m_greyscale)
+ if(m_greyscale)
{
for(py = 0; py < dy; py++)
{
}
}
}\r
-\r
// update bit map
//fl_draw_image((unsigned char*)m_pixel_buf, m_x, m_y, m_w, m_h, 4, 0);\r
- //dc.DrawLines(4, m_pBmp, 0, 0 );
-}
+ //dc.DrawLines(4, m_pBmp, 0, 0 );\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// plotPixelData()\r
+//-------------------------------------------------------------------------\r
+void PlotWaterfall::plotPixelData(wxAutoBufferedPaintDC& dc)\r
+{\r
+ float spec_index_per_px;\r
+ float intensity_per_dB;
+ int px_per_sec;
+ int index;\r
+ int dy;\r
+ int dy_blocks;\r
+ int bytes_in_row_of_blocks;\r
+ int b;
+ int px;\r
+ int py;\r
+ int intensity;
+ unsigned *last_row;\r
+ unsigned *pdest;\r
+ unsigned *psrc;\r
+\r
+ // determine dy, the height of one "block"
+ px_per_sec = (float)m_h / WATERFALL_SECS_Y;
+ dy = DT * px_per_sec;
+ // number of dy high blocks in spectrogram
+ dy_blocks = m_h / dy;
+ // shift previous bit map
+ bytes_in_row_of_blocks = dy * m_w * sizeof(unsigned);
+ for(b = 0; b < dy_blocks - 1; b++)
+ {
+ pdest = (unsigned int *)m_pBmp + b * m_w * dy;
+ psrc = (unsigned int *)m_pBmp + (b + 1) * m_w * dy;
+ memcpy(pdest, psrc, bytes_in_row_of_blocks);
+ }
+ // create a new row of blocks at bottom
+ spec_index_per_px = (float)FDMDV_NSPEC / (float) m_w;
+ intensity_per_dB = (float)256 /(MAX_DB - MIN_DB);
+ last_row = (unsigned int *)m_pBmp + dy *(dy_blocks - 1)* m_w;
+\r
+ wxNativePixelData data(*m_bmp);\r
+ if(!data)\r
+ {\r
+ // ... raw access to bitmap data unavailable, do something else ...\r
+ return;\r
+ }\r
+ if(data.GetWidth() < 20 || data.GetHeight() < 20)\r
+ {\r
+ // ... complain: the bitmap it too small ...\r
+ return;\r
+ }\r
+ wxNativePixelData::Iterator p(data);\r
+ // we draw a (10, 10)-(20, 20) rect manually using the given r, g, b\r
+ p.Offset(data, 10, 10);\r
+ for(px = 0; px < m_w; px++)
+ {
+ index = px * spec_index_per_px;
+ // intensity = intensity_per_dB * (m_av_mag[index] - MIN_DB);
+ intensity = intensity_per_dB * (((MainFrame *)GetParent())->m_rxPa->m_av_mag[index] - MIN_DB);
+ if(intensity > 255)
+ {
+ intensity = 255;
+ }
+ if(intensity > 255)
+ {
+ intensity = 255;
+ }
+ if (intensity < 0)
+ {
+ intensity = 0;
+ }
+ if(m_greyscale)
+ {
+ for(py = 0; py < dy; py++)
+ {
+ last_row[px + py * m_w] = intensity << 8;
+ }
+ }
+ else
+ {
+ for(py = 0; py < dy; py++)
+ {
+ last_row[px + py * m_w] = m_heatmap_lut[intensity];
+ }
+ }
+ }\r
+/*\r
+ for(int y = 0; y < 10; ++y)\r
+ {\r
+ wxNativePixelData::Iterator rowStart = p;\r
+ for(int x = 0; x < 10; ++x, ++p)\r
+ {\r
+ p.Red() = r;\r
+ p.Green() = g;\r
+ p.Blue() = b;\r
+ }\r
+ p = rowStart;\r
+ p.OffsetY(data, 1);\r
+ }\r
+*/\r
+}\r
+\r
//----------------------------------------------------------------\r
// paintEvent()\r
//\r
#include "fdmdv2_plot.h"\r
#include "fdmdv2_defines.h"\r
\r
-#define MAX_ZOOM 7\r
-#define MAX_BMP_X (400 * MAX_ZOOM)\r
-#define MAX_BMP_Y (400 * MAX_ZOOM)\r
-\r
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=\r
// Class PlotWaterfall\r
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=\r
~PlotWaterfall();\r
protected:
- unsigned *m_pixel_buf;
+// unsigned *m_pixel_buf;
unsigned m_heatmap_lut[256];
int m_greyscale;
\r
void drawGraticule(wxAutoBufferedPaintDC& dc);\r
void draw(wxAutoBufferedPaintDC& dc);\r
void plotData(wxAutoBufferedPaintDC& dc);\r
+ void plotPixelData(wxAutoBufferedPaintDC& dc);\r
DECLARE_EVENT_TABLE()\r
};\r
\r
+++ /dev/null
-//==========================================================================\r
-// Name: fdmdv2_process_audio.cpp\r
-// Purpose: Implements processing of data received from audio interfaces.\r
-// Created: August 12, 2012
-// Initial author: David Rowe\r
-// Derived from: code trivially converted for integration with C++ code
-// by Dave Witten\r
-// License:\r
-//\r
-// Copyright (C) 2012 David Witten
-//
-// All rights reserved.
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License version 2.1,
-// as published by the Free Software Foundation. This program is
-// distributed in the hope that it will be useful, but WITHOUT ANY
-// WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
-// License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with this program; if not, see <http://www.gnu.org/licenses/>.
-//
-//==========================================================================\r
-/*
-
- FUNCTION: per_frame_rx_processing()
- AUTHOR..: David Rowe
- DATE....: July 2012
-
- Called every rx frame to take a buffer of input modem samples and
- convert them to a buffer of output speech samples.
-
- The sample source could be a sound card or file. The sample source
- supplies a fixed number of samples with each call. However
- fdmdv_demod requires a variable number of samples for each call.
- This function will buffer as appropriate and call fdmdv_demod with
- the correct number of samples.
-
- The processing sequence is:
-
- collect demod input samples from sound card 1 A/D
- while we have enough samples:
- demod samples into bits
- decode bits into speech samples
- output a buffer of speech samples to sound card 2 D/A
-
- Note that sound card 1 and sound card 2 will have slightly different
- sample rates, as their sample clocks are not syncronised. We
- effectively lock the system to the demod A/D (sound card 1) sample
- rate. This ensures the demod gets a continuous sequence of samples,
- maintaining sync. Sample underflow or overflow will instead occur on
- the sound card 2 D/A. This is acceptable as a buffer of lost or
- extra speech samples is unlikely to be noticed.
-
- The situation is actually a little more complex than that. Through
- the demod timing estimation the buffers supplied to sound card D/A 2
- are effectively clocked at the remote modulator sound card D/A clock
- rate. We slip/gain buffers supplied to sound card 2 to compensate.
-
- The current demod handles varying clock rates by having a variable
- number of input samples, e.g. 120 160 (nominal) or 200. However the
- A/D always delivers a fixed number of samples.
-
- So we currently need some logic between the A/D and the demod:
- + A/D delivers fixed number of samples
- + demod processes a variable number of samples
- + this means we run demod 0,1 or 2 times, depending
- on number of buffered A/D samples
- + demod always outputs 1 frame of bits
- + so run demod and speech decoder 0, 1 or 2 times
-
- The ouput of the demod is codec voice data so it's OK if we miss or
- repeat a frame every now and again.
-
-*/
-#include "fdmdv2_main.h"\r
-#include "portaudio.h"
-
-//#include "fdmdv.h"
-#include "codec2.h"
-
-//-- Globals: Eliminate!! --------------------------------
-//char *fin_name = NULL;
-//char *fout_name = NULL;
-//char *sound_dev_name = NULL;
-//FILE *fin = NULL;
-//FILE *fout = NULL;
-struct FDMDV *fdmdv_state;
-struct CODEC2 *codec2;
-//float av_mag[FDMDV_NSPEC]; // shared between a few classes
-extern float *av_mag;\r
-//--------------------------------------------------------
-
-void new_data(float *);
-float Ts = 0.0;
-short input_buf[2*FDMDV_NOM_SAMPLES_PER_FRAME];
-int n_input_buf = 0;
-int nin = FDMDV_NOM_SAMPLES_PER_FRAME;
-short *output_buf;
-int n_output_buf = 0;
-int codec_bits[2*FDMDV_BITS_PER_FRAME];
-int state = 0;
-
-// Portaudio states -----------------------------
-
-PaStream *stream = NULL;
-PaError err;
-
-typedef struct
-{
- float in48k[FDMDV_OS_TAPS + N48];
- float in8k[MEM8 + N8];
-} paCallBackData;
-
-//----------------------------------------------------------------
-// per_frame_rx_processing()
-//----------------------------------------------------------------
-void per_frame_rx_processing(
- short output_buf[], /* output buf of decoded speech samples */
- int *n_output_buf, /* how many samples currently in output_buf[] */
- int codec_bits[], /* current frame of bits for decoder */
- short input_buf[], /* input buf of modem samples input to demod */
- int *n_input_buf, /* how many samples currently in input_buf[] */
- int *nin, /* amount of samples demod needs for next call */
- int *state, /* used to collect codec_bits[] halves */
- struct CODEC2 *c2 /* Codec 2 states */
- )
-{
- struct FDMDV_STATS stats;
- int sync_bit;
- float rx_fdm[FDMDV_MAX_SAMPLES_PER_FRAME];
- int rx_bits[FDMDV_BITS_PER_FRAME];
- unsigned char packed_bits[BYTES_PER_CODEC_FRAME];
- float rx_spec[FDMDV_NSPEC];
- int i;\r
- int nin_prev;\r
- int bit;\r
- int byte;
- int next_state;
-
- assert(*n_input_buf <= (2 * FDMDV_NOM_SAMPLES_PER_FRAME));
-
- /*
- This while loop will run the demod 0, 1 (nominal) or 2 times:
-
- 0: when tx sample clock runs faster than rx, occasionally we
- will run out of samples
-
- 1: normal, run decoder once, every 2nd frame output a frame of
- speech samples to D/A
-
- 2: when tx sample clock runs slower than rx, occasionally we will
- have enough samples to run demod twice.
-
- With a +/- 10 Hz sample clock difference at FS=8000Hz (+/- 1250
- ppm), case 0 or 1 occured about once every 30 seconds. This is
- no problem for the decoded audio.
- */
- while(*n_input_buf >= *nin)
- {
- // demod per frame processing
- for(i = 0; i < *nin; i++)
- {
- rx_fdm[i] = (float)input_buf[i]/FDMDV_SCALE;
- }
- nin_prev = *nin;
- fdmdv_demod(fdmdv_state, rx_bits, &sync_bit, rx_fdm, nin);
- *n_input_buf -= nin_prev;
- assert(*n_input_buf >= 0);
-
- // shift input buffer
- for(i=0; i<*n_input_buf; i++)
- {
- input_buf[i] = input_buf[i+nin_prev];
- }
-
- // compute rx spectrum & get demod stats, and update GUI plot data
- fdmdv_get_rx_spectrum(fdmdv_state, rx_spec, rx_fdm, nin_prev);
- fdmdv_get_demod_stats(fdmdv_state, &stats);
- new_data(rx_spec);
-// aScatter->add_new_samples(stats.rx_symbols);
-// aTimingEst->add_new_sample(stats.rx_timing);
-// aFreqEst->add_new_sample(stats.foff);
-// aSNR->add_new_sample(stats.snr_est);
- /*
- State machine to:
-
- + Mute decoded audio when out of sync. The demod is synced
- when we are using the fine freq estimate and SNR is above
- a thresh.
-
- + Decode codec bits only if we have a 0,1 sync bit
- sequence. Collects two frames of demod bits to decode
- one frame of codec bits.
- */
- next_state = *state;
- switch(*state)
- {
- case 0:
- // mute output audio when out of sync
- if(*n_output_buf < 2 * codec2_samples_per_frame(c2) - N8)
- {
- for(i=0; i<N8; i++)\r
- {
- output_buf[*n_output_buf + i] = 0;
- }
- *n_output_buf += N8;
- }
- assert(*n_output_buf <= (2 * codec2_samples_per_frame(c2)));
- if((stats.fest_coarse_fine == 1) && (stats.snr_est > 3.0))
- {
- next_state = 1;
- }
- break;
-
- case 1:
- if(sync_bit == 0)
- {
- next_state = 2;
- // first half of frame of codec bits
- memcpy(codec_bits, rx_bits, FDMDV_BITS_PER_FRAME * sizeof(int));
- }
- else
- {
- next_state = 1;
- }
- if(stats.fest_coarse_fine == 0)
- {
- next_state = 0;
- }
- break;
-
- case 2:
- next_state = 1;
- if(stats.fest_coarse_fine == 0)
- {
- next_state = 0;
- }
- if(sync_bit == 1)
- {
- // second half of frame of codec bits
- memcpy(&codec_bits[FDMDV_BITS_PER_FRAME], rx_bits, FDMDV_BITS_PER_FRAME*sizeof(int));
- // pack bits, MSB received first
- bit = 7;
- byte = 0;
- memset(packed_bits, 0, BYTES_PER_CODEC_FRAME);
- for(i = 0; i < BITS_PER_CODEC_FRAME; i++)
- {
- packed_bits[byte] |= (codec_bits[i] << bit);
- bit--;
- if(bit < 0)
- {
- bit = 7;
- byte++;
- }
- }
- assert(byte == BYTES_PER_CODEC_FRAME);
- // add decoded speech to end of output buffer
- if(*n_output_buf <= codec2_samples_per_frame(c2))
- {
- codec2_decode(c2, &output_buf[*n_output_buf], packed_bits);
- *n_output_buf += codec2_samples_per_frame(c2);
- }
- assert(*n_output_buf <= (2 * codec2_samples_per_frame(c2)));
- }
- break;
- }
- *state = next_state;
- }
-}
-
-//----------------------------------------------------------------
-// update average of each spectrum point
-//----------------------------------------------------------------
-void new_data(float mag_dB[])
-{
- int i;
-
- for(i = 0; i < FDMDV_NSPEC; i++)
- {
- av_mag[i] = (1.0 - BETA) * av_mag[i] + BETA * mag_dB[i];
- }
-}
-
-//----------------------------------------------------------------
-// Redraw windows every DT seconds.
-//----------------------------------------------------------------
-void update_gui(int nin, float *Ts)
-{
- *Ts += (float)nin/FS;
- *Ts += (float)nin/FS;
- if(*Ts >= DT)
- {
-/*
- *Ts -= DT;
- if(!zoomSpectrumWindow->shown() && !zoomWaterfallWindow->shown())
- {
- aSpectrum->redraw();
- aWaterfall->redraw();
- aScatter->redraw();
- aTimingEst->redraw();
- aFreqEst->redraw();
- aSNR->redraw();
- }
- if(zoomSpectrumWindow->shown())
- {
- aZoomedSpectrum->redraw();
- }
- if(zoomWaterfallWindow->shown())
- {
- aZoomedWaterfall->redraw();
- }
-*/
- }
-}
-
-//----------------------------------------------------------------
-// idle() is the FLTK function that gets continusouly called when FLTK
-// is not doing GUI work. We use this function for providing file
-// input to update the GUI when simulating real time operation.
-//----------------------------------------------------------------
-/*
-void idle(void*)
-{
- int ret, i;
- if(fin_name != NULL)
- {
- ret = fread(&input_buf[n_input_buf], sizeof(short), FDMDV_NOM_SAMPLES_PER_FRAME, fin);
- n_input_buf += FDMDV_NOM_SAMPLES_PER_FRAME;
- per_frame_rx_processing(output_buf, &n_output_buf, codec_bits, input_buf, &n_input_buf, &nin, &state, codec2);
- if(fout_name != NULL)
- {
- if(n_output_buf >= N8)
- {
- ret = fwrite(output_buf, sizeof(short), N8, fout);
- n_output_buf -= N8;
- assert(n_output_buf >= 0);
- // shift speech sample output buffer
- for(i=0; i<n_output_buf; i++)
- {
- output_buf[i] = output_buf[i+N8];
- }
- }
- }
- }
- update_gui(nin, &Ts);
- // simulate time delay from real world A/D input, and pause betwen
- // screen updates
- usleep(20000);
-}
-*/
-\r
-/*
-//----------------------------------------------------------------
-// This routine will be called by the PortAudio engine when audio
-// is available.
-//----------------------------------------------------------------
-static int callback(\r
- const void *inputBuffer,\r
- void *outputBuffer,
- unsigned long framesPerBuffer,
- const PaStreamCallbackTimeInfo* timeInfo,
- PaStreamCallbackFlags statusFlags,
- void *userData\r
- )
-{
- paCallBackData *cbData = (paCallBackData*)userData;
- unsigned int i;
- short *rptr = (short*)inputBuffer;
- short *wptr = (short*)outputBuffer;
- float *in8k = cbData->in8k;
- float *in48k = cbData->in48k;
- float out8k[N8];
- float out48k[N48];
- short out48k_short[N48];
-
- (void) timeInfo;
- (void) statusFlags;
-
- assert(inputBuffer != NULL);
- // 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
- for(i=0; i<N8; i++)
- {
- input_buf[n_input_buf + i] = (short)out8k[i];
- }
- n_input_buf += FDMDV_NOM_SAMPLES_PER_FRAME;
- per_frame_rx_processing(output_buf, &n_output_buf, codec_bits, input_buf, &n_input_buf, &nin, &state, codec2);
-
- // if demod out of sync copy input audio from A/D to aid in tuning
- if(n_output_buf >= N8)
- {
- if(state == 0)
- {
- for(i=0; i<N8; i++)
- {
- in8k[MEM8 + i] = out8k[i]; // A/D signal
- }
- }
- else
- {
- for(i=0; i < N8; i++)
- {
- in8k[MEM8+i] = output_buf[i]; // decoded speech
- }
- }
- n_output_buf -= N8;
- }
- assert(n_output_buf >= 0);
- // shift speech samples in output buffer
- for(i = 0; i < (unsigned int)n_output_buf; i++)
- {
- output_buf[i] = output_buf[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 both channels
- for(i = 0; i < N48; i++)
- {
- out48k_short[i] = (short)out48k[i];
- }
- for(i=0; i<framesPerBuffer; i++, wptr+=2)
- {
- wptr[0] = out48k_short[i];
- wptr[1] = out48k_short[i];
- }
- return paContinue;
-}\r
-*/
-/*
-//----------------------------------------------------------------
-// arg_callback()
-//----------------------------------------------------------------
-int arg_callback(int argc, char **argv, int &i)
-{
- if(argv[i][1] == 'i')
- {
- if((i+1) >= argc)
- {
- return 0;
- }
- fin_name = argv[i+1];
- i += 2;
- return 2;
- }
- if(argv[i][1] == 'o')
- {
- if((i+1) >= argc)
- {
- return 0;
- }
- fout_name = argv[i+1];
- i += 2;
- return 2;
- }
- if(argv[i][1] == 's')
- {
- if((i+1) >= argc)
- {
- return 0;
- }
- sound_dev_name = argv[i+1];
- i += 2;
- return 2;
- }
- return 0;
-}
-*/\r