mirror of
https://github.com/ggerganov/ggwave.git
synced 2026-03-28 06:59:01 +08:00
ggwave-from-file : add example to decode messages from a WAV file
This commit is contained in:
@@ -86,6 +86,7 @@ if (EMSCRIPTEN)
|
||||
add_subdirectory(buttons)
|
||||
else()
|
||||
add_subdirectory(ggwave-to-file)
|
||||
add_subdirectory(ggwave-from-file)
|
||||
|
||||
add_subdirectory(arduino-rx)
|
||||
add_subdirectory(arduino-tx)
|
||||
|
||||
@@ -18,17 +18,17 @@ int main(int argc, char** argv) {
|
||||
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(" -s - use Direct Sequence Spread (DSS)\n");
|
||||
printf(" -d - use Direct Sequence Spread (DSS)\n");
|
||||
printf(" -v - print generated tones on resend\n");
|
||||
printf("\n");
|
||||
|
||||
const auto argm = parseCmdArguments(argc, argv);
|
||||
const int captureId = argm.count("c") == 0 ? 0 : std::stoi(argm.at("c"));
|
||||
const int playbackId = argm.count("p") == 0 ? 0 : std::stoi(argm.at("p"));
|
||||
const int txProtocolId = argm.count("t") == 0 ? 1 : std::stoi(argm.at("t"));
|
||||
const int payloadLength = argm.count("l") == 0 ? -1 : std::stoi(argm.at("l"));
|
||||
const bool useDSS = argm.count("s") > 0;
|
||||
const bool printTones = argm.count("v") > 0;
|
||||
const auto argm = parseCmdArguments(argc, argv);
|
||||
const int captureId = argm.count("c") == 0 ? 0 : std::stoi(argm.at("c"));
|
||||
const int playbackId = argm.count("p") == 0 ? 0 : std::stoi(argm.at("p"));
|
||||
const int txProtocolId = argm.count("t") == 0 ? 1 : std::stoi(argm.at("t"));
|
||||
const int payloadLength = argm.count("l") == 0 ? -1 : std::stoi(argm.at("l"));
|
||||
const bool useDSS = argm.count("d") > 0;
|
||||
const bool printTones = argm.count("v") > 0;
|
||||
|
||||
if (GGWave_init(playbackId, captureId, payloadLength, 0.0f, useDSS) == false) {
|
||||
fprintf(stderr, "Failed to initialize GGWave\n");
|
||||
|
||||
15
examples/ggwave-from-file/CMakeLists.txt
Normal file
15
examples/ggwave-from-file/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
set(TARGET ggwave-from-file)
|
||||
|
||||
add_executable(${TARGET} main.cpp)
|
||||
|
||||
target_include_directories(${TARGET} PRIVATE
|
||||
..
|
||||
)
|
||||
|
||||
target_link_libraries(${TARGET} PRIVATE
|
||||
ggwave
|
||||
ggwave-common
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
install(TARGETS ${TARGET} RUNTIME DESTINATION bin)
|
||||
39
examples/ggwave-from-file/README.md
Normal file
39
examples/ggwave-from-file/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
## ggwave-from-file
|
||||
|
||||
Decode GGWave messages from an input WAV file
|
||||
|
||||
```
|
||||
Usage: ./bin/ggwave-from-file [-lN] [-d]
|
||||
-lN - fixed payload length of size N, N in [1, 64]
|
||||
-d - use Direct Sequence Spread (DSS)
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
- Basic usage with auto-detection of frequency and speed:
|
||||
|
||||
```bash
|
||||
echo "Hello world" | ./bin/ggwave-to-file > example.wav
|
||||
./bin/ggwave-from-file example.wav
|
||||
|
||||
Usage: ./bin/ggwave-from-file audio.wav [-lN] [-d]
|
||||
-lN - fixed payload length of size N, N in [1, 64]
|
||||
-d - use Direct Sequence Spread (DSS)
|
||||
|
||||
[+] Number of channels: 1
|
||||
[+] Sample rate: 48000
|
||||
[+] Bits per sample: 16
|
||||
[+] Total samples: 69632
|
||||
[+] Decoding ..
|
||||
|
||||
[+] Decoded message with length 11: 'Hello world'
|
||||
|
||||
[+] Done
|
||||
```
|
||||
|
||||
- Decoding fixed-length payload with DSS enabled:
|
||||
|
||||
```bash
|
||||
echo "Hello world" | ./bin/ggwave-to-file -l16 -d > example.wav
|
||||
./bin/ggwave-from-file example.wav -l16 -d
|
||||
```
|
||||
130
examples/ggwave-from-file/main.cpp
Normal file
130
examples/ggwave-from-file/main.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
#include "ggwave/ggwave.h"
|
||||
|
||||
#define DR_WAV_IMPLEMENTATION
|
||||
#include "dr_wav.h"
|
||||
|
||||
#include "ggwave-common.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
fprintf(stderr, "Usage: %s audio.wav [-lN] [-d]\n", argv[0]);
|
||||
fprintf(stderr, " -lN - fixed payload length of size N, N in [1, %d]\n", GGWave::kMaxLengthFixed);
|
||||
fprintf(stderr, " -d - use Direct Sequence Spread (DSS)\n");
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
if (argc < 2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto argm = parseCmdArguments(argc, argv);
|
||||
|
||||
if (argm.count("h") > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int payloadLength = argm.count("l") == 0 ? -1 : std::stoi(argm.at("l"));
|
||||
const bool useDSS = argm.count("d") > 0;
|
||||
|
||||
drwav wav;
|
||||
if (!drwav_init_file(&wav, argv[1], nullptr)) {
|
||||
fprintf(stderr, "Failed to open WAV file\n");
|
||||
return -4;
|
||||
}
|
||||
|
||||
if (wav.channels != 1) {
|
||||
fprintf(stderr, "Only mono WAV files are supported\n");
|
||||
return -5;
|
||||
}
|
||||
|
||||
// Read WAV samples into a buffer
|
||||
// Add 3 seconds of silence at the end
|
||||
const size_t samplesSilence = 3*wav.sampleRate;
|
||||
const size_t samplesCount = wav.totalPCMFrameCount;
|
||||
const size_t samplesSize = wav.bitsPerSample/8;
|
||||
size_t samplesTotal = samplesCount + samplesSilence;
|
||||
std::vector<uint8_t> samples(samplesTotal*samplesSize*wav.channels, 0);
|
||||
|
||||
printf("[+] Number of channels: %d\n", wav.channels);
|
||||
printf("[+] Sample rate: %d\n", wav.sampleRate);
|
||||
printf("[+] Bits per sample: %d\n", wav.bitsPerSample);
|
||||
printf("[+] Total samples: %zu\n", samplesCount);
|
||||
|
||||
printf("[+] Decoding .. \n\n");
|
||||
|
||||
GGWave::Parameters parameters = GGWave::getDefaultParameters();
|
||||
|
||||
parameters.payloadLength = payloadLength;
|
||||
parameters.sampleRateInp = wav.sampleRate;
|
||||
parameters.operatingMode = GGWAVE_OPERATING_MODE_RX;
|
||||
if (useDSS) parameters.operatingMode |= GGWAVE_OPERATING_MODE_USE_DSS;
|
||||
|
||||
switch (wav.bitsPerSample) {
|
||||
case 16:
|
||||
drwav_read_pcm_frames_s16(&wav, samplesCount, reinterpret_cast<int16_t*>(samples.data()));
|
||||
|
||||
if (wav.channels > 1) {
|
||||
for (size_t i = 0; i < samplesCount; ++i) {
|
||||
int16_t sample = 0;
|
||||
for (size_t j = 0; j < wav.channels; ++j) {
|
||||
sample += reinterpret_cast<int16_t*>(samples.data())[i*wav.channels + j];
|
||||
}
|
||||
reinterpret_cast<int16_t*>(samples.data())[i] = sample / wav.channels;
|
||||
}
|
||||
}
|
||||
|
||||
parameters.sampleFormatInp = GGWAVE_SAMPLE_FORMAT_I16;
|
||||
|
||||
break;
|
||||
case 32:
|
||||
drwav_read_pcm_frames_f32(&wav, samplesCount, reinterpret_cast<float*>(samples.data()));
|
||||
|
||||
if (wav.channels > 1) {
|
||||
for (size_t i = 0; i < samplesCount; ++i) {
|
||||
float sample = 0.0f;
|
||||
for (size_t j = 0; j < wav.channels; ++j) {
|
||||
sample += reinterpret_cast<float*>(samples.data())[i*wav.channels + j];
|
||||
}
|
||||
reinterpret_cast<float*>(samples.data())[i] = sample / wav.channels;
|
||||
}
|
||||
}
|
||||
|
||||
parameters.sampleFormatInp = GGWAVE_SAMPLE_FORMAT_F32;
|
||||
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unsupported WAV format\n");
|
||||
return -6;
|
||||
}
|
||||
|
||||
GGWave ggWave(parameters);
|
||||
ggWave.setLogFile(nullptr);
|
||||
|
||||
GGWave::TxRxData data;
|
||||
auto ptr = samples.data();
|
||||
while ((int) samplesTotal >= parameters.samplesPerFrame) {
|
||||
if (ggWave.decode(ptr, parameters.samplesPerFrame*samplesSize*wav.channels) == false) {
|
||||
fprintf(stderr, "Failed to decode the waveform in the WAV file\n");
|
||||
return -7;
|
||||
}
|
||||
|
||||
ptr += parameters.samplesPerFrame*samplesSize*wav.channels;
|
||||
samplesTotal -= parameters.samplesPerFrame;
|
||||
|
||||
const int n = ggWave.rxTakeData(data);
|
||||
if (n > 0) {
|
||||
printf("[+] Decoded message with length %d: '", n);
|
||||
for (auto i = 0; i < n; ++i) {
|
||||
printf("%c", data[i]);
|
||||
}
|
||||
printf("'\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
printf("\n[+] Done\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3,22 +3,26 @@
|
||||
Output a generated waveform to an uncompressed WAV file.
|
||||
|
||||
```
|
||||
Usage: ./bin/ggwave-to-file [-vN] [-sN] [-pN] [-lN]
|
||||
Usage: ./bin/ggwave-to-file [-vN] [-sN] [-pN] [-lN] [-d]
|
||||
-vN - output volume, N in (0, 100], (default: 50)
|
||||
-sN - output sample rate, N in [6000, 96000], (default: 48000)
|
||||
-pN - select the transmission protocol id (default: 1)
|
||||
-lN - fixed payload length of size N, N in [1, 16]
|
||||
-d - use Direct Sequence Spread (DSS)
|
||||
|
||||
Available protocols:
|
||||
0 - Normal
|
||||
1 - Fast
|
||||
2 - Fastest
|
||||
3 - [U] Normal
|
||||
4 - [U] Fast
|
||||
5 - [U] Fastest
|
||||
6 - [DT] Normal
|
||||
7 - [DT] Fast
|
||||
8 - [DT] Fastest
|
||||
0 - Normal
|
||||
1 - Fast
|
||||
2 - Fastest
|
||||
3 - [U] Normal
|
||||
4 - [U] Fast
|
||||
5 - [U] Fastest
|
||||
6 - [DT] Normal
|
||||
7 - [DT] Fast
|
||||
8 - [DT] Fastest
|
||||
9 - [MT] Normal
|
||||
10 - [MT] Fast
|
||||
11 - [MT] Fastest
|
||||
```
|
||||
|
||||
### Examples
|
||||
@@ -47,6 +51,12 @@ Usage: ./bin/ggwave-to-file [-vN] [-sN] [-pN] [-lN]
|
||||
echo "Hello world!" | ./bin/ggwave-to-file -l12 > example.wav
|
||||
```
|
||||
|
||||
- Use DSS when encoding the text
|
||||
|
||||
```bash
|
||||
echo "aaaaaaaa" | ./bin/ggwave-to-file -l8 -d > example.wav
|
||||
```
|
||||
|
||||
- Play the generated waveform directly through the speakers
|
||||
|
||||
```bash
|
||||
@@ -89,13 +99,13 @@ from typing import Dict, Union
|
||||
import requests
|
||||
import wave
|
||||
|
||||
|
||||
def ggwave(message: str,
|
||||
file: str,
|
||||
protocolId: int = 1,
|
||||
sampleRate: float = 48000,
|
||||
volume: int = 50,
|
||||
payloadLength: int = -1) -> None:
|
||||
payloadLength: int = -1,
|
||||
useDSS: int = 0) -> None:
|
||||
|
||||
url = 'https://ggwave-to-file.ggerganov.com/'
|
||||
|
||||
@@ -105,6 +115,7 @@ def ggwave(message: str,
|
||||
's': sampleRate, # output sample rate
|
||||
'v': volume, # output volume
|
||||
'l': payloadLength, # if positive - use fixed-length encoding
|
||||
'd': useDSS, # if positive - use DSS
|
||||
}
|
||||
|
||||
response = requests.get(url, params=params)
|
||||
|
||||
@@ -6,6 +6,7 @@ if (isset($_GET['s'])) { $cmd .= " -s".intval($_GET['s']); }
|
||||
if (isset($_GET['v'])) { $cmd .= " -v".intval($_GET['v']); }
|
||||
if (isset($_GET['p'])) { $cmd .= " -p".intval($_GET['p']); }
|
||||
if (isset($_GET['l'])) { $cmd .= " -l".intval($_GET['l']); }
|
||||
if (isset($_GET['d'])) { if (intval($_GET['d']) > 0) $cmd .= " -d"; }
|
||||
|
||||
$descriptorspec = array(
|
||||
0 => array("pipe", "r"),
|
||||
|
||||
@@ -2,13 +2,13 @@ from typing import Dict, Union
|
||||
import requests
|
||||
import wave
|
||||
|
||||
|
||||
def ggwave(message: str,
|
||||
file: str,
|
||||
protocolId: int = 1,
|
||||
sampleRate: float = 48000,
|
||||
volume: int = 50,
|
||||
payloadLength: int = -1) -> None:
|
||||
payloadLength: int = -1,
|
||||
useDSS: int = 0) -> None:
|
||||
|
||||
url = 'https://ggwave-to-file.ggerganov.com/'
|
||||
|
||||
@@ -18,6 +18,7 @@ def ggwave(message: str,
|
||||
's': sampleRate, # output sample rate
|
||||
'v': volume, # output volume
|
||||
'l': payloadLength, # if positive - use fixed-length encoding
|
||||
'd': useDSS, # if positive - use DSS
|
||||
}
|
||||
|
||||
response = requests.get(url, params=params)
|
||||
|
||||
@@ -10,18 +10,21 @@
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
fprintf(stderr, "Usage: %s [-vN] [-sN] [-pN] [-lN]\n", argv[0]);
|
||||
fprintf(stderr, "Usage: %s [-vN] [-sN] [-pN] [-lN] [-d]\n", argv[0]);
|
||||
fprintf(stderr, " -vN - output volume, N in (0, 100], (default: 50)\n");
|
||||
fprintf(stderr, " -SN - output sample rate, N in [%d, %d], (default: %d)\n", (int) GGWave::kSampleRateMin, (int) GGWave::kSampleRateMax, (int) GGWave::kDefaultSampleRate);
|
||||
fprintf(stderr, " -sN - output sample rate, N in [%d, %d], (default: %d)\n", (int) GGWave::kSampleRateMin, (int) GGWave::kSampleRateMax, (int) GGWave::kDefaultSampleRate);
|
||||
fprintf(stderr, " -pN - select the transmission protocol id (default: 1)\n");
|
||||
fprintf(stderr, " -lN - fixed payload length of size N, N in [1, %d]\n", GGWave::kMaxLengthFixed);
|
||||
fprintf(stderr, " -s - use Direct Sequence Spread (DSS)\n");
|
||||
fprintf(stderr, " -d - use Direct Sequence Spread (DSS)\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, " Available protocols:\n");
|
||||
|
||||
const auto & protocols = GGWave::Protocols::kDefault();
|
||||
for (int i = 0; i < (int) protocols.size(); ++i) {
|
||||
const auto & protocol = protocols[i];
|
||||
if (protocol.enabled == false) {
|
||||
continue;
|
||||
}
|
||||
fprintf(stderr, " %d - %s\n", i, protocol.name);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
@@ -36,11 +39,11 @@ int main(int argc, char** argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int volume = argm.count("v") == 0 ? 50 : std::stoi(argm.at("v"));
|
||||
const float sampleRateOut = argm.count("S") == 0 ? GGWave::kDefaultSampleRate : std::stof(argm.at("S"));
|
||||
const int protocolId = argm.count("p") == 0 ? 1 : std::stoi(argm.at("p"));
|
||||
const int payloadLength = argm.count("l") == 0 ? -1 : std::stoi(argm.at("l"));
|
||||
const bool useDSS = argm.count("s") > 0;
|
||||
const int volume = argm.count("v") == 0 ? 50 : std::stoi(argm.at("v"));
|
||||
const float sampleRateOut = argm.count("s") == 0 ? GGWave::kDefaultSampleRate : std::stof(argm.at("s"));
|
||||
const int protocolId = argm.count("p") == 0 ? 1 : std::stoi(argm.at("p"));
|
||||
const int payloadLength = argm.count("l") == 0 ? -1 : std::stoi(argm.at("l"));
|
||||
const bool useDSS = argm.count("d") > 0;
|
||||
|
||||
if (volume <= 0 || volume > 100) {
|
||||
fprintf(stderr, "Invalid volume\n");
|
||||
|
||||
Reference in New Issue
Block a user