Cerberus/sound.cpp
2025-01-28 21:22:41 +03:00

181 lines
5.5 KiB
C++

#include "sound.hpp"
#include "bfsk.hpp"
#include "config.hpp"
#include "x25519_handshake.hpp"
#include <iostream>
#include <thread>
#include <atomic>
#include <vector>
#include <cmath>
#include <portaudio.h>
extern "C" {
#include "monocypher.h"
}
constexpr int CAPTURE_SECONDS = 3;
static std::atomic<bool> gSoundActive{false};
static std::thread gSoundThread;
static std::vector<float> gInputBuffer;
static size_t gWritePos = 0;
static std::vector<float> gOutputBuffer;
static size_t gReadPos = 0;
static int audioCallback(const void *input,
void *output,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData)
{
(void)timeInfo; (void)statusFlags; (void)userData;
const float *in = static_cast<const float*>(input);
float *out = static_cast<float*>(output);
for (unsigned long i = 0; i < frameCount; i++) {
if (gWritePos < gInputBuffer.size()) {
gInputBuffer[gWritePos++] = in ? in[i] : 0.0f;
}
float sample = 0.0f;
if (gReadPos < gOutputBuffer.size()) {
sample = gOutputBuffer[gReadPos++];
}
out[i*2 + 0] = sample;
out[i*2 + 1] = sample;
}
return paContinue;
}
static void soundThreadFunc(AppConfig config) {
PaError err = Pa_Initialize();
if (err != paNoError) {
std::cerr << CLR_RED "[sound] Pa_Initialize error: " << Pa_GetErrorText(err) << CLR_RESET "\n";
return;
}
PaStreamParameters inParams, outParams;
inParams.device = Pa_GetDefaultInputDevice();
if (inParams.device == paNoDevice) {
std::cerr << CLR_RED "[sound] Нет устройства ввода.\n" CLR_RESET;
Pa_Terminate();
return;
}
inParams.channelCount = 1;
inParams.sampleFormat = paFloat32;
inParams.suggestedLatency = Pa_GetDeviceInfo(inParams.device)->defaultLowInputLatency;
inParams.hostApiSpecificStreamInfo = nullptr;
outParams.device = Pa_GetDefaultOutputDevice();
if (outParams.device == paNoDevice) {
std::cerr << CLR_RED "[sound] Нет устройства вывода.\n" CLR_RESET;
Pa_Terminate();
return;
}
outParams.channelCount = 2;
outParams.sampleFormat = paFloat32;
outParams.suggestedLatency = Pa_GetDeviceInfo(outParams.device)->defaultLowOutputLatency;
outParams.hostApiSpecificStreamInfo = nullptr;
PaStream *stream = nullptr;
err = Pa_OpenStream(&stream,
&inParams,
&outParams,
SAMPLE_RATE,
256,
paNoFlag,
audioCallback,
nullptr);
if (err != paNoError) {
std::cerr << CLR_RED "[sound] Pa_OpenStream error: " << Pa_GetErrorText(err) << CLR_RESET "\n";
Pa_Terminate();
return;
}
err = Pa_StartStream(stream);
if (err != paNoError) {
std::cerr << CLR_RED "[sound] Pa_StartStream error: " << Pa_GetErrorText(err) << CLR_RESET "\n";
Pa_CloseStream(stream);
Pa_Terminate();
return;
}
std::cout << CLR_BLUE "[sound] Старт записи/воспроизведения (3 сек)...\n" CLR_RESET;
Pa_Sleep(CAPTURE_SECONDS * 1000);
Pa_StopStream(stream);
Pa_CloseStream(stream);
Pa_Terminate();
std::cout << CLR_BLUE "[sound] Остановка аудиопотока...\n" CLR_RESET;
auto received = bfskDemodulate(gInputBuffer);
if (!received.empty()) {
if (received.size() >= 33 && received[0] == 'E') {
uint8_t otherPub[32];
std::memcpy(otherPub, received.data() + 1, 32);
x25519ComputeShared(config, otherPub);
std::cout << CLR_GREEN "[x25519] Общий сеансовый ключ вычислен!\n" CLR_RESET;
} else {
std::cout << CLR_YELLOW "[sound] Получены " << received.size()
<< " байт, но не формат 'E' + 32 байта.\n" CLR_RESET;
}
} else {
std::cout << CLR_YELLOW "[sound] Ничего не демодулировано.\n" CLR_RESET;
}
gSoundActive = false;
}
void soundFind(AppConfig &config) {
if (config.soundExchangeActive) {
std::cout << CLR_YELLOW "[sound] Уже идёт процесс.\n" CLR_RESET;
return;
}
config.soundExchangeActive = true;
gSoundActive = true;
x25519GenerateEphemeral(config);
std::vector<unsigned char> packet;
packet.push_back('E');
packet.insert(packet.end(), config.ephemeralPub, config.ephemeralPub + 32);
gOutputBuffer = bfskModulate(packet);
gReadPos = 0;
gInputBuffer.clear();
gInputBuffer.resize(SAMPLE_RATE * CAPTURE_SECONDS, 0.0f);
gWritePos = 0;
gSoundThread = std::thread(soundThreadFunc, config);
std::cout << CLR_GREEN "[sound] Отправляем свой публичный ключ X25519 и слушаем!\n" CLR_RESET;
}
void soundLose(AppConfig &config) {
if (!config.soundExchangeActive) {
std::cout << CLR_YELLOW "[sound] Процесс не активен.\n" CLR_RESET;
return;
}
config.soundExchangeActive = false;
if (gSoundActive) {
gSoundActive = false;
}
if (gSoundThread.joinable()) {
gSoundThread.join();
}
std::cout << CLR_GREEN "[sound] Процесс остановлен.\n" CLR_RESET;
}