core : refactoring + bug fix

- rename callback types
- fix calculation of data frames
This commit is contained in:
Georgi Gerganov
2021-01-23 15:47:59 +02:00
parent ccb7fae08d
commit 597cc48cbb
9 changed files with 165 additions and 144 deletions

View File

@@ -67,7 +67,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (GGWAVE_ALL_WARNINGS) if (GGWAVE_ALL_WARNINGS)
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -pedantic") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
else() else()
# todo : windows # todo : windows

View File

@@ -17,10 +17,10 @@ cdef extern from "ggwave.h" nogil:
GGWAVE_TX_PROTOCOL_ULTRASOUND_FASTEST GGWAVE_TX_PROTOCOL_ULTRASOUND_FASTEST
ctypedef struct ggwave_Parameters: ctypedef struct ggwave_Parameters:
int sampleRateIn int sampleRateInp
int sampleRateOut int sampleRateOut
int samplesPerFrame int samplesPerFrame
ggwave_SampleFormat sampleFormatIn ggwave_SampleFormat sampleFormatInp
ggwave_SampleFormat sampleFormatOut ggwave_SampleFormat sampleFormatOut
ctypedef int ggwave_Instance ctypedef int ggwave_Instance

View File

@@ -19,10 +19,10 @@ namespace {
std::string g_defaultCaptureDeviceName = ""; std::string g_defaultCaptureDeviceName = "";
SDL_AudioDeviceID g_devIdIn = 0; SDL_AudioDeviceID g_devIdInp = 0;
SDL_AudioDeviceID g_devIdOut = 0; SDL_AudioDeviceID g_devIdOut = 0;
SDL_AudioSpec g_obtainedSpecIn; SDL_AudioSpec g_obtainedSpecInp;
SDL_AudioSpec g_obtainedSpecOut; SDL_AudioSpec g_obtainedSpecOut;
GGWave *g_ggWave = nullptr; GGWave *g_ggWave = nullptr;
@@ -45,7 +45,7 @@ extern "C" {
} }
EMSCRIPTEN_KEEPALIVE EMSCRIPTEN_KEEPALIVE
int getSampleRate() { return g_ggWave->getSampleRateIn(); } int getSampleRate() { return g_ggWave->getSampleRateInp(); }
EMSCRIPTEN_KEEPALIVE EMSCRIPTEN_KEEPALIVE
int getFramesToRecord() { return g_ggWave->getFramesToRecord(); } int getFramesToRecord() { return g_ggWave->getFramesToRecord(); }
@@ -63,7 +63,7 @@ extern "C" {
int hasDeviceOutput() { return g_devIdOut; } int hasDeviceOutput() { return g_devIdOut; }
EMSCRIPTEN_KEEPALIVE EMSCRIPTEN_KEEPALIVE
int hasDeviceCapture() { return g_devIdIn; } int hasDeviceCapture() { return g_devIdInp; }
EMSCRIPTEN_KEEPALIVE EMSCRIPTEN_KEEPALIVE
int doInit() { int doInit() {
@@ -79,11 +79,11 @@ bool GGWave_init(
const int playbackId, const int playbackId,
const int captureId) { const int captureId) {
if (g_devIdIn && g_devIdOut) { if (g_devIdInp && g_devIdOut) {
return false; return false;
} }
if (g_devIdIn == 0 && g_devIdOut == 0) { if (g_devIdInp == 0 && g_devIdOut == 0) {
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
if (SDL_Init(SDL_INIT_AUDIO) < 0) { if (SDL_Init(SDL_INIT_AUDIO) < 0) {
@@ -157,47 +157,47 @@ bool GGWave_init(
} }
} }
if (g_devIdIn == 0) { if (g_devIdInp == 0) {
SDL_AudioSpec captureSpec; SDL_AudioSpec captureSpec;
captureSpec = g_obtainedSpecOut; captureSpec = g_obtainedSpecOut;
captureSpec.freq = GGWave::kBaseSampleRate; captureSpec.freq = GGWave::kBaseSampleRate;
captureSpec.format = AUDIO_F32SYS; captureSpec.format = AUDIO_F32SYS;
captureSpec.samples = 4096; captureSpec.samples = 4096;
SDL_zero(g_obtainedSpecIn); SDL_zero(g_obtainedSpecInp);
if (captureId >= 0) { if (captureId >= 0) {
printf("Attempt to open capture device %d : '%s' ...\n", captureId, SDL_GetAudioDeviceName(captureId, SDL_FALSE)); printf("Attempt to open capture device %d : '%s' ...\n", captureId, SDL_GetAudioDeviceName(captureId, SDL_FALSE));
g_devIdIn = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(captureId, SDL_TRUE), SDL_TRUE, &captureSpec, &g_obtainedSpecIn, 0); g_devIdInp = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(captureId, SDL_TRUE), SDL_TRUE, &captureSpec, &g_obtainedSpecInp, 0);
} else { } else {
printf("Attempt to open default capture device ...\n"); printf("Attempt to open default capture device ...\n");
g_devIdIn = SDL_OpenAudioDevice(g_defaultCaptureDeviceName.empty() ? nullptr : g_defaultCaptureDeviceName.c_str(), g_devIdInp = SDL_OpenAudioDevice(g_defaultCaptureDeviceName.empty() ? nullptr : g_defaultCaptureDeviceName.c_str(),
SDL_TRUE, &captureSpec, &g_obtainedSpecIn, 0); SDL_TRUE, &captureSpec, &g_obtainedSpecInp, 0);
} }
if (!g_devIdIn) { if (!g_devIdInp) {
printf("Couldn't open an audio device for capture: %s!\n", SDL_GetError()); printf("Couldn't open an audio device for capture: %s!\n", SDL_GetError());
g_devIdIn = 0; g_devIdInp = 0;
} else { } else {
printf("Obtained spec for input device (SDL Id = %d):\n", g_devIdIn); printf("Obtained spec for input device (SDL Id = %d):\n", g_devIdInp);
printf(" - Sample rate: %d\n", g_obtainedSpecIn.freq); printf(" - Sample rate: %d\n", g_obtainedSpecInp.freq);
printf(" - Format: %d (required: %d)\n", g_obtainedSpecIn.format, captureSpec.format); printf(" - Format: %d (required: %d)\n", g_obtainedSpecInp.format, captureSpec.format);
printf(" - Channels: %d (required: %d)\n", g_obtainedSpecIn.channels, captureSpec.channels); printf(" - Channels: %d (required: %d)\n", g_obtainedSpecInp.channels, captureSpec.channels);
printf(" - Samples per frame: %d\n", g_obtainedSpecIn.samples); printf(" - Samples per frame: %d\n", g_obtainedSpecInp.samples);
reinit = true; reinit = true;
} }
} }
GGWave::SampleFormat sampleFormatIn = GGWAVE_SAMPLE_FORMAT_UNDEFINED; GGWave::SampleFormat sampleFormatInp = GGWAVE_SAMPLE_FORMAT_UNDEFINED;
GGWave::SampleFormat sampleFormatOut = GGWAVE_SAMPLE_FORMAT_UNDEFINED; GGWave::SampleFormat sampleFormatOut = GGWAVE_SAMPLE_FORMAT_UNDEFINED;
switch (g_obtainedSpecIn.format) { switch (g_obtainedSpecInp.format) {
case AUDIO_U8: sampleFormatIn = GGWAVE_SAMPLE_FORMAT_U8; break; case AUDIO_U8: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_U8; break;
case AUDIO_S8: sampleFormatIn = GGWAVE_SAMPLE_FORMAT_I8; break; case AUDIO_S8: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_I8; break;
case AUDIO_U16SYS: sampleFormatIn = GGWAVE_SAMPLE_FORMAT_U16; break; case AUDIO_U16SYS: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_U16; break;
case AUDIO_S16SYS: sampleFormatIn = GGWAVE_SAMPLE_FORMAT_I16; break; case AUDIO_S16SYS: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_I16; break;
case AUDIO_S32SYS: sampleFormatIn = GGWAVE_SAMPLE_FORMAT_F32; break; case AUDIO_S32SYS: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_F32; break;
case AUDIO_F32SYS: sampleFormatIn = GGWAVE_SAMPLE_FORMAT_F32; break; case AUDIO_F32SYS: sampleFormatInp = GGWAVE_SAMPLE_FORMAT_F32; break;
} }
switch (g_obtainedSpecOut.format) { switch (g_obtainedSpecOut.format) {
@@ -214,10 +214,10 @@ bool GGWave_init(
if (g_ggWave) delete g_ggWave; if (g_ggWave) delete g_ggWave;
g_ggWave = new GGWave({ g_ggWave = new GGWave({
g_obtainedSpecIn.freq, g_obtainedSpecInp.freq,
g_obtainedSpecOut.freq, g_obtainedSpecOut.freq,
GGWave::kDefaultSamplesPerFrame, GGWave::kDefaultSamplesPerFrame,
sampleFormatIn, sampleFormatInp,
sampleFormatOut}); sampleFormatOut});
} }
@@ -227,16 +227,16 @@ bool GGWave_init(
GGWave * GGWave_instance() { return g_ggWave; } GGWave * GGWave_instance() { return g_ggWave; }
bool GGWave_mainLoop() { bool GGWave_mainLoop() {
if (g_devIdIn == 0 && g_devIdOut == 0) { if (g_devIdInp == 0 && g_devIdOut == 0) {
return false; return false;
} }
static GGWave::CBEnqueueAudio cbQueueAudio = [&](const void * data, uint32_t nBytes) { static GGWave::CBWaveformOut cbQueueAudio = [&](const void * data, uint32_t nBytes) {
SDL_QueueAudio(g_devIdOut, data, nBytes); SDL_QueueAudio(g_devIdOut, data, nBytes);
}; };
static GGWave::CBDequeueAudio cbDequeueAudio = [&](void * data, uint32_t nMaxBytes) { static GGWave::CBWaveformInp cbWaveformInp = [&](void * data, uint32_t nMaxBytes) {
return SDL_DequeueAudio(g_devIdIn, data, nMaxBytes); return SDL_DequeueAudio(g_devIdInp, data, nMaxBytes);
}; };
if (g_ggWave->hasTxData() == false) { if (g_ggWave->hasTxData() == false) {
@@ -246,21 +246,21 @@ bool GGWave_mainLoop() {
auto tNow = std::chrono::high_resolution_clock::now(); auto tNow = std::chrono::high_resolution_clock::now();
if ((int) SDL_GetQueuedAudioSize(g_devIdOut) < g_ggWave->getSamplesPerFrame()*g_ggWave->getSampleSizeBytesOut()) { if ((int) SDL_GetQueuedAudioSize(g_devIdOut) < g_ggWave->getSamplesPerFrame()*g_ggWave->getSampleSizeBytesOut()) {
SDL_PauseAudioDevice(g_devIdIn, SDL_FALSE); SDL_PauseAudioDevice(g_devIdInp, SDL_FALSE);
if (::getTime_ms(tLastNoData, tNow) > 500.0f) { if (::getTime_ms(tLastNoData, tNow) > 500.0f) {
g_ggWave->decode(cbDequeueAudio); g_ggWave->decode(cbWaveformInp);
if ((int) SDL_GetQueuedAudioSize(g_devIdIn) > 32*g_ggWave->getSamplesPerFrame()*g_ggWave->getSampleSizeBytesIn()) { if ((int) SDL_GetQueuedAudioSize(g_devIdInp) > 32*g_ggWave->getSamplesPerFrame()*g_ggWave->getSampleSizeBytesInp()) {
SDL_ClearQueuedAudio(g_devIdIn); SDL_ClearQueuedAudio(g_devIdInp);
} }
} else { } else {
SDL_ClearQueuedAudio(g_devIdIn); SDL_ClearQueuedAudio(g_devIdInp);
} }
} else { } else {
tLastNoData = tNow; tLastNoData = tNow;
} }
} else { } else {
SDL_PauseAudioDevice(g_devIdOut, SDL_TRUE); SDL_PauseAudioDevice(g_devIdOut, SDL_TRUE);
SDL_PauseAudioDevice(g_devIdIn, SDL_TRUE); SDL_PauseAudioDevice(g_devIdInp, SDL_TRUE);
g_ggWave->encode(cbQueueAudio); g_ggWave->encode(cbQueueAudio);
} }
@@ -269,15 +269,15 @@ bool GGWave_mainLoop() {
} }
bool GGWave_deinit() { bool GGWave_deinit() {
if (g_devIdIn == 0 && g_devIdOut == 0) { if (g_devIdInp == 0 && g_devIdOut == 0) {
return false; return false;
} }
delete g_ggWave; delete g_ggWave;
g_ggWave = nullptr; g_ggWave = nullptr;
SDL_PauseAudioDevice(g_devIdIn, 1); SDL_PauseAudioDevice(g_devIdInp, 1);
SDL_CloseAudioDevice(g_devIdIn); SDL_CloseAudioDevice(g_devIdInp);
SDL_PauseAudioDevice(g_devIdOut, 1); SDL_PauseAudioDevice(g_devIdOut, 1);
SDL_CloseAudioDevice(g_devIdOut); SDL_CloseAudioDevice(g_devIdOut);
SDL_CloseAudio(); SDL_CloseAudio();

View File

@@ -74,12 +74,12 @@ int main(int argc, char** argv) {
ggWave.init(message.size(), message.data(), ggWave.getTxProtocol(protocolId), volume); ggWave.init(message.size(), message.data(), ggWave.getTxProtocol(protocolId), volume);
std::vector<char> bufferPCM; std::vector<char> bufferPCM;
GGWave::CBEnqueueAudio cbEnqueueAudio = [&](const void * data, uint32_t nBytes) { GGWave::CBWaveformOut cbWaveformOut = [&](const void * data, uint32_t nBytes) {
bufferPCM.resize(nBytes); bufferPCM.resize(nBytes);
std::memcpy(bufferPCM.data(), data, nBytes); std::memcpy(bufferPCM.data(), data, nBytes);
}; };
if (ggWave.encode(cbEnqueueAudio) == false) { if (ggWave.encode(cbWaveformOut) == false) {
fprintf(stderr, "Failed to generate waveform!\n"); fprintf(stderr, "Failed to generate waveform!\n");
return -4; return -4;
} }

View File

@@ -821,7 +821,7 @@ void renderMain() {
ImGui::Separator(); ImGui::Separator();
ImGui::Text("%s", ""); ImGui::Text("%s", "");
ImGui::Text("Sample rate (capture): %g, %d B/sample", g_ggWave->getSampleRateIn(), g_ggWave->getSampleSizeBytesIn()); ImGui::Text("Sample rate (capture): %g, %d B/sample", g_ggWave->getSampleRateInp(), g_ggWave->getSampleSizeBytesInp());
ImGui::Text("Sample rate (playback): %g, %d B/sample", g_ggWave->getSampleRateOut(), g_ggWave->getSampleSizeBytesOut()); ImGui::Text("Sample rate (playback): %g, %d B/sample", g_ggWave->getSampleRateOut(), g_ggWave->getSampleSizeBytesOut());
const float kLabelWidth = ImGui::CalcTextSize("Tx Protocol: ").x; const float kLabelWidth = ImGui::CalcTextSize("Tx Protocol: ").x;

View File

@@ -45,10 +45,10 @@ extern "C" {
// GGWave instance parameters // GGWave instance parameters
typedef struct { typedef struct {
int sampleRateIn; // capture sample rate int sampleRateInp; // capture sample rate
int sampleRateOut; // playback sample rate int sampleRateOut; // playback sample rate
int samplesPerFrame; // number of samples per audio frame int samplesPerFrame; // number of samples per audio frame
ggwave_SampleFormat sampleFormatIn; // format of the captured audio samples ggwave_SampleFormat sampleFormatInp; // format of the captured audio samples
ggwave_SampleFormat sampleFormatOut; // format of the playback audio samples ggwave_SampleFormat sampleFormatOut; // format of the playback audio samples
} ggwave_Parameters; } ggwave_Parameters;
@@ -173,8 +173,8 @@ public:
using RecordedData = std::vector<float>; using RecordedData = std::vector<float>;
using TxRxData = std::vector<std::uint8_t>; using TxRxData = std::vector<std::uint8_t>;
using CBEnqueueAudio = std::function<void(const void * data, uint32_t nBytes)>; using CBWaveformOut = std::function<void(const void * data, uint32_t nBytes)>;
using CBDequeueAudio = std::function<uint32_t(void * data, uint32_t nMaxBytes)>; using CBWaveformInp = std::function<uint32_t(void * data, uint32_t nMaxBytes)>;
GGWave(const Parameters & parameters); GGWave(const Parameters & parameters);
~GGWave(); ~GGWave();
@@ -186,8 +186,11 @@ public:
bool init(int dataSize, const char * dataBuffer, 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); bool init(int dataSize, const char * dataBuffer, const TxProtocol & txProtocol, const int volume = kDefaultVolume);
bool encode(const CBEnqueueAudio & cbEnqueueAudio); uint32_t encodeSize_bytes() const;
void decode(const CBDequeueAudio & cbDequeueAudio); uint32_t encodeSize_samples() const;
bool encode(const CBWaveformOut & cbWaveformOut);
void decode(const CBWaveformInp & cbWaveformInp);
const bool & hasTxData() const { return m_hasNewTxData; } const bool & hasTxData() const { return m_hasNewTxData; }
const bool & isReceiving() const { return m_receivingData; } const bool & isReceiving() const { return m_receivingData; }
@@ -198,10 +201,10 @@ public:
const int & getFramesToAnalyze() const { return m_framesToAnalyze; } const int & getFramesToAnalyze() const { return m_framesToAnalyze; }
const int & getFramesLeftToAnalyze() const { return m_framesLeftToAnalyze; } const int & getFramesLeftToAnalyze() const { return m_framesLeftToAnalyze; }
const int & getSamplesPerFrame() const { return m_samplesPerFrame; } const int & getSamplesPerFrame() const { return m_samplesPerFrame; }
const int & getSampleSizeBytesIn() const { return m_sampleSizeBytesIn; } const int & getSampleSizeBytesInp() const { return m_sampleSizeBytesInp; }
const int & getSampleSizeBytesOut() const { return m_sampleSizeBytesOut; } const int & getSampleSizeBytesOut() const { return m_sampleSizeBytesOut; }
const float & getSampleRateIn() const { return m_sampleRateIn; } const float & getSampleRateInp() const { return m_sampleRateInp; }
const float & getSampleRateOut() const { return m_sampleRateOut; } const float & getSampleRateOut() const { return m_sampleRateOut; }
static TxProtocolId getDefaultTxProtocolId() { return GGWAVE_TX_PROTOCOL_AUDIBLE_FAST; } static TxProtocolId getDefaultTxProtocolId() { return GGWAVE_TX_PROTOCOL_AUDIBLE_FAST; }
@@ -225,13 +228,13 @@ private:
return m_hzPerSample*p.freqStart + m_freqDelta_hz*bit; return m_hzPerSample*p.freqStart + m_freqDelta_hz*bit;
} }
const float m_sampleRateIn; const float m_sampleRateInp;
const float m_sampleRateOut; const float m_sampleRateOut;
const int m_samplesPerFrame; const int m_samplesPerFrame;
const float m_isamplesPerFrame; const float m_isamplesPerFrame;
const int m_sampleSizeBytesIn; const int m_sampleSizeBytesInp;
const int m_sampleSizeBytesOut; const int m_sampleSizeBytesOut;
const SampleFormat m_sampleFormatIn; const SampleFormat m_sampleFormatInp;
const SampleFormat m_sampleFormatOut; const SampleFormat m_sampleFormatOut;
const float m_hzPerSample; const float m_hzPerSample;
@@ -258,7 +261,7 @@ private:
int m_framesToRecord; int m_framesToRecord;
int m_samplesNeeded; int m_samplesNeeded;
std::vector<float> m_fftIn; // real std::vector<float> m_fftInp; // real
std::vector<float> m_fftOut; // complex std::vector<float> m_fftOut; // complex
bool m_hasNewSpectrum; bool m_hasNewSpectrum;
@@ -280,8 +283,6 @@ private:
// Tx // Tx
bool m_hasNewTxData; bool m_hasNewTxData;
int m_nECCBytesPerTx;
int m_sendDataLength;
float m_sendVolume; float m_sendVolume;
int m_txDataLength; int m_txDataLength;

View File

@@ -27,10 +27,10 @@ ggwave_Instance ggwave_init(const ggwave_Parameters parameters) {
static ggwave_Instance curId = 0; static ggwave_Instance curId = 0;
g_instances[curId] = new GGWave({ g_instances[curId] = new GGWave({
parameters.sampleRateIn, parameters.sampleRateInp,
parameters.sampleRateOut, parameters.sampleRateOut,
parameters.samplesPerFrame, parameters.samplesPerFrame,
parameters.sampleFormatIn, parameters.sampleFormatInp,
parameters.sampleFormatOut}); parameters.sampleFormatOut});
return curId++; return curId++;
@@ -64,14 +64,14 @@ int ggwave_encode(
int nSamples = 0; int nSamples = 0;
GGWave::CBEnqueueAudio cbEnqueueAudio = [&](const void * data, uint32_t nBytes) { GGWave::CBWaveformOut cbWaveformOut = [&](const void * data, uint32_t nBytes) {
char * p = (char *) data; char * p = (char *) data;
std::copy(p, p + nBytes, outputBuffer); std::copy(p, p + nBytes, outputBuffer);
nSamples = nBytes/ggWave->getSampleSizeBytesOut(); nSamples = nBytes/ggWave->getSampleSizeBytesOut();
}; };
if (ggWave->encode(cbEnqueueAudio) == false) { if (ggWave->encode(cbWaveformOut) == false) {
fprintf(stderr, "Failed to encode data - GGWave instance %d\n", instance); fprintf(stderr, "Failed to encode data - GGWave instance %d\n", instance);
return -1; return -1;
} }
@@ -87,7 +87,7 @@ int ggwave_decode(
char * outputBuffer) { char * outputBuffer) {
GGWave * ggWave = (GGWave *) g_instances[instance]; GGWave * ggWave = (GGWave *) g_instances[instance];
GGWave::CBDequeueAudio cbDequeueAudio = [&](void * data, uint32_t nMaxBytes) -> uint32_t { GGWave::CBWaveformInp cbWaveformInp = [&](void * data, uint32_t nMaxBytes) -> uint32_t {
uint32_t nCopied = std::min((uint32_t) dataSize, nMaxBytes); uint32_t nCopied = std::min((uint32_t) dataSize, nMaxBytes);
std::copy(dataBuffer, dataBuffer + nCopied, (char *) data); std::copy(dataBuffer, dataBuffer + nCopied, (char *) data);
@@ -97,7 +97,7 @@ int ggwave_decode(
return nCopied; return nCopied;
}; };
ggWave->decode(cbDequeueAudio); ggWave->decode(cbWaveformInp);
// todo : avoid allocation // todo : avoid allocation
GGWave::TxRxData rxData; GGWave::TxRxData rxData;
@@ -266,15 +266,15 @@ const GGWave::Parameters & GGWave::getDefaultParameters() {
} }
GGWave::GGWave(const Parameters & parameters) : GGWave::GGWave(const Parameters & parameters) :
m_sampleRateIn(parameters.sampleRateIn), m_sampleRateInp(parameters.sampleRateInp),
m_sampleRateOut(parameters.sampleRateOut), m_sampleRateOut(parameters.sampleRateOut),
m_samplesPerFrame(parameters.samplesPerFrame), m_samplesPerFrame(parameters.samplesPerFrame),
m_isamplesPerFrame(1.0f/m_samplesPerFrame), m_isamplesPerFrame(1.0f/m_samplesPerFrame),
m_sampleSizeBytesIn(bytesForSampleFormat(parameters.sampleFormatIn)), m_sampleSizeBytesInp(bytesForSampleFormat(parameters.sampleFormatInp)),
m_sampleSizeBytesOut(bytesForSampleFormat(parameters.sampleFormatOut)), m_sampleSizeBytesOut(bytesForSampleFormat(parameters.sampleFormatOut)),
m_sampleFormatIn(parameters.sampleFormatIn), m_sampleFormatInp(parameters.sampleFormatInp),
m_sampleFormatOut(parameters.sampleFormatOut), m_sampleFormatOut(parameters.sampleFormatOut),
m_hzPerSample(m_sampleRateIn/parameters.samplesPerFrame), m_hzPerSample(m_sampleRateInp/parameters.samplesPerFrame),
m_ihzPerSample(1.0f/m_hzPerSample), m_ihzPerSample(1.0f/m_hzPerSample),
m_freqDelta_bin(1), m_freqDelta_bin(1),
m_freqDelta_hz(2*m_hzPerSample), m_freqDelta_hz(2*m_hzPerSample),
@@ -283,12 +283,12 @@ GGWave::GGWave(const Parameters & parameters) :
m_nPostMarkerFrames(0), m_nPostMarkerFrames(0),
m_encodedDataOffset(3), m_encodedDataOffset(3),
m_samplesNeeded(m_samplesPerFrame), m_samplesNeeded(m_samplesPerFrame),
m_fftIn(kMaxSamplesPerFrame), m_fftInp(kMaxSamplesPerFrame),
m_fftOut(2*kMaxSamplesPerFrame), m_fftOut(2*kMaxSamplesPerFrame),
m_hasNewSpectrum(false), m_hasNewSpectrum(false),
m_sampleSpectrum(kMaxSamplesPerFrame), m_sampleSpectrum(kMaxSamplesPerFrame),
m_sampleAmplitude(kMaxSamplesPerFrame), m_sampleAmplitude(kMaxSamplesPerFrame),
m_sampleAmplitudeTmp(kMaxSamplesPerFrame*m_sampleSizeBytesIn), m_sampleAmplitudeTmp(kMaxSamplesPerFrame*m_sampleSizeBytesInp),
m_hasNewRxData(false), m_hasNewRxData(false),
m_lastRxDataLength(0), m_lastRxDataLength(0),
m_rxData(kMaxDataSize), m_rxData(kMaxDataSize),
@@ -301,7 +301,7 @@ GGWave::GGWave(const Parameters & parameters) :
m_outputBlockTmp(kMaxRecordedFrames*kMaxSamplesPerFrame*m_sampleSizeBytesOut), m_outputBlockTmp(kMaxRecordedFrames*kMaxSamplesPerFrame*m_sampleSizeBytesOut),
m_outputBlockI16(kMaxRecordedFrames*kMaxSamplesPerFrame) { m_outputBlockI16(kMaxRecordedFrames*kMaxSamplesPerFrame) {
if (m_sampleSizeBytesIn == 0) { if (m_sampleSizeBytesInp == 0) {
throw std::runtime_error("Invalid or unsupported capture sample format"); throw std::runtime_error("Invalid or unsupported capture sample format");
} }
@@ -390,14 +390,33 @@ bool GGWave::init(int dataSize, const char * dataBuffer, const TxProtocol & txPr
return true; return true;
} }
bool GGWave::encode(const CBEnqueueAudio & cbEnqueueAudio) { uint32_t GGWave::encodeSize_bytes() const {
int samplesPerFrameOut = (m_sampleRateOut/m_sampleRateIn)*m_samplesPerFrame; return encodeSize_samples()*m_sampleSizeBytesOut;
if (m_sampleRateOut > m_sampleRateIn) { }
fprintf(stderr, "Error: capture sample rate (%d Hz) must be <= playback sample rate (%d Hz)\n", (int) m_sampleRateIn, (int) m_sampleRateOut);
uint32_t GGWave::encodeSize_samples() const {
if (m_hasNewTxData == false) {
return 0;
}
int nECCBytesPerTx = getECCBytesForLength(m_txDataLength);
int sendDataLength = m_txDataLength + m_encodedDataOffset;
int totalBytes = sendDataLength + nECCBytesPerTx;
int totalDataFrames = ((totalBytes + m_txProtocol.bytesPerTx - 1)/m_txProtocol.bytesPerTx)*m_txProtocol.framesPerTx;
return (
m_nMarkerFrames + m_nPostMarkerFrames + totalDataFrames + m_nMarkerFrames
)*m_samplesPerFrame;
}
bool GGWave::encode(const CBWaveformOut & cbWaveformOut) {
int samplesPerFrameOut = (m_sampleRateOut/m_sampleRateInp)*m_samplesPerFrame;
if (m_sampleRateOut > m_sampleRateInp) {
fprintf(stderr, "Error: capture sample rate (%d Hz) must be <= playback sample rate (%d Hz)\n", (int) m_sampleRateInp, (int) m_sampleRateOut);
return false; return false;
} }
if (m_sampleRateOut != m_sampleRateIn) { if (m_sampleRateOut != m_sampleRateInp) {
fprintf(stderr, "Resampling from %d Hz to %d Hz\n", (int) m_sampleRateIn, (int) m_sampleRateOut); fprintf(stderr, "Resampling from %d Hz to %d Hz\n", (int) m_sampleRateInp, (int) m_sampleRateOut);
} }
int frameId = 0; int frameId = 0;
@@ -438,10 +457,12 @@ bool GGWave::encode(const CBEnqueueAudio & cbEnqueueAudio) {
} }
} }
m_nECCBytesPerTx = getECCBytesForLength(m_txDataLength); int nECCBytesPerTx = getECCBytesForLength(m_txDataLength);
m_sendDataLength = m_txDataLength + m_encodedDataOffset; int sendDataLength = m_txDataLength + m_encodedDataOffset;
int totalBytes = sendDataLength + nECCBytesPerTx;
int totalDataFrames = ((totalBytes + m_txProtocol.bytesPerTx - 1)/m_txProtocol.bytesPerTx)*m_txProtocol.framesPerTx;
RS::ReedSolomon rsData = RS::ReedSolomon(m_txDataLength, m_nECCBytesPerTx); RS::ReedSolomon rsData = RS::ReedSolomon(m_txDataLength, nECCBytesPerTx);
RS::ReedSolomon rsLength(1, m_encodedDataOffset - 1); RS::ReedSolomon rsLength(1, m_encodedDataOffset - 1);
rsLength.Encode(m_txData.data(), m_txDataEncoded.data()); rsLength.Encode(m_txData.data(), m_txDataEncoded.data());
@@ -450,7 +471,7 @@ bool GGWave::encode(const CBEnqueueAudio & cbEnqueueAudio) {
while (m_hasNewTxData) { while (m_hasNewTxData) {
std::fill(m_outputBlock.begin(), m_outputBlock.end(), 0.0f); std::fill(m_outputBlock.begin(), m_outputBlock.end(), 0.0f);
if (m_sampleRateOut != m_sampleRateIn) { if (m_sampleRateOut != m_sampleRateInp) {
for (int k = 0; k < m_txProtocol.nDataBitsPerTx(); ++k) { for (int k = 0; k < m_txProtocol.nDataBitsPerTx(); ++k) {
double freq = bitFreq(m_txProtocol, k); double freq = bitFreq(m_txProtocol, k);
@@ -489,9 +510,7 @@ bool GGWave::encode(const CBEnqueueAudio & cbEnqueueAudio) {
::addAmplitudeSmooth(bit1Amplitude[i], m_outputBlock, m_sendVolume, 0, samplesPerFrameOut, frameId - m_nMarkerFrames, m_nPostMarkerFrames); ::addAmplitudeSmooth(bit1Amplitude[i], m_outputBlock, m_sendVolume, 0, samplesPerFrameOut, frameId - m_nMarkerFrames, m_nPostMarkerFrames);
} }
} }
} else if (frameId < } else if (frameId < m_nMarkerFrames + m_nPostMarkerFrames + totalDataFrames) {
(m_nMarkerFrames + m_nPostMarkerFrames) +
((m_sendDataLength + m_nECCBytesPerTx)/m_txProtocol.bytesPerTx + 2)*m_txProtocol.framesPerTx) {
int dataOffset = frameId - m_nMarkerFrames - m_nPostMarkerFrames; int dataOffset = frameId - m_nMarkerFrames - m_nPostMarkerFrames;
int cycleModMain = dataOffset%m_txProtocol.framesPerTx; int cycleModMain = dataOffset%m_txProtocol.framesPerTx;
dataOffset /= m_txProtocol.framesPerTx; dataOffset /= m_txProtocol.framesPerTx;
@@ -520,13 +539,10 @@ bool GGWave::encode(const CBEnqueueAudio & cbEnqueueAudio) {
::addAmplitudeSmooth(bit1Amplitude[k/2], m_outputBlock, m_sendVolume, 0, samplesPerFrameOut, cycleModMain, m_txProtocol.framesPerTx); ::addAmplitudeSmooth(bit1Amplitude[k/2], m_outputBlock, m_sendVolume, 0, samplesPerFrameOut, cycleModMain, m_txProtocol.framesPerTx);
} }
} }
} else if (frameId < } else if (frameId < m_nMarkerFrames + m_nPostMarkerFrames + totalDataFrames + m_nMarkerFrames) {
(m_nMarkerFrames + m_nPostMarkerFrames) +
((m_sendDataLength + m_nECCBytesPerTx)/m_txProtocol.bytesPerTx + 2)*m_txProtocol.framesPerTx +
(m_nMarkerFrames)) {
nFreq = m_nBitsInMarker; nFreq = m_nBitsInMarker;
int fId = frameId - ((m_nMarkerFrames + m_nPostMarkerFrames) + ((m_sendDataLength + m_nECCBytesPerTx)/m_txProtocol.bytesPerTx + 2)*m_txProtocol.framesPerTx); int fId = frameId - (m_nMarkerFrames + m_nPostMarkerFrames + totalDataFrames);
for (int i = 0; i < m_nBitsInMarker; ++i) { for (int i = 0; i < m_nBitsInMarker; ++i) {
if (i%2 == 0) { if (i%2 == 0) {
addAmplitudeSmooth(bit0Amplitude[i], m_outputBlock, m_sendVolume, 0, samplesPerFrameOut, fId, m_nMarkerFrames); addAmplitudeSmooth(bit0Amplitude[i], m_outputBlock, m_sendVolume, 0, samplesPerFrameOut, fId, m_nMarkerFrames);
@@ -536,6 +552,7 @@ bool GGWave::encode(const CBEnqueueAudio & cbEnqueueAudio) {
} }
} else { } else {
m_hasNewTxData = false; m_hasNewTxData = false;
break;
} }
if (nFreq == 0) nFreq = 1; if (nFreq == 0) nFreq = 1;
@@ -599,14 +616,14 @@ bool GGWave::encode(const CBEnqueueAudio & cbEnqueueAudio) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: break; case GGWAVE_SAMPLE_FORMAT_UNDEFINED: break;
case GGWAVE_SAMPLE_FORMAT_I16: case GGWAVE_SAMPLE_FORMAT_I16:
{ {
cbEnqueueAudio(m_outputBlockI16.data(), frameId*samplesPerFrameOut*m_sampleSizeBytesOut); cbWaveformOut(m_outputBlockI16.data(), frameId*samplesPerFrameOut*m_sampleSizeBytesOut);
} break; } break;
case GGWAVE_SAMPLE_FORMAT_U8: case GGWAVE_SAMPLE_FORMAT_U8:
case GGWAVE_SAMPLE_FORMAT_I8: case GGWAVE_SAMPLE_FORMAT_I8:
case GGWAVE_SAMPLE_FORMAT_U16: case GGWAVE_SAMPLE_FORMAT_U16:
case GGWAVE_SAMPLE_FORMAT_F32: case GGWAVE_SAMPLE_FORMAT_F32:
{ {
cbEnqueueAudio(m_outputBlockTmp.data(), frameId*samplesPerFrameOut*m_sampleSizeBytesOut); cbWaveformOut(m_outputBlockTmp.data(), frameId*samplesPerFrameOut*m_sampleSizeBytesOut);
} break; } break;
} }
@@ -618,49 +635,49 @@ bool GGWave::encode(const CBEnqueueAudio & cbEnqueueAudio) {
return true; return true;
} }
void GGWave::decode(const CBDequeueAudio & cbDequeueAudio) { void GGWave::decode(const CBWaveformInp & cbWaveformInp) {
while (m_hasNewTxData == false) { while (m_hasNewTxData == false) {
// read capture data // read capture data
uint32_t nBytesNeeded = m_samplesNeeded*m_sampleSizeBytesIn; uint32_t nBytesNeeded = m_samplesNeeded*m_sampleSizeBytesInp;
uint32_t nBytesRecorded = 0; uint32_t nBytesRecorded = 0;
uint32_t offset = m_samplesPerFrame - m_samplesNeeded; uint32_t offset = m_samplesPerFrame - m_samplesNeeded;
switch (m_sampleFormatIn) { switch (m_sampleFormatInp) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: break; case GGWAVE_SAMPLE_FORMAT_UNDEFINED: break;
case GGWAVE_SAMPLE_FORMAT_U8: case GGWAVE_SAMPLE_FORMAT_U8:
case GGWAVE_SAMPLE_FORMAT_I8: case GGWAVE_SAMPLE_FORMAT_I8:
case GGWAVE_SAMPLE_FORMAT_U16: case GGWAVE_SAMPLE_FORMAT_U16:
case GGWAVE_SAMPLE_FORMAT_I16: case GGWAVE_SAMPLE_FORMAT_I16:
{ {
nBytesRecorded = cbDequeueAudio(m_sampleAmplitudeTmp.data() + offset, nBytesNeeded); nBytesRecorded = cbWaveformInp(m_sampleAmplitudeTmp.data() + offset, nBytesNeeded);
} break; } break;
case GGWAVE_SAMPLE_FORMAT_F32: case GGWAVE_SAMPLE_FORMAT_F32:
{ {
nBytesRecorded = cbDequeueAudio(m_sampleAmplitude.data() + offset, nBytesNeeded); nBytesRecorded = cbWaveformInp(m_sampleAmplitude.data() + offset, nBytesNeeded);
} break; } break;
} }
if (nBytesRecorded % m_sampleSizeBytesIn != 0) { if (nBytesRecorded % m_sampleSizeBytesInp != 0) {
fprintf(stderr, "Failure during capture - provided bytes (%d) are not multiple of sample size (%d)\n", fprintf(stderr, "Failure during capture - provided bytes (%d) are not multiple of sample size (%d)\n",
nBytesRecorded, m_sampleSizeBytesIn); nBytesRecorded, m_sampleSizeBytesInp);
m_samplesNeeded = m_samplesPerFrame; m_samplesNeeded = m_samplesPerFrame;
break; break;
} }
if (nBytesRecorded > nBytesNeeded) { if (nBytesRecorded > nBytesNeeded) {
fprintf(stderr, "Failure during capture - more samples were provided (%d) than requested (%d)\n", fprintf(stderr, "Failure during capture - more samples were provided (%d) than requested (%d)\n",
nBytesRecorded/m_sampleSizeBytesIn, nBytesNeeded/m_sampleSizeBytesIn); nBytesRecorded/m_sampleSizeBytesInp, nBytesNeeded/m_sampleSizeBytesInp);
m_samplesNeeded = m_samplesPerFrame; m_samplesNeeded = m_samplesPerFrame;
break; break;
} }
// convert to 32-bit float // convert to 32-bit float
switch (m_sampleFormatIn) { switch (m_sampleFormatInp) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: break; case GGWAVE_SAMPLE_FORMAT_UNDEFINED: break;
case GGWAVE_SAMPLE_FORMAT_U8: case GGWAVE_SAMPLE_FORMAT_U8:
{ {
constexpr float scale = 1.0f/128; constexpr float scale = 1.0f/128;
int nSamplesRecorded = nBytesRecorded/m_sampleSizeBytesIn; int nSamplesRecorded = nBytesRecorded/m_sampleSizeBytesInp;
auto p = reinterpret_cast<uint8_t *>(m_sampleAmplitudeTmp.data()); auto p = reinterpret_cast<uint8_t *>(m_sampleAmplitudeTmp.data());
for (int i = 0; i < nSamplesRecorded; ++i) { for (int i = 0; i < nSamplesRecorded; ++i) {
m_sampleAmplitude[offset + i] = float(int16_t(*(p + offset + i)) - 128)*scale; m_sampleAmplitude[offset + i] = float(int16_t(*(p + offset + i)) - 128)*scale;
@@ -669,7 +686,7 @@ void GGWave::decode(const CBDequeueAudio & cbDequeueAudio) {
case GGWAVE_SAMPLE_FORMAT_I8: case GGWAVE_SAMPLE_FORMAT_I8:
{ {
constexpr float scale = 1.0f/128; constexpr float scale = 1.0f/128;
int nSamplesRecorded = nBytesRecorded/m_sampleSizeBytesIn; int nSamplesRecorded = nBytesRecorded/m_sampleSizeBytesInp;
auto p = reinterpret_cast<int8_t *>(m_sampleAmplitudeTmp.data()); auto p = reinterpret_cast<int8_t *>(m_sampleAmplitudeTmp.data());
for (int i = 0; i < nSamplesRecorded; ++i) { for (int i = 0; i < nSamplesRecorded; ++i) {
m_sampleAmplitude[offset + i] = float(*(p + offset + i))*scale; m_sampleAmplitude[offset + i] = float(*(p + offset + i))*scale;
@@ -678,7 +695,7 @@ void GGWave::decode(const CBDequeueAudio & cbDequeueAudio) {
case GGWAVE_SAMPLE_FORMAT_U16: case GGWAVE_SAMPLE_FORMAT_U16:
{ {
constexpr float scale = 1.0f/32768; constexpr float scale = 1.0f/32768;
int nSamplesRecorded = nBytesRecorded/m_sampleSizeBytesIn; int nSamplesRecorded = nBytesRecorded/m_sampleSizeBytesInp;
auto p = reinterpret_cast<uint16_t *>(m_sampleAmplitudeTmp.data()); auto p = reinterpret_cast<uint16_t *>(m_sampleAmplitudeTmp.data());
for (int i = 0; i < nSamplesRecorded; ++i) { for (int i = 0; i < nSamplesRecorded; ++i) {
m_sampleAmplitude[offset + i] = float(int32_t(*(p + offset + i)) - 32768)*scale; m_sampleAmplitude[offset + i] = float(int32_t(*(p + offset + i)) - 32768)*scale;
@@ -687,7 +704,7 @@ void GGWave::decode(const CBDequeueAudio & cbDequeueAudio) {
case GGWAVE_SAMPLE_FORMAT_I16: case GGWAVE_SAMPLE_FORMAT_I16:
{ {
constexpr float scale = 1.0f/32768; constexpr float scale = 1.0f/32768;
int nSamplesRecorded = nBytesRecorded/m_sampleSizeBytesIn; int nSamplesRecorded = nBytesRecorded/m_sampleSizeBytesInp;
auto p = reinterpret_cast<int16_t *>(m_sampleAmplitudeTmp.data()); auto p = reinterpret_cast<int16_t *>(m_sampleAmplitudeTmp.data());
for (int i = 0; i < nSamplesRecorded; ++i) { for (int i = 0; i < nSamplesRecorded; ++i) {
m_sampleAmplitude[offset + i] = float(*(p + offset + i))*scale; m_sampleAmplitude[offset + i] = float(*(p + offset + i))*scale;
@@ -721,9 +738,9 @@ void GGWave::decode(const CBDequeueAudio & cbDequeueAudio) {
} }
// calculate spectrum // calculate spectrum
std::copy(m_sampleAmplitudeAverage.begin(), m_sampleAmplitudeAverage.begin() + m_samplesPerFrame, m_fftIn.data()); std::copy(m_sampleAmplitudeAverage.begin(), m_sampleAmplitudeAverage.begin() + m_samplesPerFrame, m_fftInp.data());
FFT(m_fftIn.data(), m_fftOut.data(), m_samplesPerFrame, 1.0); FFT(m_fftInp.data(), m_fftOut.data(), m_samplesPerFrame, 1.0);
for (int i = 0; i < m_samplesPerFrame; ++i) { for (int i = 0; i < m_samplesPerFrame; ++i) {
m_sampleSpectrum[i] = (m_fftOut[2*i + 0]*m_fftOut[2*i + 0] + m_fftOut[2*i + 1]*m_fftOut[2*i + 1]); m_sampleSpectrum[i] = (m_fftOut[2*i + 0]*m_fftOut[2*i + 0] + m_fftOut[2*i + 1]*m_fftOut[2*i + 1]);
@@ -775,15 +792,15 @@ void GGWave::decode(const CBDequeueAudio & cbDequeueAudio) {
std::copy( std::copy(
m_recordedAmplitude.begin() + offsetTx*step, m_recordedAmplitude.begin() + offsetTx*step,
m_recordedAmplitude.begin() + offsetTx*step + m_samplesPerFrame, m_fftIn.data()); m_recordedAmplitude.begin() + offsetTx*step + m_samplesPerFrame, m_fftInp.data());
for (int k = 1; k < rxProtocol.framesPerTx - 1; ++k) { for (int k = 1; k < rxProtocol.framesPerTx - 1; ++k) {
for (int i = 0; i < m_samplesPerFrame; ++i) { for (int i = 0; i < m_samplesPerFrame; ++i) {
m_fftIn[i] += m_recordedAmplitude[(offsetTx + k*stepsPerFrame)*step + i]; m_fftInp[i] += m_recordedAmplitude[(offsetTx + k*stepsPerFrame)*step + i];
} }
} }
FFT(m_fftIn.data(), m_fftOut.data(), m_samplesPerFrame, 1.0); FFT(m_fftInp.data(), m_fftOut.data(), m_samplesPerFrame, 1.0);
for (int i = 0; i < m_samplesPerFrame; ++i) { for (int i = 0; i < m_samplesPerFrame; ++i) {
m_sampleSpectrum[i] = (m_fftOut[2*i + 0]*m_fftOut[2*i + 0] + m_fftOut[2*i + 1]*m_fftOut[2*i + 1]); m_sampleSpectrum[i] = (m_fftOut[2*i + 0]*m_fftOut[2*i + 0] + m_fftOut[2*i + 1]*m_fftOut[2*i + 1]);
@@ -950,7 +967,7 @@ void GGWave::decode(const CBDequeueAudio & cbDequeueAudio) {
} }
} }
} else { } else {
m_samplesNeeded -= nBytesRecorded/m_sampleSizeBytesIn; m_samplesNeeded -= nBytesRecorded/m_sampleSizeBytesInp;
break; break;
} }
} }

View File

@@ -15,7 +15,7 @@
int main() { int main() {
ggwave_Parameters parameters = ggwave_getDefaultParameters(); ggwave_Parameters parameters = ggwave_getDefaultParameters();
parameters.sampleFormatIn = GGWAVE_SAMPLE_FORMAT_I16; parameters.sampleFormatInp = GGWAVE_SAMPLE_FORMAT_I16;
parameters.sampleFormatOut = GGWAVE_SAMPLE_FORMAT_I16; parameters.sampleFormatOut = GGWAVE_SAMPLE_FORMAT_I16;
ggwave_Instance instance = ggwave_init(parameters); ggwave_Instance instance = ggwave_init(parameters);

View File

@@ -41,7 +41,7 @@ const std::set<GGWave::SampleFormat> kFormats = {
}; };
template <typename T> template <typename T>
GGWave::CBEnqueueAudio getCBEnqueueAudio(uint32_t & nSamples, std::vector<T> & buffer) { GGWave::CBWaveformOut getCBWaveformOut(uint32_t & nSamples, std::vector<T> & buffer) {
return [&nSamples, &buffer](const void * data, uint32_t nBytes) { return [&nSamples, &buffer](const void * data, uint32_t nBytes) {
nSamples = nBytes/sizeof(T); nSamples = nBytes/sizeof(T);
CHECK(nSamples*sizeof(T) == nBytes); CHECK(nSamples*sizeof(T) == nBytes);
@@ -51,7 +51,7 @@ GGWave::CBEnqueueAudio getCBEnqueueAudio(uint32_t & nSamples, std::vector<T> & b
} }
template <typename T> template <typename T>
GGWave::CBDequeueAudio getCBDequeueAudio(uint32_t & nSamples, std::vector<T> & buffer) { GGWave::CBWaveformInp getCBWaveformInp(uint32_t & nSamples, std::vector<T> & buffer) {
return [&nSamples, &buffer](void * data, uint32_t nMaxBytes) { return [&nSamples, &buffer](void * data, uint32_t nMaxBytes) {
uint32_t nCopied = std::min((uint32_t) (nSamples*sizeof(T)), nMaxBytes); uint32_t nCopied = std::min((uint32_t) (nSamples*sizeof(T)), nMaxBytes);
const char * p = (char *) (buffer.data() + buffer.size() - nSamples); const char * p = (char *) (buffer.data() + buffer.size() - nSamples);
@@ -78,12 +78,12 @@ int main() {
std::vector<int16_t> bufferI16; std::vector<int16_t> bufferI16;
std::vector<float> bufferF32; std::vector<float> bufferF32;
auto convertHelper = [&](GGWave::SampleFormat formatOut, GGWave::SampleFormat formatIn) { auto convertHelper = [&](GGWave::SampleFormat formatOut, GGWave::SampleFormat formatInp) {
switch (formatOut) { switch (formatOut) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break; case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break;
case GGWAVE_SAMPLE_FORMAT_U8: case GGWAVE_SAMPLE_FORMAT_U8:
{ {
switch (formatIn) { switch (formatInp) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break; case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break;
case GGWAVE_SAMPLE_FORMAT_U8: break; case GGWAVE_SAMPLE_FORMAT_U8: break;
case GGWAVE_SAMPLE_FORMAT_I8: convert(bufferU8, bufferI8); break; case GGWAVE_SAMPLE_FORMAT_I8: convert(bufferU8, bufferI8); break;
@@ -94,7 +94,7 @@ int main() {
} break; } break;
case GGWAVE_SAMPLE_FORMAT_I8: case GGWAVE_SAMPLE_FORMAT_I8:
{ {
switch (formatIn) { switch (formatInp) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break; case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break;
case GGWAVE_SAMPLE_FORMAT_U8: convert(bufferI8, bufferU8); break; case GGWAVE_SAMPLE_FORMAT_U8: convert(bufferI8, bufferU8); break;
case GGWAVE_SAMPLE_FORMAT_I8: break; case GGWAVE_SAMPLE_FORMAT_I8: break;
@@ -105,7 +105,7 @@ int main() {
} break; } break;
case GGWAVE_SAMPLE_FORMAT_U16: case GGWAVE_SAMPLE_FORMAT_U16:
{ {
switch (formatIn) { switch (formatInp) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break; case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break;
case GGWAVE_SAMPLE_FORMAT_U8: convert(bufferU16, bufferU8); break; case GGWAVE_SAMPLE_FORMAT_U8: convert(bufferU16, bufferU8); break;
case GGWAVE_SAMPLE_FORMAT_I8: convert(bufferU16, bufferI8); break; case GGWAVE_SAMPLE_FORMAT_I8: convert(bufferU16, bufferI8); break;
@@ -116,7 +116,7 @@ int main() {
} break; } break;
case GGWAVE_SAMPLE_FORMAT_I16: case GGWAVE_SAMPLE_FORMAT_I16:
{ {
switch (formatIn) { switch (formatInp) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break; case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break;
case GGWAVE_SAMPLE_FORMAT_U8: convert(bufferI16, bufferU8); break; case GGWAVE_SAMPLE_FORMAT_U8: convert(bufferI16, bufferU8); break;
case GGWAVE_SAMPLE_FORMAT_I8: convert(bufferI16, bufferI8); break; case GGWAVE_SAMPLE_FORMAT_I8: convert(bufferI16, bufferI8); break;
@@ -127,7 +127,7 @@ int main() {
} break; } break;
case GGWAVE_SAMPLE_FORMAT_F32: case GGWAVE_SAMPLE_FORMAT_F32:
{ {
switch (formatIn) { switch (formatInp) {
case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break; case GGWAVE_SAMPLE_FORMAT_UNDEFINED: CHECK(false); break;
case GGWAVE_SAMPLE_FORMAT_U8: convert(bufferF32, bufferU8); break; case GGWAVE_SAMPLE_FORMAT_U8: convert(bufferF32, bufferU8); break;
case GGWAVE_SAMPLE_FORMAT_I8: convert(bufferF32, bufferI8); break; case GGWAVE_SAMPLE_FORMAT_I8: convert(bufferF32, bufferI8); break;
@@ -142,20 +142,20 @@ int main() {
uint32_t nSamples = 0; uint32_t nSamples = 0;
const std::map<GGWave::SampleFormat, GGWave::CBEnqueueAudio> kCBEnqueueAudio = { const std::map<GGWave::SampleFormat, GGWave::CBWaveformOut> kCBWaveformOut = {
{ GGWAVE_SAMPLE_FORMAT_U8, getCBEnqueueAudio(nSamples, bufferU8) }, { GGWAVE_SAMPLE_FORMAT_U8, getCBWaveformOut(nSamples, bufferU8) },
{ GGWAVE_SAMPLE_FORMAT_I8, getCBEnqueueAudio(nSamples, bufferI8) }, { GGWAVE_SAMPLE_FORMAT_I8, getCBWaveformOut(nSamples, bufferI8) },
{ GGWAVE_SAMPLE_FORMAT_U16, getCBEnqueueAudio(nSamples, bufferU16) }, { GGWAVE_SAMPLE_FORMAT_U16, getCBWaveformOut(nSamples, bufferU16) },
{ GGWAVE_SAMPLE_FORMAT_I16, getCBEnqueueAudio(nSamples, bufferI16) }, { GGWAVE_SAMPLE_FORMAT_I16, getCBWaveformOut(nSamples, bufferI16) },
{ GGWAVE_SAMPLE_FORMAT_F32, getCBEnqueueAudio(nSamples, bufferF32) }, { GGWAVE_SAMPLE_FORMAT_F32, getCBWaveformOut(nSamples, bufferF32) },
}; };
const std::map<GGWave::SampleFormat, GGWave::CBDequeueAudio> kCBDequeueAudio = { const std::map<GGWave::SampleFormat, GGWave::CBWaveformInp> kCBWaveformInp = {
{ GGWAVE_SAMPLE_FORMAT_U8, getCBDequeueAudio(nSamples, bufferU8) }, { GGWAVE_SAMPLE_FORMAT_U8, getCBWaveformInp(nSamples, bufferU8) },
{ GGWAVE_SAMPLE_FORMAT_I8, getCBDequeueAudio(nSamples, bufferI8) }, { GGWAVE_SAMPLE_FORMAT_I8, getCBWaveformInp(nSamples, bufferI8) },
{ GGWAVE_SAMPLE_FORMAT_U16, getCBDequeueAudio(nSamples, bufferU16) }, { GGWAVE_SAMPLE_FORMAT_U16, getCBWaveformInp(nSamples, bufferU16) },
{ GGWAVE_SAMPLE_FORMAT_I16, getCBDequeueAudio(nSamples, bufferI16) }, { GGWAVE_SAMPLE_FORMAT_I16, getCBWaveformInp(nSamples, bufferI16) },
{ GGWAVE_SAMPLE_FORMAT_F32, getCBDequeueAudio(nSamples, bufferF32) }, { GGWAVE_SAMPLE_FORMAT_F32, getCBWaveformInp(nSamples, bufferF32) },
}; };
{ {
@@ -183,20 +183,23 @@ int main() {
for (const auto & txProtocol : GGWave::getTxProtocols()) { for (const auto & txProtocol : GGWave::getTxProtocols()) {
for (const auto & formatOut : kFormats) { for (const auto & formatOut : kFormats) {
for (const auto & formatIn : kFormats) { for (const auto & formatInp : kFormats) {
printf("Testing: protocol = %s, in = %d, out = %d\n", txProtocol.second.name, formatIn, formatOut); printf("Testing: protocol = %s, in = %d, out = %d\n", txProtocol.second.name, formatInp, formatOut);
auto parameters = GGWave::getDefaultParameters(); auto parameters = GGWave::getDefaultParameters();
parameters.sampleFormatIn = formatIn; parameters.sampleFormatInp = formatInp;
parameters.sampleFormatOut = formatOut; parameters.sampleFormatOut = formatOut;
GGWave instance(parameters); GGWave instance(parameters);
std::string payload = "test message xxxxxxxxxxxx"; std::string payload = "test";
instance.init(payload, txProtocol.second, 25); instance.init(payload, txProtocol.second, 25);
instance.encode(kCBEnqueueAudio.at(formatOut)); auto expectedSize = instance.encodeSize_samples();
convertHelper(formatOut, formatIn); instance.encode(kCBWaveformOut.at(formatOut));
instance.decode(kCBDequeueAudio.at(formatIn)); printf("Expected = %d, actual = %d\n", expectedSize, nSamples);
CHECK(expectedSize == nSamples);
convertHelper(formatOut, formatInp);
instance.decode(kCBWaveformInp.at(formatInp));
{ {
GGWave::TxRxData result; GGWave::TxRxData result;