123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 |
- #include "AudioRecord.h"
- #include "spdlog.h"
- #include <alsa/pcm.h>
- #include <chrono>
- #include <cstdint>
- #include <stdio.h>
- #include <stdlib.h>
- #include "spdlog/spdlog.h"
- /* 获取声卡名称列表 */
- bool AudioDevice::getAudioDevices(std::list<AudioDevice_t> &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::getAllSoundCard()
- // {
- // int err = 0;
- // void** hints;
- // err = snd_device_name_hint(-1, "pcm", &hints);
- // if(err != 0)
- // {
- // LH_WRITE_ERROR(QString("Get audio device count error %1").arg(snd_strerror(err)));
- // return;
- // }
- // QStringList audioDevList;
- // void** n = hints;
- // while(*n != NULL)
- // {
- // char* chname = snd_device_name_get_hint(*n, "NAME");
- // if(chname != NULL && 0 != strcmp("null", chname))
- // {
- // QByteArray devName(chname);
- // if(devName.contains("mic"))
- // {
- // audioDevList.append(QByteArray(chname));
- // }
- // //audioDevList.append(QByteArray(chname));
- // }
- // n++;
- // }
- // snd_device_name_free_hint((void**)hints);
- // }
- bool AudioDevice::getPCMAudioDevice(int cardID, std::list<AudioDeviceDesc_t> &devices)
- {
- void **hints;
- int err = snd_device_name_hint(cardID, "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<AudioDeviceDesc_t> &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;
- }
- /**
- * @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; // 返回读取到的字节数
- }
|