├── .gitignore ├── LICENSE ├── README.md ├── example ├── .gitignore ├── LICENSE ├── README.md ├── project.clj └── src │ └── example │ └── core.clj ├── project.clj ├── src └── disclojure │ ├── inst.clj │ ├── kit.clj │ ├── live.clj │ ├── melody.clj │ ├── midi.clj │ ├── play.clj │ ├── sampler.clj │ └── track.clj └── test ├── disclojure ├── live_test.clj ├── melody_test.clj ├── midi_test.clj └── sampler_test.clj ├── piano.mid └── snake-pluck.mid /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ 12 | .idea 13 | *.iml 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 7 | "Contribution" means: 8 | 9 | a) in the case of the initial Contributor, the initial code and 10 | documentation distributed under this Agreement, and 11 | 12 | b) in the case of each subsequent Contributor: 13 | 14 | i) changes to the Program, and 15 | 16 | ii) additions to the Program; 17 | 18 | where such changes and/or additions to the Program originate from and are 19 | distributed by that particular Contributor. A Contribution 'originates' from 20 | a Contributor if it was added to the Program by such Contributor itself or 21 | anyone acting on such Contributor's behalf. Contributions do not include 22 | additions to the Program which: (i) are separate modules of software 23 | distributed in conjunction with the Program under their own license 24 | agreement, and (ii) are not derivative works of the Program. 25 | 26 | "Contributor" means any person or entity that distributes the Program. 27 | 28 | "Licensed Patents" mean patent claims licensable by a Contributor which are 29 | necessarily infringed by the use or sale of its Contribution alone or when 30 | combined with the Program. 31 | 32 | "Program" means the Contributions distributed in accordance with this 33 | Agreement. 34 | 35 | "Recipient" means anyone who receives the Program under this Agreement, 36 | including all Contributors. 37 | 38 | 2. GRANT OF RIGHTS 39 | 40 | a) Subject to the terms of this Agreement, each Contributor hereby grants 41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 42 | reproduce, prepare derivative works of, publicly display, publicly perform, 43 | distribute and sublicense the Contribution of such Contributor, if any, and 44 | such derivative works, in source code and object code form. 45 | 46 | b) Subject to the terms of this Agreement, each Contributor hereby grants 47 | Recipient a non-exclusive, worldwide, royalty-free patent license under 48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 49 | transfer the Contribution of such Contributor, if any, in source code and 50 | object code form. This patent license shall apply to the combination of the 51 | Contribution and the Program if, at the time the Contribution is added by the 52 | Contributor, such addition of the Contribution causes such combination to be 53 | covered by the Licensed Patents. The patent license shall not apply to any 54 | other combinations which include the Contribution. No hardware per se is 55 | licensed hereunder. 56 | 57 | c) Recipient understands that although each Contributor grants the licenses 58 | to its Contributions set forth herein, no assurances are provided by any 59 | Contributor that the Program does not infringe the patent or other 60 | intellectual property rights of any other entity. Each Contributor disclaims 61 | any liability to Recipient for claims brought by any other entity based on 62 | infringement of intellectual property rights or otherwise. As a condition to 63 | exercising the rights and licenses granted hereunder, each Recipient hereby 64 | assumes sole responsibility to secure any other intellectual property rights 65 | needed, if any. For example, if a third party patent license is required to 66 | allow Recipient to distribute the Program, it is Recipient's responsibility 67 | to acquire that license before distributing the Program. 68 | 69 | d) Each Contributor represents that to its knowledge it has sufficient 70 | copyright rights in its Contribution, if any, to grant the copyright license 71 | set forth in this Agreement. 72 | 73 | 3. REQUIREMENTS 74 | 75 | A Contributor may choose to distribute the Program in object code form under 76 | its own license agreement, provided that: 77 | 78 | a) it complies with the terms and conditions of this Agreement; and 79 | 80 | b) its license agreement: 81 | 82 | i) effectively disclaims on behalf of all Contributors all warranties and 83 | conditions, express and implied, including warranties or conditions of title 84 | and non-infringement, and implied warranties or conditions of merchantability 85 | and fitness for a particular purpose; 86 | 87 | ii) effectively excludes on behalf of all Contributors all liability for 88 | damages, including direct, indirect, special, incidental and consequential 89 | damages, such as lost profits; 90 | 91 | iii) states that any provisions which differ from this Agreement are offered 92 | by that Contributor alone and not by any other party; and 93 | 94 | iv) states that source code for the Program is available from such 95 | Contributor, and informs licensees how to obtain it in a reasonable manner on 96 | or through a medium customarily used for software exchange. 97 | 98 | When the Program is made available in source code form: 99 | 100 | a) it must be made available under this Agreement; and 101 | 102 | b) a copy of this Agreement must be included with each copy of the Program. 103 | 104 | Contributors may not remove or alter any copyright notices contained within 105 | the Program. 106 | 107 | Each Contributor must identify itself as the originator of its Contribution, 108 | if any, in a manner that reasonably allows subsequent Recipients to identify 109 | the originator of the Contribution. 110 | 111 | 4. COMMERCIAL DISTRIBUTION 112 | 113 | Commercial distributors of software may accept certain responsibilities with 114 | respect to end users, business partners and the like. While this license is 115 | intended to facilitate the commercial use of the Program, the Contributor who 116 | includes the Program in a commercial product offering should do so in a 117 | manner which does not create potential liability for other Contributors. 118 | Therefore, if a Contributor includes the Program in a commercial product 119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend 120 | and indemnify every other Contributor ("Indemnified Contributor") against any 121 | losses, damages and costs (collectively "Losses") arising from claims, 122 | lawsuits and other legal actions brought by a third party against the 123 | Indemnified Contributor to the extent caused by the acts or omissions of such 124 | Commercial Contributor in connection with its distribution of the Program in 125 | a commercial product offering. The obligations in this section do not apply 126 | to any claims or Losses relating to any actual or alleged intellectual 127 | property infringement. In order to qualify, an Indemnified Contributor must: 128 | a) promptly notify the Commercial Contributor in writing of such claim, and 129 | b) allow the Commercial Contributor tocontrol, and cooperate with the 130 | Commercial Contributor in, the defense and any related settlement 131 | negotiations. The Indemnified Contributor may participate in any such claim 132 | at its own expense. 133 | 134 | For example, a Contributor might include the Program in a commercial product 135 | offering, Product X. That Contributor is then a Commercial Contributor. If 136 | that Commercial Contributor then makes performance claims, or offers 137 | warranties related to Product X, those performance claims and warranties are 138 | such Commercial Contributor's responsibility alone. Under this section, the 139 | Commercial Contributor would have to defend claims against the other 140 | Contributors related to those performance claims and warranties, and if a 141 | court requires any other Contributor to pay any damages as a result, the 142 | Commercial Contributor must pay those damages. 143 | 144 | 5. NO WARRANTY 145 | 146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON 147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR 149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A 150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the 151 | appropriateness of using and distributing the Program and assumes all risks 152 | associated with its exercise of rights under this Agreement , including but 153 | not limited to the risks and costs of program errors, compliance with 154 | applicable laws, damage to or loss of data, programs or equipment, and 155 | unavailability or interruption of operations. 156 | 157 | 6. DISCLAIMER OF LIABILITY 158 | 159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 166 | OF SUCH DAMAGES. 167 | 168 | 7. GENERAL 169 | 170 | If any provision of this Agreement is invalid or unenforceable under 171 | applicable law, it shall not affect the validity or enforceability of the 172 | remainder of the terms of this Agreement, and without further action by the 173 | parties hereto, such provision shall be reformed to the minimum extent 174 | necessary to make such provision valid and enforceable. 175 | 176 | If Recipient institutes patent litigation against any entity (including a 177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 178 | (excluding combinations of the Program with other software or hardware) 179 | infringes such Recipient's patent(s), then such Recipient's rights granted 180 | under Section 2(b) shall terminate as of the date such litigation is filed. 181 | 182 | All Recipient's rights under this Agreement shall terminate if it fails to 183 | comply with any of the material terms or conditions of this Agreement and 184 | does not cure such failure in a reasonable period of time after becoming 185 | aware of such noncompliance. If all Recipient's rights under this Agreement 186 | terminate, Recipient agrees to cease use and distribution of the Program as 187 | soon as reasonably practicable. However, Recipient's obligations under this 188 | Agreement and any licenses granted by Recipient relating to the Program shall 189 | continue and survive. 190 | 191 | Everyone is permitted to copy and distribute copies of this Agreement, but in 192 | order to avoid inconsistency the Agreement is copyrighted and may only be 193 | modified in the following manner. The Agreement Steward reserves the right to 194 | publish new versions (including revisions) of this Agreement from time to 195 | time. No one other than the Agreement Steward has the right to modify this 196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 197 | Eclipse Foundation may assign the responsibility to serve as the Agreement 198 | Steward to a suitable separate entity. Each new version of the Agreement will 199 | be given a distinguishing version number. The Program (including 200 | Contributions) may always be distributed subject to the version of the 201 | Agreement under which it was received. In addition, after a new version of 202 | the Agreement is published, Contributor may elect to distribute the Program 203 | (including its Contributions) under the new version. Except as expressly 204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 205 | licenses to the intellectual property of any Contributor under this 206 | Agreement, whether expressly, by implication, estoppel or otherwise. All 207 | rights in the Program not expressly granted under this Agreement are 208 | reserved. 209 | 210 | This Agreement is governed by the laws of the State of New York and the 211 | intellectual property laws of the United States of America. No party to this 212 | Agreement will bring a legal action under this Agreement more than one year 213 | after the cause of action arose. Each party waives its rights to a jury trial 214 | in any resulting litigation. 215 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Disclojure 2 | 3 | A live coding environment for [Overtone](https://github.com/overtone/overtone) and [Leipzig](https://github.com/ctford/leipzig). 4 | 5 | ```clojure 6 | [pjagielski/disclojure "0.1.4"] 7 | ``` 8 | 9 | ## Usage 10 | 11 | Create a melody using Leipzig: 12 | ```clojure 13 | (require '[repl :refer :all]) 14 | (require '[leipzig.melody :refer :all]) 15 | (require '[leipzig.scale :as scale]) 16 | (def melody 17 | (->> (phrase [2 1/2 1/2 1/2 2.5 1/2 1/2 1/2 2.5 1/2 1/2 1/2 2.5 1 1] 18 | [0 -1 0 2 -3 -4 -3 -1 -5 -6 -5 -3 -7 -6 -5]) 19 | (where :pitch (comp scale/G scale/minor)) 20 | (all :part :plucky) 21 | (all :amp 1))) 22 | ``` 23 | 24 | Initialize the track state: 25 | ```clojure 26 | (require '[disclojure.live :as l]) 27 | (def initial-track 28 | {:plucky (times 2 melody)}) 29 | 30 | (defonce state 31 | (l/reset-track initial-track)) 32 | ``` 33 | 34 | Loop your track forever and change on the fly: 35 | ```clojure 36 | (require '[leipzig.live :as live]) 37 | (live/jam (l/track)) 38 | (l/assoc-track :plucky (times 2 melody)) 39 | (def last-frame (fn [e] (>= (:time e) 12))) 40 | (def remove-last-frame (partial remove last-frame)) 41 | (l/update-track :plucky remove-last-frame) 42 | ``` 43 | 44 | ## Features 45 | 46 | ### Drum kits 47 | 48 | ```clojure 49 | (require '[disclojure.kit :as kit]) 50 | ``` 51 | 52 | Load drum kit from directory: 53 | ```clojure 54 | (kit/load-kit! (io/file "work/beats/big_room")) 55 | ``` 56 | 57 | Play a drum sound from a kit: 58 | ```clojure 59 | (leipzig.live/play-note {:part :beat :drum :kick :amp 1}) 60 | ``` 61 | 62 | This should play `kick.wav` from drum kit. See "Track helpers" for details of creating drum patterns. 63 | 64 | ### Sampler 65 | ```clojure 66 | (require '[disclojure.sampler :as samples]) 67 | ``` 68 | 69 | Load samples from directory: 70 | ```clojure 71 | (samples/load-samples! (io/file "work/samples")) 72 | ``` 73 | 74 | Play a sample: 75 | ```clojure 76 | (leipzig.live/play-note {:part :sampler :sample :loop :bpm 130}) 77 | ``` 78 | 79 | There is a convention of naming samples adopted from Sonic-Pi. Basically, you can include BPM and number of beats in a sample name. 80 | So a file named `124_4_worm.wav` would be mapped to following structure: 81 | ```clojure 82 | {:bpm 124, :beats 4, :name :worm, :sound #> 96 | (reduce with 97 | [(tap :fat-kick (range 8) 8) 98 | (tap :kick (range 8) 8) 99 | (tap :snare (range 1 8 2) 8) 100 | (tap :close-hat (sort (concat [3.75 7.75] (range 1/2 8 1))) 8) 101 | (let [horns [0 1/2 5/4 2]] 102 | (tap :horn (concat horns 103 | (map (partial + 4) horns)) 8)) ]) 104 | (all :part :beat))) 105 | ``` 106 | 107 | Basically, `tap` takes a drum name and a collection of beat times to create a drum pattern. `(range 8)` will play every beat (kick), 108 | `(range 1 8 2)` - every even beat (snare) and so on. 109 | 110 | Sequencing samples with `sampler`: 111 | 112 | ```clojure 113 | (track/sampler [[0 :apache 16 2] 114 | [(range 0 16 4) :funky-drummer 4 2] 115 | [(range 0 16 4) :hotpants 4 2]]) 116 | ``` 117 | 118 | The `sampler` function takes a vector of sample pattern with following structure: 119 | `[times sample_name beats amp ]` 120 | * `times` could be a beat or collection of beats where the sample should play 121 | * `sample_name` is a name created by `load-samples!` 122 | * `beats` is the number of beats to play 123 | * `amp` is the amplitude of the sample 124 | * optional `start_beat` allows to play a part of sample from specific point 125 | 126 | ### Custom instruments 127 | 128 | Define an Overtone synth and map it to Leipzig `live/play-note`: 129 | 130 | ```clojure 131 | (require '[overtone.live :refer :all]) 132 | (defsynth da-funk ...) 133 | (defmethod live/play-note :da-funk [{hertz :pitch seconds :duration amp :amp cutoff :cutoff}] 134 | (when hertz 135 | (da-funk :freq hertz :dur seconds :amp (or amp 1) :cutoff (or cutoff 1500)))) 136 | ``` 137 | 138 | ## License 139 | 140 | Copyright © 2016-2019 Piotr Jagielski 141 | 142 | The project name refers to [Disclosure](https://www.youtube.com/watch?v=W_vM8ePGuRM) 143 | 144 | Distributed under the Eclipse Public License either version 1.0 or (at 145 | your option) any later version. 146 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ 12 | -------------------------------------------------------------------------------- /example/LICENSE: -------------------------------------------------------------------------------- 1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 7 | "Contribution" means: 8 | 9 | a) in the case of the initial Contributor, the initial code and 10 | documentation distributed under this Agreement, and 11 | 12 | b) in the case of each subsequent Contributor: 13 | 14 | i) changes to the Program, and 15 | 16 | ii) additions to the Program; 17 | 18 | where such changes and/or additions to the Program originate from and are 19 | distributed by that particular Contributor. A Contribution 'originates' from 20 | a Contributor if it was added to the Program by such Contributor itself or 21 | anyone acting on such Contributor's behalf. Contributions do not include 22 | additions to the Program which: (i) are separate modules of software 23 | distributed in conjunction with the Program under their own license 24 | agreement, and (ii) are not derivative works of the Program. 25 | 26 | "Contributor" means any person or entity that distributes the Program. 27 | 28 | "Licensed Patents" mean patent claims licensable by a Contributor which are 29 | necessarily infringed by the use or sale of its Contribution alone or when 30 | combined with the Program. 31 | 32 | "Program" means the Contributions distributed in accordance with this 33 | Agreement. 34 | 35 | "Recipient" means anyone who receives the Program under this Agreement, 36 | including all Contributors. 37 | 38 | 2. GRANT OF RIGHTS 39 | 40 | a) Subject to the terms of this Agreement, each Contributor hereby grants 41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 42 | reproduce, prepare derivative works of, publicly display, publicly perform, 43 | distribute and sublicense the Contribution of such Contributor, if any, and 44 | such derivative works, in source code and object code form. 45 | 46 | b) Subject to the terms of this Agreement, each Contributor hereby grants 47 | Recipient a non-exclusive, worldwide, royalty-free patent license under 48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 49 | transfer the Contribution of such Contributor, if any, in source code and 50 | object code form. This patent license shall apply to the combination of the 51 | Contribution and the Program if, at the time the Contribution is added by the 52 | Contributor, such addition of the Contribution causes such combination to be 53 | covered by the Licensed Patents. The patent license shall not apply to any 54 | other combinations which include the Contribution. No hardware per se is 55 | licensed hereunder. 56 | 57 | c) Recipient understands that although each Contributor grants the licenses 58 | to its Contributions set forth herein, no assurances are provided by any 59 | Contributor that the Program does not infringe the patent or other 60 | intellectual property rights of any other entity. Each Contributor disclaims 61 | any liability to Recipient for claims brought by any other entity based on 62 | infringement of intellectual property rights or otherwise. As a condition to 63 | exercising the rights and licenses granted hereunder, each Recipient hereby 64 | assumes sole responsibility to secure any other intellectual property rights 65 | needed, if any. For example, if a third party patent license is required to 66 | allow Recipient to distribute the Program, it is Recipient's responsibility 67 | to acquire that license before distributing the Program. 68 | 69 | d) Each Contributor represents that to its knowledge it has sufficient 70 | copyright rights in its Contribution, if any, to grant the copyright license 71 | set forth in this Agreement. 72 | 73 | 3. REQUIREMENTS 74 | 75 | A Contributor may choose to distribute the Program in object code form under 76 | its own license agreement, provided that: 77 | 78 | a) it complies with the terms and conditions of this Agreement; and 79 | 80 | b) its license agreement: 81 | 82 | i) effectively disclaims on behalf of all Contributors all warranties and 83 | conditions, express and implied, including warranties or conditions of title 84 | and non-infringement, and implied warranties or conditions of merchantability 85 | and fitness for a particular purpose; 86 | 87 | ii) effectively excludes on behalf of all Contributors all liability for 88 | damages, including direct, indirect, special, incidental and consequential 89 | damages, such as lost profits; 90 | 91 | iii) states that any provisions which differ from this Agreement are offered 92 | by that Contributor alone and not by any other party; and 93 | 94 | iv) states that source code for the Program is available from such 95 | Contributor, and informs licensees how to obtain it in a reasonable manner on 96 | or through a medium customarily used for software exchange. 97 | 98 | When the Program is made available in source code form: 99 | 100 | a) it must be made available under this Agreement; and 101 | 102 | b) a copy of this Agreement must be included with each copy of the Program. 103 | 104 | Contributors may not remove or alter any copyright notices contained within 105 | the Program. 106 | 107 | Each Contributor must identify itself as the originator of its Contribution, 108 | if any, in a manner that reasonably allows subsequent Recipients to identify 109 | the originator of the Contribution. 110 | 111 | 4. COMMERCIAL DISTRIBUTION 112 | 113 | Commercial distributors of software may accept certain responsibilities with 114 | respect to end users, business partners and the like. While this license is 115 | intended to facilitate the commercial use of the Program, the Contributor who 116 | includes the Program in a commercial product offering should do so in a 117 | manner which does not create potential liability for other Contributors. 118 | Therefore, if a Contributor includes the Program in a commercial product 119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend 120 | and indemnify every other Contributor ("Indemnified Contributor") against any 121 | losses, damages and costs (collectively "Losses") arising from claims, 122 | lawsuits and other legal actions brought by a third party against the 123 | Indemnified Contributor to the extent caused by the acts or omissions of such 124 | Commercial Contributor in connection with its distribution of the Program in 125 | a commercial product offering. The obligations in this section do not apply 126 | to any claims or Losses relating to any actual or alleged intellectual 127 | property infringement. In order to qualify, an Indemnified Contributor must: 128 | a) promptly notify the Commercial Contributor in writing of such claim, and 129 | b) allow the Commercial Contributor tocontrol, and cooperate with the 130 | Commercial Contributor in, the defense and any related settlement 131 | negotiations. The Indemnified Contributor may participate in any such claim 132 | at its own expense. 133 | 134 | For example, a Contributor might include the Program in a commercial product 135 | offering, Product X. That Contributor is then a Commercial Contributor. If 136 | that Commercial Contributor then makes performance claims, or offers 137 | warranties related to Product X, those performance claims and warranties are 138 | such Commercial Contributor's responsibility alone. Under this section, the 139 | Commercial Contributor would have to defend claims against the other 140 | Contributors related to those performance claims and warranties, and if a 141 | court requires any other Contributor to pay any damages as a result, the 142 | Commercial Contributor must pay those damages. 143 | 144 | 5. NO WARRANTY 145 | 146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON 147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR 149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A 150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the 151 | appropriateness of using and distributing the Program and assumes all risks 152 | associated with its exercise of rights under this Agreement , including but 153 | not limited to the risks and costs of program errors, compliance with 154 | applicable laws, damage to or loss of data, programs or equipment, and 155 | unavailability or interruption of operations. 156 | 157 | 6. DISCLAIMER OF LIABILITY 158 | 159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 166 | OF SUCH DAMAGES. 167 | 168 | 7. GENERAL 169 | 170 | If any provision of this Agreement is invalid or unenforceable under 171 | applicable law, it shall not affect the validity or enforceability of the 172 | remainder of the terms of this Agreement, and without further action by the 173 | parties hereto, such provision shall be reformed to the minimum extent 174 | necessary to make such provision valid and enforceable. 175 | 176 | If Recipient institutes patent litigation against any entity (including a 177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 178 | (excluding combinations of the Program with other software or hardware) 179 | infringes such Recipient's patent(s), then such Recipient's rights granted 180 | under Section 2(b) shall terminate as of the date such litigation is filed. 181 | 182 | All Recipient's rights under this Agreement shall terminate if it fails to 183 | comply with any of the material terms or conditions of this Agreement and 184 | does not cure such failure in a reasonable period of time after becoming 185 | aware of such noncompliance. If all Recipient's rights under this Agreement 186 | terminate, Recipient agrees to cease use and distribution of the Program as 187 | soon as reasonably practicable. However, Recipient's obligations under this 188 | Agreement and any licenses granted by Recipient relating to the Program shall 189 | continue and survive. 190 | 191 | Everyone is permitted to copy and distribute copies of this Agreement, but in 192 | order to avoid inconsistency the Agreement is copyrighted and may only be 193 | modified in the following manner. The Agreement Steward reserves the right to 194 | publish new versions (including revisions) of this Agreement from time to 195 | time. No one other than the Agreement Steward has the right to modify this 196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 197 | Eclipse Foundation may assign the responsibility to serve as the Agreement 198 | Steward to a suitable separate entity. Each new version of the Agreement will 199 | be given a distinguishing version number. The Program (including 200 | Contributions) may always be distributed subject to the version of the 201 | Agreement under which it was received. In addition, after a new version of 202 | the Agreement is published, Contributor may elect to distribute the Program 203 | (including its Contributions) under the new version. Except as expressly 204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 205 | licenses to the intellectual property of any Contributor under this 206 | Agreement, whether expressly, by implication, estoppel or otherwise. All 207 | rights in the Program not expressly granted under this Agreement are 208 | reserved. 209 | 210 | This Agreement is governed by the laws of the State of New York and the 211 | intellectual property laws of the United States of America. No party to this 212 | Agreement will bring a legal action under this Agreement more than one year 213 | after the cause of action arose. Each party waives its rights to a jury trial 214 | in any resulting litigation. 215 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | A Clojure library designed to ... well, that part is up to you. 4 | 5 | ## Usage 6 | 7 | FIXME 8 | 9 | ## License 10 | 11 | Copyright © 2016 FIXME 12 | 13 | Distributed under the Eclipse Public License either version 1.0 or (at 14 | your option) any later version. 15 | -------------------------------------------------------------------------------- /example/project.clj: -------------------------------------------------------------------------------- 1 | (defproject example "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.8.0"] 7 | [disclojure "0.1.0-SNAPSHOT"]] 8 | :profiles 9 | {:repl {:source-paths ["src"] 10 | :prep-tasks ^:replace [["javac"] ["compile"]]}}) 11 | -------------------------------------------------------------------------------- /example/src/example/core.clj: -------------------------------------------------------------------------------- 1 | (ns example.core 2 | (:require [leipzig.melody :refer :all] 3 | [leipzig.scale :as scale] 4 | [disclojure.play] 5 | [disclojure.live :as l])) 6 | 7 | (def leanon-chords 8 | [[-9 -2 0 2 4] 9 | [-8 -1 -3 1 3] 10 | [-7 0 2 4] 11 | [-5 -1 2 4 5 6] 12 | [-5 -1 2 3 4]]) 13 | 14 | (def leanon 15 | (let [[ch1 ch2 ch3 ch4 ch5] leanon-chords] 16 | (->> (phrase (concat (take 9 (cycle [1/2 1/4])) 17 | [1/2] 18 | (take 9 (cycle [1/2 1/4])) 19 | [1/2]) 20 | [ch1 nil ch1 nil ch1 nil ch2 nil ch2 nil ch3 nil ch3 nil ch3 nil ch4 nil ch4 ch5]) 21 | (wherever :pitch, :pitch (comp scale/low scale/G scale/minor)) 22 | (all :part :plucky) 23 | (all :amp 1)))) 24 | 25 | (def initial-track 26 | {:plucky (times 2 leanon)}) 27 | 28 | (defonce state 29 | (l/reset-track initial-track)) 30 | 31 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject pjagielski/disclojure "0.1.5-SNAPSHOT" 2 | :description "A live coding environment for Overtone and Leipzig" 3 | :url "https://github.com/pjagielski/disclojure" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | 7 | :dependencies [[org.clojure/clojure "1.8.0"] 8 | [overtone "0.10.0"] 9 | [leipzig "0.10.0"] 10 | [prismatic/plumbing "0.5.3"] 11 | [org.clojure/java.data "0.1.1"] 12 | [org.clojure/math.numeric-tower "0.0.4"]] 13 | 14 | :deploy-repositories [["releases" :clojars]] 15 | :release-tasks [["vcs" "assert-committed"] 16 | ["change" "version" "leiningen.release/bump-version" "release"] 17 | ["vcs" "commit"] 18 | ["vcs" "tag"] 19 | ["deploy" "clojars"] 20 | ["change" "version" "leiningen.release/bump-version"] 21 | ["vcs" "commit"] 22 | ["vcs" "push"]] 23 | ) 24 | 25 | -------------------------------------------------------------------------------- /src/disclojure/inst.clj: -------------------------------------------------------------------------------- 1 | (ns disclojure.inst 2 | (:require [overtone.core :refer :all])) 3 | 4 | (defcgen echo [in {:default 0} max-delay {:default 1.0} delay-time {:default 0.4} decay-time {:default 2.0}] 5 | (:ar (pan2 (+ in (* 0.25 (comb-n in max-delay delay-time decay-time))))) 6 | (:default :ar)) 7 | 8 | (definst plucky [freq 440 dur 1 amp 0.8 cutoff 1000 delay-m 2 peak-freq 800] 9 | (let [env (env-gen (asr 0.02 0.5 1 -4) (line:kr 1.0 0.0 dur) :action FREE) 10 | level (+ (* 0.25 freq) (env-gen (perc 0 (* dur 1.25)) :level-scale cutoff)) 11 | snd (splay [(lf-saw freq) (pulse freq 0.3) (pink-noise)])] 12 | (-> snd 13 | (+ (delay-c snd 0.005 (ranged-rand 0.0001 0.2))) 14 | (lpf level) 15 | (clip2 0.85) 16 | (free-verb :room 2 :mix 0.40) 17 | (* env amp) 18 | (b-peak-eq peak-freq 0.6 12) 19 | (echo :delay-time (* dur delay-m) :max-delay dur)))) 20 | 21 | (definst pad [freq 220 dur 1.0 amp 0.2 pan 0 cutoff 1800 fil-amt 250] 22 | (let [env (env-gen (asr 0.01 0.6 1) (line:kr 1.0 0.0 dur) :action FREE) 23 | fil-env (+ fil-amt (env-gen (perc 0 dur) :level-scale cutoff)) 24 | osc (mix [(sin-osc freq) (saw (+ 2 freq))])] 25 | (-> osc 26 | (lpf fil-env) 27 | (+ (* 0.4 (comb-n:ar osc (* dur 8) dur))) 28 | (* env amp) 29 | (free-verb :room 0.7 :mix 0.25) 30 | pan2))) 31 | 32 | (definst supersaw [freq 440 dur 0.2 release 0.5 amp 0.6 cutoff 3500 env-amount 0.5] 33 | (let [snd-fn (fn [freq] 34 | (let [tune (ranged-rand 0.99 1.01)] 35 | (-> (lf-saw (* freq tune)) 36 | (delay-c 0.005 (ranged-rand 0.0001 0.01))))) 37 | hi-saws (splay (repeatedly 7 #(snd-fn freq))) 38 | lo-saws (splay (repeatedly 5 #(snd-fn (/ freq 2)))) 39 | noise (pink-noise) 40 | snd (+ (* 0.65 hi-saws) (* 0.85 lo-saws) (* 0.12 noise)) 41 | env (env-gen (adsr 0.001 0.7 0.2 0.1) (line:kr 1 0 (+ dur release)) :action FREE)] 42 | (-> snd 43 | (clip2 0.45) 44 | (rlpf (+ freq (env-gen (adsr 0.001) (line:kr 1 0 dur) :level-scale cutoff)) 0.75) 45 | (free-verb :room 1.8 :mix 0.45) 46 | (* env amp) 47 | pan2))) 48 | 49 | (definst stab [freq 440 detune 0.35 dur 0.2 amp 0.6 cutoff 950] 50 | (let [freqs (repeat 5 freq) 51 | freqs (map #(+ (* detune (rand)) %) freqs) 52 | saws (splay (lf-saw freqs)) 53 | snd (* 0.65 saws) 54 | env (env-gen (adsr 0.01 0.1 0.8 0.4) (line:kr 1 0 (* dur 1/2)) :action FREE)] 55 | (-> snd 56 | (rhpf (+ (* freq 2) cutoff) 0.25) 57 | (* env amp) 58 | pan2))) 59 | 60 | (definst bass [freq 220 dur 1.0 amp 0.3 osc-mix 0.5 cutoff 1500] 61 | (let [sub-freq (/ freq 2) 62 | osc1 (lf-saw freq) 63 | osc2 (pulse sub-freq 0.51) 64 | snd (+ (* osc-mix osc2) (* (- 1 osc-mix) osc1)) 65 | level (+ (/ freq 4) (env-gen (perc 0 dur) :level-scale cutoff)) 66 | snd (rlpf snd level 0.7) 67 | env (env-gen (adsr 0.0001) (line:kr 1.0 0.0 dur) :action FREE)] 68 | (pan2 (* amp env snd)))) 69 | -------------------------------------------------------------------------------- /src/disclojure/kit.clj: -------------------------------------------------------------------------------- 1 | (ns disclojure.kit 2 | (:require [overtone.core :refer :all] 3 | [clojure.java.io :as io] 4 | [leipzig.live :as live])) 5 | 6 | (defn without-extension [filename] 7 | (subs filename 0 (.lastIndexOf filename "."))) 8 | 9 | (def kit (atom {})) 10 | 11 | (definst mono-player [buf 1 amp 1] 12 | (let [env (env-gen (adsr 0.1 1 1 0.2 1) :action FREE)] 13 | (* amp env (scaled-play-buf 1 buf :action FREE)))) 14 | 15 | (definst stereo-player [buf 1 amp 1] 16 | (let [env (env-gen (adsr 0.1 1 1 0.2 1) :action FREE)] 17 | (* amp env (scaled-play-buf 2 buf :action FREE)))) 18 | 19 | (defn- -load-sample [file] 20 | (let [sample (sample (.getAbsolutePath file)) 21 | player (condp = (:n-channels sample) 22 | 1 mono-player 23 | 2 stereo-player 24 | (throw (str "Could not determine number of channels for" file)))] 25 | [(-> (without-extension (.getName file)) keyword) 26 | {:sound (partial player sample) 27 | :amp 1}])) 28 | 29 | (defn load-kit! [dir] 30 | (->> 31 | (.listFiles (io/file dir)) 32 | (map -load-sample) 33 | (into (sorted-map)) 34 | (reset! kit))) 35 | 36 | (defmethod live/play-note :beat [note] 37 | (when-let [fn (-> (get @kit (:drum note)) :sound)] 38 | (fn :amp (:amp note)))) 39 | -------------------------------------------------------------------------------- /src/disclojure/live.clj: -------------------------------------------------------------------------------- 1 | (ns disclojure.live 2 | (:require [disclojure.track :as t])) 3 | 4 | (def initial-state 5 | {:raw-track (ref {}) 6 | :track (ref [])}) 7 | 8 | (defonce state initial-state) 9 | 10 | (defn- assoc-raw-track 11 | [ref key val] 12 | (alter ref assoc key val)) 13 | 14 | (defn- update-raw-track 15 | [ref key fun] 16 | (alter ref update key fun)) 17 | 18 | (defn- raw-track [] (get state :raw-track)) 19 | (defn track [] (get state :track)) 20 | 21 | (defn- commit-track [] 22 | (let [{:keys [track raw-track]} state] 23 | (ref-set track (t/track @raw-track)))) 24 | 25 | (defn- modify-track [fun & kvs] 26 | (dosync 27 | (loop [key (first kvs) val (second kvs) rest (nnext kvs)] 28 | (fun (raw-track) key val) 29 | (if rest 30 | (recur (first rest) (second rest) (nnext rest)))) 31 | (commit-track))) 32 | 33 | (def assoc-track (partial modify-track assoc-raw-track)) 34 | 35 | (def update-track (partial modify-track update-raw-track)) 36 | 37 | (defn reset-track [raw-track] 38 | (dosync 39 | (ref-set (:raw-track state) raw-track) 40 | (commit-track))) 41 | -------------------------------------------------------------------------------- /src/disclojure/melody.clj: -------------------------------------------------------------------------------- 1 | (ns disclojure.melody 2 | (require [leipzig.melody :refer [wherever]])) 3 | 4 | (defn take-beats 5 | ([end notes] (take-beats 0 end notes)) 6 | ([start end notes] 7 | (->> notes 8 | (filter (fn [n] (and (>= (:time n) start) 9 | (< (+ (:duration n) (:time n)) end))) ) 10 | (wherever :time :time (fn [t] (- t start)))))) 11 | 12 | (defn- fill-first [notes] 13 | (let [first (first notes)] 14 | (if (not= 0 (:time first)) 15 | (concat [{:time 0 :pitch nil :duration (:time first)}] notes) 16 | notes))) 17 | 18 | (defn- fill-last [beats notes] 19 | (let [last (last notes) 20 | gap-time (+ (:time last) (:duration last)) 21 | gap (- beats gap-time) 22 | add-gap? (> gap 0)] 23 | (concat notes 24 | (when add-gap? [{:pitch nil :time gap-time :duration gap}])))) 25 | 26 | (defn fill-to-beats [beats notes] 27 | (->> notes 28 | fill-first 29 | (fill-last beats))) 30 | -------------------------------------------------------------------------------- /src/disclojure/midi.clj: -------------------------------------------------------------------------------- 1 | (ns disclojure.midi 2 | (:use clojure.java.data) 3 | (:require [clojure.math.numeric-tower :as math]) 4 | (:import (java.io File) 5 | (javax.sound.midi MidiSystem ShortMessage MetaMessage))) 6 | 7 | ; code taken from https://github.com/pbaille/bartok/blob/new-prims/src/bartok/midi/parser.clj but it's not realeased anywhere... 8 | 9 | (defmacro or= 10 | ([expr coll] `(or= ~expr ~@coll)) 11 | ([expr o & ors] 12 | `(or ~@(map (fn [o] `(= ~expr ~o)) (cons o ors))))) 13 | 14 | (defmacro or-> [arg & exprs] 15 | `(or ~@(map (fn [expr] (if (symbol? expr) 16 | `(~expr ~arg) 17 | (cons (first expr) (cons arg (next expr))))) 18 | exprs))) 19 | 20 | ;shortcuts 21 | (def a apply) 22 | 23 | ;********************************************** 24 | 25 | (defn- note-on? [msg] (or= (.getCommand msg) (range 0x90 0xA0))) 26 | (defn- note-off? [msg] (or= (.getCommand msg) (range 0x80 0x90))) 27 | (defn- poly-after? [msg] (or= (.getCommand msg) (range 0xA0 0xB0))) 28 | (defn- control-change? [msg] (or= (.getCommand msg) (range 0xB0 0xC0))) 29 | (defn- program-change? [msg] (or= (.getCommand msg) (range 0xC0 0xD0))) 30 | (defn- chan-after? [msg] (or= (.getCommand msg) (range 0xD0 0xE0))) 31 | (defn- pitch-wheel? [msg] (or= (.getCommand msg) (range 0xE0 0xF0))) 32 | 33 | (defn- note-msg? [msg] (or (note-on? msg)(note-off? msg))) 34 | 35 | (defn- tempo-msg? [msg] (= (.getType msg) 0x51)) 36 | (defn- time-signature-msg? [msg] (= (.getType msg) 0x58)) 37 | (defn- key-signature-msg? [msg] (= (.getType msg) 0x59)) 38 | 39 | (def int->key 40 | {0 :C -1 :Bb -2 :Eb -3 :Ab -4 :Db 41 | -5 :Gb -6 :Cb -7 :Fb 1 :G 2 :D 42 | 3 :A 4 :E 5 :B 6 :F# 7 :C#}) 43 | 44 | ; (defn- bpm-at [midi-pos parsed-file] ()) 45 | 46 | (defn- valid-meta-msg? [msg] 47 | (or-> msg 48 | tempo-msg? 49 | time-signature-msg? 50 | key-signature-msg?)) 51 | 52 | (defn- valid-msg? [msg] 53 | (or-> msg 54 | note-on? 55 | note-off? 56 | poly-after? 57 | control-change? 58 | program-change? 59 | chan-after? 60 | pitch-wheel?)) 61 | 62 | (defn- parse-meta-message [msg tick] 63 | (cond 64 | (tempo-msg? msg) 65 | {:type :tempo 66 | :position tick 67 | :bpm (->> (a format "0x%x%x%x" (.getData msg)) 68 | read-string 69 | (/ 60000000) 70 | float 71 | (math/round))} 72 | (time-signature-msg? msg) 73 | {:type :time-signature 74 | :position tick 75 | :signature (let [[n d] (from-java (.getData msg))] 76 | [n (int (math/expt 2 d))])} 77 | (key-signature-msg? msg) 78 | {:type :key-signature 79 | :position tick 80 | :key (get int->key (first (from-java (.getData msg))))} 81 | :else nil)) 82 | 83 | (defn- parse-message [msg tick] 84 | (cond 85 | (note-msg? msg) 86 | {:type :note 87 | :channel (.getChannel msg) 88 | :pitch (.getData1 msg) 89 | :velocity (if (note-on? msg) (.getData2 msg) 0) 90 | :position tick} 91 | (poly-after? msg) 92 | {:type :poly-after 93 | :channel (.getChannel msg) 94 | :data [(.getData1 msg) (.getData2 msg)] 95 | :position tick} 96 | (control-change? msg) 97 | {:type :control-change 98 | :channel (.getChannel msg) 99 | :data [(.getData1 msg) (.getData2 msg)] 100 | :position tick} 101 | (program-change? msg) 102 | {:type :program-change 103 | :channel (.getChannel msg) 104 | :data (.getData msg) 105 | :position tick} 106 | (chan-after? msg) 107 | {:type :chan-after 108 | :channel (.getChannel msg) 109 | :data (.getData msg) 110 | :position tick} 111 | (pitch-wheel? msg) 112 | {:type :pitch-wheel 113 | :channel (.getChannel msg) 114 | :data (.getData msg) 115 | :position tick} 116 | :else nil)) 117 | 118 | (defn find-first 119 | [f coll] 120 | (first (filter f coll))) 121 | 122 | ;set start-position to zero and convert durations and positions into beat unit 123 | (defn- time-format [resolution parsed] 124 | (let [start-offset (:position (find-first #(= (:type %) :note) parsed))] 125 | (map (fn [event] 126 | (let [pos (/ (- (:position event) start-offset) resolution) 127 | event (assoc event :position pos)] 128 | (if (:duration event) 129 | (update-in event [:duration] / resolution) 130 | event))) 131 | parsed))) 132 | 133 | ;grab all note-on and note-off message and couple them into :note type with duration 134 | (defn- on-off-coupling [parsed] 135 | (let [{notes :note :as by-type} (group-by :type parsed) 136 | {ons :ons offs :offs} (group-by #(if (zero? (:velocity %)) :offs :ons) notes) 137 | coupled (map (fn [{pos-on :position :as m} {pos-off :position}] 138 | (assoc m :duration (- pos-off pos-on))) 139 | ons offs)] 140 | (->> (assoc by-type :note coupled) vals (a concat)))) 141 | 142 | (defn- parse-track [track] 143 | (loop [parsed [] 144 | event-index 0] 145 | (let [event (.get track event-index) 146 | tick (.getTick event) 147 | message (.getMessage event)] 148 | (cond 149 | (= (inc event-index) (.size track)) parsed 150 | (and (instance? MetaMessage message) (valid-meta-msg? message)) 151 | (recur (conj parsed (parse-meta-message message tick)) (inc event-index)) 152 | (and (instance? ShortMessage message) (valid-msg? message)) 153 | (recur (conj parsed (parse-message message tick)) (inc event-index)) 154 | :else (recur parsed (inc event-index)))))) 155 | 156 | ;main 157 | (defn parse-midi-file [file-name] 158 | (let [midi-seq (-> (File. file-name) MidiSystem/getSequence) 159 | tracks (.getTracks midi-seq) 160 | res (.getResolution midi-seq) 161 | cnt (-> tracks from-java count)] 162 | (->> (for [n (range cnt)] 163 | (parse-track (aget tracks n))) 164 | (mapcat on-off-coupling) 165 | (sort-by :position) 166 | (time-format res)))) 167 | 168 | ;(parse-midi-file "src/midi-files/rmmlo.mid") 169 | 170 | (defn filter-msg-type [type-kw parsed-midi-file] 171 | (filter #(= (:type %) type-kw) parsed-midi-file)) 172 | 173 | (defn- map-midi-note [[last-time last-dur result] {:keys [position duration] :as note}] 174 | (let [to-add (concat (when (< last-time position) 175 | [{:position (+ last-dur last-time) 176 | :duration (- position last-time) 177 | :pitch nil}]) 178 | [note])] 179 | [position duration (concat result to-add)])) 180 | 181 | (defn- midi-notes->leipzig [parsed-midi-file] 182 | (as-> parsed-midi-file $ 183 | (filter-msg-type :note $) 184 | (reduce map-midi-note [0 0 []] $) 185 | (nth $ 2) 186 | (map (fn [n] (clojure.set/rename-keys n {:position :time})) $) 187 | (map (fn [n] (select-keys n [:pitch :time :duration])) $))) 188 | 189 | (defn midi-file->notes [file-name] 190 | (-> (parse-midi-file file-name) 191 | midi-notes->leipzig)) 192 | -------------------------------------------------------------------------------- /src/disclojure/play.clj: -------------------------------------------------------------------------------- 1 | (ns disclojure.play 2 | (:require [leipzig.live :as live] 3 | [leipzig.temperament :refer [equal]] 4 | [disclojure.inst :as i] 5 | [overtone.core :refer :all] 6 | [overtone.inst.synth :refer :all])) 7 | 8 | (def controls (atom {:plucky {:amp 1.0 :cutoff 900} 9 | :stab {:amp 0.75 :cutoff 2000} 10 | :supersaw {:release 0.2 :cutoff 3000} 11 | :bass {:release 0.1 :cutoff 1500}})) 12 | 13 | (defn to-args [m] 14 | (mapcat vec m)) 15 | 16 | (defn play [name params] 17 | (live/play-note (merge params {:part name :pitch (equal (:note params))}))) 18 | 19 | (defn find-instruments 20 | ([] (find-instruments 'disclojure.inst)) 21 | ([ns] 22 | (->> (ns-interns ns) 23 | (filter (fn [e] (= :overtone.studio.inst/instrument 24 | (type (val e)))))))) 25 | 26 | (doseq [[name play-fn] (find-instruments)] 27 | (let [key (keyword name)] 28 | (defmethod live/play-note key [{hertz :pitch seconds :duration amp :amp cutoff :cutoff}] 29 | (when hertz 30 | (let [params (merge {:freq hertz :dur seconds :amp (or amp 1)} 31 | (when cutoff {:cutoff cutoff}))] 32 | (apply play-fn (to-args (merge (get @controls key) params)))))))) 33 | -------------------------------------------------------------------------------- /src/disclojure/sampler.clj: -------------------------------------------------------------------------------- 1 | (ns disclojure.sampler 2 | (:require [clojure.java.io :as io] 3 | [overtone.core :refer :all] 4 | [plumbing.core :refer [map-vals]] 5 | [leipzig.live :as live]) 6 | (:import (java.io File))) 7 | 8 | (def sample-regex #"(\d*)_(\d*)_([a-zA-Z0-9\-]*).(\w*)") 9 | 10 | (def file-regex #"(.*)\.(\w*)") 11 | 12 | (def defaults {:bpm 120 :beats 8}) 13 | 14 | (defn meta-from-filename [^File file] 15 | (-> (if-let [[_ bpm beats name _] 16 | (re-matches sample-regex (.getName file))] 17 | {:bpm (read-string bpm) :beats (read-string beats) :name (keyword name)} 18 | (let [[_ name _] (re-matches file-regex (.getName file))] 19 | (merge defaults {:name (keyword name)}))) 20 | (merge {:sound (sample (.getAbsolutePath file))}))) 21 | 22 | (defonce samples (atom {})) 23 | 24 | (defn load-samples! [dir] 25 | (->> 26 | (.listFiles (io/file dir)) 27 | (map meta-from-filename) 28 | (group-by :name) 29 | (map-vals first) 30 | (into (sorted-map)) 31 | (reset! samples))) 32 | 33 | (definst sampler [in 0 bpm 120 total-beats 4 beats 4 start-beat 0 amp 1 cutoff 10000 fade 0.2 out-bus 0] 34 | (let [beat-len (/ 60 bpm) 35 | env (env-gen (envelope [1 1 0] [(* beats beat-len) fade] :welch)) 36 | rate (/ (buf-dur:kr in) (* total-beats beat-len)) 37 | frames (- (buf-frames in) 1) 38 | pos (* (/ start-beat total-beats) frames)] 39 | (-> (play-buf 1 in (* rate (buf-rate-scale in)) :start-pos pos :action FREE) 40 | (lpf cutoff) 41 | (pan2) 42 | (* amp env)))) 43 | 44 | (defmethod live/play-note :sampler [data] 45 | (when-let [s (get @samples (:sample data))] 46 | (sampler (:sound s) (:bpm data) (:beats s) 47 | (or (:beats data) (:beats s)) 48 | (or (:start-beat data) 0) 49 | (or (:amp data) 1) 50 | (or (:cutoff data) 10000)))) 51 | -------------------------------------------------------------------------------- /src/disclojure/track.clj: -------------------------------------------------------------------------------- 1 | (ns disclojure.track 2 | (:require [leipzig.melody :refer :all] 3 | [leipzig.temperament :as temperament] 4 | [disclojure.play])) 5 | 6 | (def metro (atom 100)) 7 | 8 | (defn tap [drum times length & {:keys [amp] :or {amp 1}}] 9 | (map #(zipmap [:time :duration :drum :amp] 10 | [%1 (- length %1) drum amp]) times)) 11 | 12 | (defn sampler-entry [times sample beats & {:keys [amp start-beat cutoff] :or {amp 1 start-beat 0 cutoff 10000}}] 13 | (let [base (merge {:sample sample :amp (or amp 1) :bpm @metro :cutoff (or cutoff 10000)} 14 | (when start-beat {:start-beat start-beat}) 15 | (when beats {:beats beats :duration beats}))] 16 | (if (sequential? times) 17 | (map (fn [t] (merge base {:time t})) times) 18 | [(merge base {:time times})]))) 19 | 20 | (defn sampler [meta] 21 | (->> 22 | meta 23 | (mapcat #(apply sampler-entry %)) 24 | (sort-by :time) 25 | (all :part :sampler))) 26 | 27 | (defn track [raw-track] 28 | (->> raw-track 29 | vals 30 | (reduce with) 31 | (wherever :pitch, :pitch temperament/equal) 32 | (where :time (bpm @metro)) 33 | (where :duration (bpm @metro)))) 34 | 35 | -------------------------------------------------------------------------------- /test/disclojure/live_test.clj: -------------------------------------------------------------------------------- 1 | (ns disclojure.live-test 2 | (:require [clojure.test :refer :all] 3 | [disclojure.live :as live])) 4 | 5 | (deftest live-test 6 | 7 | (testing "reset track" 8 | (live/reset-track {:instr [{:time 0 :part :test}]}) 9 | (is (= [{:time 0 :part :test}] @(live/track))) 10 | (live/reset-track {:instr [{:time 1/2 :part :test}]}) 11 | (is (= [{:time 3/10 :part :test}] @(live/track)))) 12 | 13 | (testing "assoc track" 14 | (live/reset-track {:a [{:time 0 :part :a}]}) 15 | (live/assoc-track :b [{:time 1/2 :part :b}]) 16 | (is (= [{:time 0 :part :a} {:time 3/10 :part :b}] @(live/track))) 17 | (live/assoc-track :b [{:time 1/4 :part :b}]) 18 | (is (= [{:time 0 :part :a} {:time 3/20 :part :b}] @(live/track)))) 19 | 20 | (testing "update track" 21 | (live/reset-track {:c [{:time 0 :part :c} {:time 1/2 :part :c}]}) 22 | (letfn [(pred [e] (> (:time e) 0))] 23 | (live/update-track :c (partial remove pred))) 24 | (is (= [{:time 0 :part :c}] @(live/track))))) 25 | -------------------------------------------------------------------------------- /test/disclojure/melody_test.clj: -------------------------------------------------------------------------------- 1 | (ns disclojure.melody-test 2 | (:require [clojure.test :refer :all] 3 | [disclojure.melody :refer [take-beats fill-to-beats]])) 4 | 5 | (deftest melody-test 6 | 7 | (testing "should take start beats" 8 | (let [notes (->> [{:time 0 :pitch 2 :duration 1} {:time 5 :pitch 3 :duration 2}] 9 | (take-beats 16))] 10 | (is (= notes 11 | [{:time 0 :pitch 2 :duration 1} {:time 5 :pitch 3 :duration 2}])))) 12 | 13 | (testing "should take middle beats" 14 | (let [notes (->> [{:time 32 :pitch 2 :duration 1} {:time 33 :pitch 3 :duration 2}] 15 | (take-beats 32 48))] 16 | (is (= notes 17 | [{:time 0 :pitch 2 :duration 1} {:time 1 :pitch 3 :duration 2}])))) 18 | 19 | (testing "should fill start" 20 | (let [notes (->> [{:time 7/2 :pitch 2 :duration 1/2}] 21 | (fill-to-beats 4))] 22 | (is (= notes 23 | [{:time 0 :pitch nil :duration 7/2} 24 | {:time 7/2 :pitch 2 :duration 1/2}])))) 25 | 26 | (testing "should fill end" 27 | (let [notes (->> [{:time 0 :pitch 2 :duration 7/2} {:time 7/2 :pitch 2 :duration 1/4}] 28 | (fill-to-beats 4))] 29 | (is (= notes 30 | [{:time 0 :pitch 2 :duration 7/2} 31 | {:time 7/2 :pitch 2 :duration 1/4} 32 | {:time 15/4 :pitch nil :duration 1/4}]))))) 33 | 34 | -------------------------------------------------------------------------------- /test/disclojure/midi_test.clj: -------------------------------------------------------------------------------- 1 | (ns disclojure.midi-test 2 | (:require [clojure.test :refer :all] 3 | [disclojure.midi :as midi] 4 | [disclojure.melody :refer [fill-to-beats]])) 5 | 6 | (deftest midi-parser-test 7 | (testing "should parse notest" 8 | (let [notes (->> (midi/midi-file->notes "test/piano.mid"))] 9 | (is (not-empty notes)) 10 | (is (= (first notes) {:pitch 51, :time 0, :duration 1/2})))) 11 | 12 | (testing "should fill to beats" 13 | (let [notes (->> (midi/midi-file->notes "test/snake-pluck.mid") 14 | (fill-to-beats 16))] 15 | (is (not-empty notes)) 16 | (is (= (last notes) {:pitch nil, :time 63/4, :duration 1/4}))))) 17 | -------------------------------------------------------------------------------- /test/disclojure/sampler_test.clj: -------------------------------------------------------------------------------- 1 | (ns disclojure.sampler-test 2 | (:require [clojure.test :refer :all] 3 | [disclojure.track :as track])) 4 | 5 | (deftest sampler-test 6 | (testing "data" 7 | (is (= [{:time 0 :sample :test :part :sampler :amp 1 :bpm 100 :beats 8 :start-beat 0 :duration 8 :cutoff 10000}] 8 | (track/sampler [[0 :test 8]])))) 9 | 10 | (testing "start-beat" 11 | (is (= [{:time 0 :sample :test :part :sampler :amp 1 :bpm 100 :beats 8 :start-beat 2 :duration 8 :cutoff 10000}] 12 | (track/sampler [[0 :test 8 :start-beat 2]])))) 13 | 14 | (testing "amp" 15 | (is (= [{:time 0 :sample :test :part :sampler :amp 2 :bpm 100 :beats 8 :start-beat 0 :duration 8 :cutoff 10000}] 16 | (track/sampler [[0 :test 8 :amp 2]])))) 17 | 18 | (testing "cutoff" 19 | (is (= [{:time 0 :sample :test :part :sampler :amp 1 :bpm 100 :beats 8 :start-beat 0 :duration 8 :cutoff 1000}] 20 | (track/sampler [[0 :test 8 :cutoff 1000]])))) 21 | 22 | (testing "times" 23 | (is (= [{:time 0 :sample :test} {:time 4 :sample :test}] 24 | (->> (track/sampler [[[0 4] :test 8]]) 25 | (map #(select-keys % [:time :sample]))))) 26 | (is (= [{:time 0 :sample :test} {:time 4 :sample :test2}] 27 | (->> (track/sampler [[[4] :test2 8] 28 | [[0] :test 8]]) 29 | (map #(select-keys % [:time :sample]))))))) 30 | -------------------------------------------------------------------------------- /test/piano.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjagielski/disclojure/d1d4b439154fa3bcbed5e0295b53ae4e73bbd46a/test/piano.mid -------------------------------------------------------------------------------- /test/snake-pluck.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjagielski/disclojure/d1d4b439154fa3bcbed5e0295b53ae4e73bbd46a/test/snake-pluck.mid --------------------------------------------------------------------------------