3

I'm trying to make music with the overtone library of clojure. To produce interesting sounds, additive synthesis is useful, that means that I take sinus oscillators of several frequencies and simply add them. In overtone, to create a synthesizer, which realizes that, I can write:

(definst myinst [freq 100]
  (* 0.2 (reduce + (map sin-osc [101.0 100 99.0]))))

To be a bit more reusable, I write a function, which takes a frequency and returns a list with all these frequencies, you can see in the code snippet:

(defn split-freq [freq]
  (apply vector (map #(* freq %) [1.01 1 0.99])))

When executing (split-freq 100), the REPL gives me the following:

[101.0 100 99.0]

which is exactly the same as the input, I provided the map function above. In fact, I copied the result. Now, I try this:

(definst myinst [freq 100]
  (* 0.2 (reduce + (map sin-osc (split-freq freq)))))

Unfortunately, the REPL tells me, that I'm doing wrong:

CompilerException java.lang.ClassCastException: 
overtone.sc.machinery.ugen.sc_ugen.ControlProxy cannot be cast to java.lang.Number, 
compiling:(form-init1807789563491679853.clj:1)

But this code works fine:

(definst myinst [freq 100]
  (* 0.2 (reduce + (map sin-osc (apply vector (map #(* freq %) [1.01 1 0.99]))))))

although, I simply put in the function definition.

I think I have a principal lack of understanding. I thought, that if one of those version work, the others must work, too.

My questions are: Why does my 'improved' version does not work? Why does the first snippet work? How can I circumvent this behavior?

Clojure 1.3.0
Overtone 0.8
(use 'overtone.core)
spiehr
  • 411
  • 2
  • 10
  • 3
    Try macroexpanding the call to definst. Likely definst is a complex macro that is interpreting this that are normally functions (like `+`) into synth definitions that sum a group of signals, and your new definst form breaks the macro. Clojure code cannot run in the sc synth process, so your forms need to be translated into sc synth definitions and cannot actually be true clojure code. – noisesmith Sep 23 '13 at 04:10

1 Answers1

1

ups, you are right, i haven't understand the noisesmith comment. It's true that you can't use this value "freq" in your function directly because this value is not bound at the time that your function need it (before the macro execution)

So here is a workaround to this problem, also using the let form, it is not so elegant as to make your own macro but works

(defn myinst [inst-name]
  (let [the-freq 100]
   (definst inst-name [freq the-freq] (* 0.2 (reduce + (map sin-osc (split-freq the-freq)))))))

and you can execute as a normal function

((myinst "my-instrument"))

I hope that helps you

Juan


Previous comment, now not valid response

maybe you are making a mistake with your argument value declaration in your improved version

That's your code

(definst myinst [freq 100]
  (* 0.2 (reduce + (map sin-osc (split-freq freq)))))

And, as in clojure you can't assign default value to argument function, your improved version would work if, by example, you use a let form to assign the default value

(definst myinst []
  (let [freq 100]
    (* 0.2 (reduce + (map sin-osc (split-freq freq))))))  
tangrammer
  • 3,041
  • 17
  • 24
  • thanks, first. definst is a macro, as noisesmith pointed out. It expects an even number of arguments, each second represents a default value. Interestingly, your version compiles, but I'm not able any more to call `myinst` with an argument. I follow, definst turns its arguments into these strange types I mentioned above. I need to convert them... – spiehr Sep 24 '13 at 09:48
  • hmm. not really. I want to 'instantiate' one instrument (definst) and use it with different frequencies. If I understand your solution correctly, I would need to instantiate one instrument for each frequency. I should clarify this in my question. Also, I think this question is more related to overtone than to clojure. I appreciate your effort! – spiehr Sep 24 '13 at 15:29