All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.sciss.audiofile.AudioFileHeader.scala Maven / Gradle / Ivy

/*
 *  AudioFileHeader.java
 *  (AudioFile)
 *
 *  Copyright (c) 2004-2020 Hanns Holger Rutz. All rights reserved.
 *
 *  This software is published under the GNU Affero General Public License v3+
 *
 *
 *  For further information, please contact Hanns Holger Rutz at
 *  [email protected]
 */

package de.sciss.audiofile

import java.io.{DataInput, DataInputStream, DataOutput, DataOutputStream, IOException, RandomAccessFile}
import java.nio.ByteOrder

import scala.concurrent.Future

private[audiofile] object AudioFileHeader {
  def opNotSupported = throw new IOException("Operation not supported")

  @throws(classOf[IOException])
  @inline def readLittleUShort(din: DataInput): Int = {
    val i = din.readUnsignedShort()
    (i >> 8) | ((i & 0xFF) << 8)
  }

  @throws(classOf[IOException])
  @inline def readLittleInt(din: DataInput): Int = {
    val i = din.readInt()
    ((i >> 24) & 0xFF) | ((i >> 8) & 0xFF00) | ((i << 8) & 0xFF0000) | (i << 24)
  }

  @throws(classOf[IOException])
  @inline def readLittleFloat(din: DataInput): Float = {
    val i = din.readInt()
    java.lang.Float.intBitsToFloat(((i >> 24) & 0xFF) | ((i >> 8) & 0xFF00) | ((i << 8) & 0xFF0000) | (i << 24))
  }

  @throws(classOf[IOException])
  @inline def readLittleLong(din: DataInput): Long = {
    val n = din.readLong()
    ((n >> 56) & 0xFFL) |
    ((n >> 40) & 0xFF00L) |
    ((n >> 24) & 0xFF0000L) |
    ((n >>  8) & 0xFF000000L) |
    ((n <<  8) & 0xFF00000000L) |
    ((n << 24) & 0xFF0000000000L) |
    ((n << 40) & 0xFF000000000000L) |
     (n << 56)
  }

  @throws(classOf[IOException])
  @inline def writeLittleShort(dout: DataOutput, i: Int): Unit =
    dout.writeShort((i >> 8) | ((i & 0xFF) << 8))

  @throws(classOf[IOException])
  @inline def writeLittleInt(dout: DataOutput, i: Int): Unit =
    dout.writeInt(((i >> 24) & 0xFF) | ((i >> 8) & 0xFF00) | ((i << 8) & 0xFF0000) | (i << 24))

  @throws(classOf[IOException])
  @inline def writeLittleFloat(dout: DataOutput, f: Float): Unit = {
    val i = java.lang.Float.floatToIntBits(f)
    dout.writeInt(((i >> 24) & 0xFF) | ((i >> 8) & 0xFF00) | ((i << 8) & 0xFF0000) | (i << 24))
  }

  @throws(classOf[IOException])
  @inline def writeLittleLong(dout: DataOutput, n: Long): Unit = {
    dout.writeLong(
      ((n >> 56) & 0xFFL) |
      ((n >> 40) & 0xFF00L) |
      ((n >> 24) & 0xFF0000L) |
      ((n >>  8) & 0xFF000000L) |
      ((n <<  8) & 0xFF00000000L) |
      ((n << 24) & 0xFF0000000000L) |
      ((n << 40) & 0xFF000000000000L) |
       (n << 56))
  }

  @throws(classOf[IOException])
  def readNullTermString(din: DataInput): String = {
    val buf = new StringBuffer()
    var b   = din.readByte()
    while (b != 0) {
      buf.append(b.toChar)
      b = din.readByte()
    }
    buf.toString
  }

  def formatError(detail: String) = throw new IOException(s"A header format error occurred: $detail")

  def encodingError   () = throw new IOException("File has unsupported encoding")
  def incompleteError () = throw new IOException("Header data is incomplete")

  trait DataInputReader {
    @throws(classOf[IOException]) def readInt  (): Int
    @throws(classOf[IOException]) def readFloat(): Float

    def byteOrder: ByteOrder
  }

  trait DataOutputWriter {
    @throws(classOf[IOException]) def writeInt  (i: Int  ): Unit
    @throws(classOf[IOException]) def writeFloat(f: Float): Unit

    def byteOrder: ByteOrder
  }

  def dataInputReader(din: DataInput, byteOrder: ByteOrder): DataInputReader =
    if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
      new LittleDataInputReader(din)
    } else {
      new BigDataInputReader(din)
    }

  def nativeDataInputReader(din: DataInput): DataInputReader = dataInputReader(din, ByteOrder.nativeOrder)

  def dataOutputWriter(dout: DataOutput, byteOrder: ByteOrder): DataOutputWriter =
    if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
      new LittleDataOutputWriter(dout)
    } else {
      new BigDataOutputWriter(dout)
    }

  def nativeDataOutputWriter(dout: DataOutput): DataOutputWriter = dataOutputWriter(dout, ByteOrder.nativeOrder)

  final class BigDataInputReader(din: DataInput) extends DataInputReader {
    @throws(classOf[IOException]) def readInt  (): Int    = din.readInt()
    @throws(classOf[IOException]) def readFloat(): Float  = din.readFloat()

    def byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN
  }

  final class LittleDataInputReader(din: DataInput) extends DataInputReader {
    @throws(classOf[IOException]) def readInt  (): Int    = readLittleInt(din)
    @throws(classOf[IOException]) def readFloat(): Float  = readLittleFloat(din)

    def byteOrder: ByteOrder = ByteOrder.LITTLE_ENDIAN
  }

  final class BigDataOutputWriter(dout: DataOutput) extends DataOutputWriter {
    @throws(classOf[IOException]) def writeInt(i: Int): Unit =
      dout.writeInt(i)

    @throws(classOf[IOException]) def writeFloat(f: Float): Unit =
      dout.writeFloat(f)

    def byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN
  }

  final class LittleDataOutputWriter(dout: DataOutput) extends DataOutputWriter {
    @throws(classOf[IOException]) def writeInt(i: Int): Unit =
      writeLittleInt(dout, i)

    @throws(classOf[IOException]) def writeFloat(f: Float): Unit =
      writeLittleFloat(dout, f)

    def byteOrder: ByteOrder = ByteOrder.LITTLE_ENDIAN
  }
}

