#include "RemoteSDK.h"
#include <nlohmann/json.hpp>
#include <iostream>
#include <fstream>
#include <vector>
#include <thread>
#include <chrono>
#include <mutex>
#include <atomic>
#include <cstdlib>
using namespace sanas::remote;
// Audio helpers — implement with your WAV reader/writer
std::vector<float> load_wav(const std::string& path, uint32_t& sr);
void save_wav(const std::string& path, const std::vector<float>& a, uint32_t sr);
// Transcript store — callback runs on a background thread
std::mutex g_mutex;
nlohmann::json g_transcripts = nlohmann::json::array();
void handle_transcript(const std::string& jsonData) {
try {
auto root = nlohmann::json::parse(jsonData);
std::string type = root.value("type", "");
if (!root.contains(type)) return;
const auto& body = root[type];
std::string text;
for (const auto& w : body.value("complete", nlohmann::json::array()))
text += w.value("word", "");
if (text.empty()) return;
std::lock_guard<std::mutex> lock(g_mutex);
std::cout << "["
<< (type == "translation" ? "TARGET" : "SOURCE")
<< " " << body.value("lang", "") << "] " << text << "\n";
} catch (const std::exception& e) {
std::cerr << "parse error: " << e.what() << "\n";
}
}
int main(int argc, char* argv[]) {
const std::string input = (argc > 1) ? argv[1] : "input.wav";
const std::string langIn = (argc > 2) ? argv[2] : "es-ES";
const std::string langOut = (argc > 3) ? argv[3] : "en-US";
const char* apiKey = std::getenv("SANAS_API_KEY");
if (!apiKey || !*apiKey) { std::cerr << "Set SANAS_API_KEY\n"; return 1; }
// 1. Load audio
uint32_t sampleRate = 0;
auto audio = load_wav(input, sampleRate);
if (audio.empty()) { std::cerr << "Cannot read " << input << "\n"; return 1; }
// 2. Create and initialise the SDK
auto sdk = CreateRemoteSDK();
InitParams init;
init.apiKey = apiKey;
if (sdk->Initialize(init) != InitSDKResult::kSuccess) {
std::cerr << "Initialize failed\n"; return 1; }
// 3. Create a translation processor with state + transcript callbacks
std::atomic<bool> ready{false}, failed{false};
auto onState = [&](ProcessorState s, std::string reason) {
if (s == ProcessorState::kReady) ready = true;
else if (s == ProcessorState::kFailed ||
s == ProcessorState::kDisconnected) { failed = true; }
};
CreateProcessorResult rc;
auto processor = sdk->CreateAudioProcessor(
AudioParams::WithLanguages(langIn, langOut, sampleRate), rc,
onState, handle_transcript);
if (rc != CreateProcessorResult::kSuccess || !processor) {
sdk->Shutdown(); return 1; }
// 4. Wait for kReady (10-second timeout)
auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(10);
while (!ready && !failed && std::chrono::steady_clock::now() < deadline)
std::this_thread::sleep_for(std::chrono::milliseconds(50));
if (!ready) {
sdk->DestroyAudioProcessor(processor); sdk->Shutdown(); return 1; }
// 5. Stream audio in 20 ms frames at real-time pace
const size_t frame = sampleRate / 50; // 320 samples @ 16 kHz
std::vector<float> output;
for (size_t i = 0; i < audio.size(); i += frame) {
std::vector<float> in(audio.begin() + i,
audio.begin() + std::min(i + frame, audio.size()));
in.resize(frame, 0.0f); // zero-pad last frame
std::vector<float> out;
processor->ProcessSamples(in, out);
output.insert(output.end(), out.begin(), out.end());
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
// Allow trailing transcripts to arrive
std::this_thread::sleep_for(std::chrono::seconds(1));
// 6. Write outputs
save_wav("translated_output.wav", output, sampleRate);
{
std::lock_guard<std::mutex> lock(g_mutex);
std::ofstream f("transcripts.json");
f << g_transcripts.dump(2) << "\n";
}
// 7. Clean up
sdk->DestroyAudioProcessor(processor);
sdk->Shutdown();
return 0;
}
export SANAS_API_KEY=YOUR_API_KEY
./lt_example input.wav es-ES en-US
Included sample. A ready-to-build version ships with the SDK as
remote_sdk_example_lt.cc, including load_wav / save_wav implementations.