From 4cc277a205b7d36f32919dbe092fb2ca4bbdea6c Mon Sep 17 00:00:00 2001 From: drowe67 Date: Tue, 20 Nov 2012 02:37:44 +0000 Subject: [PATCH] added record from radio feature, works OK on wave and raw files, can demod/decode aved files using command line tools git-svn-id: https://svn.code.sf.net/p/freetel/code@1032 01035d8c-6547-0410-b346-abe4f91aad63 --- fdmdv2/src/fdmdv2_main.cpp | 245 ++++++++++++++++++++++++++++--------- fdmdv2/src/fdmdv2_main.h | 18 ++- fdmdv2/src/topFrame.cpp | 12 ++ fdmdv2/src/topFrame.h | 2 + 4 files changed, 216 insertions(+), 61 deletions(-) diff --git a/fdmdv2/src/fdmdv2_main.cpp b/fdmdv2/src/fdmdv2_main.cpp index ef04159e..52bffe61 100644 --- a/fdmdv2/src/fdmdv2_main.cpp +++ b/fdmdv2/src/fdmdv2_main.cpp @@ -71,9 +71,15 @@ int g_soundCard2OutDeviceNum; int g_soundCard2SampleRate; // playing and recording from sound files -SNDFILE *g_sfFile; +SNDFILE *g_sfPlayFile; bool g_playFileToMicIn; bool g_loopPlayFileToMicIn; +int g_playFileToMicInEventId; +SNDFILE *g_sfRecFile; +bool g_recFileFromRadio; +unsigned int g_recFromRadioSamples; +int g_recFileFromRadioEventId; +wxWindow *g_parent; // Click to tune rx frequency offset states float g_RxFreqOffsetHz; @@ -83,8 +89,6 @@ COMP g_RxFreqOffsetFreqRect; // DRs debug variables, will be cleaned up eventually int cb_cnt, cb1, cb2; int mute_mic = 0; -int write_file = 0; -FILE *g_write_file; int sc1, sc2; int g_outfifo2_empty; @@ -152,6 +156,7 @@ bool MainApp::OnInit() frame->m_auiNbookCtrl->ChangeSelection(0); frame->Layout(); frame->Show(); + g_parent =frame; return true; } @@ -286,6 +291,8 @@ MainFrame::MainFrame(wxWindow *parent) : TopFrame(parent) wxGetApp().m_strRigCtrlParity = pConfig->Read("/Rig/Parity", wxT("n")); wxGetApp().m_playFileToMicInPath = pConfig->Read("/File/playFileToMicInPath", wxT("")); + wxGetApp().m_recFileFromRadioPath = pConfig->Read("/File/recFileFromRadioPath", wxT("")); + wxGetApp().m_recFileFromRadioSecs = pConfig->Read("/File/recFileFromRadioSecs", 30); pConfig->SetPath(wxT("/")); @@ -330,9 +337,12 @@ MainFrame::MainFrame(wxWindow *parent) : TopFrame(parent) Connect(wxEVT_IDLE, wxIdleEventHandler(MainFrame::OnIdle), NULL, this); #endif //_USE_TIMER - g_sfFile = NULL; + g_sfPlayFile = NULL; g_playFileToMicIn = false; g_loopPlayFileToMicIn = false; + g_sfRecFile = NULL; + g_recFileFromRadio = false; + //g_parent = parent; } //------------------------------------------------------------------------- @@ -384,6 +394,8 @@ MainFrame::~MainFrame() pConfig->Write(wxT("/Rig/Parity"), wxGetApp().m_strRigCtrlParity); pConfig->Write(wxT("/File/playFileToMicInPath"), wxGetApp().m_playFileToMicInPath); + pConfig->Write(wxT("/File/recFileFromRadioPath"), wxGetApp().m_recFileFromRadioPath); + pConfig->Write(wxT("/File/recFileFromRadioSecs"), wxGetApp().m_recFileFromRadioSecs); } m_togRxID->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnRxIDUI), NULL, this); @@ -397,10 +409,15 @@ MainFrame::~MainFrame() if (m_RxRunning) stopRxStream(); - if(g_sfFile != NULL) + if (g_sfPlayFile != NULL) { - sf_close(g_sfFile); - g_sfFile = NULL; + sf_close(g_sfPlayFile); + g_sfPlayFile = NULL; + } + if (g_sfRecFile != NULL) + { + sf_close(g_sfRecFile); + g_sfRecFile = NULL; } #ifdef _USE_TIMER if(m_plotTimer.IsRunning()) @@ -496,12 +513,7 @@ void MainFrame::OnTimer(wxTimerEvent &evt) //---------------------------------------------------------------- void MainFrame::OnIdle(wxIdleEvent& event) { - /* - if (m_RxRunning) { - //wxLogDebug("OnIdle\n"); - txRxProcessing(); - } - */ + } #endif // _USE_TIMER @@ -664,6 +676,7 @@ void MainFrame::OnTogBtnALCClick(wxCommandEvent& event) } #endif +// extra panel added to file open dialog to add loop checkbox MyExtraPlayFilePanel::MyExtraPlayFilePanel(wxWindow *parent): wxPanel(parent) { m_cb = new wxCheckBox(this, -1, wxT("Loop")); @@ -682,14 +695,17 @@ static wxWindow* createMyExtraPlayFilePanel(wxWindow *parent) } //------------------------------------------------------------------------- -// OnOpen() +// OnPlayFileToMicIn() //------------------------------------------------------------------------- void MainFrame::OnPlayFileToMicIn(wxCommandEvent& event) { + wxUnusedVar(event); + if (g_playFileToMicIn) { g_mutexProtectingCallbackData.Lock(); g_playFileToMicIn = false; - sf_close(g_sfFile); + sf_close(g_sfPlayFile); + SetStatusText(wxT("")); g_mutexProtectingCallbackData.Unlock(); } else { @@ -697,7 +713,6 @@ void MainFrame::OnPlayFileToMicIn(wxCommandEvent& event) wxString soundFile; SF_INFO sfInfo; - wxUnusedVar(event); wxFileDialog openFileDialog( this, wxT("Play File to Mic In"), @@ -719,7 +734,7 @@ void MainFrame::OnPlayFileToMicIn(wxCommandEvent& event) wxString fileName, extension; soundFile = openFileDialog.GetPath(); wxFileName::SplitPath(soundFile, &wxGetApp().m_playFileToMicInPath, &fileName, &extension); - wxLogDebug("m_playFileToMicInPath: %s", wxGetApp().m_playFileToMicInPath); + //wxLogDebug("m_playFileToMicInPath: %s", wxGetApp().m_playFileToMicInPath); sfInfo.format = 0; if(!extension.IsEmpty()) @@ -732,8 +747,8 @@ void MainFrame::OnPlayFileToMicIn(wxCommandEvent& event) sfInfo.samplerate = FS; } } - g_sfFile = sf_open(soundFile, SFM_READ, &sfInfo); - if(g_sfFile == NULL) + g_sfPlayFile = sf_open(soundFile, SFM_READ, &sfInfo); + if(g_sfPlayFile == NULL) { wxString strErr = sf_strerror(NULL); wxMessageBox(strErr, wxT("Couldn't open sound file"), wxOK); @@ -741,47 +756,132 @@ void MainFrame::OnPlayFileToMicIn(wxCommandEvent& event) } wxWindow * const ctrl = openFileDialog.GetExtraControl(); + // Huh?! I just copied wxWidgets-2.9.4/samples/dialogs .... g_loopPlayFileToMicIn = static_cast(ctrl)->getLoopPlayFileToMicIn(); - printf(" g_loopPlayFileToMicIn: %d\n", (int) g_loopPlayFileToMicIn); + SetStatusText(wxT("Playing File: ") + fileName + wxT(" to Mic Input") , 0); g_playFileToMicIn = true; } } +// extra panel added to file save dialog to set number of seconds to record for +MyExtraRecFilePanel::MyExtraRecFilePanel(wxWindow *parent): wxPanel(parent) +{ + wxBoxSizer *sizerTop = new wxBoxSizer(wxHORIZONTAL); + + wxStaticText* staticText = new wxStaticText(this, wxID_ANY, _("Seconds:"), wxDefaultPosition, wxDefaultSize, 0); + sizerTop->Add(staticText, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); + m_secondsToRecord = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_secondsToRecord->SetToolTip(_("Number of seconds to record for")); + m_secondsToRecord->SetValue(wxString::Format(wxT("%i"), wxGetApp().m_recFileFromRadioSecs)); + sizerTop->Add(m_secondsToRecord, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxALL, 5); + SetSizerAndFit(sizerTop); +} + +static wxWindow* createMyExtraRecFilePanel(wxWindow *parent) +{ + return new MyExtraRecFilePanel(parent); +} //------------------------------------------------------------------------- -// OnSave() +// OnRecFileFromRadio() //------------------------------------------------------------------------- -void MainFrame::OnSave(wxCommandEvent& WXUNUSED(event)) +void MainFrame::OnRecFileFromRadio(wxCommandEvent& event) { -/* - wxString savefilename = wxFileSelector( - wxT("Save Sound File"), - wxEmptyString, - wxEmptyString, - (const wxChar *)NULL, - wxT("WAV files (*.wav)|*.wav|") - wxT("RAW files (*.raw)|*.raw|") - wxT("SPEEX files (*.spx)|*.spx|") - wxT("FLAC files (*.flc)|*.flc|"), - wxFD_SAVE, - this - ); - if(savefilename.empty()) - { - return; + wxUnusedVar(event); + + if (g_recFileFromRadio) { + printf("Stopping Record....\n"); + g_mutexProtectingCallbackData.Lock(); + g_recFileFromRadio = false; + sf_close(g_sfRecFile); + SetStatusText(wxT("")); + g_mutexProtectingCallbackData.Unlock(); } - wxString extension; - wxFileName::SplitPath(savefilename, NULL, NULL, &extension); - bool saved = false; - if(!saved) - { - // This one guesses image format from filename extension - // (it may fail if the extension is not recognized): - //image.SaveFile(savefilename); + else { + + wxString soundFile; + SF_INFO sfInfo; + + wxFileDialog openFileDialog( + this, + wxT("Record File From Radio"), + wxGetApp().m_recFileFromRadioPath, + wxEmptyString, + wxT("WAV and RAW files (*.wav;*.raw)|*.wav;*.raw|") + wxT("All files (*.*)|*.*"), + wxFD_SAVE + ); + + // add the loop check box + openFileDialog.SetExtraControlCreator(&createMyExtraRecFilePanel); + + if(openFileDialog.ShowModal() == wxID_CANCEL) + { + return; // the user changed their mind... + } + + wxString fileName, extension; + soundFile = openFileDialog.GetPath(); + wxFileName::SplitPath(soundFile, &wxGetApp().m_recFileFromRadioPath, &fileName, &extension); + //wxLogDebug("m_recFileFromRadioPath: %s", wxGetApp().m_recFileFromRadioPath); + //wxLogDebug("sounfFile: %s", soundFile); + sfInfo.format = 0; + + if(!extension.IsEmpty()) + { + extension.LowerCase(); + if(extension == wxT("raw")) + { + sfInfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; + sfInfo.channels = 1; + sfInfo.samplerate = FS; + } + else if(extension == wxT("wav")) + { + sfInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; + sfInfo.channels = 1; + sfInfo.samplerate = FS; + } else { + wxMessageBox(wxT("Invalid file format"), wxT("Record File From Radio"), wxOK); + return; + } + } + else { + wxMessageBox(wxT("Invalid file format"), wxT("Record File From Radio"), wxOK); + return; + } + + + // work out number of samples to record + + wxWindow * const ctrl = openFileDialog.GetExtraControl(); + wxString secsString = static_cast(ctrl)->getSecondsToRecord(); + long secs; + if (secsString.ToLong(&secs)) { + wxGetApp().m_recFileFromRadioSecs = (unsigned int)secs; + //printf(" secondsToRecord: %d\n", (unsigned int)secs); + g_recFromRadioSamples = FS*(unsigned int)secs; + //printf("g_recFromRadioSamples: %d\n", g_recFromRadioSamples); + } + else { + wxMessageBox(wxT("Invalid number of Seconds"), wxT("Record File From Radio"), wxOK); + return; + } + + g_sfRecFile = sf_open(soundFile, SFM_WRITE, &sfInfo); + if(g_sfRecFile == NULL) + { + wxString strErr = sf_strerror(NULL); + wxMessageBox(strErr, wxT("Couldn't open sound file"), wxOK); + return; + } + + SetStatusText(wxT("Recording File: ") + fileName + wxT(" From Radio") , 0); + g_recFileFromRadio = true; } -*/ + } //------------------------------------------------------------------------- @@ -794,10 +894,15 @@ void MainFrame::OnClose(wxCommandEvent& event) #ifdef _USE_TIMER m_plotTimer.Stop(); #endif // _USE_TIMER - if(g_sfFile != NULL) + if(g_sfPlayFile != NULL) { - sf_close(g_sfFile); - g_sfFile = NULL; + sf_close(g_sfPlayFile); + g_sfPlayFile = NULL; + } + if(g_sfRecFile != NULL) + { + sf_close(g_sfRecFile); + g_sfRecFile = NULL; } if(m_RxRunning) { @@ -1568,11 +1673,33 @@ void txRxProcessing() while (fifo_read(cbData->infifo1, in48k_short, nsam) == 0) { g_mutexProtectingCallbackData.Unlock(); - int n8k; + unsigned int n8k; n8k = resample(cbData->insrc1, in8k_short, in48k_short, FS, g_soundCard1SampleRate, N8, nsam); fifo_write(cbData->rxinfifo, in8k_short, n8k); + // optionally save signal from radio (demod input to file). + // Really useful for testing and development as it allows to + // develop using off air signals + + g_mutexProtectingCallbackData.Lock(); + if (g_recFileFromRadio && (g_sfRecFile != NULL)) { + //printf("g_recFromRadioSamples: %d n8k: %d \n", g_recFromRadioSamples); + if (g_recFromRadioSamples < n8k) { + sf_write_short(g_sfRecFile, in8k_short, g_recFromRadioSamples); + wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, g_recFileFromRadioEventId ); + // call stop/start record menu item, should be thread safe + g_parent->GetEventHandler()->AddPendingEvent( event ); + g_recFromRadioSamples = 0; + //printf("finished recording g_recFromRadioSamples: %d n8k: %d!\n", g_recFileFromRadio, n8k); + } + else { + sf_write_short(g_sfRecFile, in8k_short, n8k); + g_recFromRadioSamples -= n8k; + } + } + g_mutexProtectingCallbackData.Unlock(); + resample_for_plot(g_plotDemodInFifo, in8k_short, n8k); per_frame_rx_processing(cbData->rxoutfifo, g_CodecBits, cbData->rxinfifo, &g_nRxIn, &g_State, g_pCodec2); @@ -1633,19 +1760,17 @@ void txRxProcessing() nout = resample(cbData->insrc2, in8k_short, in48k_short, FS, g_soundCard2SampleRate, 2*N8, nsam); - if (write_file) { - fwrite( in8k_short, sizeof(short), nout, g_write_file); - } - + // optionally use file for mic input signal g_mutexProtectingCallbackData.Lock(); - if (g_playFileToMicIn && (g_sfFile != NULL)) { - int n = sf_read_short(g_sfFile, in8k_short, 2*N8); + if (g_playFileToMicIn && (g_sfPlayFile != NULL)) { + int n = sf_read_short(g_sfPlayFile, in8k_short, 2*N8); if (n != 2*N8) { if (g_loopPlayFileToMicIn) - sf_seek(g_sfFile, 0, SEEK_SET); + sf_seek(g_sfPlayFile, 0, SEEK_SET); else { - sf_close(g_sfFile); g_sfFile = NULL; - g_playFileToMicIn = false; + wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, g_playFileToMicInEventId ); + // call stop/start play menu item, should be thread safe + g_parent->GetEventHandler()->AddPendingEvent( event ); } } } diff --git a/fdmdv2/src/fdmdv2_main.h b/fdmdv2/src/fdmdv2_main.h index 43bc2ec2..81dacbca 100644 --- a/fdmdv2/src/fdmdv2_main.h +++ b/fdmdv2/src/fdmdv2_main.h @@ -109,6 +109,8 @@ class MainApp : public wxApp wxString m_strRigCtrlParity; wxString m_playFileToMicInPath; + wxString m_recFileFromRadioPath; + unsigned int m_recFileFromRadioSecs; int m_show_wf; int m_show_spect; @@ -162,7 +164,7 @@ typedef struct int inputChannels1, inputChannels2; } paCallBackData; -// panel with custom control for play file dialog +// panel with custom loop checkbox for play file dialog class MyExtraPlayFilePanel : public wxPanel { public: @@ -173,6 +175,17 @@ private: wxCheckBox *m_cb; }; +// panel with custom Seconds-to-record control for record file dialog +class MyExtraRecFilePanel : public wxPanel +{ +public: + MyExtraRecFilePanel(wxWindow *parent); + void setSecondsToRecord(wxString value) { m_secondsToRecord->SetValue(value); } + wxString getSecondsToRecord(void) { return m_secondsToRecord->GetValue(); } +private: + wxTextCtrl *m_secondsToRecord; +}; + class txRxThread; //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= @@ -276,7 +289,10 @@ class MainFrame : public TopFrame void OnToolsOptionsUI( wxUpdateUIEvent& event ); //void OnCaptureRxStream( wxCommandEvent& event ); // void OnCaptureTxStream( wxCommandEvent& event ); + void OnPlayFileToMicIn( wxCommandEvent& event ); + void OnRecFileFromRadio( wxCommandEvent& event ); + void OnHelpCheckUpdates( wxCommandEvent& event ); void OnHelpCheckUpdatesUI( wxUpdateUIEvent& event ); void OnHelpAbout( wxCommandEvent& event ); diff --git a/fdmdv2/src/topFrame.cpp b/fdmdv2/src/topFrame.cpp index 739e8d01..c007f4dc 100644 --- a/fdmdv2/src/topFrame.cpp +++ b/fdmdv2/src/topFrame.cpp @@ -7,6 +7,9 @@ #include "topFrame.h" +extern int g_playFileToMicInEventId; +extern int g_recFileFromRadioEventId; + //========================================================================= // Code that lays out the main application window //========================================================================= @@ -92,6 +95,7 @@ TopFrame::TopFrame(wxWindow* parent, wxWindowID id, const wxString& title, const wxMenuItem* m_menuItemCaptTxInStream; m_menuItemCaptTxInStream = new wxMenuItem(tools, wxID_ANY, wxString(_("Capture Tx Input Stream")), wxEmptyString, wxITEM_NORMAL); + #ifdef __WXMSW__ m_menuItemCaptTxInStream->SetBitmaps(wxNullBitmap); #elif defined(__WXGTK__) @@ -122,8 +126,14 @@ TopFrame::TopFrame(wxWindow* parent, wxWindowID id, const wxString& title, const wxMenuItem* m_menuItemPlayFileToMicIn; m_menuItemPlayFileToMicIn = new wxMenuItem(tools, wxID_ANY, wxString(_("Start/Stop Play File to Mic In")) , wxEmptyString, wxITEM_NORMAL); + g_playFileToMicInEventId = m_menuItemPlayFileToMicIn->GetId(); tools->Append(m_menuItemPlayFileToMicIn); + wxMenuItem* m_menuItemRecFileFromRadio; + m_menuItemRecFileFromRadio = new wxMenuItem(tools, wxID_ANY, wxString(_("Start/Stop Record File from Radio")) , wxEmptyString, wxITEM_NORMAL); + g_recFileFromRadioEventId = m_menuItemRecFileFromRadio->GetId(); + tools->Append(m_menuItemRecFileFromRadio); + m_menubarMain->Append(tools, _("&Tools")); help = new wxMenu(); @@ -419,6 +429,7 @@ TopFrame::TopFrame(wxWindow* parent, wxWindowID id, const wxString& title, const #endif this->Connect(m_menuItemPlayFileToMicIn->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileToMicIn)); + this->Connect(m_menuItemRecFileFromRadio->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnRecFileFromRadio)); this->Connect(m_menuItemHelpUpdates->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpCheckUpdates)); this->Connect(m_menuItemHelpUpdates->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnHelpCheckUpdatesUI)); @@ -488,6 +499,7 @@ TopFrame::~TopFrame() #endif this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileToMicIn)); + this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnRecFileFromRadio)); this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpCheckUpdates)); this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnHelpCheckUpdatesUI)); diff --git a/fdmdv2/src/topFrame.h b/fdmdv2/src/topFrame.h index e261e5db..3e5aee0c 100644 --- a/fdmdv2/src/topFrame.h +++ b/fdmdv2/src/topFrame.h @@ -111,6 +111,8 @@ class TopFrame : public wxFrame //virtual void OnCaptureTxStream( wxCommandEvent& event ) { event.Skip(); } #endif virtual void OnPlayFileToMicIn( wxCommandEvent& event ) { event.Skip(); } + virtual void OnRecFileFromRadio( wxCommandEvent& event ) { event.Skip(); } + virtual void OnHelpCheckUpdates( wxCommandEvent& event ) { event.Skip(); } virtual void OnHelpCheckUpdatesUI( wxUpdateUIEvent& event ) { event.Skip(); } virtual void OnHelpAbout( wxCommandEvent& event ) { event.Skip(); } -- 2.25.1