Remove non-jackdbus man pages
[jackdbus.git] / freebsd / oss / JackOSSDriver.cpp
blobccdf4b67f19444c9ed98dfb0b073367f1cd95567
1 /*
2 Copyright (C) 2003-2007 Jussi Laako <jussi@sonarnerd.net>
3 Copyright (C) 2008 Grame & RTL 2008
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "driver_interface.h"
22 #include "JackThreadedDriver.h"
23 #include "JackDriverLoader.h"
24 #include "JackOSSDriver.h"
25 #include "JackEngineControl.h"
26 #include "JackGraphManager.h"
27 #include "JackError.h"
28 #include "JackTime.h"
29 #include "JackShmMem.h"
30 #include "memops.h"
32 #include <sys/ioctl.h>
33 #include <sys/soundcard.h>
34 #include <fcntl.h>
35 #include <iostream>
36 #include <assert.h>
37 #include <stdio.h>
39 using namespace std;
41 namespace
44 inline jack_nframes_t TimeToFrames(jack_time_t time, jack_nframes_t sample_rate) {
45 return ((time * sample_rate) + 500000ULL) / 1000000ULL;
48 inline long long TimeToOffset(jack_time_t time1, jack_time_t time2, jack_nframes_t sample_rate)
50 if (time2 > time1) {
51 return TimeToFrames(time2 - time1, sample_rate);
52 } else {
53 return 0LL - TimeToFrames(time1 - time2, sample_rate);
57 inline jack_time_t FramesToTime(jack_nframes_t frames, jack_nframes_t sample_rate) {
58 return ((frames * 1000000ULL) + (sample_rate / 2ULL)) / sample_rate;
61 inline jack_nframes_t RoundUp(jack_nframes_t frames, jack_nframes_t block) {
62 if (block > 0) {
63 frames += (block - 1);
64 frames -= (frames % block);
66 return frames;
69 inline jack_time_t RoundDown(jack_time_t time, jack_time_t interval) {
70 if (interval > 0) {
71 time -= (time % interval);
73 return time;
76 int GetSampleFormat(int bits)
78 switch(bits) {
79 // Native-endian signed 32 bit samples.
80 case 32:
81 return AFMT_S32_NE;
82 // Native-endian signed 24 bit (packed) samples.
83 case 24:
84 return AFMT_S24_NE;
85 // Native-endian signed 16 bit samples, used by default.
86 case 16:
87 default:
88 return AFMT_S16_NE;
92 unsigned int GetSampleSize(int format)
94 switch(format) {
95 // Native-endian signed 32 bit samples.
96 case AFMT_S32_NE:
97 return 4;
98 // Native-endian signed 24 bit (packed) samples.
99 case AFMT_S24_NE:
100 return 3;
101 // Native-endian signed 16 bit samples.
102 case AFMT_S16_NE:
103 return 2;
104 // Unsupported sample format.
105 default:
106 return 0;
110 inline int UpToPower2(int x)
112 int r = 0;
113 while ((1 << r) < x)
114 r++;
115 return r;
120 namespace Jack
123 #ifdef JACK_MONITOR
125 #define CYCLE_POINTS 500000
127 struct OSSCycle {
128 jack_time_t fBeforeRead;
129 jack_time_t fAfterRead;
130 jack_time_t fAfterReadConvert;
131 jack_time_t fBeforeWrite;
132 jack_time_t fAfterWrite;
133 jack_time_t fBeforeWriteConvert;
136 struct OSSCycleTable {
137 jack_time_t fBeforeFirstWrite;
138 jack_time_t fAfterFirstWrite;
139 OSSCycle fTable[CYCLE_POINTS];
142 OSSCycleTable gCycleTable;
143 int gCycleCount = 0;
145 #endif
147 static inline void CopyAndConvertIn(jack_sample_t *dst, void *src, size_t nframes, int channel, int chcount, int bits)
149 switch (bits) {
151 case 16: {
152 signed short *s16src = (signed short*)src;
153 s16src += channel;
154 sample_move_dS_s16(dst, (char*)s16src, nframes, chcount<<1);
155 break;
157 case 24: {
158 char *s24src = (char*)src;
159 s24src += channel * 3;
160 sample_move_dS_s24(dst, s24src, nframes, chcount*3);
161 break;
163 case 32: {
164 signed int *s32src = (signed int*)src;
165 s32src += channel;
166 sample_move_dS_s32u24(dst, (char*)s32src, nframes, chcount<<2);
167 break;
172 static inline void CopyAndConvertOut(void *dst, jack_sample_t *src, size_t nframes, int channel, int chcount, int bits)
174 switch (bits) {
176 case 16: {
177 signed short *s16dst = (signed short*)dst;
178 s16dst += channel;
179 sample_move_d16_sS((char*)s16dst, src, nframes, chcount<<1, NULL); // No dithering for now...
180 break;
182 case 24: {
183 char *s24dst = (char*)dst;
184 s24dst += channel * 3;
185 sample_move_d24_sS(s24dst, src, nframes, chcount*3, NULL);
186 break;
188 case 32: {
189 signed int *s32dst = (signed int*)dst;
190 s32dst += channel;
191 sample_move_d32u24_sS((char*)s32dst, src, nframes, chcount<<2, NULL);
192 break;
197 void JackOSSDriver::DisplayDeviceInfo()
199 audio_buf_info info;
200 memset(&info, 0, sizeof(audio_buf_info));
201 int cap = 0;
203 // Duplex cards : http://manuals.opensound.com/developer/full_duplex.html
204 jack_info("Audio Interface Description :");
205 jack_info("Sampling Frequency : %d, Sample Size : %d", fEngineControl->fSampleRate, fInSampleSize * 8);
207 if (fPlayback) {
209 oss_sysinfo si;
210 if (ioctl(fOutFD, OSS_SYSINFO, &si) == -1) {
211 jack_error("JackOSSDriver::DisplayDeviceInfo OSS_SYSINFO failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
212 } else {
213 jack_info("OSS product %s", si.product);
214 jack_info("OSS version %s", si.version);
215 jack_info("OSS version num %d", si.versionnum);
216 jack_info("OSS numaudios %d", si.numaudios);
217 jack_info("OSS numaudioengines %d", si.numaudioengines);
218 jack_info("OSS numcards %d", si.numcards);
221 jack_info("Output capabilities - %d channels : ", fPlaybackChannels);
222 jack_info("Output block size = %d", fOutputBufferSize);
224 if (ioctl(fOutFD, SNDCTL_DSP_GETOSPACE, &info) == -1) {
225 jack_error("JackOSSDriver::DisplayDeviceInfo SNDCTL_DSP_GETOSPACE failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
226 } else {
227 jack_info("output space info: fragments = %d, fragstotal = %d, fragsize = %d, bytes = %d",
228 info.fragments, info.fragstotal, info.fragsize, info.bytes);
231 if (ioctl(fOutFD, SNDCTL_DSP_GETCAPS, &cap) == -1) {
232 jack_error("JackOSSDriver::DisplayDeviceInfo SNDCTL_DSP_GETCAPS failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
233 } else {
234 if (cap & DSP_CAP_DUPLEX) jack_info(" DSP_CAP_DUPLEX");
235 if (cap & DSP_CAP_REALTIME) jack_info(" DSP_CAP_REALTIME");
236 if (cap & DSP_CAP_BATCH) jack_info(" DSP_CAP_BATCH");
237 if (cap & DSP_CAP_COPROC) jack_info(" DSP_CAP_COPROC");
238 if (cap & DSP_CAP_TRIGGER) jack_info(" DSP_CAP_TRIGGER");
239 if (cap & DSP_CAP_MMAP) jack_info(" DSP_CAP_MMAP");
240 if (cap & DSP_CAP_MULTI) jack_info(" DSP_CAP_MULTI");
241 if (cap & DSP_CAP_BIND) jack_info(" DSP_CAP_BIND");
245 if (fCapture) {
247 oss_sysinfo si;
248 if (ioctl(fInFD, OSS_SYSINFO, &si) == -1) {
249 jack_error("JackOSSDriver::DisplayDeviceInfo OSS_SYSINFO failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
250 } else {
251 jack_info("OSS product %s", si.product);
252 jack_info("OSS version %s", si.version);
253 jack_info("OSS version num %d", si.versionnum);
254 jack_info("OSS numaudios %d", si.numaudios);
255 jack_info("OSS numaudioengines %d", si.numaudioengines);
256 jack_info("OSS numcards %d", si.numcards);
259 jack_info("Input capabilities - %d channels : ", fCaptureChannels);
260 jack_info("Input block size = %d", fInputBufferSize);
262 if (ioctl(fInFD, SNDCTL_DSP_GETISPACE, &info) == -1) {
263 jack_error("JackOSSDriver::DisplayDeviceInfo SNDCTL_DSP_GETOSPACE failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
264 } else {
265 jack_info("input space info: fragments = %d, fragstotal = %d, fragsize = %d, bytes = %d",
266 info.fragments, info.fragstotal, info.fragsize, info.bytes);
269 if (ioctl(fInFD, SNDCTL_DSP_GETCAPS, &cap) == -1) {
270 jack_error("JackOSSDriver::DisplayDeviceInfo SNDCTL_DSP_GETCAPS failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
271 } else {
272 if (cap & DSP_CAP_DUPLEX) jack_info(" DSP_CAP_DUPLEX");
273 if (cap & DSP_CAP_REALTIME) jack_info(" DSP_CAP_REALTIME");
274 if (cap & DSP_CAP_BATCH) jack_info(" DSP_CAP_BATCH");
275 if (cap & DSP_CAP_COPROC) jack_info(" DSP_CAP_COPROC");
276 if (cap & DSP_CAP_TRIGGER) jack_info(" DSP_CAP_TRIGGER");
277 if (cap & DSP_CAP_MMAP) jack_info(" DSP_CAP_MMAP");
278 if (cap & DSP_CAP_MULTI) jack_info(" DSP_CAP_MULTI");
279 if (cap & DSP_CAP_BIND) jack_info(" DSP_CAP_BIND");
284 int JackOSSDriver::ProbeInBlockSize()
286 jack_nframes_t blocks[8] = {0, 0, 0, 0, 0, 0, 0, 0};
287 int probes = 0;
288 int ret = 0;
289 // Default values in case of an error.
290 fInMeanStep = fEngineControl->fBufferSize;
291 fInBlockSize = 1;
293 if (fInFD > 0) {
294 // Read one frame into a new hardware block so we can check its size.
295 // Repeat that for multiple probes, sometimes the first reads differ.
296 jack_nframes_t frames = 1;
297 for (int p = 0; p < 8 && frames > 0; ++p) {
298 ret = Discard(frames);
299 frames = 0;
300 if (ret == 0) {
301 oss_count_t ptr;
302 if (ioctl(fInFD, SNDCTL_DSP_CURRENT_IPTR, &ptr) == 0 && ptr.fifo_samples > 0) {
303 // Success, store probed hardware block size for later.
304 blocks[p] = 1U + ptr.fifo_samples;
305 ++probes;
306 // Proceed by reading one frame into the next hardware block.
307 frames = blocks[p];
309 } else {
310 // Read error - abort.
311 jack_error("JackOSSDriver::ProbeInBlockSize read failed with %d", ret);
315 // Stop recording.
316 ioctl(fInFD, SNDCTL_DSP_HALT_INPUT, NULL);
319 if (probes == 8) {
320 // Compute mean block size of the last six probes.
321 jack_nframes_t sum = 0;
322 for (int p = 2; p < 8; ++p) {
323 jack_log("JackOSSDriver::ProbeInBlockSize read block of %d frames", blocks[p]);
324 sum += blocks[p];
326 fInMeanStep = sum / 6;
328 // Check that none of the probed block sizes deviates too much.
329 jack_nframes_t slack = fInMeanStep / 16;
330 bool strict = true;
331 for (int p = 2; p < 8; ++p) {
332 strict = strict && (blocks[p] > fInMeanStep - slack) && (blocks[p] < fInMeanStep + slack);
335 if (strict && fInMeanStep <= fEngineControl->fBufferSize) {
336 // Regular hardware block size, use it for rounding.
337 jack_info("JackOSSDriver::ProbeInBlockSize read blocks are %d frames", fInMeanStep);
338 fInBlockSize = fInMeanStep;
339 } else {
340 jack_info("JackOSSDriver::ProbeInBlockSize irregular read block sizes");
341 jack_info("JackOSSDriver::ProbeInBlockSize mean read block was %d frames", fInMeanStep);
344 if (fInBlockSize > fEngineControl->fBufferSize / 2) {
345 jack_info("JackOSSDriver::ProbeInBlockSize less than two read blocks per cycle");
346 jack_info("JackOSSDriver::ProbeInBlockSize for best results make period a multiple of %d", fInBlockSize);
349 if (fInMeanStep > fEngineControl->fBufferSize) {
350 jack_error("JackOSSDriver::ProbeInBlockSize period is too small, minimum is %d frames", fInMeanStep);
351 return -1;
355 return ret;
358 int JackOSSDriver::ProbeOutBlockSize()
360 jack_nframes_t blocks[8] = {0, 0, 0, 0, 0, 0, 0, 0};
361 int probes = 0;
362 int ret = 0;
363 // Default values in case of an error.
364 fOutMeanStep = fEngineControl->fBufferSize;
365 fOutBlockSize = 1;
367 if (fOutFD) {
368 // Write one frame over the low water mark, then check the consumed block size.
369 // Repeat that for multiple probes, sometimes the initial ones differ.
370 jack_nframes_t mark = fNperiods * fEngineControl->fBufferSize;
371 WriteSilence(mark + 1);
372 for (int p = 0; p < 8 && ret >= 0; ++p) {
373 pollfd poll_fd;
374 poll_fd.fd = fOutFD;
375 poll_fd.events = POLLOUT;
376 ret = poll(&poll_fd, 1, 500);
377 if (ret < 0) {
378 jack_error("JackOSSDriver::ProbeOutBlockSize poll failed with %d", ret);
379 break;
381 if (poll_fd.revents & POLLOUT) {
382 oss_count_t ptr;
383 if (ioctl(fOutFD, SNDCTL_DSP_CURRENT_OPTR, &ptr) != -1 && ptr.fifo_samples >= 0) {
384 // Success, store probed hardware block size for later.
385 blocks[p] = mark + 1 - ptr.fifo_samples;
386 ++probes;
387 // Proceed by writing one frame over the low water mark.
388 WriteSilence(blocks[p]);
390 poll_fd.revents = 0;
394 // Stop playback.
395 ioctl(fOutFD, SNDCTL_DSP_HALT_INPUT, NULL);
398 if (probes == 8) {
399 // Compute mean and maximum block size of the last six probes.
400 jack_nframes_t sum = 0;
401 for (int p = 2; p < 8; ++p) {
402 jack_log("JackOSSDriver::ProbeOutBlockSize write block of %d frames", blocks[p]);
403 sum += blocks[p];
405 fOutMeanStep = sum / 6;
407 // Check that none of the probed block sizes deviates too much.
408 jack_nframes_t slack = fOutMeanStep / 16;
409 bool strict = true;
410 for (int p = 2; p < 8; ++p) {
411 strict = strict && (blocks[p] > fOutMeanStep - slack) && (blocks[p] < fOutMeanStep + slack);
414 if (strict && fOutMeanStep <= fEngineControl->fBufferSize) {
415 // Regular hardware block size, use it for rounding.
416 jack_info("JackOSSDriver::ProbeOutBlockSize write blocks are %d frames", fOutMeanStep);
417 fOutBlockSize = fOutMeanStep;
418 } else {
419 jack_info("JackOSSDriver::ProbeOutBlockSize irregular write block sizes");
420 jack_info("JackOSSDriver::ProbeOutBlockSize mean write block was %d frames", fOutMeanStep);
423 if (fOutBlockSize > fEngineControl->fBufferSize / 2) {
424 jack_info("JackOSSDriver::ProbeOutBlockSize less than two write blocks per cycle");
425 jack_info("JackOSSDriver::ProbeOutBlockSize for best results make period a multiple of %d", fOutBlockSize);
428 if (fOutMeanStep > fEngineControl->fBufferSize) {
429 jack_error("JackOSSDriver::ProbeOutBlockSize period is too small, minimum is %d frames", fOutMeanStep);
430 return -1;
434 return ret;
437 int JackOSSDriver::Discard(jack_nframes_t frames)
439 if (fInFD < 0) {
440 return -1;
443 // Read frames from OSS capture buffer to be discarded.
444 ssize_t size = frames * fInSampleSize * fCaptureChannels;
445 while (size > 0) {
446 ssize_t chunk = (size > fInputBufferSize) ? fInputBufferSize : size;
447 ssize_t count = ::read(fInFD, fInputBuffer, chunk);
448 if (count <= 0) {
449 jack_error("JackOSSDriver::Discard error bytes read = %ld", count);
450 return -1;
452 fOSSReadOffset += count / (fInSampleSize * fCaptureChannels);
453 size -= count;
455 return 0;
458 int JackOSSDriver::WriteSilence(jack_nframes_t frames)
460 if (fOutFD < 0) {
461 return -1;
464 // Fill OSS playback buffer, write some periods of silence.
465 memset(fOutputBuffer, 0, fOutputBufferSize);
466 ssize_t size = frames * fOutSampleSize * fPlaybackChannels;
467 while (size > 0) {
468 ssize_t chunk = (size > fOutputBufferSize) ? fOutputBufferSize : size;
469 ssize_t count = ::write(fOutFD, fOutputBuffer, chunk);
470 if (count <= 0) {
471 jack_error("JackOSSDriver::WriteSilence error bytes written = %ld", count);
472 return -1;
474 fOSSWriteOffset += (count / (fOutSampleSize * fPlaybackChannels));
475 size -= count;
477 return 0;
480 int JackOSSDriver::WaitAndSync()
482 oss_count_t ptr = {0, 0, {0}};
483 if (fInFD > 0 && fOSSReadSync != 0) {
484 // Predict time of next capture sync (poll() return).
485 if (fOSSReadOffset + fEngineControl->fBufferSize > 0) {
486 jack_nframes_t frames = fOSSReadOffset + fEngineControl->fBufferSize;
487 jack_nframes_t rounded = RoundUp(frames, fInBlockSize);
488 fOSSReadSync += FramesToTime(rounded, fEngineControl->fSampleRate);
489 fOSSReadOffset -= rounded;
492 if (fOutFD > 0 && fOSSWriteSync != 0) {
493 // Predict time of next playback sync (poll() return).
494 if (fOSSWriteOffset > fNperiods * fEngineControl->fBufferSize) {
495 jack_nframes_t frames = fOSSWriteOffset - fNperiods * fEngineControl->fBufferSize;
496 jack_nframes_t rounded = RoundUp(frames, fOutBlockSize);
497 fOSSWriteSync += FramesToTime(rounded, fEngineControl->fSampleRate);
498 fOSSWriteOffset -= rounded;
501 jack_time_t poll_start = GetMicroSeconds();
502 // Poll until recording and playback buffer are ready for this cycle.
503 pollfd poll_fd[2];
504 poll_fd[0].fd = fInFD;
505 if (fInFD > 0 && (fForceSync || poll_start < fOSSReadSync)) {
506 poll_fd[0].events = POLLIN;
507 } else {
508 poll_fd[0].events = 0;
510 poll_fd[1].fd = fOutFD;
511 if (fOutFD > 0 && (fForceSync || poll_start < fOSSWriteSync)) {
512 poll_fd[1].events = POLLOUT;
513 } else {
514 poll_fd[1].events = 0;
516 while (poll_fd[0].events != 0 || poll_fd[1].events != 0) {
517 poll_fd[0].revents = 0;
518 poll_fd[1].revents = 0;
519 int ret = poll(poll_fd, 2, 500);
520 jack_time_t now = GetMicroSeconds();
521 if (ret <= 0) {
522 jack_error("JackOSSDriver::WaitAndSync poll failed with %d after %ld us", ret, now - poll_start);
523 return ret;
525 if (poll_fd[0].revents & POLLIN) {
526 // Check the excess recording frames.
527 if (ioctl(fInFD, SNDCTL_DSP_CURRENT_IPTR, &ptr) != -1 && ptr.fifo_samples >= 0) {
528 if (fInBlockSize <= 1) {
529 // Irregular block size, let sync time converge slowly when late.
530 fOSSReadSync = min(fOSSReadSync, now) / 2 + now / 2;
531 fOSSReadOffset = -ptr.fifo_samples;
532 } else if (ptr.fifo_samples - fEngineControl->fBufferSize >= fInBlockSize) {
533 // Too late for a reliable sync, make sure sync time is not in the future.
534 if (now < fOSSReadSync) {
535 fOSSReadOffset = -ptr.fifo_samples;
536 jack_info("JackOSSDriver::WaitAndSync capture sync %ld us early, %ld frames", fOSSReadSync - now, fOSSReadOffset);
537 fOSSReadSync = now;
539 } else if (fForceSync) {
540 // Uncertain previous sync, just use sync time directly.
541 fOSSReadSync = now;
542 fOSSReadOffset = -ptr.fifo_samples;
543 } else {
544 // Adapt expected sync time when early or late - in whole block intervals.
545 // Account for some speed drift, but otherwise round down to earlier interval.
546 jack_time_t interval = FramesToTime(fInBlockSize, fEngineControl->fSampleRate);
547 jack_time_t remainder = fOSSReadSync % interval;
548 jack_time_t max_drift = interval / 4;
549 jack_time_t rounded = RoundDown((now - remainder) + max_drift, interval) + remainder;
550 // Let sync time converge slowly when late, prefer earlier sync times.
551 fOSSReadSync = min(rounded, now) / 2 + now / 2;
552 fOSSReadOffset = -ptr.fifo_samples;
555 poll_fd[0].events = 0;
557 if (poll_fd[1].revents & POLLOUT) {
558 // Check the remaining playback frames.
559 if (ioctl(fOutFD, SNDCTL_DSP_CURRENT_OPTR, &ptr) != -1 && ptr.fifo_samples >= 0) {
560 if (fOutBlockSize <= 1) {
561 // Irregular block size, let sync time converge slowly when late.
562 fOSSWriteSync = min(fOSSWriteSync, now) / 2 + now / 2;
563 fOSSWriteOffset = ptr.fifo_samples;
564 } else if (ptr.fifo_samples + fOutBlockSize <= fNperiods * fEngineControl->fBufferSize) {
565 // Too late for a reliable sync, make sure sync time is not in the future.
566 if (now < fOSSWriteSync) {
567 fOSSWriteOffset = ptr.fifo_samples;
568 jack_info("JackOSSDriver::WaitAndSync playback sync %ld us early, %ld frames", fOSSWriteSync - now, fOSSWriteOffset);
569 fOSSWriteSync = now;
571 } else if (fForceSync) {
572 // Uncertain previous sync, just use sync time directly.
573 fOSSWriteSync = now;
574 fOSSWriteOffset = ptr.fifo_samples;
575 } else {
576 // Adapt expected sync time when early or late - in whole block intervals.
577 // Account for some speed drift, but otherwise round down to earlier interval.
578 jack_time_t interval = FramesToTime(fOutBlockSize, fEngineControl->fSampleRate);
579 jack_time_t remainder = fOSSWriteSync % interval;
580 jack_time_t max_drift = interval / 4;
581 jack_time_t rounded = RoundDown((now - remainder) + max_drift, interval) + remainder;
582 // Let sync time converge slowly when late, prefer earlier sync times.
583 fOSSWriteSync = min(rounded, now) / 2 + now / 2;
584 fOSSWriteOffset = ptr.fifo_samples;
587 poll_fd[1].events = 0;
591 fForceSync = false;
593 // Compute balance of read and write buffers combined.
594 fBufferBalance = 0;
595 if (fInFD > 0 && fOutFD > 0) {
596 // Compare actual buffer content with target of (1 + n) * period.
597 fBufferBalance += ((1 + fNperiods) * fEngineControl->fBufferSize);
598 fBufferBalance -= (fOSSWriteOffset - fOSSReadOffset);
599 fBufferBalance += TimeToOffset(fOSSWriteSync, fOSSReadSync, fEngineControl->fSampleRate);
601 // Force balancing if sync times deviate too much.
602 jack_time_t slack = FramesToTime((fEngineControl->fBufferSize * 2) / 3, fEngineControl->fSampleRate);
603 fForceBalancing = fForceBalancing || (fOSSReadSync > fOSSWriteSync + slack);
604 fForceBalancing = fForceBalancing || (fOSSWriteSync > fOSSReadSync + slack);
605 // Force balancing if buffer is badly balanced.
606 fForceBalancing = fForceBalancing || (abs(fBufferBalance) > max(fInMeanStep, fOutMeanStep));
609 // Print debug info every 10 seconds.
610 if (ptr.samples > 0 && (ptr.samples % (10 * fEngineControl->fSampleRate)) < fEngineControl->fBufferSize) {
611 jack_log("JackOSSDriver::Read buffer balance is %ld frames", fBufferBalance);
612 jack_time_t now = GetMicroSeconds();
613 jack_log("JackOSSDriver::Read recording sync %ld frames %ld us ago", fOSSReadOffset, now - fOSSReadSync);
614 jack_log("JackOSSDriver::Read playback sync %ld frames %ld us ago", fOSSWriteOffset, now - fOSSWriteSync);
617 return 0;
620 int JackOSSDriver::OpenInput()
622 int flags = 0;
623 int gFragFormat;
624 int cur_capture_channels;
625 int cur_sample_format;
626 jack_nframes_t cur_sample_rate;
627 audio_buf_info info;
629 if (fCaptureChannels == 0) fCaptureChannels = 2;
631 if ((fInFD = open(fCaptureDriverName, O_RDONLY | ((fExcl) ? O_EXCL : 0))) < 0) {
632 jack_error("JackOSSDriver::OpenInput failed to open device : %s@%i, errno = %d", __FILE__, __LINE__, errno);
633 return -1;
636 jack_log("JackOSSDriver::OpenInput input fInFD = %d", fInFD);
638 if (fExcl) {
639 if (ioctl(fInFD, SNDCTL_DSP_COOKEDMODE, &flags) == -1) {
640 jack_error("JackOSSDriver::OpenInput failed to set cooked mode : %s@%i, errno = %d", __FILE__, __LINE__, errno);
641 goto error;
645 cur_sample_format = GetSampleFormat(fBits);
646 if (ioctl(fInFD, SNDCTL_DSP_SETFMT, &cur_sample_format) == -1) {
647 jack_error("JackOSSDriver::OpenInput failed to set format : %s@%i, errno = %d", __FILE__, __LINE__, errno);
648 goto error;
650 fInSampleSize = GetSampleSize(cur_sample_format);
651 if (cur_sample_format != GetSampleFormat(fBits)) {
652 if (fInSampleSize > 0) {
653 jack_info("JackOSSDriver::OpenInput driver forced %d bit sample format", fInSampleSize * 8);
654 } else {
655 jack_error("JackOSSDriver::OpenInput unsupported sample format %#x", cur_sample_format);
656 goto error;
660 cur_capture_channels = fCaptureChannels;
661 if (ioctl(fInFD, SNDCTL_DSP_CHANNELS, &fCaptureChannels) == -1) {
662 jack_error("JackOSSDriver::OpenInput failed to set channels : %s@%i, errno = %d", __FILE__, __LINE__, errno);
663 goto error;
665 if (cur_capture_channels != fCaptureChannels) {
666 jack_info("JackOSSDriver::OpenInput driver forced the number of capture channels %ld", fCaptureChannels);
669 cur_sample_rate = fEngineControl->fSampleRate;
670 if (ioctl(fInFD, SNDCTL_DSP_SPEED, &fEngineControl->fSampleRate) == -1) {
671 jack_error("JackOSSDriver::OpenInput failed to set sample rate : %s@%i, errno = %d", __FILE__, __LINE__, errno);
672 goto error;
674 if (cur_sample_rate != fEngineControl->fSampleRate) {
675 jack_info("JackOSSDriver::OpenInput driver forced the sample rate %ld", fEngineControl->fSampleRate);
678 // Internal buffer size required for one period.
679 fInputBufferSize = fEngineControl->fBufferSize * fInSampleSize * fCaptureChannels;
681 // Get the total size of the OSS recording buffer, in sample frames.
682 info = {0, 0, 0, 0};
683 if (ioctl(fInFD, SNDCTL_DSP_GETISPACE, &info) == -1 || info.fragsize <= 0 || info.fragstotal <= 0) {
684 jack_error("JackOSSDriver::OpenInput failed to get buffer info : %s@%i, errno = %d", __FILE__, __LINE__, errno);
685 goto error;
687 fOSSInBuffer = info.fragstotal * info.fragsize / (fInSampleSize * fCaptureChannels);
689 if (fOSSInBuffer < fEngineControl->fBufferSize * (1 + fNperiods)) {
690 // Total size of the OSS recording buffer is too small, resize it.
691 unsigned int buf_size = fInputBufferSize * (1 + fNperiods);
692 // Keep current fragment size if possible - respect OSS latency settings.
693 gFragFormat = UpToPower2(info.fragsize);
694 unsigned int frag_size = 1U << gFragFormat;
695 gFragFormat |= ((buf_size + frag_size - 1) / frag_size) << 16;
696 jack_info("JackOSSDriver::OpenInput request %d fragments of %d", (gFragFormat >> 16), frag_size);
697 if (ioctl(fInFD, SNDCTL_DSP_SETFRAGMENT, &gFragFormat) == -1) {
698 jack_error("JackOSSDriver::OpenInput failed to set fragments : %s@%i, errno = %d", __FILE__, __LINE__, errno);
699 goto error;
701 // Check the new OSS recording buffer size.
702 info = {0, 0, 0, 0};
703 if (ioctl(fInFD, SNDCTL_DSP_GETISPACE, &info) == -1 || info.fragsize <= 0 || info.fragstotal <= 0) {
704 jack_error("JackOSSDriver::OpenInput failed to get buffer info : %s@%i, errno = %d", __FILE__, __LINE__, errno);
705 goto error;
707 fOSSInBuffer = info.fragstotal * info.fragsize / (fInSampleSize * fCaptureChannels);
710 if (fOSSInBuffer > fEngineControl->fBufferSize) {
711 int mark = fInputBufferSize;
712 if (ioctl(fInFD, SNDCTL_DSP_LOW_WATER, &mark) != 0) {
713 jack_error("JackOSSDriver::OpenInput failed to set low water mark : %s@%i, errno = %d", __FILE__, __LINE__, errno);
714 goto error;
716 jack_info("JackOSSDriver::OpenInput set low water mark to %d", mark);
719 fInputBuffer = (void*)calloc(fInputBufferSize, 1);
720 assert(fInputBuffer);
722 if (ProbeInBlockSize() < 0) {
723 goto error;
726 return 0;
728 error:
729 ::close(fInFD);
730 return -1;
733 int JackOSSDriver::OpenOutput()
735 int flags = 0;
736 int gFragFormat;
737 int cur_sample_format;
738 int cur_playback_channels;
739 jack_nframes_t cur_sample_rate;
740 audio_buf_info info;
742 if (fPlaybackChannels == 0) fPlaybackChannels = 2;
744 if ((fOutFD = open(fPlaybackDriverName, O_WRONLY | ((fExcl) ? O_EXCL : 0))) < 0) {
745 jack_error("JackOSSDriver::OpenOutput failed to open device : %s@%i, errno = %d", __FILE__, __LINE__, errno);
746 return -1;
749 jack_log("JackOSSDriver::OpenOutput output fOutFD = %d", fOutFD);
751 if (fExcl) {
752 if (ioctl(fOutFD, SNDCTL_DSP_COOKEDMODE, &flags) == -1) {
753 jack_error("JackOSSDriver::OpenOutput failed to set cooked mode : %s@%i, errno = %d", __FILE__, __LINE__, errno);
754 goto error;
758 cur_sample_format = GetSampleFormat(fBits);
759 if (ioctl(fOutFD, SNDCTL_DSP_SETFMT, &cur_sample_format) == -1) {
760 jack_error("JackOSSDriver::OpenOutput failed to set format : %s@%i, errno = %d", __FILE__, __LINE__, errno);
761 goto error;
763 fOutSampleSize = GetSampleSize(cur_sample_format);
764 if (cur_sample_format != GetSampleFormat(fBits)) {
765 if (fOutSampleSize > 0) {
766 jack_info("JackOSSDriver::OpenOutput driver forced %d bit sample format", fOutSampleSize * 8);
767 } else {
768 jack_error("JackOSSDriver::OpenOutput unsupported sample format %#x", cur_sample_format);
769 goto error;
773 cur_playback_channels = fPlaybackChannels;
774 if (ioctl(fOutFD, SNDCTL_DSP_CHANNELS, &fPlaybackChannels) == -1) {
775 jack_error("JackOSSDriver::OpenOutput failed to set channels : %s@%i, errno = %d", __FILE__, __LINE__, errno);
776 goto error;
778 if (cur_playback_channels != fPlaybackChannels) {
779 jack_info("JackOSSDriver::OpenOutput driver forced the number of playback channels %ld", fPlaybackChannels);
782 cur_sample_rate = fEngineControl->fSampleRate;
783 if (ioctl(fOutFD, SNDCTL_DSP_SPEED, &fEngineControl->fSampleRate) == -1) {
784 jack_error("JackOSSDriver::OpenOutput failed to set sample rate : %s@%i, errno = %d", __FILE__, __LINE__, errno);
785 goto error;
787 if (cur_sample_rate != fEngineControl->fSampleRate) {
788 jack_info("JackOSSDriver::OpenInput driver forced the sample rate %ld", fEngineControl->fSampleRate);
791 // Internal buffer size required for one period.
792 fOutputBufferSize = fEngineControl->fBufferSize * fOutSampleSize * fPlaybackChannels;
794 // Get the total size of the OSS playback buffer, in sample frames.
795 info = {0, 0, 0, 0};
796 if (ioctl(fOutFD, SNDCTL_DSP_GETOSPACE, &info) == -1 || info.fragsize <= 0 || info.fragstotal <= 0) {
797 jack_error("JackOSSDriver::OpenOutput failed to get buffer info : %s@%i, errno = %d", __FILE__, __LINE__, errno);
798 goto error;
800 fOSSOutBuffer = info.fragstotal * info.fragsize / (fOutSampleSize * fPlaybackChannels);
802 if (fOSSOutBuffer < fEngineControl->fBufferSize * (1 + fNperiods)) {
803 // Total size of the OSS playback buffer is too small, resize it.
804 unsigned int buf_size = fOutputBufferSize * (1 + fNperiods);
805 // Keep current fragment size if possible - respect OSS latency settings.
806 // Some sound cards like Intel HDA may stutter when changing the fragment size.
807 gFragFormat = UpToPower2(info.fragsize);
808 unsigned int frag_size = 1U << gFragFormat;
809 gFragFormat |= ((buf_size + frag_size - 1) / frag_size) << 16;
810 jack_info("JackOSSDriver::OpenOutput request %d fragments of %d", (gFragFormat >> 16), frag_size);
811 if (ioctl(fOutFD, SNDCTL_DSP_SETFRAGMENT, &gFragFormat) == -1) {
812 jack_error("JackOSSDriver::OpenOutput failed to set fragments : %s@%i, errno = %d", __FILE__, __LINE__, errno);
813 goto error;
815 // Check the new OSS playback buffer size.
816 info = {0, 0, 0, 0};
817 if (ioctl(fOutFD, SNDCTL_DSP_GETOSPACE, &info) == -1 || info.fragsize <= 0 || info.fragstotal <= 0) {
818 jack_error("JackOSSDriver::OpenOutput failed to get buffer info : %s@%i, errno = %d", __FILE__, __LINE__, errno);
819 goto error;
821 fOSSOutBuffer = info.fragstotal * info.fragsize / (fOutSampleSize * fPlaybackChannels);
824 if (fOSSOutBuffer > fEngineControl->fBufferSize * fNperiods) {
825 jack_nframes_t low = fOSSOutBuffer - (fNperiods * fEngineControl->fBufferSize);
826 int mark = low * fOutSampleSize * fPlaybackChannels;
827 if (ioctl(fOutFD, SNDCTL_DSP_LOW_WATER, &mark) != 0) {
828 jack_error("JackOSSDriver::OpenOutput failed to set low water mark : %s@%i, errno = %d", __FILE__, __LINE__, errno);
829 goto error;
831 jack_info("JackOSSDriver::OpenOutput set low water mark to %d", mark);
834 fOutputBuffer = (void*)calloc(fOutputBufferSize, 1);
835 assert(fOutputBuffer);
837 if (ProbeOutBlockSize() < 0) {
838 goto error;
841 return 0;
843 error:
844 ::close(fOutFD);
845 return -1;
848 int JackOSSDriver::Open(jack_nframes_t nframes,
849 int user_nperiods,
850 jack_nframes_t samplerate,
851 bool capturing,
852 bool playing,
853 int inchannels,
854 int outchannels,
855 bool excl,
856 bool monitor,
857 const char* capture_driver_uid,
858 const char* playback_driver_uid,
859 jack_nframes_t capture_latency,
860 jack_nframes_t playback_latency,
861 int bits,
862 bool ignorehwbuf)
864 // Store local settings first.
865 fCapture = capturing;
866 fPlayback = playing;
867 fBits = bits;
868 fIgnoreHW = ignorehwbuf;
869 fNperiods = user_nperiods;
870 fExcl = excl;
871 fExtraCaptureLatency = capture_latency;
872 fExtraPlaybackLatency = playback_latency;
874 // Additional playback latency introduced by the OSS buffer. The extra hardware
875 // latency given by the user should then be symmetric as reported by jack_iodelay.
876 playback_latency += user_nperiods * nframes;
877 // Generic JackAudioDriver Open
878 if (JackAudioDriver::Open(nframes, samplerate, capturing, playing, inchannels, outchannels, monitor,
879 capture_driver_uid, playback_driver_uid, capture_latency, playback_latency) != 0) {
880 return -1;
881 } else {
883 #ifdef JACK_MONITOR
884 // Force memory page in
885 memset(&gCycleTable, 0, sizeof(gCycleTable));
886 #endif
888 if (OpenAux() < 0) {
889 Close();
890 return -1;
891 } else {
892 return 0;
897 int JackOSSDriver::Close()
899 #ifdef JACK_MONITOR
900 FILE* file = fopen("OSSProfiling.log", "w");
902 if (file) {
903 jack_info("Writing OSS driver timing data....");
904 for (int i = 1; i < gCycleCount; i++) {
905 int d1 = gCycleTable.fTable[i].fAfterRead - gCycleTable.fTable[i].fBeforeRead;
906 int d2 = gCycleTable.fTable[i].fAfterReadConvert - gCycleTable.fTable[i].fAfterRead;
907 int d3 = gCycleTable.fTable[i].fAfterWrite - gCycleTable.fTable[i].fBeforeWrite;
908 int d4 = gCycleTable.fTable[i].fBeforeWrite - gCycleTable.fTable[i].fBeforeWriteConvert;
909 fprintf(file, "%d \t %d \t %d \t %d \t \n", d1, d2, d3, d4);
911 fclose(file);
912 } else {
913 jack_error("JackOSSDriver::Close : cannot open OSSProfiling.log file");
916 file = fopen("TimingOSS.plot", "w");
918 if (file == NULL) {
919 jack_error("JackOSSDriver::Close cannot open TimingOSS.plot file");
920 } else {
922 fprintf(file, "set grid\n");
923 fprintf(file, "set title \"OSS audio driver timing\"\n");
924 fprintf(file, "set xlabel \"audio cycles\"\n");
925 fprintf(file, "set ylabel \"usec\"\n");
926 fprintf(file, "plot \"OSSProfiling.log\" using 1 title \"Driver read wait\" with lines, \
927 \"OSSProfiling.log\" using 2 title \"Driver read convert duration\" with lines, \
928 \"OSSProfiling.log\" using 3 title \"Driver write wait\" with lines, \
929 \"OSSProfiling.log\" using 4 title \"Driver write convert duration\" with lines\n");
931 fprintf(file, "set output 'TimingOSS.pdf\n");
932 fprintf(file, "set terminal pdf\n");
934 fprintf(file, "set grid\n");
935 fprintf(file, "set title \"OSS audio driver timing\"\n");
936 fprintf(file, "set xlabel \"audio cycles\"\n");
937 fprintf(file, "set ylabel \"usec\"\n");
938 fprintf(file, "plot \"OSSProfiling.log\" using 1 title \"Driver read wait\" with lines, \
939 \"OSSProfiling.log\" using 2 title \"Driver read convert duration\" with lines, \
940 \"OSSProfiling.log\" using 3 title \"Driver write wait\" with lines, \
941 \"OSSProfiling.log\" using 4 title \"Driver write convert duration\" with lines\n");
943 fclose(file);
945 #endif
946 int res = JackAudioDriver::Close();
947 CloseAux();
948 return res;
952 int JackOSSDriver::OpenAux()
954 // (Re-)Initialize runtime variables.
955 fInSampleSize = fOutSampleSize = 0;
956 fInputBufferSize = fOutputBufferSize = 0;
957 fInBlockSize = fOutBlockSize = 1;
958 fInMeanStep = fOutMeanStep = 0;
959 fOSSInBuffer = fOSSOutBuffer = 0;
960 fOSSReadSync = fOSSWriteSync = 0;
961 fOSSReadOffset = fOSSWriteOffset = 0;
962 fBufferBalance = 0;
963 fForceBalancing = false;
964 fForceSync = false;
966 if (fCapture && (OpenInput() < 0)) {
967 return -1;
970 if (fPlayback && (OpenOutput() < 0)) {
971 return -1;
974 DisplayDeviceInfo();
975 return 0;
978 void JackOSSDriver::CloseAux()
980 if (fCapture && fInFD > 0) {
981 close(fInFD);
982 fInFD = -1;
985 if (fPlayback && fOutFD > 0) {
986 close(fOutFD);
987 fOutFD = -1;
990 if (fInputBuffer)
991 free(fInputBuffer);
992 fInputBuffer = NULL;
994 if (fOutputBuffer)
995 free(fOutputBuffer);
996 fOutputBuffer = NULL;
999 int JackOSSDriver::Read()
1001 if (fInFD > 0 && fOSSReadSync == 0) {
1002 // First cycle, account for leftover samples from previous reads.
1003 fOSSReadOffset = 0;
1004 oss_count_t ptr;
1005 if (ioctl(fInFD, SNDCTL_DSP_CURRENT_IPTR, &ptr) == 0 && ptr.fifo_samples > 0) {
1006 jack_log("JackOSSDriver::Read pre recording samples = %ld, fifo_samples = %d", ptr.samples, ptr.fifo_samples);
1007 fOSSReadOffset = -ptr.fifo_samples;
1010 // Start capture by reading a new hardware block.,
1011 jack_nframes_t discard = fInMeanStep - fOSSReadOffset;
1012 // Let half a block or at most 1ms remain in buffer, avoid drift issues at start.
1013 discard -= min(TimeToFrames(1000, fEngineControl->fSampleRate), (fInMeanStep / 2));
1014 jack_log("JackOSSDriver::Read start recording discard %ld frames", discard);
1015 fOSSReadSync = GetMicroSeconds();
1016 Discard(discard);
1018 fForceSync = true;
1019 fForceBalancing = true;
1022 if (fOutFD > 0 && fOSSWriteSync == 0) {
1023 // First cycle, account for leftover samples from previous writes.
1024 fOSSWriteOffset = 0;
1025 oss_count_t ptr;
1026 if (ioctl(fOutFD, SNDCTL_DSP_CURRENT_OPTR, &ptr) == 0 && ptr.fifo_samples > 0) {
1027 jack_log("JackOSSDriver::Read pre playback samples = %ld, fifo_samples = %d", ptr.samples, ptr.fifo_samples);
1028 fOSSWriteOffset = ptr.fifo_samples;
1031 // Start playback with silence, target latency as given by the user.
1032 jack_nframes_t silence = (fNperiods + 1) * fEngineControl->fBufferSize;
1033 // Minus half a block or at most 1ms of frames, avoid drift issues at start.
1034 silence -= min(TimeToFrames(1000, fEngineControl->fSampleRate), (fOutMeanStep / 2));
1035 silence = max(silence - fOSSWriteOffset, 1LL);
1036 jack_log("JackOSSDriver::Read start playback with %ld frames of silence", silence);
1037 fOSSWriteSync = GetMicroSeconds();
1038 WriteSilence(silence);
1040 fForceSync = true;
1041 fForceBalancing = true;
1044 #ifdef JACK_MONITOR
1045 gCycleTable.fTable[gCycleCount].fBeforeRead = GetMicroSeconds();
1046 #endif
1048 if (WaitAndSync() < 0) {
1049 return -1;
1052 // Keep begin cycle time
1053 JackDriver::CycleTakeBeginTime();
1055 if (fInFD < 0) {
1056 return 0;
1059 // Try to read multiple times in case of short reads.
1060 size_t count = 0;
1061 for (int i = 0; i < 3 && count < fInputBufferSize; ++i) {
1062 ssize_t ret = ::read(fInFD, ((char*)fInputBuffer) + count, fInputBufferSize - count);
1063 if (ret < 0) {
1064 jack_error("JackOSSDriver::Read error = %s", strerror(errno));
1065 return -1;
1067 count += ret;
1070 // Read offset accounting and overrun detection.
1071 if (count > 0) {
1072 jack_time_t now = GetMicroSeconds();
1073 jack_time_t sync = max(fOSSReadSync, fOSSWriteSync);
1074 if (now - sync > 1000) {
1075 // Blocking read() may indicate sample loss in OSS - force resync.
1076 jack_log("JackOSSDriver::Read long read duration of %ld us", now - sync);
1077 fForceSync = true;
1079 long long passed = TimeToFrames(now - fOSSReadSync, fEngineControl->fSampleRate);
1080 passed -= (passed % fInBlockSize);
1081 if (passed > fOSSReadOffset + fOSSInBuffer) {
1082 // Overrun, adjust read and write position.
1083 long long missed = passed - (fOSSReadOffset + fOSSInBuffer);
1084 jack_error("JackOSSDriver::Read missed %ld frames by overrun, passed=%ld, sync=%ld, now=%ld", missed, passed, fOSSReadSync, now);
1085 fOSSReadOffset += missed;
1086 fOSSWriteOffset += missed;
1087 NotifyXRun(now, float(FramesToTime(missed, fEngineControl->fSampleRate)));
1089 fOSSReadOffset += count / (fInSampleSize * fCaptureChannels);
1092 #ifdef JACK_MONITOR
1093 if (count > 0 && count != (int)fInputBufferSize)
1094 jack_log("JackOSSDriver::Read count = %ld", count / (fInSampleSize * fCaptureChannels));
1095 gCycleTable.fTable[gCycleCount].fAfterRead = GetMicroSeconds();
1096 #endif
1098 // Check and clear OSS errors.
1099 audio_errinfo ei_in;
1100 if (ioctl(fInFD, SNDCTL_DSP_GETERROR, &ei_in) == 0) {
1102 // Not reliable for overrun detection, virtual_oss doesn't implement it.
1103 if (ei_in.rec_overruns > 0 ) {
1104 jack_error("JackOSSDriver::Read %d overrun events", ei_in.rec_overruns);
1107 if (ei_in.rec_errorcount > 0 && ei_in.rec_lasterror != 0) {
1108 jack_error("%d OSS rec event(s), last=%05d:%d", ei_in.rec_errorcount, ei_in.rec_lasterror, ei_in.rec_errorparm);
1112 if (count < fInputBufferSize) {
1113 jack_error("JackOSSDriver::Read incomplete read of %ld bytes", count);
1114 return -1;
1117 for (int i = 0; i < fCaptureChannels; i++) {
1118 if (fGraphManager->GetConnectionsNum(fCapturePortList[i]) > 0) {
1119 CopyAndConvertIn(GetInputBuffer(i), fInputBuffer, fEngineControl->fBufferSize, i, fCaptureChannels, fInSampleSize * 8);
1123 #ifdef JACK_MONITOR
1124 gCycleTable.fTable[gCycleCount].fAfterReadConvert = GetMicroSeconds();
1125 #endif
1127 return 0;
1130 int JackOSSDriver::Write()
1132 if (fOutFD < 0) {
1133 return 0;
1136 unsigned int skip = 0;
1137 jack_time_t start = GetMicroSeconds();
1139 if (fOSSWriteSync > 0) {
1140 // Check for underruns, rounded to hardware block size if available.
1141 long long passed = TimeToFrames(start - fOSSWriteSync, fEngineControl->fSampleRate);
1142 long long consumed = passed - (passed % fOutBlockSize);
1143 long long tolerance = (fOutBlockSize > 1) ? 0 : fOutMeanStep;
1144 long long overdue = 0;
1145 if (consumed > fOSSWriteOffset + tolerance) {
1146 // Skip playback data that already passed.
1147 overdue = consumed - fOSSWriteOffset - tolerance;
1148 jack_error("JackOSSDriver::Write underrun, late by %ld, skip %ld frames", passed - fOSSWriteOffset, overdue);
1149 jack_log("JackOSSDriver::Write playback offset %ld frames synced %ld us ago", fOSSWriteOffset, start - fOSSWriteSync);
1150 // Also consider buffer balance, there was a gap in playback anyway.
1151 fForceBalancing = true;
1153 // Account for buffer balance if needed.
1154 long long progress = fEngineControl->fBufferSize;
1155 if (fForceBalancing) {
1156 fForceBalancing = false;
1157 progress = max(progress + fBufferBalance, 0LL);
1158 jack_info("JackOSSDriver::Write buffer balancing %ld frames", fBufferBalance);
1159 jack_log("JackOSSDriver::Write recording sync %ld frames %ld us ago", fOSSReadOffset, start - fOSSReadSync);
1160 jack_log("JackOSSDriver::Write playback sync %ld frames %ld us ago", fOSSWriteOffset, start - fOSSWriteSync);
1162 // How many samples to skip or prepend due to underrun and balancing.
1163 long long write_length = progress - overdue;
1164 if (write_length <= 0) {
1165 skip += fOutputBufferSize;
1166 fOSSWriteOffset += progress;
1167 } else if (write_length < fEngineControl->fBufferSize) {
1168 skip += (fEngineControl->fBufferSize - write_length) * fOutSampleSize * fPlaybackChannels;
1169 fOSSWriteOffset += overdue;
1170 } else if (write_length > fEngineControl->fBufferSize) {
1171 jack_nframes_t fill = write_length - fEngineControl->fBufferSize;
1172 WriteSilence(fill);
1176 #ifdef JACK_MONITOR
1177 gCycleTable.fTable[gCycleCount].fBeforeWriteConvert = GetMicroSeconds();
1178 #endif
1180 memset(fOutputBuffer, 0, fOutputBufferSize);
1181 for (int i = 0; i < fPlaybackChannels; i++) {
1182 if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) > 0) {
1183 CopyAndConvertOut(fOutputBuffer, GetOutputBuffer(i), fEngineControl->fBufferSize, i, fPlaybackChannels, fOutSampleSize * 8);
1187 #ifdef JACK_MONITOR
1188 gCycleTable.fTable[gCycleCount].fBeforeWrite = GetMicroSeconds();
1189 #endif
1191 // Try multiple times in case of short writes.
1192 ssize_t count = skip;
1193 for (int i = 0; i < 3 && count < fOutputBufferSize; ++i) {
1194 ssize_t ret = ::write(fOutFD, ((char*)fOutputBuffer) + count, fOutputBufferSize - count);
1195 if (ret < 0) {
1196 jack_error("JackOSSDriver::Write error = %s", strerror(errno));
1197 return -1;
1199 count += ret;
1202 fOSSWriteOffset += ((count - skip) / (fOutSampleSize * fPlaybackChannels));
1204 jack_time_t duration = GetMicroSeconds() - start;
1205 if (duration > 1000) {
1206 // Blocking write() may indicate sample loss in OSS - force resync.
1207 jack_log("JackOSSDriver::Write long write duration of %ld us", duration);
1208 fForceSync = true;
1211 #ifdef JACK_MONITOR
1212 if (count > 0 && count != (int)fOutputBufferSize)
1213 jack_log("JackOSSDriver::Write count = %ld", (count - skip) / (fOutSampleSize * fPlaybackChannels));
1214 gCycleTable.fTable[gCycleCount].fAfterWrite = GetMicroSeconds();
1215 gCycleCount = (gCycleCount == CYCLE_POINTS - 1) ? gCycleCount: gCycleCount + 1;
1216 #endif
1218 // Check and clear OSS errors.
1219 audio_errinfo ei_out;
1220 if (ioctl(fOutFD, SNDCTL_DSP_GETERROR, &ei_out) == 0) {
1222 // Not reliable for underrun detection, virtual_oss does not implement it.
1223 if (ei_out.play_underruns > 0) {
1224 jack_error("JackOSSDriver::Write %d underrun events", ei_out.play_underruns);
1227 if (ei_out.play_errorcount > 0 && ei_out.play_lasterror != 0) {
1228 jack_error("%d OSS play event(s), last=%05d:%d",ei_out.play_errorcount, ei_out.play_lasterror, ei_out.play_errorparm);
1232 if (count < (int)fOutputBufferSize) {
1233 jack_error("JackOSSDriver::Write incomplete write of %ld bytes", count - skip);
1234 return -1;
1237 return 0;
1240 int JackOSSDriver::SetBufferSize(jack_nframes_t buffer_size)
1242 CloseAux();
1244 // Additional latency introduced by the OSS buffer, depends on buffer size.
1245 fCaptureLatency = fExtraCaptureLatency;
1246 fPlaybackLatency = fExtraPlaybackLatency + fNperiods * buffer_size;
1248 JackAudioDriver::SetBufferSize(buffer_size); // Generic change, never fails
1249 return OpenAux();
1252 } // end of namespace
1254 #ifdef __cplusplus
1255 extern "C"
1257 #endif
1259 SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor()
1261 jack_driver_desc_t * desc;
1262 jack_driver_desc_filler_t filler;
1263 jack_driver_param_value_t value;
1265 desc = jack_driver_descriptor_construct("oss", JackDriverMaster, "OSS API based audio backend", &filler);
1267 value.ui = OSS_DRIVER_DEF_FS;
1268 jack_driver_descriptor_add_parameter(desc, &filler, "rate", 'r', JackDriverParamUInt, &value, NULL, "Sample rate", NULL);
1270 value.ui = OSS_DRIVER_DEF_BLKSIZE;
1271 jack_driver_descriptor_add_parameter(desc, &filler, "period", 'p', JackDriverParamUInt, &value, NULL, "Frames per period", NULL);
1273 value.ui = OSS_DRIVER_DEF_NPERIODS;
1274 jack_driver_descriptor_add_parameter(desc, &filler, "nperiods", 'n', JackDriverParamUInt, &value, NULL, "Number of periods to prefill output buffer", NULL);
1276 value.i = OSS_DRIVER_DEF_BITS;
1277 jack_driver_descriptor_add_parameter(desc, &filler, "wordlength", 'w', JackDriverParamInt, &value, NULL, "Word length", NULL);
1279 value.ui = OSS_DRIVER_DEF_INS;
1280 jack_driver_descriptor_add_parameter(desc, &filler, "inchannels", 'i', JackDriverParamUInt, &value, NULL, "Capture channels", NULL);
1282 value.ui = OSS_DRIVER_DEF_OUTS;
1283 jack_driver_descriptor_add_parameter(desc, &filler, "outchannels", 'o', JackDriverParamUInt, &value, NULL, "Playback channels", NULL);
1285 value.i = false;
1286 jack_driver_descriptor_add_parameter(desc, &filler, "excl", 'e', JackDriverParamBool, &value, NULL, "Exclusive and direct device access", NULL);
1288 strcpy(value.str, OSS_DRIVER_DEF_DEV);
1289 jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamString, &value, NULL, "Input device", NULL);
1290 jack_driver_descriptor_add_parameter(desc, &filler, "playback", 'P', JackDriverParamString, &value, NULL, "Output device", NULL);
1291 jack_driver_descriptor_add_parameter(desc, &filler, "device", 'd', JackDriverParamString, &value, NULL, "OSS device name", NULL);
1293 value.i = false;
1294 jack_driver_descriptor_add_parameter(desc, &filler, "ignorehwbuf", 'b', JackDriverParamBool, &value, NULL, "Ignore hardware period size", NULL);
1296 value.ui = 0;
1297 jack_driver_descriptor_add_parameter(desc, &filler, "input-latency", 'I', JackDriverParamUInt, &value, NULL, "Extra input latency", NULL);
1298 jack_driver_descriptor_add_parameter(desc, &filler, "output-latency", 'O', JackDriverParamUInt, &value, NULL, "Extra output latency", NULL);
1300 return desc;
1303 SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params)
1305 int bits = OSS_DRIVER_DEF_BITS;
1306 jack_nframes_t srate = OSS_DRIVER_DEF_FS;
1307 jack_nframes_t frames_per_interrupt = OSS_DRIVER_DEF_BLKSIZE;
1308 const char* capture_pcm_name = OSS_DRIVER_DEF_DEV;
1309 const char* playback_pcm_name = OSS_DRIVER_DEF_DEV;
1310 bool capture = false;
1311 bool playback = false;
1312 int chan_in = 0;
1313 int chan_out = 0;
1314 bool monitor = false;
1315 bool excl = false;
1316 unsigned int nperiods = OSS_DRIVER_DEF_NPERIODS;
1317 const JSList *node;
1318 const jack_driver_param_t *param;
1319 bool ignorehwbuf = false;
1320 jack_nframes_t systemic_input_latency = 0;
1321 jack_nframes_t systemic_output_latency = 0;
1323 for (node = params; node; node = jack_slist_next(node)) {
1325 param = (const jack_driver_param_t *)node->data;
1327 switch (param->character) {
1329 case 'r':
1330 srate = param->value.ui;
1331 break;
1333 case 'p':
1334 frames_per_interrupt = (unsigned int)param->value.ui;
1335 break;
1337 case 'n':
1338 nperiods = (unsigned int)param->value.ui;
1339 break;
1341 case 'w':
1342 bits = param->value.i;
1343 break;
1345 case 'i':
1346 chan_in = (int)param->value.ui;
1347 break;
1349 case 'o':
1350 chan_out = (int)param->value.ui;
1351 break;
1353 case 'C':
1354 capture = true;
1355 if (strcmp(param->value.str, "none") != 0) {
1356 capture_pcm_name = param->value.str;
1358 break;
1360 case 'P':
1361 playback = true;
1362 if (strcmp(param->value.str, "none") != 0) {
1363 playback_pcm_name = param->value.str;
1365 break;
1367 case 'd':
1368 playback_pcm_name = param->value.str;
1369 capture_pcm_name = param->value.str;
1370 break;
1372 case 'b':
1373 ignorehwbuf = true;
1374 break;
1376 case 'e':
1377 excl = true;
1378 break;
1380 case 'I':
1381 systemic_input_latency = param->value.ui;
1382 break;
1384 case 'O':
1385 systemic_output_latency = param->value.ui;
1386 break;
1390 // duplex is the default
1391 if (!capture && !playback) {
1392 capture = true;
1393 playback = true;
1396 Jack::JackOSSDriver* oss_driver = new Jack::JackOSSDriver("system", "oss", engine, table);
1397 Jack::JackDriverClientInterface* threaded_driver = new Jack::JackThreadedDriver(oss_driver);
1399 // Special open for OSS driver...
1400 if (oss_driver->Open(frames_per_interrupt, nperiods, srate, capture, playback, chan_in, chan_out,
1401 excl, monitor, capture_pcm_name, playback_pcm_name, systemic_input_latency, systemic_output_latency, bits, ignorehwbuf) == 0) {
1402 return threaded_driver;
1403 } else {
1404 delete threaded_driver; // Delete the decorated driver
1405 return NULL;
1409 #ifdef __cplusplus
1411 #endif