de.sciss.fscape.stream.ResampleWindow.scala Maven / Gradle / Ivy
/*
* ResampleWindow.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 java.io.RandomAccessFile
import java.nio.DoubleBuffer
import java.nio.channels.FileChannel
import akka.stream.stage.InHandler
import akka.stream.{Attributes, FanInShape7, Inlet}
import de.sciss.file._
import de.sciss.fscape.stream.impl.{Out1DoubleImpl, Out1LogicImpl, ResampleImpl, StageImpl, NodeImpl}
object ResampleWindow {
def apply(in: OutD, size: OutI, factor: OutD, minFactor: OutD, rollOff: OutD, kaiserBeta: OutD, zeroCrossings: OutI)
(implicit b: Builder): OutD = {
val stage0 = new Stage
val stage = b.add(stage0)
b.connect(in , stage.in0)
b.connect(size , stage.in1)
b.connect(factor , stage.in2)
b.connect(minFactor , stage.in3)
b.connect(rollOff , stage.in4)
b.connect(kaiserBeta , stage.in5)
b.connect(zeroCrossings, stage.in6)
stage.out
}
private final val name = "ResampleWindow"
private type Shape = FanInShape7[BufD, BufI, BufD, BufD, BufD, BufD, BufI, BufD]
private final class Stage(implicit ctrl: Control) extends StageImpl[Shape](name) {
val shape = new FanInShape7(
in0 = InD (s"$name.in" ),
in1 = InI (s"$name.size" ),
in2 = InD (s"$name.factor" ),
in3 = InD (s"$name.minFactor" ),
in4 = InD (s"$name.rollOff" ),
in5 = InD (s"$name.kaiserBeta" ),
in6 = InI (s"$name.zeroCrossings"),
out = OutD(s"$name.out" )
)
def createLogic(attr: Attributes) = new Logic(shape)
}
private final class Logic(shape: Shape)(implicit ctrl: Control)
extends NodeImpl(name, shape)
with ResampleImpl[Shape]
with Out1LogicImpl[BufD, Shape]
with Out1DoubleImpl[Shape] {
protected val PAD = 1
private[this] var bufIn : BufD = _
private[this] var bufSize : BufI = _
private[this] var _inMainValid = false
private[this] var _canReadMain = false
// size of a window, not the resample buffer
private[this] var size: Int = 0
// this serves as the collecting window
private[this] var inArr: Array[Double] = _
// this serves as the emitting window
private[this] var outArr: Array[Double] = _
private[this] var winBuf: DoubleBuffer = _
private[this] var winF : File = _
private[this] var winRaf: RandomAccessFile = _
// true when we have begun copying data from the
// input buffer to the value array.
private[this] var lockInToVal = true
// true when the value array has been filled with
// input data. will be cleared in `copyInToWinBuf`
// after we copied the value array into the resampling
// algorithm's window buffer.
private[this] var lockValToWin = false
// true when we have begun copying data from the
// value array to the output buffer.
private[this] var lockValToOut = false
private[this] var valOff = 0
// ---- infra ----
protected def in0 : InD = shape.in0
private[this] val in1 : InI = shape.in1
protected def inFactor : InD = shape.in2
protected def inMinFactor : InD = shape.in3
protected def inRollOff : InD = shape.in4
protected def inKaiserBeta : InD = shape.in5
protected def inZeroCrossings : InI = shape.in6
protected def out0 : OutD = shape.out
protected def inMainValid: Boolean = _inMainValid
protected def canReadMain: Boolean = _canReadMain
// private[this] val DEBUG = false
//
// @inline
// private[this] def debugLog(what: => String): Unit =
// if (DEBUG) println(what)
// ---- handlers / constructor ----
private def updateCanReadMain(): Unit =
_canReadMain = isAvailable(in0) &&
((isClosed(in1) && _inMainValid) || isAvailable(in1))
private final class MainInHandler(in: Inlet[_], isProcess: Boolean) extends InHandler {
def onPush(): Unit = {
logStream(s"onPush($in)")
updateCanReadMain()
if (_canReadMain) process()
}
override def onUpstreamFinish(): Unit = {
logStream(s"onUpstreamFinish($in)")
updateCanReadMain()
if (_inMainValid) {
if (isProcess || _canReadMain) process() // may lead to `flushOut`
} else {
if (!isAvailable(in)) {
// debugLog(s"Invalid process $in")
completeStage()
}
}
}
setHandler(in, this)
}
new MainInHandler(in0, isProcess = true )
new MainInHandler(in1, isProcess = false)
// ---- start/stop ----
override def preStart(): Unit = {
super.preStart()
pull(in0)
pull(in1)
}
override protected def stopped(): Unit = {
super.stopped()
inArr = null
outArr = null
freeWinBuffer()
}
private def freeWinBuffer(): Unit = {
if (winRaf != null) {
winRaf.close()
winF.delete()
winRaf = null
winF = null
}
winBuf = null
}
protected def freeMainInputBuffers(): Unit = {
if (bufIn != null) {
bufIn.release()
bufIn = null
}
if (bufSize != null) {
bufSize.release()
bufSize = null
}
}
def freeOutputBuffers(): Unit =
if (bufOut0 != null) {
bufOut0.release()
bufOut0 = null
}
protected def readMainIns(): Int = {
freeMainInputBuffers()
bufIn = grab(in0)
tryPull(in0)
if (isAvailable(in1)) {
bufSize = grab(in1)
tryPull(in1)
if (size == 0) {
size = math.max(1, bufSize.buf(0))
inArr = new Array[Double](size)
outArr = new Array[Double](size)
}
}
_inMainValid = true
_canReadMain = false
bufIn.size
}
// ---- process ----
// private def STATE(): String =
// if (lockInToVal ) "in -> value"
// else if (lockValToOut) "value -> out"
// else if (lockValToWin) "resample"
// else ""
//
// protected def processChunk(): Boolean = {
// val before = STATE()
// val res = processChunkX()
// val after = STATE()
// // debugLog(s"BEFORE: $before; AFTER: $after; res = $res")
// res
// }
protected def processChunk(): Boolean = {
if (lockInToVal || !lockValToWin) {
val isFlush = shouldComplete()
(inMainRemain > 0 || isFlush) && {
// debugLog("---> read")
val sz = size
val valOff0 = valOff
val valRem = sz - valOff0
val chunk = math.min(inMainRemain, valRem)
Util.copy(bufIn.buf, inMainOff, inArr, valOff0, chunk)
inMainOff += chunk
inMainRemain -= chunk
val valOff1 = valOff0 + chunk
if (isFlush) Util.clear(inArr, valOff1, valRem - chunk)
if (isFlush || valOff1 == sz) {
// debugLog("---> read DONE")
// ready for resample
lockInToVal = false
lockValToWin = true
valOff = 0
resample()
} else {
lockInToVal = true
valOff = valOff1
}
true
}
} else if (lockValToOut) {
(outRemain > 0) && {
// debugLog("---> write")
val valOff0 = valOff
val sz = size
val valRem = sz - valOff0
val chunk = math.min(outRemain, valRem)
Util.copy(outArr, valOff0, bufOut0.buf, outOff, chunk)
outOff += chunk
outRemain -= chunk
val valOff1 = valOff0 + chunk
if (valOff1 == sz) {
// debugLog("---> write DONE")
// ready for next read
lockValToOut = false
valOff = 0
} else {
valOff = valOff1
}
true
}
} else if (lockValToWin || !(lockInToVal || lockValToOut)) {
resample()
} else false
}
protected def allocWinBuf(len: Int): Unit = {
freeWinBuffer()
val bufSize = len * size
if (bufSize <= ctrl.nodeBufferSize) {
val arr = new Array[Double](bufSize)
winBuf = DoubleBuffer.wrap(arr)
} else {
winF = ctrl.createTempFile()
winRaf = new RandomAccessFile(winF, "rw")
val fch = winRaf.getChannel
val bb = fch.map(FileChannel.MapMode.READ_WRITE, 0L, bufSize * 8)
winBuf = bb.asDoubleBuffer()
}
}
protected def availableInFrames : Int = {
val res = if (lockValToWin) 1 else 0
// debugLog(s"<--- availableInFrames $res")
res
}
protected def availableOutFrames: Int = {
val res = if (/* lockInToVal || lockValToWin || */ lockValToOut) 0 else 1
// debugLog(s"<--- availableOutFrames $res")
res
}
protected def clearWinBuf(off: Int, len: Int): Unit = {
val b = winBuf
val sz = size
val off1 = off * sz
b.position(off1)
var i = 0
while (i < sz) {
b.put(0.0)
i += 1
}
}
protected def copyInToWinBuf(winOff: Int, len: Int): Unit = {
// debugLog("---> copyInToWinBuf")
assert(len == 1 && !lockInToVal && lockValToWin /* && !lockValToOut */)
val b = winBuf
val off1 = winOff * size
b.position(off1)
b.put(inArr)
lockValToWin = false
}
protected def clearValue(): Unit = {
// debugLog("---> clearValue")
Util.clear(outArr, 0, outArr.length)
}
protected def addToValue(winOff: Int, weight: Double): Unit = {
val sz = size
val off1 = winOff * sz
val b = winBuf
b.position(off1)
val arr = outArr
var i = 0
while (i < sz) {
arr(i) += b.get() * weight
i += 1
}
}
protected def copyValueToOut(): Unit = {
// debugLog("---> copyValueToOut")
assert(/* !lockInToVal && !lockValToWin && */ !lockValToOut)
Util.mul(outArr, 0, size, gain)
lockValToOut = true
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy