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

com.malliina.audio.javasound.LineData.scala Maven / Gradle / Ivy

The newest version!
package com.malliina.audio.javasound

import java.io.InputStream
import javax.sound.sampled.DataLine.Info
import javax.sound.sampled._

import com.malliina.audio.AudioImplicits._
import com.malliina.audio.PlayerStates
import com.malliina.audio.PlayerStates.PlayerState
import com.malliina.audio.javasound.LineData.log
import org.slf4j.LoggerFactory
import rx.lang.scala.Subject

object LineData {
  private val log = LoggerFactory.getLogger(getClass)

  /** This factory method blocks as long as `stream` is empty, i.e. until an appropriate amount
    * of audio bytes has been made available to it.
    *
    * Therefore you must not, in the same thread, call this before bytes are made available to the stream.
    *
    * @param stream
    * @return
    */
  def fromStream(stream: InputStream, subject: Subject[PlayerState]) =
    new LineData(AudioSystem.getAudioInputStream(stream), subject)
}

class LineData(inStream: AudioInputStream, subject: Subject[PlayerState]) {
  private val baseFormat = inStream.getFormat
  private val decodedFormat = toDecodedFormat(baseFormat)
  // this is read
  private val decodedIn = AudioSystem.getAudioInputStream(decodedFormat, inStream)
  // this is written to during playback
  val line = buildLine(decodedFormat)
  line.addLineListener((lineEvent: LineEvent) => subject.onNext(toPlayerEvent(lineEvent)))
  line open decodedFormat

  def toPlayerEvent(lineEvent: LineEvent): PlayerState = {
    import LineEvent.Type._

    import PlayerStates._
    val eventType = lineEvent.getType
    if (eventType == OPEN) Open
    else if (eventType == CLOSE) Closed
    else if (eventType == START) Started
    else if (eventType == STOP) Stopped
    else Unknown
  }

  def read(buffer: Array[Byte]) = decodedIn.read(buffer)

  def skip(bytes: Long) = {
    val skipped = decodedIn skip bytes
    log debug s"Attempted to skip $bytes bytes, skipped $skipped bytes"
    skipped
  }

  def state = {
    import PlayerStates._
    if (line.isOpen) {
      if (line.isActive) {
        Started
      } else {
        Stopped
      }
    } else {
      Closed
    }
  }

  def close() {
    line.stop()
    line.flush()
    line.close()
  }

  private def buildLine(format: AudioFormat): SourceDataLine = {
    val info = new Info(classOf[SourceDataLine], format)
    val line = AudioSystem.getLine(info).asInstanceOf[SourceDataLine]
    // Add line listeners before opening the line.
    line.addLineListener((e: LineEvent) => log debug s"Line event: $e")
    line
  }

  protected def toDecodedFormat(audioFormat: AudioFormat) = new AudioFormat(
    AudioFormat.Encoding.PCM_SIGNED,
    audioFormat.getSampleRate,
    16,
    audioFormat.getChannels,
    audioFormat.getChannels * 2,
    audioFormat.getSampleRate,
    false
  )
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy