Jack2 1.9.8
|
00001 /* 00002 Copyright (C) 2009 Grame 00003 00004 This program is free software; you can redistribute it and/or modify 00005 it under the terms of the GNU General Public License as published by 00006 the Free Software Foundation; either version 2 of the License, or 00007 (at your option) any later version. 00008 00009 This program is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 GNU General Public License for more details. 00013 00014 You should have received a copy of the GNU General Public License 00015 along with this program; if not, write to the Free Software 00016 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00017 00018 */ 00019 00020 #include "driver_interface.h" 00021 #include "JackThreadedDriver.h" 00022 #include "JackDriverLoader.h" 00023 #include "JackBoomerDriver.h" 00024 #include "JackEngineControl.h" 00025 #include "JackGraphManager.h" 00026 #include "JackError.h" 00027 #include "JackTime.h" 00028 #include "JackShmMem.h" 00029 #include "JackGlobals.h" 00030 #include "memops.h" 00031 00032 #include <sys/ioctl.h> 00033 #include <sys/soundcard.h> 00034 #include <fcntl.h> 00035 #include <iostream> 00036 #include <assert.h> 00037 #include <stdio.h> 00038 00039 using namespace std; 00040 00041 namespace Jack 00042 { 00043 00044 #ifdef JACK_MONITOR 00045 00046 #define CYCLE_POINTS 500000 00047 00048 struct OSSCycle { 00049 jack_time_t fBeforeRead; 00050 jack_time_t fAfterRead; 00051 jack_time_t fAfterReadConvert; 00052 jack_time_t fBeforeWrite; 00053 jack_time_t fAfterWrite; 00054 jack_time_t fBeforeWriteConvert; 00055 }; 00056 00057 struct OSSCycleTable { 00058 jack_time_t fBeforeFirstWrite; 00059 jack_time_t fAfterFirstWrite; 00060 OSSCycle fTable[CYCLE_POINTS]; 00061 }; 00062 00063 OSSCycleTable gCycleTable; 00064 int gCycleReadCount = 0; 00065 int gCycleWriteCount = 0; 00066 00067 #endif 00068 00069 inline int int2pow2(int x) { int r = 0; while ((1 << r) < x) r++; return r; } 00070 00071 static inline void CopyAndConvertIn(jack_sample_t *dst, void *src, size_t nframes, int channel, int byte_skip, int bits) 00072 { 00073 switch (bits) { 00074 00075 case 16: { 00076 signed short *s16src = (signed short*)src; 00077 s16src += channel; 00078 sample_move_dS_s16(dst, (char*)s16src, nframes, byte_skip); 00079 break; 00080 } 00081 case 24: { 00082 signed int *s32src = (signed int*)src; 00083 s32src += channel; 00084 sample_move_dS_s24(dst, (char*)s32src, nframes, byte_skip); 00085 break; 00086 } 00087 case 32: { 00088 signed int *s32src = (signed int*)src; 00089 s32src += channel; 00090 sample_move_dS_s32u24(dst, (char*)s32src, nframes, byte_skip); 00091 break; 00092 } 00093 } 00094 } 00095 00096 static inline void CopyAndConvertOut(void *dst, jack_sample_t *src, size_t nframes, int channel, int byte_skip, int bits) 00097 { 00098 switch (bits) { 00099 00100 case 16: { 00101 signed short *s16dst = (signed short*)dst; 00102 s16dst += channel; 00103 sample_move_d16_sS((char*)s16dst, src, nframes, byte_skip, NULL); // No dithering for now... 00104 break; 00105 } 00106 case 24: { 00107 signed int *s32dst = (signed int*)dst; 00108 s32dst += channel; 00109 sample_move_d24_sS((char*)s32dst, src, nframes, byte_skip, NULL); 00110 break; 00111 } 00112 case 32: { 00113 signed int *s32dst = (signed int*)dst; 00114 s32dst += channel; 00115 sample_move_d32u24_sS((char*)s32dst, src, nframes, byte_skip, NULL); 00116 break; 00117 } 00118 } 00119 } 00120 00121 void JackBoomerDriver::SetSampleFormat() 00122 { 00123 switch (fBits) { 00124 00125 case 24: /* native-endian LSB aligned 24-bits in 32-bits integer */ 00126 fSampleFormat = AFMT_S24_NE; 00127 fSampleSize = 4; 00128 break; 00129 case 32: /* native-endian 32-bit integer */ 00130 fSampleFormat = AFMT_S32_NE; 00131 fSampleSize = 4; 00132 break; 00133 case 16: /* native-endian 16-bit integer */ 00134 default: 00135 fSampleFormat = AFMT_S16_NE; 00136 fSampleSize = 2; 00137 break; 00138 } 00139 } 00140 00141 void JackBoomerDriver::DisplayDeviceInfo() 00142 { 00143 audio_buf_info info; 00144 oss_audioinfo ai_in, ai_out; 00145 memset(&info, 0, sizeof(audio_buf_info)); 00146 int cap = 0; 00147 00148 // Duplex cards : http://manuals.opensound.com/developer/full_duplex.html 00149 jack_info("Audio Interface Description :"); 00150 jack_info("Sampling Frequency : %d, Sample Format : %d, Mode : %d", fEngineControl->fSampleRate, fSampleFormat, fRWMode); 00151 00152 if (fRWMode & kWrite) { 00153 00154 oss_sysinfo si; 00155 if (ioctl(fOutFD, OSS_SYSINFO, &si) == -1) { 00156 jack_error("JackBoomerDriver::DisplayDeviceInfo OSS_SYSINFO failed : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00157 } else { 00158 jack_info("OSS product %s", si.product); 00159 jack_info("OSS version %s", si.version); 00160 jack_info("OSS version num %d", si.versionnum); 00161 jack_info("OSS numaudios %d", si.numaudios); 00162 jack_info("OSS numaudioengines %d", si.numaudioengines); 00163 jack_info("OSS numcards %d", si.numcards); 00164 } 00165 00166 jack_info("Output capabilities - %d channels : ", fPlaybackChannels); 00167 jack_info("Output block size = %d", fOutputBufferSize); 00168 00169 if (ioctl(fOutFD, SNDCTL_DSP_GETOSPACE, &info) == -1) { 00170 jack_error("JackBoomerDriver::DisplayDeviceInfo SNDCTL_DSP_GETOSPACE failed : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00171 } else { 00172 jack_info("output space info: fragments = %d, fragstotal = %d, fragsize = %d, bytes = %d", 00173 info.fragments, info.fragstotal, info.fragsize, info.bytes); 00174 fFragmentSize = info.fragsize; 00175 } 00176 00177 if (ioctl(fOutFD, SNDCTL_DSP_GETCAPS, &cap) == -1) { 00178 jack_error("JackBoomerDriver::DisplayDeviceInfo SNDCTL_DSP_GETCAPS failed : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00179 } else { 00180 if (cap & DSP_CAP_DUPLEX) jack_info(" DSP_CAP_DUPLEX"); 00181 if (cap & DSP_CAP_REALTIME) jack_info(" DSP_CAP_REALTIME"); 00182 if (cap & DSP_CAP_BATCH) jack_info(" DSP_CAP_BATCH"); 00183 if (cap & DSP_CAP_COPROC) jack_info(" DSP_CAP_COPROC"); 00184 if (cap & DSP_CAP_TRIGGER) jack_info(" DSP_CAP_TRIGGER"); 00185 if (cap & DSP_CAP_MMAP) jack_info(" DSP_CAP_MMAP"); 00186 if (cap & DSP_CAP_MULTI) jack_info(" DSP_CAP_MULTI"); 00187 if (cap & DSP_CAP_BIND) jack_info(" DSP_CAP_BIND"); 00188 } 00189 } 00190 00191 if (fRWMode & kRead) { 00192 00193 oss_sysinfo si; 00194 if (ioctl(fInFD, OSS_SYSINFO, &si) == -1) { 00195 jack_error("JackBoomerDriver::DisplayDeviceInfo OSS_SYSINFO failed : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00196 } else { 00197 jack_info("OSS product %s", si.product); 00198 jack_info("OSS version %s", si.version); 00199 jack_info("OSS version num %d", si.versionnum); 00200 jack_info("OSS numaudios %d", si.numaudios); 00201 jack_info("OSS numaudioengines %d", si.numaudioengines); 00202 jack_info("OSS numcards %d", si.numcards); 00203 } 00204 00205 jack_info("Input capabilities - %d channels : ", fCaptureChannels); 00206 jack_info("Input block size = %d", fInputBufferSize); 00207 00208 if (ioctl(fInFD, SNDCTL_DSP_GETISPACE, &info) == -1) { 00209 jack_error("JackBoomerDriver::DisplayDeviceInfo SNDCTL_DSP_GETOSPACE failed : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00210 } else { 00211 jack_info("input space info: fragments = %d, fragstotal = %d, fragsize = %d, bytes = %d", 00212 info.fragments, info.fragstotal, info.fragsize, info.bytes); 00213 } 00214 00215 if (ioctl(fInFD, SNDCTL_DSP_GETCAPS, &cap) == -1) { 00216 jack_error("JackBoomerDriver::DisplayDeviceInfo SNDCTL_DSP_GETCAPS failed : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00217 } else { 00218 if (cap & DSP_CAP_DUPLEX) jack_info(" DSP_CAP_DUPLEX"); 00219 if (cap & DSP_CAP_REALTIME) jack_info(" DSP_CAP_REALTIME"); 00220 if (cap & DSP_CAP_BATCH) jack_info(" DSP_CAP_BATCH"); 00221 if (cap & DSP_CAP_COPROC) jack_info(" DSP_CAP_COPROC"); 00222 if (cap & DSP_CAP_TRIGGER) jack_info(" DSP_CAP_TRIGGER"); 00223 if (cap & DSP_CAP_MMAP) jack_info(" DSP_CAP_MMAP"); 00224 if (cap & DSP_CAP_MULTI) jack_info(" DSP_CAP_MULTI"); 00225 if (cap & DSP_CAP_BIND) jack_info(" DSP_CAP_BIND"); 00226 } 00227 } 00228 00229 if (ai_in.rate_source != ai_out.rate_source) { 00230 jack_info("Warning : input and output are not necessarily driven by the same clock!"); 00231 } 00232 } 00233 00234 JackBoomerDriver::JackBoomerDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) 00235 : JackAudioDriver(name, alias, engine, table), 00236 fInFD(-1), fOutFD(-1), fBits(0), 00237 fSampleFormat(0), fNperiods(0), fSampleSize(0), fFragmentSize(0), 00238 fRWMode(0), fExcl(false), fSyncIO(false), 00239 fInputBufferSize(0), fOutputBufferSize(0), 00240 fInputBuffer(NULL), fOutputBuffer(NULL), 00241 fInputThread(&fInputHandler), fOutputThread(&fOutputHandler), 00242 fInputHandler(this), fOutputHandler(this) 00243 { 00244 sem_init(&fReadSema, 0, 0); 00245 sem_init(&fWriteSema, 0, 0); 00246 } 00247 00248 JackBoomerDriver::~JackBoomerDriver() 00249 { 00250 sem_destroy(&fReadSema); 00251 sem_destroy(&fWriteSema); 00252 } 00253 00254 int JackBoomerDriver::OpenInput() 00255 { 00256 int flags = 0; 00257 int gFragFormat; 00258 int cur_capture_channels; 00259 int cur_sample_format; 00260 jack_nframes_t cur_sample_rate; 00261 00262 if (fCaptureChannels == 0) 00263 fCaptureChannels = 2; 00264 00265 if ((fInFD = open(fCaptureDriverName, O_RDONLY | ((fExcl) ? O_EXCL : 0))) < 0) { 00266 jack_error("JackBoomerDriver::OpenInput failed to open device : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00267 return -1; 00268 } 00269 00270 jack_log("JackBoomerDriver::OpenInput input fInFD = %d", fInFD); 00271 00272 if (fExcl) { 00273 if (ioctl(fInFD, SNDCTL_DSP_COOKEDMODE, &flags) == -1) { 00274 jack_error("JackBoomerDriver::OpenInput failed to set cooked mode : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00275 goto error; 00276 } 00277 } 00278 00279 gFragFormat = (2 << 16) + int2pow2(fEngineControl->fBufferSize * fSampleSize * fCaptureChannels); 00280 if (ioctl(fInFD, SNDCTL_DSP_SETFRAGMENT, &gFragFormat) == -1) { 00281 jack_error("JackBoomerDriver::OpenInput failed to set fragments : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00282 goto error; 00283 } 00284 00285 cur_sample_format = fSampleFormat; 00286 if (ioctl(fInFD, SNDCTL_DSP_SETFMT, &fSampleFormat) == -1) { 00287 jack_error("JackBoomerDriver::OpenInput failed to set format : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00288 goto error; 00289 } 00290 if (cur_sample_format != fSampleFormat) { 00291 jack_info("JackBoomerDriver::OpenInput driver forced the sample format %ld", fSampleFormat); 00292 } 00293 00294 cur_capture_channels = fCaptureChannels; 00295 if (ioctl(fInFD, SNDCTL_DSP_CHANNELS, &fCaptureChannels) == -1) { 00296 jack_error("JackBoomerDriver::OpenInput failed to set channels : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00297 goto error; 00298 } 00299 if (cur_capture_channels != fCaptureChannels) { 00300 jack_info("JackBoomerDriver::OpenInput driver forced the number of capture channels %ld", fCaptureChannels); 00301 } 00302 00303 cur_sample_rate = fEngineControl->fSampleRate; 00304 if (ioctl(fInFD, SNDCTL_DSP_SPEED, &fEngineControl->fSampleRate) == -1) { 00305 jack_error("JackBoomerDriver::OpenInput failed to set sample rate : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00306 goto error; 00307 } 00308 if (cur_sample_rate != fEngineControl->fSampleRate) { 00309 jack_info("JackBoomerDriver::OpenInput driver forced the sample rate %ld", fEngineControl->fSampleRate); 00310 } 00311 00312 // Just set the read size to the value we want... 00313 fInputBufferSize = fEngineControl->fBufferSize * fSampleSize * fCaptureChannels; 00314 00315 fInputBuffer = (void*)calloc(fInputBufferSize, 1); 00316 assert(fInputBuffer); 00317 return 0; 00318 00319 error: 00320 ::close(fInFD); 00321 return -1; 00322 } 00323 00324 int JackBoomerDriver::OpenOutput() 00325 { 00326 int flags = 0; 00327 int gFragFormat; 00328 int cur_sample_format; 00329 int cur_playback_channels; 00330 jack_nframes_t cur_sample_rate; 00331 00332 if (fPlaybackChannels == 0) 00333 fPlaybackChannels = 2; 00334 00335 if ((fOutFD = open(fPlaybackDriverName, O_WRONLY | ((fExcl) ? O_EXCL : 0))) < 0) { 00336 jack_error("JackBoomerDriver::OpenOutput failed to open device : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00337 return -1; 00338 } 00339 00340 jack_log("JackBoomerDriver::OpenOutput output fOutFD = %d", fOutFD); 00341 00342 if (fExcl) { 00343 if (ioctl(fOutFD, SNDCTL_DSP_COOKEDMODE, &flags) == -1) { 00344 jack_error("JackBoomerDriver::OpenOutput failed to set cooked mode : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00345 goto error; 00346 } 00347 } 00348 00349 gFragFormat = (2 << 16) + int2pow2(fEngineControl->fBufferSize * fSampleSize * fPlaybackChannels); 00350 if (ioctl(fOutFD, SNDCTL_DSP_SETFRAGMENT, &gFragFormat) == -1) { 00351 jack_error("JackBoomerDriver::OpenOutput failed to set fragments : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00352 goto error; 00353 } 00354 00355 cur_sample_format = fSampleFormat; 00356 if (ioctl(fOutFD, SNDCTL_DSP_SETFMT, &fSampleFormat) == -1) { 00357 jack_error("JackBoomerDriver::OpenOutput failed to set format : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00358 goto error; 00359 } 00360 if (cur_sample_format != fSampleFormat) { 00361 jack_info("JackBoomerDriver::OpenOutput driver forced the sample format %ld", fSampleFormat); 00362 } 00363 00364 cur_playback_channels = fPlaybackChannels; 00365 if (ioctl(fOutFD, SNDCTL_DSP_CHANNELS, &fPlaybackChannels) == -1) { 00366 jack_error("JackBoomerDriver::OpenOutput failed to set channels : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00367 goto error; 00368 } 00369 if (cur_playback_channels != fPlaybackChannels) { 00370 jack_info("JackBoomerDriver::OpenOutput driver forced the number of playback channels %ld", fPlaybackChannels); 00371 } 00372 00373 cur_sample_rate = fEngineControl->fSampleRate; 00374 if (ioctl(fOutFD, SNDCTL_DSP_SPEED, &fEngineControl->fSampleRate) == -1) { 00375 jack_error("JackBoomerDriver::OpenOutput failed to set sample rate : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00376 goto error; 00377 } 00378 if (cur_sample_rate != fEngineControl->fSampleRate) { 00379 jack_info("JackBoomerDriver::OpenInput driver forced the sample rate %ld", fEngineControl->fSampleRate); 00380 } 00381 00382 // Just set the write size to the value we want... 00383 fOutputBufferSize = fEngineControl->fBufferSize * fSampleSize * fPlaybackChannels; 00384 00385 fOutputBuffer = (void*)calloc(fOutputBufferSize, 1); 00386 assert(fOutputBuffer); 00387 return 0; 00388 00389 error: 00390 ::close(fOutFD); 00391 return -1; 00392 } 00393 00394 int JackBoomerDriver::Open(jack_nframes_t nframes, 00395 int user_nperiods, 00396 jack_nframes_t samplerate, 00397 bool capturing, 00398 bool playing, 00399 int inchannels, 00400 int outchannels, 00401 bool excl, 00402 bool monitor, 00403 const char* capture_driver_uid, 00404 const char* playback_driver_uid, 00405 jack_nframes_t capture_latency, 00406 jack_nframes_t playback_latency, 00407 int bits, bool syncio) 00408 { 00409 // Generic JackAudioDriver Open 00410 if (JackAudioDriver::Open(nframes, samplerate, capturing, playing, inchannels, outchannels, monitor, 00411 capture_driver_uid, playback_driver_uid, capture_latency, playback_latency) != 0) { 00412 return -1; 00413 } else { 00414 00415 if (!fEngineControl->fSyncMode) { 00416 jack_error("Cannot run in asynchronous mode, use the -S parameter for jackd"); 00417 return -1; 00418 } 00419 00420 fRWMode |= ((capturing) ? kRead : 0); 00421 fRWMode |= ((playing) ? kWrite : 0); 00422 fBits = bits; 00423 fExcl = excl; 00424 fNperiods = (user_nperiods == 0) ? 1 : user_nperiods ; 00425 fSyncIO = syncio; 00426 00427 #ifdef JACK_MONITOR 00428 // Force memory page in 00429 memset(&gCycleTable, 0, sizeof(gCycleTable)); 00430 #endif 00431 00432 if (OpenAux() < 0) { 00433 Close(); 00434 return -1; 00435 } else { 00436 return 0; 00437 } 00438 } 00439 } 00440 00441 int JackBoomerDriver::Close() 00442 { 00443 #ifdef JACK_MONITOR 00444 FILE* file = fopen("OSSProfiling.log", "w"); 00445 00446 if (file) { 00447 jack_info("Writing OSS driver timing data...."); 00448 for (int i = 1; i < std::min(gCycleReadCount, gCycleWriteCount); i++) { 00449 int d1 = gCycleTable.fTable[i].fAfterRead - gCycleTable.fTable[i].fBeforeRead; 00450 int d2 = gCycleTable.fTable[i].fAfterReadConvert - gCycleTable.fTable[i].fAfterRead; 00451 int d3 = gCycleTable.fTable[i].fAfterWrite - gCycleTable.fTable[i].fBeforeWrite; 00452 int d4 = gCycleTable.fTable[i].fBeforeWrite - gCycleTable.fTable[i].fBeforeWriteConvert; 00453 fprintf(file, "%d \t %d \t %d \t %d \t \n", d1, d2, d3, d4); 00454 } 00455 fclose(file); 00456 } else { 00457 jack_error("JackBoomerDriver::Close : cannot open OSSProfiling.log file"); 00458 } 00459 00460 file = fopen("TimingOSS.plot", "w"); 00461 00462 if (file == NULL) { 00463 jack_error("JackBoomerDriver::Close cannot open TimingOSS.plot file"); 00464 } else { 00465 00466 fprintf(file, "set grid\n"); 00467 fprintf(file, "set title \"OSS audio driver timing\"\n"); 00468 fprintf(file, "set xlabel \"audio cycles\"\n"); 00469 fprintf(file, "set ylabel \"usec\"\n"); 00470 fprintf(file, "plot \"OSSProfiling.log\" using 1 title \"Driver read wait\" with lines, \ 00471 \"OSSProfiling.log\" using 2 title \"Driver read convert duration\" with lines, \ 00472 \"OSSProfiling.log\" using 3 title \"Driver write wait\" with lines, \ 00473 \"OSSProfiling.log\" using 4 title \"Driver write convert duration\" with lines\n"); 00474 00475 fprintf(file, "set output 'TimingOSS.pdf\n"); 00476 fprintf(file, "set terminal pdf\n"); 00477 00478 fprintf(file, "set grid\n"); 00479 fprintf(file, "set title \"OSS audio driver timing\"\n"); 00480 fprintf(file, "set xlabel \"audio cycles\"\n"); 00481 fprintf(file, "set ylabel \"usec\"\n"); 00482 fprintf(file, "plot \"OSSProfiling.log\" using 1 title \"Driver read wait\" with lines, \ 00483 \"OSSProfiling.log\" using 2 title \"Driver read convert duration\" with lines, \ 00484 \"OSSProfiling.log\" using 3 title \"Driver write wait\" with lines, \ 00485 \"OSSProfiling.log\" using 4 title \"Driver write convert duration\" with lines\n"); 00486 00487 fclose(file); 00488 } 00489 #endif 00490 int res = JackAudioDriver::Close(); 00491 CloseAux(); 00492 return res; 00493 } 00494 00495 int JackBoomerDriver::OpenAux() 00496 { 00497 SetSampleFormat(); 00498 00499 if ((fRWMode & kRead) && (OpenInput() < 0)) { 00500 return -1; 00501 } 00502 00503 if ((fRWMode & kWrite) && (OpenOutput() < 0)) { 00504 return -1; 00505 } 00506 00507 DisplayDeviceInfo(); 00508 return 0; 00509 } 00510 00511 void JackBoomerDriver::CloseAux() 00512 { 00513 if (fRWMode & kRead && fInFD >= 0) { 00514 close(fInFD); 00515 fInFD = -1; 00516 } 00517 00518 if (fRWMode & kWrite && fOutFD >= 0) { 00519 close(fOutFD); 00520 fOutFD = -1; 00521 } 00522 00523 if (fInputBuffer) 00524 free(fInputBuffer); 00525 fInputBuffer = NULL; 00526 00527 if (fOutputBuffer) 00528 free(fOutputBuffer); 00529 fOutputBuffer = NULL; 00530 } 00531 00532 int JackBoomerDriver::Start() 00533 { 00534 jack_log("JackBoomerDriver::Start"); 00535 JackAudioDriver::Start(); 00536 00537 // Input/output synchronisation 00538 if (fInFD >= 0 && fOutFD >= 0 && fSyncIO) { 00539 00540 jack_log("JackBoomerDriver::Start sync input/output"); 00541 00542 // Create and fill synch group 00543 int id; 00544 oss_syncgroup group; 00545 group.id = 0; 00546 00547 group.mode = PCM_ENABLE_INPUT; 00548 if (ioctl(fInFD, SNDCTL_DSP_SYNCGROUP, &group) == -1) 00549 jack_error("JackBoomerDriver::Start failed to use SNDCTL_DSP_SYNCGROUP : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00550 00551 group.mode = PCM_ENABLE_OUTPUT; 00552 if (ioctl(fOutFD, SNDCTL_DSP_SYNCGROUP, &group) == -1) 00553 jack_error("JackBoomerDriver::Start failed to use SNDCTL_DSP_SYNCGROUP : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00554 00555 // Prefill output buffer : 2 fragments of silence as described in http://manuals.opensound.com/developer/synctest.c.html#LOC6 00556 char* silence_buf = (char*)malloc(fFragmentSize); 00557 memset(silence_buf, 0, fFragmentSize); 00558 00559 jack_log ("JackBoomerDriver::Start prefill size = %d", fFragmentSize); 00560 00561 for (int i = 0; i < 2; i++) { 00562 ssize_t count = ::write(fOutFD, silence_buf, fFragmentSize); 00563 if (count < (int)fFragmentSize) { 00564 jack_error("JackBoomerDriver::Start error bytes written = %ld", count); 00565 } 00566 } 00567 00568 free(silence_buf); 00569 00570 // Start input/output in sync 00571 id = group.id; 00572 00573 if (ioctl(fInFD, SNDCTL_DSP_SYNCSTART, &id) == -1) 00574 jack_error("JackBoomerDriver::Start failed to use SNDCTL_DSP_SYNCSTART : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00575 00576 } else if (fOutFD >= 0) { 00577 00578 // Maybe necessary to write an empty output buffer first time : see http://manuals.opensound.com/developer/fulldup.c.html 00579 memset(fOutputBuffer, 0, fOutputBufferSize); 00580 00581 // Prefill ouput buffer 00582 for (int i = 0; i < fNperiods; i++) { 00583 ssize_t count = ::write(fOutFD, fOutputBuffer, fOutputBufferSize); 00584 if (count < (int)fOutputBufferSize) { 00585 jack_error("JackBoomerDriver::Start error bytes written = %ld", count); 00586 } 00587 } 00588 } 00589 00590 // Start input thread only when needed 00591 if (fInFD >= 0) { 00592 if (fInputThread.StartSync() < 0) { 00593 jack_error("Cannot start input thread"); 00594 return -1; 00595 } 00596 } 00597 00598 // Start output thread only when needed 00599 if (fOutFD >= 0) { 00600 if (fOutputThread.StartSync() < 0) { 00601 jack_error("Cannot start output thread"); 00602 return -1; 00603 } 00604 } 00605 00606 return 0; 00607 } 00608 00609 int JackBoomerDriver::Stop() 00610 { 00611 // Stop input thread only when needed 00612 if (fInFD >= 0) { 00613 fInputThread.Kill(); 00614 } 00615 00616 // Stop output thread only when needed 00617 if (fOutFD >= 0) { 00618 fOutputThread.Kill(); 00619 } 00620 00621 return 0; 00622 } 00623 00624 bool JackBoomerDriver::JackBoomerDriverInput::Init() 00625 { 00626 if (fDriver->IsRealTime()) { 00627 jack_log("JackBoomerDriverInput::Init IsRealTime"); 00628 if (fDriver->fInputThread.AcquireRealTime(GetEngineControl()->fServerPriority) < 0) { 00629 jack_error("AcquireRealTime error"); 00630 } else { 00631 set_threaded_log_function(); 00632 } 00633 } 00634 00635 return true; 00636 } 00637 00638 // TODO : better error handling 00639 bool JackBoomerDriver::JackBoomerDriverInput::Execute() 00640 { 00641 00642 #ifdef JACK_MONITOR 00643 gCycleTable.fTable[gCycleReadCount].fBeforeRead = GetMicroSeconds(); 00644 #endif 00645 00646 audio_errinfo ei_in; 00647 ssize_t count = ::read(fDriver->fInFD, fDriver->fInputBuffer, fDriver->fInputBufferSize); 00648 00649 #ifdef JACK_MONITOR 00650 if (count > 0 && count != (int)fDriver->fInputBufferSize) 00651 jack_log("JackBoomerDriverInput::Execute count = %ld", count / (fDriver->fSampleSize * fDriver->fCaptureChannels)); 00652 gCycleTable.fTable[gCycleReadCount].fAfterRead = GetMicroSeconds(); 00653 #endif 00654 00655 // XRun detection 00656 if (ioctl(fDriver->fInFD, SNDCTL_DSP_GETERROR, &ei_in) == 0) { 00657 00658 if (ei_in.rec_overruns > 0 ) { 00659 jack_error("JackBoomerDriverInput::Execute overruns"); 00660 jack_time_t cur_time = GetMicroSeconds(); 00661 fDriver->NotifyXRun(cur_time, float(cur_time - fDriver->fBeginDateUst)); // Better this value than nothing... 00662 } 00663 00664 if (ei_in.rec_errorcount > 0 && ei_in.rec_lasterror != 0) { 00665 jack_error("%d OSS rec event(s), last=%05d:%d", ei_in.rec_errorcount, ei_in.rec_lasterror, ei_in.rec_errorparm); 00666 } 00667 } 00668 00669 if (count < 0) { 00670 jack_log("JackBoomerDriverInput::Execute error = %s", strerror(errno)); 00671 } else if (count < (int)fDriver->fInputBufferSize) { 00672 jack_error("JackBoomerDriverInput::Execute error bytes read = %ld", count); 00673 } else { 00674 00675 // Keep begin cycle time 00676 fDriver->CycleTakeBeginTime(); 00677 for (int i = 0; i < fDriver->fCaptureChannels; i++) { 00678 if (fDriver->fGraphManager->GetConnectionsNum(fDriver->fCapturePortList[i]) > 0) { 00679 CopyAndConvertIn(fDriver->GetInputBuffer(i), 00680 fDriver->fInputBuffer, 00681 fDriver->fEngineControl->fBufferSize, 00682 i, 00683 fDriver->fCaptureChannels * fDriver->fSampleSize, 00684 fDriver->fBits); 00685 } 00686 } 00687 00688 #ifdef JACK_MONITOR 00689 gCycleTable.fTable[gCycleReadCount].fAfterReadConvert = GetMicroSeconds(); 00690 gCycleReadCount = (gCycleReadCount == CYCLE_POINTS - 1) ? gCycleReadCount: gCycleReadCount + 1; 00691 #endif 00692 } 00693 00694 // Duplex : sync with write thread 00695 if (fDriver->fInFD >= 0 && fDriver->fOutFD >= 0) { 00696 fDriver->SynchronizeRead(); 00697 } else { 00698 // Otherwise direct process 00699 fDriver->Process(); 00700 } 00701 return true; 00702 } 00703 00704 bool JackBoomerDriver::JackBoomerDriverOutput::Init() 00705 { 00706 if (fDriver->IsRealTime()) { 00707 jack_log("JackBoomerDriverOutput::Init IsRealTime"); 00708 if (fDriver->fOutputThread.AcquireRealTime(GetEngineControl()->fServerPriority) < 0) { 00709 jack_error("AcquireRealTime error"); 00710 } else { 00711 set_threaded_log_function(); 00712 } 00713 } 00714 00715 int delay; 00716 if (ioctl(fDriver->fOutFD, SNDCTL_DSP_GETODELAY, &delay) == -1) { 00717 jack_error("JackBoomerDriverOutput::Init error get out delay : %s@%i, errno = %d", __FILE__, __LINE__, errno); 00718 } 00719 00720 delay /= fDriver->fSampleSize * fDriver->fPlaybackChannels; 00721 jack_info("JackBoomerDriverOutput::Init output latency frames = %ld", delay); 00722 00723 return true; 00724 } 00725 00726 // TODO : better error handling 00727 bool JackBoomerDriver::JackBoomerDriverOutput::Execute() 00728 { 00729 memset(fDriver->fOutputBuffer, 0, fDriver->fOutputBufferSize); 00730 00731 #ifdef JACK_MONITOR 00732 gCycleTable.fTable[gCycleWriteCount].fBeforeWriteConvert = GetMicroSeconds(); 00733 #endif 00734 00735 for (int i = 0; i < fDriver->fPlaybackChannels; i++) { 00736 if (fDriver->fGraphManager->GetConnectionsNum(fDriver->fPlaybackPortList[i]) > 0) { 00737 CopyAndConvertOut(fDriver->fOutputBuffer, 00738 fDriver->GetOutputBuffer(i), 00739 fDriver->fEngineControl->fBufferSize, 00740 i, 00741 fDriver->fPlaybackChannels * fDriver->fSampleSize, 00742 fDriver->fBits); 00743 } 00744 } 00745 00746 #ifdef JACK_MONITOR 00747 gCycleTable.fTable[gCycleWriteCount].fBeforeWrite = GetMicroSeconds(); 00748 #endif 00749 00750 ssize_t count = ::write(fDriver->fOutFD, fDriver->fOutputBuffer, fDriver->fOutputBufferSize); 00751 00752 #ifdef JACK_MONITOR 00753 if (count > 0 && count != (int)fDriver->fOutputBufferSize) 00754 jack_log("JackBoomerDriverOutput::Execute count = %ld", count / (fDriver->fSampleSize * fDriver->fPlaybackChannels)); 00755 gCycleTable.fTable[gCycleWriteCount].fAfterWrite = GetMicroSeconds(); 00756 gCycleWriteCount = (gCycleWriteCount == CYCLE_POINTS - 1) ? gCycleWriteCount: gCycleWriteCount + 1; 00757 #endif 00758 00759 // XRun detection 00760 audio_errinfo ei_out; 00761 if (ioctl(fDriver->fOutFD, SNDCTL_DSP_GETERROR, &ei_out) == 0) { 00762 00763 if (ei_out.play_underruns > 0) { 00764 jack_error("JackBoomerDriverOutput::Execute underruns"); 00765 jack_time_t cur_time = GetMicroSeconds(); 00766 fDriver->NotifyXRun(cur_time, float(cur_time - fDriver->fBeginDateUst)); // Better this value than nothing... 00767 } 00768 00769 if (ei_out.play_errorcount > 0 && ei_out.play_lasterror != 0) { 00770 jack_error("%d OSS play event(s), last=%05d:%d",ei_out.play_errorcount, ei_out.play_lasterror, ei_out.play_errorparm); 00771 } 00772 } 00773 00774 if (count < 0) { 00775 jack_log("JackBoomerDriverOutput::Execute error = %s", strerror(errno)); 00776 } else if (count < (int)fDriver->fOutputBufferSize) { 00777 jack_error("JackBoomerDriverOutput::Execute error bytes written = %ld", count); 00778 } 00779 00780 // Duplex : sync with read thread 00781 if (fDriver->fInFD >= 0 && fDriver->fOutFD >= 0) { 00782 fDriver->SynchronizeWrite(); 00783 } else { 00784 // Otherwise direct process 00785 fDriver->CycleTakeBeginTime(); 00786 fDriver->Process(); 00787 } 00788 return true; 00789 } 00790 00791 void JackBoomerDriver::SynchronizeRead() 00792 { 00793 sem_wait(&fWriteSema); 00794 Process(); 00795 sem_post(&fReadSema); 00796 } 00797 00798 void JackBoomerDriver::SynchronizeWrite() 00799 { 00800 sem_post(&fWriteSema); 00801 sem_wait(&fReadSema); 00802 } 00803 00804 int JackBoomerDriver::SetBufferSize(jack_nframes_t buffer_size) 00805 { 00806 CloseAux(); 00807 JackAudioDriver::SetBufferSize(buffer_size); // Generic change, never fails 00808 return OpenAux(); 00809 } 00810 00811 } // end of namespace 00812 00813 #ifdef __cplusplus 00814 extern "C" 00815 { 00816 #endif 00817 00818 SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor() 00819 { 00820 jack_driver_desc_t * desc; 00821 jack_driver_desc_filler_t filler; 00822 jack_driver_param_value_t value; 00823 00824 desc = jack_driver_descriptor_construct("boomer", JackDriverMaster, "Boomer/OSS API based audio backend", &filler); 00825 00826 value.ui = OSS_DRIVER_DEF_FS; 00827 jack_driver_descriptor_add_parameter(desc, &filler, "rate", 'r', JackDriverParamUInt, &value, NULL, "Sample rate", NULL); 00828 00829 value.ui = OSS_DRIVER_DEF_BLKSIZE; 00830 jack_driver_descriptor_add_parameter(desc, &filler, "period", 'p', JackDriverParamUInt, &value, NULL, "Frames per period", NULL); 00831 00832 value.ui = OSS_DRIVER_DEF_NPERIODS; 00833 jack_driver_descriptor_add_parameter(desc, &filler, "nperiods", 'n', JackDriverParamUInt, &value, NULL, "Number of periods to prefill output buffer", NULL); 00834 00835 value.i = OSS_DRIVER_DEF_BITS; 00836 jack_driver_descriptor_add_parameter(desc, &filler, "wordlength", 'w', JackDriverParamInt, &value, NULL, "Word length", NULL); 00837 00838 value.ui = OSS_DRIVER_DEF_INS; 00839 jack_driver_descriptor_add_parameter(desc, &filler, "inchannels", 'i', JackDriverParamUInt, &value, NULL, "Capture channels", NULL); 00840 00841 value.ui = OSS_DRIVER_DEF_OUTS; 00842 jack_driver_descriptor_add_parameter(desc, &filler, "outchannels", 'o', JackDriverParamUInt, &value, NULL, "Playback channels", NULL); 00843 00844 value.i = false; 00845 jack_driver_descriptor_add_parameter(desc, &filler, "excl", 'e', JackDriverParamBool, &value, NULL, "Exclusif (O_EXCL) access mode", NULL); 00846 00847 strcpy(value.str, OSS_DRIVER_DEF_DEV); 00848 jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamString, &value, NULL, "Input device", NULL); 00849 jack_driver_descriptor_add_parameter(desc, &filler, "playback", 'P', JackDriverParamString, &value, NULL, "Output device", NULL); 00850 jack_driver_descriptor_add_parameter(desc, &filler, "device", 'd', JackDriverParamString, &value, NULL, "OSS device name", NULL); 00851 00852 value.ui = 0; 00853 jack_driver_descriptor_add_parameter(desc, &filler, "input-latency", 'I', JackDriverParamUInt, &value, NULL, "Extra input latency", NULL); 00854 jack_driver_descriptor_add_parameter(desc, &filler, "output-latency", 'O', JackDriverParamUInt, &value, NULL, "Extra output latency", NULL); 00855 00856 value.i = false; 00857 jack_driver_descriptor_add_parameter(desc, &filler, "sync-io", 'S', JackDriverParamBool, &value, NULL, "In duplex mode, synchronize input and output", NULL); 00858 00859 return desc; 00860 } 00861 00862 EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) 00863 { 00864 int bits = OSS_DRIVER_DEF_BITS; 00865 jack_nframes_t srate = OSS_DRIVER_DEF_FS; 00866 jack_nframes_t frames_per_interrupt = OSS_DRIVER_DEF_BLKSIZE; 00867 const char* capture_pcm_name = OSS_DRIVER_DEF_DEV; 00868 const char* playback_pcm_name = OSS_DRIVER_DEF_DEV; 00869 bool capture = false; 00870 bool playback = false; 00871 int chan_in = 0; 00872 int chan_out = 0; 00873 bool monitor = false; 00874 bool excl = false; 00875 bool syncio = false; 00876 unsigned int nperiods = OSS_DRIVER_DEF_NPERIODS; 00877 const JSList *node; 00878 const jack_driver_param_t *param; 00879 jack_nframes_t systemic_input_latency = 0; 00880 jack_nframes_t systemic_output_latency = 0; 00881 00882 for (node = params; node; node = jack_slist_next(node)) { 00883 00884 param = (const jack_driver_param_t *)node->data; 00885 00886 switch (param->character) { 00887 00888 case 'r': 00889 srate = param->value.ui; 00890 break; 00891 00892 case 'p': 00893 frames_per_interrupt = (unsigned int)param->value.ui; 00894 break; 00895 00896 case 'n': 00897 nperiods = (unsigned int)param->value.ui; 00898 break; 00899 00900 case 'w': 00901 bits = param->value.i; 00902 break; 00903 00904 case 'i': 00905 chan_in = (int)param->value.ui; 00906 break; 00907 00908 case 'o': 00909 chan_out = (int)param->value.ui; 00910 break; 00911 00912 case 'C': 00913 capture = true; 00914 if (strcmp(param->value.str, "none") != 0) { 00915 capture_pcm_name = param->value.str; 00916 } 00917 break; 00918 00919 case 'P': 00920 playback = true; 00921 if (strcmp(param->value.str, "none") != 0) { 00922 playback_pcm_name = param->value.str; 00923 } 00924 break; 00925 00926 case 'd': 00927 playback_pcm_name = param->value.str; 00928 capture_pcm_name = param->value.str; 00929 break; 00930 00931 case 'e': 00932 excl = true; 00933 break; 00934 00935 case 'I': 00936 systemic_input_latency = param->value.ui; 00937 break; 00938 00939 case 'O': 00940 systemic_output_latency = param->value.ui; 00941 break; 00942 00943 case 'S': 00944 syncio = true; 00945 break; 00946 } 00947 } 00948 00949 // duplex is the default 00950 if (!capture && !playback) { 00951 capture = true; 00952 playback = true; 00953 } 00954 00955 Jack::JackBoomerDriver* boomer_driver = new Jack::JackBoomerDriver("system", "boomer", engine, table); 00956 00957 // Special open for Boomer driver... 00958 if (boomer_driver->Open(frames_per_interrupt, nperiods, srate, capture, playback, chan_in, chan_out, excl, 00959 monitor, capture_pcm_name, playback_pcm_name, systemic_input_latency, systemic_output_latency, bits, syncio) == 0) { 00960 return boomer_driver; 00961 } else { 00962 delete boomer_driver; // Delete the driver 00963 return NULL; 00964 } 00965 } 00966 00967 #ifdef __cplusplus 00968 } 00969 #endif