#include "AudioRecord.h" #include "spdlog.h" #include #include #include #include #include #include "spdlog/spdlog.h" /* 获取声卡名称列表 */ bool AudioDevice::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 AudioDevice::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 AudioDevice::printPCMAudioDevice(const std::list &devices) { for (const auto &device : devices) { SPDLOG_INFO("设备名称: {}, IOID: {}, 声卡: {}, 通道编号: {}, 设备描述: {}", device.DeviceName, device.IOID, device.Card, device.DevNum, device.DeviceDesc); } } /** * @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; } /* 关闭录音通道 */ void AudioRecord::closeRecordChannel() { if(m_captureHandle != nullptr) { snd_pcm_drain(m_captureHandle); snd_pcm_close(m_captureHandle); m_captureHandle = nullptr; } m_deviceName.clear(); } /** * @brief 读取录音大小 * * @param buffer 数据缓冲区指针 * @param bufferSize 缓冲区大小,缓冲区大小需要大于等于 recordFrames * 单个采样点的大小 * 例如:44100帧 * 2通道 * 2字节 * @param recordFrames 需要读取的帧数,一帧就是一个采样点,16bit / 2 * 2 通道 = 4字节 * 44100帧就是1秒 * @return int32_t 读取到的字节数 */ int AudioRecord::recordAudio(char* buffer, int32_t bufferSize, int32_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; // 返回读取到的字节数 }