封装音频

This commit is contained in:
GuanYuankai 2025-10-21 18:35:53 +08:00
parent 78a7c87762
commit 49879e1c59
3 changed files with 122 additions and 60 deletions

View File

@ -1,53 +1,54 @@
// #include "piper_tts_interface.h"
// #include <string>
// #include <iostream>
// int main() {
// PiperTTSInterface tts_processor;
// // 示例 1: 只提供文本和输出文件名
// std::string text1 = "你好,世界。温度传感器出现故障。只需输入文本即可。";
// std::string file1 = "hello_world.wav";
// if (tts_processor.text_to_speech(text1, file1)) {
// std::cout << "Audio for '" << text1 << "' generated successfully!" << std::endl;
// } else {
// std::cerr << "Failed to generate audio for '" << text1 << "'." << std::endl;
// }
// return 0;
// }
#include "tts/piper_tts_interface.h"
#include <string>
#include <iostream>
#include <chrono>
#include <thread>
#include "systemMonitor/iio_sensor.h" // 包含我们刚刚创建的头文件
int main() {
// --- 配置参数 ---
// !!! 修改这里以匹配你的设备和通道 !!!
const std::string iio_device_path = "/sys/bus/iio/devices/iio:device0";
const std::string iio_channel_name = "in_voltage2_raw"; // 例如,连接到 IN2 通道
const double adc_reference_voltage = 1.8; // ADC参考电压
const int64_t adc_resolution = 4096; // ADC分辨率 (e.g., 2^12 for 12-bit)
const double signal_gain = 11.0; // 信号通路的增益
std::cout << "--- Starting IIO Sensor Reader (Version 2) ---" << std::endl;
std::cout << "Reading from device: " << iio_device_path << std::endl;
std::cout << "Reading channel: " << iio_channel_name << std::endl;
std::cout << "-----------------------------------------------" << std::endl;
// 注意:传感器对象在 try 块外创建,如果构造失败(找不到文件),会立即被捕获
IioSensor sensor(iio_device_path, iio_channel_name);
while (true) {
try {
// 封装后的调用方式非常简洁!
// 每次调用 readVoltage内部都会重新打开文件读取一次
double actual_voltage = sensor.readVoltage(adc_reference_voltage, adc_resolution, signal_gain);
// 输出结果
std::cout << "Actual Voltage: " << actual_voltage << " V" << std::endl;
} catch (const IioSensorError& e) {
// 如果单次读取失败(例如文件内容格式错误、无权限等),打印错误但继续循环
std::cerr << "[!] Read Error: " << e.what() << std::endl;
}
// 每秒钟读取一次
std::this_thread::sleep_for(std::chrono::seconds(1));
// 使用默认配置 (piper 和 /app/piper_models/zh_CN-huayan-medium.onnx)
PiperTTSInterface tts_speaker;
// 如果你的piper可执行文件不在PATH中或者模型路径不同你可以这样指定
// PiperTTSInterface tts_speaker("/path/to/your/piper_executable", "/path/to/your/model.onnx");
std::string text_to_say = "请执行上述调试步骤,特别是禁用文件删除后手动检查和播放 WAV 文件,这将提供更多线索";
if (tts_speaker.say_text_and_play(text_to_say)) {
std::cout << "语音播报完成。" << std::endl;
} else {
std::cerr << "语音播报失败,请检查日志。" << std::endl;
}
return 0;
}
}
// #include <iostream>
// #include <chrono>
// #include <thread>
// #include "systemMonitor/iio_sensor.h" // 包含我们刚刚创建的头文件
// int main() {
// // --- 配置参数 ---
// // !!! 修改这里以匹配你的设备和通道 !!!
// const std::string iio_device_path = "/sys/bus/iio/devices/iio:device0";
// const std::string iio_channel_name = "in_voltage2_raw"; // 例如,连接到 IN2 通道
// const double adc_reference_voltage = 1.8; // ADC参考电压
// const int64_t adc_resolution = 4096; // ADC分辨率 (e.g., 2^12 for 12-bit)
// const double signal_gain = 11.0; // 信号通路的增益
// std::cout << "--- Starting IIO Sensor Reader (Version 2) ---" << std::endl;
// std::cout << "Reading from device: " << iio_device_path << std::endl;
// std::cout << "Reading channel: " << iio_channel_name << std::endl;
// std::cout << "-----------------------------------------------" << std::endl;
// // 注意:传感器对象在 try 块外创建,如果构造失败(找不到文件),会立即被捕获
// IioSensor sensor(iio_device_path, iio_channel_name);
// while (true) {
// try {
// // 封装后的调用方式非常简洁!
// // 每次调用 readVoltage内部都会重新打开文件读取一次
// double actual_voltage = sensor.readVoltage(adc_reference_voltage, adc_resolution, signal_gain);
// // 输出结果
// std::cout << "Actual Voltage: " << actual_voltage << " V" << std::endl;
// } catch (const IioSensorError& e) {
// // 如果单次读取失败(例如文件内容格式错误、无权限等),打印错误但继续循环
// std::cerr << "[!] Read Error: " << e.what() << std::endl;
// }
// // 每秒钟读取一次
// std::this_thread::sleep_for(std::chrono::seconds(1));
// }
// return 0;
// }

View File

