Search code examples
clojureovertone

Structuring Overtone project


I'm just getting started with Overtone, but I want to keep things somewhat organised from the start.

project.clj:

(defproject overtone-sketchbook "0.1.0-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [overtone "0.9.1"]])

src/overtone_sketchbook/synths.clj:

(ns overtone-sketchbook.synths
  (:use [overtone.live]))

(definst pluck-saw [f 800 d 3]
         (* (saw (+ 100 (* 200 (saw d))))
            (pluck (* (white-noise)
                      (env-gen (perc 0.001 2) :action FREE))
                   1 3 (/ 1 f))))

REPL session 1:

> lein repl
nREPL server started on port 52425
REPL-y 0.2.0
Clojure 1.5.1
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)

user=> (use 'overtone.live)
--> Loading Overtone...
  ********************************************************** 
       WARNING: JVM argument TieredStopAtLevel=1 is active, and may 
       lead to reduced performance. This happens to currently be the 
       default lein setting:

       https://github.com/technomancy/leiningen/pull/1230

       If you didn't intend this JVM arg to be specified, you can turn 
       it off in your project.clj file or your global 
       ~/.lein/profiles.clj file by adding the key-val

       :jvm-opts ^:replace [] 
       ********************************************************** 
--> Booting internal SuperCollider server...
Found 0 LADSPA plugins
*** ERROR: open directory failed '/Users/ilya/Library/Application Support/SuperCollider/synthdefs'
Number of Devices: 2
   0 : "Built-in Input"
   1 : "Built-in Output"

"Built-in Input" Input Device
   Streams: 1
      0  channels 2

"Built-in Output" Output Device
   Streams: 1
      0  channels 2

SC_AudioDriver: sample rate = 44100.000000, driver's block size = 512
--> Connecting to internal SuperCollider server...
--> Connection established

    _____                 __
   / __  /_  _____  _____/ /_____  ____  ___
  / / / / | / / _ \/ ___/ __/ __ \/ __ \/ _ \
 / /_/ /| |/ /  __/ /  / /_/ /_/ / / / /  __/
 \____/ |___/\___/_/   \__/\____/_/ /_/\___/

   Collaborative Programmable Music. v0.9.1


Hey Ilya, I feel something magical is only just beyond the horizon...

nil
user=> (demo overtone-sketchbook.synths/pluck-saw)

IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol  clojure.lang.RT.seqFrom (RT.java:505)
user=> (use 'overtone-sketchbook.synths)
nil
user=> (demo overtone-sketchbook.synths/pluck-saw)

IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol  clojure.lang.RT.seqFrom (RT.java:505)
user=> (demo pluck-saw)

IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol  clojure.lang.RT.seqFrom (RT.java:505)
user=> (require 'overtone-sketchbook.synths)
nil
user=> (demo pluck-saw)

IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol  clojure.lang.RT.seqFrom (RT.java:505)
user=> (demo overtone-sketchbook.synths/pluck-saw)

IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol  clojure.lang.RT.seqFrom (RT.java:505)
user=> Bye for now!

REPL session 2:

> lein repl
nREPL server started on port 52100
REPL-y 0.2.0
Clojure 1.5.1
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)

user=> (use 'overtone-sketchbook.synths)
--> Loading Overtone...
  ********************************************************** 
       WARNING: JVM argument TieredStopAtLevel=1 is active, and may 
       lead to reduced performance. This happens to currently be the 
       default lein setting:

       https://github.com/technomancy/leiningen/pull/1230

       If you didn't intend this JVM arg to be specified, you can turn 
       it off in your project.clj file or your global 
       ~/.lein/profiles.clj file by adding the key-val

       :jvm-opts ^:replace [] 
       ********************************************************** 
--> Booting internal SuperCollider server...
Found 0 LADSPA plugins
*** ERROR: open directory failed '/Users/ilya/Library/Application Support/SuperCollider/synthdefs'
Number of Devices: 2
   0 : "Built-in Input"
   1 : "Built-in Output"

"Built-in Input" Input Device
   Streams: 1
      0  channels 2

"Built-in Output" Output Device
   Streams: 1
      0  channels 2

SC_AudioDriver: sample rate = 44100.000000, driver's block size = 512
--> Connecting to internal SuperCollider server...
--> Connection established

    _____                 __
   / __  /_  _____  _____/ /_____  ____  ___
  / / / / | / / _ \/ ___/ __/ __ \/ __ \/ _ \
 / /_/ /| |/ /  __/ /  / /_/ /_/ / / / /  __/
 \____/ |___/\___/_/   \__/\____/_/ /_/\___/

   Collaborative Programmable Music. v0.9.1


Hello Ilya, may algorithmic beauty pour forth from your fingertips today.

nil
user=> pluck-saw
#overtone.studio.inst.Inst{:name "pluck-saw", :params ({:value #<Atom@24fc9cde: 800.0>, :name "f", :default 800.0, :rate :kr} {:value #<Atom@449ec5ca: 3.0>, :name "d", :default 3.0, :rate :kr}), :args ("f" "d"), :sdef {:name "overtone-sketchboo96d/pluck-saw", :constants [0.0 0 2.0 3.0 0.5 1.0 50.0 100.0 200.0 1 2 -99 -4 5 0.001], :params (800.0 3.0), :pnames ({:name "f", :index 0} {:name "d", :index 1}), :ugens ({:n-inputs 0, :args nil, :outputs ({:rate 1} {:rate 1}), :name "Control", :rate 1, :n-outputs 2, :rate-name :kr, :inputs (), :special 0, :id 280} #<sc-ugen: saw:ar [1]> #<sc-ugen: binary-op-u-gen:ar [2]> #<sc-ugen: binary-op-u-gen:ar [3]> #<sc-ugen: saw:ar [4]> #<sc-ugen: white-noise:ar [0]> #<sc-ugen: env-gen:kr [0]> #<sc-ugen: binary-op-u-gen:ar [2]> #<sc-ugen: binary-op-u-gen:kr [1]> #<sc-ugen: pluck:ar [5]> #<sc-ugen: binary-op-u-gen:ar [11]> #<sc-ugen: out:ar [12]>)}, :group #<synth-group[live]: Inst pluck-saw Container 31>, :instance-group #<synth-group[live]: Inst pluck-saw 32>, :fx-group #<synth-group[live]: Inst pluck-saw FX 33>, :mixer #<synth-node[live]: overtone.stu547/mono-inst-mixer 34>, :bus #<audio-bus: No Name, mono, id 50>, :fx-chain [], :volume #<Atom@6e9cebcc: 1.0>, :pan #<Atom@3cd6d0: 0.0>, :n-chans 1}
user=> (demo pluck-saw)

CompilerException java.lang.RuntimeException: Unable to resolve symbol: demo in this context, compiling:(NO_SOURCE_PATH:1:1) 
user=> (overtone.live/demo pluck-saw)

IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol  clojure.lang.RT.seqFrom (RT.java:505)
user=> Bye for now!

Am I missing something very basic?


Solution

  • firstly, it would help hugely if you could make your question much simpler and succinct so that it's easy to identify possible solutions.

    Also, thanks for giving Overtone a try - sorry that things aren't immediately clear. There's a lot going on here, so expect to ask a few questions on your way :-)

    However, looking at your two REPL excerpts it's clear that you don't yet fully understand three things:

    • How Overtone uses Clojure's namespaces
    • How synths are designed
    • How to trigger synths

    Let me briefly discuss each of these, and hopefully they'll shed some light on what's going on.

    Firstly, Overtone's use of Clojure's namespaces. When you use overtone.live what Overtone is doing is importing all of Overtone's public API functions into your current namespace. This means that once this operation is completed, all of the public API will be available to you - i.e. demo, defsynth, sin-osc etc.

    In your second REPL example, the first thing you try to do is: (demo overtone-sketchbook.synths/pluck-saw) which won't work because Clojure doesn't yet know what demo is and has no ideas about your namespace overtone-sketchbook.synth. For it to know about these, you need to either use or require the namespaces. For more information, read up about Clojure's ns macro.

    Secondly, Overtone's synths are designed by calling functions which represent parts of a synth (called ugens). Examples of ugens are saw, sin-osc, lpf. You can see a complete list of the available ugens in the Overtone cheatsheet: https://github.com/overtone/overtone/raw/master/docs/cheatsheet/overtone-cheat-sheet.pdf

    demo is a macro that allows you to pass in a (partial) synthdef and to play it:

    • (demo (sin-osc))
    • (demo (sin-osc 440))

    Note that ugens are functions in order to allow you to pass parameters to modify their behaviour (such as the 440 hz param to the sin-osc ugen).

    It's also important to note that ugens cannot (currently) be treated the same as synths. A ugen is a component of a synth, a synth is a tree of ugens. Which leads me on to the third issue - you were trying to demo the pluck-saw synth. This is a synth which has already been designed and may only be triggered. To trigger a synth, simply call it as a function:

    (pluck-saw)

    You can also pass params (if the design permits them) to the synth trigger function.

    So to summarise:

    • Use Clojure's ns macros load external namespaces, and pull in functions to make them available. For Overtone's default API you need to pull in either overtone.live or overtone.core (the first boots a server if one isn't already booted).
    • Synths are trees of ugens. Ugens are standard Clojure functions and return data-structures which are understood by the macros demo and defsynth. You can pass arguments to the ugen functions to specify their behaviour.
    • Synths are not ugens. Calling a ugen function returns a data structure which can be used in a synth design. Calling a synth as a function triggers (i.e. plays) that synth.

    Finally, do come and join us on the mailing list - we'd love to see what you're doing (and planning to do) with Overtone and share our passion with you:

    http://groups.google.com/group/overtone/