From 372c09451d8e99dbfab63a695436e7fcdeeaab77 Mon Sep 17 00:00:00 2001 From: ZJY <1400329747@qq.com> Date: Tue, 2 Jul 2024 14:02:59 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AC=AC=E4=B9=9D=E7=BB=84=E7=AC=AC=E4=BA=8C?= =?UTF-8?q?=E6=AC=A1=E4=BD=9C=E4=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CMakeLists.txt" | 20 + .../control.c" | 434 ++++++++++++++++++ .../key.c" | 106 +++++ .../main.c" | 5 + .../play.c" | 147 ++++++ .../record.c" | 99 ++++ .../record.h" | 18 + .../wake.c" | 107 +++++ 8 files changed, 936 insertions(+) create mode 100644 "\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/CMakeLists.txt" create mode 100644 "\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/control.c" create mode 100644 "\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/key.c" create mode 100644 "\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/main.c" create mode 100644 "\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/play.c" create mode 100644 "\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/record.c" create mode 100644 "\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/record.h" create mode 100644 "\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/wake.c" diff --git "a/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/CMakeLists.txt" "b/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/CMakeLists.txt" new file mode 100644 index 0000000..a95ffce --- /dev/null +++ "b/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/CMakeLists.txt" @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.0.0) +project(voice-assistant VERSION 0.1.0 LANGUAGES C) + +add_executable(voice-assistant main.c) + +add_executable(control control.c) +target_link_libraries(control asound) + +#add_executable(record record.c) +#target_link_libraries(record asound) + +add_executable(play play.c) +target_link_libraries(play asound) + +add_executable(key key.c record.c) +target_link_libraries(key gpiod asound) + +add_subdirectory(snowboy) +add_executable(wake wake.c record.c) +target_link_libraries(wake snowboy-wrapper snowboy-detect cblas m stdc++ asound) \ No newline at end of file diff --git "a/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/control.c" "b/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/control.c" new file mode 100644 index 0000000..af482da --- /dev/null +++ "b/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/control.c" @@ -0,0 +1,434 @@ +#include +#include +#include + +// 设置音频采集开关的函数 +// card: 声卡名称 +// selem: 控制项名称 +// enable: 开关状态 +int set_capture_switch(const char* card, const char* selem, bool enable) { + int err; + snd_mixer_t *handle; + snd_mixer_selem_id_t *sid; + + // 打开混音器 + if ((err = snd_mixer_open(&handle, 0)) < 0) { + fprintf(stderr, "Mixer %s open error: %s\n", card, snd_strerror(err)); + return err; + } + + // 附加控制接口到混音器 + if ((err = snd_mixer_attach(handle, card)) < 0) { + fprintf(stderr, "Mixer attach %s error: %s\n", card, snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 注册混音器 + if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { + fprintf(stderr, "Mixer register error: %s\n", snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 加载混音器元素 + if ((err = snd_mixer_load(handle)) < 0) { + fprintf(stderr, "Mixer %s load error: %s\n", card, snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 分配简单元素ID + snd_mixer_selem_id_alloca(&sid); + + // 设置简单元素的名称 + snd_mixer_selem_id_set_name(sid, selem); + + // 查找简单元素 + snd_mixer_elem_t *elem = snd_mixer_find_selem(handle, sid); + if (!elem) { + fprintf(stderr, "Unable to find simple control '%s',%i\n", selem, 0); + snd_mixer_close(handle); + return -ENOENT; + } + + // 设置采集开关(启用或禁用) + if ((err = snd_mixer_selem_set_capture_switch_all(elem, enable ? 1 : 0)) < 0) { + fprintf(stderr, "Unable to set capture switch: %s\n", snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 关闭混音器 + snd_mixer_close(handle); + + return 0; // 成功 +} + +// 设置音频回放开关的函数 +// card: 声卡名称 +// selem: 控制项名称 +// enable: 开关状态 +int set_playback_switch(const char* card, const char* selem, bool enable) { + int err; + snd_mixer_t *handle; + snd_mixer_selem_id_t *sid; + + // 打开混音器 + if ((err = snd_mixer_open(&handle, 0)) < 0) { + fprintf(stderr, "Mixer %s open error: %s\n", card, snd_strerror(err)); + return err; + } + + // 附加控制接口到混音器 + if ((err = snd_mixer_attach(handle, card)) < 0) { + fprintf(stderr, "Mixer attach %s error: %s\n", card, snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 注册混音器 + if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { + fprintf(stderr, "Mixer register error: %s\n", snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 加载混音器元素 + if ((err = snd_mixer_load(handle)) < 0) { + fprintf(stderr, "Mixer %s load error: %s\n", card, snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 分配简单元素ID + snd_mixer_selem_id_alloca(&sid); + + // 设置简单元素的名称 + snd_mixer_selem_id_set_name(sid, selem); + + // 查找简单元素 + snd_mixer_elem_t *elem = snd_mixer_find_selem(handle, sid); + if (!elem) { + fprintf(stderr, "Unable to find simple control '%s',%i\n", selem, 0); + snd_mixer_close(handle); + return -ENOENT; + } + + // 设置回放开关(启用或禁用) + if ((err = snd_mixer_selem_set_playback_switch_all(elem, enable ? 1 : 0)) < 0) { + fprintf(stderr, "Unable to set playback switch: %s\n", snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 关闭混音器 + snd_mixer_close(handle); + + return 0; // 成功 +} + +// 获取音频采集音量 +// card: 声卡名称 +// selem: 控制项名称 +// 返回值: 当前音量 +int get_capture_volume(const char* card, const char* selem) +{ + int err; + snd_mixer_t *handle; + snd_mixer_selem_id_t *sid; + + // 打开混音器 + if ((err = snd_mixer_open(&handle, 0)) < 0) { + fprintf(stderr, "Mixer %s open error: %s\n", card, snd_strerror(err)); + return err; + } + + // 附加控制接口到混音器 + if ((err = snd_mixer_attach(handle, card)) < 0) { + fprintf(stderr, "Mixer attach %s error: %s\n", card, snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 注册混音器 + if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { + fprintf(stderr, "Mixer register error: %s\n", snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 加载混音器元素 + if ((err = snd_mixer_load(handle)) < 0) { + fprintf(stderr, "Mixer %s load error: %s\n", card, snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 分配简单元素ID + snd_mixer_selem_id_alloca(&sid); + + // 设置简单元素的名称 + snd_mixer_selem_id_set_name(sid, selem); + + // 查找简单元素 + snd_mixer_elem_t *elem = snd_mixer_find_selem(handle, sid); + if (!elem) { + fprintf(stderr, "Unable to find simple control '%s',%i\n", selem, 0); + snd_mixer_close(handle); + return -ENOENT; + } + + // 获取采集通道音量 + long volume = 0; + if ((err = snd_mixer_selem_get_capture_volume(elem, 0, &volume)) < 0) { + fprintf(stderr, "Unable to get capture volume: %s\n", snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 关闭混音器 + snd_mixer_close(handle); + + return volume; // 成功返回音量 +} + +// 获取音频回放通道音量 +// card: 声卡名称 +// selem: 控制项名称 +// 返回值: 当前音量 +int get_playback_volume(const char* card, const char* selem) { + int err; + snd_mixer_t *handle; + snd_mixer_selem_id_t *sid; + + // 打开混音器 + if ((err = snd_mixer_open(&handle, 0)) < 0) { + fprintf(stderr, "Mixer %s open error: %s\n", card, snd_strerror(err)); + return err; + } + + // 附加控制接口到混音器 + if ((err = snd_mixer_attach(handle, card)) < 0) { + fprintf(stderr, "Mixer attach %s error: %s\n", card, snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 注册混音器 + if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { + fprintf(stderr, "Mixer register error: %s\n", snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 加载混音器元素 + if ((err = snd_mixer_load(handle)) < 0) { + fprintf(stderr, "Mixer %s load error: %s\n", card, snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 分配简单元素ID + snd_mixer_selem_id_alloca(&sid); + + // 设置简单元素的名称 + snd_mixer_selem_id_set_name(sid, selem); + + // 查找简单元素 + snd_mixer_elem_t *elem = snd_mixer_find_selem(handle, sid); + if (!elem) { + fprintf(stderr, "Unable to find simple control '%s',%i\n", selem, 0); + snd_mixer_close(handle); + return -ENOENT; + } + + // 获取回放通道音量 + long volume = 0; + if ((err = snd_mixer_selem_get_playback_volume(elem, 0, &volume)) < 0) { + fprintf(stderr, "Unable to get playback volume: %s\n", snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 关闭混音器 + snd_mixer_close(handle); + + return volume; // 成功返回音量 +} + +// 设置音频采集音量 +// card: 声卡名称 +// selem: 控制项名称 +// volume: 设置音量 +// 返回值: 成功返回设置后的音量,失败返回错误码 +int set_capture_volume(const char* card, const char* selem, long volume) +{ + int err; + snd_mixer_t *handle; + snd_mixer_selem_id_t *sid; + + // 打开混音器 + if ((err = snd_mixer_open(&handle, 0)) < 0) { + fprintf(stderr, "Mixer %s open error: %s\n", card, snd_strerror(err)); + return err; + } + + // 附加控制接口到混音器 + if ((err = snd_mixer_attach(handle, card)) < 0) { + fprintf(stderr, "Mixer attach %s error: %s\n", card, snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 注册混音器 + if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { + fprintf(stderr, "Mixer register error: %s\n", snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 加载混音器元素 + if ((err = snd_mixer_load(handle)) < 0) { + fprintf(stderr, "Mixer %s load error: %s\n", card, snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 分配简单元素ID + snd_mixer_selem_id_alloca(&sid); + + // 设置简单元素的名称 + snd_mixer_selem_id_set_name(sid, selem); + + // 查找简单元素 + snd_mixer_elem_t *elem = snd_mixer_find_selem(handle, sid); + if (!elem) { + fprintf(stderr, "Unable to find simple control '%s',%i\n", selem, 0); + snd_mixer_close(handle); + return -ENOENT; + } + + // 获取音量范围 + long min, max; + if ((err = snd_mixer_selem_get_capture_volume_range(elem, &min, &max)) < 0) + { + fprintf(stderr, "Unable to get capture volume range: %s\n", snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + if (volume < min) + { + volume = min; + } + + if (volume > max) + { + volume = max; + } + + // 设置采集通道音量 + if ((err = snd_mixer_selem_set_capture_volume_all(elem, volume)) < 0) { + fprintf(stderr, "Unable to set capture volume: %s\n", snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 关闭混音器 + snd_mixer_close(handle); + + return volume; // 成功 +} + +// 设置音频回放通道音量 +// card: 声卡名称 +// selem: 控制项名称 +// volume: 设置音量 +// 返回值: 成功返回设置后的音量,失败返回错误码 +int set_playback_volume(const char* card, const char* selem, long volume) +{ + int err; + snd_mixer_t *handle; + snd_mixer_selem_id_t *sid; + + // 打开混音器 + if ((err = snd_mixer_open(&handle, 0)) < 0) { + fprintf(stderr, "Mixer %s open error: %s\n", card, snd_strerror(err)); + return err; + } + + // 附加控制接口到混音器 + if ((err = snd_mixer_attach(handle, card)) < 0) { + fprintf(stderr, "Mixer attach %s error: %s\n", card, snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 注册混音器 + if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { + fprintf(stderr, "Mixer register error: %s\n", snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 加载混音器元素 + if ((err = snd_mixer_load(handle)) < 0) { + fprintf(stderr, "Mixer %s load error: %s\n", card, snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 分配简单元素ID + snd_mixer_selem_id_alloca(&sid); + + // 设置简单元素的名称 + snd_mixer_selem_id_set_name(sid, selem); + + // 查找简单元素 + snd_mixer_elem_t *elem = snd_mixer_find_selem(handle, sid); + if (!elem) { + fprintf(stderr, "Unable to find simple control '%s',%i\n", selem, 0); + snd_mixer_close(handle); + return -ENOENT; + } + + // 获取音量范围 + long min, max; + if ((err = snd_mixer_selem_get_playback_volume_range(elem, &min, &max)) < 0) + { + fprintf(stderr, "Unable to get playback volume range: %s\n", snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + if (volume < min) + { + volume = min; + } + + if (volume > max) + { + volume = max; + } + + // 设置回放通道音量 + if ((err = snd_mixer_selem_set_playback_volume_all(elem, volume)) < 0) { + fprintf(stderr, "Unable to set playback volume: %s\n", snd_strerror(err)); + snd_mixer_close(handle); + return err; + } + + // 关闭混音器 + snd_mixer_close(handle); + + return volume; // 成功返回设置后音量 +} + +int main(int argc, char** argv) +{ + printf("Playback Vol: %d\n", get_playback_volume("hw:0", "Analog")); + printf("New Playback Vol: %d\n", set_playback_volume("hw:0", "Analog", atoi(argv[1]))); + + return 0; +} \ No newline at end of file diff --git "a/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/key.c" "b/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/key.c" new file mode 100644 index 0000000..0565d25 --- /dev/null +++ "b/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/key.c" @@ -0,0 +1,106 @@ +#include // 包含libgpiod库的头文件 +#include // 包含标准输入输出库 +#include // 包含UNIX标准库,用于usleep函数 +#include "record.h" + +#define GPIO_LINE 9 // 定义GPIO线的编号,这里是PF9 + +int main(void) { + struct gpiod_chip *chip; // 定义指向GPIO芯片的指针 + struct gpiod_line *line; // 定义指向GPIO线的指针 + int value, last_value; // 定义当前值和上一次的值,用于检测状态变化 + snd_pcm_uframes_t period; //周期大小 + snd_pcm_t* capture; //音频采集设备 + char* buffer = NULL; //存放音频数据的缓冲区 + FILE* fp = NULL; //录音文件 + + // 打开GPIO芯片 + chip = gpiod_chip_open_by_label("GPIOF"); + if (!chip) { + perror("打开GPIO芯片失败"); + return 1; + } + + // 获取GPIO线 + line = gpiod_chip_get_line(chip, GPIO_LINE); + if (!line) { + perror("获取GPIO线失败"); + gpiod_chip_close(chip); + return 1; + } + + // 将GPIO线设置为输入模式 + if (gpiod_line_request_input(line, "key1")) { + perror("请求将GPIO线设置为输入模式失败"); + gpiod_chip_close(chip); + return 1; + } + + // 获取初始的GPIO线值 + last_value = gpiod_line_get_value(line); + + // 无限循环检测GPIO线值的变化 + while (1) { + // 获取当前的GPIO线值 + value = gpiod_line_get_value(line); + + // 如果当前值与上一次的值不同,说明按键状态发生了变化 + if (value != last_value) { + // 如果当前值为0,表示按键被按下 + if (value == 0) { + printf("key pressed\n"); + capture = record_open("hw:0,1", SND_PCM_FORMAT_S16_LE, 2, 44100, &period); + if (!capture) + { + continue; + } + + buffer = malloc(snd_pcm_frames_to_bytes(capture, period)); // 分配缓冲区 + if (!buffer) + { + perror("malloc"); + record_close(capture); + continue; + } + + fp = fopen("output.pcm", "wb"); + if (!fp) + { + perror("Error opening output file"); + free(buffer); // 释放缓冲区 + record_close(capture); // 关闭PCM设备 + continue; + } + } + // 如果当前值为1,表示按键被释放 + else { + printf("key released\n"); + record_close(capture); + capture = NULL; + } + // 更新上一次的值为当前值 + last_value = value; + } + + if (value == 0 && capture) + { + //如果按键按下并且音频采集设备已打开 + snd_pcm_sframes_t frames = snd_pcm_readi(capture, buffer, period); // 从PCM设备读取数据 + if (frames < 0) + { + fprintf(stderr, "Error from read: %s\n", snd_strerror(frames)); + snd_pcm_recover(capture, frames, 0); + } + + fwrite(buffer, snd_pcm_frames_to_bytes(capture, frames), 1, fp); // 将读取的数据写入文件 + } + // 延时100毫秒,防止检测过于频繁 + //usleep(100000); + } + + // 释放GPIO线资源 + gpiod_line_release(line); + // 关闭GPIO芯片 + gpiod_chip_close(chip); + return 0; +} \ No newline at end of file diff --git "a/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/main.c" "b/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/main.c" new file mode 100644 index 0000000..7790bb1 --- /dev/null +++ "b/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/main.c" @@ -0,0 +1,5 @@ +#include + +int main(){ + printf("Hello, from voice-assistant!\n"); +} diff --git "a/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/play.c" "b/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/play.c" new file mode 100644 index 0000000..f31987b --- /dev/null +++ "b/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/play.c" @@ -0,0 +1,147 @@ +#include +#include +#include +#include // 用于处理错误码 + +//开始播放 +snd_pcm_t* play_open(const char* name, + snd_pcm_format_t format, + unsigned int channel, + unsigned int rate, + snd_pcm_uframes_t* period) +{ + snd_pcm_t *playback; // PCM设备句柄 + snd_pcm_hw_params_t *params; // PCM硬件参数 + int err; // 用于存储错误码 + int dir; + + // 打开PCM设备用于回放 + if ((err = snd_pcm_open(&playback, name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { + fprintf(stderr, "Error opening PCM device %s: %s\n", name, snd_strerror(err)); + return NULL; + } + + // 分配参数对象,并用默认值填充 + snd_pcm_hw_params_alloca(¶ms); + snd_pcm_hw_params_any(playback, params); + + // 设置参数 + // 设置访问类型:交错模式 + if ((err = snd_pcm_hw_params_set_access(playback, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + fprintf(stderr, "Error setting access: %s\n", snd_strerror(err)); + snd_pcm_close(playback); + return NULL; + } + // 设置数据格式:16位小端 + if ((err = snd_pcm_hw_params_set_format(playback, params, format)) < 0) { + fprintf(stderr, "Error setting format: %s\n", snd_strerror(err)); + snd_pcm_close(playback); + return NULL; + } + // 设置声道数:立体声 + if ((err = snd_pcm_hw_params_set_channels(playback, params, channel)) < 0) { + fprintf(stderr, "Error setting channels: %s\n", snd_strerror(err)); + snd_pcm_close(playback); + return NULL; + } + // 设置采样率 + if ((err = snd_pcm_hw_params_set_rate_near(playback, params, &rate, &dir)) < 0) { + fprintf(stderr, "Error setting rate: %s\n", snd_strerror(err)); + snd_pcm_close(playback); + return NULL; + } + + printf("sample rate: %d Hz\n", rate); + + //设置周期大小 + if ((err = snd_pcm_hw_params_set_period_size_near(playback, params, period, &dir)) < 0) { + fprintf(stderr, "Error setting period size: %s\n", snd_strerror(err)); + snd_pcm_close(playback); + return NULL; + } + + // 设置硬件参数 + if ((err = snd_pcm_hw_params(playback, params)) < 0) { + fprintf(stderr, "Error setting HW params: %s\n", snd_strerror(err)); + snd_pcm_close(playback); + return NULL; + } + + // 获取周期大小 + snd_pcm_hw_params_get_period_size(params, period, &dir); + + return playback; +} + +//停止播放 +void play_close(snd_pcm_t* playback) +{ + snd_pcm_drain(playback); // 排空PCM设备 + snd_pcm_close(playback); // 关闭PCM设备 +} + +int main() +{ + snd_pcm_t *playback; // PCM设备句柄 + snd_pcm_uframes_t period = 999; // 每个周期的帧数 + char *buffer; // 缓冲区,用于存储从文件中读取的音频数据 + FILE *pcm_file; // 输出PCM文件 + int err; // 用于存储错误码 + + playback = play_open("hw:0,0", SND_PCM_FORMAT_S16_LE, 2, 44100, &period); + if (!playback) + { + return 1; + } + + printf("period: %d frames\n", period); + + buffer = (char *) malloc(snd_pcm_frames_to_bytes(playback, period)); // 分配缓冲区 + if (!buffer) { + perror("malloc"); + play_close(playback); + return 1; + } + + // 打开输出文件 + pcm_file = fopen("output.pcm", "rb"); + if (!pcm_file) { + perror("Error opening output file"); + free(buffer); // 释放缓冲区 + play_close(playback); // 关闭PCM设备 + return 1; + } + + // 录制数据 + printf("Playing... Press Ctrl+C to stop.\n"); + while (1) { + size_t bytes = fread(buffer, 1, snd_pcm_frames_to_bytes(playback, period), pcm_file); + if (bytes == 0) + { + if (ferror(pcm_file)) + { + perror("fread"); + continue; + } + + if (feof(pcm_file)) + { + break; + } + } + + snd_pcm_sframes_t frames = snd_pcm_writei(playback, buffer, snd_pcm_bytes_to_frames(playback, bytes)); + if (frames < 0) + { + fprintf(stderr, "Error from write: %s\n", snd_strerror(frames)); + snd_pcm_recover(playback, frames, 0); + } + } + + // 清理资源 + free(buffer); // 释放缓冲区 + fclose(pcm_file); // 关闭文件 + play_close(playback); + + return 0; +} \ No newline at end of file diff --git "a/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/record.c" "b/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/record.c" new file mode 100644 index 0000000..d891805 --- /dev/null +++ "b/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/record.c" @@ -0,0 +1,99 @@ +#include +#include +#include +#include "record.h" + +// 开始录音 +snd_pcm_t* record_open(const char* name, + snd_pcm_format_t format, + unsigned int channel, + unsigned int rate, + snd_pcm_uframes_t* period) +{ + snd_pcm_t *capture; // PCM设备句柄 + snd_pcm_hw_params_t *params; // PCM硬件参数 + int err; // 用于存储错误码 + int dir; + + // 打开PCM设备用于录音(捕捉) + if ((err = snd_pcm_open(&capture, name, SND_PCM_STREAM_CAPTURE, 0)) < 0) { + fprintf(stderr, "Error opening PCM device %s: %s\n", name, snd_strerror(err)); + return NULL; + } + + // 分配参数对象,并用默认值填充 + snd_pcm_hw_params_alloca(¶ms); + snd_pcm_hw_params_any(capture, params); + + // 设置参数 + // 设置访问类型:交错模式 + if ((err = snd_pcm_hw_params_set_access(capture, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + fprintf(stderr, "Error setting access: %s\n", snd_strerror(err)); + snd_pcm_close(capture); + return NULL; + } + // 设置数据格式:16位小端 + if ((err = snd_pcm_hw_params_set_format(capture, params, format)) < 0) { + fprintf(stderr, "Error setting format: %s\n", snd_strerror(err)); + snd_pcm_close(capture); + return NULL; + } + // 设置声道数:立体声 + if ((err = snd_pcm_hw_params_set_channels(capture, params, channel)) < 0) { + fprintf(stderr, "Error setting channels: %s\n", snd_strerror(err)); + snd_pcm_close(capture); + return NULL; + } + // 设置采样率 + if ((err = snd_pcm_hw_params_set_rate_near(capture, params, &rate, &dir)) < 0) { + fprintf(stderr, "Error setting rate: %s\n", snd_strerror(err)); + snd_pcm_close(capture); + return NULL; + } + printf("sample rate: %d Hz\n", rate); + + // 设置周期大小 + if ((err = snd_pcm_hw_params_set_period_size_near(capture, params, period, &dir)) < 0) { + fprintf(stderr, "Error setting period size: %s\n", snd_strerror(err)); + snd_pcm_close(capture); + return NULL; + } + + // 设置硬件参数 + if ((err = snd_pcm_hw_params(capture, params)) < 0) { + fprintf(stderr, "Error setting HW params: %s\n", snd_strerror(err)); + snd_pcm_close(capture); + return NULL; + } + + // 获取周期大小 + snd_pcm_hw_params_get_period_size(params, period, &dir); + + return capture; +} + +// 停止录音 +void record_close(snd_pcm_t* capture) +{ + snd_pcm_drain(capture); // 排空PCM设备 + snd_pcm_close(capture); // 关闭PCM设备 +} + +// 开始录音到文件 +FILE* start_recording(const char* filename) +{ + FILE *pcm_file = fopen(filename, "wb"); + if (!pcm_file) { + perror("Error opening output file"); + return NULL; + } + return pcm_file; +} + +// 停止录音到文件 +void stop_recording(FILE* pcm_file) +{ + if (pcm_file) { + fclose(pcm_file); + } +} diff --git "a/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/record.h" "b/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/record.h" new file mode 100644 index 0000000..618391e --- /dev/null +++ "b/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/record.h" @@ -0,0 +1,18 @@ +#ifndef RECORD_H +#define RECORD_H +FILE* start_recording(const char* filename); +void stop_recording(FILE* pcm_file); + +#include + +//开始录音 +snd_pcm_t* record_open(const char* name, + snd_pcm_format_t format, + unsigned int channel, + unsigned int rate, + snd_pcm_uframes_t* period); + +//停止录音 +void record_close(snd_pcm_t* capture); + +#endif \ No newline at end of file diff --git "a/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/wake.c" "b/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/wake.c" new file mode 100644 index 0000000..a0fdc80 --- /dev/null +++ "b/\347\254\254\344\272\214\346\254\241\344\275\234\344\270\232/wake.c" @@ -0,0 +1,107 @@ +#include "snowboy/snowboy-detect-c-wrapper.h" +#include +#include +#include +#include "record.h" + +#define SILENCE_THRESHOLD 5 // 5秒的静音 + +int main() +{ + // 创建snowboy检测器 + SnowboyDetect* detector = SnowboyDetectConstructor("common.res", "laotie.pmdl"); + if (!detector) + { + return EXIT_FAILURE; + } + + // 获取检测器支持的音频数据参数 + int bits = SnowboyDetectBitsPerSample(detector); + int channels = SnowboyDetectNumChannels(detector); + int rate = SnowboyDetectSampleRate(detector); + + printf("采样深度: %d\n", bits); + printf("声道数量: %d\n", channels); + printf("采样频率: %d\n", rate); + + // 打开音频采集设备 + snd_pcm_uframes_t period = 999; + snd_pcm_t* capture = record_open("hw:0,0", SND_PCM_FORMAT_S16_LE, channels, rate, &period); + if (!capture) + { + return EXIT_FAILURE; + } + + char* buffer = malloc(snd_pcm_frames_to_bytes(capture, period)); // 分配缓冲区 + if (!buffer) { + perror("malloc"); + record_close(capture); + SnowboyDetectDestructor(detector); + return EXIT_FAILURE; + } + + int recording = 0; // 录音状态变量 + time_t silence_start = 0; // 静音开始时间变量 + FILE *pcm_file = NULL; // PCM文件指针 + + while (1) + { + snd_pcm_sframes_t frames = snd_pcm_readi(capture, buffer, period); // 从PCM设备读取数据 + if (frames < 0) + { + fprintf(stderr, "Error from read: %s\n", snd_strerror(frames)); + snd_pcm_recover(capture, frames, 0); + continue; + } + + int status = SnowboyDetectRunDetection(detector, (int16_t*)buffer, snd_pcm_frames_to_bytes(capture, frames) / sizeof(int16_t), 0); + + if (status > 0) + { + printf("检测到唤醒词\n"); + if (!recording) + { + printf("开始录音\n"); + pcm_file = start_recording("output.pcm"); + if (!pcm_file) + { + free(buffer); + record_close(capture); + SnowboyDetectDestructor(detector); + return EXIT_FAILURE; + } + recording = 1; + silence_start = 0; // 重置静音时间 + } + } + else if (status == -2 && recording) + { + if (silence_start == 0) + { + silence_start = time(NULL); // 记录静音开始时间 + } + else if (difftime(time(NULL), silence_start) >= SILENCE_THRESHOLD) + { + printf("检测到连续5秒的静音,停止录音\n"); + stop_recording(pcm_file); + recording = 0; + silence_start = 0; + } + } + else + { + silence_start = 0; // 重置静音时间 + } + + if (recording && pcm_file) + { + fwrite(buffer, snd_pcm_frames_to_bytes(capture, frames), 1, pcm_file); // 将录音数据写入文件 + } + } + + free(buffer); + record_close(capture); + SnowboyDetectDestructor(detector); + + return EXIT_SUCCESS; +} -- Gitee