private boolean openSeekable() {
   final Info initialInfo = new Info();
   final Comment initialComment = new Comment();
   this.m_chunkSize = Math.min(8500, (int) length(this.m_vorbisStream));
   final Page page = new Page();
   final int[] testSerialno = {0};
   final int ret = this.fetchHeaders(initialInfo, initialComment, testSerialno, null);
   final int serialno = testSerialno[0];
   final int dataOffset = (int) this.m_offset;
   this.m_oggStreamState.clear();
   if (ret < 0) {
     return false;
   }
   seek(this.m_vorbisStream, 0L, 1);
   this.m_offset = tell(this.m_vorbisStream);
   final long end = this.getPreviousPage(page);
   if (page.serialno() != serialno) {
     if (this.bisectForwardSerialno(0L, 0L, end + 1L, serialno, 0) < 0) {
       return false;
     }
   } else if (this.bisectForwardSerialno(0L, end, end + 1L, serialno, 0) < 0) {
     return false;
   }
   this.prefetchAllHeaders(initialInfo, initialComment, dataOffset);
   this.rawSeek(this.m_dataOffsets[0]);
   return true;
 }
 public int fetchHeaders(final Info info, final Comment comment, final int[] serialno, Page page) {
   if (page == null) {
     page = new Page();
     final int retValue = this.getNextPage(page, this.m_chunkSize);
     if (retValue == -128) {
       return -128;
     }
     if (retValue < 0) {
       return -130;
     }
   }
   if (serialno != null) {
     serialno[0] = page.serialno();
   }
   this.m_oggStreamState.init(page.serialno());
   info.init();
   comment.init();
   final Packet packet = new Packet();
   int i = 0;
   while (i < 3) {
     this.m_oggStreamState.pagein(page);
     while (i < 3) {
       final int result = this.m_oggStreamState.packetout(packet);
       if (result == 0) {
         break;
       }
       if (result == -1) {
         info.clear();
         this.m_oggStreamState.clear();
         return -1;
       }
       if (info.synthesis_headerin(comment, packet) != 0) {
         info.clear();
         this.m_oggStreamState.clear();
         return -1;
       }
       ++i;
     }
     if (i < 3 && this.getNextPage(page, 1L) < 0) {
       info.clear();
       this.m_oggStreamState.clear();
       return -1;
     }
   }
   return 0;
 }
 private int bisectForwardSerialno(
     final long begin, long searched, final long end, final int currentno, final int m) {
   long endsearched = end;
   long next = end;
   final Page page = new Page();
   while (searched < endsearched) {
     long bisect;
     if (endsearched - searched < this.m_chunkSize) {
       bisect = searched;
     } else {
       bisect = (searched + endsearched) / 2L;
     }
     this.seekHelper(bisect);
     final int ret = this.getNextPage(page, -1L);
     if (ret == -128) {
       return -128;
     }
     if (ret < 0 || page.serialno() != currentno) {
       endsearched = bisect;
       if (ret < 0) {
         continue;
       }
       next = ret;
     } else {
       searched = ret + page.header_len + page.body_len;
     }
   }
   this.seekHelper(next);
   int ret = this.getNextPage(page, -1L);
   if (ret == -128) {
     return -128;
   }
   if (searched >= end || ret == -1) {
     this.m_links = m + 1;
     (this.m_offsets = new long[m + 2])[m + 1] = searched;
   } else {
     ret = this.bisectForwardSerialno(next, this.m_offset, end, page.serialno(), m + 1);
     if (ret == -128) {
       return -128;
     }
   }
   this.m_offsets[m] = begin;
   return 0;
 }
 private void prefetchAllHeaders(
     final Info firstInfo, final Comment firstComment, final int dataoffset) {
   final Page page = new Page();
   this.m_info = new Info[this.m_links];
   this.m_comments = new Comment[this.m_links];
   this.m_dataOffsets = new long[this.m_links];
   this.m_pcmLengths = new long[this.m_links];
   this.m_serialnos = new int[this.m_links];
   for (int i = 0; i < this.m_links; ++i) {
     if (firstInfo != null && firstComment != null && i == 0) {
       this.m_info[i] = firstInfo;
       this.m_comments[i] = firstComment;
       this.m_dataOffsets[i] = dataoffset;
     } else {
       this.seekHelper(this.m_offsets[i]);
       this.m_info[i] = new Info();
       this.m_comments[i] = new Comment();
       if (this.fetchHeaders(this.m_info[i], this.m_comments[i], null, null) == -1) {
         this.m_dataOffsets[i] = -1L;
       } else {
         this.m_dataOffsets[i] = this.m_offset;
         this.m_oggStreamState.clear();
       }
     }
     final long end = this.m_offsets[i + 1];
     this.seekHelper(end);
     while (true) {
       final int ret = this.getPreviousPage(page);
       if (ret == -1) {
         this.m_info[i].clear();
         break;
       }
       if (page.granulepos() != -1L) {
         this.m_serialnos[i] = page.serialno();
         this.m_pcmLengths[i] = page.granulepos();
         break;
       }
     }
   }
 }
 @Override
 public int pcmSeek(final long pos) {
   if (!this.m_vorbisStream.isSeekable()) {
     return -1;
   }
   long total = this.pcmTotal(-1);
   if (pos < 0L || pos > total) {
     this.m_pcmOffset = -1L;
     this.decodeClear();
     return -1;
   }
   int link;
   for (link = this.m_links - 1; link >= 0; --link) {
     total -= this.m_pcmLengths[link];
     if (pos >= total) {
       break;
     }
   }
   final long target = pos - total;
   long end = this.m_offsets[link + 1];
   long begin = this.m_offsets[link];
   int best = (int) begin;
   final Page page = new Page();
   while (begin < end) {
     long bisect;
     if (end - begin < this.m_chunkSize) {
       bisect = begin;
     } else {
       bisect = (end + begin) / 2L;
     }
     this.seekHelper(bisect);
     final int ret = this.getNextPage(page, end - bisect);
     if (ret == -1) {
       end = bisect;
     } else {
       final long granulepos = page.granulepos();
       if (granulepos < target) {
         best = ret;
         begin = this.m_offset;
       } else {
         end = bisect;
       }
     }
   }
   if (this.rawSeek(best) != 0) {
     this.m_pcmOffset = -1L;
     this.decodeClear();
     return -1;
   }
   if (this.m_pcmOffset >= pos) {
     this.m_pcmOffset = -1L;
     this.decodeClear();
     return -1;
   }
   if (pos > this.pcmTotal(-1)) {
     this.m_pcmOffset = -1L;
     this.decodeClear();
     return -1;
   }
   while (this.m_pcmOffset < pos) {
     final int target2 = (int) (pos - this.m_pcmOffset);
     final int[] _index = new int[this.m_info[this.m_currentLink].channelsCount];
     int samples = this.m_dspState.synthesis_pcmout(this.m_pcmf_buffer, _index);
     if (samples > target2) {
       samples = target2;
     }
     this.m_dspState.synthesis_read(samples);
     this.m_pcmOffset += samples;
     if (samples < target2 && this.processPacket(true) == 0) {
       this.m_pcmOffset = this.pcmTotal(-1);
     }
   }
   return 0;
 }