rtsnd.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 RWS Inc, All Rights Reserved
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of version 2 of the GNU General Public License as published by
  7. // the Free Software Foundation
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License along
  15. // with this program; if not, write to the Free Software Foundation, Inc.,
  16. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. //
  18. //////////////////////////////////////////////////////////////////////////////
  19. //
  20. // RTSND.CPP
  21. //
  22. // History:
  23. // 10/31/95 JMI Started.
  24. //
  25. //////////////////////////////////////////////////////////////////////////////
  26. //
  27. // This class is designed to receive SND data in real time and play it
  28. // on a channel basis.
  29. //
  30. //////////////////////////////////////////////////////////////////////////////
  31. //////////////////////////////////////////////////////////////////////////////
  32. // C Headers.
  33. //////////////////////////////////////////////////////////////////////////////
  34. #include <malloc.h>
  35. //////////////////////////////////////////////////////////////////////////////
  36. // Blue Headers.
  37. //////////////////////////////////////////////////////////////////////////////
  38. #include "System.h"
  39. #include "bdebug.h"
  40. //////////////////////////////////////////////////////////////////////////////
  41. // Green Headers.
  42. //////////////////////////////////////////////////////////////////////////////
  43. #include "rttypes.h"
  44. #include "rtsnd.h"
  45. //////////////////////////////////////////////////////////////////////////////
  46. // Orange Headers.
  47. //////////////////////////////////////////////////////////////////////////////
  48. //////////////////////////////////////////////////////////////////////////////
  49. // Yellow Headers.
  50. //////////////////////////////////////////////////////////////////////////////
  51. //////////////////////////////////////////////////////////////////////////////
  52. // Module specific macros.
  53. //////////////////////////////////////////////////////////////////////////////
  54. // Types of chunks.
  55. #define SND_CHUNK_HEADER 0
  56. #define SND_CHUNK_DATA 1
  57. // Status flags.
  58. #define STATUS_OPENED 0x0001
  59. #define STATUS_STARTED 0x0002
  60. #define STATUS_DONE 0x0004
  61. #define STATUS_CLOSEME 0x0008
  62. #define STATUS_ERROR 0x8000
  63. #define DATACHUNKHEADERSIZE (sizeof(long))
  64. //////////////////////////////////////////////////////////////////////////////
  65. // Module specific typedefs.
  66. //////////////////////////////////////////////////////////////////////////////
  67. //////////////////////////////////////////////////////////////////////////////
  68. // Module specific (static) variables.
  69. //////////////////////////////////////////////////////////////////////////////
  70. CList<CRtSnd::SND_RT_HDR> CRtSnd::ms_listSndhdrs; // List of active channels.
  71. //////////////////////////////////////////////////////////////////////////////
  72. // Construction/Destruction Functions.
  73. //////////////////////////////////////////////////////////////////////////////
  74. //////////////////////////////////////////////////////////////////////////////
  75. //
  76. // Default constructor.
  77. //
  78. //////////////////////////////////////////////////////////////////////////////
  79. CRtSnd::CRtSnd()
  80. {
  81. Set();
  82. }
  83. //////////////////////////////////////////////////////////////////////////////
  84. //
  85. // Destructor.
  86. //
  87. //////////////////////////////////////////////////////////////////////////////
  88. CRtSnd::~CRtSnd()
  89. {
  90. Reset();
  91. }
  92. //////////////////////////////////////////////////////////////////////////////
  93. // Internal Functions.
  94. //////////////////////////////////////////////////////////////////////////////
  95. //////////////////////////////////////////////////////////////////////////////
  96. //
  97. // Sets variables w/o regard to current values.
  98. //
  99. //////////////////////////////////////////////////////////////////////////////
  100. void CRtSnd::Set(void)
  101. {
  102. m_pdispatch = NULL;
  103. for (short i = 0; i < MAX_SND_CHANNELS; i++)
  104. {
  105. m_asndhdrs[i].usStatus = 0;
  106. }
  107. }
  108. //////////////////////////////////////////////////////////////////////////////
  109. //
  110. // Resets variables. Performs deallocation if necessary.
  111. //
  112. //////////////////////////////////////////////////////////////////////////////
  113. void CRtSnd::Reset(void)
  114. {
  115. }
  116. //////////////////////////////////////////////////////////////////////////////
  117. //
  118. // Use handler for RtSnd buffers.
  119. // Returns RET_FREE if done with data on return, RET_DONTFREE otherwise.
  120. //
  121. //////////////////////////////////////////////////////////////////////////////
  122. short CRtSnd::Use(UCHAR* puc, long lSize, USHORT usType, UCHAR ucFlags,
  123. long lTime)
  124. {
  125. short sRes = RET_FREE; // Always free.
  126. short sError = 0;
  127. ASSERT(usType == RT_TYPE_SND);
  128. ASSERT(puc != NULL);
  129. CNFile file;
  130. file.Open(puc, lSize, ENDIAN_LITTLE);
  131. // Read values common to all chunks.
  132. // Read Snd ID.
  133. USHORT usSndId;
  134. file.Read (&usSndId);
  135. // Make sure we're in range.
  136. ASSERT(usSndId < MAX_SND_CHANNELS);
  137. // Get corresponding header.
  138. PSND_RT_HDR psndhdr = &m_asndhdrs[usSndId];
  139. // If this is a header chunk . . .
  140. if (ucFlags & RT_FLAG_INIT)
  141. {
  142. // Handle header chunk.
  143. file.Read(&psndhdr->lSamplesPerSec);
  144. file.Read(&psndhdr->sNumChannels);
  145. file.Read(&psndhdr->sBitsPerSample);
  146. file.Read(&psndhdr->lLead);
  147. // Verify we didn't read too much.
  148. ASSERT(file.Error() == FALSE);
  149. // Initialize status.
  150. psndhdr->usStatus = 0;
  151. // Init dispatcher.
  152. psndhdr->pdispatch = m_pdispatch;
  153. // Attempt to open the mixer channel.
  154. if (psndhdr->mix.OpenChannel( psndhdr->lSamplesPerSec, psndhdr->sBitsPerSample,
  155. psndhdr->sNumChannels) == 0)
  156. {
  157. // Successfully opened mixer channel.
  158. psndhdr->usStatus |= STATUS_OPENED;
  159. short sWasEmpty = ms_listSndhdrs.IsEmpty();
  160. // Add to criticial list.
  161. if (ms_listSndhdrs.Add(psndhdr) == 0)
  162. {
  163. // If this is the first . . .
  164. if (sWasEmpty == TRUE)
  165. {
  166. // Start critical handler that starts the mixing . . .
  167. if (Blu_AddCritical(CritiCall, (ULONG)this) == 0)
  168. {
  169. // Success.
  170. }
  171. else
  172. {
  173. TRACE("Use(): Unable to add CritiCall to critical list.\n");
  174. sError = 6;
  175. }
  176. }
  177. }
  178. else
  179. {
  180. TRACE("Use(): Unable to add RT_SND_HDR to critical list.\n");
  181. sError = 5;
  182. }
  183. }
  184. else
  185. {
  186. TRACE("Use(): Unable to open mix channel.\n");
  187. sError = 1;
  188. }
  189. }
  190. else
  191. {
  192. // If no errors have occurred on this channel . . .
  193. if ((psndhdr->usStatus & STATUS_ERROR) == 0)
  194. {
  195. // Mixer channel must be open at this point.
  196. ASSERT(psndhdr->usStatus & STATUS_OPENED);
  197. // Create a SNDBUF for this data . . .
  198. PSNDBUF psb = new SNDBUF;
  199. if (psb != NULL)
  200. {
  201. // Fill.
  202. psb->puc = puc;
  203. psb->lSize = lSize;
  204. psb->lTime = lTime + psndhdr->lLead;
  205. psb->sLast = ((ucFlags & RT_FLAG_LAST) ? TRUE : FALSE);
  206. // Add to queue . . .
  207. if (psndhdr->qsndbufs.EnQ(psb) == 0)
  208. {
  209. // Chunk is in queue, do not free.
  210. sRes = RET_DONTFREE;
  211. }
  212. else
  213. {
  214. TRACE("Use(): Unable to EnQ SNDBUF.\n");
  215. sError = 3;
  216. // Enqueue failed.
  217. delete psb;
  218. }
  219. }
  220. else
  221. {
  222. TRACE("Use(): Unable to allocate new SNDBUF.\n");
  223. sError = 2;
  224. }
  225. }
  226. // Verify we didn't read too much.
  227. ASSERT(file.Error() == FALSE);
  228. }
  229. file.Close();
  230. if (sError != 0)
  231. {
  232. // If started . . .
  233. if (psndhdr->usStatus & STATUS_STARTED)
  234. {
  235. if (psndhdr->mix.Suspend() == 0)
  236. {
  237. // Suspended mixing on this channel.
  238. }
  239. else
  240. {
  241. TRACE("Use(): Failed to suspend mixing after error.\n");
  242. }
  243. }
  244. // If opened . . .
  245. if (psndhdr->usStatus & STATUS_OPENED)
  246. {
  247. if (psndhdr->mix.CloseChannel() == 0)
  248. {
  249. // Closed mixer channel.
  250. }
  251. else
  252. {
  253. TRACE("Use(): Failed to close mixer channel after error.\n");
  254. }
  255. }
  256. psndhdr->usStatus = STATUS_ERROR;
  257. }
  258. return sRes;
  259. }
  260. //////////////////////////////////////////////////////////////////////////////
  261. //
  262. // Callback for mixer.
  263. // Returns new buffer to play or NULL if none.
  264. // (static)
  265. //
  266. //////////////////////////////////////////////////////////////////////////////
  267. void* CRtSnd::MixCall( USHORT usMsg, void* pData, ULONG* pulBufSize,
  268. ULONG ul_psndhdr)
  269. {
  270. PSND_RT_HDR psndhdr = (PSND_RT_HDR)ul_psndhdr;
  271. PSNDBUF psb;
  272. short sLast = FALSE;
  273. switch (usMsg)
  274. {
  275. case BLU_SNDMSG_PREPLAYERR:
  276. case BLU_SNDMSG_POSTPLAYERR:
  277. case BLU_SNDMSG_OK:
  278. case MIX_SNDMSG_QUEUEING:
  279. // If this is not the first time since we (re)started . . .
  280. if (pData != NULL)
  281. {
  282. // Get buffer that's done.
  283. psb = psndhdr->qsndbufs.DeQ();
  284. // Must get.
  285. ASSERT(psb != NULL)
  286. // Should match supplied.
  287. if /*ASSERT*/(psb->puc + DATACHUNKHEADERSIZE == (UCHAR*)pData)
  288. TRACE("MixCall(): Not the expected pointer.\n");
  289. // Set last flag.
  290. sLast = psb->sLast;
  291. // Free buffer.
  292. free(psb->puc);
  293. // Delete encapsulator.
  294. delete psb;
  295. }
  296. // If previous was not the last buffer . . .
  297. if (sLast == FALSE)
  298. {
  299. // Get the next buffer that's ready . . .
  300. psb = psndhdr->qsndbufs.Peek();
  301. if (psb != NULL)
  302. {
  303. // Set data pointer and size.
  304. pData = psb->puc + DATACHUNKHEADERSIZE;
  305. *pulBufSize = psb->lSize - DATACHUNKHEADERSIZE;
  306. }
  307. else
  308. {
  309. psndhdr->usStatus |= STATUS_ERROR;
  310. TRACE("MixCall(): No buffers in queue!!\n");
  311. pData = NULL;
  312. }
  313. }
  314. else
  315. {
  316. // We're done. Let mixer know.
  317. pData = NULL;
  318. // Mark channel as done.
  319. psndhdr->usStatus |= STATUS_DONE;
  320. }
  321. // If we're going to return NULL . . .
  322. if (pData == NULL)
  323. {
  324. // Returning NULL will stop callbacks.
  325. }
  326. break;
  327. case MIX_SNDMSG_SUSPENDED:
  328. // Remove started flag.
  329. psndhdr->usStatus &= ~STATUS_STARTED;
  330. // If we stopped b/c we're done . . .
  331. if (psndhdr->usStatus & STATUS_DONE)
  332. {
  333. if (psndhdr->mix.CloseChannel() == 0)
  334. {
  335. // Success.
  336. // Remove close me flag, opened flag, and done flag.
  337. psndhdr->usStatus &= ~(STATUS_OPENED | STATUS_DONE);
  338. // No longer need the callback.
  339. ms_listSndhdrs.Remove(psndhdr);
  340. // If the reference count hits zero . . .
  341. if (ms_listSndhdrs.IsEmpty() == TRUE)
  342. {
  343. if (Blu_RemoveCritical(CritiCall) == 0)
  344. {
  345. // Success.
  346. }
  347. else
  348. {
  349. TRACE("CritiCall(): Unable to remove critical handler.\n");
  350. }
  351. }
  352. }
  353. else
  354. {
  355. TRACE("CritiCall(): Unable to close mix channel.\n");
  356. }
  357. }
  358. break;
  359. }
  360. return pData;
  361. }
  362. //////////////////////////////////////////////////////////////////////////////
  363. //
  364. // (Re)starts the mixing in the beginning and whenever a break up occurs due
  365. // to streaming and closes the mix channel when done.
  366. // (static)
  367. //
  368. //////////////////////////////////////////////////////////////////////////////
  369. void CRtSnd::CritiCall(ULONG)
  370. {
  371. PSND_RT_HDR psndhdr = ms_listSndhdrs.GetHead();
  372. while(psndhdr != NULL)
  373. {
  374. short sError = 0;
  375. long lTime = psndhdr->pdispatch->GetTime();
  376. // If channel open . . .
  377. if (psndhdr->usStatus & STATUS_OPENED)
  378. {
  379. // If channel not started . . .
  380. if ((psndhdr->usStatus & STATUS_STARTED) == 0)
  381. {
  382. // Look at next chunk.
  383. PSNDBUF psb = psndhdr->qsndbufs.Peek();
  384. if (psb != NULL)
  385. {
  386. // If it is time for this chunk . . .
  387. if (psb->lTime <= lTime)
  388. {
  389. // Attempt to start mixing in our channel . . .
  390. if (psndhdr->mix.Start(MixCall, (ULONG)psndhdr, 0) == 0)
  391. {
  392. psndhdr->usStatus |= STATUS_STARTED;
  393. }
  394. else
  395. {
  396. TRACE("CritiCall(): Unable to start mixing.\n");
  397. sError = 2;
  398. }
  399. }
  400. }
  401. }
  402. }
  403. // If any errors occurred . . .
  404. if (sError != 0)
  405. {
  406. psndhdr->usStatus |= STATUS_ERROR;
  407. }
  408. psndhdr = ms_listSndhdrs.GetNext();
  409. }
  410. }
  411. //////////////////////////////////////////////////////////////////////////////
  412. //
  413. // Callback dispatcher (calls the implied this version).
  414. // (static)
  415. //
  416. //////////////////////////////////////////////////////////////////////////////
  417. short CRtSnd::UseStatic( UCHAR* puc, long lSize, USHORT usType,
  418. UCHAR ucFlags, long lTime, long l_pRtSnd)
  419. {
  420. return ((CRtSnd*)l_pRtSnd)->Use(puc, lSize, usType, ucFlags, lTime);
  421. }
  422. //////////////////////////////////////////////////////////////////////////////
  423. // Methods.
  424. //////////////////////////////////////////////////////////////////////////////
  425. //////////////////////////////////////////////////////////////////////////////
  426. //
  427. // Set dispatcher.
  428. //
  429. //////////////////////////////////////////////////////////////////////////////
  430. void CRtSnd::SetDispatcher(CDispatch* pdispatch)
  431. {
  432. if (m_pdispatch != NULL)
  433. {
  434. m_pdispatch->SetDataHandler(RT_TYPE_SND, NULL);
  435. }
  436. m_pdispatch = pdispatch;
  437. if (m_pdispatch != NULL)
  438. {
  439. m_pdispatch->SetDataHandler(RT_TYPE_SND, UseStatic);
  440. m_pdispatch->SetUserVal(RT_TYPE_SND, (long)this);
  441. }
  442. }
  443. //////////////////////////////////////////////////////////////////////////////
  444. // EOF
  445. //////////////////////////////////////////////////////////////////////////////