-
Notifications
You must be signed in to change notification settings - Fork 139
/
ggwave.h
511 lines (437 loc) · 18.5 KB
/
ggwave.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
#ifndef GGWAVE_H
#define GGWAVE_H
#ifdef GGWAVE_SHARED
# ifdef _WIN32
# ifdef GGWAVE_BUILD
# define GGWAVE_API __declspec(dllexport)
# else
# define GGWAVE_API __declspec(dllimport)
# endif
# else
# define GGWAVE_API __attribute__ ((visibility ("default")))
# endif
#else
# define GGWAVE_API
#endif
#ifdef __cplusplus
extern "C" {
#endif
//
// C interface
//
// Data format of the audio samples
typedef enum {
GGWAVE_SAMPLE_FORMAT_UNDEFINED,
GGWAVE_SAMPLE_FORMAT_U8,
GGWAVE_SAMPLE_FORMAT_I8,
GGWAVE_SAMPLE_FORMAT_U16,
GGWAVE_SAMPLE_FORMAT_I16,
GGWAVE_SAMPLE_FORMAT_F32,
} ggwave_SampleFormat;
// TxProtocol ids
typedef enum {
GGWAVE_TX_PROTOCOL_AUDIBLE_NORMAL,
GGWAVE_TX_PROTOCOL_AUDIBLE_FAST,
GGWAVE_TX_PROTOCOL_AUDIBLE_FASTEST,
GGWAVE_TX_PROTOCOL_ULTRASOUND_NORMAL,
GGWAVE_TX_PROTOCOL_ULTRASOUND_FAST,
GGWAVE_TX_PROTOCOL_ULTRASOUND_FASTEST,
GGWAVE_TX_PROTOCOL_DT_NORMAL,
GGWAVE_TX_PROTOCOL_DT_FAST,
GGWAVE_TX_PROTOCOL_DT_FASTEST,
GGWAVE_TX_PROTOCOL_CUSTOM_0,
GGWAVE_TX_PROTOCOL_CUSTOM_1,
GGWAVE_TX_PROTOCOL_CUSTOM_2,
GGWAVE_TX_PROTOCOL_CUSTOM_3,
GGWAVE_TX_PROTOCOL_CUSTOM_4,
GGWAVE_TX_PROTOCOL_CUSTOM_5,
GGWAVE_TX_PROTOCOL_CUSTOM_6,
GGWAVE_TX_PROTOCOL_CUSTOM_7,
GGWAVE_TX_PROTOCOL_CUSTOM_8,
GGWAVE_TX_PROTOCOL_CUSTOM_9,
} ggwave_TxProtocolId;
// GGWave instance parameters
//
// If payloadLength <= 0, then GGWave will transmit with variable payload length
// depending on the provided payload. Sound markers are used to identify the
// start and end of the transmission.
//
// If payloadLength > 0, then the transmitted payload will be of the specified
// fixed length. In this case, no sound markers are emitted and a slightly
// different decoding scheme is applied. This is useful in cases where the
// length of the payload is known in advance.
//
// The sample rates are values typically between 8000 and 96000.
// Default value: GGWave::kBaseSampleRate
//
// The samplesPerFrame is the number of samples on which ggwave performs FFT.
// This affects the number of bins in the Fourier spectrum.
// Default value: GGWave::kDefaultSamplesPerFrame
//
typedef struct {
int payloadLength; // payload length
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
ggwave_SampleFormat sampleFormatOut; // format of the playback audio samples
} ggwave_Parameters;
// GGWave instances are identified with an integer and are stored
// in a private map container. Using void * caused some issues with
// the python module and unfortunately had to do it this way
typedef int ggwave_Instance;
// Helper method to get default instance parameters
GGWAVE_API ggwave_Parameters ggwave_getDefaultParameters(void);
// Create a new GGWave instance with the specified parameters
//
// The newly created instance is added to the internal map container.
// This function returns an id that can be used to identify this instance.
// Make sure to deallocate the instance at the end by calling ggwave_free()
//
GGWAVE_API ggwave_Instance ggwave_init(const ggwave_Parameters parameters);
// Free a GGWave instance
GGWAVE_API void ggwave_free(ggwave_Instance instance);
// Encode data into audio waveform
//
// instance - the GGWave instance to use
// dataBuffer - the data to encode
// dataSize - number of bytes in the input dataBuffer
// txProtocolId - the protocol to use for encoding
// volume - the volume of the generated waveform [0, 100]
// usually 25 is OK and you should not go over 50
// outputBuffer - the generated audio waveform. must be big enough to fit the generated data
// query - if != 0, do not perform encoding.
// if == 1, return waveform size in bytes
// if != 1, return waveform size in samples
//
// returns the number of generated bytes or samples (see query)
//
// returns -1 if there was an error
//
// This function can be used to encode some binary data (payload) into an audio waveform.
//
// payload -> waveform
//
// When calling it, make sure that the outputBuffer is big enough to store the
// generated waveform. This means that its size must be at least:
//
// nSamples*sizeOfSample_bytes
//
// Where nSamples is the number of audio samples in the waveform and sizeOfSample_bytes
// is the size of a single sample in bytes based on the sampleFormatOut parameter
// specified during the initialization of the GGWave instance.
//
// If query != 0, then this function does not perform the actual encoding and just
// outputs the expected size of the waveform that would be generated if you call it
// with query == 0. This mechanism can be used to ask ggwave how much memory to
// allocate for the outputBuffer. For example:
//
// // this is the data to encode
// const char * payload = "test";
//
// // query the number of bytes in the waveform
// int n = ggwave_encode(instance, payload, 4, GGWAVE_TX_PROTOCOL_AUDIBLE_FAST, 25, NULL, 1);
//
// // allocate the output buffer
// char waveform[n];
//
// // generate the waveform
// ggwave_encode(instance, payload, 4, GGWAVE_TX_PROTOCOL_AUDIBLE_FAST, 25, waveform, 0);
//
// The dataBuffer can be any binary data that you would like to transmit (i.e. the payload).
// Usually, this is some text, but it can be any sequence of bytes.
//
// todo:
// - change the type of dataBuffer to const void *
// - change the type of outputBuffer to void *
// - rename dataBuffer to payloadBuffer
// - rename dataSize to payloadSize
// - rename outputBuffer to waveformBuffer
//
GGWAVE_API int ggwave_encode(
ggwave_Instance instance,
const char * dataBuffer,
int dataSize,
ggwave_TxProtocolId txProtocolId,
int volume,
char * outputBuffer,
int query);
// Decode an audio waveform into data
//
// instance - the GGWave instance to use
// dataBuffer - the audio waveform
// dataSize - number of bytes in the input dataBuffer
// outputBuffer - stores the decoded data on success
// the maximum size of the output is GGWave::kMaxDataSize
//
// returns the number of decoded bytes
//
// Use this function to continuously provide audio samples to a GGWave instance.
// On each call, GGWave will analyze the provided data and if it detects a payload,
// it will return a non-zero result.
//
// waveform -> payload
//
// If the return value is -1 then there was an error during the decoding process.
// Usually can occur if there is a lot of background noise in the audio.
//
// If the return value is greater than 0, then there will be that number of bytes
// decoded in the outputBuffer
//
// Example:
//
// char payload[256];
//
// while (true) {
// ... capture samplesPerFrame audio samples into waveform ...
//
// int ret = ggwave_decode(instance, waveform, samplesPerFrame*sizeOfSample_bytes, payload);
// if (ret > 0) {
// printf("Received payload: '%s'\n", payload);
// }
// }
//
// todo:
// - change the type of dataBuffer to const void *
// - change the type of outputBuffer to void *
// - rename dataBuffer to waveformBuffer
// - rename dataSize to waveformSize
// - rename outputBuffer to payloadBuffer
//
GGWAVE_API int ggwave_decode(
ggwave_Instance instance,
const char * dataBuffer,
int dataSize,
char * outputBuffer);
#ifdef __cplusplus
}
//
// C++ interface
//
#include <cstdint>
#include <functional>
#include <vector>
#include <map>
#include <string>
#include <memory>
class GGWave {
public:
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;
static constexpr auto kDefaultMarkerFrames = 16;
static constexpr auto kDefaultEncodedDataOffset = 3;
static constexpr auto kMaxSamplesPerFrame = 2048;
static constexpr auto kMaxDataBits = 256;
static constexpr auto kMaxDataSize = 256;
static constexpr auto kMaxLengthVarible = 140;
static constexpr auto kMaxLengthFixed = 16;
static constexpr auto kMaxSpectrumHistory = 4;
static constexpr auto kMaxRecordedFrames = 2048;
using Parameters = ggwave_Parameters;
using SampleFormat = ggwave_SampleFormat;
using TxProtocolId = ggwave_TxProtocolId;
using RxProtocolId = ggwave_TxProtocolId;
struct TxProtocol {
const char * name; // string identifier of the protocol
int freqStart; // FFT bin index of the lowest frequency
int framesPerTx; // number of frames to transmit a single chunk of data
int bytesPerTx; // number of bytes in a chunk of data
int nDataBitsPerTx() const { return 8*bytesPerTx; }
};
using RxProtocol = TxProtocol;
using TxProtocols = std::map<TxProtocolId, TxProtocol>;
using RxProtocols = std::map<RxProtocolId, RxProtocol>;
static const TxProtocols & getTxProtocols() {
static const TxProtocols kTxProtocols {
{ GGWAVE_TX_PROTOCOL_AUDIBLE_NORMAL, { "Normal", 40, 9, 3, } },
{ GGWAVE_TX_PROTOCOL_AUDIBLE_FAST, { "Fast", 40, 6, 3, } },
{ GGWAVE_TX_PROTOCOL_AUDIBLE_FASTEST, { "Fastest", 40, 3, 3, } },
{ GGWAVE_TX_PROTOCOL_ULTRASOUND_NORMAL, { "[U] Normal", 320, 9, 3, } },
{ GGWAVE_TX_PROTOCOL_ULTRASOUND_FAST, { "[U] Fast", 320, 6, 3, } },
{ GGWAVE_TX_PROTOCOL_ULTRASOUND_FASTEST, { "[U] Fastest", 320, 3, 3, } },
{ GGWAVE_TX_PROTOCOL_DT_NORMAL, { "[DT] Normal", 24, 9, 1, } },
{ GGWAVE_TX_PROTOCOL_DT_FAST, { "[DT] Fast", 24, 6, 1, } },
{ GGWAVE_TX_PROTOCOL_DT_FASTEST, { "[DT] Fastest", 24, 3, 1, } },
};
return kTxProtocols;
}
struct ToneData {
double freq_hz;
double duration_ms;
};
using Tones = std::vector<ToneData>;
using WaveformTones = std::vector<Tones>;
using AmplitudeData = std::vector<float>;
using AmplitudeDataI16 = std::vector<int16_t>;
using SpectrumData = std::vector<float>;
using RecordedData = std::vector<float>;
using TxRxData = std::vector<std::uint8_t>;
using CBWaveformOut = std::function<void(const void * data, uint32_t nBytes)>;
using CBWaveformInp = std::function<uint32_t(void * data, uint32_t nMaxBytes)>;
GGWave(const Parameters & parameters);
~GGWave();
static const Parameters & getDefaultParameters();
// set Tx data to encode
//
// This prepares the GGWave instance for transmission.
// To perform the actual encoding, the encode() method must be called
//
// returns false upon invalid parameters or failure to initialize
//
bool init(const std::string & text, const int volume = kDefaultVolume);
bool init(const std::string & text, const TxProtocol & txProtocol, const int volume = kDefaultVolume);
bool init(int dataSize, const char * dataBuffer, const int volume = kDefaultVolume);
bool init(int dataSize, const char * dataBuffer, const TxProtocol & txProtocol, const int volume = kDefaultVolume);
// expected waveform size of the encoded Tx data in bytes
//
// When the output sampling rate is not equal to kBaseSampleRate the result of this method is overestimation of
// the actual number of bytes that would be produced
//
uint32_t encodeSize_bytes() const;
// expected waveform size of the encoded Tx data in samples
//
// When the output sampling rate is not equal to kBaseSampleRate the result of this method is overestimation of
// the actual number of samples that would be produced
//
uint32_t encodeSize_samples() const;
// encode Tx data into an audio waveform
//
// The generated waveform is returned by calling the cbWaveformOut callback.
//
// returns false if the encoding fails
//
bool encode(const CBWaveformOut & cbWaveformOut);
// decode an audio waveform
//
// This methods calls cbWaveformInp multiple times (at least once) until it returns 0.
// Use the Rx methods to check if any data was decoded successfully.
//
void decode(const CBWaveformInp & cbWaveformInp);
// instance state
const bool & hasTxData() const { return m_hasNewTxData; }
const bool & isReceiving() const { return m_receivingData; }
const bool & isAnalyzing() const { return m_analyzingData; }
const int & getFramesToRecord() const { return m_framesToRecord; }
const int & getFramesLeftToRecord() const { return m_framesLeftToRecord; }
const int & getFramesToAnalyze() const { return m_framesToAnalyze; }
const int & getFramesLeftToAnalyze() const { return m_framesLeftToAnalyze; }
const int & getSamplesPerFrame() const { return m_samplesPerFrame; }
const int & getSampleSizeBytesInp() const { return m_sampleSizeBytesInp; }
const int & getSampleSizeBytesOut() const { return m_sampleSizeBytesOut; }
const float & getSampleRateInp() const { return m_sampleRateInp; }
const float & getSampleRateOut() const { return m_sampleRateOut; }
const SampleFormat & getSampleFormatInp() const { return m_sampleFormatInp; }
const SampleFormat & getSampleFormatOut() const { return m_sampleFormatOut; }
// Tx
static TxProtocolId getDefaultTxProtocolId() { return GGWAVE_TX_PROTOCOL_AUDIBLE_FAST; }
static const TxProtocol & getDefaultTxProtocol() { return getTxProtocols().at(getDefaultTxProtocolId()); }
static const TxProtocol & getTxProtocol(int id) { return getTxProtocols().at(TxProtocolId(id)); }
static const TxProtocol & getTxProtocol(TxProtocolId id) { return getTxProtocols().at(id); }
// get a list of the tones generated for the last waveform
//
// Call this method after calling encode() to get a list of the tones participating in the generated waveform
//
const WaveformTones & getWaveformTones() { return m_waveformTones; }
bool takeTxAmplitudeI16(AmplitudeDataI16 & dst);
// Rx
bool stopReceiving();
void setRxProtocols(const RxProtocols & rxProtocols) { m_rxProtocols = rxProtocols; }
const RxProtocols & getRxProtocols() const { return m_rxProtocols; }
const TxRxData & getRxData() const { return m_rxData; }
const RxProtocol & getRxProtocol() const { return m_rxProtocol; }
const RxProtocolId & getRxProtocolId() const { return m_rxProtocolId; }
int takeRxData(TxRxData & dst);
bool takeRxSpectrum(SpectrumData & dst);
bool takeRxAmplitude(AmplitudeData & dst);
// compute FFT of real values
//
// src - input real-valued data, size is N
// dst - output complex-valued data, size is 2*N
//
// d is scaling factor
// N must be <= kMaxSamplesPerFrame
//
static bool computeFFTR(const float * src, float * dst, int N, float d);
private:
void decode_fixed();
void decode_variable();
int maxFramesPerTx() const;
int minBytesPerTx() const;
double bitFreq(const TxProtocol & p, int bit) const {
return m_hzPerSample*p.freqStart + m_freqDelta_hz*bit;
}
const float m_sampleRateInp;
const float m_sampleRateOut;
const int m_samplesPerFrame;
const float m_isamplesPerFrame;
const int m_sampleSizeBytesInp;
const int m_sampleSizeBytesOut;
const SampleFormat m_sampleFormatInp;
const SampleFormat m_sampleFormatOut;
const float m_hzPerSample;
const float m_ihzPerSample;
const int m_freqDelta_bin;
const float m_freqDelta_hz;
const int m_nBitsInMarker;
const int m_nMarkerFrames;
const int m_encodedDataOffset;
const float m_soundMarkerThreshold;
// common
bool m_isFixedPayloadLength;
int m_payloadLength;
// Rx
bool m_receivingData;
bool m_analyzingData;
int m_nMarkersSuccess;
int m_markerFreqStart;
int m_recvDuration_frames;
int m_framesLeftToAnalyze;
int m_framesLeftToRecord;
int m_framesToAnalyze;
int m_framesToRecord;
int m_samplesNeeded;
std::vector<float> m_fftInp; // real
std::vector<float> m_fftOut; // complex
bool m_hasNewSpectrum;
bool m_hasNewAmplitude;
SpectrumData m_sampleSpectrum;
AmplitudeData m_sampleAmplitude;
AmplitudeData m_sampleAmplitudeResampled;
TxRxData m_sampleAmplitudeTmp;
bool m_hasNewRxData;
int m_lastRxDataLength;
TxRxData m_rxData;
TxProtocol m_rxProtocol;
TxProtocolId m_rxProtocolId;
TxProtocols m_rxProtocols;
int m_historyId;
AmplitudeData m_sampleAmplitudeAverage;
std::vector<AmplitudeData> m_sampleAmplitudeHistory;
RecordedData m_recordedAmplitude;
int m_historyIdFixed;
std::vector<SpectrumData> m_spectrumHistoryFixed;
// Tx
bool m_hasNewTxData;
float m_sendVolume;
int m_txDataLength;
TxRxData m_txData;
TxRxData m_txDataEncoded;
TxProtocol m_txProtocol;
AmplitudeData m_outputBlock;
AmplitudeData m_outputBlockResampled;
TxRxData m_outputBlockTmp;
AmplitudeDataI16 m_outputBlockI16;
AmplitudeDataI16 m_txAmplitudeDataI16;
WaveformTones m_waveformTones;
// Impl
// todo : move all members inside Impl
struct Impl;
std::unique_ptr<Impl> m_impl;
};
#endif
#endif