secondvoice/src/utils/Config.cpp
StillHammer 5b60acaa73 feat: Implement complete MVP architecture for SecondVoice
Complete implementation of the real-time Chinese-to-French translation system:

Architecture:
- 3-threaded pipeline: Audio capture → AI processing → UI rendering
- Thread-safe queues for inter-thread communication
- Configurable audio chunk sizes for latency tuning

Core Features:
- Audio capture with PortAudio (configurable sample rate/channels)
- Whisper API integration for Chinese speech-to-text
- Claude API integration for Chinese-to-French translation
- ImGui real-time display with stop button
- Full recording saved to WAV on stop

Modules Implemented:
- audio/: AudioCapture (PortAudio wrapper) + AudioBuffer (WAV export)
- api/: WhisperClient + ClaudeClient (HTTP API wrappers)
- ui/: TranslationUI (ImGui interface)
- core/: Pipeline (orchestrates all threads)
- utils/: Config (JSON/.env loader) + ThreadSafeQueue (template)

Build System:
- CMake with vcpkg for dependency management
- vcpkg.json manifest for reproducible builds
- build.sh helper script

Configuration:
- config.json: Audio settings, API parameters, UI config
- .env: API keys (OpenAI + Anthropic)

Documentation:
- README.md: Setup instructions, usage, architecture
- docs/implementation_plan.md: Technical design document
- docs/SecondVoice.md: Project vision and motivation

Next Steps:
- Test build with vcpkg dependencies
- Test audio capture on real hardware
- Validate API integrations
- Tune chunk size for optimal latency

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 03:08:03 +08:00

107 lines
3.6 KiB
C++

#include "Config.h"
#include <nlohmann/json.hpp>
#include <fstream>
#include <iostream>
#include <cstdlib>
using json = nlohmann::json;
namespace secondvoice {
Config& Config::getInstance() {
static Config instance;
return instance;
}
bool Config::load(const std::string& config_path, const std::string& env_path) {
// Load .env file
std::ifstream env_file(env_path);
if (env_file.is_open()) {
std::string line;
while (std::getline(env_file, line)) {
if (line.empty() || line[0] == '#') continue;
auto pos = line.find('=');
if (pos != std::string::npos) {
std::string key = line.substr(0, pos);
std::string value = line.substr(pos + 1);
// Remove quotes if present
if (!value.empty() && value.front() == '"' && value.back() == '"') {
value = value.substr(1, value.length() - 2);
}
if (key == "OPENAI_API_KEY") {
openai_key_ = value;
} else if (key == "ANTHROPIC_API_KEY") {
anthropic_key_ = value;
}
}
}
env_file.close();
} else {
std::cerr << "Warning: Could not open .env file: " << env_path << std::endl;
}
// Load config.json
std::ifstream config_file(config_path);
if (!config_file.is_open()) {
std::cerr << "Error: Could not open config file: " << config_path << std::endl;
return false;
}
json config_json;
try {
config_file >> config_json;
} catch (const json::parse_error& e) {
std::cerr << "Error parsing config.json: " << e.what() << std::endl;
return false;
}
// Parse audio config
if (config_json.contains("audio")) {
auto& audio = config_json["audio"];
audio_config_.sample_rate = audio.value("sample_rate", 16000);
audio_config_.channels = audio.value("channels", 1);
audio_config_.chunk_duration_seconds = audio.value("chunk_duration_seconds", 10);
audio_config_.format = audio.value("format", "wav");
}
// Parse whisper config
if (config_json.contains("whisper")) {
auto& whisper = config_json["whisper"];
whisper_config_.model = whisper.value("model", "whisper-1");
whisper_config_.language = whisper.value("language", "zh");
whisper_config_.temperature = whisper.value("temperature", 0.0f);
}
// Parse claude config
if (config_json.contains("claude")) {
auto& claude = config_json["claude"];
claude_config_.model = claude.value("model", "claude-haiku-4-20250514");
claude_config_.max_tokens = claude.value("max_tokens", 1024);
claude_config_.temperature = claude.value("temperature", 0.3f);
claude_config_.system_prompt = claude.value("system_prompt", "");
}
// Parse UI config
if (config_json.contains("ui")) {
auto& ui = config_json["ui"];
ui_config_.window_width = ui.value("window_width", 800);
ui_config_.window_height = ui.value("window_height", 600);
ui_config_.font_size = ui.value("font_size", 16);
ui_config_.max_display_lines = ui.value("max_display_lines", 50);
}
// Parse recording config
if (config_json.contains("recording")) {
auto& recording = config_json["recording"];
recording_config_.save_audio = recording.value("save_audio", true);
recording_config_.output_directory = recording.value("output_directory", "./recordings");
}
return true;
}
} // namespace secondvoice