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

de.sciss.synth.ugen.BasicOpUGen.scala Maven / Gradle / Ivy

/*
 *  BasicOpUGen.scala
 *  (ScalaColliderUGens)
 *
 *  Copyright (c) 2008-2022 Hanns Holger Rutz. All rights reserved.
 *
 *  This software is published under the GNU Lesser General Public License v2.1+
 *
 *
 *  For further information, please contact Hanns Holger Rutz at
 *  [email protected]
 */

package de.sciss.synth
package ugen

import de.sciss.numbers.{FloatFunctions => rf, FloatFunctions2 => rf2, LongFunctions => lf}
import de.sciss.synth.UGenSource._
import de.sciss.synth.ugen.Constant.{C0, C1, Cm1}
import de.sciss.synth.ugen.{Constant => c}

import scala.annotation.switch

/** Unary operations are generally constructed by calling one of the methods of `GEOps`.
  *
  * @see  GEOps
  * @see  BinaryOpUGen
  */
object UnaryOpUGen extends ProductReader[UnaryOpUGen] {
  // note: this is not optimizing, as would be `op.make(a)`, because it guarantees that the return
  // type is UnaryOpUGen. this is used in deserialization, you should prefer `op.make` instead.
  def apply(op: Op, a: GE): UnaryOpUGen = op.makeNoOptimization(a)

  def unapply(b: UnaryOpUGen): Option[(Op, GE)] = Some((b.selector, b.a))

  object Op extends ProductReader[Op] {
    def apply(id: Int): Op = (id: @switch) match {
      case Neg        .id => Neg
      case Not        .id => Not
      case BitNot     .id => BitNot
      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 Rand       .id => Rand
      case Rand2      .id => Rand2
      case Linrand    .id => Linrand
      case Bilinrand  .id => Bilinrand
      case Sum3rand   .id => Sum3rand
      case Distort    .id => Distort
      case Softclip   .id => Softclip
      case Coin       .id => Coin
      // case DigitValue ?
      // case Silence ?
      // case Thru ?
      case RectWindow .id => RectWindow
      case HannWindow .id => HannWindow
      case WelchWindow.id => WelchWindow
      case TriWindow  .id => TriWindow
      case Ramp       .id => Ramp
      case Scurve     .id => Scurve
    }

    override def read(in: RefMapIn, key: String, arity: Int): Op = {
      require (arity == 1)
      val _id = in.readInt()
      Op(_id)
    }
  }

  private def uncapitalize(in: String): String = if (in.isEmpty) in else {
    val c0 = in.charAt(0)
    if (Character.isUpperCase(c0)) s"${Character.toLowerCase(c0)}${in.substring(1)}" else in
  }

  sealed abstract class Op extends Product {
    op =>

    def id: Int

    def make(a: GE): GE

    def make1(a: UGenIn): UGenIn

    def makeNoOptimization(a: GE): UnaryOpUGen

    override final def productPrefix  = s"UnaryOpUGen$$Op"
    override final def productArity   = 1
    override final def productElement(n: Int): Any = if (n == 0) id else throw new IndexOutOfBoundsException(n.toString)

    /** The name one would use to display the operator in
      * a user friendly way.
      * For example, for `Neg` this would be `-`.
      */
    def name: String = uncapitalize(plainName)

    /** Whether the operator should be written in prefix position.
      * For example, `Ampdb` is '''not''' in prefix position, you would write
      * `signal.ampDb`, whereas `Neg` is prefix position, as you would write
      * `-signal`.
      */
    def prefix: Boolean = false

    /** The name one would call on `GEOps` to obtain this operator.
      * For example, for `Neg` this would be `unary_-`.
      */
    def methodName: String =
      if (prefix) s"unary_$name" else name
    
