ggwave v0.2.0 (#20)

* ggwave : add support for fixed length transmissions

* spectrogram : add sample rate offset for debugging purposes

* gwave : fix decoding bug

* waver : wip

* wip

* remove post-marker frames

* add resampler

* ggwave : input/output resampling

* ggwave : fix python build

* ggwave : update spm

* ggwave : refactor duplicate encode/decode code

* ggwave : fix sound marker detection

* waver : fix typo

* ggwave : fix uninitialized members

* ggwave : more sensitive receive
This commit is contained in:
Georgi Gerganov
2021-02-20 19:16:15 +02:00
committed by GitHub
parent ff5c569071
commit 19bf22df0d
17 changed files with 921 additions and 366 deletions

View File

@@ -3,6 +3,8 @@
#include "ggwave-common.h"
#include "ggwave-common-sdl2.h"
#include <SDL.h>
#include <cstdio>
#include <string>
@@ -11,18 +13,20 @@
#include <iostream>
int main(int argc, char** argv) {
printf("Usage: %s [-cN] [-pN] [-tN]\n", argv[0]);
printf("Usage: %s [-cN] [-pN] [-tN] [-lN]\n", argv[0]);
printf(" -cN - select capture device N\n");
printf(" -pN - select playback device N\n");
printf(" -tN - transmission protocol\n");
printf(" -lN - fixed payload length of size N, N in [1, %d]\n", GGWave::kMaxLengthFixed);
printf("\n");
auto argm = parseCmdArguments(argc, argv);
int captureId = argm["c"].empty() ? 0 : std::stoi(argm["c"]);
int playbackId = argm["p"].empty() ? 0 : std::stoi(argm["p"]);
int txProtocol = argm["t"].empty() ? 1 : std::stoi(argm["t"]);
int payloadLength = argm["l"].empty() ? -1 : std::stoi(argm["l"]);
if (GGWave_init(playbackId, captureId) == false) {
if (GGWave_init(playbackId, captureId, payloadLength) == false) {
fprintf(stderr, "Failed to initialize GGWave\n");
return -1;
}
@@ -75,5 +79,8 @@ int main(int argc, char** argv) {
GGWave_deinit();
SDL_CloseAudio();
SDL_Quit();
return 0;
}

View File

@@ -77,7 +77,9 @@ void GGWave_setDefaultCaptureDeviceName(std::string name) {
bool GGWave_init(
const int playbackId,
const int captureId) {
const int captureId,
const int payloadLength,
const int sampleRateOffset) {
if (g_devIdInp && g_devIdOut) {
return false;
@@ -117,7 +119,7 @@ bool GGWave_init(
SDL_AudioSpec playbackSpec;
SDL_zero(playbackSpec);
playbackSpec.freq = GGWave::kBaseSampleRate;
playbackSpec.freq = GGWave::kBaseSampleRate + sampleRateOffset;
playbackSpec.format = AUDIO_S16SYS;
playbackSpec.channels = 1;
playbackSpec.samples = 16*1024;
@@ -160,7 +162,7 @@ bool GGWave_init(
if (g_devIdInp == 0) {
SDL_AudioSpec captureSpec;
captureSpec = g_obtainedSpecOut;
captureSpec.freq = GGWave::kBaseSampleRate;
captureSpec.freq = GGWave::kBaseSampleRate + sampleRateOffset;
captureSpec.format = AUDIO_F32SYS;
captureSpec.samples = 4096;
@@ -214,11 +216,12 @@ bool GGWave_init(
if (g_ggWave) delete g_ggWave;
g_ggWave = new GGWave({
g_obtainedSpecInp.freq,
g_obtainedSpecOut.freq,
GGWave::kDefaultSamplesPerFrame,
sampleFormatInp,
sampleFormatOut});
payloadLength,
g_obtainedSpecInp.freq,
g_obtainedSpecOut.freq,
GGWave::kDefaultSamplesPerFrame,
sampleFormatInp,
sampleFormatOut});
}
return true;
@@ -280,8 +283,9 @@ bool GGWave_deinit() {
SDL_CloseAudioDevice(g_devIdInp);
SDL_PauseAudioDevice(g_devIdOut, 1);
SDL_CloseAudioDevice(g_devIdOut);
SDL_CloseAudio();
SDL_Quit();
g_devIdInp = 0;
g_devIdOut = 0;
return true;
}

View File

@@ -7,7 +7,7 @@ class GGWave;
// GGWave helpers
void GGWave_setDefaultCaptureDeviceName(std::string name);
bool GGWave_init(const int playbackId, const int captureId);
bool GGWave_init(const int playbackId, const int captureId, const int payloadLength = -1, const int sampleRateOffset = 0);
GGWave * GGWave_instance();
bool GGWave_mainLoop();
bool GGWave_deinit();

View File

@@ -3,6 +3,8 @@
#include "ggwave-common.h"
#include "ggwave-common-sdl2.h"
#include <SDL.h>
#include <cstdio>
#include <thread>
@@ -26,5 +28,8 @@ int main(int argc, char** argv) {
GGWave_deinit();
SDL_CloseAudio();
SDL_Quit();
return 0;
}

View File

@@ -69,7 +69,7 @@ int main(int argc, char** argv) {
fprintf(stderr, "Generating waveform for message '%s' ...\n", message.c_str());
GGWave ggWave({ GGWave::kBaseSampleRate, sampleRateOut, 1024, GGWAVE_SAMPLE_FORMAT_F32, GGWAVE_SAMPLE_FORMAT_I16 });
GGWave ggWave({ -1, GGWave::kBaseSampleRate, sampleRateOut, 1024, GGWAVE_SAMPLE_FORMAT_F32, GGWAVE_SAMPLE_FORMAT_I16 });
ggWave.init(message.size(), message.data(), ggWave.getTxProtocol(protocolId), volume);
std::vector<char> bufferPCM;

View File

@@ -31,10 +31,10 @@ struct FreqData {
bool g_isCapturing = true;
constexpr int g_nSamplesPerFrame = 1024;
int g_binMin = 40;
int g_binMax = 40 + 96;
int g_binMin = 20;
int g_binMax = 60;
float g_scale = 5.0;
float g_scale = 30.0;
bool g_showControls = true;
@@ -42,6 +42,8 @@ int g_freqDataHead = 0;
int g_freqDataSize = 0;
std::vector<FreqData> g_freqData;
int g_sampleRateOffset = 0;
}
void GGWave_setDefaultCaptureDeviceName(std::string name) {
@@ -90,7 +92,7 @@ bool GGWave_init(
SDL_AudioSpec playbackSpec;
SDL_zero(playbackSpec);
playbackSpec.freq = GGWave::kBaseSampleRate;
playbackSpec.freq = GGWave::kBaseSampleRate + g_sampleRateOffset;
playbackSpec.format = AUDIO_S16SYS;
playbackSpec.channels = 1;
playbackSpec.samples = 16*1024;
@@ -133,7 +135,7 @@ bool GGWave_init(
if (g_devIdInp == 0) {
SDL_AudioSpec captureSpec;
captureSpec = g_obtainedSpecOut;
captureSpec.freq = GGWave::kBaseSampleRate;
captureSpec.freq = GGWave::kBaseSampleRate + g_sampleRateOffset;
captureSpec.format = AUDIO_F32SYS;
captureSpec.samples = g_nSamplesPerFrame;
@@ -240,8 +242,9 @@ bool GGWave_deinit() {
SDL_CloseAudioDevice(g_devIdInp);
SDL_PauseAudioDevice(g_devIdOut, 1);
SDL_CloseAudioDevice(g_devIdOut);
SDL_CloseAudio();
SDL_Quit();
g_devIdInp = 0;
g_devIdOut = 0;
return true;
}
@@ -474,6 +477,10 @@ int main(int argc, char** argv) {
ImGui::DragInt("Min", &g_binMin, 1, 0, g_binMax - 1);
ImGui::DragInt("Max", &g_binMax, 1, g_binMin + 1, g_nSamplesPerFrame/2);
ImGui::DragFloat("Scale", &g_scale, 1.0f, 1.0f, 1000.0f);
if (ImGui::SliderInt("Offset", &g_sampleRateOffset, -2048, 2048)) {
GGWave_deinit();
GGWave_init(0, 0);
}
if (ImGui::Button("Pause")) {
togglePause = true;
}
@@ -515,6 +522,7 @@ int main(int argc, char** argv) {
SDL_GL_DeleteContext(gl_context);
SDL_DestroyWindow(window);
SDL_CloseAudio();
SDL_Quit();
return 0;

View File

@@ -188,6 +188,10 @@ struct State {
struct Input {
bool update = false;
Message message;
bool reinit = false;
bool isSampleRateOffset = false;
int payloadLength = -1;
};
struct Buffer {
@@ -509,6 +513,7 @@ void updateCore() {
static Input inputCurrent;
static int lastRxDataLength = 0;
static float lastRxTimestamp = 0.0f;
static GGWave::TxRxData lastRxData;
{
@@ -519,6 +524,15 @@ void updateCore() {
}
}
if (inputCurrent.reinit) {
GGWave_deinit();
// todo : use the provided cli arguments for playback and capture device
GGWave_init(0, 0, inputCurrent.payloadLength, inputCurrent.isSampleRateOffset ? -512 : 0);
g_ggWave = GGWave_instance();
inputCurrent.reinit = false;
}
if (inputCurrent.update) {
g_ggWave->init(
(int) inputCurrent.message.data.size(),
@@ -543,7 +557,7 @@ void updateCore() {
0,
Message::Error,
};
} else if (lastRxDataLength > 0) {
} else if (lastRxDataLength > 0 && ImGui::GetTime() - lastRxTimestamp > 0.5f) {
auto message = std::string((char *) lastRxData.data(), lastRxDataLength);
const Message::Type type = isFileBroadcastMessage(message) ? Message::FileBroadcast : Message::Text;
g_buffer.stateCore.update = true;
@@ -556,6 +570,7 @@ void updateCore() {
0,
type,
};
lastRxTimestamp = ImGui::GetTime();
}
if (g_ggWave->takeSpectrum(g_buffer.stateCore.spectrum)) {
@@ -663,6 +678,9 @@ void renderMain() {
struct Settings {
int protocolId = 1;
bool isFixedLength = false;
int payloadLength = 1;
bool isSampleRateOffset = false;
float volume = 0.10f;
};
@@ -817,14 +835,14 @@ void renderMain() {
ImGui::BeginChild("Settings:main", ImGui::GetContentRegionAvail(), true);
ImGui::Text("%s", "");
ImGui::Text("%s", "");
ImGui::Text("Waver v1.3.2");
ImGui::Text("Waver v1.4.0");
ImGui::Separator();
ImGui::Text("%s", "");
ImGui::Text("Sample rate (capture): %g, %d B/sample", g_ggWave->getSampleRateInp(), g_ggWave->getSampleSizeBytesInp());
ImGui::Text("Sample rate (playback): %g, %d B/sample", g_ggWave->getSampleRateOut(), g_ggWave->getSampleSizeBytesOut());
const float kLabelWidth = ImGui::CalcTextSize("Tx Protocol: ").x;
const float kLabelWidth = ImGui::CalcTextSize("Fixed-length: ").x;
// volume
ImGui::Text("%s", "");
@@ -879,6 +897,37 @@ void renderMain() {
ImGui::SetCursorScreenPos(posSave);
}
// fixed-length
ImGui::Text("%s", "");
{
auto posSave = ImGui::GetCursorScreenPos();
ImGui::Text("Fixed-length: ");
ImGui::SetCursorScreenPos({ posSave.x + kLabelWidth, posSave.y });
}
if (ImGui::Checkbox("##fixed-length", &settings.isFixedLength)) {
g_buffer.inputUI.update = true;
g_buffer.inputUI.reinit = true;
g_buffer.inputUI.isSampleRateOffset = settings.isSampleRateOffset;
g_buffer.inputUI.payloadLength = settings.isFixedLength ? settings.payloadLength : -1;
} else {
g_buffer.inputUI.reinit = false;
g_buffer.inputUI.isSampleRateOffset = false;
}
if (settings.isFixedLength) {
ImGui::SameLine();
ImGui::PushItemWidth(0.5*ImGui::GetContentRegionAvailWidth());
ImGui::SliderInt("Bytes", &settings.payloadLength, 1, 16);
ImGui::PopItemWidth();
ImGui::SameLine();
if (ImGui::Checkbox("Offset", &settings.isSampleRateOffset)) {
g_buffer.inputUI.update = true;
g_buffer.inputUI.reinit = true;
g_buffer.inputUI.isSampleRateOffset = settings.isSampleRateOffset;
g_buffer.inputUI.payloadLength = settings.isFixedLength ? settings.payloadLength : -1;
}
}
// protocol
ImGui::Text("%s", "");
{
@@ -887,6 +936,12 @@ void renderMain() {
ImGui::SetCursorScreenPos({ posSave.x + kLabelWidth, posSave.y });
ImGui::TextDisabled("[U] = ultrasound");
}
{
auto posSave = ImGui::GetCursorScreenPos();
ImGui::Text("%s", "");
ImGui::SetCursorScreenPos({ posSave.x + kLabelWidth, posSave.y });
ImGui::TextDisabled("[DT] = dual-tone");
}
{
auto posSave = ImGui::GetCursorScreenPos();
ImGui::Text("Tx Protocol: ");
@@ -980,7 +1035,7 @@ void renderMain() {
{
auto col = ImVec4 { 1.0f, 0.0f, 0.0f, 1.0f };
col.w = interp;
ImGui::TextColored(col, "Failed to received");
ImGui::TextColored(col, "Failed to receive");
}
break;
case Message::Text:

View File

@@ -310,6 +310,7 @@ int main(int argc, char** argv) {
SDL_GL_DeleteContext(gl_context);
SDL_DestroyWindow(window);
SDL_CloseAudio();
SDL_Quit();
#endif