Jack2  1.9.9
JackOSSDriver.cpp
1 /*
2 Copyright (C) 2003-2007 Jussi Laako <jussi@sonarnerd.net>
3 Copyright (C) 2008 Grame & RTL 2008
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 
19 */
20 
21 #include "driver_interface.h"
22 #include "JackThreadedDriver.h"
23 #include "JackDriverLoader.h"
24 #include "JackOSSDriver.h"
25 #include "JackEngineControl.h"
26 #include "JackGraphManager.h"
27 #include "JackError.h"
28 #include "JackTime.h"
29 #include "JackShmMem.h"
30 #include "memops.h"
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 gCycleCount = 0;
65 
66 #endif
67 
68 inline int int2pow2(int x) { int r = 0; while ((1 << r) < x) r++; return r; }
69 
70 static inline void CopyAndConvertIn(jack_sample_t *dst, void *src, size_t nframes, int channel, int chcount, int bits)
71 {
72  switch (bits) {
73 
74  case 16: {
75  signed short *s16src = (signed short*)src;
76  s16src += channel;
77  sample_move_dS_s16(dst, (char*)s16src, nframes, chcount<<1);
78  break;
79  }
80  case 24: {
81  signed int *s32src = (signed int*)src;
82  s32src += channel;
83  sample_move_dS_s24(dst, (char*)s32src, nframes, chcount<<2);
84  break;
85  }
86  case 32: {
87  signed int *s32src = (signed int*)src;
88  s32src += channel;
89  sample_move_dS_s32u24(dst, (char*)s32src, nframes, chcount<<2);
90  break;
91  }
92  }
93 }
94 
95 static inline void CopyAndConvertOut(void *dst, jack_sample_t *src, size_t nframes, int channel, int chcount, int bits)
96 {
97  switch (bits) {
98 
99  case 16: {
100  signed short *s16dst = (signed short*)dst;
101  s16dst += channel;
102  sample_move_d16_sS((char*)s16dst, src, nframes, chcount<<1, NULL); // No dithering for now...
103  break;
104  }
105  case 24: {
106  signed int *s32dst = (signed int*)dst;
107  s32dst += channel;
108  sample_move_d24_sS((char*)s32dst, src, nframes, chcount<<2, NULL); // No dithering for now...
109  break;
110  }
111  case 32: {
112  signed int *s32dst = (signed int*)dst;
113  s32dst += channel;
114  sample_move_d32u24_sS((char*)s32dst, src, nframes, chcount<<2, NULL);
115  break;
116  }
117  }
118 }
119 
120 void JackOSSDriver::SetSampleFormat()
121 {
122  switch (fBits) {
123 
124  case 24: /* native-endian LSB aligned 24-bits in 32-bits integer */
125  fSampleFormat = AFMT_S24_NE;
126  fSampleSize = sizeof(int);
127  break;
128  case 32: /* native-endian 32-bit integer */
129  fSampleFormat = AFMT_S32_NE;
130  fSampleSize = sizeof(int);
131  break;
132  case 16: /* native-endian 16-bit integer */
133  default:
134  fSampleFormat = AFMT_S16_NE;
135  fSampleSize = sizeof(short);
136  break;
137  }
138 }
139 
140 void JackOSSDriver::DisplayDeviceInfo()
141 {
142  audio_buf_info info;
143  oss_audioinfo ai_in, ai_out;
144  memset(&info, 0, sizeof(audio_buf_info));
145  int cap = 0;
146 
147  // Duplex cards : http://manuals.opensound.com/developer/full_duplex.html
148  jack_info("Audio Interface Description :");
149  jack_info("Sampling Frequency : %d, Sample Format : %d, Mode : %d", fEngineControl->fSampleRate, fSampleFormat, fRWMode);
150 
151  if (fRWMode & kWrite) {
152 
153  oss_sysinfo si;
154  if (ioctl(fOutFD, OSS_SYSINFO, &si) == -1) {
155  jack_error("JackOSSDriver::DisplayDeviceInfo OSS_SYSINFO failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
156  } else {
157  jack_info("OSS product %s", si.product);
158  jack_info("OSS version %s", si.version);
159  jack_info("OSS version num %d", si.versionnum);
160  jack_info("OSS numaudios %d", si.numaudios);
161  jack_info("OSS numaudioengines %d", si.numaudioengines);
162  jack_info("OSS numcards %d", si.numcards);
163  }
164 
165  jack_info("Output capabilities - %d channels : ", fPlaybackChannels);
166  jack_info("Output block size = %d", fOutputBufferSize);
167 
168  if (ioctl(fOutFD, SNDCTL_DSP_GETOSPACE, &info) == -1) {
169  jack_error("JackOSSDriver::DisplayDeviceInfo SNDCTL_DSP_GETOSPACE failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
170  } else {
171  jack_info("output space info: fragments = %d, fragstotal = %d, fragsize = %d, bytes = %d",
172  info.fragments, info.fragstotal, info.fragsize, info.bytes);
173  }
174 
175  if (ioctl(fOutFD, SNDCTL_DSP_GETCAPS, &cap) == -1) {
176  jack_error("JackOSSDriver::DisplayDeviceInfo SNDCTL_DSP_GETCAPS failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
177  } else {
178  if (cap & DSP_CAP_DUPLEX) jack_info(" DSP_CAP_DUPLEX");
179  if (cap & DSP_CAP_REALTIME) jack_info(" DSP_CAP_REALTIME");
180  if (cap & DSP_CAP_BATCH) jack_info(" DSP_CAP_BATCH");
181  if (cap & DSP_CAP_COPROC) jack_info(" DSP_CAP_COPROC");
182  if (cap & DSP_CAP_TRIGGER) jack_info(" DSP_CAP_TRIGGER");
183  if (cap & DSP_CAP_MMAP) jack_info(" DSP_CAP_MMAP");
184  if (cap & DSP_CAP_MULTI) jack_info(" DSP_CAP_MULTI");
185  if (cap & DSP_CAP_BIND) jack_info(" DSP_CAP_BIND");
186  }
187  }
188 
189  if (fRWMode & kRead) {
190 
191  oss_sysinfo si;
192  if (ioctl(fInFD, OSS_SYSINFO, &si) == -1) {
193  jack_error("JackOSSDriver::DisplayDeviceInfo OSS_SYSINFO failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
194  } else {
195  jack_info("OSS product %s", si.product);
196  jack_info("OSS version %s", si.version);
197  jack_info("OSS version num %d", si.versionnum);
198  jack_info("OSS numaudios %d", si.numaudios);
199  jack_info("OSS numaudioengines %d", si.numaudioengines);
200  jack_info("OSS numcards %d", si.numcards);
201  }
202 
203  jack_info("Input capabilities - %d channels : ", fCaptureChannels);
204  jack_info("Input block size = %d", fInputBufferSize);
205 
206  if (ioctl(fInFD, SNDCTL_DSP_GETISPACE, &info) == -1) {
207  jack_error("JackOSSDriver::DisplayDeviceInfo SNDCTL_DSP_GETOSPACE failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
208  } else {
209  jack_info("input space info: fragments = %d, fragstotal = %d, fragsize = %d, bytes = %d",
210  info.fragments, info.fragstotal, info.fragsize, info.bytes);
211  }
212 
213  if (ioctl(fInFD, SNDCTL_DSP_GETCAPS, &cap) == -1) {
214  jack_error("JackOSSDriver::DisplayDeviceInfo SNDCTL_DSP_GETCAPS failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
215  } else {
216  if (cap & DSP_CAP_DUPLEX) jack_info(" DSP_CAP_DUPLEX");
217  if (cap & DSP_CAP_REALTIME) jack_info(" DSP_CAP_REALTIME");
218  if (cap & DSP_CAP_BATCH) jack_info(" DSP_CAP_BATCH");
219  if (cap & DSP_CAP_COPROC) jack_info(" DSP_CAP_COPROC");
220  if (cap & DSP_CAP_TRIGGER) jack_info(" DSP_CAP_TRIGGER");
221  if (cap & DSP_CAP_MMAP) jack_info(" DSP_CAP_MMAP");
222  if (cap & DSP_CAP_MULTI) jack_info(" DSP_CAP_MULTI");
223  if (cap & DSP_CAP_BIND) jack_info(" DSP_CAP_BIND");
224  }
225  }
226 
227  if (ai_in.rate_source != ai_out.rate_source) {
228  jack_info("Warning : input and output are not necessarily driven by the same clock!");
229  }
230 }
231 
232 int JackOSSDriver::OpenInput()
233 {
234  int flags = 0;
235  int gFragFormat;
236  int cur_capture_channels;
237  int cur_sample_format;
238  jack_nframes_t cur_sample_rate;
239 
240  if (fCaptureChannels == 0) fCaptureChannels = 2;
241 
242  if ((fInFD = open(fCaptureDriverName, O_RDONLY | ((fExcl) ? O_EXCL : 0))) < 0) {
243  jack_error("JackOSSDriver::OpenInput failed to open device : %s@%i, errno = %d", __FILE__, __LINE__, errno);
244  return -1;
245  }
246 
247  jack_log("JackOSSDriver::OpenInput input fInFD = %d", fInFD);
248 
249  if (fExcl) {
250  if (ioctl(fInFD, SNDCTL_DSP_COOKEDMODE, &flags) == -1) {
251  jack_error("JackOSSDriver::OpenInput failed to set cooked mode : %s@%i, errno = %d", __FILE__, __LINE__, errno);
252  goto error;
253  }
254  }
255 
256  gFragFormat = (2 << 16) + int2pow2(fEngineControl->fBufferSize * fSampleSize * fCaptureChannels);
257  if (ioctl(fInFD, SNDCTL_DSP_SETFRAGMENT, &gFragFormat) == -1) {
258  jack_error("JackOSSDriver::OpenInput failed to set fragments : %s@%i, errno = %d", __FILE__, __LINE__, errno);
259  goto error;
260  }
261 
262  cur_sample_format = fSampleFormat;
263  if (ioctl(fInFD, SNDCTL_DSP_SETFMT, &fSampleFormat) == -1) {
264  jack_error("JackOSSDriver::OpenInput failed to set format : %s@%i, errno = %d", __FILE__, __LINE__, errno);
265  goto error;
266  }
267  if (cur_sample_format != fSampleFormat) {
268  jack_info("JackOSSDriver::OpenInput driver forced the sample format %ld", fSampleFormat);
269  }
270 
271  cur_capture_channels = fCaptureChannels;
272  if (ioctl(fInFD, SNDCTL_DSP_CHANNELS, &fCaptureChannels) == -1) {
273  jack_error("JackOSSDriver::OpenInput failed to set channels : %s@%i, errno = %d", __FILE__, __LINE__, errno);
274  goto error;
275  }
276  if (cur_capture_channels != fCaptureChannels) {
277  jack_info("JackOSSDriver::OpenInput driver forced the number of capture channels %ld", fCaptureChannels);
278  }
279 
280  cur_sample_rate = fEngineControl->fSampleRate;
281  if (ioctl(fInFD, SNDCTL_DSP_SPEED, &fEngineControl->fSampleRate) == -1) {
282  jack_error("JackOSSDriver::OpenInput failed to set sample rate : %s@%i, errno = %d", __FILE__, __LINE__, errno);
283  goto error;
284  }
285  if (cur_sample_rate != fEngineControl->fSampleRate) {
286  jack_info("JackOSSDriver::OpenInput driver forced the sample rate %ld", fEngineControl->fSampleRate);
287  }
288 
289  fInputBufferSize = 0;
290  if (ioctl(fInFD, SNDCTL_DSP_GETBLKSIZE, &fInputBufferSize) == -1) {
291  jack_error("JackOSSDriver::OpenInput failed to get fragments : %s@%i, errno = %d", __FILE__, __LINE__, errno);
292  goto error;
293  }
294 
295  if (fInputBufferSize != fEngineControl->fBufferSize * fSampleSize * fCaptureChannels) {
296  if (fIgnoreHW) {
297  int new_buffer_size = fInputBufferSize / (fSampleSize * fCaptureChannels);
298  jack_info("JackOSSDriver::OpenInput driver forced buffer size %ld", new_buffer_size);
299  JackAudioDriver::SetBufferSize(new_buffer_size); // never fails
300  } else {
301  jack_error("JackOSSDriver::OpenInput wanted buffer size cannot be obtained");
302  goto error;
303  }
304  }
305 
306  fInputBuffer = (void*)calloc(fInputBufferSize, 1);
307  assert(fInputBuffer);
308  return 0;
309 
310 error:
311  ::close(fInFD);
312  return -1;
313 }
314 
315 int JackOSSDriver::OpenOutput()
316 {
317  int flags = 0;
318  int gFragFormat;
319  int cur_sample_format;
320  int cur_playback_channels;
321  jack_nframes_t cur_sample_rate;
322 
323  if (fPlaybackChannels == 0) fPlaybackChannels = 2;
324 
325  if ((fOutFD = open(fPlaybackDriverName, O_WRONLY | ((fExcl) ? O_EXCL : 0))) < 0) {
326  jack_error("JackOSSDriver::OpenOutput failed to open device : %s@%i, errno = %d", __FILE__, __LINE__, errno);
327  return -1;
328  }
329 
330  jack_log("JackOSSDriver::OpenOutput output fOutFD = %d", fOutFD);
331 
332  if (fExcl) {
333  if (ioctl(fOutFD, SNDCTL_DSP_COOKEDMODE, &flags) == -1) {
334  jack_error("JackOSSDriver::OpenOutput failed to set cooked mode : %s@%i, errno = %d", __FILE__, __LINE__, errno);
335  goto error;
336  }
337  }
338 
339  gFragFormat = (2 << 16) + int2pow2(fEngineControl->fBufferSize * fSampleSize * fPlaybackChannels);
340  if (ioctl(fOutFD, SNDCTL_DSP_SETFRAGMENT, &gFragFormat) == -1) {
341  jack_error("JackOSSDriver::OpenOutput failed to set fragments : %s@%i, errno = %d", __FILE__, __LINE__, errno);
342  goto error;
343  }
344 
345  cur_sample_format = fSampleFormat;
346  if (ioctl(fOutFD, SNDCTL_DSP_SETFMT, &fSampleFormat) == -1) {
347  jack_error("JackOSSDriver::OpenOutput failed to set format : %s@%i, errno = %d", __FILE__, __LINE__, errno);
348  goto error;
349  }
350  if (cur_sample_format != fSampleFormat) {
351  jack_info("JackOSSDriver::OpenOutput driver forced the sample format %ld", fSampleFormat);
352  }
353 
354  cur_playback_channels = fPlaybackChannels;
355  if (ioctl(fOutFD, SNDCTL_DSP_CHANNELS, &fPlaybackChannels) == -1) {
356  jack_error("JackOSSDriver::OpenOutput failed to set channels : %s@%i, errno = %d", __FILE__, __LINE__, errno);
357  goto error;
358  }
359  if (cur_playback_channels != fPlaybackChannels) {
360  jack_info("JackOSSDriver::OpenOutput driver forced the number of playback channels %ld", fPlaybackChannels);
361  }
362 
363  cur_sample_rate = fEngineControl->fSampleRate;
364  if (ioctl(fOutFD, SNDCTL_DSP_SPEED, &fEngineControl->fSampleRate) == -1) {
365  jack_error("JackOSSDriver::OpenOutput failed to set sample rate : %s@%i, errno = %d", __FILE__, __LINE__, errno);
366  goto error;
367  }
368  if (cur_sample_rate != fEngineControl->fSampleRate) {
369  jack_info("JackOSSDriver::OpenInput driver forced the sample rate %ld", fEngineControl->fSampleRate);
370  }
371 
372  fOutputBufferSize = 0;
373  if (ioctl(fOutFD, SNDCTL_DSP_GETBLKSIZE, &fOutputBufferSize) == -1) {
374  jack_error("JackOSSDriver::OpenOutput failed to get fragments : %s@%i, errno = %d", __FILE__, __LINE__, errno);
375  goto error;
376  }
377 
378  if (fOutputBufferSize != fEngineControl->fBufferSize * fSampleSize * fPlaybackChannels) {
379  if (fIgnoreHW) {
380  int new_buffer_size = fOutputBufferSize / (fSampleSize * fPlaybackChannels);
381  jack_info("JackOSSDriver::OpenOutput driver forced buffer size %ld", new_buffer_size);
382  JackAudioDriver::SetBufferSize(new_buffer_size); // never fails
383  } else {
384  jack_error("JackOSSDriver::OpenInput wanted buffer size cannot be obtained");
385  goto error;
386  }
387  }
388 
389  fOutputBuffer = (void*)calloc(fOutputBufferSize, 1);
390  fFirstCycle = true;
391  assert(fOutputBuffer);
392  return 0;
393 
394 error:
395  ::close(fOutFD);
396  return -1;
397 }
398 
399 int JackOSSDriver::Open(jack_nframes_t nframes,
400  int user_nperiods,
401  jack_nframes_t samplerate,
402  bool capturing,
403  bool playing,
404  int inchannels,
405  int outchannels,
406  bool excl,
407  bool monitor,
408  const char* capture_driver_uid,
409  const char* playback_driver_uid,
410  jack_nframes_t capture_latency,
411  jack_nframes_t playback_latency,
412  int bits,
413  bool ignorehwbuf)
414 {
415  // Generic JackAudioDriver Open
416  if (JackAudioDriver::Open(nframes, samplerate, capturing, playing, inchannels, outchannels, monitor,
417  capture_driver_uid, playback_driver_uid, capture_latency, playback_latency) != 0) {
418  return -1;
419  } else {
420 
421  if (!fEngineControl->fSyncMode) {
422  jack_error("Cannot run in asynchronous mode, use the -S parameter for jackd");
423  return -1;
424  }
425 
426  fRWMode |= ((capturing) ? kRead : 0);
427  fRWMode |= ((playing) ? kWrite : 0);
428  fBits = bits;
429  fIgnoreHW = ignorehwbuf;
430  fNperiods = user_nperiods;
431  fExcl = excl;
432 
433  #ifdef JACK_MONITOR
434  // Force memory page in
435  memset(&gCycleTable, 0, sizeof(gCycleTable));
436  #endif
437 
438  if (OpenAux() < 0) {
439  Close();
440  return -1;
441  } else {
442  return 0;
443  }
444  }
445 }
446 
447 int JackOSSDriver::Close()
448 {
449  #ifdef JACK_MONITOR
450  FILE* file = fopen("OSSProfiling.log", "w");
451 
452  if (file) {
453  jack_info("Writing OSS driver timing data....");
454  for (int i = 1; i < gCycleCount; i++) {
455  int d1 = gCycleTable.fTable[i].fAfterRead - gCycleTable.fTable[i].fBeforeRead;
456  int d2 = gCycleTable.fTable[i].fAfterReadConvert - gCycleTable.fTable[i].fAfterRead;
457  int d3 = gCycleTable.fTable[i].fAfterWrite - gCycleTable.fTable[i].fBeforeWrite;
458  int d4 = gCycleTable.fTable[i].fBeforeWrite - gCycleTable.fTable[i].fBeforeWriteConvert;
459  fprintf(file, "%d \t %d \t %d \t %d \t \n", d1, d2, d3, d4);
460  }
461  fclose(file);
462  } else {
463  jack_error("JackOSSDriver::Close : cannot open OSSProfiling.log file");
464  }
465 
466  file = fopen("TimingOSS.plot", "w");
467 
468  if (file == NULL) {
469  jack_error("JackOSSDriver::Close cannot open TimingOSS.plot file");
470  } else {
471 
472  fprintf(file, "set grid\n");
473  fprintf(file, "set title \"OSS audio driver timing\"\n");
474  fprintf(file, "set xlabel \"audio cycles\"\n");
475  fprintf(file, "set ylabel \"usec\"\n");
476  fprintf(file, "plot \"OSSProfiling.log\" using 1 title \"Driver read wait\" with lines, \
477  \"OSSProfiling.log\" using 2 title \"Driver read convert duration\" with lines, \
478  \"OSSProfiling.log\" using 3 title \"Driver write wait\" with lines, \
479  \"OSSProfiling.log\" using 4 title \"Driver write convert duration\" with lines\n");
480 
481  fprintf(file, "set output 'TimingOSS.pdf\n");
482  fprintf(file, "set terminal pdf\n");
483 
484  fprintf(file, "set grid\n");
485  fprintf(file, "set title \"OSS audio driver timing\"\n");
486  fprintf(file, "set xlabel \"audio cycles\"\n");
487  fprintf(file, "set ylabel \"usec\"\n");
488  fprintf(file, "plot \"OSSProfiling.log\" using 1 title \"Driver read wait\" with lines, \
489  \"OSSProfiling.log\" using 2 title \"Driver read convert duration\" with lines, \
490  \"OSSProfiling.log\" using 3 title \"Driver write wait\" with lines, \
491  \"OSSProfiling.log\" using 4 title \"Driver write convert duration\" with lines\n");
492 
493  fclose(file);
494  }
495  #endif
496  int res = JackAudioDriver::Close();
497  CloseAux();
498  return res;
499 }
500 
501 
502 int JackOSSDriver::OpenAux()
503 {
504  SetSampleFormat();
505 
506  if ((fRWMode & kRead) && (OpenInput() < 0)) {
507  return -1;
508  }
509 
510  if ((fRWMode & kWrite) && (OpenOutput() < 0)) {
511  return -1;
512  }
513 
514  // In duplex mode, check that input and output use the same buffer size
515  /*
516 
517  10/02/09 : desactivated for now, needs more check (only needed when *same* device is used for input and output ??)
518 
519  if ((fRWMode & kRead) && (fRWMode & kWrite) && (fInputBufferSize != fOutputBufferSize)) {
520  jack_error("JackOSSDriver::OpenAux input and output buffer size are not the same!!");
521  return -1;
522  }
523  */
524 
525  DisplayDeviceInfo();
526  return 0;
527 }
528 
529 void JackOSSDriver::CloseAux()
530 {
531  if (fRWMode & kRead && fInFD > 0) {
532  close(fInFD);
533  fInFD = -1;
534  }
535 
536  if (fRWMode & kWrite && fOutFD > 0) {
537  close(fOutFD);
538  fOutFD = -1;
539  }
540 
541  if (fInputBuffer)
542  free(fInputBuffer);
543  fInputBuffer = NULL;
544 
545  if (fOutputBuffer)
546  free(fOutputBuffer);
547  fOutputBuffer = NULL;
548 }
549 
550 int JackOSSDriver::Read()
551 {
552  if (fInFD < 0) {
553  // Keep begin cycle time
554  JackDriver::CycleTakeBeginTime();
555  return 0;
556  }
557 
558  ssize_t count;
559 
560 #ifdef JACK_MONITOR
561  gCycleTable.fTable[gCycleCount].fBeforeRead = GetMicroSeconds();
562 #endif
563 
564  audio_errinfo ei_in;
565  count = ::read(fInFD, fInputBuffer, fInputBufferSize);
566 
567 #ifdef JACK_MONITOR
568  if (count > 0 && count != (int)fInputBufferSize)
569  jack_log("JackOSSDriver::Read count = %ld", count / (fSampleSize * fCaptureChannels));
570  gCycleTable.fTable[gCycleCount].fAfterRead = GetMicroSeconds();
571 #endif
572 
573  // XRun detection
574  if (ioctl(fInFD, SNDCTL_DSP_GETERROR, &ei_in) == 0) {
575 
576  if (ei_in.rec_overruns > 0 ) {
577  jack_error("JackOSSDriver::Read overruns");
578  jack_time_t cur_time = GetMicroSeconds();
579  NotifyXRun(cur_time, float(cur_time - fBeginDateUst)); // Better this value than nothing...
580  }
581 
582  if (ei_in.rec_errorcount > 0 && ei_in.rec_lasterror != 0) {
583  jack_error("%d OSS rec event(s), last=%05d:%d", ei_in.rec_errorcount, ei_in.rec_lasterror, ei_in.rec_errorparm);
584  }
585  }
586 
587  if (count < 0) {
588  jack_log("JackOSSDriver::Read error = %s", strerror(errno));
589  return -1;
590  } else if (count < (int)fInputBufferSize) {
591  jack_error("JackOSSDriver::Read error bytes read = %ld", count);
592  return -1;
593  } else {
594 
595  // Keep begin cycle time
596  JackDriver::CycleTakeBeginTime();
597  for (int i = 0; i < fCaptureChannels; i++) {
598  if (fGraphManager->GetConnectionsNum(fCapturePortList[i]) > 0) {
599  CopyAndConvertIn(GetInputBuffer(i), fInputBuffer, fEngineControl->fBufferSize, i, fCaptureChannels, fBits);
600  }
601  }
602 
603  #ifdef JACK_MONITOR
604  gCycleTable.fTable[gCycleCount].fAfterReadConvert = GetMicroSeconds();
605  #endif
606 
607  return 0;
608  }
609 }
610 
611 int JackOSSDriver::Write()
612 {
613  if (fOutFD < 0) {
614  // Keep end cycle time
615  JackDriver::CycleTakeEndTime();
616  return 0;
617  }
618 
619  ssize_t count;
620  audio_errinfo ei_out;
621 
622  // Maybe necessary to write an empty output buffer first time : see http://manuals.opensound.com/developer/fulldup.c.html
623  if (fFirstCycle) {
624 
625  fFirstCycle = false;
626  memset(fOutputBuffer, 0, fOutputBufferSize);
627 
628  // Prefill ouput buffer
629  for (int i = 0; i < fNperiods; i++) {
630  count = ::write(fOutFD, fOutputBuffer, fOutputBufferSize);
631  if (count < (int)fOutputBufferSize) {
632  jack_error("JackOSSDriver::Write error bytes written = %ld", count);
633  return -1;
634  }
635  }
636 
637  int delay;
638  if (ioctl(fOutFD, SNDCTL_DSP_GETODELAY, &delay) == -1) {
639  jack_error("JackOSSDriver::Write error get out delay : %s@%i, errno = %d", __FILE__, __LINE__, errno);
640  return -1;
641  }
642 
643  delay /= fSampleSize * fPlaybackChannels;
644  jack_info("JackOSSDriver::Write output latency frames = %ld", delay);
645  }
646 
647 #ifdef JACK_MONITOR
648  gCycleTable.fTable[gCycleCount].fBeforeWriteConvert = GetMicroSeconds();
649 #endif
650 
651  memset(fOutputBuffer, 0, fOutputBufferSize);
652  for (int i = 0; i < fPlaybackChannels; i++) {
653  if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) > 0) {
654  CopyAndConvertOut(fOutputBuffer, GetOutputBuffer(i), fEngineControl->fBufferSize, i, fPlaybackChannels, fBits);
655  }
656  }
657 
658  #ifdef JACK_MONITOR
659  gCycleTable.fTable[gCycleCount].fBeforeWrite = GetMicroSeconds();
660  #endif
661 
662  // Keep end cycle time
663  JackDriver::CycleTakeEndTime();
664  count = ::write(fOutFD, fOutputBuffer, fOutputBufferSize);
665 
666  #ifdef JACK_MONITOR
667  if (count > 0 && count != (int)fOutputBufferSize)
668  jack_log("JackOSSDriver::Write count = %ld", count / (fSampleSize * fPlaybackChannels));
669  gCycleTable.fTable[gCycleCount].fAfterWrite = GetMicroSeconds();
670  gCycleCount = (gCycleCount == CYCLE_POINTS - 1) ? gCycleCount: gCycleCount + 1;
671  #endif
672 
673  // XRun detection
674  if (ioctl(fOutFD, SNDCTL_DSP_GETERROR, &ei_out) == 0) {
675 
676  if (ei_out.play_underruns > 0) {
677  jack_error("JackOSSDriver::Write underruns");
678  jack_time_t cur_time = GetMicroSeconds();
679  NotifyXRun(cur_time, float(cur_time - fBeginDateUst)); // Better this value than nothing...
680  }
681 
682  if (ei_out.play_errorcount > 0 && ei_out.play_lasterror != 0) {
683  jack_error("%d OSS play event(s), last=%05d:%d",ei_out.play_errorcount, ei_out.play_lasterror, ei_out.play_errorparm);
684  }
685  }
686 
687  if (count < 0) {
688  jack_log("JackOSSDriver::Write error = %s", strerror(errno));
689  return -1;
690  } else if (count < (int)fOutputBufferSize) {
691  jack_error("JackOSSDriver::Write error bytes written = %ld", count);
692  return -1;
693  } else {
694  return 0;
695  }
696 }
697 
698 int JackOSSDriver::SetBufferSize(jack_nframes_t buffer_size)
699 {
700  CloseAux();
701  JackAudioDriver::SetBufferSize(buffer_size); // Generic change, never fails
702  return OpenAux();
703 }
704 
705 int JackOSSDriver::ProcessSync()
706 {
707  // Read input buffers for the current cycle
708  if (Read() < 0) {
709  jack_error("ProcessSync: read error, skip cycle");
710  return 0; // Non fatal error here, skip cycle, but continue processing...
711  }
712 
713  if (fIsMaster) {
714  ProcessGraphSync();
715  } else {
716  ResumeRefNum();
717  }
718 
719  // Write output buffers for the current cycle
720  if (Write() < 0) {
721  jack_error("JackAudioDriver::ProcessSync: write error, skip cycle");
722  return 0; // Non fatal error here, skip cycle, but continue processing...
723  }
724 
725  return 0;
726 }
727 
728 } // end of namespace
729 
730 #ifdef __cplusplus
731 extern "C"
732 {
733 #endif
734 
735 SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor()
736 {
737  jack_driver_desc_t * desc;
740 
741  desc = jack_driver_descriptor_construct("oss", JackDriverMaster, "OSS API based audio backend", &filler);
742 
743  value.ui = OSS_DRIVER_DEF_FS;
744  jack_driver_descriptor_add_parameter(desc, &filler, "rate", 'r', JackDriverParamUInt, &value, NULL, "Sample rate", NULL);
745 
746  value.ui = OSS_DRIVER_DEF_BLKSIZE;
747  jack_driver_descriptor_add_parameter(desc, &filler, "period", 'p', JackDriverParamUInt, &value, NULL, "Frames per period", NULL);
748 
749  value.ui = OSS_DRIVER_DEF_NPERIODS;
750  jack_driver_descriptor_add_parameter(desc, &filler, "nperiods", 'n', JackDriverParamUInt, &value, NULL, "Number of periods to prefill output buffer", NULL);
751 
752  value.i = OSS_DRIVER_DEF_BITS;
753  jack_driver_descriptor_add_parameter(desc, &filler, "wordlength", 'w', JackDriverParamInt, &value, NULL, "Word length", NULL);
754 
755  value.ui = OSS_DRIVER_DEF_INS;
756  jack_driver_descriptor_add_parameter(desc, &filler, "inchannels", 'i', JackDriverParamUInt, &value, NULL, "Capture channels", NULL);
757 
758  value.ui = OSS_DRIVER_DEF_OUTS;
759  jack_driver_descriptor_add_parameter(desc, &filler, "outchannels", 'o', JackDriverParamUInt, &value, NULL, "Playback channels", NULL);
760 
761  value.i = false;
762  jack_driver_descriptor_add_parameter(desc, &filler, "excl", 'e', JackDriverParamBool, &value, NULL, "Exclusif (O_EXCL) access mode", NULL);
763 
764  strcpy(value.str, OSS_DRIVER_DEF_DEV);
765  jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamString, &value, NULL, "Input device", NULL);
766  jack_driver_descriptor_add_parameter(desc, &filler, "playback", 'P', JackDriverParamString, &value, NULL, "Output device", NULL);
767  jack_driver_descriptor_add_parameter(desc, &filler, "device", 'd', JackDriverParamString, &value, NULL, "OSS device name", NULL);
768 
769  value.i = false;
770  jack_driver_descriptor_add_parameter(desc, &filler, "ignorehwbuf", 'b', JackDriverParamBool, &value, NULL, "Ignore hardware period size", NULL);
771 
772  value.ui = 0;
773  jack_driver_descriptor_add_parameter(desc, &filler, "input-latency", 'I', JackDriverParamUInt, &value, NULL, "Extra input latency", NULL);
774  jack_driver_descriptor_add_parameter(desc, &filler, "output-latency", 'O', JackDriverParamUInt, &value, NULL, "Extra output latency", NULL);
775 
776  return desc;
777 }
778 
779 SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params)
780 {
781  int bits = OSS_DRIVER_DEF_BITS;
782  jack_nframes_t srate = OSS_DRIVER_DEF_FS;
783  jack_nframes_t frames_per_interrupt = OSS_DRIVER_DEF_BLKSIZE;
784  const char* capture_pcm_name = OSS_DRIVER_DEF_DEV;
785  const char* playback_pcm_name = OSS_DRIVER_DEF_DEV;
786  bool capture = false;
787  bool playback = false;
788  int chan_in = 0;
789  int chan_out = 0;
790  bool monitor = false;
791  bool excl = false;
792  unsigned int nperiods = OSS_DRIVER_DEF_NPERIODS;
793  const JSList *node;
794  const jack_driver_param_t *param;
795  bool ignorehwbuf = false;
796  jack_nframes_t systemic_input_latency = 0;
797  jack_nframes_t systemic_output_latency = 0;
798 
799  for (node = params; node; node = jack_slist_next(node)) {
800 
801  param = (const jack_driver_param_t *)node->data;
802 
803  switch (param->character) {
804 
805  case 'r':
806  srate = param->value.ui;
807  break;
808 
809  case 'p':
810  frames_per_interrupt = (unsigned int)param->value.ui;
811  break;
812 
813  case 'n':
814  nperiods = (unsigned int)param->value.ui;
815  break;
816 
817  case 'w':
818  bits = param->value.i;
819  break;
820 
821  case 'i':
822  chan_in = (int)param->value.ui;
823  break;
824 
825  case 'o':
826  chan_out = (int)param->value.ui;
827  break;
828 
829  case 'C':
830  capture = true;
831  if (strcmp(param->value.str, "none") != 0) {
832  capture_pcm_name = param->value.str;
833  }
834  break;
835 
836  case 'P':
837  playback = true;
838  if (strcmp(param->value.str, "none") != 0) {
839  playback_pcm_name = param->value.str;
840  }
841  break;
842 
843  case 'd':
844  playback_pcm_name = param->value.str;
845  capture_pcm_name = param->value.str;
846  break;
847 
848  case 'b':
849  ignorehwbuf = true;
850  break;
851 
852  case 'e':
853  excl = true;
854  break;
855 
856  case 'I':
857  systemic_input_latency = param->value.ui;
858  break;
859 
860  case 'O':
861  systemic_output_latency = param->value.ui;
862  break;
863  }
864  }
865 
866  // duplex is the default
867  if (!capture && !playback) {
868  capture = true;
869  playback = true;
870  }
871 
872  Jack::JackOSSDriver* oss_driver = new Jack::JackOSSDriver("system", "oss", engine, table);
873  Jack::JackDriverClientInterface* threaded_driver = new Jack::JackThreadedDriver(oss_driver);
874 
875  // Special open for OSS driver...
876  if (oss_driver->Open(frames_per_interrupt, nperiods, srate, capture, playback, chan_in, chan_out,
877  excl, monitor, capture_pcm_name, playback_pcm_name, systemic_input_latency, systemic_output_latency, bits, ignorehwbuf) == 0) {
878  return threaded_driver;
879  } else {
880  delete threaded_driver; // Delete the decorated driver
881  return NULL;
882  }
883 }
884 
885 #ifdef __cplusplus
886 }
887 #endif
The base class for threaded drivers using a &quot;decorator&quot; pattern. Threaded drivers are used with block...
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 OSS driver.
Definition: JackOSSDriver.h:43
The base interface for drivers clients.
Definition: JackDriver.h:122
SERVER_EXPORT void jack_log(const char *fmt,...)
Definition: JackError.cpp:107