@ -1,22 +1,39 @@
#include "piper_tts_interface.h"
#include <cstdlib> // For system()
#include <cstdlib> // For system(), WEXITSTATUS
#include <cstdio> // For remove()
#include <chrono> // For std::chrono
#include <thread> // For std::this_thread::sleep_for
#include <cstring> // For strerror <--- **在这里添加这一行**
#include <errno.h> // For errno <--- **也建议添加这一行,以防某些系统需要明确引入 errno**
// 构造函数实现
PiperTTSInterface::PiperTTSInterface(const std::string& piper_exec_path,
const std::string& model_path_fixed)
: piper_executable(piper_exec_path), fixed_model_path(model_path_fixed) {
// 构造函数实现 (注意默认参数的设置仅在头文件中一次性完成)
PiperTTSInterface::PiperTTSInterface(const std::string& piper_executable_path,
const std::string& default_model_path)
: piper_executable(piper_executable_path), fixed_model_path(default_model_path) {
// 构造时直接初始化模型路径
if (fixed_model_path.empty()) {
std::cerr << "Warning: PiperTTSInterface created without a default model path. text_to_speech calls may fail." << std::endl;
}
}
// 注意:这里不再有 set_default_model_path 方法
// 辅助函数,封装 system() 调用,以便更好地控制和调试
int PiperTTSInterface::execute_command(const std::string& command) {
std::cout << "Executing command: " << command << std::endl;
return std::system(command.c_str());
// 使用WEXITSTATUS宏获取system()返回的实际退出码
int result = std::system(command.c_str());
if (result == -1) { // system() 失败
std::cerr << "system() call failed: " << strerror(errno) << std::endl;
return -1;
}
// WEXITSTATUS 定义在 <sys/wait.h> 中,但通常 <cstdlib> 就足够了。
// 如果编译仍然报错找不到 WEXITSTATUS可能需要单独引入 <sys/wait.h>。
if (WIFEXITED(result)) { // 检查子进程是否正常退出
return WEXITSTATUS(result); // 获取子进程的退出状态码
} else {
std::cerr << "Command did not exit normally. System call result: " << result << std::endl;
return -1; // 或者其他错误码表示非正常退出
}
}
bool PiperTTSInterface::text_to_speech(const std::string& text,
@ -28,6 +45,8 @@ bool PiperTTSInterface::text_to_speech(const std::string& text,
return false;
}
// 注意:这里没有对 text 进行完整的 shell 转义,对于特殊字符可能需要更健壮的处理
// 对于更安全的shell命令构建可以考虑使用 popen 和管道,或者专业的库进行转义
std::string escaped_text = text;
std::string command = "echo \"" + escaped_text + "\" | " +
@ -36,7 +55,7 @@ bool PiperTTSInterface::text_to_speech(const std::string& text,
int result = execute_command(command);
if (result == 0) { // system() 返回 0 表明命令执行成功
if (result == 0) { // execute_command() 返回 0 表明命令执行成功
std::cout << "Successfully generated audio: " << output_filename << std::endl;
return true;
} else {
@ -44,3 +63,38 @@ bool PiperTTSInterface::text_to_speech(const std::string& text,
return false;
}
}
// 新增函数:文本转语音,播放,然后删除文件
bool PiperTTSInterface::say_text_and_play(const std::string& text) {
// 1. 生成唯一的临时文件名
auto now = std::chrono::high_resolution_clock::now();
auto nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
std::string temp_filename = "/tmp/piper_temp_" + std::to_string(nanoseconds) + ".wav";
std::cout << "Generating temporary audio file: " << temp_filename << " for text: '" << text << "'" << std::endl;
if (!text_to_speech(text, temp_filename)) {
std::cerr << "Failed to generate audio for speech playback." << std::endl;
return false;
}
// 3. 播放音频文件 - 改用 gst-launch-1.0
// 使用 gst-launch-1.0 播放,它已经证明可以在你的系统上正常工作
// 我们会直接使用你提供的成功播放的命令模板
std::string play_command = "gst-launch-1.0 -v filesrc location=" + temp_filename +
" ! decodebin ! audioconvert ! alsasink device=hw:2,0";
std::cout << "Playing generated audio file using GStreamer: " << temp_filename << std::endl;
int play_result = execute_command(play_command);
if (play_result != 0) {
std::cerr << "Failed to play audio file using GStreamer: " << temp_filename << ". Command exited with code: " << play_result << std::endl;
} else {
std::cout << "Audio playback finished using GStreamer for: " << temp_filename << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// 4. 删除音频文件(调试完成后可以取消注释)
std::cout << "Deleting temporary audio file: " << temp_filename << std::endl;
if (std::remove(temp_filename.c_str()) != 0) {
std::cerr << "Warning: Failed to delete temporary audio file: " << temp_filename << ". Error: " << strerror(errno) << std::endl;
} else {
std::cout << "Successfully deleted temporary audio file: " << temp_filename << std::endl;
}
return play_result == 0;
}

View File

@ -1,3 +1,4 @@
// piper_tts_interface.h
#ifndef PIPER_TTS_INTERFACE_H
#define PIPER_TTS_INTERFACE_H
@ -16,11 +17,17 @@ public:
* @brief WAV文件
* @param text
* @param output_filename WAV文件名 "output.wav"
* model_path参数为空使
* @return true false
*/
bool text_to_speech(const std::string& text,
const std::string& output_filename); // 移除了 model_path 参数
const std::string& output_filename);
/**
* @brief 使 aplay WAV文件
* @param text
* @return true false
*/
bool say_text_and_play(const std::string& text); // <--- 在这里添加新函数的声明
private:
std::string piper_executable; // Piper 可执行文件的路径