Skip to content

Instantly share code, notes, and snippets.

@soc
Created April 19, 2015 21:02
Show Gist options
  • Select an option

  • Save soc/3e4bb291b52d0587d866 to your computer and use it in GitHub Desktop.

Select an option

Save soc/3e4bb291b52d0587d866 to your computer and use it in GitHub Desktop.
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