00001 /**********************************************************/ 00002 /*/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\*/ 00003 /* 00004 Copyright: 1995 - Grupo de Voz (DAET) ETSII/IT-Bilbao 00005 00006 Nombre fuente................ XSNDSB16.C 00007 Nombre paquete............... - 00008 Lenguaje fuente.............. C (Borland C/C++ 3.1) 00009 Estado....................... Completado 00010 Dependencia Hard/OS.......... SoundBlaster 16, DMA, IRQs... 00011 Codigo condicional........... - 00012 00013 Codificacion................. Borja Etxebarria 00014 00015 Version dd/mm/aa Autor Proposito de la edicion 00016 ------- -------- -------- ----------------------- 00017 1.0.1 23/05/96 Borja bug: case WAIT3 sin break final 00018 1.0.0 02/03/95 Borja Codificacion inicial. 00019 00020 ======================== Contenido ======================== 00021 Conversion AD/DA de 16 bits con Sound Blaster 16. 00022 00023 Metodo habitual de funcionamiento: 00024 00025 REPRODUCCION: 00026 00027 1. initialize() 00028 2. open() 00029 3. start() 00030 4. Repetir getblk(), rellenar bloque, addblk() mientras 00031 se tengan bloques. 00032 5. Ultimo mini-bloque: getblk(), rellenar bloque, addlastblk() 00033 6. Esperar a que termine la reproduccion: blocked() == TRUE 00034 7. close() 00035 00036 Opcionalmente se pueden pre-encolar buffers antes del start(). 00037 00038 GRABACION: 00039 00040 1. initialize() 00041 2. open() 00042 3. preencolar todos los bloques con addblk() 00043 4. start() 00044 5. Repetir getblk(), salvar bloque, addblk() mientras 00045 se tengan ganas. 00046 6. close() 00047 00048 Se puede hacer un stop() antes del close, pero no hace falta. 00049 Normalmente no es necesario usar addlastblk() en grabacion: 00050 uno deja de grabar cuando quiere y coge las muestras que 00051 quiera del ultimo bloque que le interese. 00052 .................... 00053 AD/DA 16 bit conversion using Sound Blaster 16 00054 00055 Usual programming sequence: 00056 00057 PLAYING SOUNDS: 00058 00059 1. initialize() 00060 2. open() 00061 3. start() 00062 4. repeat getblk(), fill the block, addblk() while you have 00063 audio blocks to play. 00064 5. Last mini-block: getblk(), fill block, addlastblk() 00065 6. Wait until playback is finisned: blocked() == TRUE 00066 7. close() 00067 00068 You can also pre-queue audio buffers before you start() the playback. 00069 00070 RECORDING SOUNDS: 00071 00072 1. initialize() 00073 2. open() 00074 3. queue all the available blocks using addblk() 00075 4. start() 00076 5. repeat getblk(), save block, addblk(), while you want. 00077 6. close() 00078 00079 You can send an stop() before close(), but it's not necessary. 00080 It's also not necessary to use addlastblk() in recording tasks, 00081 as you can stop recording whenever you want, keep as many samples 00082 as desired, and throw away the rest of them. 00083 =========================================================== */ 00084 /*/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\*/ 00085 /**********************************************************/ 00086 00087 #include <stdlib.h> 00088 00089 #include "tdef.h" 00090 #include "xalloc.h" 00091 #include "lmem.h" 00092 #include "dma.h" 00093 #include "dmabuff.h" 00094 #include "intrs.h" 00095 #include "sb.h" 00096 #include "blaster.h" 00097 00098 #include "xsnd.h" 00099 00100 /**********************************************************/ 00101 /* numero maximo de bloques por segundo. Hay 2 interrupciones 00102 por bloque!!! 00103 .................... 00104 Max. number of blocks to process per second. Each block triggers 00105 two interrups!! */ 00106 00107 #define MAX_BLK_PER_SEC 1000; 00108 00109 /**********************************************************/ 00110 00111 PRIVATE BOOL _initialized = FALSE; 00112 00113 PRIVATE UINT16 _baseport; 00114 PRIVATE UINT16 _irq; 00115 PRIVATE UINT16 _dmach; 00116 00117 PRIVATE BOOL _record; 00118 PRIVATE UINT16 _srate; 00119 PRIVATE BOOL _stereo; 00120 00121 PRIVATE IntrServiceFunc _oldint; 00122 00123 PRIVATE UINT16 _nblk; /* numero total de bloques */ 00124 PRIVATE UINT16 _dmablk; /* bloque en que esta el DMA */ 00125 PRIVATE UINT16 _qblk; /* bloques encolados */ 00126 PRIVATE UINT32 _blklen; /* longitud de bloque */ 00127 PRIVATE UINT16 _qtail; /* hay ultimo bloque encolado */ 00128 /* (0=no, 1=si, 2=si pero de long. 0) */ 00129 PRIVATE UINT16 _taillen; /* longitud de ultimo bloque */ 00130 PRIVATE phINT8 _buffptr; /* puntero a buffer DMA */ 00131 PRIVATE UINT32 _bufflmem; /* puntero a buffer DMA (puntero plano) */ 00132 PRIVATE pfVOID _buffmem; /* puntero a buffer completo reservado para DMA */ 00133 PRIVATE UINT16 _samplelen; /* bytes que ocupa una 'muestra' */ 00134 PRIVATE UINT16 _sblen; /* longitud a dar a la SB16 */ 00135 PRIVATE BOOL _inusrproc; /* TRUE cuando se esta en usrproc() */ 00136 PRIVATE UINT16 _overrun; /* cuenta numero de usrproc() no llamados */ 00137 PRIVATE UINT16 _rectoread; /* buffer pendientes de leer (en grabacion) */ 00138 00139 PRIVATE INT16 _status; 00140 00141 enum STATUS { AWAIT=1, WAIT1, WAIT1A, WAIT2, WAIT3, 00142 WAIT4, WAIT5, WAIT6, WAIT6A, BLOCK }; /* neg -> pausa */ 00143 00144 PRIVATE VOID (PTRF _usrproc) ( BOOL last ); 00145 00146 /**********************************************************/ 00147 00148 PRIVATE VOID INTERRUPT _newint( VOID ) 00149 { 00150 BOOL up = FALSE; 00151 00152 if ((sb_IRQ_stat(_baseport) & DSP_MASK_IRQ_DMA16) != 0) { 00153 switch (_status) { 00154 case WAIT1: 00155 if (_qblk>1) { 00156 sb_IRQ_ack_dma16(_baseport); /* SB ACK */ 00157 _status = WAIT2; 00158 } 00159 else if (!_qtail) { /* no hay nada encolado */ 00160 _status = WAIT1A; 00161 } 00162 else if (_qtail==1) { /* ultimo bloque > 0 */ 00163 sb_DSP_16(_baseport, _record, SB_DSP_SINGLE_CICLE, 00164 SB_DSP_FIFO_ON, _stereo, SB_DSP_SIGNED, _taillen); 00165 sb_IRQ_ack_dma16(_baseport); /* SB ACK */ 00166 _status = WAIT3; 00167 } 00168 else { /* ultimo bloque = 0 bytes */ 00169 sb_DSP_16_exit(_baseport); 00170 sb_IRQ_ack_dma16(_baseport); /* SB ACK */ 00171 _status = WAIT4; 00172 } 00173 break; 00174 case WAIT2: 00175 sb_IRQ_ack_dma16(_baseport); /* SB ACK */ 00176 _qblk--; 00177 if ((++_dmablk)==_nblk) 00178 _dmablk = 0; 00179 _status = WAIT1; 00180 up = TRUE; 00181 break; 00182 case WAIT3: 00183 sb_IRQ_ack_dma16(_baseport); /* SB ACK */ 00184 _qblk--; 00185 _status = WAIT4; 00186 up = TRUE; 00187 break; 00188 case WAIT4: 00189 sb_IRQ_ack_dma16(_baseport); /* SB ACK */ 00190 _status = BLOCK; 00191 up = TRUE; 00192 break; 00193 case WAIT5: 00194 sb_DSP_16_exit(_baseport); 00195 sb_IRQ_ack_dma16(_baseport); /* SB ACK */ 00196 _status = WAIT4; 00197 up = TRUE; 00198 break; 00199 case WAIT6: 00200 sb_IRQ_ack_dma16(_baseport); /* SB ACK */ 00201 _status = WAIT6A; 00202 up = TRUE; 00203 break; 00204 case WAIT6A: 00205 sb_DSP_16_exit(_baseport); 00206 sb_IRQ_ack_dma16(_baseport); /* SB ACK */ 00207 _status = WAIT4; 00208 break; 00209 } 00210 PIC_SEND_EOI(_irq); /* ack para PIC */ 00211 if (_usrproc && up) { 00212 if (_inusrproc) 00213 _overrun++; 00214 else { 00215 _inusrproc = TRUE; 00216 _usrproc(_status==BLOCK); 00217 _inusrproc = FALSE; 00218 } 00219 } 00220 } 00221 else 00222 _oldint(); 00223 } 00224 00225 /**********************************************************/ 00226 /* Reset de estado. Detiene la conversion (si esta en marcha) 00227 y reinicializa todo de nuevo. {devuelve} !=0 en caso de error. 00228 .................... 00229 Reset status. This function stops the conversion process (if 00230 active) and initializes the system. {returs}!=0 if error. */ 00231 00232 UINT16 xsnd16_reset( VOID ) 00233 { 00234 sb_DSP_16_pause(_baseport); 00235 00236 _status = -AWAIT; 00237 _dmablk = 0; 00238 _qblk = 0; //(_record?_nblk:0); 00239 _qtail = 0; 00240 _overrun = 0; 00241 _rectoread = 0; 00242 00243 /* programa DMA */ 00244 dma_set(_dmach, DMA_INCREMENT, DMA_AUTO_INIT, 00245 _record?DMA_WRITE_TO_MEM:DMA_READ_FROM_MEM, 00246 _bufflmem, (UINT16)((DMA_IS_DMA16(_dmach)? 00247 _samplelen/2:_samplelen)*_nblk*_blklen - 1), DMA_ENABLE); 00248 00249 return 0; 00250 } 00251 00252 /**********************************************************/ 00253 /* Prepara el modulo de sonido. 00254 {record} es TRUE para grabar o FALSE para reproducir (alias 00255 XSND_RECORD y XSND_PLAY). 00256 {srate} es la frecuencia de muestreo en Hz (5000 a 44100). 00257 {stereo} es FALSE para mono, TRUE para estereo (alias XSND_MONO 00258 y XSND_STEREO). 00259 {blklen} es la longitud de la unidad de conversion (en muestras 00260 de 16bits para mono, o 'muestras' de 32bits para estereo). El 00261 sistema graba/reproduce a golpe de esta unidad. Si es muy corta, 00262 se sobrecarga al sistema. Si es muy larga, es poco 'agil'. Bloques 00263 que duren 1/10 de segundo estan bien (por ejemplo, 800 muestras 00264 a 8kHz). Tiene que ser un valor par de muestras! 00265 {numblk} es el numero de bloques de longitud {blklen} que el sistema 00266 puede manejar simultaneamente. Menos de 3 puede producir 'clics' en 00267 el sonido. Cuantos mas se pongan, mas seguro, pero tampoco es 00268 necesario un numero excesivo, que ocupa memoria. 4 va bien. 00269 Tambien se puede usar {numblk}=0, que se gestiona de manera especial, 00270 reservando tantos bloques como permita la memoria disponible. 00271 {usrproc} es un puntero a un call-back de usuario que recibe un 00272 parametro BOOL {last}. Este call-back es llamado cada vez que el 00273 sistema llena/vacia un bloque de muestras. Cuando se llena/vacia 00274 el ultimo bloque de muestras, el parametro {last} vale TRUE. El 00275 resto del tiempo, vale FALSE. Si se envia {usrproc}=NULL, entonces 00276 no se llama al call-back de usuario. 00277 Esta funcion {devuelve}!=0 en caso de error (parametros no validos, 00278 no hay memoria, etc). 00279 .................... 00280 Configure the audio control module. 00281 {record} is TRUE to record, or FALSE to playback (you can use 00282 the defines XSND_RECORD y XSND_PLAY). 00283 {srate} is the sampling rate in Hz (5000 a 44100). 00284 {stereo} is FALSE for mono, TRUE for stereo (alias XSND_MONO 00285 y XSND_STEREO). 00286 {blklen} is the basic block length (as 16 bits samples for mono, 00287 or 32 bits 'samples' for stereo). The system will play/record 00288 blocks of this size. If this length is too short, there is quite 00289 a lot system overhead. If this length is too long, you'll get long 00290 processing delay but little system overhead. A 1/10 sec. block 00291 length is a good choice (i.e. 800 samples at 8kHz samplerate). 00292 This value MUST be an EVEN number of samples!!!! 00293 {numblk} is the number of blocks ({blklen} 'samples' each) that the 00294 system will buffer. One or two blocks may produce audible 'clicks'. 00295 The more blocks you use, the safer and smoother the process, but it 00296 will eat more memory. A value of 4 is right. You can also 00297 use {numblk}=0, that will auto-select as many blocks as memory 00298 available in your system (max. = 128kbytes). 00299 {usrproc} is a pointer to a user call-back. This callback receives 00300 a BOOL {last} argument. The callback is called whenever the system 00301 fills/frees a basic block. When the last audio block is filled/freed, 00302 the {last} parameter is TRUE, or FALSE otherwise. 00303 You can send {usrproc}=NULL to disable the callback. 00304 00305 This function {returns}!=0 if error (invalid argument, no memory...). */ 00306 00307 UINT16 xsnd16_open( BOOL record, UINT16 srate, BOOL stereo, 00308 UINT32 blklen, UINT16 numblk, VOID (PTRF usrproc) ( BOOL last) ) 00309 { 00310 UINT32 maxlen, minlen, bufflen; 00311 UINT32 len, use; 00312 00313 if (_initialized) /* si ya esta inicializada, indica error */ 00314 return 1; /* ya inicializado */ 00315 00316 /* lee configuracion de entorno BLASTER */ 00317 _baseport = blaster_env(BLASTER_BASEPORT, 00318 BLASTER_DEFAULT_BASEPORT, BLASTER_DECHEX_BASEPORT); 00319 _irq = blaster_env(BLASTER_IRQ, 00320 BLASTER_DEFAULT_IRQ, BLASTER_DECHEX_IRQ); 00321 _dmach = blaster_env(BLASTER_DMA16, 00322 BLASTER_DEFAULT_DMA16,BLASTER_DECHEX_DMA16); 00323 00324 if (record) { 00325 if (sb_DSP_in_rate(_baseport,srate)) 00326 return 1; /* error escribiendo srate */ 00327 } 00328 else 00329 if (sb_DSP_out_rate(_baseport,srate)) 00330 return 1; /* error escribiendo srate */ 00331 00332 _record = record; 00333 _stereo = stereo; 00334 _srate = srate; 00335 _blklen = blklen; 00336 _usrproc = usrproc; 00337 _inusrproc = FALSE; 00338 00339 _samplelen = ( stereo ? 4 : 2 ); 00340 maxlen = (DMA_IS_DMA16(_dmach)?1024L*128L:1024L*64L)/_samplelen; 00341 minlen = _srate/MAX_BLK_PER_SEC; 00342 00343 if (_blklen>maxlen) 00344 return 1; /* bloque muy grande */ 00345 if (_blklen<minlen) 00346 return 1; /* bloque muy pequeno */ 00347 if (_blklen<2) 00348 return 1; /* bloque MUY pequeno */ 00349 if (_blklen % 2) 00350 return 1; /* bloque no multiplo de 2 */ 00351 00352 _sblen = (UINT16)((stereo?_blklen:_blklen/2)-1); 00353 _nblk = (UINT16)(numblk ? numblk : maxlen/_blklen); 00354 00355 do { 00356 bufflen = _blklen*_nblk; 00357 if (bufflen > maxlen) 00358 return 1; /* demasiados bloques */ 00359 _buffmem = dmabuff_malloc(bufflen*_samplelen, 00360 DMA_IS_DMA16(_dmach)?128:64, DMA_IS_DMA16(_dmach)?2:1, 00361 blklen*_samplelen,&len); 00362 if (_buffmem==NULL) { /* no se pudo reservar memoria */ 00363 /* si no se fijo el n. bloques, reduce */ 00364 if ((!numblk) && (_nblk>1)) 00365 _nblk--; 00366 else 00367 return 1; /* no hay memoria para DMA */ 00368 } 00369 } while (_buffmem==NULL); 00370 00371 _buffptr = (pfINT8)fixmem(_buffmem, len, DMA_IS_DMA16(_dmach)?128:64, 00372 DMA_IS_DMA16(_dmach)?2:1, blklen*_samplelen, &_bufflmem, &use); 00373 00374 xsnd16_reset(); 00375 /* programa rutina servicio interrupcion */ 00376 _oldint = INT_GET_VEC(IRQ_INTN(_irq)); 00377 INT_SET_VEC(IRQ_INTN(_irq),_newint); 00378 PIC_ENABLE(_irq); /* conecta IRQ */ 00379 PIC_SEND_EOI(_irq); 00380 00381 _initialized = TRUE; 00382 00383 return 0; /* no hay error */ 00384 } 00385 00386 /**********************************************************/ 00387 /* detiene la grabacion/reproduccion y libera los recursos 00388 utilizados (memoria, interrupciones, sound blaster...). 00389 {devuelve}!=0 en caso de error 00390 .................... 00391 stops the record/playback process and frees all the allocated 00392 resources (memory, interrupts, sb16...). 00393 {returns}!=0 if error. */ 00394 00395 UINT16 xsnd16_close( VOID ) 00396 { 00397 if (!_initialized) 00398 return 1; /* ni siquiera estaba abierto */ 00399 00400 sb_DSP_16_pause(_baseport); 00401 00402 /* desconecta DMA */ 00403 dma_disable(_dmach); 00404 00405 /* $$ nada de desconectar, que puede haber otros */ 00406 /* 00407 PIC_DISABLE(_irq); 00408 */ 00409 INT_SET_VEC(IRQ_INTN(_irq),_oldint); 00410 00411 xfree(_buffmem); 00412 00413 _initialized = FALSE; 00414 00415 return 0; 00416 } 00417 00418 /**********************************************************/ 00419 /* Pone en marcha la grabacion/reproduccion. 00420 {devuelve}!=0 en caso de error. 00421 .................... 00422 Starts the record/play process. 00423 {returns}!=0 if error. */ 00424 00425 UINT16 xsnd16_start( VOID ) 00426 { 00427 if (_status>AWAIT) 00428 return 1; /* ya esta en marcha */ 00429 if (_status<0) 00430 _status = -_status; 00431 00432 if (_status>AWAIT) /* estaba en pausa */ 00433 sb_DSP_16_continue(_baseport); 00434 else if (_qblk) { 00435 _status=WAIT1; 00436 sb_DSP_16(_baseport, _record, SB_DSP_AUTO_INIT, 00437 SB_DSP_FIFO_ON, _stereo, SB_DSP_SIGNED, _sblen); 00438 } 00439 else if (_qtail==1) { /* ultimo blk > 0 */ 00440 _status=WAIT4; 00441 sb_DSP_16(_baseport, _record, SB_DSP_SINGLE_CICLE, 00442 SB_DSP_FIFO_ON, _stereo, SB_DSP_SIGNED, _taillen); 00443 } 00444 else if (_qtail==2) { /* ultimo blk = 0 */ 00445 _status=BLOCK; 00446 } 00447 00448 return 0; 00449 } 00450 00451 /**********************************************************/ 00452 /* detiene la grabacion/reproduccion. 00453 {devuelve}!=0 en caso de error. 00454 .................... 00455 Stops the record/play process. 00456 {returns}!=0 if error. */ 00457 00458 UINT16 xsnd16_stop( VOID ) 00459 { 00460 sb_DSP_16_pause(_baseport); 00461 if (_status<0) 00462 return 1; /* ya estaba en pausa */ 00463 00464 _status = -_status; 00465 return 0; 00466 } 00467 00468 /**********************************************************/ 00469 /* Si el usrproc() dura mucho tiempo, puede suceder que se 00470 produzca una nueva interrupcion antes de que termine (o sea, 00471 se produce overrun). NO se llamara de nuevo a usrproc, para 00472 evitar problemas de reentrancia, pero se incrementara un 00473 contador interno para que quede reflejada la situacion. Esta 00474 funcion {devuelve} el numero de overruns (numero de bloques 00475 llenos/vacios que producen interrupcion pero no generan llamada al 00476 usrproc) desde la ultima vez que se llamo a esta misma funcion. 00477 La funcion reinicializa a cero el contador interno. 00478 .................... 00479 If the usrproc() function is too long, it could happen that a 00480 new interrupt arrives before this function ends (i.e. you get 00481 an overrun). The usrproc() will no be called again for this 00482 new interrupt to avoid reentrancy problems, but an internal 00483 counter will be incremented to reflect this fact. 00484 This function {returns} the number of overruns (number of 00485 memory blocks filled/emptyed with no associated call to 00486 usrproc()) since the last time we called this function 00487 (the function resets the internal counter). */ 00488 00489 UINT16 xsnd16_getoverrun( VOID ) 00490 { 00491 UINT16 or; 00492 00493 INT_DISABLE(); 00494 or = _overrun; 00495 _overrun = 0; 00496 INT_ENABLE(); 00497 00498 return or; 00499 } 00500 00501 /**********************************************************/ 00502 /* {devuelve} un puntero al bloque numero {nblk}. 00503 Normalmente no es necesario utilizar esta funcion. 00504 .................... 00505 {returns} a pointer to memory block number {nblk}. Normally 00506 it's not necessary to use this function at all */ 00507 00508 pfINT16 xsnd16_getblknum( UINT16 nblk ) 00509 { 00510 return (pfINT16)(_buffptr + (_samplelen*_blklen*nblk)); 00511 } 00512 00513 /**********************************************************/ 00514 /* {devuelve} el numero de bloques que mantiene el sistema. 00515 Normalmente no es necesario utilizar esta funcion. 00516 .................... 00517 {returns} the number of memory blocks kept by the module. 00518 Normally it's not necessary to use this function at all. */ 00519 00520 UINT16 xsnd16_getnblk( VOID ) 00521 { 00522 return _nblk; 00523 } 00524 00525 /**********************************************************/ 00526 /* esta funcion {devuelve} un puntero al siguiente bloque 00527 que el usuario debe procesar, o NULL en caso de no ser 00528 posible. 00529 En reproduccion, el usuario obtiene un puntero al proximo 00530 bloque que debe rellenar. Si no quedan bloques libre que 00531 el usuario pueda rellenar, la funcion {devuelve} NULL. 00532 En grabacion, el usuario obtiene un puntero al siguente 00533 bloque que debe leer. Si aun no hay bloques 00534 llenos, {devuelve} NULL. 00535 00536 El funcionamiento es algo diferente en grabacion que en 00537 reproduccion: reproduciendo, es posible llamar muchas veces 00538 a ???_getblk(), que siempre {devuelve} un puntero al *mismo* 00539 buffer mientras no se reencole con ???_addblk(). Por el 00540 contrario, en grabacion, solo se puede llamar una vez a 00541 ???_getblk() para obtener un buffer rellenado. Una vez obtenido 00542 el puntero al buffer, si llamamos de nuevo a ???_getblk() 00543 obtendremos un puntero al *siguiente* buffer lleno (NULL si 00544 aun no esta disponible) y asi sucesivamente. En reproduccion, 00545 podemos llamar a ???_addblk() para reencolar un buffer sin siquiera 00546 haber hecho primero un ???_getblk(). En cambio, en grabacion no 00547 es posible reencolar un buffer con ???_addblk (o ???_addlastblk) 00548 a no ser que primero se haya llamado a ???_getblk(). De este modo, 00549 no es posible reencolar un buffer hasta que el usuario no lo haya 00550 leido. 00551 .................... 00552 This function {returs} a pointer to the next memory block that the 00553 user must process, or NULL if not possible. 00554 On playback the user gets a pointer to the next block that he must 00555 fill. If there is no more blocks free, it {returns} NULL. 00556 On record the user gets a pointer to the next block that he must 00557 read (save). If there is no more filled block yet, it {returns} NULL. 00558 00559 The process is a bit different during recording than during 00560 playback: On playback, you can call lot's of time to ???_getblk(), 00561 as it will always {return} a pointer to the *same* buffer as 00562 long as it is not re-queued using ???_addblk(). But on recording, 00563 you can just call once to ???_getblk() to get a filled buffer. 00564 Once you get the pointer to the buffer, if we call again ???_getblk() 00565 we'll get a pointer to the *next* filled buffer (NULL if not available 00566 yet), and so on. 00567 During playback we can call ???_addblk() to re-queue a buffer even 00568 if we have not issued a ???_getblk() first. But on recording it 00569 is not possible to queue a buffer using ???_addblk (or ???_addlastblk) 00570 if you don't call first to ???_getblk(). In this way, it is not possible 00571 to queue a buffer until you read it first. */ 00572 00573 pfINT16 xsnd16_getblk( VOID ) 00574 { 00575 UINT16 n; 00576 00577 if (_record) { /* grabando */ 00578 if (!_qtail) { /* normalmente */ 00579 if (_rectoread==_qblk) /* y aun no hay bloques llenos */ 00580 return NULL; 00581 INT_DISABLE(); 00582 n = _dmablk+_qblk-_rectoread; 00583 INT_ENABLE(); 00584 } 00585 else { /* grabando con ultimo bloque encolado */ 00586 if (_status!=BLOCK) { /* aun no terminado */ 00587 if (_rectoread-1==_qblk) 00588 return NULL; 00589 INT_DISABLE(); 00590 n = _dmablk+_qblk-_rectoread+1; 00591 INT_ENABLE(); 00592 } 00593 else { /* _status==BLOCK */ 00594 if (!_rectoread) /* ya no hay mas */ 00595 return NULL; 00596 n = _dmablk; 00597 } 00598 } 00599 _rectoread--; 00600 if (n>=_nblk) /* realmente <0 */ 00601 n += _nblk; 00602 } 00603 else { /* reproduciendo */ 00604 if ((_qblk==_nblk)||(_qtail)) /* no hay bloques */ 00605 return NULL; 00606 INT_DISABLE(); 00607 n = _dmablk+_qblk; 00608 INT_ENABLE(); 00609 if (n>=_nblk) 00610 n -= _nblk; 00611 } 00612 00613 return (pfINT16)(_buffptr + (n*_blklen*_samplelen)); 00614 } 00615 00616 /**********************************************************/ 00617 /* esta funcion encola el siguente bloque, o {devuelve}!=0 00618 si no es posible. 00619 En reproduccion, el usuario obtiene el siguente bloque 00620 a rellenar llamando a la funcion ???_getblk(), rellena el 00621 bloque, y lo re-encola con esta funcion. 00622 En grabacion, el usuario obtiene el siguente bloque 00623 a leer llamando a la funcion ???_getblk(), salva el 00624 contenido del bloque, y lo re-encola con esta funcion. 00625 00626 Ver comentarios en ???_getblk() sobre diferencias entre 00627 grabacion y reproduccion 00628 .................... 00629 This function re-queues the next block, or {returns}!=0 00630 if it's not possible. 00631 During playback, the user gets the next block to fill calling 00632 to ???_getblk(), fills the block, and re-queue it with this 00633 function. 00634 During record, the user gets the next block to read calling 00635 to ???_getblk(), saves the block contents, and re-queu it wint 00636 this function. */ 00637 00638 UINT16 xsnd16_addblk( VOID ) 00639 { 00640 if (_record) { /* record */ 00641 if ((_nblk==_rectoread)||(_qtail)) /* si no quedan bloques */ 00642 return 1; 00643 } 00644 else { /* play */ 00645 if ((_qblk==_nblk)||(_qtail)) /* si no quedan bloques */ 00646 return 1; 00647 } 00648 00649 INT_DISABLE(); 00650 if (_record) 00651 _rectoread++; 00652 _qblk++; 00653 if (_status==AWAIT) 00654 xsnd16_start(); 00655 else if (abs(_status)==WAIT1A) { 00656 _status = (_status<0)?-WAIT2:WAIT2; 00657 sb_IRQ_ack_dma16(_baseport); /* SB ACK */ 00658 } 00659 INT_ENABLE(); 00660 00661 return 0; 00662 } 00663 00664 /**********************************************************/ 00665 /* Esta funcion permite encolar un ultimo bloque de longitud 00666 inferior a la habitual. Una vez encolado este bloque el 00667 sistema ya no admite encolar ningun bloque mas, y detiene 00668 la grabacion/reproduccion. El sistema quedara bloqueado 00669 al terminar la grabacion/reproduccion, y para volver a grabar 00670 o reproducir, hay que resetear con ????_reset(). 00671 En caso de error {devuelve}!=0. 00672 En grabacion no tiene mucho sentido, pero bueno, se puede hacer. 00673 En reproduccion, sirve para encolar el ultimo grupo de muestras, 00674 que no dan para rellenar un buffer completo. En {len} se envia 00675 la longitud utilizada. 00676 .................... 00677 This function allows to queue a final block with shorter length 00678 that the normal block length. Once this block is queued you will 00679 not be able to queue any more blocks. When this block is processed, 00680 the record/playback process is stopped and the system will block. 00681 To record/play again, you must reset using ???_reset(). 00682 {returns}!= if error. 00683 On recording, this function is not very usefull. 00684 On playback, this function is necessary to queue those final samples 00685 you have that are not enough to fill a complete block. In {len} you 00686 send the number of 'samples' you want to queue. */ 00687 00688 UINT16 xsnd16_addlastblk( UINT32 len ) 00689 { 00690 UINT16 i, n; 00691 pfVOID p; 00692 INT16 i16; 00693 UINT32 u32; 00694 00695 if (_record) { /* record */ 00696 if ((_nblk==_rectoread)||(_qtail)) /* si no quedan bloques */ 00697 return 1; 00698 } 00699 else { /* play */ 00700 if ((_qblk==_nblk)||(_qtail)) /* si no quedan bloques */ 00701 return 1; 00702 } 00703 00704 INT_DISABLE(); 00705 if (_record) 00706 _rectoread++; 00707 _qtail = (len>0)?1:2; 00708 _taillen = (UINT16)((_stereo?len*2:len)-1); 00709 if (_status==AWAIT) 00710 xsnd16_start(); 00711 else if (abs(_status)==WAIT1A) { 00712 sb_IRQ_ack_dma16(_baseport); /* SB ACK */ 00713 if (len<=_blklen/2) { /* 1 semi bloque */ 00714 _status = (_status<0)?-WAIT5:WAIT5; 00715 if (_record) { 00716 len=_blklen/2; 00717 n = (UINT16)(_blklen-1); 00718 } 00719 else 00720 n = (UINT16)(_blklen/2-1); 00721 p = _buffptr + (((_dmablk+_qblk) % _nblk) * _blklen*_samplelen); 00722 if (_stereo) { 00723 if (_record) 00724 u32 = 0; 00725 else if (len) 00726 u32 = ((pfINT32)p)[(UINT16)(len-1)]; 00727 else 00728 u32 = ((pfINT32)(_buffptr+(((_dmablk+_qblk-1)%_nblk)* 00729 _blklen*_samplelen)))[(UINT16)(_blklen-1)]; 00730 for (i=(UINT16)len; i<=n; i++) 00731 ((pfINT32)p)[i]=u32; 00732 } 00733 else { /* mono */ 00734 if (_record) 00735 i16 = 0; 00736 else if (len) 00737 i16 = ((pfINT16)p)[(UINT16)(len-1)]; 00738 else 00739 i16 = ((pfINT16)(_buffptr+(((_dmablk+_qblk-1)%_nblk)* 00740 _blklen*_samplelen)))[(UINT16)(_blklen-1)]; 00741 for (i=(UINT16)len; i<=n; i++) 00742 ((pfINT16)p)[i]=i16; 00743 } 00744 } 00745 else { /* 2 semi bloques */ 00746 _status = (_status<0)?-WAIT6:WAIT6; 00747 if ((!_record)&&(len<_blklen)) { 00748 n = (UINT16)(_blklen-1); 00749 p = _buffptr+(((_dmablk+_qblk) % _nblk)*_blklen*_samplelen); 00750 if (_stereo) { 00751 u32 = ((pfINT32)p)[(UINT16)(len-1)]; 00752 for (i=(UINT16)len; i<=n; i++) 00753 ((pfINT32)p)[i]=u32; 00754 } 00755 else { /* mono */ 00756 i16 = ((pfINT16)p)[(UINT16)(len-1)]; 00757 for (i=(UINT16)len; i<=n; i++) 00758 ((pfINT16)p)[i]=i16; 00759 } 00760 } 00761 } 00762 } 00763 INT_ENABLE(); 00764 00765 return 0; 00766 } 00767 00768 /**********************************************************/ 00769 /* {devuelve} TRUE si es sistema esta detenido (o en pausa). 00770 .................... 00771 {returns} TRUE if the system is stopped (or paused) */ 00772 00773 BOOL xsnd16_stopped( VOID ) 00774 { 00775 return (_status<0); 00776 } 00777 00778 /**********************************************************/ 00779 /* {devuelve} TRUE si el sistema ha terminado de grabar/reproducir 00780 el ultimo bloque y ha entrado en bloqueo. 00781 .................... 00782 {returns} TRUE if the system has already finished recorded/played 00783 the last block and is in blocked state. */ 00784 00785 BOOL xsnd16_blocked( VOID ) 00786 { 00787 return (_status==BLOCK); 00788 } 00789 00790 /**********************************************************/ 00791 /* Configura la frecuencia de muestro {srate}. 00792 {devuelve}!=0 en caso de error. 00793 .................... 00794 Configures the sampling rate to {srate}. 00795 {returns}!=0 if error. */ 00796 00797 UINT16 xsnd16_srate( UINT16 srate ) 00798 { 00799 if (!_initialized) 00800 return 1; /* ni siquiera estaba abierto */ 00801 00802 _srate = srate; 00803 if (_record) { 00804 if (sb_DSP_in_rate(_baseport,srate)) 00805 return 1; /* error escribiendo srate */ 00806 } 00807 else 00808 if (sb_DSP_out_rate(_baseport,srate)) 00809 return 1; /* error escribiendo srate */ 00810 00811 return 0; 00812 } 00813 00814 /**********************************************************/ 00815 /* {devuelve} la frecuencia de muestreo en uso. 00816 .................... 00817 {returns} the used sampling rate. */ 00818 00819 DOUBLE xsnd16_getsrate( VOID ) 00820 { 00821 return sb_DSP_tc2dsr(sb_DSP_sr2tc(_srate,(_stereo?2:1)), (_stereo?2:1)); 00822 } 00823 00824 /**********************************************************/ 00825 /* inicializacion global del sistema audio. Ejecutar 00826 antes que ninguna otra funcion. {devuelve}!=0 en caso 00827 de error 00828 .................... 00829 Global system initialization. This must be called before 00830 any other xsnd function. It {returns}!=0 if error */ 00831 00832 UINT16 xsnd16_initialize( VOID ) 00833 { 00834 return sb_DSP_reset( blaster_env(BLASTER_BASEPORT, 00835 BLASTER_DEFAULT_BASEPORT, BLASTER_DECHEX_BASEPORT)); 00836 } 00837 00838 /**********************************************************/ 00839 /* {devuelve} el numero de bloques actualmente encolados 00840 para reproduccion o grabacion. El bloque en curso tambien 00841 se considera. 00842 .................... 00843 {returns} the current number of queued blocks for playback/record. 00844 The block being played/recorded right now is also considered. */ 00845 00846 UINT16 xsnd16_getqblk( VOID ) 00847 { 00848 if (_status==BLOCK) return 0; 00849 return _qblk+(_qtail?1:0); 00850 } 00851 00852 /**********************************************************/