SoundDecoder.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. /* Copyright (c) 2002-2012 Croteam Ltd.
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of version 2 of the GNU General Public License as published by
  4. the Free Software Foundation
  5. This program is distributed in the hope that it will be useful,
  6. but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. GNU General Public License for more details.
  9. You should have received a copy of the GNU General Public License along
  10. with this program; if not, write to the Free Software Foundation, Inc.,
  11. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
  12. #include "stdh.h"
  13. #include <Engine/Sound/SoundDecoder.h>
  14. #include <Engine/Base/Stream.h>
  15. #include <Engine/Base/Console.h>
  16. #include <Engine/Base/ErrorReporting.h>
  17. #include <Engine/Base/FileName.h>
  18. #include <Engine/Base/Unzip.h>
  19. #include <Engine/Base/Translation.h>
  20. #include <Engine/Math/Functions.h>
  21. // generic function called if a dll function is not found
  22. static void FailFunction_t(const char *strName) {
  23. ThrowF_t(TRANS("Function %s not found."), strName);
  24. }
  25. // ------------------------------------ AMP11
  26. // amp11lib vars
  27. extern BOOL _bAMP11Enabled = FALSE;
  28. static HINSTANCE _hAmp11lib = NULL;
  29. // amp11lib types
  30. typedef signed char ALsint8;
  31. typedef unsigned char ALuint8;
  32. typedef signed short ALsint16;
  33. typedef unsigned short ALuint16;
  34. typedef signed int ALsint32;
  35. typedef unsigned int ALuint32;
  36. typedef signed int ALsize;
  37. typedef int ALbool;
  38. typedef float ALfloat;
  39. #define ALtrue 1
  40. #define ALfalse 0
  41. typedef ALsint32 ALhandle;
  42. // define amp11lib function pointers
  43. #define DLLFUNCTION(dll, output, name, inputs, params, required) \
  44. output (__stdcall *p##name) inputs = NULL;
  45. #include "al_functions.h"
  46. #undef DLLFUNCTION
  47. static void AMP11_SetFunctionPointers_t(void) {
  48. const char *strName;
  49. // get amp11lib function pointers
  50. #define DLLFUNCTION(dll, output, name, inputs, params, required) \
  51. strName = "_" #name "@" #params; \
  52. p##name = (output (__stdcall*) inputs) GetProcAddress( _hAmp11lib, strName); \
  53. if(p##name == NULL) FailFunction_t(strName);
  54. #include "al_functions.h"
  55. #undef DLLFUNCTION
  56. }
  57. static void AMP11_ClearFunctionPointers(void) {
  58. // clear amp11lib function pointers
  59. #define DLLFUNCTION(dll, output, name, inputs, params, required) p##name = NULL;
  60. #include "al_functions.h"
  61. #undef DLLFUNCTION
  62. }
  63. class CDecodeData_MPEG {
  64. public:
  65. ALhandle mpeg_hMainFile; // mainfile handle if using subfile
  66. ALhandle mpeg_hFile; // file handle
  67. ALhandle mpeg_hDecoder; // the decoder handle
  68. FLOAT mpeg_fSecondsLen; // length of sound in seconds
  69. WAVEFORMATEX mpeg_wfeFormat; // format of sound
  70. };
  71. // ------------------------------------ Ogg Vorbis
  72. #include <vorbis\vorbisfile.h> // we define needed stuff ourselves, and ignore the rest
  73. // vorbis vars
  74. extern BOOL _bOVEnabled = FALSE;
  75. static HINSTANCE _hOV = NULL;
  76. class CDecodeData_OGG {
  77. public:
  78. FILE *ogg_fFile; // the stdio file that ogg is in
  79. SLONG ogg_slOffset; // offset where the ogg starts in the file (!=0 for oggs in zip)
  80. SLONG ogg_slSize; // size of ogg in the file (!=filesize for oggs in zip)
  81. OggVorbis_File *ogg_vfVorbisFile; // the decoder file
  82. WAVEFORMATEX ogg_wfeFormat; // format of sound
  83. };
  84. // define vorbis function pointers
  85. #define DLLFUNCTION(dll, output, name, inputs, params, required) \
  86. output (__cdecl *p##name) inputs = NULL;
  87. #include "ov_functions.h"
  88. #undef DLLFUNCTION
  89. static void OV_SetFunctionPointers_t(void) {
  90. const char *strName;
  91. // get vo function pointers
  92. #define DLLFUNCTION(dll, output, name, inputs, params, required) \
  93. strName = #name ; \
  94. p##name = (output (__cdecl *) inputs) GetProcAddress( _hOV, strName); \
  95. if(p##name == NULL) FailFunction_t(strName);
  96. #include "ov_functions.h"
  97. #undef DLLFUNCTION
  98. }
  99. static void OV_ClearFunctionPointers(void) {
  100. // clear vo function pointers
  101. #define DLLFUNCTION(dll, output, name, inputs, params, required) p##name = NULL;
  102. #include "ov_functions.h"
  103. #undef DLLFUNCTION
  104. }
  105. // ogg file reading callbacks
  106. //
  107. static size_t ogg_read_func (void *ptr, size_t size, size_t nmemb, void *datasource)
  108. {
  109. CDecodeData_OGG *pogg = (CDecodeData_OGG *)datasource;
  110. // calculate how much can be read at most
  111. SLONG slToRead = size*nmemb;
  112. SLONG slCurrentPos = ftell(pogg->ogg_fFile)-pogg->ogg_slOffset;
  113. SLONG slSizeLeft = ClampDn(pogg->ogg_slSize-slCurrentPos, 0L);
  114. slToRead = ClampUp(slToRead, slSizeLeft);
  115. // rounded down to the block size
  116. slToRead/=size;
  117. slToRead*=size;
  118. // if there is nothing to read
  119. if (slToRead<=0) {
  120. return 0;
  121. }
  122. return fread(ptr, size, slToRead/size, pogg->ogg_fFile);
  123. }
  124. static int ogg_seek_func (void *datasource, ogg_int64_t offset, int whence)
  125. {
  126. return -1;
  127. /* !!!! seeking is evil with vorbisfile 1.0RC2
  128. CDecodeData_OGG *pogg = (CDecodeData_OGG *)datasource;
  129. SLONG slCurrentPos = ftell(pogg->ogg_fFile)-pogg->ogg_slOffset;
  130. if (whence==SEEK_CUR) {
  131. return fseek(pogg->ogg_fFile, offset, SEEK_CUR);
  132. } else if (whence==SEEK_END) {
  133. return fseek(pogg->ogg_fFile, pogg->ogg_slOffset+pogg->ogg_slSize-offset, SEEK_SET);
  134. } else {
  135. ASSERT(whence==SEEK_SET);
  136. return fseek(pogg->ogg_fFile, pogg->ogg_slOffset+offset, SEEK_SET);
  137. }
  138. */
  139. }
  140. static int ogg_close_func (void *datasource)
  141. {
  142. return 0;
  143. /* !!!! closing is evil with vorbisfile 1.0RC2
  144. CDecodeData_OGG *pogg = (CDecodeData_OGG *)datasource;
  145. fclose(pogg->ogg_fFile);
  146. */
  147. }
  148. static long ogg_tell_func (void *datasource)
  149. {
  150. return -1;
  151. /* !!!! seeking is evil with vorbisfile 1.0RC2
  152. CDecodeData_OGG *pogg = (CDecodeData_OGG *)datasource;
  153. ftell(pogg->ogg_fFile)-pogg->ogg_slOffset;
  154. */
  155. }
  156. static ov_callbacks ovcCallbacks = {
  157. ogg_read_func,
  158. ogg_seek_func,
  159. ogg_close_func,
  160. ogg_tell_func,
  161. };
  162. // initialize/end the decoding support engine(s)
  163. void CSoundDecoder::InitPlugins(void)
  164. {
  165. try {
  166. // load vorbis
  167. if (_hOV==NULL) {
  168. #ifndef NDEBUG
  169. #define VORBISLIB "libvorbisfile.dll"
  170. #else
  171. #define VORBISLIB "libvorbisfile.dll"
  172. #endif
  173. _hOV = ::LoadLibraryA(VORBISLIB);
  174. }
  175. if( _hOV == NULL) {
  176. ThrowF_t(TRANS("Cannot load libvorbisfile.dll."));
  177. }
  178. // prepare function pointers
  179. OV_SetFunctionPointers_t();
  180. // if all successful, enable mpx playing
  181. _bOVEnabled = TRUE;
  182. CPrintF(TRANS(" libvorbisfile.dll loaded, ogg playing enabled\n"));
  183. } catch (char *strError) {
  184. CPrintF(TRANS("OGG playing disabled: %s\n"), strError);
  185. }
  186. try {
  187. // load amp11lib
  188. if (_hAmp11lib==NULL) {
  189. _hAmp11lib = ::LoadLibraryA( "amp11lib.dll");
  190. }
  191. if( _hAmp11lib == NULL) {
  192. ThrowF_t(TRANS("Cannot load amp11lib.dll."));
  193. }
  194. // prepare function pointers
  195. AMP11_SetFunctionPointers_t();
  196. // initialize amp11lib before calling any of its functions
  197. palInitLibrary();
  198. // if all successful, enable mpx playing
  199. _bAMP11Enabled = TRUE;
  200. CPrintF(TRANS(" amp11lib.dll loaded, mpx playing enabled\n"));
  201. } catch (char *strError) {
  202. CPrintF(TRANS("MPX playing disabled: %s\n"), strError);
  203. }
  204. }
  205. void CSoundDecoder::EndPlugins(void)
  206. {
  207. // cleanup amp11lib when not needed anymore
  208. if (_bAMP11Enabled) {
  209. palEndLibrary();
  210. AMP11_ClearFunctionPointers();
  211. FreeLibrary(_hAmp11lib);
  212. _hAmp11lib = NULL;
  213. _bAMP11Enabled = FALSE;
  214. }
  215. // cleanup vorbis when not needed anymore
  216. if (_bOVEnabled) {
  217. OV_ClearFunctionPointers();
  218. FreeLibrary(_hOV);
  219. _hOV = NULL;
  220. _bOVEnabled = FALSE;
  221. }
  222. }
  223. // decoder that streams from file
  224. CSoundDecoder::CSoundDecoder(const CTFileName &fnm)
  225. {
  226. sdc_pogg = NULL;
  227. sdc_pmpeg = NULL;
  228. CTFileName fnmExpanded;
  229. INDEX iFileType = ExpandFilePath(EFP_READ, fnm, fnmExpanded);
  230. // if ogg
  231. if (fnmExpanded.FileExt()==".ogg") {
  232. if (!_bOVEnabled) {
  233. return;
  234. }
  235. sdc_pogg = new CDecodeData_OGG;
  236. sdc_pogg->ogg_fFile = NULL;
  237. sdc_pogg->ogg_vfVorbisFile = NULL;
  238. sdc_pogg->ogg_slOffset = 0;
  239. sdc_pogg->ogg_slSize = 0;
  240. INDEX iZipHandle = 0;
  241. try {
  242. // if in zip
  243. if (iFileType==EFP_BASEZIP || iFileType==EFP_MODZIP) {
  244. // open it
  245. iZipHandle = UNZIPOpen_t(fnmExpanded);
  246. CTFileName fnmZip;
  247. SLONG slOffset;
  248. SLONG slSizeCompressed;
  249. SLONG slSizeUncompressed;
  250. BOOL bCompressed;
  251. UNZIPGetFileInfo(iZipHandle, fnmZip, slOffset, slSizeCompressed, slSizeUncompressed, bCompressed);
  252. // if compressed
  253. if (bCompressed) {
  254. ThrowF_t(TRANS("encoded audio in archives must not be compressed!\n"));
  255. }
  256. // open ogg file
  257. sdc_pogg->ogg_fFile = fopen(fnmZip, "rb");
  258. // if error
  259. if (sdc_pogg->ogg_fFile==0) {
  260. ThrowF_t(TRANS("cannot open archive '%s'"), (const char*)fnmZip);
  261. }
  262. // remember offset and size
  263. sdc_pogg->ogg_slOffset = slOffset;
  264. sdc_pogg->ogg_slSize = slSizeUncompressed;
  265. fseek(sdc_pogg->ogg_fFile, slOffset, SEEK_SET);
  266. // if not in zip
  267. } else if (iFileType==EFP_FILE) {
  268. // open ogg file
  269. sdc_pogg->ogg_fFile = fopen(fnmExpanded, "rb");
  270. // if error
  271. if (sdc_pogg->ogg_fFile==0) {
  272. ThrowF_t(TRANS("cannot open encoded audio file"));
  273. }
  274. // remember offset and size
  275. sdc_pogg->ogg_slOffset = 0;
  276. fseek(sdc_pogg->ogg_fFile, 0, SEEK_END);
  277. sdc_pogg->ogg_slSize = ftell(sdc_pogg->ogg_fFile);
  278. fseek(sdc_pogg->ogg_fFile, 0, SEEK_SET);
  279. // if not found
  280. } else {
  281. ThrowF_t(TRANS("file not found"));
  282. }
  283. // initialize decoder
  284. sdc_pogg->ogg_vfVorbisFile = new OggVorbis_File;
  285. int iRes = pov_open_callbacks(sdc_pogg, sdc_pogg->ogg_vfVorbisFile, NULL, 0, ovcCallbacks);
  286. // if error
  287. if (iRes!=0) {
  288. ThrowF_t(TRANS("cannot open ogg decoder"));
  289. }
  290. // get info on the file
  291. vorbis_info *pvi = pov_info(sdc_pogg->ogg_vfVorbisFile, -1);
  292. // remember it's format
  293. WAVEFORMATEX form;
  294. form.wFormatTag=WAVE_FORMAT_PCM;
  295. form.nChannels=pvi->channels;
  296. form.nSamplesPerSec=pvi->rate;
  297. form.wBitsPerSample=16;
  298. form.nBlockAlign=form.nChannels*form.wBitsPerSample/8;
  299. form.nAvgBytesPerSec=form.nSamplesPerSec*form.nBlockAlign;
  300. form.cbSize=0;
  301. // check for stereo
  302. if (pvi->channels!=2) {
  303. ThrowF_t(TRANS("not stereo"));
  304. }
  305. sdc_pogg->ogg_wfeFormat = form;
  306. } catch (char*strError) {
  307. CPrintF(TRANS("Cannot open encoded audio '%s' for streaming: %s\n"), (const char*)fnm, (const char*)strError);
  308. if (sdc_pogg->ogg_vfVorbisFile!=NULL) {
  309. delete sdc_pogg->ogg_vfVorbisFile;
  310. sdc_pogg->ogg_vfVorbisFile = NULL;
  311. }
  312. if (sdc_pogg->ogg_fFile!=NULL) {
  313. fclose(sdc_pogg->ogg_fFile);
  314. sdc_pogg->ogg_fFile = NULL;
  315. }
  316. if (iZipHandle!=0) {
  317. UNZIPClose(iZipHandle);
  318. }
  319. Clear();
  320. return;
  321. }
  322. if (iZipHandle!=0) {
  323. UNZIPClose(iZipHandle);
  324. }
  325. // if mp3
  326. } else if (fnmExpanded.FileExt()==".mp3") {
  327. if (!_bAMP11Enabled) {
  328. return;
  329. }
  330. sdc_pmpeg = new CDecodeData_MPEG;
  331. sdc_pmpeg->mpeg_hMainFile = 0;
  332. sdc_pmpeg->mpeg_hFile = 0;
  333. sdc_pmpeg->mpeg_hDecoder = 0;
  334. INDEX iZipHandle = 0;
  335. try {
  336. // if in zip
  337. if (iFileType==EFP_BASEZIP || iFileType==EFP_MODZIP) {
  338. // open it
  339. iZipHandle = UNZIPOpen_t(fnmExpanded);
  340. CTFileName fnmZip;
  341. SLONG slOffset;
  342. SLONG slSizeCompressed;
  343. SLONG slSizeUncompressed;
  344. BOOL bCompressed;
  345. UNZIPGetFileInfo(iZipHandle, fnmZip, slOffset, slSizeCompressed, slSizeUncompressed, bCompressed);
  346. // if compressed
  347. if (bCompressed) {
  348. ThrowF_t(TRANS("encoded audio in archives must not be compressed!\n"));
  349. }
  350. // open the zip file
  351. sdc_pmpeg->mpeg_hMainFile = palOpenInputFile(fnmZip);
  352. // if error
  353. if (sdc_pmpeg->mpeg_hMainFile==0) {
  354. ThrowF_t(TRANS("cannot open archive '%s'"), (const char*)fnmZip);
  355. }
  356. // open the subfile
  357. sdc_pmpeg->mpeg_hFile = palOpenSubFile(sdc_pmpeg->mpeg_hMainFile, slOffset, slSizeUncompressed);
  358. // if error
  359. if (sdc_pmpeg->mpeg_hFile==0) {
  360. ThrowF_t(TRANS("cannot open encoded audio file"));
  361. }
  362. // if not in zip
  363. } else if (iFileType==EFP_FILE) {
  364. // open mpx file
  365. sdc_pmpeg->mpeg_hFile = palOpenInputFile(fnmExpanded);
  366. // if error
  367. if (sdc_pmpeg->mpeg_hFile==0) {
  368. ThrowF_t(TRANS("cannot open mpx file"));
  369. }
  370. // if not found
  371. } else {
  372. ThrowF_t(TRANS("file not found"));
  373. }
  374. // get info on the file
  375. int layer, ver, freq, stereo, rate;
  376. if (!palGetMPXHeader(sdc_pmpeg->mpeg_hFile, &layer, &ver, &freq, &stereo, &rate)) {
  377. ThrowF_t(TRANS("not a valid mpeg audio file."));
  378. }
  379. // remember it's format
  380. WAVEFORMATEX form;
  381. form.wFormatTag=WAVE_FORMAT_PCM;
  382. form.nChannels=stereo?2:1;
  383. form.nSamplesPerSec=freq;
  384. form.wBitsPerSample=16;
  385. form.nBlockAlign=form.nChannels*form.wBitsPerSample/8;
  386. form.nAvgBytesPerSec=form.nSamplesPerSec*form.nBlockAlign;
  387. form.cbSize=0;
  388. // check for stereo
  389. if (!stereo) {
  390. ThrowF_t(TRANS("not stereo"));
  391. }
  392. sdc_pmpeg->mpeg_wfeFormat = form;
  393. // initialize decoder
  394. sdc_pmpeg->mpeg_hDecoder = palOpenDecoder(sdc_pmpeg->mpeg_hFile);
  395. // if error
  396. if (sdc_pmpeg->mpeg_hDecoder==0) {
  397. ThrowF_t(TRANS("cannot open mpx decoder"));
  398. }
  399. } catch (char*strError) {
  400. CPrintF(TRANS("Cannot open mpx '%s' for streaming: %s\n"), (const char*)fnm, (const char*)strError);
  401. if (iZipHandle!=0) {
  402. UNZIPClose(iZipHandle);
  403. }
  404. Clear();
  405. return;
  406. }
  407. if (iZipHandle!=0) {
  408. UNZIPClose(iZipHandle);
  409. }
  410. sdc_pmpeg->mpeg_fSecondsLen = palDecGetLen(sdc_pmpeg->mpeg_hDecoder);
  411. }
  412. }
  413. CSoundDecoder::~CSoundDecoder(void)
  414. {
  415. Clear();
  416. }
  417. void CSoundDecoder::Clear(void)
  418. {
  419. if (sdc_pmpeg!=NULL) {
  420. if (sdc_pmpeg->mpeg_hDecoder!=0) palClose(sdc_pmpeg->mpeg_hDecoder);
  421. if (sdc_pmpeg->mpeg_hFile!=0) palClose(sdc_pmpeg->mpeg_hFile);
  422. if (sdc_pmpeg->mpeg_hMainFile!=0) palClose(sdc_pmpeg->mpeg_hMainFile);
  423. sdc_pmpeg->mpeg_hMainFile = 0;
  424. sdc_pmpeg->mpeg_hFile = 0;
  425. sdc_pmpeg->mpeg_hDecoder = 0;
  426. delete sdc_pmpeg;
  427. sdc_pmpeg = NULL;
  428. } else if (sdc_pogg!=NULL) {
  429. if (sdc_pogg->ogg_vfVorbisFile!=NULL) {
  430. pov_clear(sdc_pogg->ogg_vfVorbisFile);
  431. delete sdc_pogg->ogg_vfVorbisFile;
  432. sdc_pogg->ogg_vfVorbisFile = NULL;
  433. }
  434. if (sdc_pogg->ogg_fFile!=NULL) {
  435. fclose(sdc_pogg->ogg_fFile);
  436. sdc_pogg->ogg_fFile = NULL;
  437. }
  438. delete sdc_pogg;
  439. sdc_pogg = NULL;
  440. }
  441. }
  442. // reset decoder to start of sample
  443. void CSoundDecoder::Reset(void)
  444. {
  445. if (sdc_pmpeg!=NULL) {
  446. palDecSeekAbs(sdc_pmpeg->mpeg_hDecoder, 0.0f);
  447. } else if (sdc_pogg!=NULL) {
  448. // so instead, we reinit
  449. pov_clear(sdc_pogg->ogg_vfVorbisFile);
  450. fseek(sdc_pogg->ogg_fFile, sdc_pogg->ogg_slOffset, SEEK_SET);
  451. pov_open_callbacks(sdc_pogg, sdc_pogg->ogg_vfVorbisFile, NULL, 0, ovcCallbacks);
  452. }
  453. }
  454. BOOL CSoundDecoder::IsOpen(void)
  455. {
  456. if (sdc_pmpeg!=NULL && sdc_pmpeg->mpeg_hDecoder!=0) {
  457. return TRUE;
  458. } else if (sdc_pogg!=NULL && sdc_pogg->ogg_vfVorbisFile!=0) {
  459. return TRUE;
  460. } else {
  461. return FALSE;
  462. }
  463. }
  464. void CSoundDecoder::GetFormat(WAVEFORMATEX &wfe)
  465. {
  466. if (sdc_pmpeg!=NULL) {
  467. wfe = sdc_pmpeg->mpeg_wfeFormat;
  468. } else if (sdc_pogg!=NULL) {
  469. wfe = sdc_pogg->ogg_wfeFormat;
  470. } else {
  471. NOTHING;
  472. }
  473. }
  474. // decode a block of bytes
  475. INDEX CSoundDecoder::Decode(void *pvDestBuffer, INDEX ctBytesToDecode)
  476. {
  477. // if ogg
  478. if (sdc_pogg!=NULL && sdc_pogg->ogg_vfVorbisFile!=0) {
  479. // decode ogg
  480. static int iCurrrentSection = -1; // we don't care about this
  481. char *pch = (char *)pvDestBuffer;
  482. INDEX ctDecoded = 0;
  483. while (ctDecoded<ctBytesToDecode) {
  484. long iRes = pov_read(sdc_pogg->ogg_vfVorbisFile, pch, ctBytesToDecode-ctDecoded,
  485. 0, 2, 1, &iCurrrentSection);
  486. if (iRes<=0) {
  487. return ctDecoded;
  488. }
  489. ctDecoded+=iRes;
  490. pch+=iRes;
  491. }
  492. return ctDecoded;
  493. // if mpeg
  494. } else if (sdc_pmpeg!=NULL && sdc_pmpeg->mpeg_hDecoder!=0) {
  495. // decode mpeg
  496. return palRead(sdc_pmpeg->mpeg_hDecoder, pvDestBuffer, ctBytesToDecode);
  497. // if no decoder
  498. } else {
  499. // play all zeroes
  500. memset(pvDestBuffer, 0, ctBytesToDecode);
  501. return ctBytesToDecode;
  502. }
  503. }