From 1c59e386963e670cd43acc8a95555eb332a42e69 Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Sat, 27 Feb 2021 12:23:25 +0200 Subject: [PATCH] ggwave : switch to floating point sampling rate --- bindings/python/cggwave.pxd | 4 +-- examples/ggwave-common-sdl2.cpp | 8 +++--- examples/ggwave-common-sdl2.h | 2 +- examples/ggwave-to-file/README.md | 8 +++--- examples/ggwave-to-file/ggwave-to-file.py | 2 +- examples/ggwave-to-file/main.cpp | 6 ++--- examples/spectrogram/main.cpp | 32 ++--------------------- examples/waver/common.cpp | 12 ++++----- include/ggwave/ggwave.h | 8 +++--- src/ggwave.cpp | 14 +++++----- 10 files changed, 35 insertions(+), 61 deletions(-) diff --git a/bindings/python/cggwave.pxd b/bindings/python/cggwave.pxd index 2cb13ac..036cb0f 100644 --- a/bindings/python/cggwave.pxd +++ b/bindings/python/cggwave.pxd @@ -18,8 +18,8 @@ cdef extern from "ggwave.h" nogil: ctypedef struct ggwave_Parameters: int payloadLength - int sampleRateInp - int sampleRateOut + float sampleRateInp + float sampleRateOut int samplesPerFrame float soundMarkerThreshold ggwave_SampleFormat sampleFormatInp diff --git a/examples/ggwave-common-sdl2.cpp b/examples/ggwave-common-sdl2.cpp index 69d5ef8..458c16d 100644 --- a/examples/ggwave-common-sdl2.cpp +++ b/examples/ggwave-common-sdl2.cpp @@ -45,7 +45,7 @@ extern "C" { } EMSCRIPTEN_KEEPALIVE - int getSampleRate() { return g_ggWave->getSampleRateInp(); } + float getSampleRate() { return g_ggWave->getSampleRateInp(); } EMSCRIPTEN_KEEPALIVE int getFramesToRecord() { return g_ggWave->getFramesToRecord(); } @@ -79,7 +79,7 @@ bool GGWave_init( const int playbackId, const int captureId, const int payloadLength, - const int sampleRateOffset) { + const float sampleRateOffset) { if (g_devIdInp && g_devIdOut) { return false; @@ -217,8 +217,8 @@ bool GGWave_init( g_ggWave = new GGWave({ payloadLength, - g_obtainedSpecInp.freq, - g_obtainedSpecOut.freq, + (float) g_obtainedSpecInp.freq, + (float) g_obtainedSpecOut.freq, GGWave::kDefaultSamplesPerFrame, GGWave::kDefaultSoundMarkerThreshold, sampleFormatInp, diff --git a/examples/ggwave-common-sdl2.h b/examples/ggwave-common-sdl2.h index a2ad2e6..d7d7ad4 100644 --- a/examples/ggwave-common-sdl2.h +++ b/examples/ggwave-common-sdl2.h @@ -7,7 +7,7 @@ class GGWave; // GGWave helpers void GGWave_setDefaultCaptureDeviceName(std::string name); -bool GGWave_init(const int playbackId, const int captureId, const int payloadLength = -1, const int sampleRateOffset = 0); +bool GGWave_init(const int playbackId, const int captureId, const int payloadLength = -1, const float sampleRateOffset = 0); GGWave *& GGWave_instance(); bool GGWave_mainLoop(); bool GGWave_deinit(); diff --git a/examples/ggwave-to-file/README.md b/examples/ggwave-to-file/README.md index 8f5b041..9309095 100644 --- a/examples/ggwave-to-file/README.md +++ b/examples/ggwave-to-file/README.md @@ -5,7 +5,7 @@ Output a generated waveform to an uncompressed WAV file. ``` Usage: ./bin/ggwave-to-file [-vN] [-sN] [-pN] -vN - output volume, N in (0, 100], (default: 50) - -sN - output sample rate, N in [1024, 48000], (default: 48000) + -sN - output sample rate, N in [6000, 96000], (default: 48000) -pN - select the transmission protocol (default: 1) Available protocols: @@ -59,11 +59,11 @@ curl -sS 'https://ggwave-to-file.ggerganov.com/?m=Hello world!&p=4' --output hel ### browser - Audible example - + https://ggwave-to-file.ggerganov.com/?m=Hello%20world%21 - Ultrasound example - + https://ggwave-to-file.ggerganov.com/?m=Hello%20world%21&p=4 @@ -72,7 +72,7 @@ curl -sS 'https://ggwave-to-file.ggerganov.com/?m=Hello world!&p=4' --output hel ```python import requests -def ggwave(message: str, protocolId: int = 1, sampleRate: int = 48000, volume: int = 50): +def ggwave(message: str, protocolId: int = 1, sampleRate: float = 48000, volume: int = 50): url = 'https://ggwave-to-file.ggerganov.com/' diff --git a/examples/ggwave-to-file/ggwave-to-file.py b/examples/ggwave-to-file/ggwave-to-file.py index 32442f5..446d1c7 100644 --- a/examples/ggwave-to-file/ggwave-to-file.py +++ b/examples/ggwave-to-file/ggwave-to-file.py @@ -1,7 +1,7 @@ import sys import requests -def ggwave(message: str, protocolId: int = 1, sampleRate: int = 48000, volume: int = 50): +def ggwave(message: str, protocolId: int = 1, sampleRate: float = 48000, volume: int = 50): url = 'https://ggwave-to-file.ggerganov.com/' diff --git a/examples/ggwave-to-file/main.cpp b/examples/ggwave-to-file/main.cpp index 137c23f..fe64259 100644 --- a/examples/ggwave-to-file/main.cpp +++ b/examples/ggwave-to-file/main.cpp @@ -34,7 +34,7 @@ int main(int argc, char** argv) { } int volume = argm["v"].empty() ? 50 : std::stoi(argm["v"]); - int sampleRateOut = argm["s"].empty() ? GGWave::kBaseSampleRate : std::stoi(argm["s"]); + float sampleRateOut = argm["s"].empty() ? GGWave::kBaseSampleRate : std::stof(argm["s"]); int protocolId = argm["p"].empty() ? 1 : std::stoi(argm["p"]); if (volume <= 0 || volume > 100) { @@ -42,8 +42,8 @@ int main(int argc, char** argv) { return -1; } - if (sampleRateOut < 1024 || sampleRateOut > GGWave::kBaseSampleRate) { - fprintf(stderr, "Invalid sample rate\n"); + if (sampleRateOut < GGWave::kSampleRateMin || sampleRateOut > GGWave::kSampleRateMax) { + fprintf(stderr, "Invalid sample rate: %g\n", sampleRateOut); return -1; } diff --git a/examples/spectrogram/main.cpp b/examples/spectrogram/main.cpp index d1f8ac6..55ede41 100644 --- a/examples/spectrogram/main.cpp +++ b/examples/spectrogram/main.cpp @@ -42,7 +42,7 @@ int g_freqDataHead = 0; int g_freqDataSize = 0; std::vector g_freqData; -int g_sampleRateOffset = 0; +float g_sampleRateOffset = 0; } @@ -84,8 +84,6 @@ bool GGWave_init( } } - bool reinit = false; - if (g_devIdOut == 0) { printf("Initializing playback ...\n"); @@ -127,8 +125,6 @@ bool GGWave_init( return false; } - - reinit = true; } } @@ -158,33 +154,9 @@ bool GGWave_init( printf(" - Format: %d (required: %d)\n", g_obtainedSpecInp.format, captureSpec.format); printf(" - Channels: %d (required: %d)\n", g_obtainedSpecInp.channels, captureSpec.channels); printf(" - Samples per frame: %d\n", g_obtainedSpecInp.samples); - - reinit = true; } } - GGWave::SampleFormat sampleFormatInp = GGWAVE_SAMPLE_FORMAT_UNDEFINED; - GGWave::SampleFormat sampleFormatOut = GGWAVE_SAMPLE_FORMAT_UNDEFINED; - - switch (g_obtainedSpecInp.format) { - case AUDIO_U8: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_U8; break; - case AUDIO_S8: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_I8; break; - case AUDIO_U16SYS: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_U16; break; - case AUDIO_S16SYS: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_I16; break; - case AUDIO_S32SYS: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_F32; break; - case AUDIO_F32SYS: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_F32; break; - } - - switch (g_obtainedSpecOut.format) { - case AUDIO_U8: sampleFormatOut = GGWAVE_SAMPLE_FORMAT_U8; break; - case AUDIO_S8: sampleFormatOut = GGWAVE_SAMPLE_FORMAT_I8; break; - case AUDIO_U16SYS: sampleFormatOut = GGWAVE_SAMPLE_FORMAT_U16; break; - case AUDIO_S16SYS: sampleFormatOut = GGWAVE_SAMPLE_FORMAT_I16; break; - case AUDIO_S32SYS: sampleFormatOut = GGWAVE_SAMPLE_FORMAT_F32; break; - case AUDIO_F32SYS: sampleFormatOut = GGWAVE_SAMPLE_FORMAT_F32; break; - break; - } - return true; } @@ -477,7 +449,7 @@ 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)) { + if (ImGui::SliderFloat("Offset", &g_sampleRateOffset, -2048, 2048)) { GGWave_deinit(); GGWave_init(0, 0); } diff --git a/examples/waver/common.cpp b/examples/waver/common.cpp index 54365e5..488edb2 100644 --- a/examples/waver/common.cpp +++ b/examples/waver/common.cpp @@ -259,7 +259,7 @@ struct Input { Message message; // reinit - int sampleRateOffset = 0; + float sampleRateOffset = 0; int payloadLength = -1; // spectrum @@ -607,8 +607,8 @@ void updateCore() { } if (inputCurrent.flags.needReinit) { - static int sampleRateInpOld = ggWave->getSampleRateInp(); - static int sampleRateOutOld = ggWave->getSampleRateOut(); + static auto sampleRateInpOld = ggWave->getSampleRateInp(); + static auto sampleRateOutOld = ggWave->getSampleRateOut(); GGWave::SampleFormat sampleFormatInpOld = ggWave->getSampleFormatInp(); GGWave::SampleFormat sampleFormatOutOld = ggWave->getSampleFormatOut(); auto rxProtocolsOld = ggWave->getRxProtocols(); @@ -817,7 +817,7 @@ void renderMain() { struct Settings { int protocolId = GGWAVE_TX_PROTOCOL_AUDIBLE_FAST; bool isSampleRateOffset = false; - int sampleRateOffset = -512; + float sampleRateOffset = -512.0f; bool isFixedLength = false; int payloadLength = 8; float volume = 0.10f; @@ -1112,7 +1112,7 @@ void renderMain() { ImGui::SetCursorScreenPos({ posSave.x + kLabelWidth, posSave.y }); } { - const float df = float(statsCurrent.sampleRateBase)/statsCurrent.samplesPerFrame; + const float df = statsCurrent.sampleRateBase/statsCurrent.samplesPerFrame; const auto & protocol = settings.txProtocols.at(GGWave::TxProtocolId(settings.protocolId)); ImGui::Text("%6.2f Hz - %6.2f Hz", df*protocol.freqStart, df*(protocol.freqStart + 2*16*protocol.bytesPerTx)); } @@ -1173,7 +1173,7 @@ void renderMain() { if (settings.isSampleRateOffset) { ImGui::SameLine(); ImGui::PushItemWidth(0.5*ImGui::GetContentRegionAvailWidth()); - if (ImGui::SliderInt("Samples", &settings.sampleRateOffset, -1000, 1000)) { + 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; diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index 47c4d50..81a0e54 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -66,8 +66,8 @@ extern "C" { // typedef struct { int payloadLength; // payload length - int sampleRateInp; // capture sample rate - int sampleRateOut; // playback sample rate + float sampleRateInp; // capture sample rate + float sampleRateOut; // playback sample rate int samplesPerFrame; // number of samples per audio frame float soundMarkerThreshold; // sound marker detection threshold ggwave_SampleFormat sampleFormatInp; // format of the captured audio samples @@ -223,7 +223,9 @@ extern "C" { class GGWave { public: - static constexpr auto kBaseSampleRate = 48000; + static constexpr auto kBaseSampleRate = 48000.0f; + static constexpr auto kSampleRateMin = 6000.0f; + static constexpr auto kSampleRateMax = 96000.0f; static constexpr auto kDefaultSamplesPerFrame = 1024; static constexpr auto kDefaultVolume = 10; static constexpr auto kDefaultSoundMarkerThreshold = 3.0f; diff --git a/src/ggwave.cpp b/src/ggwave.cpp index 7c8ff65..270bc16 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -293,7 +293,7 @@ GGWave::GGWave(const Parameters & parameters) : m_sampleSizeBytesOut(bytesForSampleFormat(parameters.sampleFormatOut)), m_sampleFormatInp(parameters.sampleFormatInp), m_sampleFormatOut(parameters.sampleFormatOut), - m_hzPerSample(float(kBaseSampleRate)/parameters.samplesPerFrame), + m_hzPerSample(kBaseSampleRate/parameters.samplesPerFrame), m_ihzPerSample(1.0f/m_hzPerSample), m_freqDelta_bin(1), m_freqDelta_hz(2*m_hzPerSample), @@ -365,13 +365,13 @@ GGWave::GGWave(const Parameters & parameters) : throw std::runtime_error("Invalid samples per frame"); } - if (m_sampleRateInp < 0.125*kBaseSampleRate) { - fprintf(stderr, "Error: capture sample rate (%d Hz) must be >= %d Hz\n", (int) m_sampleRateInp, (int) 0.125*kBaseSampleRate); + if (m_sampleRateInp < kSampleRateMin) { + fprintf(stderr, "Error: capture sample rate (%g Hz) must be >= %g Hz\n", m_sampleRateInp, kSampleRateMin); throw std::runtime_error("Invalid capture/playback sample rate"); } - if (m_sampleRateInp > 2.0*kBaseSampleRate) { - fprintf(stderr, "Error: capture sample rate (%d Hz) must be <= %d Hz\n", (int) m_sampleRateInp, (int) 2.0*kBaseSampleRate); + if (m_sampleRateInp > kSampleRateMax) { + fprintf(stderr, "Error: capture sample rate (%g Hz) must be <= %g Hz\n", m_sampleRateInp, kSampleRateMax); throw std::runtime_error("Invalid capture/playback sample rate"); } @@ -474,7 +474,7 @@ uint32_t GGWave::encodeSize_samples() const { float factor = 1.0f; int samplesPerFrameOut = m_samplesPerFrame; if (m_sampleRateOut != kBaseSampleRate) { - factor = float(kBaseSampleRate)/m_sampleRateOut; + factor = kBaseSampleRate/m_sampleRateOut; // note : +1 extra sample in order to overestimate the buffer size samplesPerFrameOut = m_impl->resampler.resample(factor, m_samplesPerFrame, m_outputBlock.data(), nullptr) + 1; } @@ -543,7 +543,7 @@ bool GGWave::encode(const CBWaveformOut & cbWaveformOut) { RS::ReedSolomon rsData = RS::ReedSolomon(m_txDataLength, nECCBytesPerTx); rsData.Encode(m_txData.data() + 1, m_txDataEncoded.data() + m_encodedDataOffset); - float factor = float(kBaseSampleRate)/m_sampleRateOut; + float factor = kBaseSampleRate/m_sampleRateOut; uint32_t offset = 0; while (m_hasNewTxData) {