1 /* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997-2012 Sam Lantinga 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Library General Public 7 License as published by the Free Software Foundation; either 8 version 2 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Library General Public License for more details. 14 15 You should have received a copy of the GNU Library General Public 16 License along with this library; if not, write to the Free 17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 19 Sam Lantinga 20 slouken (at) devolution.com 21 */ 22 23 /* 24 SDL_epocaudio.cpp 25 Epoc based SDL audio driver implementation 26 27 Markus Mertama 28 */ 29 30 #ifdef SAVE_RCSID 31 static char rcsid = 32 "@(#) $Id: SDL_epocaudio.c,v 0.0.0.0 2001/06/19 17:19:56 hercules Exp $"; 33 #endif 34 35 36 #include <stdlib.h> 37 #include <stdio.h> 38 #include <string.h> 39 #include <errno.h> 40 #include <unistd.h> 41 #include <fcntl.h> 42 #include <signal.h> 43 #include <sys/time.h> 44 #include <sys/ioctl.h> 45 #include <sys/stat.h> 46 47 #include "epoc_sdl.h" 48 49 #include <e32hal.h> 50 51 52 extern "C" { 53 #include "SDL_audio.h" 54 #include "SDL_error.h" 55 #include "SDL_audiomem.h" 56 #include "SDL_audio_c.h" 57 #include "SDL_timer.h" 58 #include "SDL_audiodev_c.h" 59 } 60 61 #include "SDL_epocaudio.h" 62 63 #include "streamplayer.h" 64 65 66 //#define DEBUG_AUDIO 67 68 69 /* Audio driver functions */ 70 71 static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec); 72 static void EPOC_WaitAudio(SDL_AudioDevice *thisdevice); 73 static void EPOC_PlayAudio(SDL_AudioDevice *thisdevice); 74 static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice *thisdevice); 75 static void EPOC_CloseAudio(SDL_AudioDevice *thisdevice); 76 static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice); 77 78 static int Audio_Available(void); 79 static SDL_AudioDevice *Audio_CreateDevice(int devindex); 80 static void Audio_DeleteDevice(SDL_AudioDevice *device); 81 82 83 //void sos_adump(SDL_AudioDevice* thisdevice, void* data, int len); 84 85 #ifdef __WINS__ 86 #define DODUMP 87 #endif 88 89 #ifdef DODUMP 90 NONSHARABLE_CLASS(TDump) 91 { 92 public: 93 TInt Open(); 94 void Close(); 95 void Dump(const TDesC8& aDes); 96 private: 97 RFile iFile; 98 RFs iFs; 99 }; 100 101 TInt TDump::Open() 102 { 103 TInt err = iFs.Connect(); 104 if(err == KErrNone) 105 { 106 #ifdef __WINS__ 107 _LIT(target, "C:\\sdlau.raw"); 108 #else 109 _LIT(target, "E:\\sdlau.raw"); 110 #endif 111 err = iFile.Replace(iFs, target, EFileWrite); 112 } 113 return err; 114 } 115 void TDump::Close() 116 { 117 iFile.Close(); 118 iFs.Close(); 119 } 120 void TDump::Dump(const TDesC8& aDes) 121 { 122 iFile.Write(aDes); 123 } 124 #endif 125 126 127 NONSHARABLE_CLASS(CSimpleWait) : public CTimer 128 { 129 public: 130 void Wait(TTimeIntervalMicroSeconds32 aWait); 131 static CSimpleWait* NewL(); 132 private: 133 CSimpleWait(); 134 void RunL(); 135 }; 136 137 138 CSimpleWait* CSimpleWait::NewL() 139 { 140 CSimpleWait* wait = new (ELeave) CSimpleWait(); 141 CleanupStack::PushL(wait); 142 wait->ConstructL(); 143 CleanupStack::Pop(); 144 return wait; 145 } 146 147 void CSimpleWait::Wait(TTimeIntervalMicroSeconds32 aWait) 148 { 149 After(aWait); 150 CActiveScheduler::Start(); 151 } 152 153 CSimpleWait::CSimpleWait() : CTimer(CActive::EPriorityStandard) 154 { 155 CActiveScheduler::Add(this); 156 } 157 158 void CSimpleWait::RunL() 159 { 160 CActiveScheduler::Stop(); 161 } 162 163 const TInt KAudioBuffers(2); 164 165 166 NONSHARABLE_CLASS(CEpocAudio) : public CBase, public MStreamObs, public MStreamProvider 167 { 168 public: 169 static void* NewL(TInt BufferSize, TInt aFill); 170 inline static CEpocAudio& Current(SDL_AudioDevice* thisdevice); 171 172 static void Free(SDL_AudioDevice* thisdevice); 173 174 void Wait(); 175 void Play(); 176 // void SetBuffer(const TDesC8& aBuffer); 177 void ThreadInitL(TAny* aDevice); 178 void Open(TInt iRate, TInt iChannels, TUint32 aType, TInt aBytes); 179 ~CEpocAudio(); 180 TUint8* Buffer(); 181 TBool SetPause(TBool aPause); 182 #ifdef DODUMP 183 void Dump(const TDesC8& aBuf) {iDump.Dump(aBuf);} 184 #endif 185 private: 186 CEpocAudio(TInt aBufferSize); 187 void Complete(TInt aState, TInt aError); 188 TPtrC8 Data(); 189 void ConstructL(TInt aFill); 190 private: 191 TInt iBufferSize; 192 CStreamPlayer* iPlayer; 193 TInt iBufferRate; 194 TInt iRate; 195 TInt iChannels; 196 TUint32 iType; 197 TInt iPosition; 198 TThreadId iTid; 199 TUint8* iAudioPtr; 200 TUint8* iBuffer; 201 // TTimeIntervalMicroSeconds iStart; 202 TTime iStart; 203 TInt iTune; 204 CSimpleWait* iWait; 205 #ifdef DODUMP 206 TDump iDump; 207 #endif 208 }; 209 210 inline CEpocAudio& CEpocAudio::Current(SDL_AudioDevice* thisdevice) 211 { 212 return *static_cast<CEpocAudio*>((void*)thisdevice->hidden); 213 } 214 215 /* 216 217 TBool EndSc(TAny*) 218 { 219 CActiveScheduler::Stop(); 220 } 221 222 LOCAL_C void CleanScL() 223 { 224 CIdle* d = CIdle::NewLC(CActive:::EPriorityIdle); 225 d->Start(TCallBack(EndSc)); 226 CActiveScheduler::Start(); 227 228 } 229 */ 230 231 void CEpocAudio::Free(SDL_AudioDevice* thisdevice) 232 { 233 CEpocAudio* ea = static_cast<CEpocAudio*>((void*)thisdevice->hidden); 234 if(ea) 235 { 236 ASSERT(ea->iTid == RThread().Id()); 237 delete ea; 238 thisdevice->hidden = NULL; 239 240 CActiveScheduler* as = CActiveScheduler::Current(); 241 ASSERT(as->StackDepth() == 0); 242 delete as; 243 CActiveScheduler::Install(NULL); 244 } 245 ASSERT(thisdevice->hidden == NULL); 246 } 247 248 CEpocAudio::CEpocAudio(TInt aBufferSize) : iBufferSize(aBufferSize), iPosition(-1) 249 { 250 } 251 252 void* CEpocAudio::NewL(TInt aBufferSize, TInt aFill) 253 { 254 CEpocAudio* eAudioLib = new (ELeave) CEpocAudio(aBufferSize); 255 CleanupStack::PushL(eAudioLib); 256 eAudioLib->ConstructL(aFill); 257 CleanupStack::Pop(); 258 return eAudioLib; 259 } 260 261 void CEpocAudio::ConstructL(TInt aFill) 262 { 263 iBuffer = (TUint8*) User::AllocL(KAudioBuffers * iBufferSize); 264 memset(iBuffer, aFill, KAudioBuffers * iBufferSize); 265 iAudioPtr = iBuffer; 266 } 267 268 269 TBool CEpocAudio::SetPause(TBool aPause) 270 { 271 if(aPause && iPosition >= 0) 272 { 273 iPosition = -1; 274 if(iPlayer != NULL) 275 iPlayer->Stop(); 276 } 277 if(!aPause && iPosition < 0) 278 { 279 iPosition = 0; 280 if(iPlayer != NULL) 281 iPlayer->Start(); 282 } 283 return iPosition < 0; 284 } 285 286 void CEpocAudio::ThreadInitL(TAny* aDevice) 287 { 288 iTid = RThread().Id(); 289 CActiveScheduler* as = new (ELeave) CActiveScheduler(); 290 CActiveScheduler::Install(as); 291 292 EpocSdlEnv::AppendCleanupItem(TSdlCleanupItem((TSdlCleanupOperation)EPOC_CloseAudio, aDevice)); 293 294 iWait = CSimpleWait::NewL(); 295 296 iPlayer = new (ELeave) CStreamPlayer(*this, *this); 297 iPlayer->ConstructL(); 298 iPlayer->OpenStream(iRate, iChannels, iType); 299 300 #ifdef DODUMP 301 User::LeaveIfError(iDump.Open()); 302 #endif 303 } 304 305 306 307 TUint8* CEpocAudio::Buffer() 308 { 309 iStart.UniversalTime(); 310 // iStart = iPlayer->Position(); 311 return iAudioPtr; 312 313 } 314 315 CEpocAudio::~CEpocAudio() 316 { 317 if(iWait != NULL) 318 iWait->Cancel(); 319 delete iWait; 320 if(iPlayer != NULL) 321 iPlayer->Close(); 322 delete iPlayer; 323 delete iBuffer; 324 } 325 326 void CEpocAudio::Complete(TInt aState, TInt aError) 327 { 328 if(aState == MStreamObs::EClose) 329 { 330 } 331 if(iPlayer->Closed()) 332 return; 333 switch(aError) 334 { 335 case KErrUnderflow: 336 case KErrInUse: 337 iPlayer->Start(); 338 break; 339 case KErrAbort: 340 iPlayer->Open(); 341 } 342 } 343 344 345 void sos_adump(SDL_AudioDevice* thisdevice, void* data, int len) 346 { 347 #ifdef DODUMP 348 const TPtrC8 buf((TUint8*)data, len); 349 CEpocAudio::Current(thisdevice).Dump(buf); 350 #endif 351 } 352 353 const TInt KClip(256); 354 355 TPtrC8 CEpocAudio::Data() 356 { 357 if(iPosition < 0) 358 return KNullDesC8(); 359 360 TPtrC8 data(iAudioPtr + iPosition, KClip); 361 362 #ifdef DODUMP 363 iDump.Dump(data); 364 #endif 365 366 iPosition += KClip; 367 if(iPosition >= iBufferSize) 368 { 369 370 /* if(iAudioPtr == iBuffer) 371 iAudioPtr = iBuffer + iBufferSize; 372 else 373 iAudioPtr = iBuffer; 374 */ 375 iAudioPtr += iBufferSize; 376 377 if((iAudioPtr - iBuffer) >= KAudioBuffers * iBufferSize) 378 iAudioPtr = iBuffer; 379 380 iPosition = -1; 381 if(iWait->IsActive()) 382 { 383 iWait->Cancel(); 384 CActiveScheduler::Stop(); 385 } 386 } 387 return data; 388 } 389 390 391 392 393 void CEpocAudio::Play() 394 { 395 iPosition = 0; 396 } 397 398 void CEpocAudio::Wait() 399 { 400 if(iPosition >= 0 /*&& iPlayer->Playing()*/) 401 { 402 const TInt64 bufMs = TInt64(iBufferSize - KClip) * TInt64(1000000); 403 const TInt64 specTime = bufMs / TInt64(iRate * iChannels * 2); 404 iWait->After(specTime); 405 406 CActiveScheduler::Start(); 407 TTime end; 408 end.UniversalTime(); 409 const TTimeIntervalMicroSeconds delta = end.MicroSecondsFrom(iStart); 410 411 412 // const TTimeIntervalMicroSeconds end = iPlayer->Position(); 413 414 415 416 417 const TInt diff = specTime - delta.Int64(); 418 419 if(diff > 0 && diff < 200000) 420 { 421 User::After(diff); 422 } 423 424 } 425 else 426 { 427 User::After(10000); 428 // iWait->Wait(10000); //just give some time... 429 } 430 } 431 432 void CEpocAudio::Open(TInt aRate, TInt aChannels, TUint32 aType, TInt aBytes) 433 { 434 iRate = aRate; 435 iChannels = aChannels; 436 iType = aType; 437 iBufferRate = iRate * iChannels * aBytes; //1/x 438 } 439 440 441 /* Audio driver bootstrap functions */ 442 443 AudioBootStrap EPOCAudio_bootstrap = { 444 "epoc\0\0\0", 445 "EPOC streaming audio\0\0\0", 446 Audio_Available, 447 Audio_CreateDevice 448 }; 449 450 451 static SDL_AudioDevice *Audio_CreateDevice(int /*devindex*/) 452 { 453 SDL_AudioDevice *thisdevice; 454 455 /* Initialize all variables that we clean on shutdown */ 456 thisdevice = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice)); 457 if ( thisdevice ) { 458 memset(thisdevice, 0, (sizeof *thisdevice)); 459 thisdevice->hidden = NULL; /*(struct SDL_PrivateAudioData *) 460 malloc((sizeof thisdevice->hidden)); */ 461 } 462 if ( (thisdevice == NULL) /*|| (thisdevice->hidden == NULL) */) { 463 SDL_OutOfMemory(); 464 if ( thisdevice ) { 465 free(thisdevice); 466 } 467 return(0); 468 } 469 // memset(thisdevice->hidden, 0, (sizeof *thisdevice->hidden)); 470 471 /* Set the function pointers */ 472 thisdevice->OpenAudio = EPOC_OpenAudio; 473 thisdevice->WaitAudio = EPOC_WaitAudio; 474 thisdevice->PlayAudio = EPOC_PlayAudio; 475 thisdevice->GetAudioBuf = EPOC_GetAudioBuf; 476 thisdevice->CloseAudio = EPOC_CloseAudio; 477 thisdevice->ThreadInit = EPOC_ThreadInit; 478 thisdevice->free = Audio_DeleteDevice; 479 480 return thisdevice; 481 } 482 483 484 static void Audio_DeleteDevice(SDL_AudioDevice *device) 485 { 486 //free(device->hidden); 487 free(device); 488 } 489 490 static int Audio_Available(void) 491 { 492 return(1); // Audio stream modules should be always there! 493 } 494 495 496 static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec) 497 { 498 SDL_TRACE("SDL:EPOC_OpenAudio"); 499 500 501 TUint32 type = KMMFFourCCCodePCM16; 502 TInt bytes = 2; 503 504 switch(spec->format) 505 { 506 case AUDIO_U16LSB: 507 type = KMMFFourCCCodePCMU16; 508 break; 509 case AUDIO_S16LSB: 510 type = KMMFFourCCCodePCM16; 511 break; 512 case AUDIO_U16MSB: 513 type = KMMFFourCCCodePCMU16B; 514 break; 515 case AUDIO_S16MSB: 516 type = KMMFFourCCCodePCM16B; 517 break; 518 //8 bit not supported! 519 case AUDIO_U8: 520 case AUDIO_S8: 521 default: 522 spec->format = AUDIO_S16LSB; 523 }; 524 525 526 527 if(spec->channels > 2) 528 spec->channels = 2; 529 530 spec->freq = CStreamPlayer::ClosestSupportedRate(spec->freq); 531 532 533 /* Allocate mixing buffer */ 534 const TInt buflen = spec->size;// * bytes * spec->channels; 535 // audiobuf = NULL; 536 537 TRAPD(err, thisdevice->hidden = static_cast<SDL_PrivateAudioData*>(CEpocAudio::NewL(buflen, spec->silence))); 538 if(err != KErrNone) 539 return -1; 540 541 CEpocAudio::Current(thisdevice).Open(spec->freq, spec->channels, type, bytes); 542 543 CEpocAudio::Current(thisdevice).SetPause(ETrue); 544 545 // isSDLAudioPaused = 1; 546 547 thisdevice->enabled = 0; /* enable only after audio engine has been initialized!*/ 548 549 /* We're ready to rock and roll. :-) */ 550 return(0); 551 } 552 553 554 static void EPOC_CloseAudio(SDL_AudioDevice* thisdevice) 555 { 556 #ifdef DEBUG_AUDIO 557 SDL_TRACE("Close audio\n"); 558 #endif 559 560 CEpocAudio::Free(thisdevice); 561 } 562 563 564 static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice) 565 { 566 SDL_TRACE("SDL:EPOC_ThreadInit"); 567 CEpocAudio::Current(thisdevice).ThreadInitL(thisdevice); 568 RThread().SetPriority(EPriorityMore); 569 thisdevice->enabled = 1; 570 } 571 572 /* This function waits until it is possible to write a full sound buffer */ 573 static void EPOC_WaitAudio(SDL_AudioDevice* thisdevice) 574 { 575 #ifdef DEBUG_AUDIO 576 SDL_TRACE1("wait %d audio\n", CEpocAudio::AudioLib().StreamPlayer(KSfxChannel).SyncTime()); 577 TInt tics = User::TickCount(); 578 #endif 579 580 CEpocAudio::Current(thisdevice).Wait(); 581 582 #ifdef DEBUG_AUDIO 583 TInt ntics = User::TickCount() - tics; 584 SDL_TRACE1("audio waited %d\n", ntics); 585 SDL_TRACE1("audio at %d\n", tics); 586 #endif 587 } 588 589 590 591 static void EPOC_PlayAudio(SDL_AudioDevice* thisdevice) 592 { 593 if(CEpocAudio::Current(thisdevice).SetPause(SDL_GetAudioStatus() == SDL_AUDIO_PAUSED)) 594 SDL_Delay(500); //hold on the busy loop 595 else 596 CEpocAudio::Current(thisdevice).Play(); 597 598 #ifdef DEBUG_AUDIO 599 SDL_TRACE("buffer has audio data\n"); 600 #endif 601 602 603 #ifdef DEBUG_AUDIO 604 SDL_TRACE1("Wrote %d bytes of audio data\n", buflen); 605 #endif 606 } 607 608 static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice* thisdevice) 609 { 610 return CEpocAudio::Current(thisdevice).Buffer(); 611 } 612 613 614 615