diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ac988e..eb3c5b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -175,10 +175,12 @@ IF(USE_AUDIO_PULSE) ENDIF(USE_AUDIO_PULSE) IF(USE_AUDIO_JACK) - SET (OTHER_LIBRARIES ${OTHER_LIBRARIES} jack) + find_package(Jack) + SET (OTHER_LIBRARIES ${OTHER_LIBRARIES} ${JACK_LIBRARIES}) ADD_DEFINITIONS( -D__UNIX_JACK__ ) + include_directories(${JACK_INCLUDE_DIRS}) ENDIF(USE_AUDIO_JACK) IF(USE_AUDIO_ALSA) diff --git a/cmake/Modules/FindJack.cmake b/cmake/Modules/FindJack.cmake new file mode 100644 index 0000000..6d93087 --- /dev/null +++ b/cmake/Modules/FindJack.cmake @@ -0,0 +1,50 @@ +# Try to find JACK +# This will define the following variables: +# +# JACK_FOUND - Whether Jack was found. +# JACK_INCLUDE_DIRS - Jack include directories. +# JACK_LIBRARIES - Jack libraries. + +include(FindPackageHandleStandardArgs) + +if(JACK_LIBRARIES AND JACK_INCLUDE_DIRS) + + # in cache already + set(JACK_FOUND TRUE) + +else() + + find_package(PkgConfig) + if(PKG_CONFIG_FOUND) + pkg_check_modules(_JACK jack) + endif(PKG_CONFIG_FOUND) + + find_path(JACK_INCLUDE_DIR + NAMES + jack/jack.h + PATHS + ${_JACK_INCLUDEDIR} + ) + + find_library(JACK_LIBRARY + NAMES + jack + PATHS + ${_JACK_LIBDIR} + ) + + set(JACK_INCLUDE_DIRS + ${JACK_INCLUDE_DIR} + ) + + set(JACK_LIBRARIES + ${JACK_LIBRARY} + ) + + find_package_handle_standard_args(Jack DEFAULT_MSG JACK_LIBRARIES JACK_INCLUDE_DIRS) + + # show the JACK_INCLUDE_DIRS and JACK_LIBRARIES variables only in the advanced view + mark_as_advanced(JACK_INCLUDE_DIR JACK_LIBRARY JACK_INCLUDE_DIRS JACK_LIBRARIES) + +endif() + diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 309d517..d9c4f20 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -164,7 +164,6 @@ wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL) { i++; } - i = 0; for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) { wxMenuItem *itm = menu->AppendRadioItem(wxID_RT_AUDIO_DEVICE + mdevices_i->first, mdevices_i->second.name, wxT("Description?")); @@ -230,9 +229,61 @@ wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL) { p++; } - menuBar->Append(menu, wxT("&Device")); + menuBar->Append(menu, wxT("Input &Device")); } + + menu = new wxMenu; + + + #define NUM_RATES_DEFAULT 4 + int desired_rates[NUM_RATES_DEFAULT] = { 48000, 44100, 96000, 192000 }; + + for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) { + int desired_rate = 0; + int desired_rank = NUM_RATES_DEFAULT+1; + + for (std::vector::iterator srate = mdevices_i->second.sampleRates.begin(); srate != mdevices_i->second.sampleRates.end(); srate++) { + for (i = 0; i < NUM_RATES_DEFAULT; i++) { + if (desired_rates[i] == (*srate)) { + if (desired_rank > i) { + desired_rank = i; + desired_rate = (*srate); + } + } + } + } + + if (desired_rank > NUM_RATES_DEFAULT) { + desired_rate = mdevices_i->second.sampleRates.back(); + } + AudioThread::deviceSampleRate[mdevices_i->first] = desired_rate; + } + + for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) { + new wxMenu; + int menu_id = wxID_AUDIO_BANDWIDTH_BASE + wxID_AUDIO_DEVICE_MULTIPLIER * mdevices_i->first; + wxMenu *subMenu = new wxMenu; + menu->AppendSubMenu(subMenu,mdevices_i->second.name, wxT("Description?")); + + int j = 0; + for (std::vector::iterator srate = mdevices_i->second.sampleRates.begin(); srate != mdevices_i->second.sampleRates.end(); srate++) { + std::stringstream srateName; + srateName << ((float)(*srate)/1000.0f) << "kHz"; + wxMenuItem *itm = subMenu->AppendRadioItem(menu_id+j, srateName.str(), wxT("Description?")); + + if ((*srate) == AudioThread::deviceSampleRate[mdevices_i->first]) { + itm->Check(true); + } + audioSampleRateMenuItems[menu_id+j] = itm; + + j++; + } + } + + menuBar->Append(menu, wxT("Audio &Bandwidth")); + + SetMenuBar(menuBar); CreateStatusBar(); @@ -346,6 +397,32 @@ void AppFrame::OnMenu(wxCommandEvent& event) { if (event.GetId() >= wxID_DEVICE_ID && event.GetId() <= wxID_DEVICE_ID + devs->size()) { wxGetApp().setDevice(event.GetId() - wxID_DEVICE_ID); } + + + if (event.GetId() >= wxID_AUDIO_BANDWIDTH_BASE) { + int evId = event.GetId(); + std::vector::iterator devices_i; + std::map::iterator mdevices_i; + + int i = 0; + for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) { + int menu_id = wxID_AUDIO_BANDWIDTH_BASE + wxID_AUDIO_DEVICE_MULTIPLIER * mdevices_i->first; + + int j = 0; + for (std::vector::iterator srate = mdevices_i->second.sampleRates.begin(); srate != mdevices_i->second.sampleRates.end(); srate++) { + + if (evId == menu_id + j) { + //audioSampleRateMenuItems[menu_id+j]; + //std::cout << "Would set audio sample rate on device " << mdevices_i->second.name << " (" << mdevices_i->first << ") to " << (*srate) << "Hz" << std::endl; + AudioThread::setDeviceSampleRate(mdevices_i->first, *srate); + } + + j++; + } + i++; + } + } + } void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) { diff --git a/src/AppFrame.h b/src/AppFrame.h index 93dad38..1863dc2 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -33,6 +33,10 @@ #define wxID_DEVICE_ID 3500 +#define wxID_AUDIO_BANDWIDTH_BASE 9000 +#define wxID_AUDIO_DEVICE_MULTIPLIER 50 + + // Define a new frame type class AppFrame: public wxFrame { @@ -60,17 +64,15 @@ private: MeterCanvas *demodSignalMeter; MeterCanvas *demodGainMeter; TuningCanvas *demodTuner; -// event table DemodulatorInstance *activeDemodulator; std::vector devices; std::map inputDevices; std::map outputDevices; - std::map outputDeviceMenuItems; - - std::map sampleRateMenuItems; - + std::map outputDeviceMenuItems; + std::map sampleRateMenuItems; + std::map audioSampleRateMenuItems; std::string currentSessionFile; diff --git a/src/CubicSDR.xpm b/src/CubicSDR.xpm index 3b6f061..a283fb5 100644 --- a/src/CubicSDR.xpm +++ b/src/CubicSDR.xpm @@ -1,5 +1,5 @@ /* XPM */ -static char *cubicsdr_xpm[] = { +static char const *cubicsdr_xpm[] = { /* columns rows colors chars-per-pixel */ "256 256 256 2 ", " c #010101", diff --git a/src/CubicSDRDefs.h b/src/CubicSDRDefs.h index 8b09aa1..de171dc 100644 --- a/src/CubicSDRDefs.h +++ b/src/CubicSDRDefs.h @@ -42,7 +42,6 @@ const char filePathSeparator = #define DEFAULT_FFT_SIZE 2048 #define DEFAULT_FREQ 100000000 -#define AUDIO_FREQUENCY 44100 #define DEFAULT_DEMOD_TYPE 1 #define DEFAULT_DEMOD_BW 200000 diff --git a/src/audio/AudioThread.cpp b/src/audio/AudioThread.cpp index dab66f3..b173123 100644 --- a/src/audio/AudioThread.cpp +++ b/src/audio/AudioThread.cpp @@ -2,29 +2,25 @@ #include "CubicSDRDefs.h" #include #include +#include "CubicSDR.h" #include "DemodulatorThread.h" +#include "DemodulatorInstance.h" #include -#ifdef USE_MIXER std::map AudioThread::deviceController; +std::map AudioThread::deviceSampleRate; std::map AudioThread::deviceThread; -#endif AudioThread::AudioThread(AudioThreadInputQueue *inputQueue, DemodulatorThreadCommandQueue* threadQueueNotify) : currentInput(NULL), inputQueue(inputQueue), audioQueuePtr(0), underflowCount(0), terminated(false), active(false), outputDevice(-1), gain( - 1.0), threadQueueNotify(threadQueueNotify) { -#ifdef USE_MIXER + 1.0), threadQueueNotify(threadQueueNotify), sampleRate(0), nBufferFrames(1024) { boundThreads = new std::vector; -#endif } AudioThread::~AudioThread() { -#ifdef USE_MIXER delete boundThreads.load(); -#endif } -#ifdef USE_MIXER void AudioThread::bindThread(AudioThread *other) { if (std::find(boundThreads.load()->begin(), boundThreads.load()->end(), other) == boundThreads.load()->end()) { boundThreads.load()->push_back(other); @@ -74,33 +70,53 @@ static int audioCallback(void *outputBuffer, void *inputBuffer, unsigned int nBu } if (!srcmix->currentInput) { - if (srcmix->terminated) { + srcmix->audioQueuePtr = 0; + if (srcmix->terminated || srcmix->inputQueue->empty()) { continue; } srcmix->inputQueue->pop(srcmix->currentInput); if (srcmix->terminated) { continue; } - srcmix->audioQueuePtr = 0; continue; } - std::lock_guard < std::mutex > lock(srcmix->currentInput->m_mutex); +// std::lock_guard < std::mutex > lock(srcmix->currentInput->m_mutex); + + if (srcmix->currentInput->sampleRate != src->getSampleRate()) { + while (srcmix->inputQueue->size()) { + srcmix->inputQueue->pop(srcmix->currentInput); + if (srcmix->currentInput) { + if (srcmix->currentInput->sampleRate == src->getSampleRate()) { + break; + } + srcmix->currentInput->decRefCount(); + } + srcmix->currentInput = NULL; + } + + srcmix->audioQueuePtr = 0; + + if (!srcmix->currentInput) { + continue; + } + } + if (srcmix->currentInput->channels == 0 || !srcmix->currentInput->data.size()) { if (!srcmix->inputQueue->empty()) { + srcmix->audioQueuePtr = 0; if (srcmix->currentInput) { srcmix->currentInput->decRefCount(); srcmix->currentInput = NULL; } - if (srcmix->terminated) { + if (srcmix->terminated || srcmix->inputQueue->empty()) { continue; } srcmix->inputQueue->pop(srcmix->currentInput); if (srcmix->terminated) { continue; } - srcmix->audioQueuePtr = 0; } continue; } @@ -110,18 +126,18 @@ static int audioCallback(void *outputBuffer, void *inputBuffer, unsigned int nBu if (srcmix->currentInput->channels == 1) { for (int i = 0; i < nBufferFrames; i++) { if (srcmix->audioQueuePtr >= srcmix->currentInput->data.size()) { + srcmix->audioQueuePtr = 0; if (srcmix->currentInput) { srcmix->currentInput->decRefCount(); srcmix->currentInput = NULL; } - if (srcmix->terminated) { - continue; + if (srcmix->terminated || srcmix->inputQueue->empty()) { + break; } srcmix->inputQueue->pop(srcmix->currentInput); if (srcmix->terminated) { - continue; + break; } - srcmix->audioQueuePtr = 0; float srcPeak = srcmix->currentInput->peak * srcmix->gain; if (mixPeak < srcPeak) { mixPeak = srcPeak; @@ -137,18 +153,18 @@ static int audioCallback(void *outputBuffer, void *inputBuffer, unsigned int nBu } else { for (int i = 0, iMax = srcmix->currentInput->channels * nBufferFrames; i < iMax; i++) { if (srcmix->audioQueuePtr >= srcmix->currentInput->data.size()) { + srcmix->audioQueuePtr = 0; if (srcmix->currentInput) { srcmix->currentInput->decRefCount(); srcmix->currentInput = NULL; } - if (srcmix->terminated) { - continue; + if (srcmix->terminated || srcmix->inputQueue->empty()) { + break; } srcmix->inputQueue->pop(srcmix->currentInput); if (srcmix->terminated) { - continue; + break; } - srcmix->audioQueuePtr = 0; float srcPeak = srcmix->currentInput->peak * srcmix->gain; if (mixPeak < srcPeak) { mixPeak = srcPeak; @@ -172,112 +188,6 @@ static int audioCallback(void *outputBuffer, void *inputBuffer, unsigned int nBu return 0; } -#else - -static int audioCallback(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, - void *userData) { - AudioThread *src = (AudioThread *) userData; - float *out = (float*) outputBuffer; - memset(out, 0, nBufferFrames * 2 * sizeof(float)); - if (status) { - std::cout << "Audio buffer underflow.." << (src->underflowCount++) << std::endl; - } - - if (src->terminated || !src->active) { - if (src->currentInput) { - src->currentInput->decRefCount(); - src->currentInput = NULL; - } - return 1; - } - - if (!src->currentInput) { - if (src->inputQueue->empty()) { - return 0; - } - src->inputQueue->pop(src->currentInput); - if (src->terminated || !src->active) { - src->currentInput->decRefCount(); - src->currentInput = NULL; - return 1; - } - src->audioQueuePtr = 0; - return 0; - } - - std::lock_guard < std::mutex > lock(src->currentInput->m_mutex); - - if (src->currentInput->channels == 0 || !src->currentInput->data.size()) { - if (!src->inputQueue->empty()) { - if (src->currentInput) { - src->currentInput->decRefCount(); - src->currentInput = NULL; - } - if (src->terminated || !src->active) { - return 1; - } - src->inputQueue->pop(src->currentInput); - if (src->terminated || !src->active) { - src->currentInput->decRefCount(); - src->currentInput = NULL; - return 1; - } - src->audioQueuePtr = 0; - } - return 0; - } - - if (src->currentInput->channels == 1) { - for (int i = 0; i < nBufferFrames; i++) { - if (src->audioQueuePtr >= src->currentInput->data.size()) { - if (src->currentInput) { - src->currentInput->decRefCount(); - src->currentInput = NULL; - } - if (src->terminated || !src->active) { - return 1; - } - src->inputQueue->pop(src->currentInput); - if (src->terminated || !src->active) { - src->currentInput->decRefCount(); - src->currentInput = NULL; - return 1; - } - src->audioQueuePtr = 0; - } - if (src->currentInput && src->currentInput->data.size()) { - out[i * 2] = out[i * 2 + 1] = src->currentInput->data[src->audioQueuePtr] * src->gain; - } - src->audioQueuePtr++; - } - } else { - for (int i = 0, iMax = src->currentInput->channels * nBufferFrames; i < iMax; i++) { - if (src->audioQueuePtr >= src->currentInput->data.size()) { - if (src->currentInput) { - src->currentInput->decRefCount(); - src->currentInput = NULL; - } - if (src->terminated || !src->active) { - return 1; - } - src->inputQueue->pop(src->currentInput); - if (src->terminated || !src->active) { - src->currentInput->decRefCount(); - src->currentInput = NULL; - return 1; - } - src->audioQueuePtr = 0; - } - if (src->currentInput && src->currentInput->data.size()) { - out[i] = src->currentInput->data[src->audioQueuePtr] * src->gain; - } - src->audioQueuePtr++; - } - } - return 0; -} -#endif - void AudioThread::enumerateDevices(std::vector &devs) { RtAudio endac; @@ -330,19 +240,57 @@ void AudioThread::enumerateDevices(std::vector &devs) { } } +void AudioThread::setDeviceSampleRate(int deviceId, int sampleRate) { + if (deviceController.find(deviceId) != deviceController.end()) { + AudioThreadCommand refreshDevice; + refreshDevice.cmd = AudioThreadCommand::AUDIO_THREAD_CMD_SET_SAMPLE_RATE; + refreshDevice.int_value = sampleRate; + deviceController[deviceId]->getCommandQueue()->push(refreshDevice); + } +} + +void AudioThread::setSampleRate(int sampleRate) { + if (deviceController[outputDevice.load()] == this) { + deviceSampleRate[outputDevice.load()] = sampleRate; + + dac.stopStream(); + dac.closeStream(); + + for (int j = 0; j < boundThreads.load()->size(); j++) { + AudioThread *srcmix = (*(boundThreads.load()))[j]; + srcmix->setSampleRate(sampleRate); + } + + std::vector::iterator demod_i; + std::vector *demodulators; + + demodulators = &wxGetApp().getDemodMgr().getDemodulators(); + + for (demod_i = demodulators->begin(); demod_i != demodulators->end(); demod_i++) { + if ((*demod_i)->getOutputDevice() == outputDevice.load()) { + (*demod_i)->setAudioSampleRate(sampleRate); + } + } + + dac.openStream(¶meters, NULL, RTAUDIO_FLOAT32, sampleRate, &nBufferFrames, &audioCallback, (void *) this, &opts); + dac.startStream(); + } + + this->sampleRate = sampleRate; +} + +int AudioThread::getSampleRate() { + return this->sampleRate; +} + void AudioThread::setupDevice(int deviceId) { parameters.deviceId = deviceId; parameters.nChannels = 2; parameters.firstChannel = 0; - unsigned int sampleRate = AUDIO_FREQUENCY; - unsigned int bufferFrames = 256; - RtAudio::StreamOptions opts; opts.streamName = "CubicSDR Audio Output"; try { - -#ifdef USE_MIXER if (deviceController.find(outputDevice.load()) != deviceController.end()) { deviceController[outputDevice.load()]->removeThread(this); } @@ -352,43 +300,30 @@ void AudioThread::setupDevice(int deviceId) { // opts.flags = RTAUDIO_MINIMIZE_LATENCY; opts.flags = RTAUDIO_SCHEDULE_REALTIME; + if (deviceSampleRate.find(parameters.deviceId) != deviceSampleRate.end()) { + sampleRate = deviceSampleRate[parameters.deviceId]; + } else { + std::cout << "Error, device sample rate wasn't initialized?" << std::endl; + return; +// sampleRate = AudioThread::getDefaultAudioSampleRate(); +// deviceSampleRate[parameters.deviceId] = sampleRate; + } + if (deviceController.find(parameters.deviceId) == deviceController.end()) { deviceController[parameters.deviceId] = new AudioThread(NULL, NULL); - deviceController[parameters.deviceId]->setInitOutputDevice(parameters.deviceId); + + deviceController[parameters.deviceId]->setInitOutputDevice(parameters.deviceId, sampleRate); deviceController[parameters.deviceId]->bindThread(this); + deviceThread[parameters.deviceId] = new std::thread(&AudioThread::threadMain, deviceController[parameters.deviceId]); } else if (deviceController[parameters.deviceId] == this) { - dac.openStream(¶meters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &audioCallback, (void *) this, &opts); + dac.openStream(¶meters, NULL, RTAUDIO_FLOAT32, sampleRate, &nBufferFrames, &audioCallback, (void *) this, &opts); dac.startStream(); } else { deviceController[parameters.deviceId]->bindThread(this); } active = true; -#else - if (dac.isStreamOpen()) { - if (dac.isStreamRunning()) { - dac.stopStream(); - } - dac.closeStream(); - } - - if (deviceId != -1) { - active = true; - dac.openStream(¶meters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &audioCallback, (void *) this, &opts); - dac.startStream(); - } else { - active = false; - AudioThreadInput *dummy; - while (!inputQueue->empty()) { // flush queue - inputQueue->pop(dummy); - if (dummy) { - dummy->decRefCount(); - } - } - } - -#endif } catch (RtAudioError& e) { e.printMessage(); return; @@ -405,8 +340,16 @@ int AudioThread::getOutputDevice() { return outputDevice; } -void AudioThread::setInitOutputDevice(int deviceId) { +void AudioThread::setInitOutputDevice(int deviceId, int sampleRate) { outputDevice = deviceId; + if (sampleRate == -1) { + if (deviceSampleRate.find(parameters.deviceId) != deviceSampleRate.end()) { + sampleRate = deviceSampleRate[deviceId]; + } + } else { + deviceSampleRate[deviceId] = sampleRate; + } + this->sampleRate = sampleRate; } void AudioThread::threadMain() { @@ -437,14 +380,11 @@ void AudioThread::threadMain() { if (command.cmd == AudioThreadCommand::AUDIO_THREAD_CMD_SET_DEVICE) { setupDevice(command.int_value); } + if (command.cmd == AudioThreadCommand::AUDIO_THREAD_CMD_SET_SAMPLE_RATE) { + setSampleRate(command.int_value); + } } -#if !USE_MIXER - AudioThreadInput dummy; - inputQueue->push(&dummy); -#endif - -#ifdef USE_MIXER if (deviceController[parameters.deviceId] != this) { deviceController[parameters.deviceId]->removeThread(this); } else { @@ -459,18 +399,6 @@ void AudioThread::threadMain() { e.printMessage(); } } -#else - try { - if (dac.isStreamOpen()) { - if (dac.isStreamRunning()) { - dac.stopStream(); - } - dac.closeStream(); - } - } catch (RtAudioError& e) { - e.printMessage(); - } -#endif if (threadQueueNotify != NULL) { DemodulatorThreadCommand tCmd(DemodulatorThreadCommand::DEMOD_THREAD_CMD_AUDIO_TERMINATED); @@ -492,7 +420,6 @@ bool AudioThread::isActive() { void AudioThread::setActive(bool state) { -#ifdef USE_MIXER AudioThreadInput *dummy; if (state && !active) { while (!inputQueue->empty()) { // flush queue @@ -512,22 +439,6 @@ void AudioThread::setActive(bool state) { } } active = state; -#else - if (state && !active && outputDevice != -1) { - active = state; - AudioThreadCommand command; - command.cmd = AudioThreadCommand::AUDIO_THREAD_CMD_SET_DEVICE; - command.int_value = outputDevice; - cmdQueue.push(command); - } else if (active && !state) { - active = state; - AudioThreadCommand command; - command.cmd = AudioThreadCommand::AUDIO_THREAD_CMD_SET_DEVICE; - command.int_value = -1; - cmdQueue.push(command); - } - -#endif } AudioThreadCommandQueue *AudioThread::getCommandQueue() { diff --git a/src/audio/AudioThread.h b/src/audio/AudioThread.h index a12726b..0d10f97 100644 --- a/src/audio/AudioThread.h +++ b/src/audio/AudioThread.h @@ -11,18 +11,6 @@ #include "RtAudio.h" #include "DemodDefs.h" -#ifdef __APPLE__ -#define USE_MIXER 1 -#endif - -#ifdef __linux__ -#define USE_MIXER 1 -#endif - -#ifdef __WINDOWS_DS__ -#define USE_MIXER 1 -#endif - class AudioThreadInput: public ReferenceCounter { public: long long frequency; @@ -44,7 +32,7 @@ public: class AudioThreadCommand { public: enum AudioThreadCommandEnum { - AUDIO_THREAD_CMD_NULL, AUDIO_THREAD_CMD_SET_DEVICE + AUDIO_THREAD_CMD_NULL, AUDIO_THREAD_CMD_SET_DEVICE, AUDIO_THREAD_CMD_SET_SAMPLE_RATE }; AudioThreadCommand() : @@ -77,8 +65,10 @@ public: static void enumerateDevices(std::vector &devs); void setupDevice(int deviceId); - void setInitOutputDevice(int deviceId); + void setInitOutputDevice(int deviceId, int sampleRate=-1); int getOutputDevice(); + void setSampleRate(int sampleRate); + int getSampleRate(); void threadMain(); void terminate(); @@ -92,19 +82,22 @@ public: private: RtAudio dac; + unsigned int nBufferFrames; + RtAudio::StreamOptions opts; RtAudio::StreamParameters parameters; AudioThreadCommandQueue cmdQueue; DemodulatorThreadCommandQueue* threadQueueNotify; + int sampleRate; -#ifdef USE_MIXER public: void bindThread(AudioThread *other); void removeThread(AudioThread *other); static std::map deviceController; + static std::map deviceSampleRate; static std::map deviceThread; static void deviceCleanup(); + static void setDeviceSampleRate(int deviceId, int sampleRate); std::atomic *> boundThreads; -#endif }; diff --git a/src/demod/DemodDefs.h b/src/demod/DemodDefs.h index f68686c..6282ac0 100644 --- a/src/demod/DemodDefs.h +++ b/src/demod/DemodDefs.h @@ -21,6 +21,7 @@ public: DEMOD_THREAD_CMD_NULL, DEMOD_THREAD_CMD_SET_BANDWIDTH, DEMOD_THREAD_CMD_SET_FREQUENCY, + DEMOD_THREAD_CMD_SET_AUDIO_RATE, DEMOD_THREAD_CMD_DEMOD_PREPROCESS_TERMINATED, DEMOD_THREAD_CMD_DEMOD_TERMINATED, DEMOD_THREAD_CMD_AUDIO_TERMINATED @@ -78,9 +79,13 @@ public: msresamp_rrrf audioResampler; msresamp_rrrf stereoResampler; double audioResampleRatio; + int audioSampleRate; + + firfilt_rrrf firStereoLeft; + firfilt_rrrf firStereoRight; DemodulatorThreadPostIQData() : - sampleRate(0), audioResampler(NULL), stereoResampler(NULL), audioResampleRatio(0) { + sampleRate(0), audioResampler(NULL), stereoResampler(NULL), audioResampleRatio(0), audioSampleRate(0), firStereoLeft(NULL), firStereoRight(NULL) { } @@ -127,8 +132,8 @@ public: int demodType; DemodulatorThreadParameters() : - frequency(0), sampleRate(DEFAULT_SAMPLE_RATE), bandwidth(200000), audioSampleRate( - AUDIO_FREQUENCY), demodType(DEMOD_TYPE_FM) { + frequency(0), sampleRate(DEFAULT_SAMPLE_RATE), bandwidth(200000), audioSampleRate(0), + demodType(DEMOD_TYPE_FM) { } diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp index eba50f3..12a27fb 100644 --- a/src/demod/DemodulatorInstance.cpp +++ b/src/demod/DemodulatorInstance.cpp @@ -50,6 +50,8 @@ void DemodulatorInstance::run() { currentFrequency = demodulatorPreThread->getParams().frequency; currentDemodType = demodulatorThread->getDemodulatorType(); + currentAudioSampleRate = AudioThread::deviceSampleRate[getOutputDevice()]; + demodulatorPreThread->getParams().audioSampleRate = currentAudioSampleRate; t_Audio = new std::thread(&AudioThread::threadMain, audioThread); @@ -215,6 +217,8 @@ void DemodulatorInstance::setOutputDevice(int device_id) { if (!active) { audioThread->setInitOutputDevice(device_id); } else if (audioThread) { + setAudioSampleRate(AudioThread::deviceSampleRate[device_id]); + AudioThreadCommand command; command.cmd = AudioThreadCommand::AUDIO_THREAD_CMD_SET_DEVICE; command.int_value = device_id; @@ -225,10 +229,10 @@ void DemodulatorInstance::setOutputDevice(int device_id) { int DemodulatorInstance::getOutputDevice() { if (currentOutputDevice == -1) { - return audioThread->getOutputDevice(); - } else { - return currentOutputDevice; + currentOutputDevice = audioThread->getOutputDevice(); } + + return currentOutputDevice; } void DemodulatorInstance::checkBandwidth() { @@ -302,6 +306,28 @@ long long DemodulatorInstance::getFrequency() { return currentFrequency; } + +void DemodulatorInstance::setAudioSampleRate(int sampleRate) { + if (terminated) { + currentAudioSampleRate = sampleRate; + demodulatorPreThread->getParams().audioSampleRate = sampleRate; + } else if (demodulatorPreThread && threadQueueCommand) { + DemodulatorThreadCommand command; + command.cmd = DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_AUDIO_RATE; + currentAudioSampleRate = sampleRate; + command.llong_value = sampleRate; + threadQueueCommand->push(command); + } +} + +int DemodulatorInstance::getAudioSampleRate() { + if (!currentAudioSampleRate) { + currentAudioSampleRate = audioThread->getSampleRate(); + } + return currentAudioSampleRate; +} + + void DemodulatorInstance::setGain(float gain_in) { audioThread->setGain(gain_in); } diff --git a/src/demod/DemodulatorInstance.h b/src/demod/DemodulatorInstance.h index ee1eb42..15743b6 100644 --- a/src/demod/DemodulatorInstance.h +++ b/src/demod/DemodulatorInstance.h @@ -73,6 +73,11 @@ public: void setFrequency(long long freq); long long getFrequency(); + + void setAudioSampleRate(int sampleRate); + int getAudioSampleRate(); + + private: void checkBandwidth(); @@ -90,4 +95,5 @@ private: int currentBandwidth; int currentDemodType; int currentOutputDevice; + int currentAudioSampleRate; }; diff --git a/src/demod/DemodulatorPreThread.cpp b/src/demod/DemodulatorPreThread.cpp index 1d85c4a..3d5345d 100644 --- a/src/demod/DemodulatorPreThread.cpp +++ b/src/demod/DemodulatorPreThread.cpp @@ -11,7 +11,7 @@ DemodulatorPreThread::DemodulatorPreThread(DemodulatorThreadInputQueue* iqInputQueue, DemodulatorThreadPostInputQueue* iqOutputQueue, DemodulatorThreadControlCommandQueue *threadQueueControl, DemodulatorThreadCommandQueue* threadQueueNotify) : iqInputQueue(iqInputQueue), iqOutputQueue(iqOutputQueue), terminated(false), initialized(false), audioResampler(NULL), stereoResampler(NULL), iqResampleRatio( - 1), audioResampleRatio(1), iqResampler(NULL), commandQueue(NULL), threadQueueNotify(threadQueueNotify), threadQueueControl( + 1), audioResampleRatio(1), firStereoRight(NULL), firStereoLeft(NULL), iqResampler(NULL), commandQueue(NULL), threadQueueNotify(threadQueueNotify), threadQueueControl( threadQueueControl) { freqShifter = nco_crcf_create(LIQUID_VCO); @@ -36,8 +36,27 @@ void DemodulatorPreThread::initialize() { audioResampler = msresamp_rrrf_create(audioResampleRatio, As); stereoResampler = msresamp_rrrf_create(audioResampleRatio, As); + // Stereo filters / shifters + double firStereoCutoff = 0.5 * ((double) 36000 / (double) params.audioSampleRate); // filter cutoff frequency + float ft = 0.05f; // filter transition + float mu = 0.0f; // fractional timing offset + + if (firStereoCutoff < 0) { + firStereoCutoff = 0; + } + + if (firStereoCutoff > 0.5) { + firStereoCutoff = 0.5; + } + + unsigned int h_len = estimate_req_filter_len(ft, As); + float *h = new float[h_len]; + liquid_firdes_kaiser(h_len, firStereoCutoff, As, mu, h); + + firStereoLeft = firfilt_rrrf_create(h, h_len); + firStereoRight = firfilt_rrrf_create(h, h_len); + initialized = true; -// std::cout << "inputResampleRate " << params.bandwidth << std::endl; lastParams = params; } @@ -100,7 +119,11 @@ void DemodulatorPreThread::threadMain() { bandwidthChanged = true; break; case DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_FREQUENCY: - params.frequency = command.llong_value; + params.frequency = tempParams.frequency = command.llong_value; + break; + case DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_AUDIO_RATE: + tempParams.audioSampleRate = (int)command.llong_value; + rateChanged = true; break; default: break; @@ -228,7 +251,10 @@ void DemodulatorPreThread::threadMain() { resamp->audioResampleRatio = audioResampleRatio; resamp->audioResampler = audioResampler; + resamp->audioSampleRate = params.audioSampleRate; resamp->stereoResampler = stereoResampler; + resamp->firStereoLeft = firStereoLeft; + resamp->firStereoRight = firStereoRight; resamp->sampleRate = params.bandwidth; iqOutputQueue->push(resamp); @@ -245,23 +271,34 @@ void DemodulatorPreThread::threadMain() { case DemodulatorWorkerThreadResult::DEMOD_WORKER_THREAD_RESULT_FILTERS: msresamp_crcf_destroy(iqResampler); - if (result.iqResampler) { iqResampler = result.iqResampler; iqResampleRatio = result.iqResampleRatio; } + if (result.firStereoLeft) { + firStereoLeft = result.firStereoLeft; + } + + if (result.firStereoRight) { + firStereoRight = result.firStereoRight; + } + if (result.audioResampler) { audioResampler = result.audioResampler; audioResampleRatio = result.audioResamplerRatio; stereoResampler = result.stereoResampler; + } + + if (result.audioSampleRate) { params.audioSampleRate = result.audioSampleRate; } - if (params.bandwidth) { + if (result.bandwidth) { params.bandwidth = result.bandwidth; } - if (params.sampleRate) { + + if (result.sampleRate) { params.sampleRate = result.sampleRate; } break; diff --git a/src/demod/DemodulatorPreThread.h b/src/demod/DemodulatorPreThread.h index e5d8467..dde1239 100644 --- a/src/demod/DemodulatorPreThread.h +++ b/src/demod/DemodulatorPreThread.h @@ -58,6 +58,9 @@ protected: msresamp_rrrf stereoResampler; double audioResampleRatio; + firfilt_rrrf firStereoLeft; + firfilt_rrrf firStereoRight; + DemodulatorThreadParameters params; DemodulatorThreadParameters lastParams; diff --git a/src/demod/DemodulatorThread.cpp b/src/demod/DemodulatorThread.cpp index 169137f..a14f8e2 100644 --- a/src/demod/DemodulatorThread.cpp +++ b/src/demod/DemodulatorThread.cpp @@ -16,7 +16,7 @@ DemodulatorThread::DemodulatorThread(DemodulatorThreadPostInputQueue* iqInputQue iqInputQueue(iqInputQueue), audioVisOutputQueue(NULL), audioOutputQueue(NULL), iqAutoGain(NULL), amOutputCeil(1), amOutputCeilMA(1), amOutputCeilMAA( 1), stereo(false), terminated( false), demodulatorType(DEMOD_TYPE_FM), threadQueueNotify(threadQueueNotify), threadQueueControl(threadQueueControl), squelchLevel(0), signalLevel( - 0), squelchEnabled(false) { + 0), squelchEnabled(false), audioSampleRate(0) { demodFM = freqdem_create(0.5); demodAM_USB = ampmodem_create(0.5, 0.0, LIQUID_AMPMODEM_USB, 1); @@ -46,29 +46,6 @@ void DemodulatorThread::threadMain() { firfilt_rrrf firStereoLeft = NULL; firfilt_rrrf firStereoRight = NULL; - // Stereo filters / shifters - double firStereoCutoff = 0.5 * ((double) 36000 / (double) AUDIO_FREQUENCY); // filter cutoff frequency - float ft = 0.05f; // filter transition - float As = 120.0f; // stop-band attenuation [dB] - float mu = 0.0f; // fractional timing offset - - if (firStereoCutoff < 0) { - firStereoCutoff = 0; - } - - if (firStereoCutoff > 0.5) { - firStereoCutoff = 0.5; - } - - unsigned int h_len = estimate_req_filter_len(ft, As); - float *h = new float[h_len]; - liquid_firdes_kaiser(h_len, firStereoCutoff, As, mu, h); - - firStereoLeft = firfilt_rrrf_create(h, h_len); - firStereoRight = firfilt_rrrf_create(h, h_len); - - delete h; - liquid_float_complex x, y, z[2]; float rz[2]; @@ -124,11 +101,15 @@ void DemodulatorThread::threadMain() { if (audioResampler == NULL) { audioResampler = inp->audioResampler; stereoResampler = inp->stereoResampler; + firStereoLeft = inp->firStereoLeft; + firStereoRight = inp->firStereoRight; + audioSampleRate = inp->audioSampleRate; } else if (audioResampler != inp->audioResampler) { msresamp_rrrf_destroy(audioResampler); msresamp_rrrf_destroy(stereoResampler); audioResampler = inp->audioResampler; stereoResampler = inp->stereoResampler; + audioSampleRate = inp->audioSampleRate; if (demodAM) { ampmodem_reset(demodAM); @@ -136,6 +117,20 @@ void DemodulatorThread::threadMain() { freqdem_reset(demodFM); } + if (firStereoLeft != inp->firStereoLeft) { + if (firStereoLeft != NULL) { + firfilt_rrrf_destroy(firStereoLeft); + } + firStereoLeft = inp->firStereoLeft; + } + + if (firStereoRight != inp->firStereoRight) { + if (firStereoRight != NULL) { + firfilt_rrrf_destroy(firStereoRight); + } + firStereoRight = inp->firStereoRight; + } + if (agcData.size() != bufSize) { if (agcData.capacity() < bufSize) { agcData.reserve(bufSize); @@ -227,7 +222,7 @@ void DemodulatorThread::threadMain() { demodStereoData.resize(bufSize); } - double freq = (2.0 * M_PI) * (((double) abs(38000)) / ((double) inp->sampleRate)); + double freq = (2.0 * M_PI) * ((double) 38000) / ((double) inp->sampleRate); if (stereoShiftFrequency != freq) { nco_crcf_set_frequency(stereoShifter, freq); @@ -274,6 +269,7 @@ void DemodulatorThread::threadMain() { outputBuffers.push_back(ati); } + ati->sampleRate = audioSampleRate; ati->setRefCount(1); if (stereo) { diff --git a/src/demod/DemodulatorThread.h b/src/demod/DemodulatorThread.h index 01bd5eb..f9d013b 100644 --- a/src/demod/DemodulatorThread.h +++ b/src/demod/DemodulatorThread.h @@ -75,6 +75,7 @@ protected: std::atomic stereo; std::atomic terminated; std::atomic demodulatorType; + int audioSampleRate; DemodulatorThreadCommandQueue* threadQueueNotify; DemodulatorThreadControlCommandQueue *threadQueueControl; diff --git a/src/demod/DemodulatorWorkerThread.cpp b/src/demod/DemodulatorWorkerThread.cpp index cddd3ed..99199d1 100644 --- a/src/demod/DemodulatorWorkerThread.cpp +++ b/src/demod/DemodulatorWorkerThread.cpp @@ -49,6 +49,26 @@ void DemodulatorWorkerThread::threadMain() { result.audioResampler = msresamp_rrrf_create(result.audioResamplerRatio, As); result.stereoResampler = msresamp_rrrf_create(result.audioResamplerRatio, As); result.audioSampleRate = filterCommand.audioSampleRate; + + // Stereo filters / shifters + double firStereoCutoff = 0.5 * ((double) 36000 / (double) filterCommand.audioSampleRate); // filter cutoff frequency + float ft = 0.05f; // filter transition + float mu = 0.0f; // fractional timing offset + + if (firStereoCutoff < 0) { + firStereoCutoff = 0; + } + + if (firStereoCutoff > 0.5) { + firStereoCutoff = 0.5; + } + + unsigned int h_len = estimate_req_filter_len(ft, As); + float *h = new float[h_len]; + liquid_firdes_kaiser(h_len, firStereoCutoff, As, mu, h); + + result.firStereoLeft = firfilt_rrrf_create(h, h_len); + result.firStereoRight = firfilt_rrrf_create(h, h_len); } if (filterCommand.bandwidth) { diff --git a/src/demod/DemodulatorWorkerThread.h b/src/demod/DemodulatorWorkerThread.h index 47b014f..a14f375 100644 --- a/src/demod/DemodulatorWorkerThread.h +++ b/src/demod/DemodulatorWorkerThread.h @@ -16,14 +16,13 @@ public: DemodulatorWorkerThreadResult() : cmd(DEMOD_WORKER_THREAD_RESULT_NULL), iqResampler(NULL), iqResampleRatio(0), audioResampler(NULL), stereoResampler(NULL), audioResamplerRatio( - 0), sampleRate(0), bandwidth(0), audioSampleRate(0) { + 0), firStereoLeft(NULL), firStereoRight(NULL), sampleRate(0), bandwidth(0), audioSampleRate(0) { } DemodulatorWorkerThreadResult(DemodulatorThreadResultEnum cmd) : - cmd(cmd), iqResampler(NULL), iqResampleRatio(0), audioResampler(NULL), stereoResampler(NULL), audioResamplerRatio(0), sampleRate(0), bandwidth( - 0), audioSampleRate(0) { - + DemodulatorWorkerThreadResult() { + this->cmd = cmd; } DemodulatorThreadResultEnum cmd; @@ -34,6 +33,9 @@ public: msresamp_rrrf stereoResampler; double audioResamplerRatio; + firfilt_rrrf firStereoLeft; + firfilt_rrrf firStereoRight; + long long sampleRate; unsigned int bandwidth; unsigned int audioSampleRate; diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 297d409..424041a 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -683,7 +683,12 @@ void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) { near_dist = dist; } - if (dist <= halfBw && dist >= (int) ((float) halfBw / (1.5 - (0.65 * (1.0-(float)(wxGetApp().getSampleRate() - getBandwidth())/(float)wxGetApp().getSampleRate()))))) { + + if (dist <= halfBw && dist >= (int)((float) halfBw / 1.5)) { + if ((freqDiff > 0 && activeDemodulator->getDemodulatorType() == DEMOD_TYPE_USB) || + (freqDiff < 0 && activeDemodulator->getDemodulatorType() == DEMOD_TYPE_LSB)) { + continue; + } long edge_dist = abs(halfBw - dist); if (edge_dist < near_dist) { activeDemodulator = demod; @@ -704,9 +709,13 @@ void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) { SetCursor(wxCURSOR_SIZEWE); if (freqDiff > 0) { - nextDragState = WF_DRAG_BANDWIDTH_LEFT; + if (activeDemodulator->getDemodulatorType() != DEMOD_TYPE_USB) { + nextDragState = WF_DRAG_BANDWIDTH_LEFT; + } } else { - nextDragState = WF_DRAG_BANDWIDTH_RIGHT; + if (activeDemodulator->getDemodulatorType() != DEMOD_TYPE_LSB) { + nextDragState = WF_DRAG_BANDWIDTH_RIGHT; + } } mouseTracker.setVertDragLock(true);