    private final 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)
    }
  }

  sealed abstract class PureOp extends Op {
    op =>

    final def make(a: GE): GE = a match {
      case c(f) => c(make1(f))
      case _    => Pure(op, a)
    }

    final def make1(a: UGenIn): UGenIn = a match {
      case c(f) => c(make1(f))
      case _    => UGenImpl(op, a, isIndividual = false, hasSideEffect = false)
    }

    final def makeNoOptimization(a: GE): UnaryOpUGen = Pure(this, a)

    def make1(a: Float): Float
  }

  sealed abstract class RandomOp extends Op {
    op =>

    final def make(a: GE): GE = makeNoOptimization(a)

    final def make1(a: UGenIn): UGenIn = UGenImpl(op, a, isIndividual = true, hasSideEffect = false)

    final def makeNoOptimization(a: GE): UnaryOpUGen = Random(this, a)
  }

  case object Neg extends PureOp {
    final val id = 0
    override val name = "-"
    override def prefix = true
    def make1(a: Float): Float = rf2.neg(a)
  }

  case object Not extends PureOp {
    final val id = 1
    override val name = "!"
    override def prefix = true
    def make1(a: Float): Float = rf2.not(a)
  }

  // case object IsNil       extends Op(  2 )
  // case object NotNil      extends Op(  3 )
  case object BitNot extends PureOp {
    final val id = 4
    def make1(a: Float): Float = ~a.toInt
  }
  case object Abs extends PureOp {
    final val id = 5
    def make1(a: Float): Float = rf.abs(a)
  }

  // case object ToFloat     extends Op(  6 )
  // case object ToInt       extends Op(  7 )
  case object Ceil extends PureOp {
    final val id = 8
    def make1(a: Float): Float = rf.ceil(a)
  }

  case object Floor extends PureOp {
    final val id = 9
    def make1(a: Float): Float = rf.floor(a)
  }

  case object Frac extends PureOp {
    final val id = 10
    def make1(a: Float): Float = rf.frac(a)
  }

  case object Signum extends PureOp {
    final val id = 11
    def make1(a: Float): Float = rf.signum(a)
  }

  case object Squared extends PureOp {
    final val id = 12
    def make1(a: Float): Float = rf.squared(a)
  }

  case object Cubed extends PureOp {
    final val id = 13
    def make1(a: Float): Float = rf2.cubed(a)
  }

  case object Sqrt extends PureOp {
    final val id = 14
    def make1(a: Float): Float = rf.sqrt(a)
  }

  case object Exp extends PureOp {
    final val id = 15
    def make1(a: Float): Float = rf.exp(a)
  }

  case object Reciprocal extends PureOp {
    final val id = 16
    def make1(a: Float): Float = rf2.reciprocal(a)
  }

  // XXX TODO: Use proper camel-case naming in next major version
  case object Midicps extends PureOp {
    final val id = 17
    override val name = "midiCps"
    def make1(a: Float): Float = rf.midiCps(a)
  }

  case object Cpsmidi extends PureOp {
    final val id = 18
    override val name = "cpsMidi"
    def make1(a: Float): Float = rf.cpsMidi(a)
  }

  case object Midiratio extends PureOp {
    final val id = 19
    override val name = "midiRatio"
    def make1(a: Float): Float = rf.midiRatio(a)
  }

  case object Ratiomidi extends PureOp {
    final val id = 20
    override val name = "ratioMidi"
    def make1(a: Float): Float = rf.ratioMidi(a)
  }

  case object Dbamp extends PureOp {
    final val id = 21
    override val name = "dbAmp"
    def make1(a: Float): Float = rf.dbAmp(a)
  }

  case object Ampdb extends PureOp {
    final val id = 22
    override val name = "ampDb"
    def make1(a: Float): Float = rf.ampDb(a)
  }

  case object Octcps extends PureOp {
    final val id = 23
    override val name = "octCps"
    def make1(a: Float): Float = rf.octCps(a)
  }

  case object Cpsoct extends PureOp {
    final val id = 24
    override val name = "cpsOct"
    def make1(a: Float): Float = rf.cpsOct(a)
  }

  case object Log extends PureOp {
    final val id = 25
    def make1(a: Float): Float = rf.log(a)
  }

  case object Log2 extends PureOp {
    final val id = 26
    def make1(a: Float): Float = rf.log2(a)
  }

  case object Log10 extends PureOp {
    final val id = 27
    def make1(a: Float): Float = rf.log10(a)
  }

  case object Sin extends PureOp {
    final val id = 28
    def make1(a: Float): Float = rf.sin(a)
  }

  case object Cos extends PureOp {
    final val id = 29
    def make1(a: Float): Float = rf.cos(a)
  }

  case object Tan extends PureOp {
    final val id = 30
    def make1(a: Float): Float = rf.tan(a)
  }

  case object Asin extends PureOp {
    final val id = 31
    def make1(a: Float): Float = rf.asin(a)
  }

  case object Acos extends PureOp {
    final val id = 32
    def make1(a: Float): Float = rf.acos(a)
  }

  case object Atan extends PureOp {
    final val id = 33
    def make1(a: Float): Float = rf.atan(a)
  }

  case object Sinh extends PureOp {
    final val id = 34
    def make1(a: Float): Float = rf.sinh(a)
  }

  case object Cosh extends PureOp {
    final val id = 35
    def make1(a: Float): Float = rf.cosh(a)
  }

  case object Tanh extends PureOp {
    final val id = 36
    def make1(a: Float): Float = rf.tanh(a)
  }

  case object Rand extends RandomOp {
    final val id = 37
  }

  case object Rand2 extends RandomOp {
    final val id = 38
  }

  case object Linrand extends RandomOp {
    final val id = 39
    override val name = "linRand"
  }

  case object Bilinrand extends RandomOp {
    final val id = 40
    override val name = "bilinRand"
  }

  case object Sum3rand extends RandomOp {
    final val id = 41
    override val name = "sum3Rand"
  }

  case object Distort extends PureOp {
    final val id = 42
    def make1(a: Float): Float = rf2.distort(a)
  }

  case object Softclip extends PureOp {
    final val id = 43
    override val name = "softClip"
    def make1(a: Float): Float = rf2.softClip(a)
  }

  case object Coin extends RandomOp {
    final val id = 44
  }

  // case object DigitValue  extends Op( 45 )
  // case object Silence     extends Op( 46 )
  // case object Thru        extends Op( 47 )
   case object RectWindow extends PureOp {
    final val id = 48
    def make1(a: Float): Float = rf2.rectWindow(a)
  }
  case object HannWindow extends PureOp {
    final val id = 49
    def make1(a: Float): Float = rf2.hannWindow(a)
  }
  case object WelchWindow extends PureOp {
    final val id = 50
    def make1(a: Float): Float = rf2.welchWindow(a)
  }
  case object TriWindow extends PureOp {
    final val id = 51
    def make1(a: Float): Float = rf2.triWindow(a)
  }
  case object Ramp extends PureOp {
    final val id = 52
    def make1(a: Float): Float = rf2.ramp(a)
  }

  case object Scurve extends PureOp {
    final val id = 53
    override val name = "sCurve"
    def make1(a: Float): Float = rf2.sCurve(a)
  }

  override def read(in: RefMapIn, key: String, arity: Int): UnaryOpUGen = {
    require (arity == 2)
    val _op = in.readProductT[Op]()
    val _a  = in.readGE()
    apply(_op, _a)
  }

  private final case class Pure(selector: Op, a: GE)
    extends UnaryOpUGen

