From 1e1f6d8f41fc86effb854a370831927651b0dfe8 Mon Sep 17 00:00:00 2001 From: zhaohe Date: Thu, 29 Jun 2023 13:41:08 +0800 Subject: [PATCH] update --- README.md | 2 + demo/adc_capture/libiflytop_adc_capture.c | 90 +++++++++++++++---- demo/adc_capture/libiflytop_adc_capture.h | 8 +- demo/adc_capture/main.cpp | 143 +++++++++++++++++++++++++++++- 4 files changed, 225 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 821d6bd..12d6423 100644 --- a/README.md +++ b/README.md @@ -24,5 +24,7 @@ ADC 修改用户 DC +如何远程登陆 + ``` diff --git a/demo/adc_capture/libiflytop_adc_capture.c b/demo/adc_capture/libiflytop_adc_capture.c index b33b169..6aa947d 100644 --- a/demo/adc_capture/libiflytop_adc_capture.c +++ b/demo/adc_capture/libiflytop_adc_capture.c @@ -13,7 +13,7 @@ #include //for strerror() #include #include -static const char *m_deviceName = NULL; +// static const char *m_deviceName = NULL; static sound_capture_callback_t m_callback = NULL; static int m_fd = 0; static int m_sys_error = 0; @@ -91,7 +91,7 @@ error_code_t sound_capturer_init(const char *deviceName, int csgpionameId, sound IOCTL(m_fd, SPI_IOC_WR_MAX_SPEED_HZ, speed, ksp_init_spi_error_set_mode_fail); char cmd[256] = {0}; - sprintf(cmd, "echo %d > /sys/class/gpio/export", m_cs_gpio_id); + sprintf(cmd, "echo %d 1> /sys/class/gpio/export 2>/dev/null", m_cs_gpio_id); system(cmd); sprintf(cmd, "echo out > /sys/class/gpio/gpio%d/direction", m_cs_gpio_id); @@ -134,32 +134,90 @@ int spi_read(unsigned char *rxbuf, int len) { // gpio_set_value(1); printf("read %d bytes\n", ret); - return ret; } +#define MAX_ONE_FRAME_VOICE_SIZE 2000 + +static int32_t *multiplierX2(int32_t *voice, int32_t size, int32_t *outsize) { + static int32_t voicebuf[MAX_ONE_FRAME_VOICE_SIZE * 2]; // 50ms + if (size > MAX_ONE_FRAME_VOICE_SIZE) { + size = MAX_ONE_FRAME_VOICE_SIZE; + } + + for (size_t i = 0; i < size; i++) { + voicebuf[i * 2] = voice[i]; + if (i + 1 < size) { + voicebuf[i * 2 + 1] = (voice[i] + voice[i + 1]) / 2; + } + } + *outsize = size * 2; + return voicebuf; +} -void readvoice() { - static int32_t voicebuf[4800]; // 50ms - static int32_t voiceNum; // +static void filterVoice(int32_t *voice, int32_t size) { + /** + * @brief 简单的过滤器,如果当前点大于上一个点和下一个点的平均值的1.5倍,则将当前点设置为平均值 + */ + static int32_t lastvoice = 0; + lastvoice = voice[size - 1]; + + { // + int32_t avg = (lastvoice + voice[1]) / 2; + if (abs(voice[0]) > abs(avg * 1.5)) { + voice[0] = avg; + } + } - voiceNum = 0; + for (size_t i = 1; i < size - 1; i++) { + int32_t avg = (voice[i - 1] + voice[i + 1]) / 2; + if (abs(voice[i]) > abs(avg * 1.5)) { + voice[i] = avg; + } + } + + { + int32_t expectendvoice = voice[size - 2] + (voice[size - 2] - voice[size - 3]); + if (abs(voice[size - 1]) > abs(expectendvoice * 1.5)) { + voice[size - 1] = expectendvoice; + } + } +} + +static void readvoice() { + static int32_t voicebuf[MAX_ONE_FRAME_VOICE_SIZE]; // 50ms + static int32_t rxdatabytesize; // + + rxdatabytesize = 0; memset(voicebuf, 0, sizeof(voicebuf)); gpio_set_value(0); - usleep(20); - int ret = read(m_fd, &voiceNum, 4); - printf("read voiceNum:%d\n", voiceNum); - if (voiceNum <= 0) { + usleep(1500); // CS信号触发后,给单片机足够的时间准备音频数据 + int ret = read(m_fd, &rxdatabytesize, 4); + // printf("read rxdatabytesize:%d\n", rxdatabytesize); + if (rxdatabytesize <= 0) { gpio_set_value(1); return; } - if (voiceNum > 4800) { - printf("WARNING: voiceNum(%d) > voiceBufSize \n", voiceNum); - voiceNum = 4800; + if (rxdatabytesize > MAX_ONE_FRAME_VOICE_SIZE * 4) { + printf("WARNING: rxdatabytesize(%d) > voiceBufSize \n", rxdatabytesize); + gpio_set_value(1); + return; } - ret = read(m_fd, voicebuf, voiceNum * 4); + ret = read(m_fd, voicebuf, rxdatabytesize); gpio_set_value(1); + + // for (size_t i = 0; i < rxdatabytesize/4; i++) { + // printf("%d \n", voicebuf[i]); + // } + + { + int32_t voiceframesize = 0; + filterVoice(voicebuf, rxdatabytesize / 4); + int32_t *voicereport = multiplierX2(voicebuf, rxdatabytesize / 4, &voiceframesize); + if (m_callback) m_callback(voicereport, voiceframesize); + } + return; } @@ -167,7 +225,7 @@ static void *sound_read_thread_func(void *v) { while (true) { // 一次系统调用时间大概500us readvoice(); - usleep(5000); + usleep(10000); } return NULL; } diff --git a/demo/adc_capture/libiflytop_adc_capture.h b/demo/adc_capture/libiflytop_adc_capture.h index 7f289a1..7f49b66 100644 --- a/demo/adc_capture/libiflytop_adc_capture.h +++ b/demo/adc_capture/libiflytop_adc_capture.h @@ -4,7 +4,13 @@ #ifdef cplusplus extern "C" { #endif -typedef void (*sound_capture_callback_t)(uint32_t* data, uint32_t len); +/** + * @brief + * + * 该模块目前只支持上报48K,24bit的采样精度的音频数据 + * + */ +typedef void (*sound_capture_callback_t)(int32_t* data, uint32_t len); typedef enum { ksp_success, diff --git a/demo/adc_capture/main.cpp b/demo/adc_capture/main.cpp index 1e9388c..a7b2c77 100644 --- a/demo/adc_capture/main.cpp +++ b/demo/adc_capture/main.cpp @@ -10,13 +10,154 @@ #include #include #include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + extern "C" { #include "libiflytop_adc_capture.h" } -void sound_capture_callback(uint32_t* data, uint32_t len) {} +using namespace std; + +class WAVHeader { + public: + /** + * @brief WAV 文件头部信息的二进制格式 + * + * WAV 文件头部信息包含了如下字段: + * - 固定为 "RIFF" 的 4 字节 chunk_id + * - 文件总大小 - 8 的 4 字节 chunk_size + * - 固定为 "WAVE" 的 4 字节 format + * - 固定为 "fmt " 的 4 字节 subchunk1_id + * - 固定为 16 的 4 字节 subchunk1_size + * - 编码格式,1 表示 PCM 编码的 2 字节 audio_format + * - 声道数,1 为单声道,2 为立体声的 2 字节 num_channels + * - 采样率,如 44100 的 4 字节 sample_rate + * - 数据传输速率,即采样率 * 声道数 * 位数 / 8 的 4 字节 byte_rate + * - 数据块对齐,即每个采样点占用的字节数,如 2 表示 16 位,即 2 字节的 2 字节 block_align + * - 位数,如 8、16、24、32 的 2 字节 bits_per_sample + * - 固定为 "data" 的 4 字节 subchunk2_id + * - 数据长度,即文件总大小 - 44 的 4 字节 subchunk2_size + */ + struct WAVHeaderBinary { + char chunk_id[4]; // 固定为 "RIFF" + uint32_t chunk_size; // 文件总大小 - 8 + char format[4]; // 固定为 "WAVE" + char subchunk1_id[4]; // 固定为 "fmt " + uint32_t subchunk1_size; // 固定为 16 + uint16_t audio_format; // 编码格式,1 表示 PCM 编码 + uint16_t num_channels; // 声道数,1 为单声道,2 为立体声 + uint32_t sample_rate; // 采样率,如 44100 + uint32_t byte_rate; // 数据传输速率,即采样率 * 声道数 * 位数 / 8 + uint16_t block_align; // 数据块对齐,即每个采样点占用的字节数,如 2 表示 16 位,即 2 字节 + uint16_t bits_per_sample; // 位数,如 8、16、24、32 + char subchunk2_id[4]; // 固定为 "data" + uint32_t subchunk2_size; // 数据长度,即文件总大小 - 44 + }; + WAVHeaderBinary header; + + public: + WAVHeader() {} + void initialize(uint32_t sample_rate, uint16_t bits_per_sample, uint16_t num_channels, uint32_t wavbinsize) { + // 填充 RIFF 头 + memcpy(header.chunk_id, "RIFF", 4); + header.chunk_size = wavbinsize + 44 - 8; + memcpy(header.format, "WAVE", 4); + + // 填充 fmt 子块 + memcpy(header.subchunk1_id, "fmt ", 4); + header.subchunk1_size = 16; + header.audio_format = 1; // PCM 编码 + header.num_channels = num_channels; + header.sample_rate = sample_rate; + header.byte_rate = sample_rate * num_channels * bits_per_sample / 8; + header.block_align = num_channels * bits_per_sample / 8; + header.bits_per_sample = bits_per_sample; + + // 填充 data 子块 + memcpy(header.subchunk2_id, "data", 4); + header.subchunk2_size = wavbinsize; + } + void resize(uint32_t wavbinsize) { + header.chunk_size = wavbinsize + 44 - 8; + header.subchunk2_size = wavbinsize; + } + + void dump() { + printf("chunk_id: %s\n", string(header.chunk_id, 4).c_str()); + printf("chunk_size: %d\n", header.chunk_size); + printf("format: %s\n", string(header.format, 4).c_str()); + printf("subchunk1_id: %s\n", string(header.subchunk1_id, 4).c_str()); + printf("subchunk1_size: %d\n", header.subchunk1_size); + printf("audio_format: %d\n", header.audio_format); + printf("num_channels: %d\n", header.num_channels); + printf("sample_rate: %d\n", header.sample_rate); + printf("byte_rate: %d\n", header.byte_rate); + printf("block_align: %d\n", header.block_align); + printf("bits_per_sample: %d\n", header.bits_per_sample); + printf("subchunk2_id: %s\n", header.subchunk2_id); + printf("subchunk2_size: %d\n", header.subchunk2_size); + } + + WAVHeader(uint32_t sample_rate, uint16_t bits_per_sample, uint16_t num_channels, uint32_t num_samples) { + initialize(sample_rate, bits_per_sample, num_channels, num_samples); + } + uint8_t* data() { return (uint8_t*)&header; } + size_t size() { return sizeof(header); } +}; + +void wavWriteVoice(string filename, uint32_t sample_rate, uint16_t bits_per_sample, uint16_t num_channels, const char* data, uint32_t size) { + fstream file; + WAVHeader wavHeader; + // logger->info("writeVoice filename:{} {} {} {} {}", filename, sample_rate, bits_per_sample, num_channels, size); + file.open(filename, fstream::in | fstream::out | fstream::binary); + if (!file.is_open()) { + file.open(filename, fstream::out | fstream::binary | fstream::trunc); + wavHeader.initialize(sample_rate, bits_per_sample, num_channels, 0); + file.seekp(0, ios::beg); + file.write((char*)wavHeader.data(), wavHeader.size()); + file.close(); + file.open(filename, fstream::in | fstream::out | fstream::binary); + } + + // file.write(data, size); +#if 1 + /** + * @brief 判断文件大小,文件大小大于等于44字节,说明文件已经存在,不需要写入头部 + */ + file.seekp(0, ios::end); + file.write(data, size); + /** + * @brief 更新头部信息 + */ + uint32_t filesize = file.tellp(); + file.seekp(4, ios::beg); + uint32_t chunk_size = filesize - 8; + file.write((char*)&chunk_size, 4); + file.seekp(40, ios::beg); + uint32_t subchunk2_size = filesize - 44; + file.write((char*)&subchunk2_size, 4); +#endif + + file.close(); +} + +void sound_capture_callback(int32_t* voicedata, uint32_t len) { + printf("sound_capture_callback:%d\n", len); + wavWriteVoice("record.wav", 48000, 32, 1, (char*)voicedata, len * sizeof(int32_t)); +} int main(int argc, char const* argv[]) { + system("rm -rf record.wav"); + error_code_t erro_code = sound_capturer_init("/dev/spidev1.0", 59, sound_capture_callback); if (erro_code != ksp_success) { sound_capturer_dump_last_error();