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.

256 lines
10KB

  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_READCHANNEL_HPP
  17. #define SOSSO_READCHANNEL_HPP
  18. #include "sosso/Buffer.hpp"
  19. #include "sosso/Channel.hpp"
  20. #include "sosso/Logging.hpp"
  21. #include <fcntl.h>
  22. namespace sosso {
  23. /*!
  24. * \brief Recording Channel
  25. *
  26. * Specializes the generic Channel class into a recording channel. It keeps
  27. * track of the OSS recording progress, and reads the available audio data to an
  28. * external buffer. If the OSS buffer is memory mapped, the audio data is copied
  29. * from there. Otherwise I/O read() system calls are used.
  30. */
  31. class ReadChannel : public Channel {
  32. public:
  33. /*!
  34. * \brief Open a device for recording.
  35. * \param device Path to the device, e.g. "/dev/dsp1".
  36. * \param exclusive Try to get exclusive access to the device.
  37. * \return True if the device was opened successfully.
  38. */
  39. bool open(const char *device, bool exclusive = true) {
  40. int mode = O_RDONLY | O_NONBLOCK;
  41. if (exclusive) {
  42. mode |= O_EXCL;
  43. }
  44. return Channel::open(device, mode);
  45. }
  46. //! Available audio data to be read, in frames.
  47. std::int64_t oss_available() const {
  48. std::int64_t result = last_progress() - _read_position;
  49. if (result < 0) {
  50. result = 0;
  51. } else if (result > buffer_frames()) {
  52. result = buffer_frames();
  53. }
  54. return result;
  55. }
  56. /*!
  57. * \brief Calculate next wakeup time.
  58. * \param sync_frames Required sync event (e.g. buffer end), in frame time.
  59. * \return Suggested and safe wakeup time for next process(), in frame time.
  60. */
  61. std::int64_t wakeup_time(std::int64_t sync_frames) const {
  62. return Channel::wakeup_time(sync_frames, oss_available());
  63. }
  64. /*!
  65. * \brief Check OSS progress and read recorded audio to the buffer.
  66. * \param buffer Buffer to write to, untouched if invalid.
  67. * \param end Buffer end position, matching channel progress.
  68. * \param now Current time in frame time, see FrameClock.
  69. * \return True if successful, false means there was an error.
  70. */
  71. bool process(Buffer &buffer, std::int64_t end, std::int64_t now) {
  72. if (map()) {
  73. return (progress_done(now) || check_map_progress(now)) &&
  74. (buffer_done(buffer, end) || process_mapped(buffer, end, now));
  75. } else {
  76. return (progress_done(now) || check_read_progress(now)) &&
  77. (buffer_done(buffer, end) || process_read(buffer, end, now));
  78. }
  79. }
  80. protected:
  81. // Indicate that OSS progress has already been checked.
  82. bool progress_done(std::int64_t now) { return (last_processing() == now); }
  83. // Check OSS progress in case of memory mapped buffer.
  84. bool check_map_progress(std::int64_t now) {
  85. // Get OSS progress through map pointer.
  86. if (get_rec_pointer()) {
  87. std::int64_t progress = map_progress() - _oss_progress;
  88. _oss_progress += progress;
  89. std::int64_t available = last_progress() + progress - _read_position;
  90. std::int64_t loss = mark_loss(available - buffer_frames());
  91. mark_progress(progress, now);
  92. if (loss > 0) {
  93. Log::warn(SOSSO_LOC, "OSS recording buffer overrun, %lld lost.", loss);
  94. _read_position = last_progress() - buffer_frames();
  95. }
  96. }
  97. return progress_done(now);
  98. }
  99. // Read recorded audio data to buffer, in case of memory mapped OSS buffer.
  100. bool process_mapped(Buffer &buffer, std::int64_t end, std::int64_t now) {
  101. // Calculate current read buffer position.
  102. std::int64_t position = buffer_position(buffer, end);
  103. // Only read what is available until OSS captured its complete buffer.
  104. std::int64_t oldest = last_progress() - buffer_frames();
  105. if (_oss_progress < buffer_frames()) {
  106. oldest = last_progress() - _oss_progress;
  107. }
  108. if (std::int64_t skip = buffer_advance(buffer, oldest - position)) {
  109. // First part of the read buffer already passed, fill it up.
  110. Log::info(SOSSO_LOC, "@%lld - %lld Read buffer late by %lld, skip %lld.",
  111. now, end, oldest - position, skip);
  112. position += skip;
  113. } else if (position != _read_position) {
  114. // Position mismatch, reread what is available.
  115. if (std::int64_t rewind = buffer_rewind(buffer, position - oldest)) {
  116. Log::info(SOSSO_LOC,
  117. "@%lld - %lld Read position mismatch, reread %lld.", now, end,
  118. rewind);
  119. position -= rewind;
  120. }
  121. }
  122. if (position >= oldest && position < last_progress() && !buffer.done()) {
  123. // Read from offset up to current position, if read buffer can hold it.
  124. std::int64_t offset = last_progress() - position;
  125. std::size_t length = buffer.remaining(offset * frame_size());
  126. unsigned pointer = (_oss_progress - offset) % buffer_frames();
  127. length = read_map(buffer.position(), pointer * frame_size(), length);
  128. buffer.advance(length);
  129. _read_position = buffer_position(buffer, end);
  130. }
  131. _read_position += freewheel_finish(buffer, end, now);
  132. return true;
  133. }
  134. // Check progress when using I/O read() system call.
  135. bool check_read_progress(std::int64_t now) {
  136. // Check for OSS buffer overruns.
  137. std::int64_t overdue = now - estimated_dropout(oss_available());
  138. if ((overdue > 0 && get_rec_overruns() > 0) || overdue > max_progress()) {
  139. std::int64_t progress = buffer_frames() - oss_available();
  140. std::int64_t loss = mark_loss(progress, now);
  141. Log::warn(SOSSO_LOC, "OSS recording buffer overrun, %lld lost.", loss);
  142. mark_progress(progress + loss, now);
  143. _read_position = last_progress() - buffer_frames();
  144. } else {
  145. // Infer progress from OSS queue changes.
  146. std::int64_t queued = queued_samples();
  147. std::int64_t progress = queued - (last_progress() - _read_position);
  148. mark_progress(progress, now);
  149. _read_position = last_progress() - queued;
  150. }
  151. return progress_done(now);
  152. }
  153. // Read recorded audio data to buffer, using I/O read() syscall.
  154. bool process_read(Buffer &buffer, std::int64_t end, std::int64_t now) {
  155. bool ok = true;
  156. std::int64_t position = buffer_position(buffer, end);
  157. if (std::int64_t skip = buffer_advance(buffer, _read_position - position)) {
  158. // Overlapping buffers, skip the overlapping part.
  159. Log::info(SOSSO_LOC, "@%lld - %lld Read buffer overlap %lld, skip %lld.",
  160. now, end, _read_position - position, skip);
  161. position += skip;
  162. } else if (std::int64_t rewind =
  163. buffer_rewind(buffer, position - _read_position)) {
  164. // Gap between reads, try to rewind to last read position.
  165. Log::info(SOSSO_LOC, "@%lld - %lld Read buffer gap %lld, rewind %lld.",
  166. now, end, position - _read_position, rewind);
  167. position -= rewind;
  168. }
  169. if (oss_available() == 0) {
  170. // OSS buffer is empty, nothing to do.
  171. } else if (position > _read_position) {
  172. // Read and omit data of remaining gap, drain OSS buffer.
  173. std::int64_t gap = position - _read_position;
  174. std::size_t read_limit = buffer.remaining(gap * frame_size());
  175. std::size_t bytes_read = 0;
  176. ok = read_io(buffer.position(), read_limit, bytes_read);
  177. Log::info(SOSSO_LOC, "@%lld - %lld Read buffer gap %lld, drain %lu.", now,
  178. end, gap, bytes_read / frame_size());
  179. _read_position += bytes_read / frame_size();
  180. } else if (position == _read_position) {
  181. // Read as much as currently available.
  182. std::size_t bytes_read = 0;
  183. ok = read_io(buffer.position(), buffer.remaining(), bytes_read);
  184. _read_position += bytes_read / frame_size();
  185. buffer.advance(bytes_read);
  186. }
  187. freewheel_finish(buffer, end, now);
  188. return ok;
  189. }
  190. private:
  191. // Calculate read position of the remaining buffer.
  192. std::int64_t buffer_position(const Buffer &buffer, std::int64_t end) const {
  193. return end - extra_latency() - (buffer.remaining() / frame_size());
  194. }
  195. // Indicate that a buffer doesn't need further processing.
  196. bool buffer_done(const Buffer &buffer, std::int64_t end) const {
  197. return buffer.done() && buffer_position(buffer, end) <= _read_position;
  198. }
  199. // Extra latency to always finish on time, regardless of OSS progress steps.
  200. std::int64_t extra_latency() const { return max_progress(); }
  201. // Avoid stalled buffers with irregular OSS progress in freewheel mode.
  202. std::int64_t freewheel_finish(Buffer &buffer, std::int64_t end,
  203. std::int64_t now) {
  204. std::int64_t advance = 0;
  205. if (freewheel() && now >= end + balance() && !buffer.done()) {
  206. // Buffer is overdue in freewheel sync mode, finish immediately.
  207. std::memset(buffer.position(), 0, buffer.remaining());
  208. advance = buffer.advance(buffer.remaining()) / frame_size();
  209. Log::info(SOSSO_LOC, "@%lld - %lld Read buffer overdue, fill by %lu.",
  210. now, end, advance);
  211. }
  212. return advance;
  213. }
  214. // Skip reading part of the buffer to match OSS read position.
  215. std::int64_t buffer_advance(Buffer &buffer, std::int64_t frames) {
  216. if (frames > 0) {
  217. std::size_t skip = buffer.remaining(frames * frame_size());
  218. std::memset(buffer.position(), 0, skip);
  219. return buffer.advance(skip) / frame_size();
  220. }
  221. return 0;
  222. }
  223. // Rewind part of the buffer to match OSS read position.
  224. std::int64_t buffer_rewind(Buffer &buffer, std::int64_t frames) {
  225. if (frames > 0) {
  226. return buffer.rewind(frames * frame_size()) / frame_size();
  227. }
  228. return 0;
  229. }
  230. std::int64_t _oss_progress = 0; // Last memory mapped OSS progress.
  231. std::int64_t _read_position = 0; // Current read position of channel.
  232. };
  233. } // namespace sosso
  234. #endif // SOSSO_READCHANNEL_HPP