//  private final case class Impure(selector: Op, a: GE)
//    extends UnaryOpUGen with HasSideEffect {
//  }

  private final case class Random(selector: Op, a: GE)
    extends UnaryOpUGen with UsesRandSeed

  private def UGenImpl(selector: Op, a: UGenIn, isIndividual: Boolean, hasSideEffect: Boolean): UGen.SingleOut =
      UGen.SingleOut("UnaryOpUGen", a.rate, Vector(a),
        isIndividual = isIndividual, hasSideEffect = hasSideEffect, specialIndex = selector.id)
}

abstract class UnaryOpUGen extends UGenSource.SingleOut {

  def selector: UnaryOpUGen.Op
  def a: GE

  override final def productPrefix = "UnaryOpUGen"

  final def rate: MaybeRate = a.rate

  protected final def makeUGens: UGenInLike = unwrap(this, Vector(a.expand))

  protected final def makeUGen(args: Vec[UGenIn]): UGenInLike = {
    val a = args.head
    selector.make1(a)
  }

  override def toString: String = if (selector.prefix)
    s"(${selector.name}$a)"
  else
    s"$a.${selector.name}"
}

/** Binary operations are generally constructed by calling one of the methods of `GEOps`.
  *
  * @see  GEOps
  * @see  UnaryOpUGen
  */
