pa_linux_asihpi.c (117654B)
1 /* 2 * $Id:$ 3 * PortAudio Portable Real-Time Audio Library 4 * Latest Version at: http://www.portaudio.com 5 * AudioScience HPI implementation by Fred Gleason, Ludwig Schwardt and 6 * Eliot Blennerhassett 7 * 8 * Copyright (c) 2003 Fred Gleason <fredg@salemradiolabs.com> 9 * Copyright (c) 2005,2006 Ludwig Schwardt <schwardt@sun.ac.za> 10 * Copyright (c) 2011 Eliot Blennerhassett <eblennerhassett@audioscience.com> 11 * 12 * Based on the Open Source API proposed by Ross Bencina 13 * Copyright (c) 1999-2008 Ross Bencina, Phil Burk 14 * 15 * Permission is hereby granted, free of charge, to any person obtaining 16 * a copy of this software and associated documentation files 17 * (the "Software"), to deal in the Software without restriction, 18 * including without limitation the rights to use, copy, modify, merge, 19 * publish, distribute, sublicense, and/or sell copies of the Software, 20 * and to permit persons to whom the Software is furnished to do so, 21 * subject to the following conditions: 22 * 23 * The above copyright notice and this permission notice shall be 24 * included in all copies or substantial portions of the Software. 25 * 26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 27 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 28 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 29 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 30 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 31 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 32 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 33 */ 34 35 /* 36 * The text above constitutes the entire PortAudio license; however, 37 * the PortAudio community also makes the following non-binding requests: 38 * 39 * Any person wishing to distribute modifications to the Software is 40 * requested to send the modifications to the original developer so that 41 * they can be incorporated into the canonical version. It is also 42 * requested that these non-binding requests be included along with the 43 * license above. 44 */ 45 46 /* 47 * Modification History 48 * 12/2003 - Initial version 49 * 09/2005 - v19 version [rewrite] 50 */ 51 52 /** @file 53 @ingroup hostapi_src 54 @brief Host API implementation supporting AudioScience cards 55 via the Linux HPI interface. 56 57 <h3>Overview</h3> 58 59 This is a PortAudio implementation for the AudioScience HPI Audio API 60 on the Linux platform. AudioScience makes a range of audio adapters customised 61 for the broadcasting industry, with support for both Windows and Linux. 62 More information on their products can be found on their website: 63 64 http://www.audioscience.com 65 66 Documentation for the HPI API can be found at: 67 68 http://www.audioscience.com/internet/download/sdk/hpi_usermanual_html/html/index.html 69 70 The Linux HPI driver itself (a kernel module + library) can be downloaded from: 71 72 http://www.audioscience.com/internet/download/linux_drivers.htm 73 74 <h3>Implementation strategy</h3> 75 76 *Note* Ideally, AudioScience cards should be handled by the PortAudio ALSA 77 implementation on Linux, as ALSA is the preferred Linux soundcard API. The existence 78 of this host API implementation might therefore seem a bit flawed. Unfortunately, at 79 the time of the creation of this implementation (June 2006), the PA ALSA implementation 80 could not make use of the existing AudioScience ALSA driver. PA ALSA uses the 81 "memory-mapped" (mmap) ALSA access mode to interact with the ALSA library, while the 82 AudioScience ALSA driver only supports the "read-write" access mode. The appropriate 83 solution to this problem is to add "read-write" support to PortAudio ALSA, thereby 84 extending the range of soundcards it supports (AudioScience cards are not the only 85 ones with this problem). Given the author's limited knowledge of ALSA and the 86 simplicity of the HPI API, the second-best solution was born... 87 88 The following mapping between HPI and PA was followed: 89 HPI subsystem => PortAudio host API 90 HPI adapter => nothing specific 91 HPI stream => PortAudio device 92 93 Each HPI stream is either input or output (not both), and can support 94 different channel counts, sampling rates and sample formats. It is therefore 95 a more natural fit to a PA device. A PA stream can therefore combine two 96 HPI streams (one input and one output) into a "full-duplex" stream. These 97 HPI streams can even be on different physical adapters. The two streams ought to be 98 sample-synchronised when they reside on the same adapter, as most AudioScience adapters 99 derive their ADC and DAC clocks from one master clock. When combining two adapters 100 into one full-duplex stream, however, the use of a word clock connection between the 101 adapters is strongly recommended. 102 103 The HPI interface is inherently blocking, making use of read and write calls to 104 transfer data between user buffers and driver buffers. The callback interface therefore 105 requires a helper thread ("callback engine") which periodically transfers data (one thread 106 per PA stream, in fact). The current implementation explicitly sleeps via Pa_Sleep() until 107 enough samples can be transferred (select() or poll() would be better, but currently seems 108 impossible...). The thread implementation makes use of the Unix thread helper functions 109 and some pthread calls here and there. If a unified PA thread exists, this host API 110 implementation might also compile on Windows, as this is the only real Linux-specific 111 part of the code. 112 113 There is no inherent fixed buffer size in the HPI interface, as in some other host APIs. 114 The PortAudio implementation contains a buffer that is allocated during OpenStream and 115 used to transfer data between the callback and the HPI driver buffer. The size of this 116 buffer is quite flexible and is derived from latency suggestions and matched to the 117 requested callback buffer size as far as possible. It can become quite huge, as the 118 AudioScience cards are typically geared towards higher-latency applications and contain 119 large hardware buffers. 120 121 The HPI interface natively supports most common sample formats and sample rates (some 122 conversion is done on the adapter itself). 123 124 Stream time is measured based on the number of processed frames, which is adjusted by the 125 number of frames currently buffered by the HPI driver. 126 127 There is basic support for detecting overflow and underflow. The HPI interface does not 128 explicitly indicate this, so thresholds on buffer levels are used in combination with 129 stream state. Recovery from overflow and underflow is left to the PA client. 130 131 Blocking streams are also implemented. It makes use of the same polling routines that 132 the callback interface uses, in order to prevent the allocation of variable-sized 133 buffers during reading and writing. The framesPerBuffer parameter is therefore still 134 relevant, and this can be increased in the blocking case to improve efficiency. 135 136 The implementation contains extensive reporting macros (slightly modified PA_ENSURE and 137 PA_UNLESS versions) and a useful stream dump routine to provide debugging feedback. 138 139 Output buffer priming via the user callback (i.e. paPrimeOutputBuffersUsingStreamCallback 140 and friends) is not implemented yet. All output is primed with silence. 141 */ 142 143 #include <unistd.h> 144 #include <stdio.h> 145 #include <stdlib.h> 146 #include <string.h> /* strlen() */ 147 #include <pthread.h> /* pthreads and friends */ 148 #include <assert.h> /* assert */ 149 #include <math.h> /* ceil, floor */ 150 151 #include <asihpi/hpi.h> /* HPI API */ 152 153 #include "portaudio.h" /* PortAudio API */ 154 #include "pa_util.h" /* PA_DEBUG, other small utilities */ 155 #include "pa_unix_util.h" /* Unix threading utilities */ 156 #include "pa_allocation.h" /* Group memory allocation */ 157 #include "pa_hostapi.h" /* Host API structs */ 158 #include "pa_stream.h" /* Stream interface structs */ 159 #include "pa_cpuload.h" /* CPU load measurer */ 160 #include "pa_process.h" /* Buffer processor */ 161 #include "pa_converters.h" /* PaUtilZeroer */ 162 #include "pa_debugprint.h" 163 164 /* -------------------------------------------------------------------------- */ 165 166 /* 167 * Defines 168 */ 169 170 /* Error reporting and assertions */ 171 172 /** Evaluate expression, and return on any PortAudio errors */ 173 #define PA_ENSURE_(expr) \ 174 do { \ 175 PaError paError = (expr); \ 176 if( UNLIKELY( paError < paNoError ) ) \ 177 { \ 178 PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ 179 result = paError; \ 180 goto error; \ 181 } \ 182 } while (0); 183 184 /** Assert expression, else return the provided PaError */ 185 #define PA_UNLESS_(expr, paError) \ 186 do { \ 187 if( UNLIKELY( (expr) == 0 ) ) \ 188 { \ 189 PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ 190 result = (paError); \ 191 goto error; \ 192 } \ 193 } while( 0 ); 194 195 /** Check return value of HPI function, and map it to PaError */ 196 #define PA_ASIHPI_UNLESS_(expr, paError) \ 197 do { \ 198 hpi_err_t hpiError = (expr); \ 199 /* If HPI error occurred */ \ 200 if( UNLIKELY( hpiError ) ) \ 201 { \ 202 char szError[256]; \ 203 HPI_GetErrorText( hpiError, szError ); \ 204 PA_DEBUG(( "HPI error %d occurred: %s\n", hpiError, szError )); \ 205 /* This message will always be displayed, even if debug info is disabled */ \ 206 PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ 207 if( (paError) == paUnanticipatedHostError ) \ 208 { \ 209 PA_DEBUG(( "Host error description: %s\n", szError )); \ 210 /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ 211 if( pthread_equal( pthread_self(), paUnixMainThread ) ) \ 212 { \ 213 PaUtil_SetLastHostErrorInfo( paInDevelopment, hpiError, szError ); \ 214 } \ 215 } \ 216 /* If paNoError is specified, continue as usual */ \ 217 /* (useful if you only want to print out the debug messages above) */ \ 218 if( (paError) < 0 ) \ 219 { \ 220 result = (paError); \ 221 goto error; \ 222 } \ 223 } \ 224 } while( 0 ); 225 226 /** Report HPI error code and text */ 227 #define PA_ASIHPI_REPORT_ERROR_(hpiErrorCode) \ 228 do { \ 229 char szError[256]; \ 230 HPI_GetErrorText( hpiError, szError ); \ 231 PA_DEBUG(( "HPI error %d occurred: %s\n", hpiError, szError )); \ 232 /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ 233 if( pthread_equal( pthread_self(), paUnixMainThread ) ) \ 234 { \ 235 PaUtil_SetLastHostErrorInfo( paInDevelopment, (hpiErrorCode), szError ); \ 236 } \ 237 } while( 0 ); 238 239 /* Defaults */ 240 241 /** Sample formats available natively on AudioScience hardware */ 242 #define PA_ASIHPI_AVAILABLE_FORMATS_ (paFloat32 | paInt32 | paInt24 | paInt16 | paUInt8) 243 /** Enable background bus mastering (BBM) for buffer transfers, if available (see HPI docs) */ 244 #define PA_ASIHPI_USE_BBM_ 1 245 /** Minimum number of frames in HPI buffer (for either data or available space). 246 If buffer contains less data/space, it indicates xrun or completion. */ 247 #define PA_ASIHPI_MIN_FRAMES_ 1152 248 /** Minimum polling interval in milliseconds, which determines minimum host buffer size */ 249 #define PA_ASIHPI_MIN_POLLING_INTERVAL_ 10 250 251 /* -------------------------------------------------------------------------- */ 252 253 /* 254 * Structures 255 */ 256 257 /** Host API global data */ 258 typedef struct PaAsiHpiHostApiRepresentation 259 { 260 /* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */ 261 PaUtilHostApiRepresentation baseHostApiRep; 262 PaUtilStreamInterface callbackStreamInterface; 263 PaUtilStreamInterface blockingStreamInterface; 264 265 PaUtilAllocationGroup *allocations; 266 267 /* implementation specific data goes here */ 268 269 PaHostApiIndex hostApiIndex; 270 } 271 PaAsiHpiHostApiRepresentation; 272 273 274 /** Device data */ 275 typedef struct PaAsiHpiDeviceInfo 276 { 277 /* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */ 278 /** Common PortAudio device information */ 279 PaDeviceInfo baseDeviceInfo; 280 281 /* implementation specific data goes here */ 282 283 /** Adapter index */ 284 uint16_t adapterIndex; 285 /** Adapter model number (hex) */ 286 uint16_t adapterType; 287 /** Adapter HW/SW version */ 288 uint16_t adapterVersion; 289 /** Adapter serial number */ 290 uint32_t adapterSerialNumber; 291 /** Stream number */ 292 uint16_t streamIndex; 293 /** 0=Input, 1=Output (HPI streams are either input or output but not both) */ 294 uint16_t streamIsOutput; 295 } 296 PaAsiHpiDeviceInfo; 297 298 299 /** Stream state as defined by PortAudio. 300 It seems that the host API implementation has to keep track of the PortAudio stream state. 301 Please note that this is NOT the same as the state of the underlying HPI stream. By separating 302 these two concepts, a lot of flexibility is gained. There is a rough match between the two, 303 of course, but forcing a precise match is difficult. For example, HPI_STATE_DRAINED can occur 304 during the Active state of PortAudio (due to underruns) and also during CallBackFinished in 305 the case of an output stream. Similarly, HPI_STATE_STOPPED mostly coincides with the Stopped 306 PortAudio state, by may also occur in the CallbackFinished state when recording is finished. 307 308 Here is a rough match-up: 309 310 PortAudio state => HPI state 311 --------------- --------- 312 Active => HPI_STATE_RECORDING, HPI_STATE_PLAYING, (HPI_STATE_DRAINED) 313 Stopped => HPI_STATE_STOPPED 314 CallbackFinished => HPI_STATE_STOPPED, HPI_STATE_DRAINED */ 315 typedef enum PaAsiHpiStreamState 316 { 317 paAsiHpiStoppedState=0, 318 paAsiHpiActiveState=1, 319 paAsiHpiCallbackFinishedState=2 320 } 321 PaAsiHpiStreamState; 322 323 324 /** Stream component data (associated with one direction, i.e. either input or output) */ 325 typedef struct PaAsiHpiStreamComponent 326 { 327 /** Device information (HPI handles, etc) */ 328 PaAsiHpiDeviceInfo *hpiDevice; 329 /** Stream handle, as passed to HPI interface. */ 330 hpi_handle_t hpiStream; 331 /** Stream format, as passed to HPI interface */ 332 struct hpi_format hpiFormat; 333 /** Number of bytes per frame, derived from hpiFormat and saved for convenience */ 334 uint32_t bytesPerFrame; 335 /** Size of hardware (on-card) buffer of stream in bytes */ 336 uint32_t hardwareBufferSize; 337 /** Size of host (BBM) buffer of stream in bytes (if used) */ 338 uint32_t hostBufferSize; 339 /** Upper limit on the utilization of output stream buffer (both hardware and host). 340 This prevents large latencies in an output-only stream with a potentially huge buffer 341 and a fast data generator, which would otherwise keep the hardware buffer filled to 342 capacity. See also the "Hardware Buffering=off" option in the AudioScience WAV driver. */ 343 uint32_t outputBufferCap; 344 /** Sample buffer (halfway station between HPI and buffer processor) */ 345 uint8_t *tempBuffer; 346 /** Sample buffer size, in bytes */ 347 uint32_t tempBufferSize; 348 } 349 PaAsiHpiStreamComponent; 350 351 352 /** Stream data */ 353 typedef struct PaAsiHpiStream 354 { 355 /* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */ 356 PaUtilStreamRepresentation baseStreamRep; 357 PaUtilCpuLoadMeasurer cpuLoadMeasurer; 358 PaUtilBufferProcessor bufferProcessor; 359 360 PaUtilAllocationGroup *allocations; 361 362 /* implementation specific data goes here */ 363 364 /** Separate structs for input and output sides of stream */ 365 PaAsiHpiStreamComponent *input, *output; 366 367 /** Polling interval (in milliseconds) */ 368 uint32_t pollingInterval; 369 /** Are we running in callback mode? */ 370 int callbackMode; 371 /** Number of frames to transfer at a time to/from HPI */ 372 unsigned long maxFramesPerHostBuffer; 373 /** Indicates that the stream is in the paNeverDropInput mode */ 374 int neverDropInput; 375 /** Contains copy of user buffers, used by blocking interface to transfer non-interleaved data. 376 It went here instead of to each stream component, as the stream component buffer setup in 377 PaAsiHpi_SetupBuffers doesn't know the stream details such as callbackMode. 378 (Maybe a problem later if ReadStream and WriteStream happens concurrently on same stream.) */ 379 void **blockingUserBufferCopy; 380 381 /* Thread-related variables */ 382 383 /** Helper thread which will deliver data to user callback */ 384 PaUnixThread thread; 385 /** PortAudio stream state (Active/Stopped/CallbackFinished) */ 386 volatile sig_atomic_t state; 387 /** Hard abort, i.e. drop frames? */ 388 volatile sig_atomic_t callbackAbort; 389 /** True if stream stopped via exiting callback with paComplete/paAbort flag 390 (as opposed to explicit call to StopStream/AbortStream) */ 391 volatile sig_atomic_t callbackFinished; 392 } 393 PaAsiHpiStream; 394 395 396 /** Stream state information, collected together for convenience */ 397 typedef struct PaAsiHpiStreamInfo 398 { 399 /** HPI stream state (HPI_STATE_STOPPED, HPI_STATE_PLAYING, etc.) */ 400 uint16_t state; 401 /** Size (in bytes) of recording/playback data buffer in HPI driver */ 402 uint32_t bufferSize; 403 /** Amount of data (in bytes) available in the buffer */ 404 uint32_t dataSize; 405 /** Number of frames played/recorded since last stream reset */ 406 uint32_t frameCounter; 407 /** Amount of data (in bytes) in hardware (on-card) buffer. 408 This differs from dataSize if bus mastering (BBM) is used, which introduces another 409 driver-level buffer to which dataSize/bufferSize then refers. */ 410 uint32_t auxDataSize; 411 /** Total number of data frames currently buffered by HPI driver (host + hw buffers) */ 412 uint32_t totalBufferedData; 413 /** Size of immediately available data (for input) or space (for output) in frames. 414 This only checks the first-level buffer (typically host buffer). This amount can be 415 transferred immediately. */ 416 uint32_t availableFrames; 417 /** Indicates that hardware buffer is getting too full */ 418 int overflow; 419 /** Indicates that hardware buffer is getting too empty */ 420 int underflow; 421 } 422 PaAsiHpiStreamInfo; 423 424 /* -------------------------------------------------------------------------- */ 425 426 /* 427 * Function prototypes 428 */ 429 430 #ifdef __cplusplus 431 extern "C" 432 { 433 #endif /* __cplusplus */ 434 435 /* The only exposed function in the entire host API implementation */ 436 PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); 437 438 #ifdef __cplusplus 439 } 440 #endif /* __cplusplus */ 441 442 static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); 443 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, 444 const PaStreamParameters *inputParameters, 445 const PaStreamParameters *outputParameters, 446 double sampleRate ); 447 448 /* Stream prototypes */ 449 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, 450 PaStream **s, 451 const PaStreamParameters *inputParameters, 452 const PaStreamParameters *outputParameters, 453 double sampleRate, 454 unsigned long framesPerBuffer, 455 PaStreamFlags streamFlags, 456 PaStreamCallback *streamCallback, 457 void *userData ); 458 static PaError CloseStream( PaStream *s ); 459 static PaError StartStream( PaStream *s ); 460 static PaError StopStream( PaStream *s ); 461 static PaError AbortStream( PaStream *s ); 462 static PaError IsStreamStopped( PaStream *s ); 463 static PaError IsStreamActive( PaStream *s ); 464 static PaTime GetStreamTime( PaStream *s ); 465 static double GetStreamCpuLoad( PaStream *s ); 466 467 /* Blocking prototypes */ 468 static PaError ReadStream( PaStream *s, void *buffer, unsigned long frames ); 469 static PaError WriteStream( PaStream *s, const void *buffer, unsigned long frames ); 470 static signed long GetStreamReadAvailable( PaStream *s ); 471 static signed long GetStreamWriteAvailable( PaStream *s ); 472 473 /* Callback prototypes */ 474 static void *CallbackThreadFunc( void *userData ); 475 476 /* Functions specific to this API */ 477 static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostApi ); 478 static uint16_t PaAsiHpi_PaToHpiFormat( PaSampleFormat paFormat ); 479 static PaSampleFormat PaAsiHpi_HpiToPaFormat( uint16_t hpiFormat ); 480 static PaError PaAsiHpi_CreateFormat( struct PaUtilHostApiRepresentation *hostApi, 481 const PaStreamParameters *parameters, double sampleRate, 482 PaAsiHpiDeviceInfo **hpiDevice, struct hpi_format *hpiFormat ); 483 static PaError PaAsiHpi_OpenInput( struct PaUtilHostApiRepresentation *hostApi, 484 const PaAsiHpiDeviceInfo *hpiDevice, const struct hpi_format *hpiFormat, 485 hpi_handle_t *hpiStream ); 486 static PaError PaAsiHpi_OpenOutput( struct PaUtilHostApiRepresentation *hostApi, 487 const PaAsiHpiDeviceInfo *hpiDevice, const struct hpi_format *hpiFormat, 488 hpi_handle_t *hpiStream ); 489 static PaError PaAsiHpi_GetStreamInfo( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStreamInfo *info ); 490 static void PaAsiHpi_StreamComponentDump( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStream *stream ); 491 static void PaAsiHpi_StreamDump( PaAsiHpiStream *stream ); 492 static PaError PaAsiHpi_SetupBuffers( PaAsiHpiStreamComponent *streamComp, uint32_t pollingInterval, 493 unsigned long framesPerPaHostBuffer, PaTime suggestedLatency ); 494 static PaError PaAsiHpi_PrimeOutputWithSilence( PaAsiHpiStream *stream ); 495 static PaError PaAsiHpi_StartStream( PaAsiHpiStream *stream, int outputPrimed ); 496 static PaError PaAsiHpi_StopStream( PaAsiHpiStream *stream, int abort ); 497 static PaError PaAsiHpi_ExplicitStop( PaAsiHpiStream *stream, int abort ); 498 static void PaAsiHpi_OnThreadExit( void *userData ); 499 static PaError PaAsiHpi_WaitForFrames( PaAsiHpiStream *stream, unsigned long *framesAvail, 500 PaStreamCallbackFlags *cbFlags ); 501 static void PaAsiHpi_CalculateTimeInfo( PaAsiHpiStream *stream, PaStreamCallbackTimeInfo *timeInfo ); 502 static PaError PaAsiHpi_BeginProcessing( PaAsiHpiStream* stream, unsigned long* numFrames, 503 PaStreamCallbackFlags *cbFlags ); 504 static PaError PaAsiHpi_EndProcessing( PaAsiHpiStream *stream, unsigned long numFrames, 505 PaStreamCallbackFlags *cbFlags ); 506 507 /* ========================================================================== 508 * ============================= IMPLEMENTATION ============================= 509 * ========================================================================== */ 510 511 /* --------------------------- Host API Interface --------------------------- */ 512 513 /** Enumerate all PA devices (= HPI streams). 514 This compiles a list of all HPI adapters, and registers a PA device for each input and 515 output stream it finds. Most errors are ignored, as missing or erroneous devices are 516 simply skipped. 517 518 @param hpiHostApi Pointer to HPI host API struct 519 520 @return PortAudio error code (only paInsufficientMemory in practice) 521 */ 522 static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostApi ) 523 { 524 PaError result = paNoError; 525 PaUtilHostApiRepresentation *hostApi = &hpiHostApi->baseHostApiRep; 526 PaHostApiInfo *baseApiInfo = &hostApi->info; 527 PaAsiHpiDeviceInfo *hpiDeviceList; 528 int numAdapters; 529 hpi_err_t hpiError = 0; 530 int i, j, deviceCount = 0, deviceIndex = 0; 531 532 assert( hpiHostApi ); 533 534 /* Errors not considered critical here (subsystem may report 0 devices), but report them */ 535 /* in debug mode. */ 536 PA_ASIHPI_UNLESS_( HPI_SubSysGetNumAdapters( NULL, &numAdapters), paNoError ); 537 538 for( i=0; i < numAdapters; ++i ) 539 { 540 uint16_t inStreams, outStreams; 541 uint16_t version; 542 uint32_t serial; 543 uint16_t type; 544 uint32_t idx; 545 546 hpiError = HPI_SubSysGetAdapter(NULL, i, &idx, &type); 547 if (hpiError) 548 continue; 549 550 /* Try to open adapter */ 551 hpiError = HPI_AdapterOpen( NULL, idx ); 552 /* Report error and skip to next device on failure */ 553 if( hpiError ) 554 { 555 PA_ASIHPI_REPORT_ERROR_( hpiError ); 556 continue; 557 } 558 hpiError = HPI_AdapterGetInfo( NULL, idx, &outStreams, &inStreams, 559 &version, &serial, &type ); 560 /* Skip to next device on failure */ 561 if( hpiError ) 562 { 563 PA_ASIHPI_REPORT_ERROR_( hpiError ); 564 continue; 565 } 566 else 567 { 568 /* Assign default devices if available and increment device count */ 569 if( (baseApiInfo->defaultInputDevice == paNoDevice) && (inStreams > 0) ) 570 baseApiInfo->defaultInputDevice = deviceCount; 571 deviceCount += inStreams; 572 if( (baseApiInfo->defaultOutputDevice == paNoDevice) && (outStreams > 0) ) 573 baseApiInfo->defaultOutputDevice = deviceCount; 574 deviceCount += outStreams; 575 } 576 } 577 578 /* Register any discovered devices */ 579 if( deviceCount > 0 ) 580 { 581 /* Memory allocation */ 582 PA_UNLESS_( hostApi->deviceInfos = (PaDeviceInfo**) PaUtil_GroupAllocateMemory( 583 hpiHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount ), 584 paInsufficientMemory ); 585 /* Allocate all device info structs in a contiguous block */ 586 PA_UNLESS_( hpiDeviceList = (PaAsiHpiDeviceInfo*) PaUtil_GroupAllocateMemory( 587 hpiHostApi->allocations, sizeof(PaAsiHpiDeviceInfo) * deviceCount ), 588 paInsufficientMemory ); 589 590 /* Now query devices again for information */ 591 for( i=0; i < numAdapters; ++i ) 592 { 593 uint16_t inStreams, outStreams; 594 uint16_t version; 595 uint32_t serial; 596 uint16_t type; 597 uint32_t idx; 598 599 hpiError = HPI_SubSysGetAdapter( NULL, i, &idx, &type ); 600 if (hpiError) 601 continue; 602 603 /* Assume adapter is still open from previous round */ 604 hpiError = HPI_AdapterGetInfo( NULL, idx, 605 &outStreams, &inStreams, &version, &serial, &type ); 606 /* Report error and skip to next device on failure */ 607 if( hpiError ) 608 { 609 PA_ASIHPI_REPORT_ERROR_( hpiError ); 610 continue; 611 } 612 else 613 { 614 PA_DEBUG(( "Found HPI Adapter ID=%4X Idx=%d #In=%d #Out=%d S/N=%d HWver=%c%d DSPver=%03d\n", 615 type, idx, inStreams, outStreams, serial, 616 ((version>>3)&0xf)+'A', /* Hw version major */ 617 version&0x7, /* Hw version minor */ 618 ((version>>13)*100)+((version>>7)&0x3f) /* DSP code version */ 619 )); 620 } 621 622 /* First add all input streams as devices */ 623 for( j=0; j < inStreams; ++j ) 624 { 625 PaAsiHpiDeviceInfo *hpiDevice = &hpiDeviceList[deviceIndex]; 626 PaDeviceInfo *baseDeviceInfo = &hpiDevice->baseDeviceInfo; 627 char srcName[72]; 628 char *deviceName; 629 630 memset( hpiDevice, 0, sizeof(PaAsiHpiDeviceInfo) ); 631 /* Set implementation-specific device details */ 632 hpiDevice->adapterIndex = idx; 633 hpiDevice->adapterType = type; 634 hpiDevice->adapterVersion = version; 635 hpiDevice->adapterSerialNumber = serial; 636 hpiDevice->streamIndex = j; 637 hpiDevice->streamIsOutput = 0; 638 /* Set common PortAudio device stats */ 639 baseDeviceInfo->structVersion = 2; 640 /* Make sure name string is owned by API info structure */ 641 sprintf( srcName, 642 "Adapter %d (%4X) - Input Stream %d", i+1, type, j+1 ); 643 PA_UNLESS_( deviceName = (char *) PaUtil_GroupAllocateMemory( 644 hpiHostApi->allocations, strlen(srcName) + 1 ), paInsufficientMemory ); 645 strcpy( deviceName, srcName ); 646 baseDeviceInfo->name = deviceName; 647 baseDeviceInfo->hostApi = hpiHostApi->hostApiIndex; 648 baseDeviceInfo->maxInputChannels = HPI_MAX_CHANNELS; 649 baseDeviceInfo->maxOutputChannels = 0; 650 /* Default latency values for interactive performance */ 651 baseDeviceInfo->defaultLowInputLatency = 0.01; 652 baseDeviceInfo->defaultLowOutputLatency = -1.0; 653 /* Default latency values for robust non-interactive applications (eg. playing sound files) */ 654 baseDeviceInfo->defaultHighInputLatency = 0.2; 655 baseDeviceInfo->defaultHighOutputLatency = -1.0; 656 /* HPI interface can actually handle any sampling rate to 1 Hz accuracy, 657 * so this default is as good as any */ 658 baseDeviceInfo->defaultSampleRate = 44100; 659 660 /* Store device in global PortAudio list */ 661 hostApi->deviceInfos[deviceIndex++] = (PaDeviceInfo *) hpiDevice; 662 } 663 664 /* Now add all output streams as devices (I know, the repetition is painful) */ 665 for( j=0; j < outStreams; ++j ) 666 { 667 PaAsiHpiDeviceInfo *hpiDevice = &hpiDeviceList[deviceIndex]; 668 PaDeviceInfo *baseDeviceInfo = &hpiDevice->baseDeviceInfo; 669 char srcName[72]; 670 char *deviceName; 671 672 memset( hpiDevice, 0, sizeof(PaAsiHpiDeviceInfo) ); 673 /* Set implementation-specific device details */ 674 hpiDevice->adapterIndex = idx; 675 hpiDevice->adapterType = type; 676 hpiDevice->adapterVersion = version; 677 hpiDevice->adapterSerialNumber = serial; 678 hpiDevice->streamIndex = j; 679 hpiDevice->streamIsOutput = 1; 680 /* Set common PortAudio device stats */ 681 baseDeviceInfo->structVersion = 2; 682 /* Make sure name string is owned by API info structure */ 683 sprintf( srcName, 684 "Adapter %d (%4X) - Output Stream %d", i+1, type, j+1 ); 685 PA_UNLESS_( deviceName = (char *) PaUtil_GroupAllocateMemory( 686 hpiHostApi->allocations, strlen(srcName) + 1 ), paInsufficientMemory ); 687 strcpy( deviceName, srcName ); 688 baseDeviceInfo->name = deviceName; 689 baseDeviceInfo->hostApi = hpiHostApi->hostApiIndex; 690 baseDeviceInfo->maxInputChannels = 0; 691 baseDeviceInfo->maxOutputChannels = HPI_MAX_CHANNELS; 692 /* Default latency values for interactive performance. */ 693 baseDeviceInfo->defaultLowInputLatency = -1.0; 694 baseDeviceInfo->defaultLowOutputLatency = 0.01; 695 /* Default latency values for robust non-interactive applications (eg. playing sound files). */ 696 baseDeviceInfo->defaultHighInputLatency = -1.0; 697 baseDeviceInfo->defaultHighOutputLatency = 0.2; 698 /* HPI interface can actually handle any sampling rate to 1 Hz accuracy, 699 * so this default is as good as any */ 700 baseDeviceInfo->defaultSampleRate = 44100; 701 702 /* Store device in global PortAudio list */ 703 hostApi->deviceInfos[deviceIndex++] = (PaDeviceInfo *) hpiDevice; 704 } 705 } 706 } 707 708 /* Finally acknowledge checked devices */ 709 baseApiInfo->deviceCount = deviceIndex; 710 711 error: 712 return result; 713 } 714 715 716 /** Initialize host API implementation. 717 This is the only function exported beyond this file. It is called by PortAudio to initialize 718 the host API. It stores API info, finds and registers all devices, and sets up callback and 719 blocking interfaces. 720 721 @param hostApi Pointer to host API struct 722 723 @param hostApiIndex Index of current (HPI) host API 724 725 @return PortAudio error code 726 */ 727 PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) 728 { 729 PaError result = paNoError; 730 PaAsiHpiHostApiRepresentation *hpiHostApi = NULL; 731 PaHostApiInfo *baseApiInfo; 732 733 /* Try to initialize HPI subsystem */ 734 if (!HPI_SubSysCreate()) 735 { 736 /* the V19 development docs say that if an implementation 737 * detects that it cannot be used, it should return a NULL 738 * interface and paNoError */ 739 PA_DEBUG(( "Could not open HPI interface\n" )); 740 741 *hostApi = NULL; 742 return paNoError; 743 } 744 else 745 { 746 uint32_t hpiVersion; 747 PA_ASIHPI_UNLESS_( HPI_SubSysGetVersionEx( NULL, &hpiVersion ), paUnanticipatedHostError ); 748 PA_DEBUG(( "HPI interface v%d.%02d.%02d\n", 749 hpiVersion >> 16, (hpiVersion >> 8) & 0x0F, (hpiVersion & 0x0F) )); 750 } 751 752 /* Allocate host API structure */ 753 PA_UNLESS_( hpiHostApi = (PaAsiHpiHostApiRepresentation*) PaUtil_AllocateMemory( 754 sizeof(PaAsiHpiHostApiRepresentation) ), paInsufficientMemory ); 755 PA_UNLESS_( hpiHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); 756 757 hpiHostApi->hostApiIndex = hostApiIndex; 758 759 *hostApi = &hpiHostApi->baseHostApiRep; 760 baseApiInfo = &((*hostApi)->info); 761 /* Fill in common API details */ 762 baseApiInfo->structVersion = 1; 763 baseApiInfo->type = paAudioScienceHPI; 764 baseApiInfo->name = "AudioScience HPI"; 765 baseApiInfo->deviceCount = 0; 766 baseApiInfo->defaultInputDevice = paNoDevice; 767 baseApiInfo->defaultOutputDevice = paNoDevice; 768 769 PA_ENSURE_( PaAsiHpi_BuildDeviceList( hpiHostApi ) ); 770 771 (*hostApi)->Terminate = Terminate; 772 (*hostApi)->OpenStream = OpenStream; 773 (*hostApi)->IsFormatSupported = IsFormatSupported; 774 775 PaUtil_InitializeStreamInterface( &hpiHostApi->callbackStreamInterface, CloseStream, StartStream, 776 StopStream, AbortStream, IsStreamStopped, IsStreamActive, 777 GetStreamTime, GetStreamCpuLoad, 778 PaUtil_DummyRead, PaUtil_DummyWrite, 779 PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); 780 781 PaUtil_InitializeStreamInterface( &hpiHostApi->blockingStreamInterface, CloseStream, StartStream, 782 StopStream, AbortStream, IsStreamStopped, IsStreamActive, 783 GetStreamTime, PaUtil_DummyGetCpuLoad, 784 ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); 785 786 /* Store identity of main thread */ 787 PA_ENSURE_( PaUnixThreading_Initialize() ); 788 789 return result; 790 error: 791 if (hpiHostApi) 792 PaUtil_FreeMemory( hpiHostApi ); 793 return result; 794 } 795 796 797 /** Terminate host API implementation. 798 This closes all HPI adapters and frees the HPI subsystem. It also frees the host API struct 799 memory. It should be called once for every PaAsiHpi_Initialize call. 800 801 @param Pointer to host API struct 802 */ 803 static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) 804 { 805 PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi; 806 int i; 807 PaError result = paNoError; 808 809 if( hpiHostApi ) 810 { 811 /* Get rid of HPI-specific structures */ 812 uint16_t lastAdapterIndex = HPI_MAX_ADAPTERS; 813 /* Iterate through device list and close adapters */ 814 for( i=0; i < hostApi->info.deviceCount; ++i ) 815 { 816 PaAsiHpiDeviceInfo *hpiDevice = (PaAsiHpiDeviceInfo *) hostApi->deviceInfos[ i ]; 817 /* Close adapter only if it differs from previous one */ 818 if( hpiDevice->adapterIndex != lastAdapterIndex ) 819 { 820 /* Ignore errors (report only during debugging) */ 821 PA_ASIHPI_UNLESS_( HPI_AdapterClose( NULL, 822 hpiDevice->adapterIndex ), paNoError ); 823 lastAdapterIndex = hpiDevice->adapterIndex; 824 } 825 } 826 /* Finally dismantle HPI subsystem */ 827 HPI_SubSysFree( NULL ); 828 829 if( hpiHostApi->allocations ) 830 { 831 PaUtil_FreeAllAllocations( hpiHostApi->allocations ); 832 PaUtil_DestroyAllocationGroup( hpiHostApi->allocations ); 833 } 834 835 PaUtil_FreeMemory( hpiHostApi ); 836 } 837 error: 838 return; 839 } 840 841 842 /** Converts PortAudio sample format to equivalent HPI format. 843 844 @param paFormat PortAudio sample format 845 846 @return HPI sample format 847 */ 848 static uint16_t PaAsiHpi_PaToHpiFormat( PaSampleFormat paFormat ) 849 { 850 /* Ignore interleaving flag */ 851 switch( paFormat & ~paNonInterleaved ) 852 { 853 case paFloat32: 854 return HPI_FORMAT_PCM32_FLOAT; 855 856 case paInt32: 857 return HPI_FORMAT_PCM32_SIGNED; 858 859 case paInt24: 860 return HPI_FORMAT_PCM24_SIGNED; 861 862 case paInt16: 863 return HPI_FORMAT_PCM16_SIGNED; 864 865 case paUInt8: 866 return HPI_FORMAT_PCM8_UNSIGNED; 867 868 /* Default is 16-bit signed */ 869 case paInt8: 870 default: 871 return HPI_FORMAT_PCM16_SIGNED; 872 } 873 } 874 875 876 /** Converts HPI sample format to equivalent PortAudio format. 877 878 @param paFormat HPI sample format 879 880 @return PortAudio sample format 881 */ 882 static PaSampleFormat PaAsiHpi_HpiToPaFormat( uint16_t hpiFormat ) 883 { 884 switch( hpiFormat ) 885 { 886 case HPI_FORMAT_PCM32_FLOAT: 887 return paFloat32; 888 889 case HPI_FORMAT_PCM32_SIGNED: 890 return paInt32; 891 892 case HPI_FORMAT_PCM24_SIGNED: 893 return paInt24; 894 895 case HPI_FORMAT_PCM16_SIGNED: 896 return paInt16; 897 898 case HPI_FORMAT_PCM8_UNSIGNED: 899 return paUInt8; 900 901 /* Default is custom format (e.g. for HPI MP3 format) */ 902 default: 903 return paCustomFormat; 904 } 905 } 906 907 908 /** Creates HPI format struct based on PortAudio parameters. 909 This also does some checks to see whether the desired format is valid, and whether 910 the device allows it. This only checks the format of one half (input or output) of the 911 PortAudio stream. 912 913 @param hostApi Pointer to host API struct 914 915 @param parameters Pointer to stream parameter struct 916 917 @param sampleRate Desired sample rate 918 919 @param hpiDevice Pointer to HPI device struct 920 921 @param hpiFormat Resulting HPI format returned here 922 923 @return PortAudio error code (typically indicating a problem with stream format) 924 */ 925 static PaError PaAsiHpi_CreateFormat( struct PaUtilHostApiRepresentation *hostApi, 926 const PaStreamParameters *parameters, double sampleRate, 927 PaAsiHpiDeviceInfo **hpiDevice, struct hpi_format *hpiFormat ) 928 { 929 int maxChannelCount = 0; 930 PaSampleFormat hostSampleFormat = 0; 931 hpi_err_t hpiError = 0; 932 933 /* Unless alternate device specification is supported, reject the use of 934 paUseHostApiSpecificDeviceSpecification */ 935 if( parameters->device == paUseHostApiSpecificDeviceSpecification ) 936 return paInvalidDevice; 937 else 938 { 939 assert( parameters->device < hostApi->info.deviceCount ); 940 *hpiDevice = (PaAsiHpiDeviceInfo*) hostApi->deviceInfos[ parameters->device ]; 941 } 942 943 /* Validate streamInfo - this implementation doesn't use custom stream info */ 944 if( parameters->hostApiSpecificStreamInfo ) 945 return paIncompatibleHostApiSpecificStreamInfo; 946 947 /* Check that device can support channel count */ 948 if( (*hpiDevice)->streamIsOutput ) 949 { 950 maxChannelCount = (*hpiDevice)->baseDeviceInfo.maxOutputChannels; 951 } 952 else 953 { 954 maxChannelCount = (*hpiDevice)->baseDeviceInfo.maxInputChannels; 955 } 956 if( (maxChannelCount == 0) || (parameters->channelCount > maxChannelCount) ) 957 return paInvalidChannelCount; 958 959 /* All standard sample formats are supported by the buffer adapter, 960 and this implementation doesn't support any custom sample formats */ 961 if( parameters->sampleFormat & paCustomFormat ) 962 return paSampleFormatNotSupported; 963 964 /* Switch to closest HPI native format */ 965 hostSampleFormat = PaUtil_SelectClosestAvailableFormat(PA_ASIHPI_AVAILABLE_FORMATS_, 966 parameters->sampleFormat ); 967 /* Setup format + info objects */ 968 hpiError = HPI_FormatCreate( hpiFormat, (uint16_t)parameters->channelCount, 969 PaAsiHpi_PaToHpiFormat( hostSampleFormat ), 970 (uint32_t)sampleRate, 0, 0 ); 971 if( hpiError ) 972 { 973 PA_ASIHPI_REPORT_ERROR_( hpiError ); 974 switch( hpiError ) 975 { 976 case HPI_ERROR_INVALID_FORMAT: 977 return paSampleFormatNotSupported; 978 979 case HPI_ERROR_INVALID_SAMPLERATE: 980 case HPI_ERROR_INCOMPATIBLE_SAMPLERATE: 981 return paInvalidSampleRate; 982 983 case HPI_ERROR_INVALID_CHANNELS: 984 return paInvalidChannelCount; 985 } 986 } 987 988 return paNoError; 989 } 990 991 992 /** Open HPI input stream with given format. 993 This attempts to open HPI input stream with desired format. If the format is not supported 994 or the device is unavailable, the stream is closed and a PortAudio error code is returned. 995 996 @param hostApi Pointer to host API struct 997 998 @param hpiDevice Pointer to HPI device struct 999 1000 @param hpiFormat Pointer to HPI format struct 1001 1002 @return PortAudio error code (typically indicating a problem with stream format or device) 1003 */ 1004 static PaError PaAsiHpi_OpenInput( struct PaUtilHostApiRepresentation *hostApi, 1005 const PaAsiHpiDeviceInfo *hpiDevice, const struct hpi_format *hpiFormat, 1006 hpi_handle_t *hpiStream ) 1007 { 1008 PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi; 1009 PaError result = paNoError; 1010 hpi_err_t hpiError = 0; 1011 1012 /* Catch misplaced output devices, as they typically have 0 input channels */ 1013 PA_UNLESS_( !hpiDevice->streamIsOutput, paInvalidChannelCount ); 1014 /* Try to open input stream */ 1015 PA_ASIHPI_UNLESS_( HPI_InStreamOpen( NULL, hpiDevice->adapterIndex, 1016 hpiDevice->streamIndex, hpiStream ), paDeviceUnavailable ); 1017 /* Set input format (checking it in the process) */ 1018 /* Could also use HPI_InStreamQueryFormat, but this economizes the process */ 1019 hpiError = HPI_InStreamSetFormat( NULL, *hpiStream, (struct hpi_format*)hpiFormat ); 1020 if( hpiError ) 1021 { 1022 PA_ASIHPI_REPORT_ERROR_( hpiError ); 1023 PA_ASIHPI_UNLESS_( HPI_InStreamClose( NULL, *hpiStream ), paNoError ); 1024 switch( hpiError ) 1025 { 1026 case HPI_ERROR_INVALID_FORMAT: 1027 return paSampleFormatNotSupported; 1028 1029 case HPI_ERROR_INVALID_SAMPLERATE: 1030 case HPI_ERROR_INCOMPATIBLE_SAMPLERATE: 1031 return paInvalidSampleRate; 1032 1033 case HPI_ERROR_INVALID_CHANNELS: 1034 return paInvalidChannelCount; 1035 1036 default: 1037 /* In case anything else went wrong */ 1038 return paInvalidDevice; 1039 } 1040 } 1041 1042 error: 1043 return result; 1044 } 1045 1046 1047 /** Open HPI output stream with given format. 1048 This attempts to open HPI output stream with desired format. If the format is not supported 1049 or the device is unavailable, the stream is closed and a PortAudio error code is returned. 1050 1051 @param hostApi Pointer to host API struct 1052 1053 @param hpiDevice Pointer to HPI device struct 1054 1055 @param hpiFormat Pointer to HPI format struct 1056 1057 @return PortAudio error code (typically indicating a problem with stream format or device) 1058 */ 1059 static PaError PaAsiHpi_OpenOutput( struct PaUtilHostApiRepresentation *hostApi, 1060 const PaAsiHpiDeviceInfo *hpiDevice, const struct hpi_format *hpiFormat, 1061 hpi_handle_t *hpiStream ) 1062 { 1063 PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi; 1064 PaError result = paNoError; 1065 hpi_err_t hpiError = 0; 1066 1067 /* Catch misplaced input devices, as they typically have 0 output channels */ 1068 PA_UNLESS_( hpiDevice->streamIsOutput, paInvalidChannelCount ); 1069 /* Try to open output stream */ 1070 PA_ASIHPI_UNLESS_( HPI_OutStreamOpen( NULL, hpiDevice->adapterIndex, 1071 hpiDevice->streamIndex, hpiStream ), paDeviceUnavailable ); 1072 1073 /* Check output format (format is set on first write to output stream) */ 1074 hpiError = HPI_OutStreamQueryFormat( NULL, *hpiStream, (struct hpi_format*)hpiFormat ); 1075 if( hpiError ) 1076 { 1077 PA_ASIHPI_REPORT_ERROR_( hpiError ); 1078 PA_ASIHPI_UNLESS_( HPI_OutStreamClose( NULL, *hpiStream ), paNoError ); 1079 switch( hpiError ) 1080 { 1081 case HPI_ERROR_INVALID_FORMAT: 1082 return paSampleFormatNotSupported; 1083 1084 case HPI_ERROR_INVALID_SAMPLERATE: 1085 case HPI_ERROR_INCOMPATIBLE_SAMPLERATE: 1086 return paInvalidSampleRate; 1087 1088 case HPI_ERROR_INVALID_CHANNELS: 1089 return paInvalidChannelCount; 1090 1091 default: 1092 /* In case anything else went wrong */ 1093 return paInvalidDevice; 1094 } 1095 } 1096 1097 error: 1098 return result; 1099 } 1100 1101 1102 /** Checks whether the desired stream formats and devices are supported 1103 (for both input and output). 1104 This is done by actually opening the appropriate HPI streams and closing them again. 1105 1106 @param hostApi Pointer to host API struct 1107 1108 @param inputParameters Pointer to stream parameter struct for input side of stream 1109 1110 @param outputParameters Pointer to stream parameter struct for output side of stream 1111 1112 @param sampleRate Desired sample rate 1113 1114 @return PortAudio error code (paFormatIsSupported on success) 1115 */ 1116 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, 1117 const PaStreamParameters *inputParameters, 1118 const PaStreamParameters *outputParameters, 1119 double sampleRate ) 1120 { 1121 PaError result = paFormatIsSupported; 1122 PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi; 1123 PaAsiHpiDeviceInfo *hpiDevice = NULL; 1124 struct hpi_format hpiFormat; 1125 1126 /* Input stream */ 1127 if( inputParameters ) 1128 { 1129 hpi_handle_t hpiStream; 1130 PA_DEBUG(( "%s: Checking input params: dev=%d, sr=%d, chans=%d, fmt=%d\n", 1131 __FUNCTION__, inputParameters->device, (int)sampleRate, 1132 inputParameters->channelCount, inputParameters->sampleFormat )); 1133 /* Create and validate format */ 1134 PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, inputParameters, sampleRate, 1135 &hpiDevice, &hpiFormat ) ); 1136 /* Open stream to further check format */ 1137 PA_ENSURE_( PaAsiHpi_OpenInput( hostApi, hpiDevice, &hpiFormat, &hpiStream ) ); 1138 /* Close stream again */ 1139 PA_ASIHPI_UNLESS_( HPI_InStreamClose( NULL, hpiStream ), paNoError ); 1140 } 1141 1142 /* Output stream */ 1143 if( outputParameters ) 1144 { 1145 hpi_handle_t hpiStream; 1146 PA_DEBUG(( "%s: Checking output params: dev=%d, sr=%d, chans=%d, fmt=%d\n", 1147 __FUNCTION__, outputParameters->device, (int)sampleRate, 1148 outputParameters->channelCount, outputParameters->sampleFormat )); 1149 /* Create and validate format */ 1150 PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, outputParameters, sampleRate, 1151 &hpiDevice, &hpiFormat ) ); 1152 /* Open stream to further check format */ 1153 PA_ENSURE_( PaAsiHpi_OpenOutput( hostApi, hpiDevice, &hpiFormat, &hpiStream ) ); 1154 /* Close stream again */ 1155 PA_ASIHPI_UNLESS_( HPI_OutStreamClose( NULL, hpiStream ), paNoError ); 1156 } 1157 1158 error: 1159 return result; 1160 } 1161 1162 /* ---------------------------- Stream Interface ---------------------------- */ 1163 1164 /** Obtain HPI stream information. 1165 This obtains info such as stream state and available data/space in buffers. It also 1166 estimates whether an underflow or overflow occurred. 1167 1168 @param streamComp Pointer to stream component (input or output) to query 1169 1170 @param info Pointer to stream info struct that will contain result 1171 1172 @return PortAudio error code (either paNoError, paDeviceUnavailable or paUnanticipatedHostError) 1173 */ 1174 static PaError PaAsiHpi_GetStreamInfo( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStreamInfo *info ) 1175 { 1176 PaError result = paDeviceUnavailable; 1177 uint16_t state; 1178 uint32_t bufferSize, dataSize, frameCounter, auxDataSize, threshold; 1179 uint32_t hwBufferSize, hwDataSize; 1180 1181 assert( streamComp ); 1182 assert( info ); 1183 1184 /* First blank the stream info struct, in case something goes wrong below. 1185 This saves the caller from initializing the struct. */ 1186 info->state = 0; 1187 info->bufferSize = 0; 1188 info->dataSize = 0; 1189 info->frameCounter = 0; 1190 info->auxDataSize = 0; 1191 info->totalBufferedData = 0; 1192 info->availableFrames = 0; 1193 info->underflow = 0; 1194 info->overflow = 0; 1195 1196 if( streamComp->hpiDevice && streamComp->hpiStream ) 1197 { 1198 /* Obtain detailed stream info (either input or output) */ 1199 if( streamComp->hpiDevice->streamIsOutput ) 1200 { 1201 PA_ASIHPI_UNLESS_( HPI_OutStreamGetInfoEx( NULL, 1202 streamComp->hpiStream, 1203 &state, &bufferSize, &dataSize, &frameCounter, 1204 &auxDataSize ), paUnanticipatedHostError ); 1205 } 1206 else 1207 { 1208 PA_ASIHPI_UNLESS_( HPI_InStreamGetInfoEx( NULL, 1209 streamComp->hpiStream, 1210 &state, &bufferSize, &dataSize, &frameCounter, 1211 &auxDataSize ), paUnanticipatedHostError ); 1212 } 1213 /* Load stream info */ 1214 info->state = state; 1215 info->bufferSize = bufferSize; 1216 info->dataSize = dataSize; 1217 info->frameCounter = frameCounter; 1218 info->auxDataSize = auxDataSize; 1219 /* Determine total buffered data */ 1220 info->totalBufferedData = dataSize; 1221 if( streamComp->hostBufferSize > 0 ) 1222 info->totalBufferedData += auxDataSize; 1223 info->totalBufferedData /= streamComp->bytesPerFrame; 1224 /* Determine immediately available frames */ 1225 info->availableFrames = streamComp->hpiDevice->streamIsOutput ? 1226 bufferSize - dataSize : dataSize; 1227 info->availableFrames /= streamComp->bytesPerFrame; 1228 /* Minimum space/data required in buffers */ 1229 threshold = PA_MIN( streamComp->tempBufferSize, 1230 streamComp->bytesPerFrame * PA_ASIHPI_MIN_FRAMES_ ); 1231 /* Obtain hardware buffer stats first, to simplify things */ 1232 hwBufferSize = streamComp->hardwareBufferSize; 1233 hwDataSize = streamComp->hostBufferSize > 0 ? auxDataSize : dataSize; 1234 /* Underflow is a bit tricky */ 1235 info->underflow = streamComp->hpiDevice->streamIsOutput ? 1236 /* Stream seems to start in drained state sometimes, so ignore initial underflow */ 1237 (frameCounter > 0) && ( (state == HPI_STATE_DRAINED) || (hwDataSize == 0) ) : 1238 /* Input streams check the first-level (host) buffer for underflow */ 1239 (state != HPI_STATE_STOPPED) && (dataSize < threshold); 1240 /* Check for overflow in second-level (hardware) buffer for both input and output */ 1241 info->overflow = (state != HPI_STATE_STOPPED) && (hwBufferSize - hwDataSize < threshold); 1242 1243 return paNoError; 1244 } 1245 1246 error: 1247 return result; 1248 } 1249 1250 1251 /** Display stream component information for debugging purposes. 1252 1253 @param streamComp Pointer to stream component (input or output) to query 1254 1255 @param stream Pointer to stream struct which contains the component above 1256 */ 1257 static void PaAsiHpi_StreamComponentDump( PaAsiHpiStreamComponent *streamComp, 1258 PaAsiHpiStream *stream ) 1259 { 1260 PaAsiHpiStreamInfo streamInfo; 1261 1262 assert( streamComp ); 1263 assert( stream ); 1264 1265 /* Name of soundcard/device used by component */ 1266 PA_DEBUG(( "device: %s\n", streamComp->hpiDevice->baseDeviceInfo.name )); 1267 /* Unfortunately some overlap between input and output here */ 1268 if( streamComp->hpiDevice->streamIsOutput ) 1269 { 1270 /* Settings on the user side (as experienced by user callback) */ 1271 PA_DEBUG(( "user: %d-bit, %d ", 1272 8*stream->bufferProcessor.bytesPerUserOutputSample, 1273 stream->bufferProcessor.outputChannelCount)); 1274 if( stream->bufferProcessor.userOutputIsInterleaved ) 1275 { 1276 PA_DEBUG(( "interleaved channels, " )); 1277 } 1278 else 1279 { 1280 PA_DEBUG(( "non-interleaved channels, " )); 1281 } 1282 PA_DEBUG(( "%d frames/buffer, latency = %5.1f ms\n", 1283 stream->bufferProcessor.framesPerUserBuffer, 1284 1000*stream->baseStreamRep.streamInfo.outputLatency )); 1285 /* Settings on the host side (internal to PortAudio host API) */ 1286 PA_DEBUG(( "host: %d-bit, %d interleaved channels, %d frames/buffer ", 1287 8*stream->bufferProcessor.bytesPerHostOutputSample, 1288 stream->bufferProcessor.outputChannelCount, 1289 stream->bufferProcessor.framesPerHostBuffer )); 1290 } 1291 else 1292 { 1293 /* Settings on the user side (as experienced by user callback) */ 1294 PA_DEBUG(( "user: %d-bit, %d ", 1295 8*stream->bufferProcessor.bytesPerUserInputSample, 1296 stream->bufferProcessor.inputChannelCount)); 1297 if( stream->bufferProcessor.userInputIsInterleaved ) 1298 { 1299 PA_DEBUG(( "interleaved channels, " )); 1300 } 1301 else 1302 { 1303 PA_DEBUG(( "non-interleaved channels, " )); 1304 } 1305 PA_DEBUG(( "%d frames/buffer, latency = %5.1f ms\n", 1306 stream->bufferProcessor.framesPerUserBuffer, 1307 1000*stream->baseStreamRep.streamInfo.inputLatency )); 1308 /* Settings on the host side (internal to PortAudio host API) */ 1309 PA_DEBUG(( "host: %d-bit, %d interleaved channels, %d frames/buffer ", 1310 8*stream->bufferProcessor.bytesPerHostInputSample, 1311 stream->bufferProcessor.inputChannelCount, 1312 stream->bufferProcessor.framesPerHostBuffer )); 1313 } 1314 switch( stream->bufferProcessor.hostBufferSizeMode ) 1315 { 1316 case paUtilFixedHostBufferSize: 1317 PA_DEBUG(( "[fixed] " )); 1318 break; 1319 case paUtilBoundedHostBufferSize: 1320 PA_DEBUG(( "[bounded] " )); 1321 break; 1322 case paUtilUnknownHostBufferSize: 1323 PA_DEBUG(( "[unknown] " )); 1324 break; 1325 case paUtilVariableHostBufferSizePartialUsageAllowed: 1326 PA_DEBUG(( "[variable] " )); 1327 break; 1328 } 1329 PA_DEBUG(( "(%d max)\n", streamComp->tempBufferSize / streamComp->bytesPerFrame )); 1330 /* HPI hardware settings */ 1331 PA_DEBUG(( "HPI: adapter %d stream %d, %d-bit, %d-channel, %d Hz\n", 1332 streamComp->hpiDevice->adapterIndex, streamComp->hpiDevice->streamIndex, 1333 8 * streamComp->bytesPerFrame / streamComp->hpiFormat.wChannels, 1334 streamComp->hpiFormat.wChannels, 1335 streamComp->hpiFormat.dwSampleRate )); 1336 /* Stream state and buffer levels */ 1337 PA_DEBUG(( "HPI: " )); 1338 PaAsiHpi_GetStreamInfo( streamComp, &streamInfo ); 1339 switch( streamInfo.state ) 1340 { 1341 case HPI_STATE_STOPPED: 1342 PA_DEBUG(( "[STOPPED] " )); 1343 break; 1344 case HPI_STATE_PLAYING: 1345 PA_DEBUG(( "[PLAYING] " )); 1346 break; 1347 case HPI_STATE_RECORDING: 1348 PA_DEBUG(( "[RECORDING] " )); 1349 break; 1350 case HPI_STATE_DRAINED: 1351 PA_DEBUG(( "[DRAINED] " )); 1352 break; 1353 default: 1354 PA_DEBUG(( "[unknown state] " )); 1355 break; 1356 } 1357 if( streamComp->hostBufferSize ) 1358 { 1359 PA_DEBUG(( "host = %d/%d B, ", streamInfo.dataSize, streamComp->hostBufferSize )); 1360 PA_DEBUG(( "hw = %d/%d (%d) B, ", streamInfo.auxDataSize, 1361 streamComp->hardwareBufferSize, streamComp->outputBufferCap )); 1362 } 1363 else 1364 { 1365 PA_DEBUG(( "hw = %d/%d B, ", streamInfo.dataSize, streamComp->hardwareBufferSize )); 1366 } 1367 PA_DEBUG(( "count = %d", streamInfo.frameCounter )); 1368 if( streamInfo.overflow ) 1369 { 1370 PA_DEBUG(( " [overflow]" )); 1371 } 1372 else if( streamInfo.underflow ) 1373 { 1374 PA_DEBUG(( " [underflow]" )); 1375 } 1376 PA_DEBUG(( "\n" )); 1377 } 1378 1379 1380 /** Display stream information for debugging purposes. 1381 1382 @param stream Pointer to stream to query 1383 */ 1384 static void PaAsiHpi_StreamDump( PaAsiHpiStream *stream ) 1385 { 1386 assert( stream ); 1387 1388 PA_DEBUG(( "\n------------------------- STREAM INFO FOR %p ---------------------------\n", stream )); 1389 /* General stream info (input+output) */ 1390 if( stream->baseStreamRep.streamCallback ) 1391 { 1392 PA_DEBUG(( "[callback] " )); 1393 } 1394 else 1395 { 1396 PA_DEBUG(( "[blocking] " )); 1397 } 1398 PA_DEBUG(( "sr=%d Hz, poll=%d ms, max %d frames/buf ", 1399 (int)stream->baseStreamRep.streamInfo.sampleRate, 1400 stream->pollingInterval, stream->maxFramesPerHostBuffer )); 1401 switch( stream->state ) 1402 { 1403 case paAsiHpiStoppedState: 1404 PA_DEBUG(( "[stopped]\n" )); 1405 break; 1406 case paAsiHpiActiveState: 1407 PA_DEBUG(( "[active]\n" )); 1408 break; 1409 case paAsiHpiCallbackFinishedState: 1410 PA_DEBUG(( "[cb fin]\n" )); 1411 break; 1412 default: 1413 PA_DEBUG(( "[unknown state]\n" )); 1414 break; 1415 } 1416 if( stream->callbackMode ) 1417 { 1418 PA_DEBUG(( "cb info: thread=%p, cbAbort=%d, cbFinished=%d\n", 1419 stream->thread.thread, stream->callbackAbort, stream->callbackFinished )); 1420 } 1421 1422 PA_DEBUG(( "----------------------------------- Input ------------------------------------\n" )); 1423 if( stream->input ) 1424 { 1425 PaAsiHpi_StreamComponentDump( stream->input, stream ); 1426 } 1427 else 1428 { 1429 PA_DEBUG(( "*none*\n" )); 1430 } 1431 1432 PA_DEBUG(( "----------------------------------- Output ------------------------------------\n" )); 1433 if( stream->output ) 1434 { 1435 PaAsiHpi_StreamComponentDump( stream->output, stream ); 1436 } 1437 else 1438 { 1439 PA_DEBUG(( "*none*\n" )); 1440 } 1441 PA_DEBUG(( "-------------------------------------------------------------------------------\n\n" )); 1442 1443 } 1444 1445 1446 /** Determine buffer sizes and allocate appropriate stream buffers. 1447 This attempts to allocate a BBM (host) buffer for the HPI stream component (either input 1448 or output, as both have similar buffer needs). Not all AudioScience adapters support BBM, 1449 in which case the hardware buffer has to suffice. The size of the HPI host buffer is chosen 1450 as a multiple of framesPerPaHostBuffer, and also influenced by the suggested latency and the 1451 estimated minimum polling interval. The HPI host and hardware buffer sizes are stored, and an 1452 appropriate cap for the hardware buffer is also calculated. Finally, the temporary stream 1453 buffer which serves as the PortAudio host buffer for this implementation is allocated. 1454 This buffer contains an integer number of user buffers, to simplify buffer adaption in the 1455 buffer processor. The function returns paBufferTooBig if the HPI interface cannot allocate 1456 an HPI host buffer of the desired size. 1457 1458 @param streamComp Pointer to stream component struct 1459 1460 @param pollingInterval Polling interval for stream, in milliseconds 1461 1462 @param framesPerPaHostBuffer Size of PortAudio host buffer, in frames 1463 1464 @param suggestedLatency Suggested latency for stream component, in seconds 1465 1466 @return PortAudio error code (possibly paBufferTooBig or paInsufficientMemory) 1467 */ 1468 static PaError PaAsiHpi_SetupBuffers( PaAsiHpiStreamComponent *streamComp, uint32_t pollingInterval, 1469 unsigned long framesPerPaHostBuffer, PaTime suggestedLatency ) 1470 { 1471 PaError result = paNoError; 1472 PaAsiHpiStreamInfo streamInfo; 1473 unsigned long hpiBufferSize = 0, paHostBufferSize = 0; 1474 1475 assert( streamComp ); 1476 assert( streamComp->hpiDevice ); 1477 1478 /* Obtain size of hardware buffer of HPI stream, since we will be activating BBM shortly 1479 and afterwards the buffer size will refer to the BBM (host-side) buffer. 1480 This is necessary to enable reliable detection of xruns. */ 1481 PA_ENSURE_( PaAsiHpi_GetStreamInfo( streamComp, &streamInfo ) ); 1482 streamComp->hardwareBufferSize = streamInfo.bufferSize; 1483 hpiBufferSize = streamInfo.bufferSize; 1484 1485 /* Check if BBM (background bus mastering) is to be enabled */ 1486 if( PA_ASIHPI_USE_BBM_ ) 1487 { 1488 uint32_t bbmBufferSize = 0, preLatencyBufferSize = 0; 1489 hpi_err_t hpiError = 0; 1490 PaTime pollingOverhead; 1491 1492 /* Check overhead of Pa_Sleep() call (minimum sleep duration in ms -> OS dependent) */ 1493 pollingOverhead = PaUtil_GetTime(); 1494 Pa_Sleep( 0 ); 1495 pollingOverhead = 1000*(PaUtil_GetTime() - pollingOverhead); 1496 PA_DEBUG(( "polling overhead = %f ms (length of 0-second sleep)\n", pollingOverhead )); 1497 /* Obtain minimum recommended size for host buffer (in bytes) */ 1498 PA_ASIHPI_UNLESS_( HPI_StreamEstimateBufferSize( &streamComp->hpiFormat, 1499 pollingInterval + (uint32_t)ceil( pollingOverhead ), 1500 &bbmBufferSize ), paUnanticipatedHostError ); 1501 /* BBM places more stringent requirements on buffer size (see description */ 1502 /* of HPI_StreamEstimateBufferSize in HPI API document) */ 1503 bbmBufferSize *= 3; 1504 /* Make sure the BBM buffer contains multiple PA host buffers */ 1505 if( bbmBufferSize < 3 * streamComp->bytesPerFrame * framesPerPaHostBuffer ) 1506 bbmBufferSize = 3 * streamComp->bytesPerFrame * framesPerPaHostBuffer; 1507 /* Try to honor latency suggested by user by growing buffer (no decrease possible) */ 1508 if( suggestedLatency > 0.0 ) 1509 { 1510 PaTime bufferDuration = ((PaTime)bbmBufferSize) / streamComp->bytesPerFrame 1511 / streamComp->hpiFormat.dwSampleRate; 1512 /* Don't decrease buffer */ 1513 if( bufferDuration < suggestedLatency ) 1514 { 1515 /* Save old buffer size, to be retried if new size proves too big */ 1516 preLatencyBufferSize = bbmBufferSize; 1517 bbmBufferSize = (uint32_t)ceil( suggestedLatency * streamComp->bytesPerFrame 1518 * streamComp->hpiFormat.dwSampleRate ); 1519 } 1520 } 1521 /* Choose closest memory block boundary (HPI API document states that 1522 "a buffer size of Nx4096 - 20 makes the best use of memory" 1523 (under the entry for HPI_StreamEstimateBufferSize)) */ 1524 bbmBufferSize = ((uint32_t)ceil((bbmBufferSize + 20)/4096.0))*4096 - 20; 1525 streamComp->hostBufferSize = bbmBufferSize; 1526 /* Allocate BBM host buffer (this enables bus mastering transfers in background) */ 1527 if( streamComp->hpiDevice->streamIsOutput ) 1528 hpiError = HPI_OutStreamHostBufferAllocate( NULL, 1529 streamComp->hpiStream, 1530 bbmBufferSize ); 1531 else 1532 hpiError = HPI_InStreamHostBufferAllocate( NULL, 1533 streamComp->hpiStream, 1534 bbmBufferSize ); 1535 if( hpiError ) 1536 { 1537 /* Indicate that BBM is disabled */ 1538 streamComp->hostBufferSize = 0; 1539 /* Retry with smaller buffer size (transfers will still work, but not via BBM) */ 1540 if( hpiError == HPI_ERROR_INVALID_DATASIZE ) 1541 { 1542 /* Retry BBM allocation with smaller size if requested latency proved too big */ 1543 if( preLatencyBufferSize > 0 ) 1544 { 1545 PA_DEBUG(( "Retrying BBM allocation with smaller size (%d vs. %d bytes)\n", 1546 preLatencyBufferSize, bbmBufferSize )); 1547 bbmBufferSize = preLatencyBufferSize; 1548 if( streamComp->hpiDevice->streamIsOutput ) 1549 hpiError = HPI_OutStreamHostBufferAllocate( NULL, 1550 streamComp->hpiStream, 1551 bbmBufferSize ); 1552 else 1553 hpiError = HPI_InStreamHostBufferAllocate( NULL, 1554 streamComp->hpiStream, 1555 bbmBufferSize ); 1556 /* Another round of error checking */ 1557 if( hpiError ) 1558 { 1559 PA_ASIHPI_REPORT_ERROR_( hpiError ); 1560 /* No escapes this time */ 1561 if( hpiError == HPI_ERROR_INVALID_DATASIZE ) 1562 { 1563 result = paBufferTooBig; 1564 goto error; 1565 } 1566 else if( hpiError != HPI_ERROR_INVALID_OPERATION ) 1567 { 1568 result = paUnanticipatedHostError; 1569 goto error; 1570 } 1571 } 1572 else 1573 { 1574 streamComp->hostBufferSize = bbmBufferSize; 1575 hpiBufferSize = bbmBufferSize; 1576 } 1577 } 1578 else 1579 { 1580 result = paBufferTooBig; 1581 goto error; 1582 } 1583 } 1584 /* If BBM not supported, foreground transfers will be used, but not a show-stopper */ 1585 /* Anything else is an error */ 1586 else if (( hpiError != HPI_ERROR_INVALID_OPERATION ) && 1587 ( hpiError != HPI_ERROR_INVALID_FUNC )) 1588 { 1589 PA_ASIHPI_REPORT_ERROR_( hpiError ); 1590 result = paUnanticipatedHostError; 1591 goto error; 1592 } 1593 } 1594 else 1595 { 1596 hpiBufferSize = bbmBufferSize; 1597 } 1598 } 1599 1600 /* Final check of buffer size */ 1601 paHostBufferSize = streamComp->bytesPerFrame * framesPerPaHostBuffer; 1602 if( hpiBufferSize < 3*paHostBufferSize ) 1603 { 1604 result = paBufferTooBig; 1605 goto error; 1606 } 1607 /* Set cap on output buffer size, based on latency suggestions */ 1608 if( streamComp->hpiDevice->streamIsOutput ) 1609 { 1610 PaTime latency = suggestedLatency > 0.0 ? suggestedLatency : 1611 streamComp->hpiDevice->baseDeviceInfo.defaultHighOutputLatency; 1612 streamComp->outputBufferCap = 1613 (uint32_t)ceil( latency * streamComp->bytesPerFrame * streamComp->hpiFormat.dwSampleRate ); 1614 /* The cap should not be too small, to prevent underflow */ 1615 if( streamComp->outputBufferCap < 4*paHostBufferSize ) 1616 streamComp->outputBufferCap = 4*paHostBufferSize; 1617 } 1618 else 1619 { 1620 streamComp->outputBufferCap = 0; 1621 } 1622 /* Temp buffer size should be multiple of PA host buffer size (or 1x, if using fixed blocks) */ 1623 streamComp->tempBufferSize = paHostBufferSize; 1624 /* Allocate temp buffer */ 1625 PA_UNLESS_( streamComp->tempBuffer = (uint8_t *)PaUtil_AllocateMemory( streamComp->tempBufferSize ), 1626 paInsufficientMemory ); 1627 error: 1628 return result; 1629 } 1630 1631 1632 /** Opens PortAudio stream. 1633 This determines a suitable value for framesPerBuffer, if the user didn't specify it, 1634 based on the suggested latency. It then opens each requested stream direction with the 1635 appropriate stream format, and allocates the required stream buffers. It sets up the 1636 various PortAudio structures dealing with streams, and estimates the stream latency. 1637 1638 See pa_hostapi.h for a list of validity guarantees made about OpenStream parameters. 1639 1640 @param hostApi Pointer to host API struct 1641 1642 @param s List of open streams, where successfully opened stream will go 1643 1644 @param inputParameters Pointer to stream parameter struct for input side of stream 1645 1646 @param outputParameters Pointer to stream parameter struct for output side of stream 1647 1648 @param sampleRate Desired sample rate 1649 1650 @param framesPerBuffer Desired number of frames per buffer passed to user callback 1651 (or chunk size for blocking stream) 1652 1653 @param streamFlags Stream flags 1654 1655 @param streamCallback Pointer to user callback function (zero for blocking interface) 1656 1657 @param userData Pointer to user data that will be passed to callback function along with data 1658 1659 @return PortAudio error code 1660 */ 1661 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, 1662 PaStream **s, 1663 const PaStreamParameters *inputParameters, 1664 const PaStreamParameters *outputParameters, 1665 double sampleRate, 1666 unsigned long framesPerBuffer, 1667 PaStreamFlags streamFlags, 1668 PaStreamCallback *streamCallback, 1669 void *userData ) 1670 { 1671 PaError result = paNoError; 1672 PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi; 1673 PaAsiHpiStream *stream = NULL; 1674 unsigned long framesPerHostBuffer = framesPerBuffer; 1675 int inputChannelCount = 0, outputChannelCount = 0; 1676 PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0; 1677 PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0; 1678 PaTime maxSuggestedLatency = 0.0; 1679 1680 /* Validate platform-specific flags -> none expected for HPI */ 1681 if( (streamFlags & paPlatformSpecificFlags) != 0 ) 1682 return paInvalidFlag; /* unexpected platform-specific flag */ 1683 1684 /* Create blank stream structure */ 1685 PA_UNLESS_( stream = (PaAsiHpiStream *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStream) ), 1686 paInsufficientMemory ); 1687 memset( stream, 0, sizeof(PaAsiHpiStream) ); 1688 1689 /* If the number of frames per buffer is unspecified, we have to come up with one. */ 1690 if( framesPerHostBuffer == paFramesPerBufferUnspecified ) 1691 { 1692 if( inputParameters ) 1693 maxSuggestedLatency = inputParameters->suggestedLatency; 1694 if( outputParameters && (outputParameters->suggestedLatency > maxSuggestedLatency) ) 1695 maxSuggestedLatency = outputParameters->suggestedLatency; 1696 /* Use suggested latency if available */ 1697 if( maxSuggestedLatency > 0.0 ) 1698 framesPerHostBuffer = (unsigned long)ceil( maxSuggestedLatency * sampleRate ); 1699 else 1700 /* AudioScience cards like BIG buffers by default */ 1701 framesPerHostBuffer = 4096; 1702 } 1703 /* Lower bounds on host buffer size, due to polling and HPI constraints */ 1704 if( 1000.0*framesPerHostBuffer/sampleRate < PA_ASIHPI_MIN_POLLING_INTERVAL_ ) 1705 framesPerHostBuffer = (unsigned long)ceil( sampleRate * PA_ASIHPI_MIN_POLLING_INTERVAL_ / 1000.0 ); 1706 /* if( framesPerHostBuffer < PA_ASIHPI_MIN_FRAMES_ ) 1707 framesPerHostBuffer = PA_ASIHPI_MIN_FRAMES_; */ 1708 /* Efficient if host buffer size is integer multiple of user buffer size */ 1709 if( framesPerBuffer > 0 ) 1710 framesPerHostBuffer = (unsigned long)ceil( (double)framesPerHostBuffer / framesPerBuffer ) * framesPerBuffer; 1711 /* Buffer should always be a multiple of 4 bytes to facilitate 32-bit PCI transfers. 1712 By keeping the frames a multiple of 4, this is ensured even for 8-bit mono sound. */ 1713 framesPerHostBuffer = (framesPerHostBuffer / 4) * 4; 1714 /* Polling is based on time length (in milliseconds) of user-requested block size */ 1715 stream->pollingInterval = (uint32_t)ceil( 1000.0*framesPerHostBuffer/sampleRate ); 1716 assert( framesPerHostBuffer > 0 ); 1717 1718 /* Open underlying streams, check formats and allocate buffers */ 1719 if( inputParameters ) 1720 { 1721 /* Create blank stream component structure */ 1722 PA_UNLESS_( stream->input = (PaAsiHpiStreamComponent *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStreamComponent) ), 1723 paInsufficientMemory ); 1724 memset( stream->input, 0, sizeof(PaAsiHpiStreamComponent) ); 1725 /* Create/validate format */ 1726 PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, inputParameters, sampleRate, 1727 &stream->input->hpiDevice, &stream->input->hpiFormat ) ); 1728 /* Open stream and set format */ 1729 PA_ENSURE_( PaAsiHpi_OpenInput( hostApi, stream->input->hpiDevice, &stream->input->hpiFormat, 1730 &stream->input->hpiStream ) ); 1731 inputChannelCount = inputParameters->channelCount; 1732 inputSampleFormat = inputParameters->sampleFormat; 1733 hostInputSampleFormat = PaAsiHpi_HpiToPaFormat( stream->input->hpiFormat.wFormat ); 1734 stream->input->bytesPerFrame = inputChannelCount * Pa_GetSampleSize( hostInputSampleFormat ); 1735 assert( stream->input->bytesPerFrame > 0 ); 1736 /* Allocate host and temp buffers of appropriate size */ 1737 PA_ENSURE_( PaAsiHpi_SetupBuffers( stream->input, stream->pollingInterval, 1738 framesPerHostBuffer, inputParameters->suggestedLatency ) ); 1739 } 1740 if( outputParameters ) 1741 { 1742 /* Create blank stream component structure */ 1743 PA_UNLESS_( stream->output = (PaAsiHpiStreamComponent *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStreamComponent) ), 1744 paInsufficientMemory ); 1745 memset( stream->output, 0, sizeof(PaAsiHpiStreamComponent) ); 1746 /* Create/validate format */ 1747 PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, outputParameters, sampleRate, 1748 &stream->output->hpiDevice, &stream->output->hpiFormat ) ); 1749 /* Open stream and check format */ 1750 PA_ENSURE_( PaAsiHpi_OpenOutput( hostApi, stream->output->hpiDevice, 1751 &stream->output->hpiFormat, 1752 &stream->output->hpiStream ) ); 1753 outputChannelCount = outputParameters->channelCount; 1754 outputSampleFormat = outputParameters->sampleFormat; 1755 hostOutputSampleFormat = PaAsiHpi_HpiToPaFormat( stream->output->hpiFormat.wFormat ); 1756 stream->output->bytesPerFrame = outputChannelCount * Pa_GetSampleSize( hostOutputSampleFormat ); 1757 /* Allocate host and temp buffers of appropriate size */ 1758 PA_ENSURE_( PaAsiHpi_SetupBuffers( stream->output, stream->pollingInterval, 1759 framesPerHostBuffer, outputParameters->suggestedLatency ) ); 1760 } 1761 1762 /* Determine maximum frames per host buffer (least common denominator of input/output) */ 1763 if( inputParameters && outputParameters ) 1764 { 1765 stream->maxFramesPerHostBuffer = PA_MIN( stream->input->tempBufferSize / stream->input->bytesPerFrame, 1766 stream->output->tempBufferSize / stream->output->bytesPerFrame ); 1767 } 1768 else 1769 { 1770 stream->maxFramesPerHostBuffer = inputParameters ? stream->input->tempBufferSize / stream->input->bytesPerFrame 1771 : stream->output->tempBufferSize / stream->output->bytesPerFrame; 1772 } 1773 assert( stream->maxFramesPerHostBuffer > 0 ); 1774 /* Initialize various other stream parameters */ 1775 stream->neverDropInput = streamFlags & paNeverDropInput; 1776 stream->state = paAsiHpiStoppedState; 1777 1778 /* Initialize either callback or blocking interface */ 1779 if( streamCallback ) 1780 { 1781 PaUtil_InitializeStreamRepresentation( &stream->baseStreamRep, 1782 &hpiHostApi->callbackStreamInterface, 1783 streamCallback, userData ); 1784 stream->callbackMode = 1; 1785 } 1786 else 1787 { 1788 PaUtil_InitializeStreamRepresentation( &stream->baseStreamRep, 1789 &hpiHostApi->blockingStreamInterface, 1790 streamCallback, userData ); 1791 /* Pre-allocate non-interleaved user buffer pointers for blocking interface */ 1792 PA_UNLESS_( stream->blockingUserBufferCopy = 1793 PaUtil_AllocateMemory( sizeof(void *) * PA_MAX( inputChannelCount, outputChannelCount ) ), 1794 paInsufficientMemory ); 1795 stream->callbackMode = 0; 1796 } 1797 PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); 1798 1799 /* Following pa_linux_alsa's lead, we operate with fixed host buffer size by default, */ 1800 /* since other modes will invariably lead to block adaption (maybe Bounded better?) */ 1801 PA_ENSURE_( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, 1802 inputChannelCount, inputSampleFormat, hostInputSampleFormat, 1803 outputChannelCount, outputSampleFormat, hostOutputSampleFormat, 1804 sampleRate, streamFlags, 1805 framesPerBuffer, framesPerHostBuffer, paUtilFixedHostBufferSize, 1806 streamCallback, userData ) ); 1807 1808 stream->baseStreamRep.streamInfo.structVersion = 1; 1809 stream->baseStreamRep.streamInfo.sampleRate = sampleRate; 1810 /* Determine input latency from buffer processor and buffer sizes */ 1811 if( stream->input ) 1812 { 1813 PaTime bufferDuration = ( stream->input->hostBufferSize + stream->input->hardwareBufferSize ) 1814 / sampleRate / stream->input->bytesPerFrame; 1815 stream->baseStreamRep.streamInfo.inputLatency = 1816 bufferDuration + 1817 ((PaTime)PaUtil_GetBufferProcessorInputLatencyFrames( &stream->bufferProcessor ) - 1818 stream->maxFramesPerHostBuffer) / sampleRate; 1819 assert( stream->baseStreamRep.streamInfo.inputLatency > 0.0 ); 1820 } 1821 /* Determine output latency from buffer processor and buffer sizes */ 1822 if( stream->output ) 1823 { 1824 PaTime bufferDuration = ( stream->output->hostBufferSize + stream->output->hardwareBufferSize ) 1825 / sampleRate / stream->output->bytesPerFrame; 1826 /* Take buffer size cap into account (see PaAsiHpi_WaitForFrames) */ 1827 if( !stream->input && (stream->output->outputBufferCap > 0) ) 1828 { 1829 bufferDuration = PA_MIN( bufferDuration, 1830 stream->output->outputBufferCap / sampleRate / stream->output->bytesPerFrame ); 1831 } 1832 stream->baseStreamRep.streamInfo.outputLatency = 1833 bufferDuration + 1834 ((PaTime)PaUtil_GetBufferProcessorOutputLatencyFrames( &stream->bufferProcessor ) - 1835 stream->maxFramesPerHostBuffer) / sampleRate; 1836 assert( stream->baseStreamRep.streamInfo.outputLatency > 0.0 ); 1837 } 1838 1839 /* Report stream info, for debugging purposes */ 1840 PaAsiHpi_StreamDump( stream ); 1841 1842 /* Save initialized stream to PA stream list */ 1843 *s = (PaStream*)stream; 1844 return result; 1845 1846 error: 1847 CloseStream( (PaStream*)stream ); 1848 return result; 1849 } 1850 1851 1852 /** Close PortAudio stream. 1853 When CloseStream() is called, the multi-api layer ensures that the stream has already 1854 been stopped or aborted. This closes the underlying HPI streams and deallocates stream 1855 buffers and structs. 1856 1857 @param s Pointer to PortAudio stream 1858 1859 @return PortAudio error code 1860 */ 1861 static PaError CloseStream( PaStream *s ) 1862 { 1863 PaError result = paNoError; 1864 PaAsiHpiStream *stream = (PaAsiHpiStream*)s; 1865 1866 /* If stream is already gone, all is well */ 1867 if( stream == NULL ) 1868 return paNoError; 1869 1870 /* Generic stream cleanup */ 1871 PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); 1872 PaUtil_TerminateStreamRepresentation( &stream->baseStreamRep ); 1873 1874 /* Implementation-specific details - close internal streams */ 1875 if( stream->input ) 1876 { 1877 /* Close HPI stream (freeing BBM host buffer in the process, if used) */ 1878 if( stream->input->hpiStream ) 1879 { 1880 PA_ASIHPI_UNLESS_( HPI_InStreamClose( NULL, 1881 stream->input->hpiStream ), paUnanticipatedHostError ); 1882 } 1883 /* Free temp buffer and stream component */ 1884 PaUtil_FreeMemory( stream->input->tempBuffer ); 1885 PaUtil_FreeMemory( stream->input ); 1886 } 1887 if( stream->output ) 1888 { 1889 /* Close HPI stream (freeing BBM host buffer in the process, if used) */ 1890 if( stream->output->hpiStream ) 1891 { 1892 PA_ASIHPI_UNLESS_( HPI_OutStreamClose( NULL, 1893 stream->output->hpiStream ), paUnanticipatedHostError ); 1894 } 1895 /* Free temp buffer and stream component */ 1896 PaUtil_FreeMemory( stream->output->tempBuffer ); 1897 PaUtil_FreeMemory( stream->output ); 1898 } 1899 1900 PaUtil_FreeMemory( stream->blockingUserBufferCopy ); 1901 PaUtil_FreeMemory( stream ); 1902 1903 error: 1904 return result; 1905 } 1906 1907 1908 /** Prime HPI output stream with silence. 1909 This resets the output stream and uses PortAudio helper routines to fill the 1910 temp buffer with silence. It then writes two host buffers to the stream. This is supposed 1911 to be called before the stream is started. It has no effect on input-only streams. 1912 1913 @param stream Pointer to stream struct 1914 1915 @return PortAudio error code 1916 */ 1917 static PaError PaAsiHpi_PrimeOutputWithSilence( PaAsiHpiStream *stream ) 1918 { 1919 PaError result = paNoError; 1920 PaAsiHpiStreamComponent *out; 1921 PaUtilZeroer *zeroer; 1922 PaSampleFormat outputFormat; 1923 assert( stream ); 1924 out = stream->output; 1925 /* Only continue if stream has output channels */ 1926 if( !out ) 1927 return result; 1928 assert( out->tempBuffer ); 1929 1930 /* Clear all existing data in hardware playback buffer */ 1931 PA_ASIHPI_UNLESS_( HPI_OutStreamReset( NULL, 1932 out->hpiStream ), paUnanticipatedHostError ); 1933 /* Fill temp buffer with silence */ 1934 outputFormat = PaAsiHpi_HpiToPaFormat( out->hpiFormat.wFormat ); 1935 zeroer = PaUtil_SelectZeroer( outputFormat ); 1936 zeroer(out->tempBuffer, 1, out->tempBufferSize / Pa_GetSampleSize(outputFormat) ); 1937 /* Write temp buffer to hardware fifo twice, to get started */ 1938 PA_ASIHPI_UNLESS_( HPI_OutStreamWriteBuf( NULL, out->hpiStream, 1939 out->tempBuffer, out->tempBufferSize, &out->hpiFormat), 1940 paUnanticipatedHostError ); 1941 PA_ASIHPI_UNLESS_( HPI_OutStreamWriteBuf( NULL, out->hpiStream, 1942 out->tempBuffer, out->tempBufferSize, &out->hpiFormat), 1943 paUnanticipatedHostError ); 1944 error: 1945 return result; 1946 } 1947 1948 1949 /** Start HPI streams (both input + output). 1950 This starts all HPI streams in the PortAudio stream. Output streams are first primed with 1951 silence, if required. After this call the PA stream is in the Active state. 1952 1953 @todo Implement priming via the user callback 1954 1955 @param stream Pointer to stream struct 1956 1957 @param outputPrimed True if output is already primed (if false, silence will be loaded before starting) 1958 1959 @return PortAudio error code 1960 */ 1961 static PaError PaAsiHpi_StartStream( PaAsiHpiStream *stream, int outputPrimed ) 1962 { 1963 PaError result = paNoError; 1964 1965 if( stream->input ) 1966 { 1967 PA_ASIHPI_UNLESS_( HPI_InStreamStart( NULL, 1968 stream->input->hpiStream ), paUnanticipatedHostError ); 1969 } 1970 if( stream->output ) 1971 { 1972 if( !outputPrimed ) 1973 { 1974 /* Buffer isn't primed, so load stream with silence */ 1975 PA_ENSURE_( PaAsiHpi_PrimeOutputWithSilence( stream ) ); 1976 } 1977 PA_ASIHPI_UNLESS_( HPI_OutStreamStart( NULL, 1978 stream->output->hpiStream ), paUnanticipatedHostError ); 1979 } 1980 stream->state = paAsiHpiActiveState; 1981 stream->callbackFinished = 0; 1982 1983 /* Report stream info for debugging purposes */ 1984 /* PaAsiHpi_StreamDump( stream ); */ 1985 1986 error: 1987 return result; 1988 } 1989 1990 1991 /** Start PortAudio stream. 1992 If the stream has a callback interface, this starts a helper thread to feed the user callback. 1993 The thread will then take care of starting the HPI streams, and this function will block 1994 until the streams actually start. In the case of a blocking interface, the HPI streams 1995 are simply started. 1996 1997 @param s Pointer to PortAudio stream 1998 1999 @return PortAudio error code 2000 */ 2001 static PaError StartStream( PaStream *s ) 2002 { 2003 PaError result = paNoError; 2004 PaAsiHpiStream *stream = (PaAsiHpiStream*)s; 2005 2006 assert( stream ); 2007 2008 /* Ready the processor */ 2009 PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); 2010 2011 if( stream->callbackMode ) 2012 { 2013 /* Create and start callback engine thread */ 2014 /* Also waits 1 second for stream to be started by engine thread (otherwise aborts) */ 2015 PA_ENSURE_( PaUnixThread_New( &stream->thread, &CallbackThreadFunc, stream, 1., 0 /*rtSched*/ ) ); 2016 } 2017 else 2018 { 2019 PA_ENSURE_( PaAsiHpi_StartStream( stream, 0 ) ); 2020 } 2021 2022 error: 2023 return result; 2024 } 2025 2026 2027 /** Stop HPI streams (input + output), either softly or abruptly. 2028 If abort is false, the function blocks until the output stream is drained, otherwise it 2029 stops immediately and discards data in the stream hardware buffers. 2030 2031 This function is safe to call from the callback engine thread as well as the main thread. 2032 2033 @param stream Pointer to stream struct 2034 2035 @param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done) 2036 2037 @return PortAudio error code 2038 2039 */ 2040 static PaError PaAsiHpi_StopStream( PaAsiHpiStream *stream, int abort ) 2041 { 2042 PaError result = paNoError; 2043 2044 assert( stream ); 2045 2046 /* Input channels */ 2047 if( stream->input ) 2048 { 2049 PA_ASIHPI_UNLESS_( HPI_InStreamReset( NULL, 2050 stream->input->hpiStream ), paUnanticipatedHostError ); 2051 } 2052 /* Output channels */ 2053 if( stream->output ) 2054 { 2055 if( !abort ) 2056 { 2057 /* Wait until HPI output stream is drained */ 2058 while( 1 ) 2059 { 2060 PaAsiHpiStreamInfo streamInfo; 2061 PaTime timeLeft; 2062 2063 /* Obtain number of samples waiting to be played */ 2064 PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &streamInfo ) ); 2065 /* Check if stream is drained */ 2066 if( (streamInfo.state != HPI_STATE_PLAYING) && 2067 (streamInfo.dataSize < stream->output->bytesPerFrame * PA_ASIHPI_MIN_FRAMES_) ) 2068 break; 2069 /* Sleep amount of time represented by remaining samples */ 2070 timeLeft = 1000.0 * streamInfo.dataSize / stream->output->bytesPerFrame 2071 / stream->baseStreamRep.streamInfo.sampleRate; 2072 Pa_Sleep( (long)ceil( timeLeft ) ); 2073 } 2074 } 2075 PA_ASIHPI_UNLESS_( HPI_OutStreamReset( NULL, 2076 stream->output->hpiStream ), paUnanticipatedHostError ); 2077 } 2078 2079 /* Report stream info for debugging purposes */ 2080 /* PaAsiHpi_StreamDump( stream ); */ 2081 2082 error: 2083 return result; 2084 } 2085 2086 2087 /** Stop or abort PortAudio stream. 2088 2089 This function is used to explicitly stop the PortAudio stream (via StopStream/AbortStream), 2090 as opposed to the situation when the callback finishes with a result other than paContinue. 2091 If a stream is in callback mode we will have to inspect whether the background thread has 2092 finished, or we will have to take it out. In either case we join the thread before returning. 2093 In blocking mode, we simply tell HPI to stop abruptly (abort) or finish buffers (drain). 2094 The PortAudio stream will be in the Stopped state after a call to this function. 2095 2096 Don't call this from the callback engine thread! 2097 2098 @param stream Pointer to stream struct 2099 2100 @param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done) 2101 2102 @return PortAudio error code 2103 */ 2104 static PaError PaAsiHpi_ExplicitStop( PaAsiHpiStream *stream, int abort ) 2105 { 2106 PaError result = paNoError; 2107 2108 /* First deal with the callback thread, cancelling and/or joining it if necessary */ 2109 if( stream->callbackMode ) 2110 { 2111 PaError threadRes; 2112 stream->callbackAbort = abort; 2113 if( abort ) 2114 { 2115 PA_DEBUG(( "Aborting callback\n" )); 2116 } 2117 else 2118 { 2119 PA_DEBUG(( "Stopping callback\n" )); 2120 } 2121 PA_ENSURE_( PaUnixThread_Terminate( &stream->thread, !abort, &threadRes ) ); 2122 if( threadRes != paNoError ) 2123 { 2124 PA_DEBUG(( "Callback thread returned: %d\n", threadRes )); 2125 } 2126 } 2127 else 2128 { 2129 PA_ENSURE_( PaAsiHpi_StopStream( stream, abort ) ); 2130 } 2131 2132 stream->state = paAsiHpiStoppedState; 2133 2134 error: 2135 return result; 2136 } 2137 2138 2139 /** Stop PortAudio stream. 2140 This blocks until the output buffers are drained. 2141 2142 @param s Pointer to PortAudio stream 2143 2144 @return PortAudio error code 2145 */ 2146 static PaError StopStream( PaStream *s ) 2147 { 2148 return PaAsiHpi_ExplicitStop( (PaAsiHpiStream *) s, 0 ); 2149 } 2150 2151 2152 /** Abort PortAudio stream. 2153 This discards any existing data in output buffers and stops the stream immediately. 2154 2155 @param s Pointer to PortAudio stream 2156 2157 @return PortAudio error code 2158 */ 2159 static PaError AbortStream( PaStream *s ) 2160 { 2161 return PaAsiHpi_ExplicitStop( (PaAsiHpiStream * ) s, 1 ); 2162 } 2163 2164 2165 /** Determine whether the stream is stopped. 2166 A stream is considered to be stopped prior to a successful call to StartStream and after 2167 a successful call to StopStream or AbortStream. If a stream callback returns a value other 2168 than paContinue the stream is NOT considered to be stopped (it is in CallbackFinished state). 2169 2170 @param s Pointer to PortAudio stream 2171 2172 @return Returns one (1) when the stream is stopped, zero (0) when the stream is running, or 2173 a PaErrorCode (which are always negative) if PortAudio is not initialized or an 2174 error is encountered. 2175 */ 2176 static PaError IsStreamStopped( PaStream *s ) 2177 { 2178 PaAsiHpiStream *stream = (PaAsiHpiStream*)s; 2179 2180 assert( stream ); 2181 return stream->state == paAsiHpiStoppedState ? 1 : 0; 2182 } 2183 2184 2185 /** Determine whether the stream is active. 2186 A stream is active after a successful call to StartStream(), until it becomes inactive either 2187 as a result of a call to StopStream() or AbortStream(), or as a result of a return value 2188 other than paContinue from the stream callback. In the latter case, the stream is considered 2189 inactive after the last buffer has finished playing. 2190 2191 @param s Pointer to PortAudio stream 2192 2193 @return Returns one (1) when the stream is active (i.e. playing or recording audio), 2194 zero (0) when not playing, or a PaErrorCode (which are always negative) 2195 if PortAudio is not initialized or an error is encountered. 2196 */ 2197 static PaError IsStreamActive( PaStream *s ) 2198 { 2199 PaAsiHpiStream *stream = (PaAsiHpiStream*)s; 2200 2201 assert( stream ); 2202 return stream->state == paAsiHpiActiveState ? 1 : 0; 2203 } 2204 2205 2206 /** Returns current stream time. 2207 This corresponds to the system clock. The clock should run continuously while the stream 2208 is open, i.e. between calls to OpenStream() and CloseStream(), therefore a frame counter 2209 is not good enough. 2210 2211 @param s Pointer to PortAudio stream 2212 2213 @return Stream time, in seconds 2214 */ 2215 static PaTime GetStreamTime( PaStream *s ) 2216 { 2217 return PaUtil_GetTime(); 2218 } 2219 2220 2221 /** Returns CPU load. 2222 2223 @param s Pointer to PortAudio stream 2224 2225 @return CPU load (0.0 if blocking interface is used) 2226 */ 2227 static double GetStreamCpuLoad( PaStream *s ) 2228 { 2229 PaAsiHpiStream *stream = (PaAsiHpiStream*)s; 2230 2231 return stream->callbackMode ? PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) : 0.0; 2232 } 2233 2234 /* --------------------------- Callback Interface --------------------------- */ 2235 2236 /** Exit routine which is called when callback thread quits. 2237 This takes care of stopping the HPI streams (either waiting for output to finish, or 2238 abruptly). It also calls the user-supplied StreamFinished callback, and sets the 2239 stream state to CallbackFinished if it was reached via a non-paContinue return from 2240 the user callback function. 2241 2242 @param userData A pointer to an open stream previously created with Pa_OpenStream 2243 */ 2244 static void PaAsiHpi_OnThreadExit( void *userData ) 2245 { 2246 PaAsiHpiStream *stream = (PaAsiHpiStream *) userData; 2247 2248 assert( stream ); 2249 2250 PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer ); 2251 2252 PA_DEBUG(( "%s: Stopping HPI streams\n", __FUNCTION__ )); 2253 PaAsiHpi_StopStream( stream, stream->callbackAbort ); 2254 PA_DEBUG(( "%s: Stoppage\n", __FUNCTION__ )); 2255 2256 /* Eventually notify user all buffers have played */ 2257 if( stream->baseStreamRep.streamFinishedCallback ) 2258 { 2259 stream->baseStreamRep.streamFinishedCallback( stream->baseStreamRep.userData ); 2260 } 2261 2262 /* Unfortunately both explicit calls to Stop/AbortStream (leading to Stopped state) 2263 and implicit stops via paComplete/paAbort (leading to CallbackFinished state) 2264 end up here - need another flag to remind us which is the case */ 2265 if( stream->callbackFinished ) 2266 stream->state = paAsiHpiCallbackFinishedState; 2267 } 2268 2269 2270 /** Wait until there is enough frames to fill a host buffer. 2271 The routine attempts to sleep until at least a full host buffer can be retrieved from the 2272 input HPI stream and passed to the output HPI stream. It will first sleep until enough 2273 output space is available, as this is usually easily achievable. If it is an output-only 2274 stream, it will also sleep if the hardware buffer is too full, thereby throttling the 2275 filling of the output buffer and reducing output latency. The routine then blocks until 2276 enough input samples are available, unless this will cause an output underflow. In the 2277 process, input overflows and output underflows are indicated. 2278 2279 @param stream Pointer to stream struct 2280 2281 @param framesAvail Returns the number of available frames 2282 2283 @param cbFlags Overflows and underflows indicated in here 2284 2285 @return PortAudio error code (only paUnanticipatedHostError expected) 2286 */ 2287 static PaError PaAsiHpi_WaitForFrames( PaAsiHpiStream *stream, unsigned long *framesAvail, 2288 PaStreamCallbackFlags *cbFlags ) 2289 { 2290 PaError result = paNoError; 2291 double sampleRate; 2292 unsigned long framesTarget; 2293 uint32_t outputData = 0, outputSpace = 0, inputData = 0, framesLeft = 0; 2294 2295 assert( stream ); 2296 assert( stream->input || stream->output ); 2297 2298 sampleRate = stream->baseStreamRep.streamInfo.sampleRate; 2299 /* We have to come up with this much frames on both input and output */ 2300 framesTarget = stream->bufferProcessor.framesPerHostBuffer; 2301 assert( framesTarget > 0 ); 2302 2303 while( 1 ) 2304 { 2305 PaAsiHpiStreamInfo info; 2306 /* Check output first, as this takes priority in the default full-duplex mode */ 2307 if( stream->output ) 2308 { 2309 PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) ); 2310 /* Wait until enough space is available in output buffer to receive a full block */ 2311 if( info.availableFrames < framesTarget ) 2312 { 2313 framesLeft = framesTarget - info.availableFrames; 2314 Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) ); 2315 continue; 2316 } 2317 /* Wait until the data in hardware buffer has dropped to a sensible level. 2318 Without this, the hardware buffer quickly fills up in the absence of an input 2319 stream to regulate its data rate (if data generation is fast). This leads to 2320 large latencies, as the AudioScience hardware buffers are humongous. 2321 This is similar to the default "Hardware Buffering=off" option in the 2322 AudioScience WAV driver. */ 2323 if( !stream->input && (stream->output->outputBufferCap > 0) && 2324 ( info.totalBufferedData > stream->output->outputBufferCap / stream->output->bytesPerFrame ) ) 2325 { 2326 framesLeft = info.totalBufferedData - stream->output->outputBufferCap / stream->output->bytesPerFrame; 2327 Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) ); 2328 continue; 2329 } 2330 outputData = info.totalBufferedData; 2331 outputSpace = info.availableFrames; 2332 /* Report output underflow to callback */ 2333 if( info.underflow ) 2334 { 2335 *cbFlags |= paOutputUnderflow; 2336 } 2337 } 2338 2339 /* Now check input side */ 2340 if( stream->input ) 2341 { 2342 PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) ); 2343 /* If a full block of samples hasn't been recorded yet, wait for it if possible */ 2344 if( info.availableFrames < framesTarget ) 2345 { 2346 framesLeft = framesTarget - info.availableFrames; 2347 /* As long as output is not disrupted in the process, wait for a full 2348 block of input samples */ 2349 if( !stream->output || (outputData > framesLeft) ) 2350 { 2351 Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) ); 2352 continue; 2353 } 2354 } 2355 inputData = info.availableFrames; 2356 /** @todo The paInputOverflow flag should be set in the callback containing the 2357 first input sample following the overflow. That means the block currently sitting 2358 at the fore-front of recording, i.e. typically the one containing the newest (last) 2359 sample in the HPI buffer system. This is most likely not the same as the current 2360 block of data being passed to the callback. The current overflow should ideally 2361 be noted in an overflow list of sorts, with an indication of when it should be 2362 reported. The trouble starts if there are several separate overflow incidents, 2363 given a big input buffer. Oh well, something to try out later... */ 2364 if( info.overflow ) 2365 { 2366 *cbFlags |= paInputOverflow; 2367 } 2368 } 2369 break; 2370 } 2371 /* Full-duplex stream */ 2372 if( stream->input && stream->output ) 2373 { 2374 if( outputSpace >= framesTarget ) 2375 *framesAvail = outputSpace; 2376 /* If input didn't make the target, keep the output count instead (input underflow) */ 2377 if( (inputData >= framesTarget) && (inputData < outputSpace) ) 2378 *framesAvail = inputData; 2379 } 2380 else 2381 { 2382 *framesAvail = stream->input ? inputData : outputSpace; 2383 } 2384 2385 error: 2386 return result; 2387 } 2388 2389 2390 /** Obtain recording, current and playback timestamps of stream. 2391 The current time is determined by the system clock. This "now" timestamp occurs at the 2392 forefront of recording (and playback in the full-duplex case), which happens later than the 2393 input timestamp by an amount equal to the total number of recorded frames in the input buffer. 2394 The output timestamp indicates when the next generated sample will actually be played. This 2395 happens after all the samples currently in the output buffer are played. The output timestamp 2396 therefore follows the current timestamp by an amount equal to the number of frames yet to be 2397 played back in the output buffer. 2398 2399 If the current timestamp is the present, the input timestamp is in the past and the output 2400 timestamp is in the future. 2401 2402 @param stream Pointer to stream struct 2403 2404 @param timeInfo Pointer to timeInfo struct that will contain timestamps 2405 */ 2406 static void PaAsiHpi_CalculateTimeInfo( PaAsiHpiStream *stream, PaStreamCallbackTimeInfo *timeInfo ) 2407 { 2408 PaAsiHpiStreamInfo streamInfo; 2409 double sampleRate; 2410 2411 assert( stream ); 2412 assert( timeInfo ); 2413 sampleRate = stream->baseStreamRep.streamInfo.sampleRate; 2414 2415 /* The current time ("now") is at the forefront of both recording and playback */ 2416 timeInfo->currentTime = GetStreamTime( (PaStream *)stream ); 2417 /* The last sample in the input buffer was recorded just now, so the first sample 2418 happened (number of recorded samples)/sampleRate ago */ 2419 timeInfo->inputBufferAdcTime = timeInfo->currentTime; 2420 if( stream->input ) 2421 { 2422 PaAsiHpi_GetStreamInfo( stream->input, &streamInfo ); 2423 timeInfo->inputBufferAdcTime -= streamInfo.totalBufferedData / sampleRate; 2424 } 2425 /* The first of the outgoing samples will be played after all the samples in the output 2426 buffer is done */ 2427 timeInfo->outputBufferDacTime = timeInfo->currentTime; 2428 if( stream->output ) 2429 { 2430 PaAsiHpi_GetStreamInfo( stream->output, &streamInfo ); 2431 timeInfo->outputBufferDacTime += streamInfo.totalBufferedData / sampleRate; 2432 } 2433 } 2434 2435 2436 /** Read from HPI input stream and register buffers. 2437 This reads data from the HPI input stream (if it exists) and registers the temp stream 2438 buffers of both input and output streams with the buffer processor. In the process it also 2439 handles input underflows in the full-duplex case. 2440 2441 @param stream Pointer to stream struct 2442 2443 @param numFrames On entrance the number of available frames, on exit the number of 2444 received frames 2445 2446 @param cbFlags Indicates overflows and underflows 2447 2448 @return PortAudio error code 2449 */ 2450 static PaError PaAsiHpi_BeginProcessing( PaAsiHpiStream *stream, unsigned long *numFrames, 2451 PaStreamCallbackFlags *cbFlags ) 2452 { 2453 PaError result = paNoError; 2454 2455 assert( stream ); 2456 if( *numFrames > stream->maxFramesPerHostBuffer ) 2457 *numFrames = stream->maxFramesPerHostBuffer; 2458 2459 if( stream->input ) 2460 { 2461 PaAsiHpiStreamInfo info; 2462 2463 uint32_t framesToGet = *numFrames; 2464 2465 /* Check for overflows and underflows yet again */ 2466 PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) ); 2467 if( info.overflow ) 2468 { 2469 *cbFlags |= paInputOverflow; 2470 } 2471 /* Input underflow if less than expected number of samples pitch up */ 2472 if( framesToGet > info.availableFrames ) 2473 { 2474 PaUtilZeroer *zeroer; 2475 PaSampleFormat inputFormat; 2476 2477 /* Never call an input-only stream with InputUnderflow set */ 2478 if( stream->output ) 2479 *cbFlags |= paInputUnderflow; 2480 framesToGet = info.availableFrames; 2481 /* Fill temp buffer with silence (to make up for missing input samples) */ 2482 inputFormat = PaAsiHpi_HpiToPaFormat( stream->input->hpiFormat.wFormat ); 2483 zeroer = PaUtil_SelectZeroer( inputFormat ); 2484 zeroer(stream->input->tempBuffer, 1, 2485 stream->input->tempBufferSize / Pa_GetSampleSize(inputFormat) ); 2486 } 2487 2488 /* Read block of data into temp buffer */ 2489 PA_ASIHPI_UNLESS_( HPI_InStreamReadBuf( NULL, 2490 stream->input->hpiStream, 2491 stream->input->tempBuffer, 2492 framesToGet * stream->input->bytesPerFrame), 2493 paUnanticipatedHostError ); 2494 /* Register temp buffer with buffer processor (always FULL buffer) */ 2495 PaUtil_SetInputFrameCount( &stream->bufferProcessor, *numFrames ); 2496 /* HPI interface only allows interleaved channels */ 2497 PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 2498 0, stream->input->tempBuffer, 2499 stream->input->hpiFormat.wChannels ); 2500 } 2501 if( stream->output ) 2502 { 2503 /* Register temp buffer with buffer processor */ 2504 PaUtil_SetOutputFrameCount( &stream->bufferProcessor, *numFrames ); 2505 /* HPI interface only allows interleaved channels */ 2506 PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 2507 0, stream->output->tempBuffer, 2508 stream->output->hpiFormat.wChannels ); 2509 } 2510 2511 error: 2512 return result; 2513 } 2514 2515 2516 /** Flush output buffers to HPI output stream. 2517 This completes the processing cycle by writing the temp buffer to the HPI interface. 2518 Additional output underflows are caught before data is written to the stream, as this 2519 action typically remedies the underflow and hides it in the process. 2520 2521 @param stream Pointer to stream struct 2522 2523 @param numFrames The number of frames to write to the output stream 2524 2525 @param cbFlags Indicates overflows and underflows 2526 */ 2527 static PaError PaAsiHpi_EndProcessing( PaAsiHpiStream *stream, unsigned long numFrames, 2528 PaStreamCallbackFlags *cbFlags ) 2529 { 2530 PaError result = paNoError; 2531 2532 assert( stream ); 2533 2534 if( stream->output ) 2535 { 2536 PaAsiHpiStreamInfo info; 2537 /* Check for underflows after the (potentially time-consuming) callback */ 2538 PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) ); 2539 if( info.underflow ) 2540 { 2541 *cbFlags |= paOutputUnderflow; 2542 } 2543 2544 /* Write temp buffer to HPI stream */ 2545 PA_ASIHPI_UNLESS_( HPI_OutStreamWriteBuf( NULL, 2546 stream->output->hpiStream, 2547 stream->output->tempBuffer, 2548 numFrames * stream->output->bytesPerFrame, 2549 &stream->output->hpiFormat), 2550 paUnanticipatedHostError ); 2551 } 2552 2553 error: 2554 return result; 2555 } 2556 2557 2558 /** Main callback engine. 2559 This function runs in a separate thread and does all the work of fetching audio data from 2560 the AudioScience card via the HPI interface, feeding it to the user callback via the buffer 2561 processor, and delivering the resulting output data back to the card via HPI calls. 2562 It is started and terminated when the PortAudio stream is started and stopped, and starts 2563 the HPI streams on startup. 2564 2565 @param userData A pointer to an open stream previously created with Pa_OpenStream. 2566 */ 2567 static void *CallbackThreadFunc( void *userData ) 2568 { 2569 PaError result = paNoError; 2570 PaAsiHpiStream *stream = (PaAsiHpiStream *) userData; 2571 int callbackResult = paContinue; 2572 2573 assert( stream ); 2574 2575 /* Cleanup routine stops streams on thread exit */ 2576 pthread_cleanup_push( &PaAsiHpi_OnThreadExit, stream ); 2577 2578 /* Start HPI streams and notify parent when we're done */ 2579 PA_ENSURE_( PaUnixThread_PrepareNotify( &stream->thread ) ); 2580 /* Buffer will be primed with silence */ 2581 PA_ENSURE_( PaAsiHpi_StartStream( stream, 0 ) ); 2582 PA_ENSURE_( PaUnixThread_NotifyParent( &stream->thread ) ); 2583 2584 /* MAIN LOOP */ 2585 while( 1 ) 2586 { 2587 PaStreamCallbackFlags cbFlags = 0; 2588 unsigned long framesAvail, framesGot; 2589 2590 pthread_testcancel(); 2591 2592 /** @concern StreamStop if the main thread has requested a stop and the stream has not 2593 * been effectively stopped we signal this condition by modifying callbackResult 2594 * (we'll want to flush buffered output). */ 2595 if( PaUnixThread_StopRequested( &stream->thread ) && (callbackResult == paContinue) ) 2596 { 2597 PA_DEBUG(( "Setting callbackResult to paComplete\n" )); 2598 callbackResult = paComplete; 2599 } 2600 2601 /* Start winding down thread if requested */ 2602 if( callbackResult != paContinue ) 2603 { 2604 stream->callbackAbort = (callbackResult == paAbort); 2605 if( stream->callbackAbort || 2606 /** @concern BlockAdaption: Go on if adaption buffers are empty */ 2607 PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) ) 2608 { 2609 goto end; 2610 } 2611 PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ )); 2612 /* There is still buffered output that needs to be processed */ 2613 } 2614 2615 /* SLEEP */ 2616 /* Wait for data (or buffer space) to become available. This basically sleeps and 2617 polls the HPI interface until a full block of frames can be moved. */ 2618 PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) ); 2619 2620 /* Consume buffer space. Once we have a number of frames available for consumption we 2621 must retrieve the data from the HPI interface and pass it to the PA buffer processor. 2622 We should be prepared to process several chunks successively. */ 2623 while( framesAvail > 0 ) 2624 { 2625 PaStreamCallbackTimeInfo timeInfo = {0, 0, 0}; 2626 2627 pthread_testcancel(); 2628 2629 framesGot = framesAvail; 2630 if( stream->bufferProcessor.hostBufferSizeMode == paUtilFixedHostBufferSize ) 2631 { 2632 /* We've committed to a fixed host buffer size, stick to that */ 2633 framesGot = framesGot >= stream->maxFramesPerHostBuffer ? stream->maxFramesPerHostBuffer : 0; 2634 } 2635 else 2636 { 2637 /* We've committed to an upper bound on the size of host buffers */ 2638 assert( stream->bufferProcessor.hostBufferSizeMode == paUtilBoundedHostBufferSize ); 2639 framesGot = PA_MIN( framesGot, stream->maxFramesPerHostBuffer ); 2640 } 2641 2642 /* Obtain buffer timestamps */ 2643 PaAsiHpi_CalculateTimeInfo( stream, &timeInfo ); 2644 PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags ); 2645 /* CPU load measurement should include processing activivity external to the stream callback */ 2646 PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); 2647 if( framesGot > 0 ) 2648 { 2649 /* READ FROM HPI INPUT STREAM */ 2650 PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) ); 2651 /* Input overflow in a full-duplex stream makes for interesting times */ 2652 if( stream->input && stream->output && (cbFlags & paInputOverflow) ) 2653 { 2654 /* Special full-duplex paNeverDropInput mode */ 2655 if( stream->neverDropInput ) 2656 { 2657 PaUtil_SetNoOutput( &stream->bufferProcessor ); 2658 cbFlags |= paOutputOverflow; 2659 } 2660 } 2661 /* CALL USER CALLBACK WITH INPUT DATA, AND OBTAIN OUTPUT DATA */ 2662 PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); 2663 /* Clear overflow and underflow information (but PaAsiHpi_EndProcessing might 2664 still show up output underflow that will carry over to next round) */ 2665 cbFlags = 0; 2666 /* WRITE TO HPI OUTPUT STREAM */ 2667 PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) ); 2668 /* Advance frame counter */ 2669 framesAvail -= framesGot; 2670 } 2671 PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot ); 2672 2673 if( framesGot == 0 ) 2674 { 2675 /* Go back to polling for more frames */ 2676 break; 2677 2678 } 2679 if( callbackResult != paContinue ) 2680 break; 2681 } 2682 } 2683 2684 /* This code is unreachable, but important to include regardless because it 2685 * is possibly a macro with a closing brace to match the opening brace in 2686 * pthread_cleanup_push() above. The documentation states that they must 2687 * always occur in pairs. */ 2688 pthread_cleanup_pop( 1 ); 2689 2690 end: 2691 /* Indicates normal exit of callback, as opposed to the thread getting killed explicitly */ 2692 stream->callbackFinished = 1; 2693 PA_DEBUG(( "%s: Thread %d exiting (callbackResult = %d)\n ", 2694 __FUNCTION__, pthread_self(), callbackResult )); 2695 /* Exit from thread and report any PortAudio error in the process */ 2696 PaUnixThreading_EXIT( result ); 2697 error: 2698 goto end; 2699 } 2700 2701 /* --------------------------- Blocking Interface --------------------------- */ 2702 2703 /* As separate stream interfaces are used for blocking and callback streams, the following 2704 functions can be guaranteed to only be called for blocking streams. */ 2705 2706 /** Read data from input stream. 2707 This reads the indicated number of frames into the supplied buffer from an input stream, 2708 and blocks until this is done. 2709 2710 @param s Pointer to PortAudio stream 2711 2712 @param buffer Pointer to buffer that will receive interleaved data (or an array of pointers 2713 to a buffer for each non-interleaved channel) 2714 2715 @param frames Number of frames to read from stream 2716 2717 @return PortAudio error code (also indicates overflow via paInputOverflowed) 2718 */ 2719 static PaError ReadStream( PaStream *s, 2720 void *buffer, 2721 unsigned long frames ) 2722 { 2723 PaError result = paNoError; 2724 PaAsiHpiStream *stream = (PaAsiHpiStream*)s; 2725 PaAsiHpiStreamInfo info; 2726 void *userBuffer; 2727 2728 assert( stream ); 2729 PA_UNLESS_( stream->input, paCanNotReadFromAnOutputOnlyStream ); 2730 2731 /* Check for input overflow since previous call to ReadStream */ 2732 PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) ); 2733 if( info.overflow ) 2734 { 2735 result = paInputOverflowed; 2736 } 2737 2738 /* NB Make copy of user buffer pointers, since they are advanced by buffer processor */ 2739 if( stream->bufferProcessor.userInputIsInterleaved ) 2740 { 2741 userBuffer = buffer; 2742 } 2743 else 2744 { 2745 /* Copy channels into local array */ 2746 userBuffer = stream->blockingUserBufferCopy; 2747 memcpy( userBuffer, buffer, sizeof (void *) * stream->input->hpiFormat.wChannels ); 2748 } 2749 2750 while( frames > 0 ) 2751 { 2752 unsigned long framesGot, framesAvail; 2753 PaStreamCallbackFlags cbFlags = 0; 2754 2755 PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) ); 2756 framesGot = PA_MIN( framesAvail, frames ); 2757 PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) ); 2758 2759 if( framesGot > 0 ) 2760 { 2761 framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot ); 2762 PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) ); 2763 /* Advance frame counter */ 2764 frames -= framesGot; 2765 } 2766 } 2767 2768 error: 2769 return result; 2770 } 2771 2772 2773 /** Write data to output stream. 2774 This writes the indicated number of frames from the supplied buffer to an output stream, 2775 and blocks until this is done. 2776 2777 @param s Pointer to PortAudio stream 2778 2779 @param buffer Pointer to buffer that provides interleaved data (or an array of pointers 2780 to a buffer for each non-interleaved channel) 2781 2782 @param frames Number of frames to write to stream 2783 2784 @return PortAudio error code (also indicates underflow via paOutputUnderflowed) 2785 */ 2786 static PaError WriteStream( PaStream *s, 2787 const void *buffer, 2788 unsigned long frames ) 2789 { 2790 PaError result = paNoError; 2791 PaAsiHpiStream *stream = (PaAsiHpiStream*)s; 2792 PaAsiHpiStreamInfo info; 2793 const void *userBuffer; 2794 2795 assert( stream ); 2796 PA_UNLESS_( stream->output, paCanNotWriteToAnInputOnlyStream ); 2797 2798 /* Check for output underflow since previous call to WriteStream */ 2799 PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) ); 2800 if( info.underflow ) 2801 { 2802 result = paOutputUnderflowed; 2803 } 2804 2805 /* NB Make copy of user buffer pointers, since they are advanced by buffer processor */ 2806 if( stream->bufferProcessor.userOutputIsInterleaved ) 2807 { 2808 userBuffer = buffer; 2809 } 2810 else 2811 { 2812 /* Copy channels into local array */ 2813 userBuffer = stream->blockingUserBufferCopy; 2814 memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->output->hpiFormat.wChannels ); 2815 } 2816 2817 while( frames > 0 ) 2818 { 2819 unsigned long framesGot, framesAvail; 2820 PaStreamCallbackFlags cbFlags = 0; 2821 2822 PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) ); 2823 framesGot = PA_MIN( framesAvail, frames ); 2824 PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) ); 2825 2826 if( framesGot > 0 ) 2827 { 2828 framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot ); 2829 PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) ); 2830 /* Advance frame counter */ 2831 frames -= framesGot; 2832 } 2833 } 2834 2835 error: 2836 return result; 2837 } 2838 2839 2840 /** Number of frames that can be read from input stream without blocking. 2841 2842 @param s Pointer to PortAudio stream 2843 2844 @return Number of frames, or PortAudio error code 2845 */ 2846 static signed long GetStreamReadAvailable( PaStream *s ) 2847 { 2848 PaError result = paNoError; 2849 PaAsiHpiStream *stream = (PaAsiHpiStream*)s; 2850 PaAsiHpiStreamInfo info; 2851 2852 assert( stream ); 2853 PA_UNLESS_( stream->input, paCanNotReadFromAnOutputOnlyStream ); 2854 2855 PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) ); 2856 /* Round down to the nearest host buffer multiple */ 2857 result = (info.availableFrames / stream->maxFramesPerHostBuffer) * stream->maxFramesPerHostBuffer; 2858 if( info.overflow ) 2859 { 2860 result = paInputOverflowed; 2861 } 2862 2863 error: 2864 return result; 2865 } 2866 2867 2868 /** Number of frames that can be written to output stream without blocking. 2869 2870 @param s Pointer to PortAudio stream 2871 2872 @return Number of frames, or PortAudio error code 2873 */ 2874 static signed long GetStreamWriteAvailable( PaStream *s ) 2875 { 2876 PaError result = paNoError; 2877 PaAsiHpiStream *stream = (PaAsiHpiStream*)s; 2878 PaAsiHpiStreamInfo info; 2879 2880 assert( stream ); 2881 PA_UNLESS_( stream->output, paCanNotWriteToAnInputOnlyStream ); 2882 2883 PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) ); 2884 /* Round down to the nearest host buffer multiple */ 2885 result = (info.availableFrames / stream->maxFramesPerHostBuffer) * stream->maxFramesPerHostBuffer; 2886 if( info.underflow ) 2887 { 2888 result = paOutputUnderflowed; 2889 } 2890 2891 error: 2892 return result; 2893 }