jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

679 lines
23KB

  1. /*
  2. * Copyright (c) 2023 Florian Walpen <dev@submerge.ch>
  3. *
  4. * Permission to use, copy, modify, and distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #ifndef SOSSO_DEVICE_HPP
  17. #define SOSSO_DEVICE_HPP
  18. #include "sosso/Logging.hpp"
  19. #include <cstdint>
  20. #include <cstring>
  21. #include <fcntl.h>
  22. #include <sys/errno.h>
  23. #include <sys/ioctl.h>
  24. #include <sys/mman.h>
  25. #include <sys/soundcard.h>
  26. #include <unistd.h>
  27. namespace sosso {
  28. /*!
  29. * \brief Manage OSS devices.
  30. *
  31. * Encapsulates all the low-level handling of a FreeBSD OSS pcm device. Due to
  32. * restrictions of the OSS API, the device can be opened for either playback or
  33. * recording, not both. For duplex operation, separate instances of Device have
  34. * to be opened.
  35. * By default a Device opens 2 channels of 32 bit samples at 48 kHz, but the
  36. * OSS API will force that to be whatever is supported by the hardware.
  37. * Different default parameters can be set via set_parameters() prior to opening
  38. * the Device. Always check the effective parameters before any use.
  39. */
  40. class Device {
  41. public:
  42. /*!
  43. * \brief Translate OSS sample formats to sample size.
  44. * \param format OSS sample format, see sys/soundcard.h header.
  45. * \return Sample size in bytes, 0 if unsupported.
  46. */
  47. static std::size_t bytes_per_sample(int format) {
  48. switch (format) {
  49. case AFMT_S16_LE:
  50. case AFMT_S16_BE:
  51. return 2;
  52. case AFMT_S24_LE:
  53. case AFMT_S24_BE:
  54. return 3;
  55. case AFMT_S32_LE:
  56. case AFMT_S32_BE:
  57. return 4;
  58. default:
  59. return 0;
  60. }
  61. }
  62. //! Always close device before destruction.
  63. ~Device() { close(); }
  64. //! Effective OSS sample format, see sys/soundcard.h header.
  65. int sample_format() const { return _sample_format; }
  66. //! Effective sample size in bytes.
  67. std::size_t bytes_per_sample() const {
  68. return bytes_per_sample(_sample_format);
  69. }
  70. //! Indicate that the device is open.
  71. bool is_open() const { return _fd >= 0; }
  72. //! Indicate that the device is opened in playback mode.
  73. bool playback() const { return _fd >= 0 && (_file_mode & O_WRONLY); }
  74. //! Indicate that the device is opened in recording mode.
  75. bool recording() const { return _fd >= 0 && !playback(); }
  76. //! Get the file descriptor of the device, -1 if not open.
  77. int file_descriptor() const { return _fd; }
  78. //! Effective number of audio channels.
  79. unsigned channels() const { return _channels; }
  80. //! Effective frame size, one sample for each channel.
  81. std::size_t frame_size() const { return _channels * bytes_per_sample(); }
  82. //! Effective OSS buffer size in bytes.
  83. std::size_t buffer_size() const { return _fragments * _fragment_size; }
  84. //! Effective OSS buffer size in frames, samples per channel.
  85. unsigned buffer_frames() const { return buffer_size() / frame_size(); }
  86. //! Effective sample rate in Hz.
  87. unsigned sample_rate() const { return _sample_rate; }
  88. //! Suggested minimal polling step, in frames.
  89. unsigned stepping() const { return 16U * (1U + (_sample_rate / 50000)); }
  90. //! Indicate that the OSS buffer can be memory mapped.
  91. bool can_memory_map() const { return has_capability(PCM_CAP_MMAP); }
  92. //! A pointer to the memory mapped OSS buffer, null if not mapped.
  93. char *map() const { return static_cast<char *>(_map); }
  94. //! Current read / write position in the mapped OSS buffer.
  95. unsigned map_pointer() const { return _map_progress % buffer_size(); }
  96. //! Total progress of the mapped OSS buffer, in frames.
  97. std::int64_t map_progress() const { return _map_progress / frame_size(); }
  98. /*!
  99. * \brief Set preferred audio parameters before opening device.
  100. * \param format OSS sample formet, see sys/soundcard.h header.
  101. * \param rate Sample rate in Hz.
  102. * \param channels Number of recording / playback channels.
  103. * \return True if successful, false means unsupported parameters.
  104. */
  105. bool set_parameters(int format, int rate, int channels) {
  106. if (bytes_per_sample(format) && channels > 0) {
  107. _sample_format = format;
  108. _sample_rate = rate;
  109. _channels = channels;
  110. return true;
  111. }
  112. return false;
  113. }
  114. /*!
  115. * \brief Open the device for either recording or playback.
  116. * \param device Path to the OSS device (e.g. "/dev/dsp1").
  117. * \param mode Open mode read or write, optional exclusive and non-blocking.
  118. * \return True if successful.
  119. */
  120. bool open(const char *device, int mode) {
  121. if (mode & O_RDWR) {
  122. Log::warn(SOSSO_LOC, "Only one direction allowed, open %s in read mode.",
  123. device);
  124. mode = O_RDONLY | (mode & O_EXCL) | (mode & O_NONBLOCK);
  125. }
  126. _fd = ::open(device, mode);
  127. if (_fd >= 0) {
  128. _file_mode = mode;
  129. if (bitperfect_mode(_fd) && set_sample_format(_fd) && set_channels(_fd) &&
  130. set_sample_rate(_fd) && get_buffer_info() && get_capabilities()) {
  131. return true;
  132. }
  133. }
  134. Log::warn(SOSSO_LOC, "Unable to open device %s, errno %d.", device, errno);
  135. close();
  136. return false;
  137. }
  138. //! Close the device.
  139. void close() {
  140. if (map()) {
  141. memory_unmap();
  142. }
  143. if (_fd >= 0) {
  144. ::close(_fd);
  145. _fd = -1;
  146. }
  147. }
  148. /*!
  149. * \brief Request a specific OSS buffer size.
  150. * \param fragments Number of fragments.
  151. * \param fragment_size Size of the fragments in bytes.
  152. * \return True if successful.
  153. * \warning Due to OSS API limitations, resulting buffer sizes are not really
  154. * predictable and may cause problems with some soundcards.
  155. */
  156. bool set_buffer_size(unsigned fragments, unsigned fragment_size) {
  157. int frg = 0;
  158. while ((1U << frg) < fragment_size) {
  159. ++frg;
  160. }
  161. frg |= (fragments << 16);
  162. Log::info(SOSSO_LOC, "Request %d fragments of %u bytes.", (frg >> 16),
  163. (1U << (frg & 0xffff)));
  164. if (ioctl(_fd, SNDCTL_DSP_SETFRAGMENT, &frg) != 0) {
  165. Log::warn(SOSSO_LOC, "Set fragments failed with %d.", errno);
  166. return false;
  167. }
  168. return get_buffer_info();
  169. }
  170. /*!
  171. * \brief Request a specific OSS buffer size.
  172. * \param total_size Total size of all buffer fragments.
  173. * \return True if successful.
  174. * \warning Due to OSS API limitations, resulting buffer sizes are not really
  175. * predictable and may cause problems with some soundcards.
  176. */
  177. bool set_buffer_size(unsigned total_size) {
  178. if (_fragment_size > 0) {
  179. unsigned fragments = (total_size + _fragment_size - 1) / _fragment_size;
  180. return set_buffer_size(fragments, _fragment_size);
  181. }
  182. return false;
  183. }
  184. /*!
  185. * \brief Read recorded audio data from OSS buffer.
  186. * \param buffer Pointer to destination buffer.
  187. * \param length Maximum read length in bytes.
  188. * \param count Byte counter, increased by effective read length.
  189. * \return True if successful or if nothing to do.
  190. */
  191. bool read_io(char *buffer, std::size_t length, std::size_t &count) {
  192. if (buffer && length > 0 && recording()) {
  193. ssize_t result = ::read(_fd, buffer, length);
  194. if (result >= 0) {
  195. count += result;
  196. } else if (errno == EAGAIN) {
  197. count += 0;
  198. } else {
  199. Log::warn(SOSSO_LOC, "Data read failed with %d.", errno);
  200. return false;
  201. }
  202. }
  203. return true;
  204. }
  205. /*!
  206. * \brief Read recorded audio data from memory mapped OSS buffer.
  207. * \param buffer Pointer to destination buffer.
  208. * \param offset Read offset into the OSS buffer, in bytes.
  209. * \param length Maximum read length in bytes.
  210. * \return The number of bytes read.
  211. */
  212. std::size_t read_map(char *buffer, std::size_t offset, std::size_t length) {
  213. std::size_t bytes_read = 0;
  214. if (length > 0 && map()) {
  215. // Sanitize offset and length parameters.
  216. offset = offset % buffer_size();
  217. if (length > buffer_size()) {
  218. length = buffer_size();
  219. }
  220. // Check if the read length spans across an OSS buffer cycle.
  221. if (offset + length > buffer_size()) {
  222. // Read until buffer end first.
  223. bytes_read = read_map(buffer, offset, buffer_size() - offset);
  224. length -= bytes_read;
  225. buffer += bytes_read;
  226. offset = 0;
  227. }
  228. // Read remaining data.
  229. std::memcpy(buffer, map() + offset, length);
  230. bytes_read += length;
  231. }
  232. return bytes_read;
  233. }
  234. /*!
  235. * \brief Write audio data to OSS buffer.
  236. * \param buffer Pointer to source buffer.
  237. * \param length Maximum write length in bytes.
  238. * \param count Byte counter, increased by effective write length.
  239. * \return True if successful or if nothing to do.
  240. */
  241. bool write_io(char *buffer, std::size_t length, std::size_t &count) {
  242. if (buffer && length > 0 && playback()) {
  243. ssize_t result = ::write(file_descriptor(), buffer, length);
  244. if (result >= 0) {
  245. count += result;
  246. } else if (errno == EAGAIN) {
  247. count += 0;
  248. } else {
  249. Log::warn(SOSSO_LOC, "Data write failed with %d.", errno);
  250. return false;
  251. }
  252. }
  253. return true;
  254. }
  255. /*!
  256. * \brief Write audio data to a memory mapped OSS buffer.
  257. * \param buffer Pointer to source buffer, null writes zeros to OSS buffer.
  258. * \param offset Write offset into the OSS buffer, in bytes.
  259. * \param length Maximum write length in bytes.
  260. * \return The number of bytes written.
  261. */
  262. std::size_t write_map(const char *buffer, std::size_t offset,
  263. std::size_t length) {
  264. std::size_t bytes_written = 0;
  265. if (length > 0 && map()) {
  266. // Sanitize pointer and length parameters.
  267. offset = offset % buffer_size();
  268. if (length > buffer_size()) {
  269. length = buffer_size();
  270. }
  271. // Check if the write length spans across an OSS buffer cycle.
  272. if (offset + length > buffer_size()) {
  273. // Write until buffer end first.
  274. bytes_written += write_map(buffer, offset, buffer_size() - offset);
  275. length -= bytes_written;
  276. if (buffer) {
  277. buffer += bytes_written;
  278. }
  279. offset = 0;
  280. }
  281. // Write source if available, otherwise clear the buffer.
  282. if (buffer) {
  283. std::memcpy(map() + offset, buffer, length);
  284. } else {
  285. std::memset(map() + offset, 0, length);
  286. }
  287. bytes_written += length;
  288. }
  289. return bytes_written;
  290. }
  291. /*!
  292. * \brief Query number of frames in the OSS buffer (non-mapped).
  293. * \return Number of frames, 0 if not successful.
  294. */
  295. int queued_samples() {
  296. unsigned long request =
  297. playback() ? SNDCTL_DSP_CURRENT_OPTR : SNDCTL_DSP_CURRENT_IPTR;
  298. oss_count_t ptr;
  299. if (ioctl(_fd, request, &ptr) == 0) {
  300. return ptr.fifo_samples;
  301. }
  302. return 0;
  303. }
  304. //! Indicate that the device can be triggered to start.
  305. bool can_trigger() const { return has_capability(PCM_CAP_TRIGGER); }
  306. //! Trigger the device to start recording / playback.
  307. bool start() const {
  308. if (!can_trigger()) {
  309. Log::warn(SOSSO_LOC, "Trigger start not supported by device.");
  310. return false;
  311. }
  312. int trigger = recording() ? PCM_ENABLE_INPUT : PCM_ENABLE_OUTPUT;
  313. if (ioctl(file_descriptor(), SNDCTL_DSP_SETTRIGGER, &trigger) != 0) {
  314. const char *direction = recording() ? "recording" : "playback";
  315. Log::warn(SOSSO_LOC, "Starting %s channel failed with error %d.",
  316. direction, errno);
  317. return false;
  318. }
  319. return true;
  320. }
  321. /*!
  322. * \brief Add device to a sync group for synchronized start.
  323. * \param id Id of the sync group, 0 will initialize a new group.
  324. * \return True if successful.
  325. */
  326. bool add_to_sync_group(int &id) {
  327. oss_syncgroup sync_group = {0, 0, {0}};
  328. sync_group.id = id;
  329. sync_group.mode |= (recording() ? PCM_ENABLE_INPUT : PCM_ENABLE_OUTPUT);
  330. if (ioctl(file_descriptor(), SNDCTL_DSP_SYNCGROUP, &sync_group) == 0 &&
  331. (id == 0 || sync_group.id == id)) {
  332. id = sync_group.id;
  333. return true;
  334. }
  335. Log::warn(SOSSO_LOC, "Sync grouping channel failed with error %d.", errno);
  336. return false;
  337. }
  338. /*!
  339. * \brief Synchronized start of all devices in the sync group.
  340. * \param id Id of the sync group.
  341. * \return True if successful.
  342. */
  343. bool start_sync_group(int id) {
  344. if (ioctl(file_descriptor(), SNDCTL_DSP_SYNCSTART, &id) == 0) {
  345. return true;
  346. }
  347. Log::warn(SOSSO_LOC, "Start of sync group failed with error %d.", errno);
  348. return false;
  349. }
  350. //! Query the number of playback underruns since last called.
  351. int get_play_underruns() {
  352. int play_underruns = 0;
  353. int rec_overruns = 0;
  354. get_errors(play_underruns, rec_overruns);
  355. return play_underruns;
  356. }
  357. //! Query the number of recording overruns since last called.
  358. int get_rec_overruns() {
  359. int play_underruns = 0;
  360. int rec_overruns = 0;
  361. get_errors(play_underruns, rec_overruns);
  362. return rec_overruns;
  363. }
  364. //! Update current playback position for memory mapped OSS buffer.
  365. bool get_play_pointer() {
  366. count_info info = {};
  367. if (ioctl(file_descriptor(), SNDCTL_DSP_GETOPTR, &info) == 0) {
  368. if (info.ptr >= 0 && static_cast<unsigned>(info.ptr) < buffer_size() &&
  369. (info.ptr % frame_size()) == 0 && info.blocks >= 0) {
  370. // Calculate pointer delta without complete buffer cycles.
  371. unsigned delta =
  372. (info.ptr + buffer_size() - map_pointer()) % buffer_size();
  373. // Get upper bound on progress from blocks info.
  374. unsigned max_bytes = (info.blocks + 1) * _fragment_size - 1;
  375. if (max_bytes >= delta) {
  376. // Estimate cycle part and round it down to buffer cycles.
  377. unsigned cycles = max_bytes - delta;
  378. cycles -= (cycles % buffer_size());
  379. delta += cycles;
  380. }
  381. int fragments = delta / _fragment_size;
  382. if (info.blocks < fragments || info.blocks > fragments + 1) {
  383. Log::warn(SOSSO_LOC, "Play pointer blocks: %u - %d, %d, %d.",
  384. map_pointer(), info.ptr, info.blocks, info.bytes);
  385. }
  386. _map_progress += delta;
  387. return true;
  388. }
  389. Log::warn(SOSSO_LOC, "Play pointer out of bounds: %d, %d blocks.",
  390. info.ptr, info.blocks);
  391. } else {
  392. Log::warn(SOSSO_LOC, "Play pointer failed with error: %d.", errno);
  393. }
  394. return false;
  395. }
  396. //! Update current recording position for memory mapped OSS buffer.
  397. bool get_rec_pointer() {
  398. count_info info = {};
  399. if (ioctl(file_descriptor(), SNDCTL_DSP_GETIPTR, &info) == 0) {
  400. if (info.ptr >= 0 && static_cast<unsigned>(info.ptr) < buffer_size() &&
  401. (info.ptr % frame_size()) == 0 && info.blocks >= 0) {
  402. // Calculate pointer delta without complete buffer cycles.
  403. unsigned delta =
  404. (info.ptr + buffer_size() - map_pointer()) % buffer_size();
  405. // Get upper bound on progress from blocks info.
  406. unsigned max_bytes = (info.blocks + 1) * _fragment_size - 1;
  407. if (max_bytes >= delta) {
  408. // Estimate cycle part and round it down to buffer cycles.
  409. unsigned cycles = max_bytes - delta;
  410. cycles -= (cycles % buffer_size());
  411. delta += cycles;
  412. }
  413. int fragments = delta / _fragment_size;
  414. if (info.blocks < fragments || info.blocks > fragments + 1) {
  415. Log::warn(SOSSO_LOC, "Rec pointer blocks: %u - %d, %d, %d.",
  416. map_pointer(), info.ptr, info.blocks, info.bytes);
  417. }
  418. _map_progress += delta;
  419. return true;
  420. }
  421. Log::warn(SOSSO_LOC, "Rec pointer out of bounds: %d, %d blocks.",
  422. info.ptr, info.blocks);
  423. } else {
  424. Log::warn(SOSSO_LOC, "Rec pointer failed with error: %d.", errno);
  425. }
  426. return false;
  427. }
  428. //! Memory map the OSS buffer.
  429. bool memory_map() {
  430. if (!can_memory_map()) {
  431. Log::warn(SOSSO_LOC, "Memory map not supported by device.");
  432. return false;
  433. }
  434. int protection = PROT_NONE;
  435. if (playback()) {
  436. protection = PROT_WRITE;
  437. }
  438. if (recording()) {
  439. protection = PROT_READ;
  440. }
  441. if (_map == nullptr && protection != PROT_NONE) {
  442. _map = mmap(NULL, buffer_size(), protection, MAP_SHARED,
  443. file_descriptor(), 0);
  444. if (_map == MAP_FAILED) {
  445. Log::warn(SOSSO_LOC, "Memory map failed with error %d.", errno);
  446. _map = nullptr;
  447. }
  448. }
  449. return (_map != nullptr);
  450. }
  451. //! Unmap a previously memory mapped OSS buffer.
  452. bool memory_unmap() {
  453. if (_map) {
  454. if (munmap(_map, buffer_size()) != 0) {
  455. Log::warn(SOSSO_LOC, "Memory unmap failed with error %d.", errno);
  456. return false;
  457. }
  458. _map = nullptr;
  459. }
  460. return true;
  461. }
  462. /*!
  463. * \brief Check device capabilities.
  464. * \param capabilities Device capabilities, see sys/soundcard.h header.
  465. * \return True if the device has the capabilities in question.
  466. */
  467. bool has_capability(int capabilities) const {
  468. return (_capabilities & capabilities) == capabilities;
  469. }
  470. //! Print device info to user information log.
  471. void log_device_info() const {
  472. if (!is_open()) {
  473. return;
  474. }
  475. const char *direction = (recording() ? "Recording" : "Playback");
  476. Log::info(SOSSO_LOC, "%s device is %u channels at %u Hz, %lu bits.",
  477. direction, _channels, _sample_rate, bytes_per_sample() * 8);
  478. Log::info(SOSSO_LOC, "Device buffer is %u fragments of size %u, %u frames.",
  479. _fragments, _fragment_size, buffer_frames());
  480. oss_sysinfo sys_info = {};
  481. if (ioctl(_fd, SNDCTL_SYSINFO, &sys_info) == 0) {
  482. Log::info(SOSSO_LOC, "OSS version %s number %d on %s.", sys_info.version,
  483. sys_info.versionnum, sys_info.product);
  484. }
  485. Log::info(SOSSO_LOC, "PCM capabilities:");
  486. if (has_capability(PCM_CAP_TRIGGER))
  487. Log::info(SOSSO_LOC, " PCM_CAP_TRIGGER (Trigger start)");
  488. if (has_capability(PCM_CAP_MMAP))
  489. Log::info(SOSSO_LOC, " PCM_CAP_MMAP (Memory map)");
  490. if (has_capability(PCM_CAP_MULTI))
  491. Log::info(SOSSO_LOC, " PCM_CAP_MULTI (Multiple open)");
  492. if (has_capability(PCM_CAP_INPUT))
  493. Log::info(SOSSO_LOC, " PCM_CAP_INPUT (Recording)");
  494. if (has_capability(PCM_CAP_OUTPUT))
  495. Log::info(SOSSO_LOC, " PCM_CAP_OUTPUT (Playback)");
  496. if (has_capability(PCM_CAP_VIRTUAL))
  497. Log::info(SOSSO_LOC, " PCM_CAP_VIRTUAL (Virtual device)");
  498. if (has_capability(PCM_CAP_ANALOGIN))
  499. Log::info(SOSSO_LOC, " PCM_CAP_ANALOGIN (Analog input)");
  500. if (has_capability(PCM_CAP_ANALOGOUT))
  501. Log::info(SOSSO_LOC, " PCM_CAP_ANALOGOUT (Analog output)");
  502. if (has_capability(PCM_CAP_DIGITALIN))
  503. Log::info(SOSSO_LOC, " PCM_CAP_DIGITALIN (Digital input)");
  504. if (has_capability(PCM_CAP_DIGITALOUT))
  505. Log::info(SOSSO_LOC, " PCM_CAP_DIGITALOUT (Digital output)");
  506. }
  507. private:
  508. // Disable auto-conversion (bitperfect) when opened in exclusive mode.
  509. bool bitperfect_mode(int fd) {
  510. if (_file_mode & O_EXCL) {
  511. int flags = 0;
  512. int result = ioctl(fd, SNDCTL_DSP_COOKEDMODE, &flags);
  513. if (result < 0) {
  514. Log::warn(SOSSO_LOC, "Unable to set cooked mode.");
  515. }
  516. return result >= 0;
  517. }
  518. return true;
  519. }
  520. // Set sample format and the check the result.
  521. bool set_sample_format(int fd) {
  522. int format = _sample_format;
  523. int result = ioctl(fd, SNDCTL_DSP_SETFMT, &format);
  524. if (result != 0) {
  525. Log::warn(SOSSO_LOC, "Unable to set sample format, error %d.", errno);
  526. return false;
  527. } else if (bytes_per_sample(format) == 0) {
  528. Log::warn(SOSSO_LOC, "Unsupported sample format %d.", format);
  529. return false;
  530. } else if (format != _sample_format) {
  531. Log::warn(
  532. SOSSO_LOC, "Driver changed the sample format, %lu bit vs %lu bit.",
  533. bytes_per_sample(format) * 8, bytes_per_sample(_sample_format) * 8);
  534. }
  535. _sample_format = format;
  536. return true;
  537. }
  538. // Set sample rate and then check the result.
  539. bool set_sample_rate(int fd) {
  540. int rate = _sample_rate;
  541. if (ioctl(fd, SNDCTL_DSP_SPEED, &rate) == 0) {
  542. if (rate != _sample_rate) {
  543. Log::warn(SOSSO_LOC, "Driver changed the sample rate, %d vs %d.", rate,
  544. _sample_rate);
  545. _sample_rate = rate;
  546. }
  547. return true;
  548. }
  549. Log::warn(SOSSO_LOC, "Unable to set sample rate, error %d.", errno);
  550. return false;
  551. }
  552. // Set the number of channels and then check the result.
  553. bool set_channels(int fd) {
  554. int channels = _channels;
  555. if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == 0) {
  556. if (channels != _channels) {
  557. Log::warn(SOSSO_LOC, "Driver changed number of channels, %d vs %d.",
  558. channels, _channels);
  559. _channels = channels;
  560. }
  561. return true;
  562. }
  563. Log::warn(SOSSO_LOC, "Unable to set channels, error %d.", errno);
  564. return false;
  565. }
  566. // Query fragments and size of the OSS buffer.
  567. bool get_buffer_info() {
  568. audio_buf_info info = {0, 0, 0, 0};
  569. unsigned long request =
  570. playback() ? SNDCTL_DSP_GETOSPACE : SNDCTL_DSP_GETISPACE;
  571. if (ioctl(_fd, request, &info) >= 0) {
  572. _fragments = info.fragstotal;
  573. _fragment_size = info.fragsize;
  574. return true;
  575. } else {
  576. Log::warn(SOSSO_LOC, "Unable to get buffer info.");
  577. return false;
  578. }
  579. }
  580. // Query capabilities of the device.
  581. bool get_capabilities() {
  582. if (ioctl(_fd, SNDCTL_DSP_GETCAPS, &_capabilities) == 0) {
  583. oss_sysinfo sysinfo = {};
  584. if (ioctl(_fd, OSS_SYSINFO, &sysinfo) == 0) {
  585. if (std::strncmp(sysinfo.version, "1302000", 7) < 0) {
  586. // Memory map on FreeBSD prior to 13.2 may use wrong buffer size.
  587. Log::warn(SOSSO_LOC,
  588. "Disable memory map, workaround OSS bug on FreeBSD < 13.2");
  589. _capabilities &= ~PCM_CAP_MMAP;
  590. }
  591. return true;
  592. } else {
  593. Log::warn(SOSSO_LOC, "Unable to get system info, error %d.", errno);
  594. }
  595. } else {
  596. Log::warn(SOSSO_LOC, "Unable to get device capabilities, error %d.",
  597. errno);
  598. _capabilities = 0;
  599. }
  600. return false;
  601. }
  602. // Query error information from the device.
  603. bool get_errors(int &play_underruns, int &rec_overruns) {
  604. audio_errinfo error_info = {};
  605. if (ioctl(file_descriptor(), SNDCTL_DSP_GETERROR, &error_info) == 0) {
  606. play_underruns = error_info.play_underruns;
  607. rec_overruns = error_info.rec_overruns;
  608. return true;
  609. }
  610. return false;
  611. }
  612. private:
  613. int _fd = -1; // File descriptor.
  614. int _file_mode = O_RDONLY; // File open mode.
  615. void *_map = nullptr; // Memory map pointer.
  616. std::uint64_t _map_progress = 0; // Memory map progress.
  617. int _channels = 2; // Number of channels.
  618. int _capabilities = 0; // Device capabilities.
  619. int _sample_format = AFMT_S32_NE; // Sample format.
  620. int _sample_rate = 48000; // Sample rate.
  621. unsigned _fragments = 0; // Number of OSS buffer fragments.
  622. unsigned _fragment_size = 0; // OSS buffer fragment size.
  623. };
  624. } // namespace sosso
  625. #endif // SOSSO_DEVICE_HPP