加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
hebing.c 13.16 KB
一键复制 编辑 原始数据 按行查看 历史
hzq_scsx 提交于 2024-03-19 18:52 . 更改
#include <alsa/asoundlib.h>
#include "snowboy/snowboy-detect-c-wrapper.h"
#include "auth.h"
#include <stdio.h>
#include <gpiod.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <cjson/cJSON.h>
#include <alsa/asoundlib.h>
char *ak = "9okSLw5RD72NeEGGO0Md607G";
char *sk = "f472Lir20fChVMBH2YvuJrGiQcaUWmHV";
char *nlp_token = "token: c6f20e77-f36a-45a1-acc1-3d80e54c024c";
// 从文件中读取音频数据
// file: 文件名
// buf: 缓冲区指针的地址
// return: 读取数据大小
size_t load_file(char *file, char **buf)
{
FILE *fp = fopen(file, "r");
if (!fp)
{
perror("fopen");
return 0;
}
// 获取文件大小
if (fseek(fp, 0, SEEK_END) < 0)
{
perror("fseek");
fclose(fp);
return 0;
}
long size = ftell(fp);
if (size < 0)
{
perror("ftell");
fclose(fp);
return 0;
}
// 分配缓冲区,并将文件中的数据读入到缓冲区中
*buf = malloc(size);
if (!*buf)
{
perror("malloc");
fclose(fp);
return 0;
}
// 将文件指针移动到文件头
fseek(fp, 0, SEEK_SET);
size_t readn = fread(*buf, 1, size, fp);
if (readn != size)
{
fprintf(stderr, "file size: %ld, read: %zd\n", size, readn);
}
fclose(fp);
return readn;
}
// 发送HTTP请求报文,并返回服务器响应报文
// token: 访问API需要的access token
// audio: 需要转换的音频数据
// size: 音频数据的大小
// return: 服务器返回的响应报文
char *send_request(char *token, char *audio, size_t size)
{
char *respdata = NULL;
size_t respsize = 0;
FILE *fp = open_memstream(&respdata, &respsize);
if (!fp)
{
perror("open_memstream");
return NULL;
}
CURL *client = curl_easy_init();
if (!client)
{
perror("curl_easy_init");
fclose(fp);
return NULL;
}
// 设置API地址和参数
char *url = NULL;
asprintf(&url, "http://vop.baidu.com/server_api?dev_pid=1537&cuid=liuyu&token=%s", token);
curl_easy_setopt(client, CURLOPT_URL, url);
// 增加请求头部字段
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type: audio/pcm;rate=16000");
curl_easy_setopt(client, CURLOPT_HTTPHEADER, headers);
// 发送POST请求
curl_easy_setopt(client, CURLOPT_POST, 1);
curl_easy_setopt(client, CURLOPT_POSTFIELDS, audio);
curl_easy_setopt(client, CURLOPT_POSTFIELDSIZE, size);
// 将服务器返回的响应报文保存到文件流中
curl_easy_setopt(client, CURLOPT_WRITEDATA, fp);
// 发送请求消息
CURLcode error = curl_easy_perform(client);
if (error != CURLE_OK)
{
fprintf(stderr, "curl_easy_perform: %s\n", curl_easy_strerror(error));
free(url);
curl_slist_free_all(headers);
curl_easy_cleanup(client);
fclose(fp);
return NULL;
}
free(url);
curl_slist_free_all(headers);
curl_easy_cleanup(client);
// 关闭文件流之后才能访问内存中的数据
fclose(fp);
return respdata;
}
// 解析服务器响应报文,打印识别结果
char *process_response(char *respdata)
{
// 解析json
cJSON *root = cJSON_Parse(respdata);
if (!root)
{
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL)
{
fprintf(stderr, "Error before: %s\n", error_ptr);
}
return NULL;
}
// 获取错误码
cJSON *err_no = cJSON_GetObjectItem(root, "err_no");
if (!err_no)
{
fprintf(stderr, "err_no attribute not found\n");
cJSON_Delete(root);
return NULL;
}
if (err_no->valueint != 0)
{
cJSON *err_msg = cJSON_GetObjectItem(root, "err_msg");
if (err_msg)
{
fprintf(stderr, "Error: %s\n", err_msg->valuestring);
}
cJSON_Delete(root);
return NULL;
}
// 获取转换结果
cJSON *result = cJSON_GetObjectItem(root, "result");
if (!result)
{
fprintf(stderr, "result attribute not found\n");
cJSON_Delete(root);
return NULL;
}
if (!cJSON_IsArray(result))
{
fprintf(stderr, "result attribute format error\n");
cJSON_Delete(root);
return NULL;
}
cJSON_Delete(root);
cJSON *item ;
int n = cJSON_GetArraySize(result);
for (int i = 0; i < n; i++)
{
item = cJSON_GetArrayItem(result, i);
puts(item->valuestring);
//printf("元素%s", item->valuestring);
}
return item->valuestring;
}
char *send_nlp_request(char *token, char *msg, cJSON *session_info)
{
char *respdata;
size_t respsize;
FILE *fp = open_memstream(&respdata, &respsize);
if (!fp)
{
perror("open_memstream");
return NULL;
}
CURL *client = curl_easy_init();
if (!client)
{
perror("curl_easy_init");
fclose(fp);
return NULL;
}
char *url = "https://keyue.cloud.baidu.com/online/core/v5/block/query";
curl_easy_setopt(client, CURLOPT_URL, url);
// 构造请求消息
cJSON *root = cJSON_CreateObject();
cJSON *session_id = cJSON_GetObjectItem(session_info, "id");
if (session_id == NULL)
{
cJSON_AddStringToObject(root, "sessionId", "");
}
else
{
cJSON_AddStringToObject(root, "sessionId", session_id->valuestring);
}
cJSON_AddStringToObject(root, "queryText", msg);
char *postdata = cJSON_Print(root);
puts(postdata);
curl_easy_setopt(client, CURLOPT_POST, 1);
curl_easy_setopt(client, CURLOPT_POSTFIELDS, postdata);
// 增加请求头部字段
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, token);
curl_easy_setopt(client, CURLOPT_HTTPHEADER, headers);
// 将服务器返回的响应报文保存到文件流中
curl_easy_setopt(client, CURLOPT_WRITEDATA, fp);
CURLcode error = curl_easy_perform(client);
if (error != CURLE_OK)
{
fprintf(stderr, "curl_easy_perform: %s\n", curl_easy_strerror(error));
curl_easy_cleanup(client);
free(postdata);
cJSON_Delete(root);
fclose(fp);
return NULL;
}
curl_easy_cleanup(client);
free(postdata);
cJSON_Delete(root);
fclose(fp);
return respdata;
}
void process_nlp_response(char *json, cJSON *session_info)
{
cJSON *root = cJSON_Parse(json);
if (!root)
{
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL)
{
fprintf(stderr, "Error before: %s\n", error_ptr);
}
return NULL;
}
puts(cJSON_Print(root));
cJSON *code = cJSON_GetObjectItem(root, "code");
if (code->valueint != 200)
{
cJSON *msg = cJSON_GetObjectItem(root, "msg");
if (msg)
{
fprintf(stderr, "Error: %s\n", msg->valuestring);
}
cJSON_Delete(root);
return NULL;
}
cJSON *data = cJSON_GetObjectItem(root, "data");
cJSON *session_id = cJSON_GetObjectItem(data, "sessionId");
if (cJSON_GetObjectItem(session_info, "id") == NULL)
{
cJSON_AddStringToObject(session_info, "id", session_id->valuestring);
}
cJSON *answers = cJSON_GetObjectItem(data, "answer");
cJSON *answer = cJSON_GetArrayItem(answers, 0);
cJSON *reply = cJSON_GetObjectItem(answer, "reply");
cJSON *text = cJSON_GetObjectItem(reply, "text");
puts(text->valuestring);
cJSON_Delete(root);
}
int main()
{
SnowboyDetect *detector = SnowboyDetectConstructor("common.res", "hxc.pmdl");
SnowboyDetectSetSensitivity(detector, "0.5");
SnowboyDetectSetAudioGain(detector, 1);
snd_pcm_t *mic;
// 打开录音设备,阻塞方式
int error = snd_pcm_open(&mic, "hw:0,1", SND_PCM_STREAM_CAPTURE, 0);
if (error)
{
fprintf(stderr, "snd_pcm_open: %s\n", snd_strerror(error));
return EXIT_FAILURE;
}
// 设置录音参数
snd_pcm_hw_params_t *record_param;
// 在栈上分配参数内存并初始化
snd_pcm_hw_params_alloca(&record_param);
error += snd_pcm_hw_params_any(mic, record_param);
// 设置采样格式(有符号16位整数,小端序)
error += snd_pcm_hw_params_set_format(mic, record_param, SND_PCM_FORMAT_S16_LE);
// 设置访问方式(交错读写)
error += snd_pcm_hw_params_set_access(mic, record_param, SND_PCM_ACCESS_RW_INTERLEAVED);
// 设置采样频率(16KHz)
error += snd_pcm_hw_params_set_rate(mic, record_param, 16000, 0);
// 设置声道数量(单声道)
error += snd_pcm_hw_params_set_channels(mic, record_param, 1);
// 根据硬件参数配置声卡
error = snd_pcm_hw_params(mic, record_param);
if (error)
{
fprintf(stderr, "snd_pcm_hw_params: %s\n", snd_strerror(error));
return EXIT_FAILURE;
}
// 获取一个周期数据量,从声卡获取数据时,读写的最小单位是1个周期
snd_pcm_uframes_t nframe;
int dir = 0;
error = snd_pcm_hw_params_get_period_size(record_param, &nframe, &dir);
if (error)
{
fprintf(stderr, "snd_pcm_hw_params_get_period_size: %s\n", snd_strerror(error));
return EXIT_FAILURE;
}
printf("period = %lu frames\n", nframe);
// 分配缓冲区,大小必须为周期的倍数
char *buf = malloc(snd_pcm_frames_to_bytes(mic, nframe));
FILE *fp = NULL;
// 记录静音时间
int silence = 0;
// 语音识别
char *token = get_token(ak, sk);
if (!token)
{
return EXIT_FAILURE;
}
// 缓冲区
char *audio = NULL;
// 开始采集音频
snd_pcm_prepare(mic);
while (1)
{
snd_pcm_sframes_t rframe;
// 从声卡中读取数据,写入缓冲区
rframe = snd_pcm_readi(mic, buf, nframe);
if (rframe < 0)
{
fprintf(stderr, "snd_pcm_readi: %s\n", snd_strerror(rframe));
continue;
}
if (fp)
{
fwrite(buf, snd_pcm_frames_to_bytes(mic, rframe), 1, fp);
}
// 返回值:
// >0 检测到关键词
// =0 有声音但不是关键词
// =-1 出错
// =-2 没有声音
int result = SnowboyDetectRunDetection(detector, (int16_t *)buf, rframe, 0);
if (result > 0 && (fp == NULL))
{
// 开始录音
puts("recording");
// 暂停采集
snd_pcm_drop(mic);
// 打开录音文件
fp = fopen("command.pcm", "w+");
if (!fp)
{
perror("fopen");
}
system("aplay -q ding.wav"); // 提示录音开始
// 恢复采集
snd_pcm_prepare(mic);
}
if (result == -2 && (fp != NULL))
{
// 检测到静音并计时
silence++;
if (silence > 10)
{
// 停止录音
puts("stop");
// 暂停采集
snd_pcm_drop(mic);
fclose(fp);
fp = NULL;
system("aplay -q dong.wav"); // 提示录音结束
// 1.从文件中读取音频数据
size_t size = load_file("command.pcm", &audio);
if (!size)
{
free(token);
return EXIT_FAILURE;
}
// 2.发送HTTP请求报文,并返回服务器响应报文
char *response = send_request(token, audio, size);
free(audio);
// 3.识别
if (!response)
{
return EXIT_FAILURE;
}
char *line = NULL;
char *destination_string = NULL;
destination_string = process_response(response);
if (!destination_string)
{
line = (char *)malloc(strlen(destination_string) + 1);
strcpy(line, destination_string);
}
else
{
perror("process_response return NULL");
}
free(destination_string);
free(response);
// 4. NPL识别
//
cJSON *session_info = cJSON_CreateObject();
// while (fgets(line, sizeof line, stdin))
// {
char *nlp_response = send_nlp_request(nlp_token, line, session_info);
if (!nlp_response)
continue;
process_nlp_response(nlp_response, session_info);
// }
// 进行对话
// asr
// 恢复采集
free(line);
snd_pcm_prepare(mic);
}
}
if (result == 0)
{
// 有声音但不是唤醒词,静音检测时间归零
silence = 0;
}
}
// 停止采集
snd_pcm_drop(mic);
free(buf);
free(token);
// 关闭设备
snd_pcm_close(mic);
SnowboyDetectDestructor(detector);
return 0;
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化