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

de.sciss.fscape.stream.Concat.scala Maven / Gradle / Ivy

/*
 *  Concat.scala
 *  (FScape)
 *
 *  Copyright (c) 2001-2016 Hanns Holger Rutz. All rights reserved.
 *
 *  This software is published under the GNU General Public License v2+
 *
 *
 *  For further information, please contact Hanns Holger Rutz at
 *  [email protected]
 */

package de.sciss.fscape
package stream

import akka.stream.stage.InHandler
import akka.stream.{Attributes, FanInShape2}
import de.sciss.fscape.stream.impl.{InOutImpl, Out1DoubleImpl, Out1LogicImpl, ProcessOutHandlerImpl, SameChunkImpl, StageImpl, NodeImpl}

/** Binary operator assuming stream is complex signal (real and imaginary interleaved).
  * Outputs another complex stream even if the operator yields a purely real-valued result.
  */
object Concat {
  def apply(a: OutD, b: OutD)(implicit builder: Builder): OutD = {
    val stage0  = new Stage
    val stage   = builder.add(stage0)
    builder.connect(a, stage.in0)
    builder.connect(b, stage.in1)
    stage.out
  }

  private final val name = "Concat"

  private type Shape = FanInShape2[BufD, BufD, BufD]

  private final class Stage(implicit ctrl: Control) extends StageImpl[Shape](name) {
    val shape = new FanInShape2(
      in0 = InD (s"$name.a" ),
      in1 = InD (s"$name.b" ),
      out = OutD(s"$name.out")
    )

    def createLogic(attr: Attributes) = new Logic(shape)
  }

  // XXX TODO --- abstract across BufI / BufD?
  private final class Logic(shape: Shape)(implicit ctrl: Control)
    extends NodeImpl(name, shape)
      with InOutImpl[Shape]
      with SameChunkImpl[Shape]
      with Out1LogicImpl[BufD, Shape]
      with Out1DoubleImpl[Shape] {


    // ---- impl ----

    protected var bufIn0 : BufD = _
    protected var bufOut0: BufD = _

    protected val in0  = shape.in0
    protected val in1  = shape.in1
    protected val out0 = shape.out

    private[this] var _canRead = false

    def canRead: Boolean = _canRead
    def inValid: Boolean = true

    override def preStart(): Unit = {
      pull(in0)
      pull(in1)
    }

    override protected def stopped(): Unit = {
      super.stopped()
      freeInputBuffers()
      freeOutputBuffers()
    }

    protected def readIns(): Int = {
      freeInputBuffers()

      if (isAvailable(in0)) {
        bufIn0 = grab(in0)
        bufIn0.assertAllocated()
        tryPull(in0)
      } else {
        assert(isClosed(in0) && isAvailable(in1))
        bufIn0 = grab(in1)
        tryPull(in1)
      }

      updateCanRead()
      bufIn0.size
    }

    protected def freeInputBuffers(): Unit =
      if (bufIn0 != null) {
        bufIn0.release()
        bufIn0 = null
      }

    protected def freeOutputBuffers(): Unit =
      if (bufOut0 != null) {
        bufOut0.release()
        bufOut0 = null
      }

    def updateCanRead(): Unit =
      _canRead = isAvailable(in0) || (isClosed(in0) && isAvailable(in1))

    protected def shouldComplete(): Boolean =
      inRemain == 0 && isClosed(in0) && !isAvailable(in0) && isClosed(in1) && !isAvailable(in1)

    protected def processChunk(inOff: Int, outOff: Int, chunk: Int): Unit =
      Util.copy(bufIn0.buf, inOff, bufOut0.buf, outOff, chunk)

    private object InHandlerImpl extends InHandler {
      def onPush(): Unit = {
        updateCanRead()
        if (canRead) process()
      }

      override def onUpstreamFinish(): Unit = {
        updateCanRead()
        process()
      }
    }

    setHandler(in0, InHandlerImpl)
    setHandler(in1, InHandlerImpl)
    new ProcessOutHandlerImpl(out0, this)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy