ggwave : switch to floating point sampling rate

This commit is contained in:
Georgi Gerganov
2021-02-27 12:23:25 +02:00
parent 5b5293d764
commit 1c59e38696
10 changed files with 35 additions and 61 deletions

View File

@@ -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

View File

@@ -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,

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, 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();

View File

@@ -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/'

View File

@@ -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/'

View File

@@ -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;
}

View File

@@ -42,7 +42,7 @@ int g_freqDataHead = 0;
int g_freqDataSize = 0;
std::vector<FreqData> 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);
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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) {