PageRenderTime 43ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/org/newdawn/slick/openal/OggInputStream.java

https://bitbucket.org/lothario/slick-mod
Java | 506 lines | 302 code | 64 blank | 140 comment | 71 complexity | d8eb13862c3b30feff4a3400ee31b5c7 MD5 | raw file
  1. package org.newdawn.slick.openal;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.nio.ByteBuffer;
  5. import java.nio.ByteOrder;
  6. import org.lwjgl.BufferUtils;
  7. import org.newdawn.slick.util.Log;
  8. import com.jcraft.jogg.Packet;
  9. import com.jcraft.jogg.Page;
  10. import com.jcraft.jogg.StreamState;
  11. import com.jcraft.jogg.SyncState;
  12. import com.jcraft.jorbis.Block;
  13. import com.jcraft.jorbis.Comment;
  14. import com.jcraft.jorbis.DspState;
  15. import com.jcraft.jorbis.Info;
  16. /**
  17. * An input stream that can extract ogg data. This class is a bit of an experiment with continuations
  18. * so uses thread where possibly not required. It's just a test to see if continuations make sense in
  19. * some cases.
  20. *
  21. * @author kevin
  22. */
  23. public class OggInputStream extends InputStream implements AudioInputStream {
  24. /** The conversion buffer size */
  25. private int convsize = 4096 * 4;
  26. /** The buffer used to read OGG file */
  27. private byte[] convbuffer = new byte[convsize]; // take 8k out of the data segment, not the stack
  28. /** The stream we're reading the OGG file from */
  29. private InputStream input;
  30. /** The audio information from the OGG header */
  31. private Info oggInfo = new Info(); // struct that stores all the static vorbis bitstream settings
  32. /** True if we're at the end of the available data */
  33. private boolean endOfStream;
  34. /** The Vorbis SyncState used to decode the OGG */
  35. private SyncState syncState = new SyncState(); // sync and verify incoming physical bitstream
  36. /** The Vorbis Stream State used to decode the OGG */
  37. private StreamState streamState = new StreamState(); // take physical pages, weld into a logical stream of packets
  38. /** The current OGG page */
  39. private Page page = new Page(); // one Ogg bitstream page. Vorbis packets are inside
  40. /** The current packet page */
  41. private Packet packet = new Packet(); // one raw packet of data for decode
  42. /** The comment read from the OGG file */
  43. private Comment comment = new Comment(); // struct that stores all the bitstream user comments
  44. /** The Vorbis DSP stat eused to decode the OGG */
  45. private DspState dspState = new DspState(); // central working state for the packet->PCM decoder
  46. /** The OGG block we're currently working with to convert PCM */
  47. private Block vorbisBlock = new Block(dspState); // local working space for packet->PCM decode
  48. /** Temporary scratch buffer */
  49. byte[] buffer;
  50. /** The number of bytes read */
  51. int bytes = 0;
  52. /** The true if we should be reading big endian */
  53. boolean bigEndian = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN);
  54. /** True if we're reached the end of the current bit stream */
  55. boolean endOfBitStream = true;
  56. /** True if we're initialise the OGG info block */
  57. boolean inited = false;
  58. /** The index into the byte array we currently read from */
  59. private int readIndex;
  60. /** The byte array store used to hold the data read from the ogg */
  61. private ByteBuffer pcmBuffer = BufferUtils.createByteBuffer(4096 * 500);
  62. /** The total number of bytes */
  63. private int total;
  64. /**
  65. * Create a new stream to decode OGG data
  66. *
  67. * @param input The input stream from which to read the OGG file
  68. * @throws IOException Indicates a failure to read from the supplied stream
  69. */
  70. public OggInputStream(InputStream input) throws IOException {
  71. this.input = input;
  72. total = input.available();
  73. init();
  74. }
  75. /**
  76. * Get the number of bytes on the stream
  77. *
  78. * @return The number of the bytes on the stream
  79. */
  80. public int getLength() {
  81. return total;
  82. }
  83. /**
  84. * @see org.newdawn.slick.openal.AudioInputStream#getChannels()
  85. */
  86. public int getChannels() {
  87. return oggInfo.channels;
  88. }
  89. /**
  90. * @see org.newdawn.slick.openal.AudioInputStream#getRate()
  91. */
  92. public int getRate() {
  93. return oggInfo.rate;
  94. }
  95. /**
  96. * Initialise the streams and thread involved in the streaming of OGG data
  97. *
  98. * @throws IOException Indicates a failure to link up the streams
  99. */
  100. private void init() throws IOException {
  101. initVorbis();
  102. readPCM();
  103. }
  104. /**
  105. * @see java.io.InputStream#available()
  106. */
  107. public int available() {
  108. return endOfStream ? 0 : 1;
  109. }
  110. /**
  111. * Initialise the vorbis decoding
  112. */
  113. private void initVorbis() {
  114. syncState.init();
  115. }
  116. /**
  117. * Get a page and packet from that page
  118. *
  119. * @return True if there was a page available
  120. */
  121. private boolean getPageAndPacket() {
  122. // grab some data at the head of the stream. We want the first page
  123. // (which is guaranteed to be small and only contain the Vorbis
  124. // stream initial header) We need the first page to get the stream
  125. // serialno.
  126. // submit a 4k block to libvorbis' Ogg layer
  127. int index = syncState.buffer(4096);
  128. buffer = syncState.data;
  129. if (buffer == null) {
  130. endOfStream = true;
  131. return false;
  132. }
  133. try {
  134. bytes = input.read(buffer, index, 4096);
  135. } catch (Exception e) {
  136. Log.error("Failure reading in vorbis");
  137. Log.error(e);
  138. endOfStream = true;
  139. return false;
  140. }
  141. syncState.wrote(bytes);
  142. // Get the first page.
  143. if (syncState.pageout(page) != 1) {
  144. // have we simply run out of data? If so, we're done.
  145. if (bytes < 4096)
  146. return false;
  147. // error case. Must not be Vorbis data
  148. Log.error("Input does not appear to be an Ogg bitstream.");
  149. endOfStream = true;
  150. return false;
  151. }
  152. // Get the serial number and set up the rest of decode.
  153. // serialno first; use it to set up a logical stream
  154. streamState.init(page.serialno());
  155. // extract the initial header from the first page and verify that the
  156. // Ogg bitstream is in fact Vorbis data
  157. // I handle the initial header first instead of just having the code
  158. // read all three Vorbis headers at once because reading the initial
  159. // header is an easy way to identify a Vorbis bitstream and it's
  160. // useful to see that functionality seperated out.
  161. oggInfo.init();
  162. comment.init();
  163. if (streamState.pagein(page) < 0) {
  164. // error; stream version mismatch perhaps
  165. Log.error("Error reading first page of Ogg bitstream data.");
  166. endOfStream = true;
  167. return false;
  168. }
  169. if (streamState.packetout(packet) != 1) {
  170. // no page? must not be vorbis
  171. Log.error("Error reading initial header packet.");
  172. endOfStream = true;
  173. return false;
  174. }
  175. if (oggInfo.synthesis_headerin(comment, packet) < 0) {
  176. // error case; not a vorbis header
  177. Log.error("This Ogg bitstream does not contain Vorbis audio data.");
  178. endOfStream = true;
  179. return false;
  180. }
  181. // At this point, we're sure we're Vorbis. We've set up the logical
  182. // (Ogg) bitstream decoder. Get the comment and codebook headers and
  183. // set up the Vorbis decoder
  184. // The next two packets in order are the comment and codebook headers.
  185. // They're likely large and may span multiple pages. Thus we reead
  186. // and submit data until we get our two pacakets, watching that no
  187. // pages are missing. If a page is missing, error out; losing a
  188. // header page is the only place where missing data is fatal. */
  189. int i = 0;
  190. while (i < 2) {
  191. while (i < 2) {
  192. int result = syncState.pageout(page);
  193. if (result == 0)
  194. break; // Need more data
  195. // Don't complain about missing or corrupt data yet. We'll
  196. // catch it at the packet output phase
  197. if (result == 1) {
  198. streamState.pagein(page); // we can ignore any errors here
  199. // as they'll also become apparent
  200. // at packetout
  201. while (i < 2) {
  202. result = streamState.packetout(packet);
  203. if (result == 0)
  204. break;
  205. if (result == -1) {
  206. // Uh oh; data at some point was corrupted or missing!
  207. // We can't tolerate that in a header. Die.
  208. Log.error("Corrupt secondary header. Exiting.");
  209. endOfStream = true;
  210. return false;
  211. }
  212. oggInfo.synthesis_headerin(comment, packet);
  213. i++;
  214. }
  215. }
  216. }
  217. // no harm in not checking before adding more
  218. index = syncState.buffer(4096);
  219. buffer = syncState.data;
  220. try {
  221. bytes = input.read(buffer, index, 4096);
  222. } catch (Exception e) {
  223. Log.error("Failed to read Vorbis: ");
  224. Log.error(e);
  225. endOfStream = true;
  226. return false;
  227. }
  228. if (bytes == 0 && i < 2) {
  229. Log.error("End of file before finding all Vorbis headers!");
  230. endOfStream = true;
  231. return false;
  232. }
  233. syncState.wrote(bytes);
  234. }
  235. convsize = 4096 / oggInfo.channels;
  236. // OK, got and parsed all three headers. Initialize the Vorbis
  237. // packet->PCM decoder.
  238. dspState.synthesis_init(oggInfo); // central decode state
  239. vorbisBlock.init(dspState); // local state for most of the decode
  240. // so multiple block decodes can
  241. // proceed in parallel. We could init
  242. // multiple vorbis_block structures
  243. // for vd here
  244. return true;
  245. }
  246. /**
  247. * Decode the OGG file as shown in the jogg/jorbis examples
  248. *
  249. * @throws IOException Indicates a failure to read from the supplied stream
  250. */
  251. private void readPCM() throws IOException {
  252. boolean wrote = false;
  253. while (true) { // we repeat if the bitstream is chained
  254. if (endOfBitStream) {
  255. if (!getPageAndPacket()) {
  256. break;
  257. }
  258. endOfBitStream = false;
  259. }
  260. if (!inited) {
  261. inited = true;
  262. return;
  263. }
  264. float[][][] _pcm = new float[1][][];
  265. int[] _index = new int[oggInfo.channels];
  266. // The rest is just a straight decode loop until end of stream
  267. while (!endOfBitStream) {
  268. while (!endOfBitStream) {
  269. int result = syncState.pageout(page);
  270. if (result == 0) {
  271. break; // need more data
  272. }
  273. if (result == -1) { // missing or corrupt data at this page position
  274. Log.error("Corrupt or missing data in bitstream; continuing...");
  275. } else {
  276. streamState.pagein(page); // can safely ignore errors at
  277. // this point
  278. while (true) {
  279. result = streamState.packetout(packet);
  280. if (result == 0)
  281. break; // need more data
  282. if (result == -1) { // missing or corrupt data at this page position
  283. // no reason to complain; already complained above
  284. } else {
  285. // we have a packet. Decode it
  286. int samples;
  287. if (vorbisBlock.synthesis(packet) == 0) { // test for success!
  288. dspState.synthesis_blockin(vorbisBlock);
  289. }
  290. // **pcm is a multichannel float vector. In stereo, for
  291. // example, pcm[0] is left, and pcm[1] is right. samples is
  292. // the size of each channel. Convert the float values
  293. // (-1.<=range<=1.) to whatever PCM format and write it out
  294. while ((samples = dspState.synthesis_pcmout(_pcm,
  295. _index)) > 0) {
  296. float[][] pcm = _pcm[0];
  297. //boolean clipflag = false;
  298. int bout = (samples < convsize ? samples
  299. : convsize);
  300. // convert floats to 16 bit signed ints (host order) and
  301. // interleave
  302. for (int i = 0; i < oggInfo.channels; i++) {
  303. int ptr = i * 2;
  304. //int ptr=i;
  305. int mono = _index[i];
  306. for (int j = 0; j < bout; j++) {
  307. int val = (int) (pcm[i][mono + j] * 32767.);
  308. // might as well guard against clipping
  309. if (val > 32767) {
  310. val = 32767;
  311. }
  312. if (val < -32768) {
  313. val = -32768;
  314. }
  315. if (val < 0)
  316. val = val | 0x8000;
  317. if (bigEndian) {
  318. convbuffer[ptr] = (byte) (val >>> 8);
  319. convbuffer[ptr + 1] = (byte) (val);
  320. } else {
  321. convbuffer[ptr] = (byte) (val);
  322. convbuffer[ptr + 1] = (byte) (val >>> 8);
  323. }
  324. ptr += 2 * (oggInfo.channels);
  325. }
  326. }
  327. int bytesToWrite = 2 * oggInfo.channels * bout;
  328. if (bytesToWrite >= pcmBuffer.remaining()) {
  329. Log.warn("Read block from OGG that was too big to be buffered: " + bytesToWrite);
  330. } else {
  331. pcmBuffer.put(convbuffer, 0, bytesToWrite);
  332. }
  333. wrote = true;
  334. dspState.synthesis_read(bout); // tell libvorbis how
  335. // many samples we
  336. // actually consumed
  337. }
  338. }
  339. }
  340. if (page.eos() != 0) {
  341. endOfBitStream = true;
  342. }
  343. if ((!endOfBitStream) && (wrote)) {
  344. return;
  345. }
  346. }
  347. }
  348. if (!endOfBitStream) {
  349. bytes = 0;
  350. int index = syncState.buffer(4096);
  351. if (index >= 0) {
  352. buffer = syncState.data;
  353. try {
  354. bytes = input.read(buffer, index, 4096);
  355. } catch (Exception e) {
  356. Log.error("Failure during vorbis decoding");
  357. Log.error(e);
  358. endOfStream = true;
  359. return;
  360. }
  361. } else {
  362. bytes = 0;
  363. }
  364. syncState.wrote(bytes);
  365. if (bytes == 0) {
  366. endOfBitStream = true;
  367. }
  368. }
  369. }
  370. // clean up this logical bitstream; before exit we see if we're
  371. // followed by another [chained]
  372. streamState.clear();
  373. // ogg_page and ogg_packet structs always point to storage in
  374. // libvorbis. They're never freed or manipulated directly
  375. vorbisBlock.clear();
  376. dspState.clear();
  377. oggInfo.clear(); // must be called last
  378. }
  379. // OK, clean up the framer
  380. syncState.clear();
  381. endOfStream = true;
  382. }
  383. /**
  384. * @see java.io.InputStream#read()
  385. */
  386. public int read() throws IOException {
  387. if (readIndex >= pcmBuffer.position()) {
  388. pcmBuffer.clear();
  389. readPCM();
  390. readIndex = 0;
  391. }
  392. if (readIndex >= pcmBuffer.position()) {
  393. return -1;
  394. }
  395. int value = pcmBuffer.get(readIndex);
  396. if (value < 0) {
  397. value = 256 + value;
  398. }
  399. readIndex++;
  400. return value;
  401. }
  402. /**
  403. * @see org.newdawn.slick.openal.AudioInputStream#atEnd()
  404. */
  405. public boolean atEnd() {
  406. return endOfStream && (readIndex >= pcmBuffer.position());
  407. }
  408. /**
  409. * @see java.io.InputStream#read(byte[], int, int)
  410. */
  411. public int read(byte[] b, int off, int len) throws IOException {
  412. for (int i=0;i<len;i++) {
  413. try {
  414. int value = read();
  415. if (value >= 0) {
  416. b[i] = (byte) value;
  417. } else {
  418. if (i == 0) {
  419. return -1;
  420. } else {
  421. return i;
  422. }
  423. }
  424. } catch (IOException e) {
  425. Log.error(e);
  426. return i;
  427. }
  428. }
  429. return len;
  430. }
  431. /**
  432. * @see java.io.InputStream#read(byte[])
  433. */
  434. public int read(byte[] b) throws IOException {
  435. return read(b, 0, b.length);
  436. }
  437. /**
  438. * @see java.io.InputStream#close()
  439. */
  440. public void close() throws IOException {
  441. }
  442. }