object BinaryOpUGen extends ProductReader[BinaryOpUGen] {
  // note: this is not optimizing, as would be `op.make(a, b)`, because it guarantees that the return
  // type is BinaryOpUGen. this is used in deserialization, you should prefer `op.make` instead.
  def apply(op: Op, a: GE, b: GE): BinaryOpUGen = op.makeNoOptimization(a, b)

  def unapply(b: BinaryOpUGen): Option[(Op, GE, GE)] = Some((b.selector, b.a, b.b))

  object Op extends ProductReader[Op] {
    def apply(id: Int): Op = (id: @switch) match {
      case Plus     .id => Plus
      case Minus    .id => Minus
      case Times    .id => Times
      case Div      .id => Div
      case Mod      .id => Mod
      case Eq       .id => Eq
      case Neq      .id => Neq
      case Lt       .id => Lt
      case Gt       .id => Gt
      case Leq      .id => Leq
      case Geq      .id => Geq
      case Min      .id => Min
      case Max      .id => Max
      case BitAnd   .id => BitAnd
      case BitOr    .id => BitOr
      case BitXor   .id => BitXor
      case Lcm      .id => Lcm
      case Gcd      .id => Gcd
      case RoundTo  .id => RoundTo
      case RoundUpTo.id => RoundUpTo
      case Trunc    .id => Trunc
      case Atan2    .id => Atan2
      case Hypot    .id => Hypot
      case Hypotx   .id => Hypotx
      case Pow      .id => Pow
      case LeftShift.id => LeftShift
      case RightShift.id => RightShift
      case Ring1    .id => Ring1
      case Ring2    .id => Ring2
      case Ring3    .id => Ring3
      case Ring4    .id => Ring4
      case Difsqr   .id => Difsqr
      case Sumsqr   .id => Sumsqr
      case Sqrsum   .id => Sqrsum
      case Sqrdif   .id => Sqrdif
      case Absdif   .id => Absdif
      case Thresh   .id => Thresh
      case Amclip   .id => Amclip
      case Scaleneg .id => Scaleneg
      case Clip2    .id => Clip2
      case Excess   .id => Excess
      case Fold2    .id => Fold2
      case Wrap2    .id => Wrap2
      case Firstarg .id => Firstarg
      case Rrand    .id => Rrand
      case Exprand  .id => Exprand
    }

    override def read(in: RefMapIn, key: String, arity: Int): Op = {
      require (arity == 1)
      val _id = in.readInt()
      Op(_id)
    }
  }

  import UnaryOpUGen.{Neg, Reciprocal}

  private def uncapitalize(in: String): String = if (in.isEmpty) in else {
    val c0 = in.charAt(0)
    if (Character.isUpperCase(c0)) s"${Character.toLowerCase(c0)}${in.substring(1)}" else in
  }

  sealed abstract class Op extends Product {
    op =>

    def id: Int

    def make(a: GE, b: GE): GE

    protected[synth] def make1(a: UGenIn, b: UGenIn): UGenIn

    /** Whether the operator should be written in infix position.
      * For example, `Lcm` is '''not''' in prefix position, you would write
      * `a.lcm(b)`, whereas `Plus` is prefix position, as you would write
      * `a + b`.
      */
    def infix: Boolean = false

    /** The name one would call on `GEOps` to obtain this operator.
      * For example, for `Times` this would be `*`.
      */
    def methodName: String = name

    def makeNoOptimization(a: GE, b: GE): BinaryOpUGen

    override def productPrefix  = s"BinaryOpUGen$$Op"
    override def productArity   = 1
    override def productElement(n: Int): Any = if (n == 0) id else throw new IndexOutOfBoundsException(n.toString)

