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

eu.joaocosta.minart.audio.AudioQueue.scala Maven / Gradle / Ivy

package eu.joaocosta.minart.audio

import scala.collection.JavaConverters._

/** Internal AudioQueue abstraction.
  *
  *  This is not expected to be used by user code, but it's helpful to implement custom backends
  */
sealed trait AudioQueue {
  def isEmpty(): Boolean
  def nonEmpty(): Boolean = !isEmpty()
  def size: Int

  def enqueue(clip: AudioClip): this.type
  def dequeue(): Double
  def dequeueByte(): Byte = {
    (math.min(math.max(-1.0, dequeue()), 1.0) * 127).toByte
  }
  def clear(): this.type
}

object AudioQueue {

  private val maxBufferSize: Double = 10.0

  class SingleChannelAudioQueue(sampleRate: Int) extends AudioQueue {
    private val valueQueue = scala.collection.mutable.Queue[Double]()
    private val clipQueue  = new java.util.ArrayDeque[AudioClip]() // Use scala's ArrayDeque on 2.13+

    def isEmpty() = synchronized { valueQueue.isEmpty && clipQueue.isEmpty }
    def size = clipQueue.iterator.asScala.foldLeft(valueQueue.size) { case (acc, clip) =>
      if (acc == Int.MaxValue || clip.duration.isInfinite) Int.MaxValue
      else {
        val newValue = acc + Sampler.numSamples(clip, sampleRate)
        if (newValue < 0) Int.MaxValue
        else newValue
      }
    }

    def enqueue(clip: AudioClip): this.type = synchronized {
      clipQueue.addLast(clip)
      this
    }
    def dequeue(): Double = synchronized {
      if (valueQueue.nonEmpty) {
        valueQueue.dequeue()
      } else if (!clipQueue.isEmpty()) {
        val nextClip = clipQueue.removeFirst()
        if (nextClip.duration > maxBufferSize) {
          valueQueue ++= Sampler.sampleClip(nextClip.take(maxBufferSize), sampleRate)
          clipQueue.addFirst(nextClip.drop(maxBufferSize))
        } else {
          valueQueue ++= Sampler.sampleClip(nextClip, sampleRate)
        }
        valueQueue.dequeue()
      } else {
        0.0
      }
    }

    def clear(): this.type = synchronized {
      clipQueue.clear()
      valueQueue.clear()
      this
    }
  }

  class MultiChannelAudioQueue(sampleRate: Int) extends AudioQueue {
    private val channels = scala.collection.mutable.Map[Int, AudioQueue]()

    def isEmpty()              = channels.values.forall(_.isEmpty())
    def isEmpty(channel: Int)  = channels.get(channel).map(_.isEmpty()).getOrElse(true)
    def nonEmpty(channel: Int) = !isEmpty(channel)
    def size =
      if (channels.isEmpty) 0
      else channels.values.maxBy(_.size).size

    def enqueue(clip: AudioClip): this.type = enqueue(clip, 0)
    def enqueue(clip: AudioClip, channel: Int): this.type = synchronized {
      val queue = channels.getOrElseUpdate(channel, new SingleChannelAudioQueue(sampleRate))
      queue.enqueue(clip)
      this
    }
    def dequeue(): Double = synchronized {
      math.max(-1.0, math.min(channels.values.map(_.dequeue()).sum, 1.0))
    }

    def clear(): this.type = synchronized {
      channels.clear()
      this
    }
    def clear(channel: Int): this.type = synchronized {
      channels.get(channel).foreach(_.clear())
      this
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy