AudioRecord.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. #include "AudioRecord.h"
  2. #include "spdlog.h"
  3. #include <alsa/pcm.h>
  4. #include <chrono>
  5. #include <cstdint>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include "spdlog/spdlog.h"
  9. /* 获取声卡名称列表 */
  10. bool getAudioDevices(std::list<AudioDevice_t> &devices)
  11. {
  12. snd_ctl_t *card_handle;
  13. snd_ctl_card_info_t *info;
  14. int card = -1;
  15. while (snd_card_next(&card) >= 0 && card >= 0)
  16. {
  17. char name[32];
  18. snprintf(name, sizeof(name), "hw:%d", card);
  19. if (snd_ctl_open(&card_handle, name, 0) < 0) {
  20. continue; // 无法打开声卡
  21. }
  22. snd_ctl_card_info_alloca(&info);
  23. if (snd_ctl_card_info(card_handle, info) < 0) {
  24. snd_ctl_close(card_handle);
  25. continue; // 获取声卡信息失败
  26. }
  27. AudioDevice_t device;
  28. /* 获取声卡信息 */
  29. device.CardNumber = card;
  30. device.CardID = snd_ctl_card_info_get_id(info);
  31. device.CardName = snd_ctl_card_info_get_name(info);
  32. device.CardDriver = snd_ctl_card_info_get_driver(info);
  33. device.CardLongName = snd_ctl_card_info_get_longname(info);
  34. device.CardMixername = snd_ctl_card_info_get_mixername(info);
  35. device.CardComponents = snd_ctl_card_info_get_components(info);
  36. /* 获取声卡中的所有pcm设备 */
  37. snd_pcm_info_t *pcm_info;
  38. snd_pcm_info_alloca(&pcm_info);
  39. int pcmDevice = -1;
  40. while(true)
  41. {
  42. if(snd_ctl_pcm_next_device(card_handle, &pcmDevice) < 0 || pcmDevice < 0)
  43. {
  44. break; // 没有更多的PCM设备
  45. }
  46. snd_pcm_info_set_device(pcm_info, pcmDevice);
  47. snd_pcm_info_set_subdevice(pcm_info, 0); // 获取主设备
  48. snd_pcm_info_set_stream(pcm_info, SND_PCM_STREAM_CAPTURE); // 获取捕获设备信息
  49. if(snd_ctl_pcm_info(card_handle, pcm_info) < 0)
  50. {
  51. continue; // 获取PCM设备信息失败
  52. }
  53. PCMDevice_t pcmDeviceInfo;
  54. pcmDeviceInfo.PCMDevice = snd_pcm_info_get_device(pcm_info);
  55. pcmDeviceInfo.SubDevice = snd_pcm_info_get_subdevice(pcm_info);
  56. pcmDeviceInfo.CardNumber = card;
  57. pcmDeviceInfo.PCMID = snd_pcm_info_get_id(pcm_info);
  58. pcmDeviceInfo.PCMName = snd_pcm_info_get_name(pcm_info);
  59. pcmDeviceInfo.PCMSubName = snd_pcm_info_get_subdevice_name(pcm_info);
  60. // 将PCM设备信息添加到声卡设备列表中
  61. device.PCMDevices.push_back(pcmDeviceInfo);
  62. }
  63. devices.push_back(device);
  64. snd_ctl_close(card_handle);
  65. }
  66. return true;
  67. }
  68. bool getPCMAudioDevice(std::list<AudioDeviceDesc_t> &devices)
  69. {
  70. void **hints;
  71. int err = snd_device_name_hint(-1, "pcm", &hints);
  72. if (err < 0) {
  73. SPDLOG_ERROR("无法获取声卡设备信息: {}", snd_strerror(err));
  74. return false;
  75. }
  76. if (hints == NULL) {
  77. SPDLOG_ERROR("没有找到任何声卡设备");
  78. return false;
  79. }
  80. void **n = hints;
  81. while (*n != NULL) {
  82. char *name = snd_device_name_get_hint(*n, "NAME"); // 设备字符
  83. char *desc = snd_device_name_get_hint(*n, "DESC"); // 设备描述
  84. char *ioid = snd_device_name_get_hint(*n, "IOID"); // 输入/输出类型
  85. char *card = snd_device_name_get_hint(*n, "CARD"); // 声卡名称
  86. char* devName = snd_device_name_get_hint(*n, "DEV"); /* 设备编号 */
  87. if (name == nullptr)
  88. {
  89. continue; // 跳过没有名称的设备
  90. }
  91. AudioDeviceDesc_t deviceDesc;
  92. deviceDesc.DeviceName = name;
  93. deviceDesc.DeviceDesc = desc ? desc : "无描述";
  94. deviceDesc.IOID = ioid ? ioid : "未知类型";
  95. deviceDesc.Card = card ? card : "未知声卡";
  96. deviceDesc.DevNum = devName ? devName : "未知设备编号";
  97. devices.push_back(deviceDesc);
  98. if (name) free(name);
  99. if (desc) free(desc);
  100. if (ioid) free(ioid);
  101. if (card) free(card);
  102. if (devName) free(devName);
  103. n++;
  104. }
  105. snd_device_name_free_hint(hints);
  106. return true;
  107. }
  108. // void record(std::string deviceName)
  109. // {
  110. // uint32_t sample_rate = 44100;
  111. // uint32_t channels = 2;
  112. // int i;
  113. // int err;
  114. // long unsigned int rframes = 44100; // 每次读取的帧数
  115. // short* buf = (short*)malloc(rframes * sizeof(short) * channels);
  116. // if (!buf) {
  117. // fprintf(stderr, "Failed to allocate buffer memory\n");
  118. // exit(1);
  119. // }
  120. // snd_pcm_t *capture_handle;
  121. // snd_pcm_hw_params_t *hw_params;
  122. // if ((err = snd_pcm_open (&capture_handle, deviceName.c_str(), SND_PCM_STREAM_CAPTURE, 0)) < 0) {
  123. // fprintf (stderr, "cannot open audio device %s (%s)\n",
  124. // deviceName.c_str(),
  125. // snd_strerror (err));
  126. // exit (1);
  127. // }
  128. // /* 给参数分配内存 */
  129. // if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
  130. // fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
  131. // snd_strerror (err));
  132. // exit (1);
  133. // }
  134. // /* 初始化硬件参数结构 */
  135. // if ((err = snd_pcm_hw_params_any (capture_handle, hw_params)) < 0) {
  136. // fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
  137. // snd_strerror (err));
  138. // exit (1);
  139. // }
  140. // /* 设置硬件参数 */
  141. // if ((err = snd_pcm_hw_params_set_access (capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
  142. // fprintf (stderr, "cannot set access type (%s)\n",
  143. // snd_strerror (err));
  144. // exit (1);
  145. // }
  146. // /* 设置采样格式 */
  147. // // SND_PCM_FORMAT_S16_LE: 16位小端格式
  148. // // SND_PCM_FORMAT_FLOAT_LE: 32位小端浮点格式
  149. // // SND_PCM_FORMAT_U8: 8位无符号格式
  150. // // SND_PCM_FORMAT_S32_LE: 32位小端整数格式
  151. // // SND_PCM_FORMAT_S24_LE: 24位小端整数格式
  152. // if ((err = snd_pcm_hw_params_set_format (capture_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
  153. // fprintf (stderr, "cannot set sample format (%s)\n",
  154. // snd_strerror (err));
  155. // exit (1);
  156. // }
  157. // /* 设置采样率 */
  158. // if ((err = snd_pcm_hw_params_set_rate_near (capture_handle, hw_params, &sample_rate, 0)) < 0) {
  159. // fprintf (stderr, "cannot set sample rate (%s)\n",
  160. // snd_strerror (err));
  161. // exit (1);
  162. // }
  163. // /* 设置通道数 */
  164. // if ((err = snd_pcm_hw_params_set_channels (capture_handle, hw_params, channels)) < 0) {
  165. // fprintf (stderr, "cannot set channel count (%s)\n",
  166. // snd_strerror (err));
  167. // exit (1);
  168. // }
  169. // /* 设置交错模式 */
  170. // err = snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
  171. // if (err < 0) {
  172. // fprintf(stderr, "cannot set interleaved mode (%s)\n", snd_strerror(err));
  173. // exit(1);
  174. // }
  175. // /* 设置周期大小 */
  176. // err = snd_pcm_hw_params_set_period_size(capture_handle, hw_params, 882, 0);
  177. // if (err < 0) {
  178. // fprintf(stderr, "cannot set period size (%s)\n", snd_strerror(err));
  179. // exit(1);
  180. // }
  181. // /* 设置缓冲区大小 */
  182. // err = snd_pcm_hw_params_set_buffer_size(capture_handle, hw_params, 8820);
  183. // if (err < 0) {
  184. // fprintf(stderr, "cannot set buffer size (%s)\n", snd_strerror(err));
  185. // exit(1);
  186. // }
  187. // /* 设置参数给声卡 */
  188. // if ((err = snd_pcm_hw_params (capture_handle, hw_params)) < 0) {
  189. // fprintf (stderr, "cannot set parameters (%s)\n",
  190. // snd_strerror (err));
  191. // exit (1);
  192. // }
  193. // /* 释放参数内存 */
  194. // snd_pcm_hw_params_free (hw_params);
  195. // if ((err = snd_pcm_prepare (capture_handle)) < 0) {
  196. // fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
  197. // snd_strerror (err));
  198. // exit (1);
  199. // }
  200. // auto f1 = fopen("r1.wav", "wb");
  201. // if(!f1) {
  202. // fprintf(stderr, "Failed to open file for writing\n");
  203. // exit(1);
  204. // }
  205. // std::chrono::steady_clock::time_point start_time = std::chrono::steady_clock::now();
  206. // for (i = 0; i < 10; ++i)
  207. // {
  208. // long unsigned int readNum;
  209. // start_time = std::chrono::steady_clock::now();
  210. // if ((readNum = snd_pcm_readi (capture_handle, buf, rframes)) != rframes) {
  211. // fprintf (stderr, "read from audio interface failed (%s)\n",
  212. // snd_strerror (readNum));
  213. // exit (1);
  214. // }
  215. // auto end_time = std::chrono::steady_clock::now();
  216. // auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
  217. // SPDLOG_DEBUG("第 {} 次录音,读取数据量: {} 帧,{} 字节,耗时: {} 毫秒", i + 1, readNum, readNum, duration.count());
  218. // if (fwrite(buf, sizeof(short), readNum * 2, f1) != readNum * 2) {
  219. // fprintf(stderr, "Failed to write data to file\n");
  220. // fclose(f1);
  221. // exit(1);
  222. // }
  223. // }
  224. // fclose(f1);
  225. // snd_pcm_close (capture_handle);
  226. // free(buf);
  227. // printf("录音完成,数据已保存到 r1.wav\n");
  228. // exit (0);
  229. // }
  230. /**
  231. * @brief Set the Record Params object
  232. *
  233. * @param sampleRate 采样率
  234. * @param bits 位深度,支持8位,16位,24位,32位
  235. * @param channels 通道数
  236. */
  237. void AudioRecord::setRecordParams(int sampleRate, int bits, int channels)
  238. {
  239. m_sampleRate = sampleRate;
  240. m_bitDepth = bits;
  241. m_channels = channels;
  242. m_oneSampleSize = (m_bitDepth / 8) * m_channels; // 单个采样点的大小,单位字节
  243. }
  244. /* 打开录音通道 */
  245. bool AudioRecord::openRecordChannel(const std::string &deviceName)
  246. {
  247. m_deviceName = deviceName;
  248. /* 计算缓冲区大小和周期大小
  249. * 周期大小是指一个周期有多少个采样点,缓冲区大小是周期大小 * 周期个数,这里不需要设置周期个数
  250. * 直接设置缓冲区大小,系统会自动计算周期个数,缓冲区是个环形队列
  251. * 周期大小最好设置成可以被采样率的整数除尽,每次取出1秒数据的时候,间隔相差时间最短 */
  252. snd_pcm_uframes_t buffer_size = m_sampleRate / 5; // 设置缓冲区大小为采样率的1/5秒
  253. snd_pcm_uframes_t period_size = buffer_size / 10; // 设置周期大小为缓冲区大小的1/10秒
  254. _snd_pcm_format bit_frame = _snd_pcm_format::SND_PCM_FORMAT_UNKNOWN;
  255. if(m_bitDepth == 8)
  256. {
  257. bit_frame = SND_PCM_FORMAT_S8; // 8位采样格式
  258. }
  259. else if (m_bitDepth == 16)
  260. {
  261. bit_frame = SND_PCM_FORMAT_S16_LE; // 16位小端格式
  262. }
  263. else if (m_bitDepth == 24)
  264. {
  265. bit_frame = SND_PCM_FORMAT_S24_LE; // 24位小端格式
  266. }
  267. else if (m_bitDepth == 32)
  268. {
  269. bit_frame = SND_PCM_FORMAT_S32_LE; // 32位小端格式
  270. }
  271. else
  272. {
  273. SPDLOG_ERROR("不支持的采样位深度: {}", m_bitDepth);
  274. return false;
  275. }
  276. snd_pcm_hw_params_t *hw_params;
  277. int err;
  278. /* 打开声卡设备 */
  279. if ((err = snd_pcm_open(&m_captureHandle, deviceName.c_str(), SND_PCM_STREAM_CAPTURE, 0)) < 0) {
  280. SPDLOG_ERROR("无法打开音频设备 {}: {}", deviceName, snd_strerror(err));
  281. return false;
  282. }
  283. /* 分配硬件参数结构 */
  284. if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
  285. SPDLOG_ERROR("无法分配硬件参数结构: {}", snd_strerror(err));
  286. snd_pcm_close(m_captureHandle);
  287. return false;
  288. }
  289. /* 初始化硬件参数结构 */
  290. if ((err = snd_pcm_hw_params_any(m_captureHandle, hw_params)) < 0) {
  291. SPDLOG_ERROR("无法初始化硬件参数结构: {}", snd_strerror(err));
  292. goto snd_free;
  293. return false;
  294. }
  295. /* 设置访问类型 */
  296. if ((err = snd_pcm_hw_params_set_access(m_captureHandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
  297. SPDLOG_ERROR("无法设置访问类型: {}", snd_strerror(err));
  298. goto snd_free;
  299. return false;
  300. }
  301. /* 设置采样格式 */
  302. if ((err = snd_pcm_hw_params_set_format(m_captureHandle, hw_params, bit_frame)) < 0) {
  303. SPDLOG_ERROR("无法设置采样格式: {}", snd_strerror(err));
  304. goto snd_free;
  305. return false;
  306. }
  307. /* 设置采样率 */
  308. if ((err = snd_pcm_hw_params_set_rate(m_captureHandle, hw_params, m_sampleRate, 0)) < 0) {
  309. SPDLOG_ERROR("无法设置采样率: {}", snd_strerror(err));
  310. goto snd_free;
  311. return false;
  312. }
  313. /* 设置通道数 */
  314. if ((err = snd_pcm_hw_params_set_channels(m_captureHandle, hw_params, m_channels)) < 0) {
  315. SPDLOG_ERROR("无法设置通道数: {}", snd_strerror(err));
  316. goto snd_free;
  317. return false;
  318. }
  319. /* 设置缓冲区大小 */
  320. if ((err = snd_pcm_hw_params_set_buffer_size(m_captureHandle, hw_params, buffer_size)) < 0) {
  321. SPDLOG_ERROR("无法设置缓冲区大小: {}", snd_strerror(err));
  322. goto snd_free;
  323. return false;
  324. }
  325. /* 设置周期大小 */
  326. if ((err = snd_pcm_hw_params_set_period_size(m_captureHandle, hw_params, period_size, 0)) < 0) {
  327. SPDLOG_ERROR("无法设置周期大小: {}", snd_strerror(err));
  328. goto snd_free;
  329. return false;
  330. }
  331. /* 应用硬件参数 */
  332. if ((err = snd_pcm_hw_params(m_captureHandle, hw_params)) < 0) {
  333. SPDLOG_ERROR("无法应用硬件参数: {}", snd_strerror(err));
  334. goto snd_free;
  335. return false;
  336. }
  337. /* 释放硬件参数结构 */
  338. snd_pcm_hw_params_free(hw_params);
  339. /* 准备PCM设备 */
  340. if ((err = snd_pcm_prepare(m_captureHandle)) < 0) {
  341. SPDLOG_ERROR("无法准备PCM设备: {}", snd_strerror(err));
  342. snd_pcm_close(m_captureHandle);
  343. return false;
  344. }
  345. return true;
  346. snd_free:
  347. snd_pcm_hw_params_free(hw_params);
  348. snd_pcm_close(m_captureHandle);
  349. return false;
  350. }
  351. /**
  352. * @brief 读取录音大小
  353. *
  354. * @param buffer 数据缓冲区指针
  355. * @param bufferSize 缓冲区大小,缓冲区大小需要大于等于 recordFrames * 单个采样点的大小
  356. * 例如:44100帧 * 2通道 * 2字节
  357. * @param recordFrames 需要读取的帧数,一帧就是一个采样点,16bit / 2 * 2 通道 = 4字节
  358. * 44100帧就是1秒
  359. * @return uint32_t 读取到的字节数
  360. */
  361. int AudioRecord::recordAudio(char* buffer, uint32_t bufferSize, uint32_t recordFrames)
  362. {
  363. if(m_captureHandle == nullptr)
  364. {
  365. SPDLOG_WARN("PCM设备未打开,请先调用 openAudioChannel 打开设备");
  366. return -1;
  367. }
  368. if (buffer == nullptr || bufferSize == 0)
  369. {
  370. SPDLOG_WARN("缓冲区指针为空或缓冲区大小为0");
  371. return -1;
  372. }
  373. if(recordFrames * m_oneSampleSize > bufferSize)
  374. {
  375. SPDLOG_ERROR("缓冲区大小不足,无法存储 {} 帧数据", recordFrames);
  376. return -1;
  377. }
  378. auto readFrames = snd_pcm_readi(m_captureHandle, buffer, recordFrames);
  379. if (readFrames < 0)
  380. {
  381. SPDLOG_ERROR("获取录音数据失败: {}", snd_strerror(readFrames));
  382. return -1;
  383. }
  384. return readFrames * m_oneSampleSize; // 返回读取到的字节数
  385. }