    def name: String = uncapitalize(plainName)

    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)
    }
  }

  sealed abstract class PureOp extends Op {
    def make(a: GE, b: GE): GE = (a, b) match {
      case (c(af), c(bf)) => c(make1(af, bf))
      case _              => Pure(this, a, b)
    }

    protected[synth] def make1(a: UGenIn, b: UGenIn): UGenIn = (a, b) match {
      case (c(af), c(bf)) => c(make1(af, bf))
      case _              => UGenImpl(this, a, b, isIndividual = false, hasSideEffect = false)
    }

    def make1(a: Float, b: Float): Float

    final def makeNoOptimization(a: GE, b: GE): BinaryOpUGen = Pure(this, a, b)
  }

  sealed abstract class ImpureOp extends Op {
    final def makeNoOptimization(a: GE, b: GE): BinaryOpUGen = Impure(this, a, b)
  }

  sealed abstract class RandomOp extends Op {
    final def make(a: GE, b: GE): GE = makeNoOptimization(a, b)

    final protected[synth] def make1(a: UGenIn, b: UGenIn): UGenIn =
      UGenImpl(this, a, b, isIndividual = true, hasSideEffect = false)

    final def makeNoOptimization(a: GE, b: GE): BinaryOpUGen = Random(this, a, b)
  }

  case object Plus extends PureOp {
    final val id = 0
    override val name = "+"
    override def infix = true

    def make1(a: Float, b: Float): Float = rf.+(a, b)

    override protected[synth] def make1(a: UGenIn, b: UGenIn): UGenIn = (a, b) match {
      case (C0, _)  => b
      case (_, C0)  => a
      case _        => super.make1(a, b)
    }
  }

  case object Minus extends PureOp {
    final val id = 1
    override val name = "-"
    override def infix = true

    def make1(a: Float, b: Float): Float = rf.-(a, b)

    override protected[synth] def make1(a: UGenIn, b: UGenIn): UGenIn = (a, b) match {
      case (C0, _) => Neg.make1(b)
      case (_, C0) => a
      case _ => super.make1(a, b)
    }
  }

  case object Times extends PureOp {
    final val id = 2
    override val name = "*"
    override def infix = true

    def make1(a: Float, b: Float): Float = rf.*(a, b)

    override protected[synth] def make1(a: UGenIn, b: UGenIn): UGenIn = (a, b) match {
      case (C0, _)  => a
      case (_, C0)  => b
      case (C1, _)  => b
      case (_, C1)  => a
      case (Cm1, _) => Neg.make1(b) // -b
      case (_, Cm1) => Neg.make1(a) // -a
      case _        => super.make1(a, b)
    }
  }

  // case object IDiv           extends Op(  3 )
  case object Div extends PureOp {
    final val id = 4
    override val name = "/"
    override def infix = true

    def make1(a: Float, b: Float): Float = rf./(a, b)

    override protected[synth] def make1(a: UGenIn, b: UGenIn): UGenIn = (a, b) match {
      case (_, C1)  => a
      case (_, Cm1) => Neg.make1(a) // -a
      case _          =>
        if (b.rate == scalar) {
          Times.make1(a, Reciprocal.make1(b))
        } else {
          super.make1(a, b)
        }
    }
  }

  case object Mod extends PureOp {
    final val id = 5
    override val name = "%"
    override def infix = true
    def make1(a: Float, b: Float): Float = rf.mod(a, b)
  }

  case object Eq extends PureOp {
    final val id = 6
    override val name = "sig_=="
    override def infix = true
    def make1(a: Float, b: Float): Float = if (a == b) 1 else 0
  }

  case object Neq extends PureOp {
    final val id = 7
    override val name = "sig_!="
    override def infix = true
    def make1(a: Float, b: Float): Float = if (a != b) 1 else 0
  }

  case object Lt extends PureOp {
    final val id = 8
    override val name = "<"
    override def infix = true
    def make1(a: Float, b: Float): Float = if (a < b) 1f else 0f // NOT rf.< !
  }

  case object Gt extends PureOp {
    final val id = 9
    override val name = ">"
    override def infix = true
    def make1(a: Float, b: Float): Float = if (a > b) 1f else 0f // NOT rf.> !
  }

  case object Leq extends PureOp {
    final val id = 10
    override val name = "<="
    override def infix = true
    def make1(a: Float, b: Float): Float = if (a <= b) 1f else 0f // NOT rf.<= !
  }

  case object Geq extends PureOp {
    final val id = 11
    override val name = ">="
    override def infix = true
    def make1(a: Float, b: Float): Float = if (a >= b) 1f else 0f // NOT rf.>= !
  }

  case object Min extends PureOp {
    final val id = 12
    def make1(a: Float, b: Float): Float = rf.min(a, b)
  }

  case object Max extends PureOp {
    final val id = 13
    def make1(a: Float, b: Float): Float = rf.max(a, b)
  }

  case object BitAnd extends PureOp {
    final val id = 14
    override def infix = true
    override val name = "&"
    def make1(a: Float, b: Float): Float = rf.&(a, b)
  }

  case object BitOr extends PureOp {
    final val id = 15
    override def infix = true
    override val name = "|"
    def make1(a: Float, b: Float): Float = rf.|(a, b)
  }

  case object BitXor extends PureOp {
    final val id = 16
    override def infix = true
    override val name = "^"
    def make1(a: Float, b: Float): Float = rf.^(a, b)
  }

  case object Lcm extends PureOp {
    final val id = 17
    def make1(a: Float, b: Float): Float = lf.lcm(a.toLong, b.toLong).toFloat
  }

  case object Gcd extends PureOp {
    final val id = 18
    def make1(a: Float, b: Float): Float = lf.gcd(a.toLong, b.toLong).toFloat
  }

  case object RoundTo extends PureOp {
    final val id = 19
    def make1(a: Float, b: Float): Float = rf.roundTo(a, b)
  }

  case object RoundUpTo extends PureOp {
    final val id = 20
    def make1(a: Float, b: Float): Float = rf.roundUpTo(a, b)
  }

  case object Trunc extends PureOp {
    final val id = 21
    def make1(a: Float, b: Float): Float = rf.trunc(a, b)
  }

  case object Atan2 extends PureOp {
    final val id = 22
    def make1(a: Float, b: Float): Float = rf.atan2(a, b)
  }

  case object Hypot extends PureOp {
    final val id = 23
    def make1(a: Float, b: Float): Float = rf.hypot(a, b)
  }

  case object Hypotx extends PureOp {
    final val id = 24
    override val name = "hypotApx"
    def make1(a: Float, b: Float): Float = rf.hypotApx(a, b)
  }

  /** '''Warning:''' Unlike a normal power operation, the signum of the
    * left operand is always preserved. I.e. `DC.kr(-0.5).pow(2)` will
    * not output `0.25` but `-0.25`. This is to avoid problems with
    * floating point noise and negative input numbers, so
    * `DC.kr(-0.5).pow(2.001)` does not result in a `NaN`, for example.
    */
  case object Pow extends PureOp {
    final val id = 25
    def make1(a: Float, b: Float): Float = rf.pow(a, b)
  }

  case object LeftShift extends PureOp {
    final val id = 26
    override def infix = true
    override val name = "<<"
    def make1(a: Float, b: Float): Float = (a.toLong << b.toInt).toFloat
  }

  case object RightShift extends PureOp {
    final val id = 27
    override def infix = true
    override val name = ">>"
    def make1(a: Float, b: Float): Float = (a.toLong >> b.toInt).toFloat
  }
  // case object UnsgnRghtShft  extends Op( 28 )
  // case object Fill           extends Op( 29 )
  case object Ring1 extends PureOp {
    final val id = 30
    def make1(a: Float, b: Float): Float = rf2.ring1(a, b)
  }

  case object Ring2 extends PureOp {
    final val id = 31
    def make1(a: Float, b: Float): Float = rf2.ring2(a, b)
  }

  case object Ring3 extends PureOp {
    final val id = 32
    def make1(a: Float, b: Float): Float = rf2.ring3(a, b)
  }

  case object Ring4 extends PureOp {
    final val id = 33
    def make1(a: Float, b: Float): Float = rf2.ring4(a, b)
  }

  /*
   * Note: we do not use camel-case for the object name
   * because it would break serialization for older versions.
   */
  case object Difsqr extends PureOp {
    final val id = 34
    override val name = "difSqr"
    def make1(a: Float, b: Float): Float = rf.difSqr(a, b)
  }

  case object Sumsqr extends PureOp {
    final val id = 35
    override val name = "sumSqr"
    def make1(a: Float, b: Float): Float = rf.sumSqr(a, b)
  }

  case object Sqrsum extends PureOp {
    final val id = 36
    override val name = "sqrSum"
    def make1(a: Float, b: Float): Float = rf.sqrSum(a, b)
  }

  case object Sqrdif extends PureOp {
    final val id = 37
    override val name = "sqrDif"
    def make1(a: Float, b: Float): Float = rf.sqrDif(a, b)
  }

  case object Absdif extends PureOp {
    final val id = 38
    override val name = "absDif"
    def make1(a: Float, b: Float): Float = rf.absDif(a, b)
  }

  case object Thresh extends PureOp {
    final val id = 39
    def make1(a: Float, b: Float): Float = rf2.thresh(a, b)
  }

  case object Amclip extends PureOp {
    final val id = 40
    override val name = "amClip"
    def make1(a: Float, b: Float): Float = rf2.amClip(a, b)
  }

  case object Scaleneg extends PureOp {
    final val id = 41
    override val name = "scaleNeg"
    def make1(a: Float, b: Float): Float = rf2.scaleNeg(a, b)
  }

  case object Clip2 extends PureOp {
    final val id = 42
    def make1(a: Float, b: Float): Float = rf.clip2(a, b)
  }

  case object Excess extends PureOp {
    final val id = 43
    def make1(a: Float, b: Float): Float = rf.excess(a, b)
  }

  case object Fold2 extends PureOp {
    final val id = 44
    def make1(a: Float, b: Float): Float = rf.fold2(a, b)
  }

  case object Wrap2 extends PureOp {
    final val id = 45
    def make1(a: Float, b: Float): Float = rf.wrap2(a, b)
  }

  case object Firstarg extends ImpureOp {
    final val id = 46

    override val name = "firstArg"

    override def make(a: GE, b: GE): GE = (a, b) match {
      case (c(af), c(bf)) => c(make1(af, bf))
      case _              => Impure(this, a, b)
    }

    override protected[synth] def make1(a: UGenIn, b: UGenIn): UGenIn = (a, b) match {
      case (c(af), c(bf)) => c(make1(af, bf))
      case _              => UGenImpl(this, a, b, isIndividual = false, hasSideEffect = true)
    }

    def make1(a: Float, b: Float): Float = a
  }

  case object Rrand extends RandomOp {
    final val id = 47
    override val name = "rangeRand"
  }

  case object Exprand extends RandomOp {
    final val id = 48
    override val name = "expRand"
  }

  override def read(in: RefMapIn, key: String, arity: Int): BinaryOpUGen = {
    require (arity == 3)
    val _op = in.readProductT[Op]()
    val _a  = in.readGE()
    val _b  = in.readGE()
    apply(_op, _a, _b)
  }

  private final case class Pure(selector: Op, a: GE, b: GE)
    extends BinaryOpUGen

  private final case class Impure(selector: Op, a: GE, b: GE)
    extends BinaryOpUGen with HasSideEffect

  private final case class Random(selector: Op, a: GE, b: GE)
    extends BinaryOpUGen with UsesRandSeed

  private[this] def UGenImpl(selector: Op, a: UGenIn, b: UGenIn,
                             isIndividual: Boolean, hasSideEffect: Boolean): UGen.SingleOut =
      UGen.SingleOut("BinaryOpUGen", a.rate max b.rate, Vector(a, b),
        isIndividual = isIndividual, hasSideEffect = hasSideEffect, specialIndex = selector.id)
}

abstract class BinaryOpUGen extends UGenSource.SingleOut {
  def selector: BinaryOpUGen.Op
  def a: GE
  def b: GE

  override final def productPrefix = "BinaryOpUGen"

  final def rate: MaybeRate = MaybeRate.max_?( a.rate, b.rate )

  protected final def makeUGens: UGenInLike = unwrap(this, Vector(a.expand, b.expand))

  protected final def makeUGen(args: Vec[UGenIn]): UGenInLike = {
    val a0 = args(0)
    val a1 = args(1)
    selector.make1(a0, a1)
  }

  override def toString: String = if (selector.infix)
    s"($a ${selector.name} $b)"
  else
    s"$a.${selector.name}($b)"
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy