Created
April 19, 2015 21:02
-
-
Save soc/3e4bb291b52d0587d866 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package parser.opus | |
| import scodec.Codec | |
| case object OggS | |
| case object Version | |
| case class Header(continuation: Boolean, beginningOfStream: Boolean, endOfStream: Boolean) | |
| case class Page( | |
| oggS: OggS.type, | |
| version: Version.type, | |
| header: Header, | |
| granulePosition: Long, | |
| bitstreamSerialNumber: Int, | |
| pageSequenceNumber: Int, | |
| checksum: Int, | |
| segmentTable: ByteVector, | |
| segmentTableEntrySize: SegmentTableEntrySize, | |
| segmentBody: List[ByteVector]) { | |
| def pageSegment: Int = segmentTable.length | |
| def headerType: String = header match { | |
| case Header(true, false, false) => "CONT" | |
| case Header(false, true, false) => "BOS" | |
| case Header(false, false, true) => "EOS" | |
| case _ => "NONE" //throw new IllegalStateException(s"Header is invalid: $header") | |
| } | |
| def debugString = | |
| s"""Page(header: $headerType, granulePosition: $granulePosition, bitstreamSerialNumber: $bitstreamSerialNumber, | |
| |pageSequenceNumber: $pageSequenceNumber, pageSegment: $pageSegment, segmentTable: $segmentTable, | |
| |segmentTableEntrySize: $segmentTableEntrySize, segmentBody: $segmentBody""".stripMargin | |
| } | |
| case class SegmentBody(value: ByteVector) | |
| case class SegmentTableEntrySize(value: Int) | |
| object OpusParser extends App { | |
| val path = java.nio.file.Paths.get("examples/ehren-paper_lights-96.opus") | |
| val channel = java.nio.file.Files.newByteChannel(path) | |
| val vector = BitVector.fromChannel(channel, direct = true) | |
| implicit val oggSCodec: Codec[OggS.type] = | |
| constant(ByteVector(79, 103, 103, 83)).xmap(_ => OggS, _ => ()) | |
| implicit val versionCodec: Codec[Version.type] = | |
| constant(ByteVector(0)).xmap(_ => Version, _ => ()) | |
| implicit val headerCodec: Codec[Header] = fixedSizeBytes(1, { | |
| ("unallocated" | constant(bin"00000")) :~>: | |
| ("continuation" | bool) :: | |
| ("beginningOfStream" | bool) :: | |
| ("endOfStream" | bool) | |
| }).as[Header] | |
| implicit val pageCodec: Codec[Page] = { | |
| ("oggS" | oggSCodec) :: | |
| ("version" | versionCodec) :: | |
| ("header" | headerCodec) :: | |
| ("granulePosition" | int64) :: | |
| ("bitstreamSerialNumber" | int32) :: | |
| ("pageSequenceNumber" | int32) :: | |
| ("checksum" | int32) :: | |
| (variableSizeBytes(uint8, ("segmentTable" | bytes)) flatPrepend { bv => | |
| println(bv) | |
| ("segmentTableEntrySize" | segmentTableEntrySizeCodec) flatPrepend { | |
| sz => | |
| println(sz) | |
| list(fixedSizeBytes(sz.value, ("data" | bytes))).hlist | |
| } | |
| }) | |
| }.as[Page] | |
| val segmentTableEntrySizeEncoder: SegmentTableEntrySize => Attempt[BitVector] = { | |
| size => | |
| try { | |
| val full = size.value / 255 | |
| val rest = size.value % 255 | |
| val array = new Array[Byte](full + 1) | |
| val i = 0 | |
| while (i < full) { | |
| array(i) = -1 | |
| } | |
| array(full) = (rest & 0xFF).toByte | |
| Attempt.successful(BitVector.view(array)) | |
| } catch { | |
| case ex: ArrayIndexOutOfBoundsException => Attempt.failure(Err(ex.getMessage)) | |
| } | |
| } | |
| val segmentTableEntrySizeDecoder: BitVector => Attempt[DecodeResult[SegmentTableEntrySize]] = | |
| vector => { | |
| val result = getSegmentTableEntrySize(vector) | |
| Attempt.successful(result) | |
| } | |
| implicit val segmentTableEntrySizeCodec: Codec[SegmentTableEntrySize] = | |
| Codec.apply(segmentTableEntrySizeEncoder, segmentTableEntrySizeDecoder) | |
| /** Returns the size and the remaining vector. */ | |
| def getSegmentTableEntrySize(vector: BitVector): DecodeResult[SegmentTableEntrySize] = { | |
| def loop(index: Int = 0, accum: Int = 0): (Int, BitVector) = { | |
| val value = (vector.getByte(index) & 0xFF) | |
| println(s"$index: $value") | |
| if (value != 255) { | |
| println(s"segmentTableEntrySze: $value") | |
| (value + accum, vector.drop((index * 8) + 1)) | |
| } else { | |
| loop(index + 1, value + accum) | |
| } | |
| } | |
| println(vector.take(64)) | |
| val (value, remainder) = loop() | |
| DecodeResult(SegmentTableEntrySize(value), remainder) | |
| } | |
| val page = pageCodec.decode(vector) | |
| println(page.map(_.value.debugString)) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment