| 2156 | | stream->streamRepresentation.streamInfo.inputLatency = |
| 2157 | | (double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) |
| 2158 | | + stream->inputLatency) / sampleRate; // seconds |
| 2159 | | stream->streamRepresentation.streamInfo.outputLatency = |
| 2160 | | (double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) |
| 2161 | | + stream->outputLatency) / sampleRate; // seconds |
| 2162 | | stream->streamRepresentation.streamInfo.sampleRate = sampleRate; |
| 2163 | | |
| 2164 | | // the code below prints the ASIO latency which doesn't include the |
| 2165 | | // buffer processor latency. it reports the added latency separately |
| 2166 | | PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n", |
| 2167 | | stream->inputLatency, |
| 2168 | | (long)((stream->inputLatency*1000)/ sampleRate), |
| 2169 | | PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor), |
| 2170 | | (long)((PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)*1000)/ sampleRate) |
| 2171 | | )); |
| 2172 | | |
| 2173 | | PA_DEBUG(("PaAsio : ASIO OuputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n", |
| 2174 | | stream->outputLatency, |
| 2175 | | (long)((stream->outputLatency*1000)/ sampleRate), |
| 2176 | | PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor), |
| 2177 | | (long)((PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)*1000)/ sampleRate) |
| 2178 | | )); |
| | 2224 | |
| | 2225 | /* Using blocking i/o interface... */ |
| | 2226 | if( usingBlockingIo ) |
| | 2227 | { |
| | 2228 | /* Allocate the blocking i/o input ring buffer memory. */ |
| | 2229 | stream->blockingState = (PaAsioStreamBlockingState*)PaUtil_AllocateMemory( sizeof(PaAsioStreamBlockingState) ); |
| | 2230 | if( !stream->blockingState ) |
| | 2231 | { |
| | 2232 | result = paInsufficientMemory; |
| | 2233 | PA_DEBUG(("ERROR! Blocking i/o interface struct allocation failed in OpenStream()\n")); |
| | 2234 | goto error; |
| | 2235 | } |
| | 2236 | |
| | 2237 | /* Initialize blocking i/o interface struct. */ |
| | 2238 | stream->blockingState->readFramesReadyEvent = NULL; /* Uninitialized, yet. */ |
| | 2239 | stream->blockingState->writeBuffersReadyEvent = NULL; /* Uninitialized, yet. */ |
| | 2240 | stream->blockingState->readRingBufferData = NULL; /* Uninitialized, yet. */ |
| | 2241 | stream->blockingState->writeRingBufferData = NULL; /* Uninitialized, yet. */ |
| | 2242 | stream->blockingState->readStreamBuffer = NULL; /* Uninitialized, yet. */ |
| | 2243 | stream->blockingState->writeStreamBuffer = NULL; /* Uninitialized, yet. */ |
| | 2244 | stream->blockingState->stopFlag = TRUE; /* Not started, yet. */ |
| | 2245 | |
| | 2246 | |
| | 2247 | /* If the user buffer is unspecified */ |
| | 2248 | if( framesPerBuffer == paFramesPerBufferUnspecified ) |
| | 2249 | { |
| | 2250 | /* Make the user buffer the same size as the host buffer. */ |
| | 2251 | framesPerBuffer = framesPerHostBuffer; |
| | 2252 | } |
| | 2253 | |
| | 2254 | |
| | 2255 | /* Initialize callback buffer processor. */ |
| | 2256 | result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor , |
| | 2257 | inputChannelCount , |
| | 2258 | inputSampleFormat & ~paNonInterleaved , /* Ring buffer. */ |
| | 2259 | hostInputSampleFormat , /* Host format. */ |
| | 2260 | outputChannelCount , |
| | 2261 | outputSampleFormat & ~paNonInterleaved, /* Ring buffer. */ |
| | 2262 | hostOutputSampleFormat , /* Host format. */ |
| | 2263 | sampleRate , |
| | 2264 | streamFlags , |
| | 2265 | framesPerBuffer , /* Frames per ring buffer block. */ |
| | 2266 | framesPerHostBuffer , /* Frames per asio buffer. */ |
| | 2267 | paUtilFixedHostBufferSize , |
| | 2268 | streamCallback , |
| | 2269 | userData ); |
| | 2270 | if( result != paNoError ){ |
| | 2271 | PA_DEBUG(("OpenStream ERROR13\n")); |
| | 2272 | goto error; |
| | 2273 | } |
| | 2274 | callbackBufferProcessorInited = TRUE; |
| | 2275 | |
| | 2276 | /* Initialize the blocking i/o buffer processor. */ |
| | 2277 | result = PaUtil_InitializeBufferProcessor(&stream->blockingState->bufferProcessor, |
| | 2278 | inputChannelCount , |
| | 2279 | inputSampleFormat , /* User format. */ |
| | 2280 | inputSampleFormat & ~paNonInterleaved , /* Ring buffer. */ |
| | 2281 | outputChannelCount , |
| | 2282 | outputSampleFormat , /* User format. */ |
| | 2283 | outputSampleFormat & ~paNonInterleaved, /* Ring buffer. */ |
| | 2284 | sampleRate , |
| | 2285 | paClipOff | paDitherOff , /* Don't use dither nor clipping. */ |
| | 2286 | framesPerBuffer , /* Frames per user buffer. */ |
| | 2287 | framesPerBuffer , /* Frames per ring buffer block. */ |
| | 2288 | paUtilBoundedHostBufferSize , |
| | 2289 | NULL, NULL );/* No callback! */ |
| | 2290 | if( result != paNoError ){ |
| | 2291 | PA_DEBUG(("ERROR! Blocking i/o buffer processor initialization failed in OpenStream()\n")); |
| | 2292 | goto error; |
| | 2293 | } |
| | 2294 | blockingBufferProcessorInited = TRUE; |
| | 2295 | |
| | 2296 | /* If input is requested. */ |
| | 2297 | if( inputChannelCount ) |
| | 2298 | { |
| | 2299 | /* Create the callback sync-event. */ |
| | 2300 | stream->blockingState->readFramesReadyEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); |
| | 2301 | if( stream->blockingState->readFramesReadyEvent == NULL ) |
| | 2302 | { |
| | 2303 | result = paUnanticipatedHostError; |
| | 2304 | PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() ); |
| | 2305 | PA_DEBUG(("ERROR! Blocking i/o \"read frames ready\" event creation failed in OpenStream()\n")); |
| | 2306 | goto error; |
| | 2307 | } |
| | 2308 | blockingReadFramesReadyEventInitialized = 1; |
| | 2309 | |
| | 2310 | |
| | 2311 | /* Create pointer buffer to access non-interleaved data in ReadStream() */ |
| | 2312 | stream->blockingState->readStreamBuffer = (void**)PaUtil_AllocateMemory( sizeof(void*) * inputChannelCount ); |
| | 2313 | if( !stream->blockingState->readStreamBuffer ) |
| | 2314 | { |
| | 2315 | result = paInsufficientMemory; |
| | 2316 | PA_DEBUG(("ERROR! Blocking i/o read stream buffer allocation failed in OpenStream()\n")); |
| | 2317 | goto error; |
| | 2318 | } |
| | 2319 | |
| | 2320 | /* The ring buffer should store as many data blocks as needed |
| | 2321 | to achieve the requested latency. Whereas it must be large |
| | 2322 | enough to store at least two complete data blocks. |
| | 2323 | |
| | 2324 | 1) Determine the amount of latency to be added to the |
| | 2325 | prefered ASIO latency. |
| | 2326 | 2) Make sure we have at lest one additional latency frame. |
| | 2327 | 3) Divide the number of frames by the desired block size to |
| | 2328 | get the number (rounded up to pure integer) of blocks to |
| | 2329 | be stored in the buffer. |
| | 2330 | 4) Add one additional block for block processing and convert |
| | 2331 | to samples frames. |
| | 2332 | 5) Get the next larger (or equal) power-of-two buffer size. |
| | 2333 | */ |
| | 2334 | lBlockingBufferSize = suggestedInputLatencyFrames - stream->inputLatency; |
| | 2335 | lBlockingBufferSize = (lBlockingBufferSize > 0) ? lBlockingBufferSize : 1; |
| | 2336 | lBlockingBufferSize = (lBlockingBufferSize + framesPerBuffer - 1) / framesPerBuffer; |
| | 2337 | lBlockingBufferSize = (lBlockingBufferSize + 1) * framesPerBuffer; |
| | 2338 | |
| | 2339 | /* Get the next larger or equal power-of-two buffersize. */ |
| | 2340 | lBlockingBufferSizePow2 = 1; |
| | 2341 | while( lBlockingBufferSize > (lBlockingBufferSizePow2<<=1) ); |
| | 2342 | lBlockingBufferSize = lBlockingBufferSizePow2; |
| | 2343 | |
| | 2344 | /* Compute total intput latency in seconds */ |
| | 2345 | stream->streamRepresentation.streamInfo.inputLatency = |
| | 2346 | (double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor ) |
| | 2347 | + PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor) |
| | 2348 | + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer |
| | 2349 | + stream->inputLatency ) |
| | 2350 | / sampleRate; |
| | 2351 | |
| | 2352 | /* The code below prints the ASIO latency which doesn't include |
| | 2353 | the buffer processor latency nor the blocking i/o latency. It |
| | 2354 | reports the added latency separately. |
| | 2355 | */ |
| | 2356 | PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n", |
| | 2357 | stream->inputLatency, |
| | 2358 | (long)( stream->inputLatency * (1000.0 / sampleRate) ), |
| | 2359 | PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor), |
| | 2360 | (long)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) * (1000.0 / sampleRate) ), |
| | 2361 | PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer, |
| | 2362 | (long)( (PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) ) |
| | 2363 | )); |
| | 2364 | |
| | 2365 | /* Determine the size of ring buffer in bytes. */ |
| | 2366 | lBytesPerFrame = inputChannelCount * Pa_GetSampleSize(inputSampleFormat ); |
| | 2367 | |
| | 2368 | /* Allocate the blocking i/o input ring buffer memory. */ |
| | 2369 | stream->blockingState->readRingBufferData = (void*)PaUtil_AllocateMemory( lBlockingBufferSize * lBytesPerFrame ); |
| | 2370 | if( !stream->blockingState->readRingBufferData ) |
| | 2371 | { |
| | 2372 | result = paInsufficientMemory; |
| | 2373 | PA_DEBUG(("ERROR! Blocking i/o input ring buffer allocation failed in OpenStream()\n")); |
| | 2374 | goto error; |
| | 2375 | } |
| | 2376 | |
| | 2377 | /* Initialize the input ring buffer struct. */ |
| | 2378 | PaUtil_InitializeRingBuffer( &stream->blockingState->readRingBuffer , |
| | 2379 | lBytesPerFrame , |
| | 2380 | lBlockingBufferSize , |
| | 2381 | stream->blockingState->readRingBufferData ); |
| | 2382 | } |
| | 2383 | |
| | 2384 | /* If output is requested. */ |
| | 2385 | if( outputChannelCount ) |
| | 2386 | { |
| | 2387 | stream->blockingState->writeBuffersReadyEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); |
| | 2388 | if( stream->blockingState->writeBuffersReadyEvent == NULL ) |
| | 2389 | { |
| | 2390 | result = paUnanticipatedHostError; |
| | 2391 | PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() ); |
| | 2392 | PA_DEBUG(("ERROR! Blocking i/o \"write buffers ready\" event creation failed in OpenStream()\n")); |
| | 2393 | goto error; |
| | 2394 | } |
| | 2395 | blockingWriteBuffersReadyEventInitialized = 1; |
| | 2396 | |
| | 2397 | /* Create pointer buffer to access non-interleaved data in WriteStream() */ |
| | 2398 | stream->blockingState->writeStreamBuffer = (const void**)PaUtil_AllocateMemory( sizeof(const void*) * outputChannelCount ); |
| | 2399 | if( !stream->blockingState->writeStreamBuffer ) |
| | 2400 | { |
| | 2401 | result = paInsufficientMemory; |
| | 2402 | PA_DEBUG(("ERROR! Blocking i/o write stream buffer allocation failed in OpenStream()\n")); |
| | 2403 | goto error; |
| | 2404 | } |
| | 2405 | |
| | 2406 | /* The ring buffer should store as many data blocks as needed |
| | 2407 | to achieve the requested latency. Whereas it must be large |
| | 2408 | enough to store at least two complete data blocks. |
| | 2409 | |
| | 2410 | 1) Determine the amount of latency to be added to the |
| | 2411 | prefered ASIO latency. |
| | 2412 | 2) Make sure we have at lest one additional latency frame. |
| | 2413 | 3) Divide the number of frames by the desired block size to |
| | 2414 | get the number (rounded up to pure integer) of blocks to |
| | 2415 | be stored in the buffer. |
| | 2416 | 4) Add one additional block for block processing and convert |
| | 2417 | to samples frames. |
| | 2418 | 5) Get the next larger (or equal) power-of-two buffer size. |
| | 2419 | */ |
| | 2420 | lBlockingBufferSize = suggestedOutputLatencyFrames - stream->outputLatency; |
| | 2421 | lBlockingBufferSize = (lBlockingBufferSize > 0) ? lBlockingBufferSize : 1; |
| | 2422 | lBlockingBufferSize = (lBlockingBufferSize + framesPerBuffer - 1) / framesPerBuffer; |
| | 2423 | lBlockingBufferSize = (lBlockingBufferSize + 1) * framesPerBuffer; |
| | 2424 | |
| | 2425 | /* The buffer size (without the additional block) corresponds |
| | 2426 | to the initial number of silent samples in the output ring |
| | 2427 | buffer. */ |
| | 2428 | stream->blockingState->writeRingBufferInitialFrames = lBlockingBufferSize - framesPerBuffer; |
| | 2429 | |
| | 2430 | /* Get the next larger or equal power-of-two buffersize. */ |
| | 2431 | lBlockingBufferSizePow2 = 1; |
| | 2432 | while( lBlockingBufferSize > (lBlockingBufferSizePow2<<=1) ); |
| | 2433 | lBlockingBufferSize = lBlockingBufferSizePow2; |
| | 2434 | |
| | 2435 | /* Compute total output latency in seconds */ |
| | 2436 | stream->streamRepresentation.streamInfo.outputLatency = |
| | 2437 | (double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor ) |
| | 2438 | + PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor) |
| | 2439 | + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer |
| | 2440 | + stream->outputLatency ) |
| | 2441 | / sampleRate; |
| | 2442 | |
| | 2443 | /* The code below prints the ASIO latency which doesn't include |
| | 2444 | the buffer processor latency nor the blocking i/o latency. It |
| | 2445 | reports the added latency separately. |
| | 2446 | */ |
| | 2447 | PA_DEBUG(("PaAsio : ASIO OutputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n", |
| | 2448 | stream->outputLatency, |
| | 2449 | (long)( stream->inputLatency * (1000.0 / sampleRate) ), |
| | 2450 | PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor), |
| | 2451 | (long)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) * (1000.0 / sampleRate) ), |
| | 2452 | PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer, |
| | 2453 | (long)( (PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) ) |
| | 2454 | )); |
| | 2455 | |
| | 2456 | /* Determine the size of ring buffer in bytes. */ |
| | 2457 | lBytesPerFrame = outputChannelCount * Pa_GetSampleSize(outputSampleFormat); |
| | 2458 | |
| | 2459 | /* Allocate the blocking i/o output ring buffer memory. */ |
| | 2460 | stream->blockingState->writeRingBufferData = (void*)PaUtil_AllocateMemory( lBlockingBufferSize * lBytesPerFrame ); |
| | 2461 | if( !stream->blockingState->writeRingBufferData ) |
| | 2462 | { |
| | 2463 | result = paInsufficientMemory; |
| | 2464 | PA_DEBUG(("ERROR! Blocking i/o output ring buffer allocation failed in OpenStream()\n")); |
| | 2465 | goto error; |
| | 2466 | } |
| | 2467 | |
| | 2468 | /* Initialize the output ring buffer struct. */ |
| | 2469 | PaUtil_InitializeRingBuffer( &stream->blockingState->writeRingBuffer , |
| | 2470 | lBytesPerFrame , |
| | 2471 | lBlockingBufferSize , |
| | 2472 | stream->blockingState->writeRingBufferData ); |
| | 2473 | } |
| | 2474 | |
| | 2475 | stream->streamRepresentation.streamInfo.sampleRate = sampleRate; |
| | 2476 | |
| | 2477 | |
| | 2478 | } |
| | 2479 | else /* Using callback interface... */ |
| | 2480 | { |
| | 2481 | result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, |
| | 2482 | inputChannelCount, inputSampleFormat, hostInputSampleFormat, |
| | 2483 | outputChannelCount, outputSampleFormat, hostOutputSampleFormat, |
| | 2484 | sampleRate, streamFlags, framesPerBuffer, |
| | 2485 | framesPerHostBuffer, paUtilFixedHostBufferSize, |
| | 2486 | streamCallback, userData ); |
| | 2487 | if( result != paNoError ){ |
| | 2488 | PA_DEBUG(("OpenStream ERROR13\n")); |
| | 2489 | goto error; |
| | 2490 | } |
| | 2491 | callbackBufferProcessorInited = TRUE; |
| | 2492 | |
| | 2493 | stream->streamRepresentation.streamInfo.inputLatency = |
| | 2494 | (double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) |
| | 2495 | + stream->inputLatency) / sampleRate; // seconds |
| | 2496 | stream->streamRepresentation. |