2 Copyright (C) 2009 Grame
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include "driver_interface.h"
21 #include "JackThreadedDriver.h"
22 #include "JackDriverLoader.h"
23 #include "JackBoomerDriver.h"
24 #include "JackEngineControl.h"
25 #include "JackGraphManager.h"
26 #include "JackError.h"
28 #include "JackShmMem.h"
29 #include "JackGlobals.h"
32 #include <sys/ioctl.h>
33 #include <sys/soundcard.h>
46 #define CYCLE_POINTS 500000
49 jack_time_t fBeforeRead
;
50 jack_time_t fAfterRead
;
51 jack_time_t fAfterReadConvert
;
52 jack_time_t fBeforeWrite
;
53 jack_time_t fAfterWrite
;
54 jack_time_t fBeforeWriteConvert
;
57 struct OSSCycleTable
{
58 jack_time_t fBeforeFirstWrite
;
59 jack_time_t fAfterFirstWrite
;
60 OSSCycle fTable
[CYCLE_POINTS
];
63 OSSCycleTable gCycleTable
;
64 int gCycleReadCount
= 0;
65 int gCycleWriteCount
= 0;
69 inline int int2pow2(int x
) { int r
= 0; while ((1 << r
) < x
) r
++; return r
; }
71 static inline void CopyAndConvertIn(jack_sample_t
*dst
, void *src
, size_t nframes
, int channel
, int byte_skip
, int bits
)
76 signed short *s16src
= (signed short*)src
;
78 sample_move_dS_s16(dst
, (char*)s16src
, nframes
, byte_skip
);
82 signed int *s32src
= (signed int*)src
;
84 sample_move_dS_s24(dst
, (char*)s32src
, nframes
, byte_skip
);
88 signed int *s32src
= (signed int*)src
;
90 sample_move_dS_s32u24(dst
, (char*)s32src
, nframes
, byte_skip
);
96 static inline void CopyAndConvertOut(void *dst
, jack_sample_t
*src
, size_t nframes
, int channel
, int byte_skip
, int bits
)
101 signed short *s16dst
= (signed short*)dst
;
103 sample_move_d16_sS((char*)s16dst
, src
, nframes
, byte_skip
, NULL
); // No dithering for now...
107 signed int *s32dst
= (signed int*)dst
;
109 sample_move_d24_sS((char*)s32dst
, src
, nframes
, byte_skip
, NULL
);
113 signed int *s32dst
= (signed int*)dst
;
115 sample_move_d32u24_sS((char*)s32dst
, src
, nframes
, byte_skip
, NULL
);
121 void JackBoomerDriver::SetSampleFormat()
125 case 24: /* native-endian LSB aligned 24-bits in 32-bits integer */
126 fSampleFormat
= AFMT_S24_NE
;
129 case 32: /* native-endian 32-bit integer */
130 fSampleFormat
= AFMT_S32_NE
;
133 case 16: /* native-endian 16-bit integer */
135 fSampleFormat
= AFMT_S16_NE
;
141 void JackBoomerDriver::DisplayDeviceInfo()
144 oss_audioinfo ai_in
, ai_out
;
145 memset(&info
, 0, sizeof(audio_buf_info
));
148 // Duplex cards : http://manuals.opensound.com/developer/full_duplex.html
149 jack_info("Audio Interface Description :");
150 jack_info("Sampling Frequency : %d, Sample Format : %d, Mode : %d", fEngineControl
->fSampleRate
, fSampleFormat
, fRWMode
);
152 if (fRWMode
& kWrite
) {
155 if (ioctl(fOutFD
, OSS_SYSINFO
, &si
) == -1) {
156 jack_error("JackBoomerDriver::DisplayDeviceInfo OSS_SYSINFO failed : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
158 jack_info("OSS product %s", si
.product
);
159 jack_info("OSS version %s", si
.version
);
160 jack_info("OSS version num %d", si
.versionnum
);
161 jack_info("OSS numaudios %d", si
.numaudios
);
162 jack_info("OSS numaudioengines %d", si
.numaudioengines
);
163 jack_info("OSS numcards %d", si
.numcards
);
166 jack_info("Output capabilities - %d channels : ", fPlaybackChannels
);
167 jack_info("Output block size = %d", fOutputBufferSize
);
169 if (ioctl(fOutFD
, SNDCTL_DSP_GETOSPACE
, &info
) == -1) {
170 jack_error("JackBoomerDriver::DisplayDeviceInfo SNDCTL_DSP_GETOSPACE failed : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
172 jack_info("output space info: fragments = %d, fragstotal = %d, fragsize = %d, bytes = %d",
173 info
.fragments
, info
.fragstotal
, info
.fragsize
, info
.bytes
);
174 fFragmentSize
= info
.fragsize
;
177 if (ioctl(fOutFD
, SNDCTL_DSP_GETCAPS
, &cap
) == -1) {
178 jack_error("JackBoomerDriver::DisplayDeviceInfo SNDCTL_DSP_GETCAPS failed : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
180 if (cap
& DSP_CAP_DUPLEX
) jack_info(" DSP_CAP_DUPLEX");
181 if (cap
& DSP_CAP_REALTIME
) jack_info(" DSP_CAP_REALTIME");
182 if (cap
& DSP_CAP_BATCH
) jack_info(" DSP_CAP_BATCH");
183 if (cap
& DSP_CAP_COPROC
) jack_info(" DSP_CAP_COPROC");
184 if (cap
& DSP_CAP_TRIGGER
) jack_info(" DSP_CAP_TRIGGER");
185 if (cap
& DSP_CAP_MMAP
) jack_info(" DSP_CAP_MMAP");
186 if (cap
& DSP_CAP_MULTI
) jack_info(" DSP_CAP_MULTI");
187 if (cap
& DSP_CAP_BIND
) jack_info(" DSP_CAP_BIND");
191 if (fRWMode
& kRead
) {
194 if (ioctl(fInFD
, OSS_SYSINFO
, &si
) == -1) {
195 jack_error("JackBoomerDriver::DisplayDeviceInfo OSS_SYSINFO failed : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
197 jack_info("OSS product %s", si
.product
);
198 jack_info("OSS version %s", si
.version
);
199 jack_info("OSS version num %d", si
.versionnum
);
200 jack_info("OSS numaudios %d", si
.numaudios
);
201 jack_info("OSS numaudioengines %d", si
.numaudioengines
);
202 jack_info("OSS numcards %d", si
.numcards
);
205 jack_info("Input capabilities - %d channels : ", fCaptureChannels
);
206 jack_info("Input block size = %d", fInputBufferSize
);
208 if (ioctl(fInFD
, SNDCTL_DSP_GETISPACE
, &info
) == -1) {
209 jack_error("JackBoomerDriver::DisplayDeviceInfo SNDCTL_DSP_GETOSPACE failed : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
211 jack_info("input space info: fragments = %d, fragstotal = %d, fragsize = %d, bytes = %d",
212 info
.fragments
, info
.fragstotal
, info
.fragsize
, info
.bytes
);
215 if (ioctl(fInFD
, SNDCTL_DSP_GETCAPS
, &cap
) == -1) {
216 jack_error("JackBoomerDriver::DisplayDeviceInfo SNDCTL_DSP_GETCAPS failed : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
218 if (cap
& DSP_CAP_DUPLEX
) jack_info(" DSP_CAP_DUPLEX");
219 if (cap
& DSP_CAP_REALTIME
) jack_info(" DSP_CAP_REALTIME");
220 if (cap
& DSP_CAP_BATCH
) jack_info(" DSP_CAP_BATCH");
221 if (cap
& DSP_CAP_COPROC
) jack_info(" DSP_CAP_COPROC");
222 if (cap
& DSP_CAP_TRIGGER
) jack_info(" DSP_CAP_TRIGGER");
223 if (cap
& DSP_CAP_MMAP
) jack_info(" DSP_CAP_MMAP");
224 if (cap
& DSP_CAP_MULTI
) jack_info(" DSP_CAP_MULTI");
225 if (cap
& DSP_CAP_BIND
) jack_info(" DSP_CAP_BIND");
229 if (ai_in
.rate_source
!= ai_out
.rate_source
) {
230 jack_info("Warning : input and output are not necessarily driven by the same clock!");
234 JackBoomerDriver::JackBoomerDriver(const char* name
, const char* alias
, JackLockedEngine
* engine
, JackSynchro
* table
)
235 : JackAudioDriver(name
, alias
, engine
, table
),
236 fInFD(-1), fOutFD(-1), fBits(0),
237 fSampleFormat(0), fNperiods(0), fSampleSize(0), fFragmentSize(0),
238 fRWMode(0), fExcl(false), fSyncIO(false),
239 fInputBufferSize(0), fOutputBufferSize(0),
240 fInputBuffer(NULL
), fOutputBuffer(NULL
),
241 fInputThread(&fInputHandler
), fOutputThread(&fOutputHandler
),
242 fInputHandler(this), fOutputHandler(this)
245 sem_init(&fReadSema
, 0, 0);
246 sem_init(&fWriteSema
, 0, 0);
249 JackBoomerDriver::~JackBoomerDriver()
251 sem_destroy(&fReadSema
);
252 sem_destroy(&fWriteSema
);
255 int JackBoomerDriver::OpenInput()
259 int cur_capture_channels
;
260 int cur_sample_format
;
261 jack_nframes_t cur_sample_rate
;
263 if (fCaptureChannels
== 0) fCaptureChannels
= 2;
265 if ((fInFD
= open(fCaptureDriverName
, O_RDONLY
| ((fExcl
) ? O_EXCL
: 0))) < 0) {
266 jack_error("JackBoomerDriver::OpenInput failed to open device : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
270 jack_log("JackBoomerDriver::OpenInput input fInFD = %d", fInFD
);
273 if (ioctl(fInFD
, SNDCTL_DSP_COOKEDMODE
, &flags
) == -1) {
274 jack_error("JackBoomerDriver::OpenInput failed to set cooked mode : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
279 gFragFormat
= (2 << 16) + int2pow2(fEngineControl
->fBufferSize
* fSampleSize
* fCaptureChannels
);
280 if (ioctl(fInFD
, SNDCTL_DSP_SETFRAGMENT
, &gFragFormat
) == -1) {
281 jack_error("JackBoomerDriver::OpenInput failed to set fragments : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
285 cur_sample_format
= fSampleFormat
;
286 if (ioctl(fInFD
, SNDCTL_DSP_SETFMT
, &fSampleFormat
) == -1) {
287 jack_error("JackBoomerDriver::OpenInput failed to set format : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
290 if (cur_sample_format
!= fSampleFormat
) {
291 jack_info("JackBoomerDriver::OpenInput driver forced the sample format %ld", fSampleFormat
);
294 cur_capture_channels
= fCaptureChannels
;
295 if (ioctl(fInFD
, SNDCTL_DSP_CHANNELS
, &fCaptureChannels
) == -1) {
296 jack_error("JackBoomerDriver::OpenInput failed to set channels : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
299 if (cur_capture_channels
!= fCaptureChannels
) {
300 jack_info("JackBoomerDriver::OpenInput driver forced the number of capture channels %ld", fCaptureChannels
);
303 cur_sample_rate
= fEngineControl
->fSampleRate
;
304 if (ioctl(fInFD
, SNDCTL_DSP_SPEED
, &fEngineControl
->fSampleRate
) == -1) {
305 jack_error("JackBoomerDriver::OpenInput failed to set sample rate : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
308 if (cur_sample_rate
!= fEngineControl
->fSampleRate
) {
309 jack_info("JackBoomerDriver::OpenInput driver forced the sample rate %ld", fEngineControl
->fSampleRate
);
312 // Just set the read size to the value we want...
313 fInputBufferSize
= fEngineControl
->fBufferSize
* fSampleSize
* fCaptureChannels
;
315 fInputBuffer
= (void*)calloc(fInputBufferSize
, 1);
316 assert(fInputBuffer
);
324 int JackBoomerDriver::OpenOutput()
328 int cur_sample_format
;
329 int cur_playback_channels
;
330 jack_nframes_t cur_sample_rate
;
332 if (fPlaybackChannels
== 0) fPlaybackChannels
= 2;
334 if ((fOutFD
= open(fPlaybackDriverName
, O_WRONLY
| ((fExcl
) ? O_EXCL
: 0))) < 0) {
335 jack_error("JackBoomerDriver::OpenOutput failed to open device : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
339 jack_log("JackBoomerDriver::OpenOutput output fOutFD = %d", fOutFD
);
342 if (ioctl(fOutFD
, SNDCTL_DSP_COOKEDMODE
, &flags
) == -1) {
343 jack_error("JackBoomerDriver::OpenOutput failed to set cooked mode : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
348 gFragFormat
= (2 << 16) + int2pow2(fEngineControl
->fBufferSize
* fSampleSize
* fPlaybackChannels
);
349 if (ioctl(fOutFD
, SNDCTL_DSP_SETFRAGMENT
, &gFragFormat
) == -1) {
350 jack_error("JackBoomerDriver::OpenOutput failed to set fragments : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
354 cur_sample_format
= fSampleFormat
;
355 if (ioctl(fOutFD
, SNDCTL_DSP_SETFMT
, &fSampleFormat
) == -1) {
356 jack_error("JackBoomerDriver::OpenOutput failed to set format : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
359 if (cur_sample_format
!= fSampleFormat
) {
360 jack_info("JackBoomerDriver::OpenOutput driver forced the sample format %ld", fSampleFormat
);
363 cur_playback_channels
= fPlaybackChannels
;
364 if (ioctl(fOutFD
, SNDCTL_DSP_CHANNELS
, &fPlaybackChannels
) == -1) {
365 jack_error("JackBoomerDriver::OpenOutput failed to set channels : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
368 if (cur_playback_channels
!= fPlaybackChannels
) {
369 jack_info("JackBoomerDriver::OpenOutput driver forced the number of playback channels %ld", fPlaybackChannels
);
372 cur_sample_rate
= fEngineControl
->fSampleRate
;
373 if (ioctl(fOutFD
, SNDCTL_DSP_SPEED
, &fEngineControl
->fSampleRate
) == -1) {
374 jack_error("JackBoomerDriver::OpenOutput failed to set sample rate : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
377 if (cur_sample_rate
!= fEngineControl
->fSampleRate
) {
378 jack_info("JackBoomerDriver::OpenInput driver forced the sample rate %ld", fEngineControl
->fSampleRate
);
381 // Just set the write size to the value we want...
382 fOutputBufferSize
= fEngineControl
->fBufferSize
* fSampleSize
* fPlaybackChannels
;
384 fOutputBuffer
= (void*)calloc(fOutputBufferSize
, 1);
385 assert(fOutputBuffer
);
393 int JackBoomerDriver::Open(jack_nframes_t nframes
,
395 jack_nframes_t samplerate
,
402 const char* capture_driver_uid
,
403 const char* playback_driver_uid
,
404 jack_nframes_t capture_latency
,
405 jack_nframes_t playback_latency
,
406 int bits
, bool syncio
)
408 // Generic JackAudioDriver Open
409 if (JackAudioDriver::Open(nframes
, samplerate
, capturing
, playing
, inchannels
, outchannels
, monitor
,
410 capture_driver_uid
, playback_driver_uid
, capture_latency
, playback_latency
) != 0) {
414 if (!fEngineControl
->fSyncMode
) {
415 jack_error("Cannot run in asynchronous mode, use the -S parameter for jackd");
419 fRWMode
|= ((capturing
) ? kRead
: 0);
420 fRWMode
|= ((playing
) ? kWrite
: 0);
423 fNperiods
= (user_nperiods
== 0) ? 1 : user_nperiods
;
427 // Force memory page in
428 memset(&gCycleTable
, 0, sizeof(gCycleTable
));
440 int JackBoomerDriver::Close()
443 FILE* file
= fopen("OSSProfiling.log", "w");
446 jack_info("Writing OSS driver timing data....");
447 for (int i
= 1; i
< std::min(gCycleReadCount
, gCycleWriteCount
); i
++) {
448 int d1
= gCycleTable
.fTable
[i
].fAfterRead
- gCycleTable
.fTable
[i
].fBeforeRead
;
449 int d2
= gCycleTable
.fTable
[i
].fAfterReadConvert
- gCycleTable
.fTable
[i
].fAfterRead
;
450 int d3
= gCycleTable
.fTable
[i
].fAfterWrite
- gCycleTable
.fTable
[i
].fBeforeWrite
;
451 int d4
= gCycleTable
.fTable
[i
].fBeforeWrite
- gCycleTable
.fTable
[i
].fBeforeWriteConvert
;
452 fprintf(file
, "%d \t %d \t %d \t %d \t \n", d1
, d2
, d3
, d4
);
456 jack_error("JackBoomerDriver::Close : cannot open OSSProfiling.log file");
459 file
= fopen("TimingOSS.plot", "w");
462 jack_error("JackBoomerDriver::Close cannot open TimingOSS.plot file");
465 fprintf(file
, "set grid\n");
466 fprintf(file
, "set title \"OSS audio driver timing\"\n");
467 fprintf(file
, "set xlabel \"audio cycles\"\n");
468 fprintf(file
, "set ylabel \"usec\"\n");
469 fprintf(file
, "plot \"OSSProfiling.log\" using 1 title \"Driver read wait\" with lines, \
470 \"OSSProfiling.log\" using 2 title \"Driver read convert duration\" with lines, \
471 \"OSSProfiling.log\" using 3 title \"Driver write wait\" with lines, \
472 \"OSSProfiling.log\" using 4 title \"Driver write convert duration\" with lines\n");
474 fprintf(file
, "set output 'TimingOSS.pdf\n");
475 fprintf(file
, "set terminal pdf\n");
477 fprintf(file
, "set grid\n");
478 fprintf(file
, "set title \"OSS audio driver timing\"\n");
479 fprintf(file
, "set xlabel \"audio cycles\"\n");
480 fprintf(file
, "set ylabel \"usec\"\n");
481 fprintf(file
, "plot \"OSSProfiling.log\" using 1 title \"Driver read wait\" with lines, \
482 \"OSSProfiling.log\" using 2 title \"Driver read convert duration\" with lines, \
483 \"OSSProfiling.log\" using 3 title \"Driver write wait\" with lines, \
484 \"OSSProfiling.log\" using 4 title \"Driver write convert duration\" with lines\n");
489 int res
= JackAudioDriver::Close();
494 int JackBoomerDriver::OpenAux()
498 if ((fRWMode
& kRead
) && (OpenInput() < 0)) {
502 if ((fRWMode
& kWrite
) && (OpenOutput() < 0)) {
510 void JackBoomerDriver::CloseAux()
512 if (fRWMode
& kRead
&& fInFD
>= 0) {
517 if (fRWMode
& kWrite
&& fOutFD
>= 0) {
528 fOutputBuffer
= NULL
;
531 int JackBoomerDriver::Start()
533 jack_log("JackBoomerDriver::Start");
534 JackAudioDriver::Start();
536 // Input/output synchronisation
537 if (fInFD
>= 0 && fOutFD
>= 0 && fSyncIO
) {
539 jack_log("JackBoomerDriver::Start sync input/output");
541 // Create and fill synch group
546 group
.mode
= PCM_ENABLE_INPUT
;
547 if (ioctl(fInFD
, SNDCTL_DSP_SYNCGROUP
, &group
) == -1)
548 jack_error("JackBoomerDriver::Start failed to use SNDCTL_DSP_SYNCGROUP : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
550 group
.mode
= PCM_ENABLE_OUTPUT
;
551 if (ioctl(fOutFD
, SNDCTL_DSP_SYNCGROUP
, &group
) == -1)
552 jack_error("JackBoomerDriver::Start failed to use SNDCTL_DSP_SYNCGROUP : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
554 // Prefill output buffer : 2 fragments of silence as described in http://manuals.opensound.com/developer/synctest.c.html#LOC6
555 char* silence_buf
= (char*)malloc(fFragmentSize
);
556 memset(silence_buf
, 0, fFragmentSize
);
558 jack_log ("JackBoomerDriver::Start prefill size = %d", fFragmentSize
);
560 for (int i
= 0; i
< 2; i
++) {
561 ssize_t count
= ::write(fOutFD
, silence_buf
, fFragmentSize
);
562 if (count
< (int)fFragmentSize
) {
563 jack_error("JackBoomerDriver::Start error bytes written = %ld", count
);
569 // Start input/output in sync
572 if (ioctl(fInFD
, SNDCTL_DSP_SYNCSTART
, &id
) == -1)
573 jack_error("JackBoomerDriver::Start failed to use SNDCTL_DSP_SYNCSTART : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
575 } else if (fOutFD
>= 0) {
577 // Maybe necessary to write an empty output buffer first time : see http://manuals.opensound.com/developer/fulldup.c.html
578 memset(fOutputBuffer
, 0, fOutputBufferSize
);
580 // Prefill ouput buffer
581 for (int i
= 0; i
< fNperiods
; i
++) {
582 ssize_t count
= ::write(fOutFD
, fOutputBuffer
, fOutputBufferSize
);
583 if (count
< (int)fOutputBufferSize
) {
584 jack_error("JackBoomerDriver::Start error bytes written = %ld", count
);
589 // Start input thread only when needed
591 if (fInputThread
.StartSync() < 0) {
592 jack_error("Cannot start input thread");
597 // Start output thread only when needed
599 if (fOutputThread
.StartSync() < 0) {
600 jack_error("Cannot start output thread");
608 int JackBoomerDriver::Stop()
610 // Stop input thread only when needed
615 // Stop output thread only when needed
617 fOutputThread
.Kill();
623 bool JackBoomerDriver::JackBoomerDriverInput::Init()
625 if (fDriver
->IsRealTime()) {
626 jack_log("JackBoomerDriverInput::Init IsRealTime");
627 if (fDriver
->fInputThread
.AcquireRealTime(GetEngineControl()->fServerPriority
) < 0) {
628 jack_error("AcquireRealTime error");
630 set_threaded_log_function();
637 bool JackBoomerDriver::JackBoomerDriverInput::Execute()
641 gCycleTable
.fTable
[gCycleReadCount
].fBeforeRead
= GetMicroSeconds();
645 ssize_t count
= ::read(fDriver
->fInFD
, fDriver
->fInputBuffer
, fDriver
->fInputBufferSize
);
648 if (count
> 0 && count
!= (int)fDriver
->fInputBufferSize
)
649 jack_log("JackBoomerDriverInput::Execute count = %ld", count
/ (fDriver
->fSampleSize
* fDriver
->fCaptureChannels
));
650 gCycleTable
.fTable
[gCycleReadCount
].fAfterRead
= GetMicroSeconds();
654 if (ioctl(fDriver
->fInFD
, SNDCTL_DSP_GETERROR
, &ei_in
) == 0) {
656 if (ei_in
.rec_overruns
> 0 ) {
657 jack_error("JackBoomerDriverInput::Execute overruns");
658 jack_time_t cur_time
= GetMicroSeconds();
659 fDriver
->NotifyXRun(cur_time
, float(cur_time
- fDriver
->fBeginDateUst
)); // Better this value than nothing...
662 if (ei_in
.rec_errorcount
> 0 && ei_in
.rec_lasterror
!= 0) {
663 jack_error("%d OSS rec event(s), last=%05d:%d", ei_in
.rec_errorcount
, ei_in
.rec_lasterror
, ei_in
.rec_errorparm
);
668 jack_log("JackBoomerDriverInput::Execute error = %s", strerror(errno
));
669 } else if (count
< (int)fDriver
->fInputBufferSize
) {
670 jack_error("JackBoomerDriverInput::Execute error bytes read = %ld", count
);
673 // Keep begin cycle time
674 fDriver
->CycleTakeBeginTime();
675 for (int i
= 0; i
< fDriver
->fCaptureChannels
; i
++) {
676 if (fDriver
->fGraphManager
->GetConnectionsNum(fDriver
->fCapturePortList
[i
]) > 0) {
677 CopyAndConvertIn(fDriver
->GetInputBuffer(i
),
678 fDriver
->fInputBuffer
,
679 fDriver
->fEngineControl
->fBufferSize
,
681 fDriver
->fCaptureChannels
* fDriver
->fSampleSize
,
687 gCycleTable
.fTable
[gCycleReadCount
].fAfterReadConvert
= GetMicroSeconds();
688 gCycleReadCount
= (gCycleReadCount
== CYCLE_POINTS
- 1) ? gCycleReadCount
: gCycleReadCount
+ 1;
692 // Duplex : sync with write thread
693 if (fDriver
->fInFD
>= 0 && fDriver
->fOutFD
>= 0) {
694 fDriver
->SynchronizeRead();
696 // Otherwise direct process
702 bool JackBoomerDriver::JackBoomerDriverOutput::Init()
704 if (fDriver
->IsRealTime()) {
705 jack_log("JackBoomerDriverOutput::Init IsRealTime");
706 if (fDriver
->fOutputThread
.AcquireRealTime(GetEngineControl()->fServerPriority
) < 0) {
707 jack_error("AcquireRealTime error");
709 set_threaded_log_function();
714 if (ioctl(fDriver
->fOutFD
, SNDCTL_DSP_GETODELAY
, &delay
) == -1) {
715 jack_error("JackBoomerDriverOutput::Init error get out delay : %s@%i, errno = %d", __FILE__
, __LINE__
, errno
);
718 delay
/= fDriver
->fSampleSize
* fDriver
->fPlaybackChannels
;
719 jack_info("JackBoomerDriverOutput::Init output latency frames = %ld", delay
);
724 bool JackBoomerDriver::JackBoomerDriverOutput::Execute()
726 memset(fDriver
->fOutputBuffer
, 0, fDriver
->fOutputBufferSize
);
729 gCycleTable
.fTable
[gCycleWriteCount
].fBeforeWriteConvert
= GetMicroSeconds();
732 for (int i
= 0; i
< fDriver
->fPlaybackChannels
; i
++) {
733 if (fDriver
->fGraphManager
->GetConnectionsNum(fDriver
->fPlaybackPortList
[i
]) > 0) {
734 CopyAndConvertOut(fDriver
->fOutputBuffer
,
735 fDriver
->GetOutputBuffer(i
),
736 fDriver
->fEngineControl
->fBufferSize
,
738 fDriver
->fPlaybackChannels
* fDriver
->fSampleSize
,
744 gCycleTable
.fTable
[gCycleWriteCount
].fBeforeWrite
= GetMicroSeconds();
747 ssize_t count
= ::write(fDriver
->fOutFD
, fDriver
->fOutputBuffer
, fDriver
->fOutputBufferSize
);
750 if (count
> 0 && count
!= (int)fDriver
->fOutputBufferSize
)
751 jack_log("JackBoomerDriverOutput::Execute count = %ld", count
/ (fDriver
->fSampleSize
* fDriver
->fPlaybackChannels
));
752 gCycleTable
.fTable
[gCycleWriteCount
].fAfterWrite
= GetMicroSeconds();
753 gCycleWriteCount
= (gCycleWriteCount
== CYCLE_POINTS
- 1) ? gCycleWriteCount
: gCycleWriteCount
+ 1;
757 audio_errinfo ei_out
;
758 if (ioctl(fDriver
->fOutFD
, SNDCTL_DSP_GETERROR
, &ei_out
) == 0) {
760 if (ei_out
.play_underruns
> 0) {
761 jack_error("JackBoomerDriverOutput::Execute underruns");
762 jack_time_t cur_time
= GetMicroSeconds();
763 fDriver
->NotifyXRun(cur_time
, float(cur_time
- fDriver
->fBeginDateUst
)); // Better this value than nothing...
766 if (ei_out
.play_errorcount
> 0 && ei_out
.play_lasterror
!= 0) {
767 jack_error("%d OSS play event(s), last=%05d:%d",ei_out
.play_errorcount
, ei_out
.play_lasterror
, ei_out
.play_errorparm
);
772 jack_log("JackBoomerDriverOutput::Execute error = %s", strerror(errno
));
773 } else if (count
< (int)fDriver
->fOutputBufferSize
) {
774 jack_error("JackBoomerDriverOutput::Execute error bytes written = %ld", count
);
777 // Duplex : sync with read thread
778 if (fDriver
->fInFD
>= 0 && fDriver
->fOutFD
>= 0) {
779 fDriver
->SynchronizeWrite();
781 // Otherwise direct process
782 fDriver
->CycleTakeBeginTime();
788 void JackBoomerDriver::SynchronizeRead()
790 sem_wait(&fWriteSema
);
792 sem_post(&fReadSema
);
795 void JackBoomerDriver::SynchronizeWrite()
797 sem_post(&fWriteSema
);
798 sem_wait(&fReadSema
);
801 int JackBoomerDriver::SetBufferSize(jack_nframes_t buffer_size
)
804 JackAudioDriver::SetBufferSize(buffer_size
); // never fails
809 } // end of namespace
816 SERVER_EXPORT jack_driver_desc_t
* driver_get_descriptor()
818 jack_driver_desc_t
*desc
;
820 desc
= (jack_driver_desc_t
*)calloc(1, sizeof(jack_driver_desc_t
));
822 strcpy(desc
->name
, "boomer"); // size MUST be less then JACK_DRIVER_NAME_MAX + 1
823 strcpy(desc
->desc
, "Boomer/OSS API based audio backend"); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1
825 desc
->nparams
= OSS_DRIVER_N_PARAMS
;
826 desc
->params
= (jack_driver_param_desc_t
*)calloc(desc
->nparams
, sizeof(jack_driver_param_desc_t
));
829 strcpy(desc
->params
[i
].name
, "rate");
830 desc
->params
[i
].character
= 'r';
831 desc
->params
[i
].type
= JackDriverParamUInt
;
832 desc
->params
[i
].value
.ui
= OSS_DRIVER_DEF_FS
;
833 strcpy(desc
->params
[i
].short_desc
, "Sample rate");
834 strcpy(desc
->params
[i
].long_desc
, desc
->params
[i
].short_desc
);
837 strcpy(desc
->params
[i
].name
, "period");
838 desc
->params
[i
].character
= 'p';
839 desc
->params
[i
].type
= JackDriverParamUInt
;
840 desc
->params
[i
].value
.ui
= OSS_DRIVER_DEF_BLKSIZE
;
841 strcpy(desc
->params
[i
].short_desc
, "Frames per period");
842 strcpy(desc
->params
[i
].long_desc
, desc
->params
[i
].short_desc
);
845 strcpy(desc
->params
[i
].name
, "nperiods");
846 desc
->params
[i
].character
= 'n';
847 desc
->params
[i
].type
= JackDriverParamUInt
;
848 desc
->params
[i
].value
.ui
= OSS_DRIVER_DEF_NPERIODS
;
849 strcpy(desc
->params
[i
].short_desc
, "Number of periods to prefill output buffer");
850 strcpy(desc
->params
[i
].long_desc
, desc
->params
[i
].short_desc
);
853 strcpy(desc
->params
[i
].name
, "wordlength");
854 desc
->params
[i
].character
= 'w';
855 desc
->params
[i
].type
= JackDriverParamInt
;
856 desc
->params
[i
].value
.i
= OSS_DRIVER_DEF_BITS
;
857 strcpy(desc
->params
[i
].short_desc
, "Word length");
858 strcpy(desc
->params
[i
].long_desc
, desc
->params
[i
].short_desc
);
861 strcpy(desc
->params
[i
].name
, "inchannels");
862 desc
->params
[i
].character
= 'i';
863 desc
->params
[i
].type
= JackDriverParamUInt
;
864 desc
->params
[i
].value
.ui
= OSS_DRIVER_DEF_INS
;
865 strcpy(desc
->params
[i
].short_desc
, "Capture channels");
866 strcpy(desc
->params
[i
].long_desc
, desc
->params
[i
].short_desc
);
869 strcpy(desc
->params
[i
].name
, "outchannels");
870 desc
->params
[i
].character
= 'o';
871 desc
->params
[i
].type
= JackDriverParamUInt
;
872 desc
->params
[i
].value
.ui
= OSS_DRIVER_DEF_OUTS
;
873 strcpy(desc
->params
[i
].short_desc
, "Playback channels");
874 strcpy(desc
->params
[i
].long_desc
, desc
->params
[i
].short_desc
);
877 strcpy(desc
->params
[i
].name
, "excl");
878 desc
->params
[i
].character
= 'e';
879 desc
->params
[i
].type
= JackDriverParamBool
;
880 desc
->params
[i
].value
.i
= false;
881 strcpy(desc
->params
[i
].short_desc
, "Exclusif (O_EXCL) access mode");
882 strcpy(desc
->params
[i
].long_desc
, desc
->params
[i
].short_desc
);
885 strcpy(desc
->params
[i
].name
, "capture");
886 desc
->params
[i
].character
= 'C';
887 desc
->params
[i
].type
= JackDriverParamString
;
888 strcpy(desc
->params
[i
].value
.str
, OSS_DRIVER_DEF_DEV
);
889 strcpy(desc
->params
[i
].short_desc
, "Input device");
890 strcpy(desc
->params
[i
].long_desc
, desc
->params
[i
].short_desc
);
893 strcpy(desc
->params
[i
].name
, "playback");
894 desc
->params
[i
].character
= 'P';
895 desc
->params
[i
].type
= JackDriverParamString
;
896 strcpy(desc
->params
[i
].value
.str
, OSS_DRIVER_DEF_DEV
);
897 strcpy(desc
->params
[i
].short_desc
, "Output device");
898 strcpy(desc
->params
[i
].long_desc
, desc
->params
[i
].short_desc
);
901 strcpy (desc
->params
[i
].name
, "device");
902 desc
->params
[i
].character
= 'd';
903 desc
->params
[i
].type
= JackDriverParamString
;
904 strcpy(desc
->params
[i
].value
.str
, OSS_DRIVER_DEF_DEV
);
905 strcpy(desc
->params
[i
].short_desc
, "OSS device name");
906 strcpy(desc
->params
[i
].long_desc
, desc
->params
[i
].short_desc
);
909 strcpy(desc
->params
[i
].name
, "input-latency");
910 desc
->params
[i
].character
= 'I';
911 desc
->params
[i
].type
= JackDriverParamUInt
;
912 desc
->params
[i
].value
.i
= 0;
913 strcpy(desc
->params
[i
].short_desc
, "Extra input latency");
914 strcpy(desc
->params
[i
].long_desc
, desc
->params
[i
].short_desc
);
917 strcpy(desc
->params
[i
].name
, "output-latency");
918 desc
->params
[i
].character
= 'O';
919 desc
->params
[i
].type
= JackDriverParamUInt
;
920 desc
->params
[i
].value
.i
= 0;
921 strcpy(desc
->params
[i
].short_desc
, "Extra output latency");
922 strcpy(desc
->params
[i
].long_desc
, desc
->params
[i
].short_desc
);
925 strcpy(desc
->params
[i
].name
, "sync-io");
926 desc
->params
[i
].character
= 'S';
927 desc
->params
[i
].type
= JackDriverParamBool
;
928 desc
->params
[i
].value
.i
= false;
929 strcpy(desc
->params
[i
].short_desc
, "In duplex mode, synchronize input and output");
930 strcpy(desc
->params
[i
].long_desc
, desc
->params
[i
].short_desc
);
935 EXPORT
Jack::JackDriverClientInterface
* driver_initialize(Jack::JackLockedEngine
* engine
, Jack::JackSynchro
* table
, const JSList
* params
)
937 int bits
= OSS_DRIVER_DEF_BITS
;
938 jack_nframes_t srate
= OSS_DRIVER_DEF_FS
;
939 jack_nframes_t frames_per_interrupt
= OSS_DRIVER_DEF_BLKSIZE
;
940 const char* capture_pcm_name
= OSS_DRIVER_DEF_DEV
;
941 const char* playback_pcm_name
= OSS_DRIVER_DEF_DEV
;
942 bool capture
= false;
943 bool playback
= false;
946 bool monitor
= false;
949 unsigned int nperiods
= OSS_DRIVER_DEF_NPERIODS
;
951 const jack_driver_param_t
*param
;
952 jack_nframes_t systemic_input_latency
= 0;
953 jack_nframes_t systemic_output_latency
= 0;
955 for (node
= params
; node
; node
= jack_slist_next(node
)) {
957 param
= (const jack_driver_param_t
*)node
->data
;
959 switch (param
->character
) {
962 srate
= param
->value
.ui
;
966 frames_per_interrupt
= (unsigned int)param
->value
.ui
;
970 nperiods
= (unsigned int)param
->value
.ui
;
974 bits
= param
->value
.i
;
978 chan_in
= (int)param
->value
.ui
;
982 chan_out
= (int)param
->value
.ui
;
987 if (strcmp(param
->value
.str
, "none") != 0) {
988 capture_pcm_name
= strdup(param
->value
.str
);
994 if (strcmp(param
->value
.str
, "none") != 0) {
995 playback_pcm_name
= strdup(param
->value
.str
);
1000 playback_pcm_name
= strdup (param
->value
.str
);
1001 capture_pcm_name
= strdup (param
->value
.str
);
1009 systemic_input_latency
= param
->value
.ui
;
1013 systemic_output_latency
= param
->value
.ui
;
1022 // duplex is the default
1023 if (!capture
&& !playback
) {
1028 Jack::JackBoomerDriver
* boomer_driver
= new Jack::JackBoomerDriver("system", "boomer", engine
, table
);
1030 // Special open for Boomer driver...
1031 if (boomer_driver
->Open(frames_per_interrupt
, nperiods
, srate
, capture
, playback
, chan_in
, chan_out
, excl
,
1032 monitor
, capture_pcm_name
, playback_pcm_name
, systemic_input_latency
, systemic_output_latency
, bits
, syncio
) == 0) {
1033 return boomer_driver
;
1035 delete boomer_driver
; // Delete the driver