├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENCE.txt ├── README.md ├── build.sh ├── package.json ├── project.clj ├── resources └── public │ └── index.html ├── src └── cljs_bach │ └── synthesis.cljs └── system.properties /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | profiles.clj 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | /.lein-* 10 | /.nrepl-port 11 | /resources/public/js 12 | /out 13 | /.repl 14 | *.log 15 | /.env 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: clojure 2 | script: lein cljsbuild once dev 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | 0.3.0 5 | ----- 6 | 7 | * Document `sample`. 8 | * Fixed ADSR duration (thanks @esp1!). 9 | * Fixed raw buffer range (thanks @0x2493). 10 | 11 | 0.2.0 12 | ----- 13 | 14 | * Memoize `buffer`, so that e.g. `reverb` and `white-noise` don't repeatedly calculate their bits. 15 | 16 | 0.1.0 17 | ----- 18 | 19 | * First release, with all the major parts of the Web Audio API in place. 20 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | Open Source Initiative OSI - The MIT License:Licensing 2 | 3 | The MIT License 4 | 5 | Copyright (c) 2015, Chris Ford (christophertford at gmail) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CLJS Bach 2 | ========= 3 | 4 | A Clojurescript wrapper for the Web Audio API, extracted from [Klangmeister](http://ctford.github.io/klangmeister/). 5 | 6 | [](https://travis-ci.org/ctford/cljs-bach) 7 | 8 | Importing it into your project 9 | ------------------------------ 10 | 11 | CLJS Bach is a Clojurescript library. To include it in your Clojurescript project, you need to 12 | include the following in your `project.clj` or `build.boot`. 13 | 14 | [](http://clojars.org/cljs-bach) 15 | 16 | Once you've done that, you can use it like any other library. 17 | 18 | Usage 19 | ----- 20 | 21 | Firstly, create an audio context. You only need one, and if you keep creating them the browser will run out and error on you. 22 | 23 | (defonce context (audio-context)) 24 | 25 | See [Klangmeister](http://ctford.github.io/klangmeister/) for examples of how to build synthesisers. Here's a simple 26 | one. Note the use of `connect->` to join together simple parts together. 27 | 28 | (defn ping [freq] 29 | (connect-> 30 | (square freq) ; Try a sawtooth wave. 31 | (percussive 0.01 0.4) ; Try varying the attack and decay. 32 | (gain 0.1))) ; Try a bigger gain. 33 | 34 | Once you have a synthesiser, connect it to `destination` and use `run-with` to give it an audio context, a time to run at 35 | and a duration. 36 | 37 | ; Play the ping synthesiser now, at 440 hertz. 38 | (-> (ping 440) 39 | (connect-> destination) 40 | (run-with context (current-time context) 1.0))) 41 | 42 | If you forget to connect a synthesiser to `destination`, then you'll here no sound, because nothing will be sent to the speakers. 43 | 44 | Vanilla javascript usage 45 | ------------------------ 46 | 47 | See `resources/public/index.html`. To try it out, run `lein figwheel` and then navigate to `http://localhost:3449/`. 48 | 49 | API 50 | --- 51 | 52 | ### Machinery 53 | 54 | * `(audio-context)` - returns the browser's Audio Context. 55 | * `(current-time context)` - returns the current time according to the supplied audio context. 56 | * `(run-with node context at duration)` - runs the synthesiser in the supplied context. 57 | * `destination` - a node representing the browser's speakers. 58 | 59 | ### Oscillators 60 | 61 | * `(sawtooth frequency)` - a sawtooth wave oscillating at `frequency` hertz. 62 | * `(sine frequency)` - a sine wave oscillating at `frequency` hertz. 63 | * `(square frequency)` - a square wave oscillating at `frequency` hertz. 64 | * `(triangle frequency)` - a triangle wave oscillating at `frequency` hertz. 65 | * `white-noise` - a node emitting random noise. 66 | 67 | ### Modifiers 68 | 69 | * `(adsr attack decay sustain release)` - an envelope for shaping a note. 70 | * `(gain level)` - a node that multiplies its input by `level`. 71 | * `(high-pass cutoff)` - filter out frequencies below `cutoff`. 72 | * `(low-pass cutoff)` - filter out frequencies above `cutoff`. 73 | * `(percussive attack decay)` - a simple envelope for shaping a note. 74 | 75 | ### Effects 76 | 77 | * `(stereo-panner pan)` - pan the signal left (-1) or right (1). 78 | * `(delay-line seconds)` - delay the signal by `seconds`. 79 | * `reverb` - apply reverb to the signal. 80 | 81 | ### Combinators 82 | 83 | * `(connect-> node1 node2)` - connect `node1`'s output to `node2`'s input. 84 | * `(add node1 node2)` - add together the outputs of `node1` and `node2`. 85 | 86 | ### Samples 87 | 88 | * `(sample uri)` - load a sample from a URI, which can then be used as a source node in the same way that oscillators can. The asynchronicity can make this a little glitchy. 89 | 90 | Getting a REPL 91 | -------------- 92 | 93 | CLJS Bach relies on the Web Audio API, so it will only work if your javascript environment supports that. Fortunately, all 94 | major browsers except Internet Explorer do (and it will on next major release). 95 | 96 | If you have this project checked out locally, you can use the [Figwheel](https://github.com/bhauman/lein-figwheel) REPL. Firstly, 97 | start Figwheel. 98 | 99 | lein figwheel 100 | 101 | Figwheel will tell you where's running, so you can open it in your browser. 102 | 103 | Figwheel: Starting server at http://localhost:3449 104 | .... 105 | Prompt will show when Figwheel connects to your application 106 | 107 | Once you've done that, the REPL will come to life. 108 | 109 | To quit, type: :cljs/quit 110 | cljs.user=> 111 | 112 | From then on, you can type commands into the REPL, and they'll be executed by your browser. 113 | 114 | Design 115 | ------ 116 | 117 | CLJS Bach is purely functional. To make synthesiser composition easier, synthesisers are actually functions that take 118 | an audio context, a time and a duration and return the synthesis graph. 119 | 120 | (defn some-synth [...] 121 | (fn [context at duration] 122 | ; Create a graph of synthesis nodes. 123 | )) 124 | 125 | That's why you need `run-with` to make sound actually happen. 126 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | lein clean && lein cljsbuild once prod 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bach.js", 3 | "version": "0.3.0-pre1", 4 | "description": "A modular synthesis library. Nothing works yet.", 5 | "main": "resources/public/js/compiled/bach.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/ctford/cljs-bach.git" 15 | }, 16 | "keywords": [ 17 | "synthesis", 18 | "web audio" 19 | ], 20 | "author": "Chris Ford", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/ctford/cljs-bach/issues" 24 | }, 25 | "homepage": "https://github.com/ctford/cljs-bach#readme" 26 | } 27 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject cljs-bach "0.4.0-SNAPSHOT" 2 | :description "A Clojurescript wrapper for the Web Audio API." 3 | :license {:name "MIT" } 4 | :dependencies [[org.clojure/clojure "1.7.0"] 5 | [org.clojure/clojurescript "1.7.228"] 6 | [cljs-ajax "0.5.5"]] 7 | 8 | :min-lein-version "2.5.0" 9 | 10 | :plugins [[lein-cljsbuild "1.1.2"] 11 | [lein-figwheel "0.5.0-2"]] 12 | 13 | :clean-targets ^{:protect false} ["resources/public/js/compiled" 14 | "target" 15 | "out"] 16 | 17 | :source-paths ["src"] 18 | :resource-paths ["resources" "target/cljsbuild"] 19 | 20 | :cljsbuild {:builds [{:id "dev" 21 | :source-paths ["src"] 22 | :figwheel true 23 | :compiler {:main "cljs_bach.synthesis" 24 | :optimizations :none 25 | :pretty-print true 26 | :output-to "resources/public/js/compiled/bach.js" 27 | :output-dir "resources/public/js/compiled" 28 | :asset-path "js/compiled"}} 29 | 30 | {:id "prod" 31 | :source-paths ["src"] 32 | :compiler {:main "cljs_bach.synthesis" 33 | :static-fns true 34 | :optimizations :advanced 35 | :pretty-print false 36 | :optimize-constants true 37 | :output-to "resources/public/js/compiled/bach.js" 38 | :asset-path "js/compiled"}}]}) 39 | -------------------------------------------------------------------------------- /resources/public/index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 |