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

de.sciss.fscape.graph.ComplexUnaryOp.scala Maven / Gradle / Ivy

There is a newer version: 2.28.0
Show newest version
/*
 *  ComplexUnaryOp.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 graph

import de.sciss.fscape.stream.{StreamIn, StreamOut}

import scala.annotation.switch
import scala.collection.immutable.{IndexedSeq => Vec}

/** Unary operator assuming stream is complex signal (real and imaginary interleaved).
  * Outputs another complex stream even if the operator yields a purely real-valued result
  * (ex. `abs`).
  *
  * XXX TODO - need more ops such as conjugate, polar-to-cartesian, ...
  */
object ComplexUnaryOp {
  object Op {
    def apply(id: Int): Op = (id: @switch) match {
      // case Neg        .id => Neg
      //      case Not        .id => Not
      case Abs        .id => Abs
      //      case Ceil       .id => Ceil
      //      case Floor      .id => Floor
      //      case Frac       .id => Frac
      //      case Signum     .id => Signum
      case Squared    .id => Squared
      case Cubed      .id => Cubed
      //      case Sqrt       .id => Sqrt
      case Exp        .id => Exp
      case Reciprocal .id => Reciprocal
      //      case Midicps    .id => Midicps
      //      case Cpsmidi    .id => Cpsmidi
      //      case Midiratio  .id => Midiratio
      //      case Ratiomidi  .id => Ratiomidi
      //      case Dbamp      .id => Dbamp
      //      case Ampdb      .id => Ampdb
      //      case Octcps     .id => Octcps
      //      case Cpsoct     .id => Cpsoct
      case Log        .id => Log
      case Log2       .id => Log2
      case Log10      .id => Log10
      //      case Sin        .id => Sin
      //      case Cos        .id => Cos
      //      case Tan        .id => Tan
      //      case Asin       .id => Asin
      //      case Acos       .id => Acos
      //      case Atan       .id => Atan
      //      case Sinh       .id => Sinh
      //      case Cosh       .id => Cosh
      //      case Tanh       .id => Tanh
      //      case Distort    .id => Distort
      //      case Softclip   .id => Softclip
      //      case Ramp       .id => Ramp
      //      case Scurve     .id => Scurve
      case Conj       .id => Conj
    }
  }

  sealed trait Op {
    op =>

    def id: Int

    final def make(a: GE): GE = new ComplexUnaryOp(op.id, a)

    /** Transfers values from an input buffer
      * to an output buffer,
      * applying the operator.
      *
      * @param in       the buffer to read from, assuming interleaved re, im data
      * @param inOff    the index into `in`. this is a direct array index, not
      *                 a logical index which must be multiplied by two!
      * @param out      the buffer to read from, assuming interleaved re, im data
      * @param outOff   the index into `out`. this is a direct array index, not
      *                 a logical index which must be multiplied by two!
      * @param len      logical length of the operation, that is the number of
      *                 complex numbers to transfer. the number of `Double` values
      *                 read from `in` and written to `out` is twice `len`!
      */
    def apply(in: Array[Double], inOff: Int, out: Array[Double], outOff: Int, len: Int): Unit

    def name: String = plainName.capitalize

    private def plainName: String = {
      val cn = getClass.getName
      val sz = cn.length
      val i  = cn.indexOf('$') + 1
      cn.substring(i, if (cn.charAt(sz - 1) == '$') sz - 1 else sz)
    }
  }

  //  case object Neg extends Op {
  //    final val id = 0
  //    def apply(a: Double): Double = -a
  //  }

  //  case object Not extends Op {
  //    final val id = 1
  //    def apply(a: Double): Double = rd2.not(a)
  //  }

  // case object IsNil       extends Op(  2 )
  // case object NotNil      extends Op(  3 )
  // case object BitNot      extends Op(  4 )
  case object Abs extends Op {
    final val id = 5

    def apply(in: Array[Double], inOff: Int, out: Array[Double], outOff: Int, len: Int): Unit = {
      val inStop = inOff + (len << 1)
      var i = inOff
      var j = outOff
      while (i < inStop) {
        val inRe  = in(i); i += 1
        val inIm  = in(i); i += 1
        val outRe = math.sqrt(inRe * inRe + inIm * inIm)
        val outIm = 0.0
        out(j) = outRe; j += 1
        out(j) = outIm; j += 1
      }
    }
  }

  case object Squared extends Op {
    final val id = 12

    def apply(in: Array[Double], inOff: Int, out: Array[Double], outOff: Int, len: Int): Unit = {
      val inStop = inOff + (len << 1)
      var i = inOff
      var j = outOff
      while (i < inStop) {
        val inRe  = in(i); i += 1
        val inIm  = in(i); i += 1
        val outRe = inRe * inRe - inIm * inIm
        val outIm = inRe * inIm * 2
        out(j) = outRe; j += 1
        out(j) = outIm; j += 1
      }
    }
  }

