Jack2  1.9.9
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;
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
Inter process synchronization using using Mach semaphore.
Locked Engine, access to methods is serialized using a mutex.
SERVER_EXPORT void jack_error(const char *fmt,...)
Definition: JackError.cpp:91
SERVER_EXPORT void jack_info(const char *fmt,...)
Definition: JackError.cpp:99
The Boomer driver.
The base interface for drivers clients.
Definition: JackDriver.h:122
SERVER_EXPORT void jack_log(const char *fmt,...)
Definition: JackError.cpp:107