overtone

diego.videco 2026-01-13T18:22:30.127289Z

Does anyone understand why this fails?

(o/defsynth test-pan-az
    [freq 200
     amp 0.5
     out 0]
    (o/out out (-> 
                   (o/pan-az  4
                              (o/sin-osc 200)   
                              1)
                   (* amp (o/env-gen (o/env-perc) :action o/FREE)))))
With the following error?
Error in checker for ugen ==> pan-az Input with index 1 must be an input
   stream i.e. a ugen at :kr or :ar Supplied args: (4 #
   1) Expected arg keys: (:num-channels :in :pos :level :width :orientation)
   Merged args: {:num-channels 4, :in #, :pos 1, :level
   1.0, :width 2.0, :orientation 0.5}
In .machinery.ugen.metadata.pan pan-az is defined like:
{:name "PanAz",
    :args [{:name "num-channels"
            :mode :num-outs,
            :doc "number of output channels"}

           {:name "in",
            :doc "input signal"}

           {:name "pos",
            :default 0.0,
            :doc "pan position. Channels are evenly spaced over a
                   cyclic period of 2.0 with 0.0 equal to the position
                   directly in front, 2.0/numChans a clockwise shift
                   1/numChans of the way around the ring, 4.0/numChans
                   equal to a shift of 2/numChans, etc. Thus all
                   channels will be cyclically panned through if a
                   sawtooth wave from -1 to +1 is used to modulate the
                   pos. N.B. Front may or may not correspond to a
                   speaker depending on the setting of the orientation
                   arg, see below." }

           {:name "level",
            :default 1.0,
            :doc "a control rate level input."}

           {:name "width",
            :default 2.0,
            :doc "The width of the panning envelope. Nominally this is
                   2.0 which pans between pairs of adjacent
                   speakers. Width values greater than two will spread
                   the pan over greater numbers of speakers. Width
                   values less than one will leave silent gaps between
                   speakers." }

           {:name "orientation",
            :default 0.5,
            :doc "Should be zero if the front is a vertex of the
                   polygon. The first speaker will be directly in
                   front. Should be 0.5 if the front bisects a side of
                   the polygon. Then the first speaker will be the one
                   left of center. Default is 0.5." }]
    :check (nth-input-stream? 1)
    :doc "Multichannel equal power panner."}
The check should apply to the second arg, which in theory should be the in signal, but it doesn’t. Actually, this compiles, which shouldn’t:
(o/defsynth test-pan-az
    [freq 200
     amp 0.5
     out 0]
    (o/out out (-> (o/pan-az  4
                              200
                              (o/saw 1))
                   (* amp (o/env-gen (o/env-perc) :action o/FREE)))))
My theory is that because num-channels is a compile time value, that the checker is not accounting for it. One possible fix would be to check for the 0 index, but that just feels wrong from the code perspective, though it’s an easy change. ==== UPDATE: ==== Just confirmed that functions created with the defchecker macro don’t receive the :num-channels, or rather, those args defined with :mode :num-outs as an argument to check. This clearly affects ohter ugens, and there are inconsistencies with the usage of nth-input-stream?, see for example GrainIn, where the offset is accounted for in the call itself:
{:name "GrainIn"
    :args [{:name "num-channels"
            :mode :num-outs
            :default 1
            :doc "the number of channels to output. If 1, mono is
                  returned and pan is ignored." }

           {:name "trigger"
            :default 0
            :doc "a kr or ar trigger to start a new grain. If ar, grains
                  after the start of the synth are sample accurate." }

           {:name "dur"
            :default 1
            :doc "size of the grain."}

           {:name "in"
            :default :none
            :doc "the input to granulate"}

           {:name "pan"
            :default 0
            :doc "Determines where to pan the output. If num-channels =
                  1, no panning is done; if num-channels = 2, panning is
                  similar to Pan2; if num-channels > 2, pannins is the
                  same as PanAz." }

           {:name "envbufnum"
            :default -1
            :doc "the buffer number containing a singal to use for the
                  grain envelope. -1 uses a built-in Hanning envelope."                  }

           {:name "max-grains"
            :default 512
            :doc "the maximum number of overlapping grains that can be
                  used at a given time. This value is set at the UGens
                  init time and can't be modified. This can be set lower
                  for more efficient use of memory." }]
    :rates #{:ar}
    :check (nth-input-stream? 2)
    :doc "Granulate an input signal"}
Perhaps in order to keep changes in the code base simple, it may be a good idea to follow the GrainIn solution, as I don’t know if providing the :mode :num-outs value in the args list could break other stuff. In some instances such as with BufWr there is the following solution: :check (when-ar (nth-input-ar 1 "Phase must be audio rate. The following message is offset by 1 due to first arg being special.")) ;; first arg is in mode append-sequence. Anyways there’s more than one UGen with positional problems. Wonder what you all think?