#include "AudioRecord.h" #include "spdlog.h" #include #include #include #include #include #include "spdlog/spdlog.h" /* 获取声卡名称列表 */ bool getAudioDevices(std::list &devices) { snd_ctl_t *card_handle; snd_ctl_card_info_t *info; int card = -1; while (snd_card_next(&card) >= 0 && card >= 0) { char name[32]; snprintf(name, sizeof(name), "hw:%d", card); if (snd_ctl_open(&card_handle, name, 0) < 0) { continue; // 无法打开声卡 } snd_ctl_card_info_alloca(&info); if (snd_ctl_card_info(card_handle, info) < 0) { snd_ctl_close(card_handle); continue; // 获取声卡信息失败 } AudioDevice_t device; /* 获取声卡信息 */ device.CardNumber = card; device.CardID = snd_ctl_card_info_get_id(info); device.CardName = snd_ctl_card_info_get_name(info); device.CardDriver = snd_ctl_card_info_get_driver(info); device.CardLongName = snd_ctl_card_info_get_longname(info); device.CardMixername = snd_ctl_card_info_get_mixername(info); device.CardComponents = snd_ctl_card_info_get_components(info); /* 获取声卡中的所有pcm设备 */ snd_pcm_info_t *pcm_info; snd_pcm_info_alloca(&pcm_info); int pcmDevice = -1; while(true) { if(snd_ctl_pcm_next_device(card_handle, &pcmDevice) < 0 || pcmDevice < 0) { break; // 没有更多的PCM设备 } snd_pcm_info_set_device(pcm_info, pcmDevice); snd_pcm_info_set_subdevice(pcm_info, 0); // 获取主设备 snd_pcm_info_set_stream(pcm_info, SND_PCM_STREAM_CAPTURE); // 获取捕获设备信息 if(snd_ctl_pcm_info(card_handle, pcm_info) < 0) { continue; // 获取PCM设备信息失败 } PCMDevice_t pcmDeviceInfo; pcmDeviceInfo.PCMDevice = snd_pcm_info_get_device(pcm_info); pcmDeviceInfo.SubDevice = snd_pcm_info_get_subdevice(pcm_info); pcmDeviceInfo.CardNumber = card; pcmDeviceInfo.PCMID = snd_pcm_info_get_id(pcm_info); pcmDeviceInfo.PCMName = snd_pcm_info_get_name(pcm_info); pcmDeviceInfo.PCMSubName = snd_pcm_info_get_subdevice_name(pcm_info); // 将PCM设备信息添加到声卡设备列表中 device.PCMDevices.push_back(pcmDeviceInfo); } devices.push_back(device); snd_ctl_close(card_handle); } return true; } bool getPCMAudioDevice(std::list &devices) { void **hints; int err = snd_device_name_hint(-1, "pcm", &hints); if (err < 0) { SPDLOG_ERROR("无法获取声卡设备信息: {}", snd_strerror(err)); return false; } if (hints == NULL) { SPDLOG_ERROR("没有找到任何声卡设备"); return false; } void **n = hints; while (*n != NULL) { char *name = snd_device_name_get_hint(*n, "NAME"); // 设备字符 char *desc = snd_device_name_get_hint(*n, "DESC"); // 设备描述 char *ioid = snd_device_name_get_hint(*n, "IOID"); // 输入/输出类型 char *card = snd_device_name_get_hint(*n, "CARD"); // 声卡名称 char* devName = snd_device_name_get_hint(*n, "DEV"); /* 设备编号 */ if (name == nullptr) { continue; // 跳过没有名称的设备 } AudioDeviceDesc_t deviceDesc; deviceDesc.DeviceName = name; deviceDesc.DeviceDesc = desc ? desc : "无描述"; deviceDesc.IOID = ioid ? ioid : "未知类型"; deviceDesc.Card = card ? card : "未知声卡"; deviceDesc.DevNum = devName ? devName : "未知设备编号"; devices.push_back(deviceDesc); if (name) free(name); if (desc) free(desc); if (ioid) free(ioid); if (card) free(card); if (devName) free(devName); n++; } snd_device_name_free_hint(hints); return true; } // void record(std::string deviceName) // { // uint32_t sample_rate = 44100; // uint32_t channels = 2; // int i; // int err; // long unsigned int rframes = 44100; // 每次读取的帧数 // short* buf = (short*)malloc(rframes * sizeof(short) * channels); // if (!buf) { // fprintf(stderr, "Failed to allocate buffer memory\n"); // exit(1); // } // snd_pcm_t *capture_handle; // snd_pcm_hw_params_t *hw_params; // if ((err = snd_pcm_open (&capture_handle, deviceName.c_str(), SND_PCM_STREAM_CAPTURE, 0)) < 0) { // fprintf (stderr, "cannot open audio device %s (%s)\n", // deviceName.c_str(), // snd_strerror (err)); // exit (1); // } // /* 给参数分配内存 */ // if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { // fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n", // snd_strerror (err)); // exit (1); // } // /* 初始化硬件参数结构 */ // if ((err = snd_pcm_hw_params_any (capture_handle, hw_params)) < 0) { // fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", // snd_strerror (err)); // exit (1); // } // /* 设置硬件参数 */ // if ((err = snd_pcm_hw_params_set_access (capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { // fprintf (stderr, "cannot set access type (%s)\n", // snd_strerror (err)); // exit (1); // } // /* 设置采样格式 */ // // SND_PCM_FORMAT_S16_LE: 16位小端格式 // // SND_PCM_FORMAT_FLOAT_LE: 32位小端浮点格式 // // SND_PCM_FORMAT_U8: 8位无符号格式 // // SND_PCM_FORMAT_S32_LE: 32位小端整数格式 // // SND_PCM_FORMAT_S24_LE: 24位小端整数格式 // if ((err = snd_pcm_hw_params_set_format (capture_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) { // fprintf (stderr, "cannot set sample format (%s)\n", // snd_strerror (err)); // exit (1); // } // /* 设置采样率 */ // if ((err = snd_pcm_hw_params_set_rate_near (capture_handle, hw_params, &sample_rate, 0)) < 0) { // fprintf (stderr, "cannot set sample rate (%s)\n", // snd_strerror (err)); // exit (1); // } // /* 设置通道数 */ // if ((err = snd_pcm_hw_params_set_channels (capture_handle, hw_params, channels)) < 0) { // fprintf (stderr, "cannot set channel count (%s)\n", // snd_strerror (err)); // exit (1); // } // /* 设置交错模式 */ // err = snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); // if (err < 0) { // fprintf(stderr, "cannot set interleaved mode (%s)\n", snd_strerror(err)); // exit(1); // } // /* 设置周期大小 */ // err = snd_pcm_hw_params_set_period_size(capture_handle, hw_params, 882, 0); // if (err < 0) { // fprintf(stderr, "cannot set period size (%s)\n", snd_strerror(err)); // exit(1); // } // /* 设置缓冲区大小 */ // err = snd_pcm_hw_params_set_buffer_size(capture_handle, hw_params, 8820); // if (err < 0) { // fprintf(stderr, "cannot set buffer size (%s)\n", snd_strerror(err)); // exit(1); // } // /* 设置参数给声卡 */ // if ((err = snd_pcm_hw_params (capture_handle, hw_params)) < 0) { // fprintf (stderr, "cannot set parameters (%s)\n", // snd_strerror (err)); // exit (1); // } // /* 释放参数内存 */ // snd_pcm_hw_params_free (hw_params); // if ((err = snd_pcm_prepare (capture_handle)) < 0) { // fprintf (stderr, "cannot prepare audio interface for use (%s)\n", // snd_strerror (err)); // exit (1); // } // auto f1 = fopen("r1.wav", "wb"); // if(!f1) { // fprintf(stderr, "Failed to open file for writing\n"); // exit(1); // } // std::chrono::steady_clock::time_point start_time = std::chrono::steady_clock::now(); // for (i = 0; i < 10; ++i) // { // long unsigned int readNum; // start_time = std::chrono::steady_clock::now(); // if ((readNum = snd_pcm_readi (capture_handle, buf, rframes)) != rframes) { // fprintf (stderr, "read from audio interface failed (%s)\n", // snd_strerror (readNum)); // exit (1); // } // auto end_time = std::chrono::steady_clock::now(); // auto duration = std::chrono::duration_cast(end_time - start_time); // SPDLOG_DEBUG("第 {} 次录音,读取数据量: {} 帧,{} 字节,耗时: {} 毫秒", i + 1, readNum, readNum, duration.count()); // if (fwrite(buf, sizeof(short), readNum * 2, f1) != readNum * 2) { // fprintf(stderr, "Failed to write data to file\n"); // fclose(f1); // exit(1); // } // } // fclose(f1); // snd_pcm_close (capture_handle); // free(buf); // printf("录音完成,数据已保存到 r1.wav\n"); // exit (0); // } /** * @brief Set the Record Params object * * @param sampleRate 采样率 * @param bits 位深度,支持8位,16位,24位,32位 * @param channels 通道数 */ void AudioRecord::setRecordParams(int sampleRate, int bits, int channels) { m_sampleRate = sampleRate; m_bitDepth = bits; m_channels = channels; m_oneSampleSize = (m_bitDepth / 8) * m_channels; // 单个采样点的大小,单位字节 } /* 打开录音通道 */ bool AudioRecord::openRecordChannel(const std::string &deviceName) { m_deviceName = deviceName; /* 计算缓冲区大小和周期大小 * 周期大小是指一个周期有多少个采样点,缓冲区大小是周期大小 * 周期个数,这里不需要设置周期个数 * 直接设置缓冲区大小,系统会自动计算周期个数,缓冲区是个环形队列 * 周期大小最好设置成可以被采样率的整数除尽,每次取出1秒数据的时候,间隔相差时间最短 */ snd_pcm_uframes_t buffer_size = m_sampleRate / 5; // 设置缓冲区大小为采样率的1/5秒 snd_pcm_uframes_t period_size = buffer_size / 10; // 设置周期大小为缓冲区大小的1/10秒 _snd_pcm_format bit_frame = _snd_pcm_format::SND_PCM_FORMAT_UNKNOWN; if(m_bitDepth == 8) { bit_frame = SND_PCM_FORMAT_S8; // 8位采样格式 } else if (m_bitDepth == 16) { bit_frame = SND_PCM_FORMAT_S16_LE; // 16位小端格式 } else if (m_bitDepth == 24) { bit_frame = SND_PCM_FORMAT_S24_LE; // 24位小端格式 } else if (m_bitDepth == 32) { bit_frame = SND_PCM_FORMAT_S32_LE; // 32位小端格式 } else { SPDLOG_ERROR("不支持的采样位深度: {}", m_bitDepth); return false; } snd_pcm_hw_params_t *hw_params; int err; /* 打开声卡设备 */ if ((err = snd_pcm_open(&m_captureHandle, deviceName.c_str(), SND_PCM_STREAM_CAPTURE, 0)) < 0) { SPDLOG_ERROR("无法打开音频设备 {}: {}", deviceName, snd_strerror(err)); return false; } /* 分配硬件参数结构 */ if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) { SPDLOG_ERROR("无法分配硬件参数结构: {}", snd_strerror(err)); snd_pcm_close(m_captureHandle); return false; } /* 初始化硬件参数结构 */ if ((err = snd_pcm_hw_params_any(m_captureHandle, hw_params)) < 0) { SPDLOG_ERROR("无法初始化硬件参数结构: {}", snd_strerror(err)); goto snd_free; return false; } /* 设置访问类型 */ if ((err = snd_pcm_hw_params_set_access(m_captureHandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { SPDLOG_ERROR("无法设置访问类型: {}", snd_strerror(err)); goto snd_free; return false; } /* 设置采样格式 */ if ((err = snd_pcm_hw_params_set_format(m_captureHandle, hw_params, bit_frame)) < 0) { SPDLOG_ERROR("无法设置采样格式: {}", snd_strerror(err)); goto snd_free; return false; } /* 设置采样率 */ if ((err = snd_pcm_hw_params_set_rate(m_captureHandle, hw_params, m_sampleRate, 0)) < 0) { SPDLOG_ERROR("无法设置采样率: {}", snd_strerror(err)); goto snd_free; return false; } /* 设置通道数 */ if ((err = snd_pcm_hw_params_set_channels(m_captureHandle, hw_params, m_channels)) < 0) { SPDLOG_ERROR("无法设置通道数: {}", snd_strerror(err)); goto snd_free; return false; } /* 设置缓冲区大小 */ if ((err = snd_pcm_hw_params_set_buffer_size(m_captureHandle, hw_params, buffer_size)) < 0) { SPDLOG_ERROR("无法设置缓冲区大小: {}", snd_strerror(err)); goto snd_free; return false; } /* 设置周期大小 */ if ((err = snd_pcm_hw_params_set_period_size(m_captureHandle, hw_params, period_size, 0)) < 0) { SPDLOG_ERROR("无法设置周期大小: {}", snd_strerror(err)); goto snd_free; return false; } /* 应用硬件参数 */ if ((err = snd_pcm_hw_params(m_captureHandle, hw_params)) < 0) { SPDLOG_ERROR("无法应用硬件参数: {}", snd_strerror(err)); goto snd_free; return false; } /* 释放硬件参数结构 */ snd_pcm_hw_params_free(hw_params); /* 准备PCM设备 */ if ((err = snd_pcm_prepare(m_captureHandle)) < 0) { SPDLOG_ERROR("无法准备PCM设备: {}", snd_strerror(err)); snd_pcm_close(m_captureHandle); return false; } return true; snd_free: snd_pcm_hw_params_free(hw_params); snd_pcm_close(m_captureHandle); return false; } /** * @brief 读取录音大小 * * @param buffer 数据缓冲区指针 * @param bufferSize 缓冲区大小,缓冲区大小需要大于等于 recordFrames * 单个采样点的大小 * 例如:44100帧 * 2通道 * 2字节 * @param recordFrames 需要读取的帧数,一帧就是一个采样点,16bit / 2 * 2 通道 = 4字节 * 44100帧就是1秒 * @return uint32_t 读取到的字节数 */ int AudioRecord::recordAudio(char* buffer, uint32_t bufferSize, uint32_t recordFrames) { if(m_captureHandle == nullptr) { SPDLOG_WARN("PCM设备未打开,请先调用 openAudioChannel 打开设备"); return -1; } if (buffer == nullptr || bufferSize == 0) { SPDLOG_WARN("缓冲区指针为空或缓冲区大小为0"); return -1; } if(recordFrames * m_oneSampleSize > bufferSize) { SPDLOG_ERROR("缓冲区大小不足,无法存储 {} 帧数据", recordFrames); return -1; } auto readFrames = snd_pcm_readi(m_captureHandle, buffer, recordFrames); if (readFrames < 0) { SPDLOG_ERROR("获取录音数据失败: {}", snd_strerror(readFrames)); return -1; } return readFrames * m_oneSampleSize; // 返回读取到的字节数 }