181 lines
5.5 KiB
C++
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;
|
|
}
|