  case object Cubed extends Op {
    final val id = 13

    def apply(in: Array[Double], inOff: Int, out: Array[Double], outOff: Int, len: Int): Unit = {
      val inStop = inOff + (len << 1)
      var i = inOff
      var j = outOff
      while (i < inStop) {
        val inRe  = in(i); i += 1
        val inIm  = in(i); i += 1
        val tmpRe = inRe * inRe - inIm * inIm
        val tmpIm = inRe * inIm * 2
        val outRe = tmpRe * inRe - tmpIm * inIm   // XXX TODO -- can we simplify this?
        val outIm = tmpRe * inIm + inRe  * tmpIm
        out(j) = outRe; j += 1
        out(j) = outIm; j += 1
      }
    }
  }

  case object Exp extends Op {
    final val id = 15

    def apply(in: Array[Double], inOff: Int, out: Array[Double], outOff: Int, len: Int): Unit = {
      // Mag  (out) = Exp(Re(in))
      // Phase(out) = Im(in)
      val inStop = inOff + (len << 1)
      var i = inOff
      var j = outOff
      while (i < inStop) {
        val inRe    = in(i); i += 1
        val inIm    = in(i); i += 1
        val outMag  = math.exp(inRe)
        val outPhase= inIm
        val outRe   = outMag * math.cos(outPhase)
        val outIm   = outMag * math.sin(outPhase)
        out(j) = outRe; j += 1
        out(j) = outIm; j += 1
      }
    }
  }

  case object Reciprocal extends Op {
    final val id = 16

    def apply(in: Array[Double], inOff: Int, out: Array[Double], outOff: Int, len: Int): Unit = {
      // Re(out) =  Re(in) / (Re(in)^2 + Im(in)^2)
      // Im(out) = -Im(in) / (Re(in)^2 + Im(in)^2)
      val inStop = inOff + (len << 1)
      var i = inOff
      var j = outOff
      while (i < inStop) {
        val inRe    = in(i); i += 1
        val inIm    = in(i); i += 1
        val div     = inRe * inRe + inIm * inIm
        val outRe   =  inRe / div
        val outIm   = -inIm / div
        out(j) = outRe; j += 1
        out(j) = outIm; j += 1
      }
    }
  }

  case object Log extends Op {
    final val id = 25

    def apply(in: Array[Double], inOff: Int, out: Array[Double], outOff: Int, len: Int): Unit =
      base(in = in, inOff = inOff, out = out, outOff = outOff, len = len, mul = 1.0)

    private[ComplexUnaryOp] def base(in: Array[Double], inOff: Int, out: Array[Double], outOff: Int, len: Int,
                                     mul: Double): Unit = {
      // Re(out) = Log(Mag(in))
      // Im(out) = Phase(in)
      val inStop = inOff + (len << 1)
      var i = inOff
      var j = outOff
      while (i < inStop) {
        val inRe    = in(i); i += 1
        val inIm    = in(i); i += 1
        val inMag   = math.sqrt(inRe * inRe + inIm * inIm)
        val inPhase = math.atan2(inIm, inRe)
        val outRe   = math.log(inMag) * mul
        val outIm   = inPhase         * mul
        out(j) = outRe; j += 1
        out(j) = outIm; j += 1
      }
    }
  }

  case object Log2 extends Op {
    final val id = 26

    private[this] final val Ln2R = 1.0 / math.log(2)

    def apply(in: Array[Double], inOff: Int, out: Array[Double], outOff: Int, len: Int): Unit =
      Log.base(in = in, inOff = inOff, out = out, outOff = outOff, len = len, mul = Ln2R)
  }

  case object Log10 extends Op {
    final val id = 27

    private[this] final val Ln10R = 1.0 / math.log(10)

    def apply(in: Array[Double], inOff: Int, out: Array[Double], outOff: Int, len: Int): Unit =
      Log.base(in = in, inOff = inOff, out = out, outOff = outOff, len = len, mul = Ln10R)
  }

  case object Conj extends Op {
    final val id = 100

    def apply(in: Array[Double], inOff: Int, out: Array[Double], outOff: Int, len: Int): Unit = {
      val inStop = inOff + (len << 1)
      var i = inOff
      var j = outOff
      while (i < inStop) {
        val inRe    = in(i); i += 1
        val inIm    = in(i); i += 1
        val outRe   = inRe
        val outIm   = -inIm
        out(j) = outRe; j += 1
        out(j) = outIm; j += 1
      }
    }
  }
}
final case class ComplexUnaryOp(op: Int, in: GE) extends UGenSource.SingleOut {

  protected def makeUGens(implicit b: UGenGraph.Builder): UGenInLike =
    unwrap(Vector(in.expand))

  protected def makeUGen(args: Vec[UGenIn])(implicit b: UGenGraph.Builder): UGenInLike =
    UGen.SingleOut(this, inputs = args, rest = op)

  private[fscape] def makeStream(args: Vec[StreamIn])(implicit b: stream.Builder): StreamOut = {
    val Vec(in) = args
    val op0 = ComplexUnaryOp.Op(op)
    stream.ComplexUnaryOp(op = op0, in = in.toDouble)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy