mirror of
https://github.com/ggerganov/ggwave.git
synced 2026-05-10 10:57:39 +08:00
core : refactoring + bug fix
- rename callback types - fix calculation of data frames
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
121
src/ggwave.cpp
121
src/ggwave.cpp
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user