Jack2: JackBoomerDriver.cpp Source File

Jack2 1.9.12

JackBoomerDriver.cpp
1 /*
2 Copyright (C) 2009 Grame
3
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.
8
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.
13
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.
17
18 */
19
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"
27 #include "JackTime.h"
28 #include "JackShmMem.h"
29 #include "JackGlobals.h"
30 #include "memops.h"
31
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>
38
39 using namespace std;
40
41 namespace Jack
42 {
43
44 #ifdef JACK_MONITOR
45
46 #define CYCLE_POINTS 500000
47
48 struct OSSCycle {
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;
55 };
56
57 struct OSSCycleTable {
58 jack_time_t fBeforeFirstWrite;
59 jack_time_t fAfterFirstWrite;
60 OSSCycle fTable[CYCLE_POINTS];
61 };
62
63 OSSCycleTable gCycleTable;
64 int gCycleReadCount = 0;
65 int gCycleWriteCount = 0;
66
67 #endif
68
69 inline int int2pow2(int x) { int r = 0; while ((1 << r) < x) r++; return r; }
70
71 static inline void CopyAndConvertIn(jack_sample_t *dst, void *src, size_t nframes, int channel, int byte_skip, int bits)
72 {
73 switch (bits) {
74
75 case 16: {
76 signed short *s16src = (signed short*)src;
77 s16src += channel;
78 sample_move_dS_s16(dst, (char*)s16src, nframes, byte_skip);
79 break;
80 }
81 case 24: {
82 signed int *s32src = (signed int*)src;
83 s32src += channel;
84 sample_move_dS_s24(dst, (char*)s32src, nframes, byte_skip);
85 break;
86 }
87 case 32: {
88 signed int *s32src = (signed int*)src;
89 s32src += channel;
90 sample_move_dS_s32u24(dst, (char*)s32src, nframes, byte_skip);
91 break;
92 }
93 }
94 }
95
96 static inline void CopyAndConvertOut(void *dst, jack_sample_t *src, size_t nframes, int channel, int byte_skip, int bits)
97 {
98 switch (bits) {
99
100 case 16: {
101 signed short *s16dst = (signed short*)dst;
102 s16dst += channel;
103 sample_move_d16_sS((char*)s16dst, src, nframes, byte_skip, NULL); // No dithering for now...
104 break;
105 }
106 case 24: {
107 signed int *s32dst = (signed int*)dst;
108 s32dst += channel;
109 sample_move_d24_sS((char*)s32dst, src, nframes, byte_skip, NULL);
110 break;
111 }
112 case 32: {
113 signed int *s32dst = (signed int*)dst;
114 s32dst += channel;
115 sample_move_d32u24_sS((char*)s32dst, src, nframes, byte_skip, NULL);
116 break;
117 }
118 }
119 }
120
121 void JackBoomerDriver::SetSampleFormat()
122 {
123 switch (fBits) {
124
125 case 24: /* native-endian LSB aligned 24-bits in 32-bits integer */
126 fSampleFormat = AFMT_S24_NE;
127 fSampleSize = 4;
128 break;
129 case 32: /* native-endian 32-bit integer */
130 fSampleFormat = AFMT_S32_NE;
131 fSampleSize = 4;
132 break;
133 case 16: /* native-endian 16-bit integer */
134 default:
135 fSampleFormat = AFMT_S16_NE;
136 fSampleSize = 2;
137 break;
138 }
139 }
140
141 void JackBoomerDriver::DisplayDeviceInfo()
142 {
143 audio_buf_info info;
144 oss_audioinfo ai_in, ai_out;
145 memset(&info, 0, sizeof(audio_buf_info));
146 int cap = 0;
147
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);
151
152 if (fRWMode & kWrite) {
153
154 oss_sysinfo si;
155 if (ioctl(fOutFD, OSS_SYSINFO, &si) == -1) {
156 jack_error("JackBoomerDriver::DisplayDeviceInfo OSS_SYSINFO failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
157 } else {
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);
164 }
165
166 jack_info("Output capabilities - %d channels : ", fPlaybackChannels);
167 jack_info("Output block size = %d", fOutputBufferSize);
168
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);
171 } else {
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;
175 }
176
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);
179 } else {
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");
188 }
189 }
190
191 if (fRWMode & kRead) {
192
193 oss_sysinfo si;
194 if (ioctl(fInFD, OSS_SYSINFO, &si) == -1) {
195 jack_error("JackBoomerDriver::DisplayDeviceInfo OSS_SYSINFO failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
196 } else {
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);
203 }
204
205 jack_info("Input capabilities - %d channels : ", fCaptureChannels);
206 jack_info("Input block size = %d", fInputBufferSize);
207
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);
210 } else {
211 jack_info("input space info: fragments = %d, fragstotal = %d, fragsize = %d, bytes = %d",
212 info.fragments, info.fragstotal, info.fragsize, info.bytes);
213 }
214
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);
217 } else {
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");
226 }
227 }
228
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!");
231 }
232 }
233
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)
243 {
244 sem_init(&fReadSema, 0, 0);
245 sem_init(&fWriteSema, 0, 0);
246 }
247
248 JackBoomerDriver::~JackBoomerDriver()
249 {
250 sem_destroy(&fReadSema);
251 sem_destroy(&fWriteSema);
252 }
253
254 int JackBoomerDriver::OpenInput()
255 {
256 int flags = 0;
257 int gFragFormat;
258 int cur_capture_channels;
259 int cur_sample_format;
260 jack_nframes_t cur_sample_rate;
261
262 if (fCaptureChannels == 0)
263 fCaptureChannels = 2;
264
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);
267 return -1;
268 }
269
270 jack_log("JackBoomerDriver::OpenInput input fInFD = %d", fInFD);
271
272 if (fExcl) {
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);
275 goto error;
276 }
277 }
278
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);
282 goto error;
283 }
284
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);
288 goto error;
289 }
290 if (cur_sample_format != fSampleFormat) {
291 jack_info("JackBoomerDriver::OpenInput driver forced the sample format %ld", fSampleFormat);
292 }
293
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);
297 goto error;
298 }
299 if (cur_capture_channels != fCaptureChannels) {
300 jack_info("JackBoomerDriver::OpenInput driver forced the number of capture channels %ld", fCaptureChannels);
301 }
302
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);
306 goto error;
307 }
308 if (cur_sample_rate != fEngineControl->fSampleRate) {
309 jack_info("JackBoomerDriver::OpenInput driver forced the sample rate %ld", fEngineControl->fSampleRate);
310 }
311
312 // Just set the read size to the value we want...
313 fInputBufferSize = fEngineControl->fBufferSize * fSampleSize * fCaptureChannels;
314
315 fInputBuffer = (void*)calloc(fInputBufferSize, 1);
316 assert(fInputBuffer);
317 return 0;
318
319 error:
320 ::close(fInFD);
321 return -1;
322 }
323
324 int JackBoomerDriver::OpenOutput()
325 {
326 int flags = 0;
327 int gFragFormat;
328 int cur_sample_format;
329 int cur_playback_channels;
330 jack_nframes_t cur_sample_rate;
331
332 if (fPlaybackChannels == 0)
333 fPlaybackChannels = 2;
334
335 if ((fOutFD = open(fPlaybackDriverName, O_WRONLY | ((fExcl) ? O_EXCL : 0))) < 0) {
336 jack_error("JackBoomerDriver::OpenOutput failed to open device : %s@%i, errno = %d", __FILE__, __LINE__, errno);
337 return -1;
338 }
339
340 jack_log("JackBoomerDriver::OpenOutput output fOutFD = %d", fOutFD);
341
342 if (fExcl) {
343 if (ioctl(fOutFD, SNDCTL_DSP_COOKEDMODE, &flags) == -1) {
344 jack_error("JackBoomerDriver::OpenOutput failed to set cooked mode : %s@%i, errno = %d", __FILE__, __LINE__, errno);
345 goto error;
346 }
347 }
348
349 gFragFormat = (2 << 16) + int2pow2(fEngineControl->fBufferSize * fSampleSize * fPlaybackChannels);
350 if (ioctl(fOutFD, SNDCTL_DSP_SETFRAGMENT, &gFragFormat) == -1) {
351 jack_error("JackBoomerDriver::OpenOutput failed to set fragments : %s@%i, errno = %d", __FILE__, __LINE__, errno);
352 goto error;
353 }
354
355 cur_sample_format = fSampleFormat;
356 if (ioctl(fOutFD, SNDCTL_DSP_SETFMT, &fSampleFormat) == -1) {
357 jack_error("JackBoomerDriver::OpenOutput failed to set format : %s@%i, errno = %d", __FILE__, __LINE__, errno);
358 goto error;
359 }
360 if (cur_sample_format != fSampleFormat) {
361 jack_info("JackBoomerDriver::OpenOutput driver forced the sample format %ld", fSampleFormat);
362 }
363
364 cur_playback_channels = fPlaybackChannels;
365 if (ioctl(fOutFD, SNDCTL_DSP_CHANNELS, &fPlaybackChannels) == -1) {
366 jack_error("JackBoomerDriver::OpenOutput failed to set channels : %s@%i, errno = %d", __FILE__, __LINE__, errno);
367 goto error;
368 }
369 if (cur_playback_channels != fPlaybackChannels) {
370 jack_info("JackBoomerDriver::OpenOutput driver forced the number of playback channels %ld", fPlaybackChannels);
371 }
372
373 cur_sample_rate = fEngineControl->fSampleRate;
374 if (ioctl(fOutFD, SNDCTL_DSP_SPEED, &fEngineControl->fSampleRate) == -1) {
375 jack_error("JackBoomerDriver::OpenOutput failed to set sample rate : %s@%i, errno = %d", __FILE__, __LINE__, errno);
376 goto error;
377 }
378 if (cur_sample_rate != fEngineControl->fSampleRate) {
379 jack_info("JackBoomerDriver::OpenInput driver forced the sample rate %ld", fEngineControl->fSampleRate);
380 }
381
382 // Just set the write size to the value we want...
383 fOutputBufferSize = fEngineControl->fBufferSize * fSampleSize * fPlaybackChannels;
384
385 fOutputBuffer = (void*)calloc(fOutputBufferSize, 1);
386 assert(fOutputBuffer);
387 return 0;
388
389 error:
390 ::close(fOutFD);
391 return -1;
392 }
393
394 int JackBoomerDriver::Open(jack_nframes_t nframes,
395 int user_nperiods,
396 jack_nframes_t samplerate,
397 bool capturing,
398 bool playing,
399 int inchannels,
400 int outchannels,
401 bool excl,
402 bool monitor,
403 const char* capture_driver_uid,
404 const char* playback_driver_uid,
405 jack_nframes_t capture_latency,
406 jack_nframes_t playback_latency,
407 int bits, bool syncio)
408 {
409 // Generic JackAudioDriver Open
410 if (JackAudioDriver::Open(nframes, samplerate, capturing, playing, inchannels, outchannels, monitor,
411 capture_driver_uid, playback_driver_uid, capture_latency, playback_latency) != 0) {
412 return -1;
413 } else {
414
415 if (!fEngineControl->fSyncMode) {
416 jack_error("Cannot run in asynchronous mode, use the -S parameter for jackd");
417 return -1;
418 }
419
420 fRWMode |= ((capturing) ? kRead : 0);
421 fRWMode |= ((playing) ? kWrite : 0);
422 fBits = bits;
423 fExcl = excl;
424 fNperiods = (user_nperiods == 0) ? 1 : user_nperiods ;
425 fSyncIO = syncio;
426
427 #ifdef JACK_MONITOR
428 // Force memory page in
429 memset(&gCycleTable, 0, sizeof(gCycleTable));
430 #endif
431
432 if (OpenAux() < 0) {
433 Close();
434 return -1;
435 } else {
436 return 0;
437 }
438 }
439 }
440
441 int JackBoomerDriver::Close()
442 {
443 #ifdef JACK_MONITOR
444 FILE* file = fopen("OSSProfiling.log", "w");
445
446 if (file) {
447 jack_info("Writing OSS driver timing data....");
448 for (int i = 1; i < std::min(gCycleReadCount, gCycleWriteCount); i++) {
449 int d1 = gCycleTable.fTable[i].fAfterRead - gCycleTable.fTable[i].fBeforeRead;
450 int d2 = gCycleTable.fTable[i].fAfterReadConvert - gCycleTable.fTable[i].fAfterRead;
451 int d3 = gCycleTable.fTable[i].fAfterWrite - gCycleTable.fTable[i].fBeforeWrite;
452 int d4 = gCycleTable.fTable[i].fBeforeWrite - gCycleTable.fTable[i].fBeforeWriteConvert;
453 fprintf(file, "%d \t %d \t %d \t %d \t \n", d1, d2, d3, d4);
454 }
455 fclose(file);
456 } else {
457 jack_error("JackBoomerDriver::Close : cannot open OSSProfiling.log file");
458 }
459
460 file = fopen("TimingOSS.plot", "w");
461
462 if (file == NULL) {
463 jack_error("JackBoomerDriver::Close cannot open TimingOSS.plot file");
464 } else {
465
466 fprintf(file, "set grid\n");
467 fprintf(file, "set title \"OSS audio driver timing\"\n");
468 fprintf(file, "set xlabel \"audio cycles\"\n");
469 fprintf(file, "set ylabel \"usec\"\n");
470 fprintf(file, "plot \"OSSProfiling.log\" using 1 title \"Driver read wait\" with lines, \
471 \"OSSProfiling.log\" using 2 title \"Driver read convert duration\" with lines, \
472 \"OSSProfiling.log\" using 3 title \"Driver write wait\" with lines, \
473 \"OSSProfiling.log\" using 4 title \"Driver write convert duration\" with lines\n");
474
475 fprintf(file, "set output 'TimingOSS.pdf\n");
476 fprintf(file, "set terminal pdf\n");
477
478 fprintf(file, "set grid\n");
479 fprintf(file, "set title \"OSS audio driver timing\"\n");
480 fprintf(file, "set xlabel \"audio cycles\"\n");
481 fprintf(file, "set ylabel \"usec\"\n");
482 fprintf(file, "plot \"OSSProfiling.log\" using 1 title \"Driver read wait\" with lines, \
483 \"OSSProfiling.log\" using 2 title \"Driver read convert duration\" with lines, \
484 \"OSSProfiling.log\" using 3 title \"Driver write wait\" with lines, \
485 \"OSSProfiling.log\" using 4 title \"Driver write convert duration\" with lines\n");
486
487 fclose(file);
488 }
489 #endif
490 int res = JackAudioDriver::Close();
491 CloseAux();
492 return res;
493 }
494
495 int JackBoomerDriver::OpenAux()
496 {
497 SetSampleFormat();
498
499 if ((fRWMode & kRead) && (OpenInput() < 0)) {
500 return -1;
501 }
502
503 if ((fRWMode & kWrite) && (OpenOutput() < 0)) {
504 return -1;
505 }
506
507 DisplayDeviceInfo();
508 return 0;
509 }
510
511 void JackBoomerDriver::CloseAux()
512 {
513 if (fRWMode & kRead && fInFD >= 0) {
514 close(fInFD);
515 fInFD = -1;
516 }
517
518 if (fRWMode & kWrite && fOutFD >= 0) {
519 close(fOutFD);
520 fOutFD = -1;
521 }
522
523 if (fInputBuffer)
524 free(fInputBuffer);
525 fInputBuffer = NULL;
526
527 if (fOutputBuffer)
528 free(fOutputBuffer);
529 fOutputBuffer = NULL;
530 }
531
532 int JackBoomerDriver::Start()
533 {
534 jack_log("JackBoomerDriver::Start");
535 JackAudioDriver::Start();
536
537 // Input/output synchronisation
538 if (fInFD >= 0 && fOutFD >= 0 && fSyncIO) {
539
540 jack_log("JackBoomerDriver::Start sync input/output");
541
542 // Create and fill synch group
543 int id;
544 oss_syncgroup group;
545 group.id = 0;
546
547 group.mode = PCM_ENABLE_INPUT;
548 if (ioctl(fInFD, SNDCTL_DSP_SYNCGROUP, &group) == -1)
549 jack_error("JackBoomerDriver::Start failed to use SNDCTL_DSP_SYNCGROUP : %s@%i, errno = %d", __FILE__, __LINE__, errno);
550
551 group.mode = PCM_ENABLE_OUTPUT;
552 if (ioctl(fOutFD, SNDCTL_DSP_SYNCGROUP, &group) == -1)
553 jack_error("JackBoomerDriver::Start failed to use SNDCTL_DSP_SYNCGROUP : %s@%i, errno = %d", __FILE__, __LINE__, errno);
554
555 // Prefill output buffer : 2 fragments of silence as described in http://manuals.opensound.com/developer/synctest.c.html#LOC6
556 char* silence_buf = (char*)malloc(fFragmentSize);
557 memset(silence_buf, 0, fFragmentSize);
558
559 jack_log ("JackBoomerDriver::Start prefill size = %d", fFragmentSize);
560
561 for (int i = 0; i < 2; i++) {
562 ssize_t count = ::write(fOutFD, silence_buf, fFragmentSize);
563 if (count < (int)fFragmentSize) {
564 jack_error("JackBoomerDriver::Start error bytes written = %ld", count);
565 }
566 }
567
568 free(silence_buf);
569
570 // Start input/output in sync
571 id = group.id;
572
573 if (ioctl(fInFD, SNDCTL_DSP_SYNCSTART, &id) == -1)
574 jack_error("JackBoomerDriver::Start failed to use SNDCTL_DSP_SYNCSTART : %s@%i, errno = %d", __FILE__, __LINE__, errno);
575
576 } else if (fOutFD >= 0) {
577
578 // Maybe necessary to write an empty output buffer first time : see http://manuals.opensound.com/developer/fulldup.c.html
579 memset(fOutputBuffer, 0, fOutputBufferSize);
580
581 // Prefill ouput buffer
582 for (int i = 0; i < fNperiods; i++) {
583 ssize_t count = ::write(fOutFD, fOutputBuffer, fOutputBufferSize);
584 if (count < (int)fOutputBufferSize) {
585 jack_error("JackBoomerDriver::Start error bytes written = %ld", count);
586 }
587 }
588 }
589
590 // Start input thread only when needed
591 if (fInFD >= 0) {
592 if (fInputThread.StartSync() < 0) {
593 jack_error("Cannot start input thread");
594 return -1;
595 }
596 }
597
598 // Start output thread only when needed
599 if (fOutFD >= 0) {
600 if (fOutputThread.StartSync() < 0) {
601 jack_error("Cannot start output thread");
602 return -1;
603 }
604 }
605
606 return 0;
607 }
608
609 int JackBoomerDriver::Stop()
610 {
611 // Stop input thread only when needed
612 if (fInFD >= 0) {
613 fInputThread.Kill();
614 }
615
616 // Stop output thread only when needed
617 if (fOutFD >= 0) {
618 fOutputThread.Kill();
619 }
620
621 return 0;
622 }
623
624 bool JackBoomerDriver::JackBoomerDriverInput::Init()
625 {
626 if (fDriver->IsRealTime()) {
627 jack_log("JackBoomerDriverInput::Init IsRealTime");
628 if (fDriver->fInputThread.AcquireRealTime(GetEngineControl()->fServerPriority) < 0) {
629 jack_error("AcquireRealTime error");
630 } else {
631 set_threaded_log_function();
632 }
633 }
634
635 return true;
636 }
637
638 // TODO : better error handling
639 bool JackBoomerDriver::JackBoomerDriverInput::Execute()
640 {
641
642 #ifdef JACK_MONITOR
643 gCycleTable.fTable[gCycleReadCount].fBeforeRead = GetMicroSeconds();
644 #endif
645
646 audio_errinfo ei_in;
647 ssize_t count = ::read(fDriver->fInFD, fDriver->fInputBuffer, fDriver->fInputBufferSize);
648
649 #ifdef JACK_MONITOR
650 if (count > 0 && count != (int)fDriver->fInputBufferSize)
651 jack_log("JackBoomerDriverInput::Execute count = %ld", count / (fDriver->fSampleSize * fDriver->fCaptureChannels));
652 gCycleTable.fTable[gCycleReadCount].fAfterRead = GetMicroSeconds();
653 #endif
654
655 // XRun detection
656 if (ioctl(fDriver->fInFD, SNDCTL_DSP_GETERROR, &ei_in) == 0) {
657
658 if (ei_in.rec_overruns > 0 ) {
659 jack_error("JackBoomerDriverInput::Execute overruns");
660 jack_time_t cur_time = GetMicroSeconds();
661 fDriver->NotifyXRun(cur_time, float(cur_time - fDriver->fBeginDateUst)); // Better this value than nothing...
662 }
663
664 if (ei_in.rec_errorcount > 0 && ei_in.rec_lasterror != 0) {
665 jack_error("%d OSS rec event(s), last=%05d:%d", ei_in.rec_errorcount, ei_in.rec_lasterror, ei_in.rec_errorparm);
666 }
667 }
668
669 if (count < 0) {
670 jack_log("JackBoomerDriverInput::Execute error = %s", strerror(errno));
671 } else if (count < (int)fDriver->fInputBufferSize) {
672 jack_error("JackBoomerDriverInput::Execute error bytes read = %ld", count);
673 } else {
674
675 // Keep begin cycle time
676 fDriver->CycleTakeBeginTime();
677 for (int i = 0; i < fDriver->fCaptureChannels; i++) {
678 if (fDriver->fGraphManager->GetConnectionsNum(fDriver->fCapturePortList[i]) > 0) {
679 CopyAndConvertIn(fDriver->GetInputBuffer(i),
680 fDriver->fInputBuffer,
681 fDriver->fEngineControl->fBufferSize,
682 i,
683 fDriver->fCaptureChannels * fDriver->fSampleSize,
684 fDriver->fBits);
685 }
686 }
687
688 #ifdef JACK_MONITOR
689 gCycleTable.fTable[gCycleReadCount].fAfterReadConvert = GetMicroSeconds();
690 gCycleReadCount = (gCycleReadCount == CYCLE_POINTS - 1) ? gCycleReadCount: gCycleReadCount + 1;
691 #endif
692 }
693
694 // Duplex : sync with write thread
695 if (fDriver->fInFD >= 0 && fDriver->fOutFD >= 0) {
696 fDriver->SynchronizeRead();
697 } else {
698 // Otherwise direct process
699 fDriver->Process();
700 }
701 return true;
702 }
703
704 bool JackBoomerDriver::JackBoomerDriverOutput::Init()
705 {
706 if (fDriver->IsRealTime()) {
707 jack_log("JackBoomerDriverOutput::Init IsRealTime");
708 if (fDriver->fOutputThread.AcquireRealTime(GetEngineControl()->fServerPriority) < 0) {
709 jack_error("AcquireRealTime error");
710 } else {
711 set_threaded_log_function();
712 }
713 }
714
715 int delay;
716 if (ioctl(fDriver->fOutFD, SNDCTL_DSP_GETODELAY, &delay) == -1) {
717 jack_error("JackBoomerDriverOutput::Init error get out delay : %s@%i, errno = %d", __FILE__, __LINE__, errno);
718 }
719
720 delay /= fDriver->fSampleSize * fDriver->fPlaybackChannels;
721 jack_info("JackBoomerDriverOutput::Init output latency frames = %ld", delay);
722
723 return true;
724 }
725
726 // TODO : better error handling
727 bool JackBoomerDriver::JackBoomerDriverOutput::Execute()
728 {
729 memset(fDriver->fOutputBuffer, 0, fDriver->fOutputBufferSize);
730
731 #ifdef JACK_MONITOR
732 gCycleTable.fTable[gCycleWriteCount].fBeforeWriteConvert = GetMicroSeconds();
733 #endif
734
735 for (int i = 0; i < fDriver->fPlaybackChannels; i++) {
736 if (fDriver->fGraphManager->GetConnectionsNum(fDriver->fPlaybackPortList[i]) > 0) {
737 CopyAndConvertOut(fDriver->fOutputBuffer,
738 fDriver->GetOutputBuffer(i),
739 fDriver->fEngineControl->fBufferSize,
740 i,
741 fDriver->fPlaybackChannels * fDriver->fSampleSize,
742 fDriver->fBits);
743 }
744 }
745
746 #ifdef JACK_MONITOR
747 gCycleTable.fTable[gCycleWriteCount].fBeforeWrite = GetMicroSeconds();
748 #endif
749
750 ssize_t count = ::write(fDriver->fOutFD, fDriver->fOutputBuffer, fDriver->fOutputBufferSize);
751
752 #ifdef JACK_MONITOR
753 if (count > 0 && count != (int)fDriver->fOutputBufferSize)
754 jack_log("JackBoomerDriverOutput::Execute count = %ld", count / (fDriver->fSampleSize * fDriver->fPlaybackChannels));
755 gCycleTable.fTable[gCycleWriteCount].fAfterWrite = GetMicroSeconds();
756 gCycleWriteCount = (gCycleWriteCount == CYCLE_POINTS - 1) ? gCycleWriteCount: gCycleWriteCount + 1;
757 #endif
758
759 // XRun detection
760 audio_errinfo ei_out;
761 if (ioctl(fDriver->fOutFD, SNDCTL_DSP_GETERROR, &ei_out) == 0) {
762
763 if (ei_out.play_underruns > 0) {
764 jack_error("JackBoomerDriverOutput::Execute underruns");
765 jack_time_t cur_time = GetMicroSeconds();
766 fDriver->NotifyXRun(cur_time, float(cur_time - fDriver->fBeginDateUst)); // Better this value than nothing...
767 }
768
769 if (ei_out.play_errorcount > 0 && ei_out.play_lasterror != 0) {
770 jack_error("%d OSS play event(s), last=%05d:%d",ei_out.play_errorcount, ei_out.play_lasterror, ei_out.play_errorparm);
771 }
772 }
773
774 if (count < 0) {
775 jack_log("JackBoomerDriverOutput::Execute error = %s", strerror(errno));
776 } else if (count < (int)fDriver->fOutputBufferSize) {
777 jack_error("JackBoomerDriverOutput::Execute error bytes written = %ld", count);
778 }
779
780 // Duplex : sync with read thread
781 if (fDriver->fInFD >= 0 && fDriver->fOutFD >= 0) {
782 fDriver->SynchronizeWrite();
783 } else {
784 // Otherwise direct process
785 fDriver->CycleTakeBeginTime();
786 fDriver->Process();
787 }
788 return true;
789 }
790
791 void JackBoomerDriver::SynchronizeRead()
792 {
793 sem_wait(&fWriteSema);
794 Process();
795 sem_post(&fReadSema);
796 }
797
798 void JackBoomerDriver::SynchronizeWrite()
799 {
800 sem_post(&fWriteSema);
801 sem_wait(&fReadSema);
802 }
803
804 int JackBoomerDriver::SetBufferSize(jack_nframes_t buffer_size)
805 {
806 CloseAux();
807 JackAudioDriver::SetBufferSize(buffer_size); // Generic change, never fails
808 return OpenAux();
809 }
810
811 } // end of namespace
812
813 #ifdef __cplusplus
814 extern "C"
815 {
816 #endif
817
818 SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor()
819 {
820 jack_driver_desc_t * desc;
821 jack_driver_desc_filler_t filler;
822 jack_driver_param_value_t value;
823
824 desc = jack_driver_descriptor_construct("boomer", JackDriverMaster, "Boomer/OSS API based audio backend", &filler);
825
826 value.ui = OSS_DRIVER_DEF_FS;
827 jack_driver_descriptor_add_parameter(desc, &filler, "rate", 'r', JackDriverParamUInt, &value, NULL, "Sample rate", NULL);
828
829 value.ui = OSS_DRIVER_DEF_BLKSIZE;
830 jack_driver_descriptor_add_parameter(desc, &filler, "period", 'p', JackDriverParamUInt, &value, NULL, "Frames per period", NULL);
831
832 value.ui = OSS_DRIVER_DEF_NPERIODS;
833 jack_driver_descriptor_add_parameter(desc, &filler, "nperiods", 'n', JackDriverParamUInt, &value, NULL, "Number of periods to prefill output buffer", NULL);
834
835 value.i = OSS_DRIVER_DEF_BITS;
836 jack_driver_descriptor_add_parameter(desc, &filler, "wordlength", 'w', JackDriverParamInt, &value, NULL, "Word length", NULL);
837
838 value.ui = OSS_DRIVER_DEF_INS;
839 jack_driver_descriptor_add_parameter(desc, &filler, "inchannels", 'i', JackDriverParamUInt, &value, NULL, "Capture channels", NULL);
840
841 value.ui = OSS_DRIVER_DEF_OUTS;
842 jack_driver_descriptor_add_parameter(desc, &filler, "outchannels", 'o', JackDriverParamUInt, &value, NULL, "Playback channels", NULL);
843
844 value.i = false;
845 jack_driver_descriptor_add_parameter(desc, &filler, "excl", 'e', JackDriverParamBool, &value, NULL, "Exclusif (O_EXCL) access mode", NULL);
846
847 strcpy(value.str, OSS_DRIVER_DEF_DEV);
848 jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamString, &value, NULL, "Input device", NULL);
849 jack_driver_descriptor_add_parameter(desc, &filler, "playback", 'P', JackDriverParamString, &value, NULL, "Output device", NULL);
850 jack_driver_descriptor_add_parameter(desc, &filler, "device", 'd', JackDriverParamString, &value, NULL, "OSS device name", NULL);
851
852 value.ui = 0;
853 jack_driver_descriptor_add_parameter(desc, &filler, "input-latency", 'I', JackDriverParamUInt, &value, NULL, "Extra input latency", NULL);
854 jack_driver_descriptor_add_parameter(desc, &filler, "output-latency", 'O', JackDriverParamUInt, &value, NULL, "Extra output latency", NULL);
855
856 value.i = false;
857 jack_driver_descriptor_add_parameter(desc, &filler, "sync-io", 'S', JackDriverParamBool, &value, NULL, "In duplex mode, synchronize input and output", NULL);
858
859 return desc;
860 }
861
862 SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params)
863 {
864 int bits = OSS_DRIVER_DEF_BITS;
865 jack_nframes_t srate = OSS_DRIVER_DEF_FS;
866 jack_nframes_t frames_per_interrupt = OSS_DRIVER_DEF_BLKSIZE;
867 const char* capture_pcm_name = OSS_DRIVER_DEF_DEV;
868 const char* playback_pcm_name = OSS_DRIVER_DEF_DEV;
869 bool capture = false;
870 bool playback = false;
871 int chan_in = 0;
872 int chan_out = 0;
873 bool monitor = false;
874 bool excl = false;
875 bool syncio = false;
876 unsigned int nperiods = OSS_DRIVER_DEF_NPERIODS;
877 const JSList *node;
878 const jack_driver_param_t *param;
879 jack_nframes_t systemic_input_latency = 0;
880 jack_nframes_t systemic_output_latency = 0;
881
882 for (node = params; node; node = jack_slist_next(node)) {
883
884 param = (const jack_driver_param_t *)node->data;
885
886 switch (param->character) {
887
888 case 'r':
889 srate = param->value.ui;
890 break;
891
892 case 'p':
893 frames_per_interrupt = (unsigned int)param->value.ui;
894 break;
895
896 case 'n':
897 nperiods = (unsigned int)param->value.ui;
898 break;
899
900 case 'w':
901 bits = param->value.i;
902 break;
903
904 case 'i':
905 chan_in = (int)param->value.ui;
906 break;
907
908 case 'o':
909 chan_out = (int)param->value.ui;
910 break;
911
912 case 'C':
913 capture = true;
914 if (strcmp(param->value.str, "none") != 0) {
915 capture_pcm_name = param->value.str;
916 }
917 break;
918
919 case 'P':
920 playback = true;
921 if (strcmp(param->value.str, "none") != 0) {
922 playback_pcm_name = param->value.str;
923 }
924 break;
925
926 case 'd':
927 playback_pcm_name = param->value.str;
928 capture_pcm_name = param->value.str;
929 break;
930
931 case 'e':
932 excl = true;
933 break;
934
935 case 'I':
936 systemic_input_latency = param->value.ui;
937 break;
938
939 case 'O':
940 systemic_output_latency = param->value.ui;
941 break;
942
943 case 'S':
944 syncio = true;
945 break;
946 }
947 }
948
949 // duplex is the default
950 if (!capture && !playback) {
951 capture = true;
952 playback = true;
953 }
954
955 Jack::JackBoomerDriver* boomer_driver = new Jack::JackBoomerDriver("system", "boomer", engine, table);
956
957 // Special open for Boomer driver...
958 if (boomer_driver->Open(frames_per_interrupt, nperiods, srate, capture, playback, chan_in, chan_out, excl,
959 monitor, capture_pcm_name, playback_pcm_name, systemic_input_latency, systemic_output_latency, bits, syncio) == 0) {
960 return boomer_driver;
961 } else {
962 delete boomer_driver; // Delete the driver
963 return NULL;
964 }
965 }
966
967 #ifdef __cplusplus
968 }
969 #endif
Jack::JackLockedEngine
Locked Engine, access to methods is serialized using a mutex.
Definition: JackLockedEngine.h:78
jack_driver_param_value_t
Definition: driver_interface.h:64
Jack::JackPosixSemaphore
Inter process synchronization using POSIX semaphore.
Definition: JackPosixSemaphore.h:36
jack_error
SERVER_EXPORT void jack_error(const char *fmt,...)
Definition: JackError.cpp:92
jack_info
SERVER_EXPORT void jack_info(const char *fmt,...)
Definition: JackError.cpp:100
Jack::JackBoomerDriver
The Boomer driver.
Definition: JackBoomerDriver.h:45
jack_driver_desc_filler_t
Definition: driver_interface.h:142
Jack::JackDriverClientInterface
The base interface for drivers clients.
Definition: JackDriver.h:114
jack_driver_param_t
Definition: driver_interface.h:125
_JSList
Definition: linux/alsa/jslist.h:35
jack_log
SERVER_EXPORT void jack_log(const char *fmt,...)
Definition: JackError.cpp:108
jack_driver_desc_t
Definition: driver_interface.h:132