1 /* 2 * snd_android.c 3 * Android-specific sound interface 4 * 5 */ 6 7 #include "quakedef.h" 8 9 #include <pthread.h> 10 #include <time.h> 11 #include <math.h> 12 #include <stdlib.h> 13 #include <unistd.h> 14 15 #include <android/log.h> 16 #include <SLES/OpenSLES.h> 17 18 #define LOG_TAG "Quake snd_android" 19 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 20 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 21 22 const size_t SAMPLE_RATE = 11025; 23 24 25 const size_t BYTES_PER_SAMPLE = 2; 26 const size_t CHANNEL_COUNT = 2; 27 const size_t BITS_PER_SAMPLE = 8 * BYTES_PER_SAMPLE; 28 29 const size_t TOTAL_BUFFER_SIZE = 4 * 1024; 30 31 #define MAX_NUMBER_INTERFACES 3 32 33 /* Local storage for Audio data in 16 bit words */ 34 #define AUDIO_DATA_STORAGE_SIZE (TOTAL_BUFFER_SIZE / 2) 35 /* Audio data buffer size in 16 bit words. 8 data segments are used in 36 this simple example */ 37 #define AUDIO_DATA_BUFFER_SIZE (4096/8) 38 39 const size_t NUMBER_OF_BUFFERS = AUDIO_DATA_STORAGE_SIZE / AUDIO_DATA_BUFFER_SIZE; 40 41 /* Checks for error. If any errors exit the application! */ 42 void CheckErr( SLresult res ) 43 { 44 if ( res != SL_RESULT_SUCCESS ) 45 { 46 fprintf(stdout, "%u SL failure, exiting\n", res); 47 exit(EXIT_FAILURE); 48 } 49 else { 50 //fprintf(stdout, "%d SL success, proceeding...\n", res); 51 } 52 } 53 54 /* Structure for passing information to callback function */ 55 typedef struct CallbackCntxt_ { 56 SLPlayItf playItf; 57 SLint16* pDataBase; // Base adress of local audio data storage 58 SLint16* pData; // Current adress of local audio data storage 59 SLuint32 size; 60 } CallbackCntxt; 61 62 /* Local storage for Audio data */ 63 SLint16 pcmData[AUDIO_DATA_STORAGE_SIZE]; 64 65 /* Callback for Buffer Queue events */ 66 void BufferQueueCallback( 67 SLBufferQueueItf queueItf, 68 void *pContext) 69 { 70 //fprintf(stdout, "BufferQueueCallback called\n"); 71 SLresult res; 72 //fprintf(stdout, " pContext=%p\n", pContext); 73 CallbackCntxt *pCntxt = (CallbackCntxt*)pContext; 74 75 if (pCntxt->pData >= (pCntxt->pDataBase + pCntxt->size)) { 76 pCntxt->pData = pCntxt->pDataBase; 77 } 78 { 79 //fprintf(stdout, "callback: before enqueue\n"); 80 res = (*queueItf)->Enqueue(queueItf, (void*) pCntxt->pData, 81 2 * AUDIO_DATA_BUFFER_SIZE); /* Size given in bytes. */ 82 CheckErr(res); 83 /* Increase data pointer by buffer size */ 84 pCntxt->pData += AUDIO_DATA_BUFFER_SIZE; 85 } 86 //fprintf(stdout, "end of BufferQueueCallback()\n"); 87 } 88 89 SLEngineItf EngineItf; 90 91 SLint32 numOutputs = 0; 92 SLuint32 deviceID = 0; 93 94 95 SLDataSource audioSource; 96 SLDataLocator_BufferQueue bufferQueue; 97 SLDataFormat_PCM pcm; 98 99 SLDataSink audioSink; 100 SLDataLocator_OutputMix locator_outputmix; 101 102 103 SLVolumeItf volumeItf; 104 105 106 SLboolean required[MAX_NUMBER_INTERFACES]; 107 SLInterfaceID iidArray[MAX_NUMBER_INTERFACES]; 108 109 /* Callback context for the buffer queue callback function */ 110 CallbackCntxt cntxt; 111 112 static SLObjectItf OutputMix; 113 static SLPlayItf playItf; 114 static SLObjectItf player; 115 static SLBufferQueueItf bufferQueueItf; 116 static SLBufferQueueState state; 117 118 /* Play some audio from a buffer queue */ 119 void TestPlaySawtoothBufferQueue( SLObjectItf sl ) 120 { 121 SLresult res; 122 int i; 123 124 /* Get the SL Engine Interface which is implicit */ 125 res = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void*)&EngineItf); 126 CheckErr(res); 127 128 /* Initialize arrays required[] and iidArray[] */ 129 for (i=0;i<MAX_NUMBER_INTERFACES;i++) 130 { 131 required[i] = SL_BOOLEAN_FALSE; 132 iidArray[i] = SL_IID_NULL; 133 } 134 135 // Set arrays required[] and iidArray[] for VOLUME interface 136 required[0] = SL_BOOLEAN_TRUE; 137 iidArray[0] = SL_IID_VOLUME; 138 // Create Output Mix object to be used by player 139 res = (*EngineItf)->CreateOutputMix(EngineItf, &OutputMix, 0, 140 iidArray, required); CheckErr(res); 141 142 // Realizing the Output Mix object in synchronous mode. 143 res = (*OutputMix)->Realize(OutputMix, SL_BOOLEAN_FALSE); 144 CheckErr(res); 145 146 #if 0 147 res = (*OutputMix)->GetInterface(OutputMix, SL_IID_VOLUME, 148 (void*)&volumeItf); CheckErr(res); 149 #endif 150 151 /* Setup the data source structure for the buffer queue */ 152 bufferQueue.locatorType = SL_DATALOCATOR_BUFFERQUEUE; 153 bufferQueue.numBuffers = 4; /* Four buffers in our buffer queue */ 154 155 /* Setup the format of the content in the buffer queue */ 156 pcm.formatType = SL_DATAFORMAT_PCM; 157 pcm.numChannels = 2; 158 pcm.samplesPerSec = SL_SAMPLINGRATE_11_025; 159 pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; 160 pcm.containerSize = 16; 161 pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; 162 pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; 163 164 audioSource.pFormat = (void *)&pcm; 165 audioSource.pLocator = (void *)&bufferQueue; 166 167 /* Setup the data sink structure */ 168 locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; 169 locator_outputmix.outputMix = OutputMix; 170 audioSink.pLocator = (void *)&locator_outputmix; 171 audioSink.pFormat = NULL; 172 173 /* Initialize the audio data to silence */ 174 memset(pcmData, 0, sizeof(pcmData)); 175 176 /* Initialize the context for Buffer queue callbacks */ 177 cntxt.pDataBase = /*(void*)&*/pcmData; 178 cntxt.pData = cntxt.pDataBase; 179 cntxt.size = sizeof(pcmData) / 2; 180 181 /* Set arrays required[] and iidArray[] for SEEK interface 182 (PlayItf is implicit) */ 183 required[0] = SL_BOOLEAN_TRUE; 184 iidArray[0] = SL_IID_BUFFERQUEUE; 185 186 /* Create the music player */ 187 res = (*EngineItf)->CreateAudioPlayer(EngineItf, &player, 188 &audioSource, &audioSink, 1, iidArray, required); CheckErr(res); 189 fprintf(stdout, "bufferQueue example: after CreateAudioPlayer\n"); 190 191 /* Realizing the player in synchronous mode. */ 192 res = (*player)->Realize(player, SL_BOOLEAN_FALSE); CheckErr(res); 193 fprintf(stdout, "bufferQueue example: after Realize\n"); 194 195 /* Get seek and play interfaces */ 196 res = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf); 197 CheckErr(res); 198 fprintf(stdout, "bufferQueue example: after GetInterface(PLAY)\n"); 199 200 res = (*player)->GetInterface(player, SL_IID_BUFFERQUEUE, 201 (void*)&bufferQueueItf); CheckErr(res); 202 203 /* Setup to receive buffer queue event callbacks */ 204 res = (*bufferQueueItf)->RegisterCallback(bufferQueueItf, 205 BufferQueueCallback, &cntxt); CheckErr(res); 206 207 #if 0 208 /* Before we start set volume to -3dB (-300mB) */ 209 res = (*volumeItf)->SetVolumeLevel(volumeItf, -300); CheckErr(res); 210 #endif 211 212 /* Enqueue a few buffers to get the ball rolling */ 213 res = (*bufferQueueItf)->Enqueue(bufferQueueItf, cntxt.pData, 214 2 * AUDIO_DATA_BUFFER_SIZE); /* Size given in bytes. */ 215 CheckErr(res); 216 cntxt.pData += AUDIO_DATA_BUFFER_SIZE; 217 218 res = (*bufferQueueItf)->Enqueue(bufferQueueItf, cntxt.pData, 219 2 * AUDIO_DATA_BUFFER_SIZE); /* Size given in bytes. */ 220 CheckErr(res); 221 cntxt.pData += AUDIO_DATA_BUFFER_SIZE; 222 223 res = (*bufferQueueItf)->Enqueue(bufferQueueItf, cntxt.pData, 224 2 * AUDIO_DATA_BUFFER_SIZE); /* Size given in bytes. */ 225 CheckErr(res); 226 cntxt.pData += AUDIO_DATA_BUFFER_SIZE; 227 228 /* Play the PCM samples using a buffer queue */ 229 fprintf(stdout, "bufferQueue example: starting to play\n"); 230 res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PLAYING ); 231 CheckErr(res); 232 233 /* Wait until the PCM data is done playing, the buffer queue callback 234 will continue to queue buffers until the entire PCM data has been 235 played. This is indicated by waiting for the count member of the 236 SLBufferQueueState to go to zero. 237 */ 238 res = (*bufferQueueItf)->GetState(bufferQueueItf, &state); 239 CheckErr(res); 240 241 #if 0 242 // while (state.playIndex < 100) { 243 while (state.count) { 244 usleep(10000); 245 (*bufferQueueItf)->GetState(bufferQueueItf, &state); 246 } 247 248 #endif 249 } 250 251 SLObjectItf gSoundEngine; 252 253 int startAndroidSound() 254 { 255 SLresult res; 256 257 SLEngineOption EngineOption[] = { 258 {(SLuint32) SL_ENGINEOPTION_THREADSAFE, 259 (SLuint32) SL_BOOLEAN_TRUE}}; 260 261 res = slCreateEngine( &gSoundEngine, 1, EngineOption, 0, NULL, NULL); 262 CheckErr(res); 263 /* Realizing the SL Engine in synchronous mode. */ 264 res = (*gSoundEngine)->Realize(gSoundEngine, SL_BOOLEAN_FALSE); CheckErr(res); 265 266 /* Run the test */ 267 TestPlaySawtoothBufferQueue(gSoundEngine); 268 return EXIT_SUCCESS; 269 } 270 271 void finishAndroidSound() 272 { 273 SLresult res; 274 275 if (gSoundEngine == NULL) { 276 return; 277 } 278 279 /* Make sure player is stopped */ 280 if (playItf != NULL) { 281 res = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED); 282 CheckErr(res); 283 playItf = NULL; 284 } 285 286 if (player != NULL) { 287 /* Destroy the player */ 288 (*player)->Destroy(player); 289 player = NULL; 290 } 291 292 if (OutputMix != NULL) { 293 /* Destroy Output Mix object */ 294 (*OutputMix)->Destroy(OutputMix); 295 OutputMix = NULL; 296 } 297 298 /* Shutdown OpenSL ES */ 299 (*gSoundEngine)->Destroy(gSoundEngine); 300 gSoundEngine = NULL; 301 } 302 303 #if 1 304 305 /* 306 ================== 307 SNDDMA_Init 308 309 Try to find a sound device to mix for. 310 Returns false if nothing is found. 311 ================== 312 */ 313 qboolean SNDDMA_Init(void) 314 { 315 // Initialize Quake's idea of a DMA buffer. 316 317 shm = &sn; 318 memset((void*)&sn, 0, sizeof(sn)); 319 320 shm->splitbuffer = false; // Not used. 321 shm->samplebits = 16; 322 shm->speed = 11025; 323 shm->channels = 2; 324 shm->samples = TOTAL_BUFFER_SIZE / BYTES_PER_SAMPLE; 325 shm->samplepos = 0; // Not used. 326 shm->buffer = (unsigned char*) pcmData; 327 shm->submission_chunk = 1; // Not used. 328 329 shm->soundalive = true; 330 331 if ( (shm->samples & 0x1ff) != 0 ) { 332 LOGE("SNDDDMA_Init: samples must be power of two."); 333 return false; 334 } 335 336 if ( shm->buffer == 0 ) { 337 LOGE("SNDDDMA_Init: Could not allocate sound buffer."); 338 return false; 339 } 340 341 int result = startAndroidSound(); 342 return result == EXIT_SUCCESS; 343 } 344 345 /* 346 ============== 347 SNDDMA_GetDMAPos 348 349 return the current sample position (in mono samples read) 350 inside the recirculating dma buffer, so the mixing code will know 351 how many sample are required to fill it up. 352 =============== 353 */ 354 int SNDDMA_GetDMAPos(void) 355 { 356 SLresult res; 357 if (bufferQueueItf != NULL) { 358 res = (*bufferQueueItf)->GetState(bufferQueueItf, &state); 359 CheckErr(res); 360 // Index of the currently playing or filling buffer. 361 SLuint32 playIndex = state.playIndex; 362 int ringIndex = playIndex % NUMBER_OF_BUFFERS; 363 return ringIndex * AUDIO_DATA_BUFFER_SIZE; 364 } 365 return 0; 366 } 367 368 /* 369 =============== 370 SNDDMA_ReportWrite 371 372 Report valid data being written into the DMA buffer by the sound mixing code. 373 This is an Android specific API. 374 ================ 375 */ 376 void SNDDMA_ReportWrite(size_t lengthBytes) { 377 // TODO: keep track of how much of the sound ring buffer has sound in it, 378 // detect starvation, and mix silence when we are starving. 379 } 380 381 /* 382 ============== 383 SNDDMA_Submit 384 385 Send sound to device if buffer isn't really the dma buffer 386 =============== 387 */ 388 void SNDDMA_Submit(void) 389 { 390 } 391 392 /* 393 ============== 394 SNDDMA_Shutdown 395 396 Reset the sound device for exiting 397 =============== 398 */ 399 void SNDDMA_Shutdown(void) 400 { 401 finishAndroidSound(); 402 } 403 404 405 #else 406 407 // Written by the callback function running in an audio thread. 408 // index in bytes of where we last read. 409 410 static volatile size_t gDMAByteIndex; 411 412 413 // Written by main thread 414 static size_t gAvailableBytes; 415 static bool gSoundMixingStarted; 416 417 // The condition is "new data is now available" 418 419 static pthread_mutex_t condition_mutex = PTHREAD_MUTEX_INITIALIZER; 420 static pthread_cond_t condition_cond = PTHREAD_COND_INITIALIZER; 421 422 /* 423 ================== 424 SNDDMA_Init 425 426 Try to find a sound device to mix for. 427 Returns false if nothing is found. 428 ================== 429 */ 430 431 432 const size_t SAMPLE_RATE = 11025; 433 434 435 const size_t BYTES_PER_SAMPLE = 2; 436 const size_t CHANNEL_COUNT = 2; 437 const size_t BITS_PER_SAMPLE = 8 * BYTES_PER_SAMPLE; 438 439 const size_t TOTAL_BUFFER_SIZE = 16 * 1024; 440 441 static size_t min(size_t a, size_t b) { 442 return a < b ? a : b; 443 } 444 445 static size_t mod(size_t value, size_t mod) { 446 return value % mod; 447 } 448 449 static size_t next(size_t value, size_t mod) { 450 value = value + 1; 451 if ( value >= mod ) { 452 value = 0; 453 } 454 return value; 455 } 456 457 static size_t prev(size_t value, size_t mod) { 458 if ( value <= 0 ) { 459 value = mod; 460 } 461 return value - 1; 462 } 463 464 465 static bool enableSound() { 466 467 if (COM_CheckParm("-nosound")) 468 return false; 469 470 return true; 471 } 472 473 // Choose one: 474 475 // #define GENERATE_SINE_WAVE 476 #define NORMAL_SOUND 477 478 #ifdef GENERATE_SINE_WAVE 479 480 static const float p = 2 * M_PI * 440.0f / SAMPLE_RATE; 481 static float left = 0.0f; 482 static float right = 0.0f; 483 484 static float sinef(float x) 485 { 486 const float A = 1.0f / (2.0f*M_PI); 487 const float B = -16.0f; 488 const float C = 8.0f; 489 490 // scale angle for easy argument reduction 491 x *= A; 492 493 if (fabsf(x) >= 0.5f) { 494 // Argument reduction 495 x = x - ceilf(x + 0.5f) + 1.0f; 496 } 497 498 const float y = B*x*fabsf(x) + C*x; 499 return 0.2215f * (y*fabsf(y) - y) + y; 500 } 501 502 static 503 void AndroidQuakeSoundCallback(int event, void* user, void *info) { 504 505 if (event != AudioTrack::EVENT_MORE_DATA) return; 506 507 const AudioTrack::Buffer *buffer = static_cast<const AudioTrack::Buffer *>(info); 508 size_t bytesToCopy = buffer->size; 509 size_t framesToCopy = buffer->size / (BYTES_PER_SAMPLE * CHANNEL_COUNT); 510 short* pData = buffer->i16; 511 512 for(size_t frame = 0; frame < framesToCopy; frame++) { 513 short leftSample = (short) (32767.0f * sinef(left)); 514 left += p; 515 if (left > 2*M_PI) { 516 left -= 2*M_PI; 517 } 518 pData[frame * CHANNEL_COUNT] = leftSample; 519 520 short rightSample = (short) (32767.0f * sinef(right)); 521 right += 2 * p; 522 if (right > 2*M_PI) { 523 right -= 2*M_PI; 524 } 525 pData[1 + frame * CHANNEL_COUNT] = rightSample; 526 } 527 528 gDMAByteIndex = mod(gDMAByteIndex + bytesToCopy, TOTAL_BUFFER_SIZE); 529 asm volatile ("":::"memory"); 530 } 531 532 #endif 533 534 #ifdef NORMAL_SOUND 535 536 static bool gWaitingForMixerToRestart; 537 538 // Assumes the mutex is acquired. 539 // Waits until audio is available or a time period has elapsed. 540 static bool shouldMixSilence() { 541 if (!gSoundMixingStarted) { 542 return true; 543 } 544 while (gAvailableBytes == 0) { 545 if (gWaitingForMixerToRestart) { 546 return true; 547 } 548 timeval tp; 549 if (gettimeofday(&tp, NULL)) { 550 return true; 551 } 552 const long WAIT_NS = 40 * 1000 * 1000; 553 const long NS_PER_SECOND = 1000 * 1000 * 1000; 554 timespec ts; 555 ts.tv_sec = tp.tv_sec; 556 ts.tv_nsec = tp.tv_usec * 1000 + WAIT_NS; 557 if (ts.tv_nsec >= NS_PER_SECOND) { 558 ts.tv_nsec -= NS_PER_SECOND; 559 ts.tv_sec += 1; 560 } 561 if (ETIMEDOUT == pthread_cond_timedwait( &condition_cond, &condition_mutex, &ts)) { 562 gWaitingForMixerToRestart = true; 563 return true; 564 } 565 } 566 gWaitingForMixerToRestart = false; 567 return false; 568 } 569 570 static 571 void AndroidQuakeSoundCallback(int event, void* user, void *info) { 572 573 if (event != AudioTrack::EVENT_MORE_DATA) return; 574 575 const AudioTrack::Buffer *buffer = static_cast<const AudioTrack::Buffer *>(info); 576 size_t dmaByteIndex = gDMAByteIndex; 577 size_t size = buffer->size; 578 unsigned char* pDestBuffer = (unsigned char*) buffer->raw; 579 580 if (size == 0) return; 581 582 if ( ! shm ) { 583 memset(pDestBuffer, 0, size); 584 return; 585 } 586 587 const unsigned char* pSrcBuffer = shm->buffer; 588 589 while(size > 0) { 590 pthread_mutex_lock( &condition_mutex ); 591 592 if (shouldMixSilence()) { 593 memset(pDestBuffer, 0, size); 594 pthread_mutex_unlock( &condition_mutex ); 595 return; 596 } 597 598 size_t chunkSize = min(gAvailableBytes, min(TOTAL_BUFFER_SIZE-dmaByteIndex, size)); 599 gAvailableBytes -= chunkSize; 600 601 pthread_mutex_unlock( &condition_mutex ); 602 603 memcpy(pDestBuffer, pSrcBuffer + dmaByteIndex, chunkSize); 604 size -= chunkSize; 605 pDestBuffer += chunkSize; 606 dmaByteIndex += chunkSize; 607 if (dmaByteIndex >= TOTAL_BUFFER_SIZE) { 608 dmaByteIndex = 0; 609 } 610 } 611 612 gDMAByteIndex = dmaByteIndex; 613 asm volatile ("":::"memory"); 614 } 615 616 #endif 617 618 qboolean SNDDMA_Init(void) 619 { 620 if ( ! enableSound() ) { 621 return false; 622 } 623 624 gDMAByteIndex = 0; 625 626 // Initialize the AudioTrack. 627 628 status_t result = gAudioTrack.set( 629 AUDIO_STREAM_DEFAULT, // stream type 630 SAMPLE_RATE, // sample rate 631 BITS_PER_SAMPLE == 16 ? AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT, // format (8 or 16) 632 (CHANNEL_COUNT > 1) ? AUDIO_CHANNEL_OUT_STEREO : AUDIO_CHANNEL_OUT_MONO, // channel mask 633 0, // default buffer size 634 0, // flags 635 AndroidQuakeSoundCallback, // callback 636 0, // user 637 0); // default notification size 638 639 LOGI("AudioTrack status = %d (%s)\n", result, result == NO_ERROR ? "success" : "error"); 640 641 if ( result == NO_ERROR ) { 642 LOGI("AudioTrack latency = %u ms\n", gAudioTrack.latency()); 643 LOGI("AudioTrack format = %u bits\n", gAudioTrack.format() == AUDIO_FORMAT_PCM_16_BIT ? 16 : 8); 644 LOGI("AudioTrack sample rate = %u Hz\n", gAudioTrack.getSampleRate()); 645 LOGI("AudioTrack frame count = %d\n", int(gAudioTrack.frameCount())); 646 LOGI("AudioTrack channel count = %d\n", gAudioTrack.channelCount()); 647 648 // Initialize Quake's idea of a DMA buffer. 649 650 shm = &sn; 651 memset((void*)&sn, 0, sizeof(sn)); 652 653 shm->splitbuffer = false; // Not used. 654 shm->samplebits = gAudioTrack.format() == AUDIO_FORMAT_PCM_16_BIT ? 16 : 8; 655 shm->speed = gAudioTrack.getSampleRate(); 656 shm->channels = gAudioTrack.channelCount(); 657 shm->samples = TOTAL_BUFFER_SIZE / BYTES_PER_SAMPLE; 658 shm->samplepos = 0; // Not used. 659 shm->buffer = (unsigned char*) Hunk_AllocName(TOTAL_BUFFER_SIZE, (char*) "shmbuf"); 660 shm->submission_chunk = 1; // Not used. 661 662 shm->soundalive = true; 663 664 if ( (shm->samples & 0x1ff) != 0 ) { 665 LOGE("SNDDDMA_Init: samples must be power of two."); 666 return false; 667 } 668 669 if ( shm->buffer == 0 ) { 670 LOGE("SNDDDMA_Init: Could not allocate sound buffer."); 671 return false; 672 } 673 674 gAudioTrack.setVolume(1.0f, 1.0f); 675 gAudioTrack.start(); 676 } 677 678 return result == NO_ERROR; 679 } 680 681 /* 682 ============== 683 SNDDMA_GetDMAPos 684 685 return the current sample position (in mono samples read) 686 inside the recirculating dma buffer, so the mixing code will know 687 how many sample are required to fill it up. 688 =============== 689 */ 690 int SNDDMA_GetDMAPos(void) 691 { 692 int dmaPos = gDMAByteIndex / BYTES_PER_SAMPLE; 693 asm volatile ("":::"memory"); 694 return dmaPos; 695 } 696 697 /* 698 =============== 699 SNDDMA_ReportWrite 700 701 Report valid data being written into the DMA buffer by the sound mixing code. 702 This is an Android specific API. 703 ================ 704 */ 705 void SNDDMA_ReportWrite(size_t lengthBytes) { 706 pthread_mutex_lock( &condition_mutex ); 707 gSoundMixingStarted = true; 708 if (gAvailableBytes == 0) { 709 pthread_cond_signal( &condition_cond ); 710 } 711 gAvailableBytes += lengthBytes; 712 pthread_mutex_unlock( &condition_mutex ); 713 } 714 715 /* 716 ============== 717 SNDDMA_Submit 718 719 Send sound to device if buffer isn't really the dma buffer 720 =============== 721 */ 722 void SNDDMA_Submit(void) 723 { 724 } 725 726 /* 727 ============== 728 SNDDMA_Shutdown 729 730 Reset the sound device for exiting 731 =============== 732 */ 733 void SNDDMA_Shutdown(void) 734 { 735 gAudioTrack.stop(); 736 } 737 738 #endif 739