audio_mediaserver.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /*******************************************************************
  2. *
  3. * File: Audio_mediaserver.cpp
  4. *
  5. * Author: Peter van Sebille (peter@yipton.net)
  6. *
  7. * Modified/adapted for picodriveN by notaz, 2006
  8. *
  9. * (c) Copyright 2006, notaz
  10. * (c) Copyright 2001, Peter van Sebille
  11. * All Rights Reserved
  12. *
  13. *******************************************************************/
  14. #include "audio_mediaserver.h"
  15. #include "debug.h"
  16. //#define DEBUG_UNDERFLOWS
  17. //#undef DEBUGPRINT
  18. //#define DEBUGPRINT(x...)
  19. const TInt KUpdatesPerSec = 10;
  20. const TInt KBlockTime = 1000000 / KUpdatesPerSec;
  21. const TInt KMaxLag = 200000; // max sound lag, lower values increase chance of underflow
  22. const TInt KMaxUnderflows = 50; // max underflows/API errors we are going allow in a row (to prevent lockups)
  23. /*******************************************
  24. *
  25. * CGameAudioMS
  26. *
  27. *******************************************/
  28. CGameAudioMS::CGameAudioMS(TInt aRate, TBool aStereo, TInt aWritesPerSec, TInt aVolume)
  29. : iRate(aRate), iStereo(aStereo), iWritesPerSec(aWritesPerSec), iVolume(aVolume)
  30. {
  31. }
  32. CGameAudioMS* CGameAudioMS::NewL(TInt aRate, TBool aStereo, TInt aWritesPerSec, TInt aVolume)
  33. {
  34. DEBUGPRINT(_L("CGameAudioMS::NewL(%i, %i, %i, %i)"), aRate, aStereo, aWritesPerSec, aVolume);
  35. CGameAudioMS* self = new(ELeave) CGameAudioMS(aRate, aStereo, aWritesPerSec, aVolume);
  36. CleanupStack::PushL(self);
  37. self->ConstructL();
  38. CleanupStack::Pop(); // self
  39. return self;
  40. }
  41. CGameAudioMS::~CGameAudioMS()
  42. {
  43. DEBUGPRINT(_L("CGameAudioMS::~CGameAudioMS()"));
  44. if(iMdaAudioOutputStream) {
  45. iScheduler->Schedule(); // let it finish it's stuff
  46. iMdaAudioOutputStream->Stop();
  47. delete iMdaAudioOutputStream;
  48. }
  49. if(iServer) delete iServer;
  50. for (TInt i=0; i<KSoundBuffers; i++)
  51. delete iSoundBuffers[i];
  52. // Polled AS
  53. //if(iScheduler) delete iScheduler;
  54. }
  55. void CGameAudioMS::ConstructL()
  56. {
  57. iServer = CMdaServer::NewL();
  58. // iScheduler = CPolledActiveScheduler::NewL();
  59. iScheduler = CPolledActiveScheduler::Instance();
  60. switch(iRate) {
  61. case 11025: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate11025Hz; break;
  62. case 16000: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate16000Hz; break;
  63. case 22050: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate22050Hz; break;
  64. case 44100: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate44100Hz; break;
  65. default: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate8000Hz; break;
  66. }
  67. iMdaAudioDataSettings.iChannels = (iStereo) ? TMdaAudioDataSettings::EChannelsStereo : TMdaAudioDataSettings::EChannelsMono;
  68. iMdaAudioDataSettings.iCaps = TMdaAudioDataSettings::ESampleRateFixed | iMdaAudioDataSettings.iSampleRate;
  69. iMdaAudioDataSettings.iFlags = TMdaAudioDataSettings::ENoNetworkRouting;
  70. iMaxWriteSamples = iRate / iWritesPerSec;
  71. if (iRate % iWritesPerSec)
  72. iMaxWriteSamples++;
  73. int bufferedFrames = iWritesPerSec / KUpdatesPerSec;
  74. iBufferSize = iMaxWriteSamples * (iStereo ? 4 : 2);
  75. iBufferSize *= bufferedFrames;
  76. for (TInt i=0 ; i<KSoundBuffers ; i++)
  77. {
  78. iSoundBuffers[i] = HBufC8::NewL(iBufferSize);
  79. iSoundBuffers[i]->Des().FillZ (iBufferSize);
  80. }
  81. iCurrentBuffer = 0;
  82. iCurrentBufferSize = 0;
  83. DEBUGPRINT(_L("sound: iMaxWriteSamples: %i, iBufferSize: %i"), iMaxWriteSamples, iBufferSize);
  84. // here we actually test if we can create and open CMdaAudioOutputStream at all, but really create and use it later.
  85. iMdaAudioOutputStream = CMdaAudioOutputStream::NewL(iListener, iServer);
  86. if (iMdaAudioOutputStream) {
  87. if (iVolume < 0 || iVolume > iMdaAudioOutputStream->MaxVolume())
  88. iVolume = iMdaAudioOutputStream->MaxVolume();
  89. delete iMdaAudioOutputStream;
  90. iMdaAudioOutputStream = 0;
  91. }
  92. }
  93. // returns a pointer to buffer for next frame,
  94. // to be used when iSoundBuffers are used directly
  95. TInt16 *CGameAudioMS::NextFrameL(TInt aPcmFrames)
  96. {
  97. TInt mul = iStereo ? 4 : 2;
  98. TInt bytes = aPcmFrames * mul;
  99. iCurrentPosition += bytes / 2;
  100. iCurrentBufferSize += bytes;
  101. if (aPcmFrames > iMaxWriteSamples) {
  102. DEBUGPRINT(_L("too many samples: %i > %i"), aPcmFrames, iMaxWriteSamples);
  103. }
  104. if (iCurrentBufferSize + iMaxWriteSamples * mul > iBufferSize)
  105. {
  106. //DEBUGPRINT(_L("write on iCurrentBufferSize %i"), iCurrentBufferSize);
  107. WriteBlockL();
  108. }
  109. iScheduler->Schedule();
  110. if(iListener.iUnderflowed) {
  111. if(iListener.iUnderflowed > KMaxUnderflows) {
  112. delete iMdaAudioOutputStream;
  113. iMdaAudioOutputStream = 0;
  114. return 0;
  115. }
  116. UnderflowedL(); // not again!
  117. }
  118. return iCurrentPosition;
  119. }
  120. void CGameAudioMS::WriteBlockL()
  121. {
  122. iScheduler->Schedule();
  123. // do not write until stream is open
  124. if(!iListener.iIsOpen) WaitForOpenToCompleteL();
  125. //if(!iListener.iHasCopied) WaitForCopyToCompleteL(); // almost never happens anyway and sometimes even deadlocks?
  126. //iListener.iHasCopied = EFalse;
  127. if(!iListener.iUnderflowed) {
  128. TInt64 delta;
  129. // don't write if sound is lagging too much
  130. delta = iTime - iMdaAudioOutputStream->Position().Int64();
  131. if (delta > MAKE_TINT64(0, KMaxLag))
  132. // another query sometimes returns very different result
  133. delta = iTime - iMdaAudioOutputStream->Position().Int64();
  134. if(delta <= MAKE_TINT64(0, KMaxLag)) {
  135. //RDebug::Print(_L("delta: %i"), iTime.Low() - iMdaAudioOutputStream->Position().Int64().Low());
  136. iSoundBuffers[iCurrentBuffer]->Des().SetLength(iCurrentBufferSize);
  137. iMdaAudioOutputStream->WriteL(*iSoundBuffers[iCurrentBuffer]);
  138. iTime += KBlockTime;
  139. } else {
  140. DEBUGPRINT(_L("lag: %i"), I64LOW(delta));
  141. }
  142. }
  143. if (++iCurrentBuffer == KSoundBuffers)
  144. iCurrentBuffer = 0;
  145. iSoundBuffers[iCurrentBuffer]->Des().SetMax();
  146. iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();
  147. iCurrentBufferSize = 0;
  148. }
  149. void CGameAudioMS::Pause()
  150. {
  151. if(!iMdaAudioOutputStream) return;
  152. iScheduler->Schedule(); // let it finish it's stuff
  153. iMdaAudioOutputStream->Stop();
  154. delete iMdaAudioOutputStream;
  155. iMdaAudioOutputStream = 0;
  156. }
  157. // call this before doing any playback!
  158. TInt16 *CGameAudioMS::ResumeL()
  159. {
  160. DEBUGPRINT(_L("CGameAudioMS::Resume()"));
  161. iScheduler->Schedule();
  162. // we act a bit strange here: simulate buffer underflow, which actually starts audio
  163. iListener.iIsOpen = ETrue;
  164. iListener.iUnderflowed = 1;
  165. iListener.iLastError = 0;
  166. iCurrentBufferSize = 0;
  167. iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();
  168. return iCurrentPosition;
  169. }
  170. // handles underflow condition
  171. void CGameAudioMS::UnderflowedL()
  172. {
  173. #ifdef DEBUG_UNDERFLOWS
  174. DEBUGPRINT(_L("UnderflowedL()"));
  175. #endif
  176. if (iListener.iLastError != KErrUnderflow)
  177. {
  178. // recreate the stream
  179. //iMdaAudioOutputStream->Stop();
  180. if(iMdaAudioOutputStream) delete iMdaAudioOutputStream;
  181. iMdaAudioOutputStream = CMdaAudioOutputStream::NewL(iListener, iServer);
  182. iMdaAudioOutputStream->Open(&iMdaAudioDataSettings);
  183. iMdaAudioOutputStream->SetAudioPropertiesL(iMdaAudioDataSettings.iSampleRate, iMdaAudioDataSettings.iChannels);
  184. iMdaAudioOutputStream->SetVolume(iVolume); // new in UIQ3
  185. iListener.iIsOpen = EFalse; // wait for it to open
  186. //iListener.iHasCopied = ETrue; // but don't wait for last copy to complete
  187. // let it open and feed some stuff to make it happy
  188. User::After(0);
  189. iScheduler->Schedule();
  190. iListener.iLastError = 0;
  191. if(!iListener.iIsOpen) WaitForOpenToCompleteL();
  192. } else {
  193. iListener.iLastError = iListener.iUnderflowed = 0;
  194. }
  195. iTime = iMdaAudioOutputStream->Position().Int64();
  196. }
  197. void CGameAudioMS::WaitForOpenToCompleteL()
  198. {
  199. DEBUGPRINT(_L("CGameAudioMS::WaitForOpenToCompleteL"));
  200. TInt count = 20; // 2 seconds
  201. TInt waitPeriod = 100 * 1000;
  202. if(!iListener.iIsOpen) {
  203. // it is often enough to do this
  204. User::After(0);
  205. iScheduler->Schedule();
  206. }
  207. while (!iListener.iIsOpen && --count)
  208. {
  209. User::After(waitPeriod);
  210. iScheduler->Schedule();
  211. }
  212. if (!iListener.iIsOpen)
  213. User::LeaveIfError(KErrNotSupported);
  214. }
  215. TInt CGameAudioMS::ChangeVolume(TInt aUp)
  216. {
  217. //DEBUGPRINT(_L("CGameAudioMS::ChangeVolume(%i)"), aUp);
  218. if (iMdaAudioOutputStream) {
  219. if (aUp) {
  220. iVolume += 5;
  221. if (iVolume > iMdaAudioOutputStream->MaxVolume())
  222. iVolume = iMdaAudioOutputStream->MaxVolume();
  223. } else {
  224. iVolume -= 5;
  225. if (iVolume < 0) iVolume = 0;
  226. }
  227. iMdaAudioOutputStream->SetVolume(iVolume);
  228. }
  229. return iVolume;
  230. }
  231. void TGameAudioEventListener::MaoscOpenComplete(TInt aError)
  232. {
  233. #ifdef DEBUG_UNDERFLOWS
  234. DEBUGPRINT(_L("CGameAudioMS::MaoscOpenComplete, error=%d"), aError);
  235. #endif
  236. iIsOpen = ETrue;
  237. if(aError) {
  238. iLastError = aError;
  239. iUnderflowed++;
  240. }
  241. else iUnderflowed = 0;
  242. }
  243. void TGameAudioEventListener::MaoscBufferCopied(TInt aError, const TDesC8& aBuffer)
  244. {
  245. if (aError)
  246. DEBUGPRINT(_L("CGameAudioMS::MaoscBufferCopied, error=%d"), aError);
  247. // iHasCopied = ETrue;
  248. if(aError) { // shit!
  249. iLastError = aError;
  250. iUnderflowed++;
  251. }
  252. }
  253. void TGameAudioEventListener::MaoscPlayComplete(TInt aError)
  254. {
  255. #ifdef DEBUG_UNDERFLOWS
  256. DEBUGPRINT(_L("CGameAudioMS::MaoscPlayComplete: %i"), aError);
  257. #endif
  258. if(aError) {
  259. iLastError = aError;
  260. iUnderflowed++; // never happened to me while testing, but just in case
  261. }
  262. }