diff --git a/core/src/core.cpp b/core/src/core.cpp index 7f4a507e..c0051268 100644 --- a/core/src/core.cpp +++ b/core/src/core.cpp @@ -66,18 +66,16 @@ duk_ret_t test_func(duk_context *ctx) { // main int sdrpp_main(int argc, char *argv[]) { -#ifdef _WIN32 - //FreeConsole(); - // ConfigManager::setResourceDir("./res"); - // ConfigManager::setConfigDir("."); -#endif - spdlog::info("SDR++ v" VERSION_STR); // Load default options and parse command line options::loadDefaults(); if (!options::parse(argc, argv)) { return -1; } +#ifdef _WIN32 + if (!options::opts.showConsole) { FreeConsole(); } +#endif + // Check root directory if (!std::filesystem::exists(options::opts.root)) { spdlog::warn("Root directory {0} does not exist, creating it", options::opts.root); diff --git a/core/src/dsp/measure.h b/core/src/dsp/measure.h index 00b25be8..b8d79710 100644 --- a/core/src/dsp/measure.h +++ b/core/src/dsp/measure.h @@ -7,93 +7,76 @@ #include namespace dsp { - class VolumeMeasure : public generic_block { + class LevelMeter : public generic_block { public: - VolumeMeasure() {} + LevelMeter() {} - VolumeMeasure(stream* in) { init(in); } + LevelMeter(stream* in) { init(in); } - ~VolumeMeasure() { - generic_block::stop(); - delete[] leftBuf; - delete[] rightBuf; - } + ~LevelMeter() { generic_block::stop(); } void init(stream* in) { _in = in; - leftBuf = new float[STREAM_BUFFER_SIZE]; - rightBuf = new float[STREAM_BUFFER_SIZE]; - generic_block::registerInput(_in); - generic_block::registerOutput(&out); + generic_block::registerInput(_in); } void setInput(stream* in) { - std::lock_guard lck(generic_block::ctrlMtx); - generic_block::tempStop(); - generic_block::unregisterInput(_in); + std::lock_guard lck(generic_block::ctrlMtx); + generic_block::tempStop(); + generic_block::unregisterInput(_in); _in = in; - generic_block::registerInput(_in); - generic_block::tempStart(); + generic_block::registerInput(_in); + generic_block::tempStart(); } int run() { count = _in->read(); if (count < 0) { return -1; } - memcpy(out.writeBuf, _in->readBuf, count * sizeof(stereo_t)); - volk_32fc_deinterleave_32f_x2(leftBuf, rightBuf, (lv_32fc_t*)_in->readBuf, count); - - _in->flush(); - if (!out.swap(count)) { return -1; } - - // Get peak from last value - float time = (float)count / sampleRate; - peak.l -= peakFall * time; - peak.r -= peakFall * time; - stereo_t _peak; - _peak.l = powf(10, peak.l / 10.0f); - _peak.r = powf(10, peak.r / 10.0f); - - stereo_t _average; - - // Calculate average - volk_32f_s32f_power_32f(leftBuf, leftBuf, 2, count); - volk_32f_s32f_power_32f(rightBuf, rightBuf, 2, count); - volk_32f_sqrt_32f(leftBuf, leftBuf, count); - volk_32f_sqrt_32f(rightBuf, rightBuf, count); - volk_32f_accumulator_s32f(&_average.l, leftBuf, count); - volk_32f_accumulator_s32f(&_average.r, rightBuf, count); - _average.l /= (float)count; - _average.r /= (float)count; - - // Calculate peak + float maxL = 0, maxR = 0; for (int i = 0; i < count; i++) { - if (leftBuf[i] > _peak.l) { _peak.l = leftBuf[i]; } - if (rightBuf[i] > _peak.r) { _peak.r = rightBuf[i]; } + float absL = fabs(_in->readBuf[i].l); + float absR = fabs(_in->readBuf[i].r); + if (absL > maxL) { maxL = absL; } + if (absR > maxR) { maxR = absR; } } - // Assign - peak.l = 10.0f * log10f(_peak.l); - peak.r = 10.0f * log10f(_peak.r); - average.l = (average.l * (1.0f - avgFilt)) + (10.0f * log10f(_average.l) * avgFilt); - average.r = (average.r * (1.0f - avgFilt)) + (10.0f * log10f(_average.r) * avgFilt); + _in->flush(); + + float _lvlL = 10.0f * logf(maxL); + float _lvlR = 10.0f * logf(maxL); + + // Update max values + { + std::lock_guard lck(lvlMtx); + if (_lvlL > lvlL) { lvlL = _lvlL; } + if (_lvlR > lvlR) { lvlR = _lvlR; } + } + return count; } - stream out; + float getLeftLevel() { + std::lock_guard lck(lvlMtx); + float ret = lvlL; + lvlL = -90.0f; + return ret; + } - stereo_t peak = {0, 0}; - stereo_t average = {0, 0}; + float getRightLevel() { + std::lock_guard lck(lvlMtx); + float ret = lvlR; + lvlR = -90.0f; + return ret; + } private: + float lvlL = -90.0f; + float lvlR = -90.0f; int count; - float peakFall = 10.0f; // dB/S - float avgFilt = 0.2f; // IIR filter coef - float sampleRate = 48000; stream* _in; + std::mutex lvlMtx; - float* leftBuf; - float* rightBuf; }; } \ No newline at end of file diff --git a/core/src/options.cpp b/core/src/options.cpp index 803203fa..58d7a994 100644 --- a/core/src/options.cpp +++ b/core/src/options.cpp @@ -8,6 +8,7 @@ namespace options { void loadDefaults() { #ifdef _WIN32 opts.root = "."; + opts.showConsole = false; #else std::string homedir = getenv("HOME"); opts.root = homedir + "/.config/sdrpp"; @@ -21,6 +22,9 @@ namespace options { if (i == argc - 1) { return false; } opts.root = argv[++i]; } + else if (!strcmp(arg, "-s") || !strcmp(arg, "--show-console")) { + opts.showConsole = true; + } else { spdlog::error("Invalid command line option: {0}", arg); return false; diff --git a/core/src/options.h b/core/src/options.h index c7e88b34..f170c784 100644 --- a/core/src/options.h +++ b/core/src/options.h @@ -5,6 +5,7 @@ namespace options { struct CMDLineOptions { std::string root; + bool showConsole; }; SDRPP_EXPORT CMDLineOptions opts; diff --git a/readme.md b/readme.md index 4e8b6ad5..c20c8b0c 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # SDR++, The bloat-free SDR software -![Screenshot](https://i.imgur.com/WejsiFN.png) +![Screenshot](https://i.imgur.com/EFOqwQQ.png) SDR++ is a cross-platform and open source SDR software with the aim of being bloat free and simple to use. ![Linux Build](https://github.com/AlexandreRouma/SDRPlusPlus/workflows/Linux%20Build/badge.svg) diff --git a/recorder/src/main.cpp b/recorder/src/main.cpp index 7ce1dd62..05fb946d 100644 --- a/recorder/src/main.cpp +++ b/recorder/src/main.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -11,6 +12,7 @@ #include #include #include +#include #include #include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -19,13 +21,15 @@ SDRPP_MOD_INFO { /* Name: */ "recorder", /* Description: */ "Recorder module for SDR++", /* Author: */ "Ryzerth", - /* Version: */ 0, 1, 2, + /* Version: */ 0, 2, 0, /* Max instances */ -1 }; -// TODO: Fix this and finish module -ConfigManager config; +std::string expandString(std::string input) { + input = std::regex_replace(input, std::regex("%ROOT%"), options::opts.root); + return std::regex_replace(input, std::regex("//"), "/"); +} std::string genFileName(std::string prefix) { time_t now = time(0); @@ -35,43 +39,36 @@ std::string genFileName(std::string prefix) { return prefix + buf; } -void streamRemovedHandler(void* ctx) { - -} - -void sampleRateChanged(void* ctx, double sampleRate, int blockSize) { - -} - -std::string expandString(std::string input) { - input = std::regex_replace(input, std::regex("%ROOT%"), options::opts.root); - return std::regex_replace(input, std::regex("//"), "/"); -} - class RecorderModule : public ModuleManager::Instance { public: RecorderModule(std::string name) { this->name = name; - recording = false; - selectedStreamName = ""; - selectedStreamId = 0; - lastNameList = ""; - config.aquire(); - if (!config.conf.contains(name)) { - config.conf[name]["recMode"] = 1; - config.conf[name]["directory"] = "%ROOT%/recordings"; - } - recMode = config.conf[name]["recMode"]; - std::string recPath = config.conf[name]["directory"]; - strcpy(path, recPath.c_str()); - config.release(true); + strcpy(recPath, "%ROOT%/recordings"); + + // Init audio path + vol.init(&dummyStream, 1.0f); + audioSplit.init(&vol.out); + audioSplit.bindStream(&meterStream); + meter.init(&meterStream); + audioHandler.init(&audioHandlerStream, _audioHandler, this); + + vol.start(); + audioSplit.start(); + meter.start(); + + // Init baseband path + basebandHandler.init(&basebandStream, _basebandHandler, this); + + wavSampleBuf = new int16_t[2 * STREAM_BUFFER_SIZE]; + + refreshStreams(); gui::menu.registerEntry(name, menuHandler, this); } ~RecorderModule() { - + delete[] wavSampleBuf; } void enable() { @@ -87,45 +84,64 @@ public: } private: + void refreshStreams() { + std::vector names = sigpath::sinkManager.getStreamNames(); + streamNames.clear(); + streamNamesTxt = ""; + for (auto const& name : names) { + streamNames.push_back(name); + streamNamesTxt += name; + streamNamesTxt += '\0'; + } + if (selectedStreamName == "") { + selectStream(streamNames[0]); + } + else { + selectStream(selectedStreamName); + } + } + + void selectStream(std::string name) { + auto it = std::find(streamNames.begin(), streamNames.end(), name); + if (it == streamNames.end()) { return; } + streamId = std::distance(streamNames.begin(), it); + + vol.stop(); + if (audioInput != NULL) { sigpath::sinkManager.unbindStream(selectedStreamName, audioInput); } + audioInput = sigpath::sinkManager.bindStream(name); + if (audioInput == NULL) { return; } + selectedStreamName = name; + vol.setInputSize(audioInput); + vol.start(); + } + static void menuHandler(void* ctx) { RecorderModule* _this = (RecorderModule*)ctx; float menuColumnWidth = ImGui::GetContentRegionAvailWidth(); - std::vector streamNames = sigpath::sinkManager.getStreamNames(); - std::string nameList; - for (std::string name : streamNames) { - nameList += name; - nameList += '\0'; - } - - if (nameList == "") { - ImGui::Text("No audio stream available"); - return; - } - - if (_this->lastNameList != nameList) { - _this->lastNameList = nameList; - auto _nameIt = std::find(streamNames.begin(), streamNames.end(), _this->selectedStreamName); - if (_nameIt == streamNames.end()) { - // TODO: verify if there even is a stream - _this->selectedStreamId = 0; - _this->selectedStreamName = streamNames[_this->selectedStreamId]; - } - else { - _this->selectedStreamId = std::distance(streamNames.begin(), _nameIt); - _this->selectedStreamName = streamNames[_this->selectedStreamId]; - } - } - - ImGui::BeginGroup(); + // Recording mode if (_this->recording) { style::beginDisabled(); } + ImGui::BeginGroup(); + ImGui::Columns(2, CONCAT("AirspyGainModeColumns##_", _this->name), false); + if (ImGui::RadioButton(CONCAT("Baseband##_recmode_", _this->name), !_this->recMode)) { + _this->recMode = false; + } + ImGui::NextColumn(); + if (ImGui::RadioButton(CONCAT("Audio##_recmode_", _this->name), _this->recMode)) { + _this->recMode = true; + } + ImGui::Columns(1, CONCAT("EndAirspyGainModeColumns##_", _this->name), false); + ImGui::EndGroup(); + if (_this->recording) { style::endDisabled(); } + + // Recording path ImGui::SetNextItemWidth(menuColumnWidth); bool lastPathValid = _this->pathValid; if (!lastPathValid) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f)); } - if (ImGui::InputText(CONCAT("##_recorder_path_", _this->name), _this->path, 4095)) { - std::string expandedPath = expandString(_this->path); + if (ImGui::InputText(CONCAT("##_recorder_path_", _this->name), _this->recPath, 4095)) { + std::string expandedPath = expandString(_this->recPath); if (!std::filesystem::exists(expandedPath)) { _this->pathValid = false; } @@ -134,169 +150,166 @@ private: } else { _this->pathValid = true; - config.aquire(); - config.conf[_this->name]["directory"] = _this->path; - config.release(true); + // Save config here } } if (!lastPathValid) { ImGui::PopStyleColor(); } - // TODO: Change VFO ref in signal path - // TODO: Add VFO record - ImGui::Columns(2, CONCAT("RecordModeColumns##_", _this->name), false); - if (ImGui::RadioButton(CONCAT("Baseband##_", _this->name), _this->recMode == 0) && _this->recMode != 0) { - _this->recMode = 0; - config.aquire(); - config.conf[_this->name]["recMode"] = _this->recMode; - config.release(true); + // Mode specific menu + if (_this->recMode) { + _this->audioMenu(menuColumnWidth); } - ImGui::NextColumn(); - if (ImGui::RadioButton(CONCAT("Audio##_", _this->name), _this->recMode == 1) && _this->recMode != 1) { - _this->recMode = 1; - config.aquire(); - config.conf[_this->name]["recMode"] = _this->recMode; - config.release(true); - } - if (_this->recording) { style::endDisabled(); } - ImGui::Columns(1, CONCAT("EndRecordModeColumns##_", _this->name), false); - - ImGui::EndGroup(); - - if (_this->recMode == 0) { - ImGui::PushItemWidth(menuColumnWidth); - if (!_this->recording) { - if (!_this->pathValid) { style::beginDisabled(); } - if (ImGui::Button("Record", ImVec2(menuColumnWidth, 0))) { - std::string expandedPath = expandString(std::string(_this->path) + genFileName("/baseband_")); - _this->samplesWritten = 0; - _this->sampleRate = sigpath::signalPath.getSampleRate(); - _this->writer = new WavWriter(expandedPath, 16, 2, _this->sampleRate); - _this->iqStream = new dsp::stream; - sigpath::signalPath.bindIQStream(_this->iqStream); - _this->workerThread = std::thread(_iqWriteWorker, _this); - _this->recording = true; - _this->startTime = time(0); - } - if (!_this->pathValid) { style::endDisabled(); } - ImGui::TextColored(ImGui::GetStyleColorVec4(ImGuiCol_Text), "Idle --:--:--"); - } - else { - if (ImGui::Button("Stop", ImVec2(menuColumnWidth, 0))) { - _this->iqStream->stopReader(); - _this->workerThread.join(); - _this->iqStream->clearReadStop(); - sigpath::signalPath.unbindIQStream(_this->iqStream); - _this->writer->close(); - delete _this->writer; - _this->recording = false; - } - uint64_t seconds = _this->samplesWritten / (uint64_t)_this->sampleRate; - time_t diff = seconds; - tm *dtm = gmtime(&diff); - ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec); - } - } - else if (_this->recMode == 1) { - ImGui::PushItemWidth(menuColumnWidth); - if (!_this->recording) { - if (ImGui::Combo(CONCAT("##_strea_select_", _this->name), &_this->selectedStreamId, nameList.c_str())) { - _this->selectedStreamName = streamNames[_this->selectedStreamId]; - } - } - else { - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.44f, 0.44f, 0.44f, 0.15f)); - ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.20f, 0.21f, 0.22f, 0.30f)); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 0.65f)); - ImGui::Combo(CONCAT("##_strea_select_", _this->name), &_this->selectedStreamId, nameList.c_str()); - ImGui::PopItemFlag(); - ImGui::PopStyleColor(3); - } - if (!_this->recording) { - if (!_this->pathValid) { style::beginDisabled(); } - if (ImGui::Button("Record", ImVec2(menuColumnWidth, 0))) { - std::string expandedPath = expandString(std::string(_this->path) + genFileName("/audio_")); - _this->samplesWritten = 0; - _this->sampleRate = sigpath::sinkManager.getStreamSampleRate(_this->selectedStreamName); - _this->writer = new WavWriter(expandedPath, 16, 2, _this->sampleRate); - _this->audioStream = sigpath::sinkManager.bindStream(_this->selectedStreamName); - _this->workerThread = std::thread(_audioWriteWorker, _this); - _this->recording = true; - _this->startTime = time(0); - } - if (!_this->pathValid) { style::endDisabled(); } - ImGui::TextColored(ImGui::GetStyleColorVec4(ImGuiCol_Text), "Idle --:--:--"); - } - else { - if (ImGui::Button("Stop", ImVec2(menuColumnWidth, 0))) { - _this->audioStream->stopReader(); - _this->workerThread.join(); - _this->audioStream->clearReadStop(); - sigpath::sinkManager.unbindStream(_this->selectedStreamName, _this->audioStream); - _this->writer->close(); - delete _this->writer; - _this->recording = false; - } - uint64_t seconds = _this->samplesWritten / (uint64_t)_this->sampleRate; - time_t diff = seconds; - tm *dtm = gmtime(&diff); - ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec); - } + else { + _this->basebandMenu(menuColumnWidth); } } - static void _audioWriteWorker(RecorderModule* _this) { - int16_t* sampleBuf = new int16_t[STREAM_BUFFER_SIZE * 2]; - while (true) { - int count = _this->audioStream->read(); - if (count < 0) { break; } - for (int i = 0; i < count; i++) { - sampleBuf[(i * 2) + 0] = _this->audioStream->readBuf[i].l * 512; - sampleBuf[(i * 2) + 1] = _this->audioStream->readBuf[i].r * 512; + void basebandMenu(float menuColumnWidth) { + if (!pathValid) { style::beginDisabled(); } + if (!recording) { + if (ImGui::Button(CONCAT("Record##_recorder_rec_", name), ImVec2(menuColumnWidth, 0))) { + recording = true; + samplesWritten = 0; + std::string expandedPath = expandString(std::string(recPath) + genFileName("/baseband_")); + sampleRate = sigpath::signalPath.getSampleRate(); + basebandWriter = new WavWriter(expandedPath, 16, 2, sigpath::signalPath.getSampleRate()); + basebandHandler.start(); + sigpath::signalPath.bindIQStream(&basebandStream); } - _this->audioStream->flush(); - _this->samplesWritten += count; - _this->writer->writeSamples(sampleBuf, count * sizeof(int16_t) * 2); + ImGui::TextColored(ImGui::GetStyleColorVec4(ImGuiCol_Text), "Idle --:--:--"); } - delete[] sampleBuf; + else { + if (ImGui::Button(CONCAT("Stop##_recorder_rec_", name), ImVec2(menuColumnWidth, 0))) { + recording = false; + sigpath::signalPath.unbindIQStream(&basebandStream); + basebandHandler.stop(); + basebandWriter->close(); + delete basebandWriter; + } + uint64_t seconds = samplesWritten / (uint64_t)sampleRate; + time_t diff = seconds; + tm *dtm = gmtime(&diff); + ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec); + } + if (!pathValid) { style::endDisabled(); } } - static void _iqWriteWorker(RecorderModule* _this) { - int16_t* sampleBuf = new int16_t[STREAM_BUFFER_SIZE]; - while (true) { - int count = _this->iqStream->read(); - if (count < 0) { break; } - for (int i = 0; i < count; i++) { - sampleBuf[(i * 2) + 0] = _this->iqStream->readBuf[i].q * 0x7FFF; - sampleBuf[(i * 2) + 1] = _this->iqStream->readBuf[i].i * 0x7FFF; - } - _this->iqStream->flush(); - _this->samplesWritten += count; - _this->writer->writeSamples(sampleBuf, count * sizeof(int16_t) * 2); + void audioMenu(float menuColumnWidth) { + ImGui::PushItemWidth(menuColumnWidth); + if (recording) { style::beginDisabled(); } + if (ImGui::Combo(CONCAT("##_recorder_strm_", name), &streamId, streamNamesTxt.c_str())) { + selectStream(streamNames[streamId]); } - delete[] sampleBuf; + if (recording) { style::endDisabled(); } + + double frameTime = 1.0 / ImGui::GetIO().Framerate; + lvlL = std::max(lvlL - (frameTime * 50.0), -90); + lvlR = std::max(lvlR - (frameTime * 50.0), -90); + + float _lvlL = meter.getLeftLevel(); + float _lvlR = meter.getRightLevel(); + if (_lvlL > lvlL) { lvlL = _lvlL; } + if (_lvlR > lvlR) { lvlR = _lvlR; } + ImGui::VolumeMeter(lvlL, lvlL, -60, 10); + ImGui::VolumeMeter(lvlR, lvlR, -60, 10); + + if (ImGui::SliderFloat(CONCAT("##_recorder_vol_", name), &audioVolume, 0, 1, "")) { + vol.setVolume(audioVolume); + } + ImGui::PopItemWidth(); + + if (!pathValid || selectedStreamName == "") { style::beginDisabled(); } + if (!recording) { + if (ImGui::Button(CONCAT("Record##_recorder_rec_", name), ImVec2(menuColumnWidth, 0))) { + recording = true; + samplesWritten = 0; + std::string expandedPath = expandString(std::string(recPath) + genFileName("/audio_")); + sampleRate = sigpath::sinkManager.getStreamSampleRate(selectedStreamName); + audioWriter = new WavWriter(expandedPath, 16, 2, sigpath::sinkManager.getStreamSampleRate(selectedStreamName)); + audioHandler.start(); + audioSplit.bindStream(&audioHandlerStream); + } + ImGui::TextColored(ImGui::GetStyleColorVec4(ImGuiCol_Text), "Idle --:--:--"); + } + else { + if (ImGui::Button(CONCAT("Stop##_recorder_rec_", name), ImVec2(menuColumnWidth, 0))) { + recording = false; + audioSplit.unbindStream(&audioHandlerStream); + audioHandler.stop(); + audioWriter->close(); + delete audioWriter; + } + uint64_t seconds = samplesWritten / (uint64_t)sampleRate; + time_t diff = seconds; + tm *dtm = gmtime(&diff); + ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec); + } + if (!pathValid || selectedStreamName == "") { style::endDisabled(); } } + static void _audioHandler(dsp::stereo_t *data, int count, void *ctx) { + RecorderModule* _this = (RecorderModule*)ctx; + for (int i = 0; i < count; i++) { + _this->wavSampleBuf[(2*i)] = data[i].l * 32768.0f; + _this->wavSampleBuf[(2*i) + 1] = data[i].r * 32768.0f; + } + _this->audioWriter->writeSamples(_this->wavSampleBuf, count * 2 * sizeof(int16_t)); + _this->samplesWritten += count; + } + + static void _basebandHandler(dsp::complex_t *data, int count, void *ctx) { + RecorderModule* _this = (RecorderModule*)ctx; + for (int i = 0; i < count; i++) { + _this->wavSampleBuf[(2*i)] = data[i].q * 32768.0f; + _this->wavSampleBuf[(2*i) + 1] = data[i].i * 32768.0f; + } + _this->basebandWriter->writeSamples(_this->wavSampleBuf, count * 2 * sizeof(int16_t)); + _this->samplesWritten += count; + } + + std::string name; bool enabled = true; - char path[4096]; + + char recPath[1024]; + + bool recMode = 1; + bool recording = false; bool pathValid = true; - dsp::stream* audioStream; - dsp::stream* iqStream; - WavWriter* writer; - VFOManager::VFO* vfo; - std::thread workerThread; - bool recording; - time_t startTime; - std::string lastNameList; - std::string selectedStreamName; - int selectedStreamId; + + float audioVolume = 1.0f; + + double sampleRate = 48000; + + float lvlL = -90.0f; + float lvlR = -90.0f; + + dsp::stream dummyStream; + + // Audio path + dsp::stream* audioInput; + dsp::Volume vol; + dsp::Splitter audioSplit; + dsp::stream meterStream; + dsp::LevelMeter meter; + dsp::stream audioHandlerStream; + dsp::HandlerSink audioHandler; + WavWriter* audioWriter; + + std::vector streamNames; + std::string streamNamesTxt; + int streamId = 0; + std::string selectedStreamName = ""; + + // Baseband path + dsp::stream basebandStream; + dsp::HandlerSink basebandHandler; + WavWriter* basebandWriter; + uint64_t samplesWritten; - float sampleRate; - float vfoSampleRate = 200000; - int recMode = 1; + int16_t* wavSampleBuf; }; @@ -305,18 +318,7 @@ struct RecorderContext_t { }; MOD_EXPORT void _INIT_() { - json def = json({}); - config.setPath(options::opts.root + "/recorder_config.json"); - config.load(def); - config.enableAutoSave(); - - // Create default recording directory - if (!std::filesystem::exists(options::opts.root + "/recordings")) { - spdlog::warn("Recordings directory does not exist, creating it"); - if (!std::filesystem::create_directory(options::opts.root + "/recordings")) { - spdlog::error("Could not create recordings directory"); - } - } + } MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { @@ -328,6 +330,5 @@ MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* inst) { } MOD_EXPORT void _END_(RecorderContext_t* ctx) { - config.disableAutoSave(); - config.save(); + } \ No newline at end of file diff --git a/root/res/bandplans/russia.json b/root/res/bandplans/russia.json index 3ebcb11c..2c127264 100644 --- a/root/res/bandplans/russia.json +++ b/root/res/bandplans/russia.json @@ -5,6 +5,24 @@ "author_name": "Raov", "author_url": "https://twitter.com/raov_birbtog", "bands": [ + { + "name": "2200m", + "type": "amateur", + "start": 135700, + "end": 137800 + }, + { + "name": "LW", + "type": "broadcast", + "start": 144000, + "end": 415000 + }, + { + "name": "MW", + "type": "broadcast", + "start": 520000, + "end": 1602000 + }, { "name": "160m CW", "type": "amateur", @@ -29,6 +47,12 @@ "start": 1843000, "end": 2000000 }, + { + "name": "SW 90m", + "type": "broadcast", + "start": 3200000, + "end": 3400000 + }, { "name": "80m CW", "type": "amateur", @@ -78,22 +102,22 @@ "end": 3800000 }, { - "name": "60m CW 200Hz", - "type": "amateur", - "start": 5351500, - "end": 5354000 + "name": "SW 75m", + "type": "broadcast", + "start": 3900000, + "end": 4000000 }, { - "name": "60m USB", - "type": "amateur", - "start": 5354000, - "end": 5366000 + "name": "SW 60m", + "type": "broadcast", + "start": 4750000, + "end": 5060000 }, { - "name": "60m CW 20Hz", - "type": "amateur", - "start": 5356000, - "end": 5366500 + "name": "SW 49m", + "type": "broadcast", + "start": 5900000, + "end": 6200000 }, { "name": "40m CW", @@ -130,6 +154,480 @@ "type": "amateur", "start": 7130000, "end": 7200000 + }, + { + "name": "SW 31m", + "type": "broadcast", + "start": 9500000, + "end": 9900000 + }, + { + "name": "30m CW", + "type": "amateur", + "start": 10100000, + "end": 10130000 + }, + { + "name": "30m NB", + "type": "amateur", + "start": 10130000, + "end": 10150000 + }, + { + "name": "SW 25m", + "type": "broadcast", + "start": 11650000, + "end": 12050000 + }, + { + "name": "SW 22m", + "type": "broadcast", + "start": 13600000, + "end": 13800000 + }, + { + "name": "20m CW Contest", + "type": "amateur", + "start": 14000000, + "end": 14060000 + }, + { + "name": "20m CW", + "type": "amateur", + "start": 14060000, + "end": 14070000 + }, + { + "name": "20m NB", + "type": "amateur", + "start": 14070000, + "end": 14099000 + }, + { + "name": "20m Beacons", + "type": "amateur", + "start": 14099000, + "end": 14101000 + }, + { + "name": "20m Digital", + "type": "amateur", + "start": 14101000, + "end": 14112000 + }, + { + "name": "20m", + "type": "amateur", + "start": 14112000, + "end": 14125000 + }, + { + "name": "20m Contest", + "type": "amateur", + "start": 14125000, + "end": 14300000 + }, + { + "name": "20m", + "type": "amateur", + "start": 14300000, + "end": 14350000 + }, + { + "name": "SW 19m", + "type": "broadcast", + "start": 15100000, + "end": 15600000 + }, + { + "name": "SW 16m", + "type": "broadcast", + "start": 17550000, + "end": 17900000 + }, + { + "name": "17m CW", + "type": "amateur", + "start": 18068000, + "end": 18095000 + }, + { + "name": "17m NB", + "type": "amateur", + "start": 18095000, + "end": 18109000 + }, + { + "name": "17m Beacons", + "type": "amateur", + "start": 18109000, + "end": 18111000 + }, + { + "name": "17m Digital", + "type": "amateur", + "start": 18111000, + "end": 18120000 + }, + { + "name": "17m", + "type": "amateur", + "start": 18120000, + "end": 18168000 + }, + { + "name": "15m CW", + "type": "amateur", + "start": 21000000, + "end": 21070000 + }, + { + "name": "15m NB", + "type": "amateur", + "start": 21070000, + "end": 21110000 + }, + { + "name": "15m Digital", + "type": "amateur", + "start": 21110000, + "end": 21120000 + }, + { + "name": "15m NB", + "type": "amateur", + "start": 21120000, + "end": 21149000 + }, + { + "name": "15m Beacons", + "type": "amateur", + "start": 21149000, + "end": 21151000 + }, + { + "name": "15m", + "type": "amateur", + "start": 21151000, + "end": 21450000 + }, + { + "name": "SW 13m", + "type": "broadcast", + "start": 21450000, + "end": 21850000 + }, + { + "name": "12m CW", + "type": "amateur", + "start": 24890000, + "end": 24915000 + }, + { + "name": "12m NB", + "type": "amateur", + "start": 24915000, + "end": 24929000 + }, + { + "name": "12m Beacons", + "type": "amateur", + "start": 24929000, + "end": 24931000 + }, + { + "name": "12m Digital", + "type": "amateur", + "start": 24931000, + "end": 24940000 + }, + { + "name": "12m", + "type": "amateur", + "start": 24940000, + "end": 24990000 + }, + { + "name": "SW 11m", + "type": "broadcast", + "start": 25650000, + "end": 26100000 + }, + { + "name": "CB", + "type": "amateur", + "start": 25165000, + "end": 30105000 + }, + { + "name": "10m CW", + "type": "amateur", + "start": 28000000, + "end": 28070000 + }, + { + "name": "10m NB Digital", + "type": "amateur", + "start": 28070000, + "end": 28150000 + }, + { + "name": "10m NB", + "type": "amateur", + "start": 28150000, + "end": 28190000 + }, + { + "name": "10m Beacons", + "type": "amateur", + "start": 28190000, + "end": 28225000 + }, + { + "name": "10m Beacons", + "type": "amateur", + "start": 28225000, + "end": 28300000 + }, + { + "name": "10m Digital", + "type": "amateur", + "start": 28300000, + "end": 28320000 + }, + { + "name": "10m", + "type": "amateur", + "start": 28320000, + "end": 29000000 + }, + { + "name": "10m", + "type": "amateur", + "start": 29000000, + "end": 29300000 + }, + { + "name": "10m Sat", + "type": "amateur", + "start": 29300000, + "end": 29510000 + }, + { + "name": "10m FM-R", + "type": "amateur", + "start": 29520000, + "end": 29590000 + }, + { + "name": "10m FM-R", + "type": "amateur", + "start": 29620000, + "end": 29700000 + }, + { + "name": "UHF Soviet", + "type": "broadcast", + "start": 65900000, + "end": 74000000 + }, + { + "name": "UHF(FM)", + "type": "broadcast", + "start": 87500000, + "end": 108000000 + }, + { + "name": "Airband", + "type": "military", + "start": 108000000, + "end": 117950000 + }, + { + "name": "2m CW", + "type": "amateur", + "start": 144035000, + "end": 144100000 + }, + { + "name": "2m CW", + "type": "amateur", + "start": 144100000, + "end": 144150000 + }, + { + "name": "2m Digital", + "type": "amateur", + "start": 144165000, + "end": 144180000 + }, + { + "name": "2m", + "type": "amateur", + "start": 144180000, + "end": 144360000 + }, + { + "name": "2m Digital", + "type": "amateur", + "start": 144360000, + "end": 144400000 + }, + { + "name": "2m Beacons", + "type": "amateur", + "start": 144400000, + "end": 144490000 + }, + { + "name": "2m Digital", + "type": "amateur", + "start": 144500000, + "end": 144794000 + }, + { + "name": "2m Digital", + "type": "amateur", + "start": 144794000, + "end": 144990000 + }, + { + "name": "2m FM-R", + "type": "amateur", + "start": 144990000, + "end": 145194000 + }, + { + "name": "2m FM Sat", + "type": "amateur", + "start": 145194000, + "end": 145206000 + }, + { + "name": "2m FM", + "type": "amateur", + "start": 145206000, + "end": 145594000 + }, + { + "name": "2m FM-R", + "type": "amateur", + "start": 145594000, + "end": 145793500 + }, + { + "name": "2m FM Sat", + "type": "amateur", + "start": 145793500, + "end": 145806000 + }, + { + "name": "2m Sat", + "type": "amateur", + "start": 145806000, + "end": 146000000 + }, + { + "name": "70cm", + "type": "amateur", + "start": 430000000, + "end": 432000000 + }, + { + "name": "70cm CW", + "type": "amateur", + "start": 432025000, + "end": 432100000 + }, + { + "name": "70cm", + "type": "amateur", + "start": 432100000, + "end": 432400000 + }, + { + "name": "70cm Beacons", + "type": "amateur", + "start": 432400000, + "end": 432500000 + }, + { + "name": "70cm", + "type": "amateur", + "start": 432500000, + "end": 433000000 + }, + { + "name": "LPD", + "type": "amateur", + "start": 433075000, + "end": 434775000 + }, + { + "name": "70cm", + "type": "amateur", + "start": 435000000, + "end": 440000000 + }, + { + "name": "PMR", + "type": "amateur", + "start": 446000000, + "end": 446100000 + }, + { + "name": "23cm", + "type": "amateur", + "start": 1260000000, + "end": 1290994000 + }, + { + "name": "23cm FM-R", + "type": "amateur", + "start": 1290994000, + "end": 1291481000 + }, + { + "name": "23cm", + "type": "amateur", + "start": 1291481000, + "end": 1296000000 + }, + { + "name": "23cm CW", + "type": "amateur", + "start": 1296025000, + "end": 1296150000 + }, + { + "name": "23cm", + "type": "amateur", + "start": 1296150000, + "end": 1296800000 + }, + { + "name": "23cm Beacons", + "type": "amateur", + "start": 1296800000, + "end": 1296994000 + }, + { + "name": "23cm FM-R", + "type": "amateur", + "start": 1296994000, + "end": 1297490000 + }, + { + "name": "23cm FM", + "type": "amateur", + "start": 1297490000, + "end": 1298000000 + }, + { + "name": "23cm", + "type": "amateur", + "start": 1298000000, + "end": 1300000000 } ] } \ No newline at end of file