PageRenderTime 54ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/Multimedia/OpenTK/OggInputStream.cs

#
C# | 691 lines | 411 code | 82 blank | 198 comment | 68 complexity | ebe1bbbbf18f207bf76741a1146d351c MD5 | raw file
Possible License(s): Apache-2.0
  1. using System;
  2. using System.IO;
  3. using csogg;
  4. using csvorbis;
  5. using Delta.Utilities;
  6. using OpenTK.Audio.OpenAL;
  7. namespace Delta.Multimedia.OpenTK
  8. {
  9. /// <summary>
  10. /// Ogg input stream class used to decode an ogg vorbis input stream into
  11. /// usable pcm data for OpenAL.
  12. /// <para />
  13. /// As reference the original file can be found here:
  14. /// http://www.opentk.com/files/OggInputStream.cs
  15. /// </summary>
  16. internal class OggInputStream
  17. {
  18. #region Constants
  19. /// <summary>
  20. /// Default conversion buffer size is 8192 bytes!
  21. /// </summary>
  22. private const int DefaultConvsize = 4096 * 2;
  23. /// <summary>
  24. /// Initialize conversion buffer with 8192 bytes by default.
  25. /// The buffer will contain all channels at once, so the size of
  26. /// the convbuffer per channel can be calculated by DefaultConvsize / channels.
  27. /// See convsizePerChannel variable and the "InitVorbis" method.
  28. /// </summary>
  29. private static byte[] convbuffer = new byte[DefaultConvsize];
  30. #endregion
  31. #region Format (Public)
  32. /// <summary>
  33. /// Gets the format of the ogg file. Is either MONO16 or STEREO16.
  34. /// </summary>
  35. public ALFormat Format
  36. {
  37. get
  38. {
  39. return info.channels > 1
  40. ? ALFormat.Stereo16
  41. : ALFormat.Mono16;
  42. }
  43. }
  44. #endregion
  45. #region SampleRate (Public)
  46. public int SampleRate
  47. {
  48. get
  49. {
  50. return info.rate;
  51. }
  52. }
  53. #endregion
  54. #region Private
  55. #region convsizePerChannel (Private)
  56. /// <summary>
  57. /// The size of the convbuffer per channel. Currently set as
  58. /// the Default, but will be changed in the "InitVorbis" method to
  59. /// DefaultConvsize / channels.
  60. /// </summary>
  61. private static int convsizePerChannel = DefaultConvsize;
  62. #endregion
  63. #region _pcm (Private)
  64. /// <summary>
  65. /// temp vars
  66. /// </summary>
  67. private readonly float[][][] _pcm = new float[1][][];
  68. #endregion
  69. #region _index (Private)
  70. /// <summary>
  71. /// _index
  72. /// </summary>
  73. private readonly int[] _index;
  74. #endregion
  75. #region eos (Private)
  76. /// <summary>
  77. /// end of stream
  78. /// </summary>
  79. private bool eos;
  80. #endregion
  81. #region syncState (Private)
  82. /// <summary>
  83. /// sync and verify incoming physical bitstream
  84. /// </summary>
  85. private readonly SyncState syncState = new SyncState();
  86. #endregion
  87. #region streamState (Private)
  88. /// <summary>
  89. /// take physical pages, weld into a logical stream of packets
  90. /// </summary>
  91. private readonly StreamState streamState = new StreamState();
  92. #endregion
  93. #region page (Private)
  94. /// <summary>
  95. /// one Ogg bitstream page. Vorbis packets are inside
  96. /// </summary>
  97. private readonly Page page = new Page();
  98. #endregion
  99. #region packet (Private)
  100. /// <summary>
  101. /// one raw packet of data for decode
  102. /// </summary>
  103. private readonly Packet packet = new Packet();
  104. #endregion
  105. #region info (Private)
  106. /// <summary>
  107. /// struct that stores all the static vorbis bitstream settings
  108. /// </summary>
  109. private readonly Info info = new Info();
  110. #endregion
  111. #region comment (Private)
  112. /// <summary>
  113. /// struct that stores all the bitstream user comments
  114. /// </summary>
  115. private readonly Comment comment = new Comment();
  116. #endregion
  117. #region dspState (Private)
  118. /// <summary>
  119. /// central working state for the packet->PCM decoder
  120. /// </summary>
  121. private readonly DspState dspState = new DspState();
  122. #endregion
  123. #region block (Private)
  124. /// <summary>
  125. /// local working space for packet->PCM decode
  126. /// </summary>
  127. private readonly Block block;
  128. #endregion
  129. #region convbufferOff (Private)
  130. /// <summary>
  131. /// where we are in the convbuffer
  132. /// </summary>
  133. private int convbufferOff;
  134. #endregion
  135. #region convbufferSize (Private)
  136. /// <summary>
  137. /// bytes ready in convbuffer.
  138. /// </summary>
  139. private int convbufferSize;
  140. #endregion
  141. #region readDummy (Private)
  142. /// <summary>
  143. /// a dummy used by read() to read 1 byte.
  144. /// </summary>
  145. private readonly byte[] readDummy = new byte[1];
  146. #endregion
  147. #region input (Private)
  148. /// <summary>
  149. /// Input
  150. /// </summary>
  151. private readonly Stream input;
  152. #endregion
  153. #endregion
  154. #region Constructors
  155. /// <summary>
  156. /// Creates an OggInputStream that decompressed the specified ogg file.
  157. /// </summary>
  158. /// <param name="setInput">Input file stream.</param>
  159. public OggInputStream(Stream setInput)
  160. {
  161. input = setInput;
  162. block = new Block(dspState);
  163. try
  164. {
  165. InitVorbis();
  166. _index = new int[info.channels];
  167. }
  168. catch (Exception ex)
  169. {
  170. Log.Warning("Failed to initialize ogg input stream: " + ex);
  171. eos = true;
  172. }
  173. }
  174. #endregion
  175. #region Read (Public)
  176. /// <summary>
  177. /// Reads the next byte of data from this input stream. The value byte is
  178. /// returned as an int in the range 0 to 255. If no byte is available
  179. /// because the end of the stream has been reached, the value -1 is
  180. /// returned. This method blocks until input data is available, the end
  181. /// of the stream is detected, or an exception is thrown.
  182. /// </summary>
  183. /// <returns>the next byte of data, or -1 if the end of the stream is
  184. /// reached.</returns>
  185. public int Read()
  186. {
  187. int retVal = Read(readDummy, 0, 1);
  188. return
  189. retVal == -1
  190. ? -1
  191. : readDummy[0];
  192. }
  193. /// <summary>
  194. /// Reads up to len bytes of data from the input stream into an
  195. /// array of bytes.
  196. /// </summary>
  197. /// <param name="b">the buffer into which the data is read.</param>
  198. /// <param name="off">the start offset of the data.</param>
  199. /// <param name="len">the maximum number of bytes read.</param>
  200. /// <returns>
  201. /// The total number of bytes read into the buffer, or -1 if there is no
  202. /// more data because the end of the stream has been reached.
  203. /// </returns>
  204. public int Read(byte[] b, int off, int len)
  205. {
  206. if (eos)
  207. {
  208. return 0;
  209. }
  210. int bytesRead = 0;
  211. while (!eos &&
  212. (len > 0))
  213. {
  214. FillConvbuffer();
  215. if (!eos)
  216. {
  217. int bytesToCopy = Math.Min(len, convbufferSize - convbufferOff);
  218. Array.Copy(convbuffer, convbufferOff, b, off, bytesToCopy);
  219. convbufferOff += bytesToCopy;
  220. bytesRead += bytesToCopy;
  221. len -= bytesToCopy;
  222. off += bytesToCopy;
  223. } // if
  224. } // while
  225. return bytesRead;
  226. }
  227. /// <summary>
  228. /// Reads up to len bytes of data from the input stream into a ByteBuffer.
  229. /// </summary>
  230. /// <param name="stream"></param>
  231. /// <param name="off">the start offset of the data.</param>
  232. /// <param name="len">the maximum number of bytes read.</param>
  233. /// <returns>the total number of bytes read into the buffer, or -1 if
  234. /// there is no more data because the end of the stream has been reached.
  235. /// </returns>
  236. public int Read(MemoryStream stream, int off, int len)
  237. {
  238. if (eos)
  239. {
  240. return 0;
  241. }
  242. stream.Seek(off, SeekOrigin.Begin);
  243. int bytesRead = 0;
  244. while (!eos &&
  245. (len > 0))
  246. {
  247. FillConvbuffer();
  248. if (!eos)
  249. {
  250. int bytesToCopy = Math.Min(len, convbufferSize - convbufferOff);
  251. stream.Write(convbuffer, convbufferOff, bytesToCopy);
  252. convbufferOff += bytesToCopy;
  253. bytesRead += bytesToCopy;
  254. len -= bytesToCopy;
  255. }
  256. } // while
  257. return bytesRead;
  258. }
  259. #endregion
  260. #region Skip (Public)
  261. /// <summary>
  262. /// Skips over and discards n bytes of data from the input stream.
  263. /// The skip method may, for a variety of reasons, end up skipping over
  264. /// some smaller number of bytes, possibly 0. The actual number of bytes
  265. /// skipped is returned.
  266. /// </summary>
  267. /// <param name="n">the number of bytes to be skipped.</param>
  268. /// <returns>the actual number of bytes skipped.</returns>
  269. public long Skip(long n)
  270. {
  271. int bytesRead = 0;
  272. while (bytesRead < n)
  273. {
  274. int res = Read();
  275. if (res == -1)
  276. {
  277. break;
  278. }
  279. bytesRead++;
  280. } // while
  281. return bytesRead;
  282. }
  283. #endregion
  284. #region ToString (Public)
  285. /// <summary>
  286. /// Gets information on the ogg.
  287. /// </summary>
  288. /// <returns>Returns a string representing this ogg input stream.</returns>
  289. public override string ToString()
  290. {
  291. return
  292. "Version=" + info.version + "\n" +
  293. "Channels=" + info.channels + "\n" +
  294. "Rate (hz)=" + info.rate;
  295. }
  296. #endregion
  297. #region Methods (Private)
  298. #region FillConvbuffer
  299. /// <summary>
  300. /// Helper function. Decodes a packet to the convbuffer if it is empty.
  301. /// Updates convbufferSize, convbufferOff, and eos.
  302. /// </summary>
  303. private void FillConvbuffer()
  304. {
  305. if (convbufferOff >= convbufferSize)
  306. {
  307. convbufferSize = LazyDecodePacket();
  308. convbufferOff = 0;
  309. if (convbufferSize == -1)
  310. {
  311. eos = true;
  312. }
  313. }
  314. }
  315. #endregion
  316. #region InitVorbis
  317. /// <summary>
  318. /// Initalizes the vorbis stream. Reads the stream until info
  319. /// and comment are read.
  320. /// </summary>
  321. private void InitVorbis()
  322. {
  323. // Now we can read pages
  324. syncState.init();
  325. // grab some data at the head of the stream. We want the first page
  326. // (which is guaranteed to be small and only contain the Vorbis
  327. // stream initial header) We need the first page to get the stream
  328. // serialno.
  329. // submit a 4k block to libvorbis' Ogg layer
  330. int index = syncState.buffer(4096);
  331. byte[] buffer = syncState.data;
  332. int bytes = input.Read(buffer, 0, buffer.Length);
  333. syncState.wrote(bytes);
  334. // Get the first page.
  335. if (syncState.pageout(page) != 1)
  336. {
  337. // have we simply run out of data? If so, we're done.
  338. if (bytes < 4096)
  339. {
  340. return; //break;
  341. }
  342. // error case. Must not be Vorbis data
  343. throw new Exception("Input does not appear to be an Ogg bitstream.");
  344. } // if
  345. // Get the serial number and set up the rest of decode.
  346. // serialno first; use it to set up a logical stream
  347. streamState.init(page.serialno());
  348. // extract the initial header from the first page and verify that the
  349. // Ogg bitstream is in fact Vorbis data
  350. // I handle the initial header first instead of just having the code
  351. // read all three Vorbis headers at once because reading the initial
  352. // header is an easy way to identify a Vorbis bitstream and it's
  353. // useful to see that functionality seperated out.
  354. info.init();
  355. comment.init();
  356. if (streamState.pagein(page) < 0)
  357. {
  358. // error; stream version mismatch perhaps
  359. throw new Exception("Error reading first page of Ogg bitstream data.");
  360. }
  361. if (streamState.packetout(packet) != 1)
  362. {
  363. // no page? must not be vorbis
  364. throw new Exception("Error reading initial header packet.");
  365. }
  366. if (info.synthesis_headerin(comment, packet) < 0)
  367. {
  368. // error case; not a vorbis header
  369. throw new Exception(
  370. "This Ogg bitstream does not contain Vorbis audio data.");
  371. }
  372. // At this point, we're sure we're Vorbis. We've set up the logical
  373. // (Ogg) bitstream decoder. Get the comment and codebook headers and
  374. // set up the Vorbis decoder
  375. // The next two packets in order are the comment and codebook headers.
  376. // They're likely large and may span multiple pages. Thus we read
  377. // and submit data until we get our two packets, watching that no
  378. // pages are missing. If a page is missing, error out; losing a
  379. // header page is the only place where missing data is fatal.
  380. int i = 0;
  381. while (i < 2)
  382. {
  383. while (i < 2)
  384. {
  385. int result = syncState.pageout(page);
  386. if (result == 0)
  387. {
  388. break; // Need more data
  389. }
  390. // Don't complain about missing or corrupt data yet. We'll
  391. // catch it at the packet output phase
  392. if (result == 1)
  393. {
  394. streamState.pagein(page);
  395. // We can ignore any errors here as they'll also become apparent
  396. // at packetout.
  397. while (i < 2)
  398. {
  399. result = streamState.packetout(packet);
  400. if (result == 0)
  401. {
  402. break;
  403. }
  404. if (result == -1)
  405. {
  406. // Uh oh; data at some point was corrupted or missing!
  407. // We can't tolerate that in a header. Die.
  408. throw new Exception("Corrupt secondary header. Exiting.");
  409. }
  410. info.synthesis_headerin(comment, packet);
  411. i++;
  412. } // while
  413. } // if
  414. } // while
  415. // no harm in not checking before adding more
  416. index = syncState.buffer(4096);
  417. buffer = syncState.data;
  418. bytes = input.Read(buffer, index, 4096);
  419. // NOTE: This is a bugfix. read will return -1 which will mess up
  420. // syncState.
  421. if (bytes < 0)
  422. {
  423. bytes = 0;
  424. }
  425. if (bytes == 0 &&
  426. i < 2)
  427. {
  428. throw new Exception(
  429. "End of file before finding all Vorbis headers!");
  430. }
  431. syncState.wrote(bytes);
  432. } // while
  433. convsizePerChannel = DefaultConvsize / info.channels;
  434. // OK, got and parsed all three headers. Initialize the Vorbis
  435. // packet->PCM decoder. central decode state
  436. dspState.synthesis_init(info);
  437. // local state for most of the decode so multiple block decodes can
  438. // proceed in parallel. We could init multiple vorbis_block structures
  439. // for vd here.
  440. block.init(dspState);
  441. }
  442. #endregion
  443. #region DecodePacket
  444. /// <summary>
  445. /// Decodes a packet.
  446. /// </summary>
  447. /// <returns>Packet length to write into convbufferSize</returns>
  448. private int DecodePacket()
  449. {
  450. // check the endianes of the computer.
  451. bool bigEndian = !BitConverter.IsLittleEndian;
  452. if (block.synthesis(packet) == 0)
  453. {
  454. // test for success!
  455. dspState.synthesis_blockin(block);
  456. }
  457. // **pcm is a multichannel float vector. In stereo, for
  458. // example, pcm[0] is left, and pcm[1] is right. samples is
  459. // the size of each channel. Convert the float values
  460. // (-1.<=range<=1.) to whatever PCM format and write it out
  461. int convOff = 0;
  462. int samples;
  463. while ((samples = dspState.synthesis_pcmout(_pcm, _index)) > 0)
  464. {
  465. float[][] pcm = _pcm[0];
  466. int bout =
  467. samples < convsizePerChannel
  468. ? samples
  469. : convsizePerChannel;
  470. // convert floats to 16 bit signed ints (host order) and interleave
  471. for (int i = 0; i < info.channels; i++)
  472. {
  473. int ptr = (i << 1) + convOff;
  474. int mono = _index[i];
  475. for (int j = 0; j < bout; j++)
  476. {
  477. int val = (int)(pcm[i][mono + j] * 32767);
  478. // might as well guard against clipping
  479. val = Math.Max(-32768, Math.Min(32767, val));
  480. val |=
  481. val < 0
  482. ? 0x8000
  483. : 0;
  484. convbuffer[ptr + 0] =
  485. (byte)
  486. (bigEndian
  487. ? unchecked((int)((uint)val >> 8))
  488. : val);
  489. convbuffer[ptr + 1] =
  490. (byte)
  491. (bigEndian
  492. ? val
  493. : unchecked((int)((uint)val >> 8)));
  494. ptr += (info.channels) << 1;
  495. } // for
  496. } // for
  497. convOff += 2 * info.channels * bout;
  498. // Tell orbis how many samples were consumed
  499. dspState.synthesis_read(bout);
  500. } // while
  501. return convOff;
  502. }
  503. #endregion
  504. #region LazyDecodePacket
  505. /// <summary>
  506. /// Decodes the next packet.
  507. /// </summary>
  508. /// <returns>Bytes read into convbuffer of -1 if end of file</returns>
  509. private int LazyDecodePacket()
  510. {
  511. int result = GetNextPacket();
  512. if (result == -1)
  513. {
  514. return -1;
  515. }
  516. // We have a packet. Decode it.
  517. return DecodePacket();
  518. }
  519. #endregion
  520. #region GetNextPacket
  521. /// <summary>
  522. /// Get the next packet in the music file.
  523. /// </summary>
  524. /// <returns>
  525. /// Returns -1 if getting the next packet failed, otherwise 0.
  526. /// </returns>
  527. private int GetNextPacket()
  528. {
  529. // get next packet.
  530. bool fetchedPacket = false;
  531. while (eos == false &&
  532. fetchedPacket == false)
  533. {
  534. int result1 = streamState.packetout(packet);
  535. if (result1 == 0)
  536. {
  537. // no more packets in page. Fetch new page.
  538. int result2 = 0;
  539. while (eos == false &&
  540. result2 == 0)
  541. {
  542. result2 = syncState.pageout(page);
  543. if (result2 == 0)
  544. {
  545. FetchData();
  546. }
  547. }
  548. // return if we have reaced end of file.
  549. if ((result2 == 0) && (page.eos() != 0))
  550. {
  551. return -1;
  552. }
  553. if (result2 == 0)
  554. {
  555. // need more data fetching page..
  556. FetchData();
  557. }
  558. else if (result2 == -1)
  559. {
  560. //throw new Exception("syncState.pageout(page) result == -1");
  561. Log.Test("syncState.pageout(page) result == -1");
  562. return -1;
  563. }
  564. else
  565. {
  566. int result3 = streamState.pagein(page);
  567. }
  568. } // if
  569. else if (result1 == -1)
  570. {
  571. //throw new Exception("streamState.packetout(packet) result == -1");
  572. Log.Test("streamState.packetout(packet) result == -1");
  573. return -1;
  574. }
  575. else
  576. {
  577. fetchedPacket = true;
  578. }
  579. } // while
  580. return 0;
  581. }
  582. #endregion
  583. #region FetchData
  584. /// <summary>
  585. /// Copies data from input stream to syncState.
  586. /// </summary>
  587. private void FetchData()
  588. {
  589. if (eos == false)
  590. {
  591. // copy 4096 bytes from compressed stream to syncState.
  592. int index = syncState.buffer(4096);
  593. if (index < 0)
  594. {
  595. eos = true;
  596. return;
  597. }
  598. int bytes = input.Read(syncState.data, index, 4096);
  599. syncState.wrote(bytes);
  600. if (bytes == 0)
  601. {
  602. eos = true;
  603. }
  604. }
  605. }
  606. #endregion
  607. #endregion
  608. }
  609. }