diff --git a/examples/arduino-tx/arduino-tx.ino b/examples/arduino-tx/arduino-tx.ino index 466ee9c..51eb2f0 100644 --- a/examples/arduino-tx/arduino-tx.ino +++ b/examples/arduino-tx/arduino-tx.ino @@ -89,6 +89,13 @@ void setup() { // Protocols to use for TX GGWave::Protocols::tx().only(GGWAVE_PROTOCOL_MT_FASTEST); + // Sometimes, the speaker might not be able to produce frequencies in the Mono-tone range of 1-2 kHz. + // In such cases, you can shift the base frequency up by changing the "freqStart" parameter of the protocol. + // Make sure that in the receiver (for example, the "Waver" app) the base frequency is shifted by the same amount. + // Here we shift the frequency by +48 bins. Each bin is equal to 48000/1024 = 46.875 Hz. + // So the base frequency is shifted by +2250 Hz. + //GGWave::Protocols::tx()[GGWAVE_PROTOCOL_MT_FASTEST].freqStart += 48; + // Initialize the ggwave instance and print the memory usage ggwave.prepare(p); Serial.println(ggwave.heapSize()); diff --git a/examples/waver/common.cpp b/examples/waver/common.cpp index ccb240a..8f45fd8 100644 --- a/examples/waver/common.cpp +++ b/examples/waver/common.cpp @@ -236,6 +236,7 @@ struct Input { dst.update = true; dst.flags.needReinit = true; dst.sampleRateOffset = std::move(this->sampleRateOffset); + dst.freqStartShift = std::move(this->freqStartShift); dst.payloadLength = std::move(this->payloadLength); dst.directSequenceSpread = std::move(this->directSequenceSpread); } @@ -266,6 +267,7 @@ struct Input { // reinit float sampleRateOffset = 0; + int freqStartShift = 0; int payloadLength = -1; // spectrum @@ -636,10 +638,19 @@ void updateCore() { if (inputCurrent.flags.needReinit) { static auto sampleRateInpOld = ggWave->sampleRateInp(); static auto sampleRateOutOld = ggWave->sampleRateOut(); + static auto freqStartShiftOld = 0; + auto sampleFormatInpOld = ggWave->sampleFormatInp(); auto sampleFormatOutOld = ggWave->sampleFormatOut(); auto rxProtocolsOld = ggWave->rxProtocols(); + for (int i = 0; i < GGWAVE_PROTOCOL_COUNT; ++i) { + GGWave::Protocols::tx()[i].freqStart = std::max(1, GGWave::Protocols::tx()[i].freqStart + inputCurrent.freqStartShift - freqStartShiftOld); + rxProtocolsOld[i].freqStart = std::max(1, rxProtocolsOld[i].freqStart + inputCurrent.freqStartShift - freqStartShiftOld); + } + + freqStartShiftOld = inputCurrent.freqStartShift; + GGWave::OperatingMode mode = GGWAVE_OPERATING_MODE_RX_AND_TX; if (inputCurrent.directSequenceSpread) mode |= GGWAVE_OPERATING_MODE_USE_DSS; @@ -853,6 +864,8 @@ void renderMain() { int protocolId = GGWAVE_PROTOCOL_AUDIBLE_FAST; bool isSampleRateOffset = false; float sampleRateOffset = -512.0f; + bool isFreqStartShift = false; + int freqStartShift = 48; bool isFixedLength = false; bool directSequenceSpread = false; int payloadLength = 16; @@ -1055,7 +1068,7 @@ void renderMain() { if (windowId == WindowId::Settings) { ImGui::BeginChild("Settings:main", ImGui::GetContentRegionAvail(), true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); - ImGui::Text("Waver v1.5.1"); + ImGui::Text("Waver v1.5.2"); ImGui::Separator(); ImGui::Text("%s", ""); @@ -1177,7 +1190,10 @@ void renderMain() { { const float df = statsCurrent.sampleRate/statsCurrent.samplesPerFrame; const auto & protocol = settings.txProtocols[settings.protocolId]; - ImGui::Text("%6.2f Hz - %6.2f Hz", df*protocol.freqStart, df*(protocol.freqStart + float(2*16*protocol.bytesPerTx)/protocol.extra)); + const auto freqStart = std::max(1, protocol.freqStart + (settings.isFreqStartShift ? settings.freqStartShift : 0)); + const float f0 = df*freqStart; + const float f1 = df*(freqStart + float(2*16*protocol.bytesPerTx)/protocol.extra); + ImGui::Text("%6.2f Hz - %6.2f Hz", f0, f1); } // fixed-length @@ -1204,7 +1220,7 @@ void renderMain() { if (settings.isFixedLength) { ImGui::SameLine(); ImGui::PushItemWidth(0.5*ImGui::GetContentRegionAvailWidth()); - if (ImGui::SliderInt("Bytes", &settings.payloadLength, 1, GGWave::kMaxLengthFixed)) { + if (ImGui::DragInt("Bytes", &settings.payloadLength, 1, 1, GGWave::kMaxLengthFixed)) { g_buffer.inputUI.update = true; g_buffer.inputUI.flags.needReinit = true; g_buffer.inputUI.payloadLength = settings.isFixedLength ? settings.payloadLength : -1; @@ -1212,57 +1228,99 @@ void renderMain() { ImGui::PopItemWidth(); } - // Output sample-rate offset - ImGui::Text("%s", ""); + // Direct-sequence spread + //ImGui::Text("%s", ""); { auto posSave = ImGui::GetCursorScreenPos(); ImGui::Text("%s", ""); ImGui::SetCursorScreenPos({ posSave.x + kLabelWidth, posSave.y }); ImGui::PushTextWrapPos(); - ImGui::TextDisabled("Modify the output Sampling Rate"); + ImGui::TextDisabled("Direct-sequence spread"); ImGui::PopTextWrapPos(); } { auto posSave = ImGui::GetCursorScreenPos(); - ImGui::Text("Pitch shift: "); + ImGui::Text("Use DSS: "); ImGui::SetCursorScreenPos({ posSave.x + kLabelWidth, posSave.y }); } - if (ImGui::Checkbox("##output-sample-rate-offset", &settings.isSampleRateOffset)) { + if (ImGui::Checkbox("##direct-sequence-spread", &settings.directSequenceSpread)) { g_buffer.inputUI.update = true; g_buffer.inputUI.flags.needReinit = true; - g_buffer.inputUI.sampleRateOffset = settings.isSampleRateOffset ? settings.sampleRateOffset : 0; + g_buffer.inputUI.directSequenceSpread = settings.directSequenceSpread; } - if (settings.isSampleRateOffset) { + // FreqStart offset + //ImGui::Text("%s", ""); + { + auto posSave = ImGui::GetCursorScreenPos(); + ImGui::Text("%s", ""); + ImGui::SetCursorScreenPos({ posSave.x + kLabelWidth, posSave.y }); + ImGui::PushTextWrapPos(); + ImGui::TextDisabled("Apply tx/rx frequency shift"); + ImGui::PopTextWrapPos(); + } + { + auto posSave = ImGui::GetCursorScreenPos(); + ImGui::Text("Freq shift: "); + ImGui::SetCursorScreenPos({ posSave.x + kLabelWidth, posSave.y }); + } + if (ImGui::Checkbox("##freq-start-offset", &settings.isFreqStartShift)) { + g_buffer.inputUI.update = true; + g_buffer.inputUI.flags.needReinit = true; + g_buffer.inputUI.freqStartShift = settings.isFreqStartShift ? settings.freqStartShift : 0; + } + + if (settings.isFreqStartShift) { ImGui::SameLine(); ImGui::PushItemWidth(0.5*ImGui::GetContentRegionAvailWidth()); - if (ImGui::SliderFloat("Samples", &settings.sampleRateOffset, -1000, 1000, "%.0f")) { + if (ImGui::DragInt("bins", &settings.freqStartShift, 1, -64, 64, "%d")) { g_buffer.inputUI.update = true; g_buffer.inputUI.flags.needReinit = true; - g_buffer.inputUI.sampleRateOffset = settings.isSampleRateOffset ? settings.sampleRateOffset : 0; + g_buffer.inputUI.freqStartShift = settings.isFreqStartShift ? settings.freqStartShift : 0; } ImGui::PopItemWidth(); + + { + auto posSave = ImGui::GetCursorScreenPos(); + ImGui::Text(""); + ImGui::SetCursorScreenPos({ posSave.x + kLabelWidth, posSave.y }); + } + { + const float df = statsCurrent.sampleRate/statsCurrent.samplesPerFrame; + ImGui::Text("%6.2f Hz", df*settings.freqStartShift); + } } - // Direct-sequence spread - ImGui::Text("%s", ""); + // Output sample-rate offset + //ImGui::Text("%s", ""); { auto posSave = ImGui::GetCursorScreenPos(); ImGui::Text("%s", ""); ImGui::SetCursorScreenPos({ posSave.x + kLabelWidth, posSave.y }); ImGui::PushTextWrapPos(); - ImGui::TextDisabled("Direct-sequence spread"); + ImGui::TextDisabled("Modify the output Sampling Rate"); ImGui::PopTextWrapPos(); } { auto posSave = ImGui::GetCursorScreenPos(); - ImGui::Text("Use DSS: "); + ImGui::Text("Pitch shift: "); ImGui::SetCursorScreenPos({ posSave.x + kLabelWidth, posSave.y }); } - if (ImGui::Checkbox("##direct-sequence-spread", &settings.directSequenceSpread)) { + if (ImGui::Checkbox("##output-sample-rate-offset", &settings.isSampleRateOffset)) { g_buffer.inputUI.update = true; g_buffer.inputUI.flags.needReinit = true; - g_buffer.inputUI.directSequenceSpread = settings.directSequenceSpread; + g_buffer.inputUI.sampleRateOffset = settings.isSampleRateOffset ? settings.sampleRateOffset : 0; + } + + if (settings.isSampleRateOffset) { + ImGui::SameLine(); + ImGui::PushItemWidth(0.5*ImGui::GetContentRegionAvailWidth()); + if (ImGui::SliderFloat("Samples", &settings.sampleRateOffset, -1000, 1000, "%.0f")) { + g_buffer.inputUI.update = true; + g_buffer.inputUI.flags.needReinit = true; + g_buffer.inputUI.sampleRateOffset = settings.isSampleRateOffset ? settings.sampleRateOffset : 0; + } + ImGui::PopItemWidth(); } // rx protocols diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 87988ce..9dcf351 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,6 +1,6 @@ name: waver - version: '1.5.1' + version: '1.5.2' summary: Data over sound description: | Waver allows you to send and receive text messages from nearby devices through sound waves.