mirror of
https://github.com/ggerganov/ggwave.git
synced 2026-02-06 08:37:59 +08:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -310,6 +310,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
SDL_GL_DeleteContext(gl_context);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_CloseAudio();
|
||||
SDL_Quit();
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user