I'm trying to add in some microtonal functionality to overtone, but I'm having issues when it comes to dealing with the function midicps, which seems to be a requirement for dealing with midi input.
This is what I want:
(definst instrument [note 64 amp 0.5 gate 1]
(* amp (lpf (lf-tri (foo note) ) 1100)
(env-gen (adsr 0.1 0.2 2 2 0.3) gate :action FREE)))
foo is a function that takes a midi value and maps it to a value in hz (sort of like midicps), however, the above code doesn't run correctly.
For the sake of testing, I've defined foo as:
(defn foo [x] (if (= 64 x) 880 440))
Thus, (instrument 64)
should play a note an octave higher than any other foo, but it doesn't.
I believe what is happening is that when I run (instrument x)
, it passes something else (a midi event?) to foo instead of the integer itself.
Any time I try running midi->hz
over midicps
, I get the error:
CompilerException java.lang.ClassCastException: overtone.sc.machinery.ugen.sc_ugen.SCUGen
cannot be cast to java.lang.Number, compiling:(form-init7628662755818333410.clj:1:1)
Calling midi->hz on note without midicps yields the same error.
How do I convert this ugen (or midi event) to an integer representing the midi note?
Note: I'm using midi-poly-player
to add the midi handler for my instrument. I can get my pitch mapping function to work if I just make a regular note-on event handler, but I would much rather use midi-poly-player
so I don't have to re-implement all of it's functionality.
You're mixing scsynth ugens and Clojure code when you call the foo function like this. Inside the definst, it is a macro taking that Clojure code and converting it to supercollider synthesizer code. I don't actually understand why the code behaves like it does, but I think I can show you how to make it do what you want. Just keep the code within the definst macro.
Here is the midi->hz function in Clojure. midicps does this as a ugen in supercollider for you:
(defn midi->hz
"Convert a midi note number to a frequency in hz."
[note]
(* 440.0 (java.lang.Math/pow 2.0 (/ (- note 69.0) 12.0))))
You can just inline that note->frequency conversion in your definst:
(definst instrument2 [note 64 amp 0.5 gate 1]
(let [freq (* 440.0 (pow 2.0 (/ (- note 69.0) 12.0)))]
(* amp (lpf (lf-tri freq) 1100)
(env-gen (adsr 0.1 0.2 2 2 0.3) gate :action FREE))))
and listen to the standard 12 notes per scale output like:
(def x (instrument2 69))
(ctl x :gate 0)
(def x (instrument2 70))
(ctl x :gate 0)
(def x (instrument2 71))
(ctl x :gate 0)
to adjust for more tones-per-octave, for example, this gives 24...
(definst instrument3 [note 64 amp 0.5 gate 1]
(let [freq (* 440.0 (pow 2.0 (/ (- note 69.0) 24.0)))]
(* amp (lpf (lf-tri freq) 1100)
(env-gen (adsr 0.1 0.2 2 2 0.3) gate :action FREE))))
(def x (instrument3 69))
(ctl x :gate 0)
(def x (instrument3 70))
(ctl x :gate 0)
(def x (instrument3 71))
(ctl x :gate 0)
I hear a series of smaller steps. I hope you do, too.
Cheers,
Roger