mirror of
https://github.com/ggerganov/ggwave.git
synced 2026-02-06 16:47:59 +08:00
ggwave : switch to floating point sampling rate
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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/'
|
||||
|
||||
|
||||
@@ -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/'
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user