private[audiofile] trait AudioFileHeader {
  def byteOrder: ByteOrder

  def spec: AudioFileSpec
}

private[audiofile] class ReadableAudioFileHeader(val spec: AudioFileSpec, val byteOrder: ByteOrder)
  extends AudioFileHeader

private[audiofile] trait WritableAudioFileHeader extends AudioFileHeader {
  @throws(classOf[IOException])
  def update(numFrames: Long): Unit
}

private[audiofile] trait AsyncWritableAudioFileHeader extends AudioFileHeader {
  def updateAsync(numFrames: Long): Future[Unit]
}

private[audiofile] trait AudioFileHeaderReader {
  /** Reads in the header information. Seek position
    * should remain at the first frame of the audio data.
    * If the header does not support a plain input stream
    * but requires random access or length information,
    * it should throw an IOException
    */
  @throws(classOf[IOException])
  def read(dis: DataInputStream): AudioFileHeader

  @throws(classOf[IOException])
  def read(raf: RandomAccessFile): AudioFileHeader
}

private[audiofile] trait AudioFileHeaderWriter {
  @throws(classOf[IOException])
  def write(dos: DataOutputStream, spec: AudioFileSpec): WritableAudioFileHeader

  @throws(classOf[IOException])
  def write(raf: RandomAccessFile, spec: AudioFileSpec): WritableAudioFileHeader
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy