├── .gitignore ├── LICENSE ├── README.md ├── project.clj ├── src └── uncomplicate │ └── clojure_sound │ ├── core.clj │ ├── internal.clj │ ├── midi.clj │ └── sampled.clj └── test ├── clojure └── uncomplicate │ └── clojure_sound │ ├── midi_test.clj │ └── sampled_test.clj └── resources ├── Noise.wav └── maple.mid /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /lib 3 | /classes 4 | /checkouts 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | /.lein-* 10 | /.nrepl-port 11 | doc 12 | docs 13 | hs_*.log 14 | .#* 15 | .DS_Store 16 | *.o 17 | *.so 18 | */nrepl-port 19 | */target 20 | -------------------------------------------------------------------------------- /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 | [New books available](https://aiprobook.com) 2 | 3 | Deep Learning for Programmers 4 | 5 | Numerical Linear Algebra for Programmers 6 | 7 | # Clojure Sound 8 | 9 | [Adopt your pet function](https://dragan.rocks/articles/18/Patreon-Announcement-Adopt-a-Function) and [become a patron](https://patreon.com/draganrocks). 10 | 11 | Clojure Sound is a library for transforming digital media and communicating with MIDI devices. 12 | It is based on Java Sound API, but improves on Java Sound's UX shortcomings as much as possible, 13 | to provide nice interactive programming experience. 14 | 15 | ## How to use it 16 | 17 | I haven't written documentation for each function yet, but the whole [Java Sound tutorial](https://docs.oracle.com/javase/tutorial/sound/TOC.html) is covered as [midje tests](https://github.com/uncomplicate/clojure-sound/tree/master/test/clojure/uncomplicate/clojure_sound), which is even more useful because it shows how each function works in context of real use cases. 18 | 19 | You might also follow the tutorials that I write on [dragan.rocks](https://dragan.rocks) 20 | 21 | ## License 22 | 23 | Copyright © 2022-2022 Dragan Djuric 24 | 25 | Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version. 26 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject org.uncomplicate/clojure-sound "0.4.0-SNAPSHOT" 2 | :description "Clojure Sound is a library for transforming digital media and communicating with MIDI devices." 3 | :url "https://github.com/uncomplicate/clojure-sound" 4 | :scm {:name "git" 5 | :url "https://github.com/uncomplicate/clojure-sound"} 6 | :license {:name "Eclipse Public License" 7 | :url "http://www.eclipse.org/legal/epl-v10.html"} 8 | :dependencies [[org.clojure/clojure "1.11.1"] 9 | [uncomplicate/commons "0.13.0"]] 10 | :profiles {:dev {:plugins [[lein-midje "3.2.1"] 11 | [lein-codox "0.10.7"]] 12 | :global-vars {*warn-on-reflection* true 13 | *assert* false 14 | *unchecked-math* :warn-on-boxed 15 | *print-length* 128} 16 | :dependencies [[midje "1.10.5"] 17 | [codox-theme-rdash "0.1.2"]] 18 | :codox {:metadata {:doc/format :markdown} 19 | :source-uri "http://github.com/uncomplicate/clojure_sound/blob/master/{filepath}#L{line}" 20 | :themes [:rdash] 21 | :output-path "docs/codox"}}} 22 | 23 | :test-paths ["test/clojure" "test/resources"]) 24 | -------------------------------------------------------------------------------- /src/uncomplicate/clojure_sound/core.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Dragan Djuric. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) or later 4 | ;; which can be found in the file LICENSE at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns uncomplicate.clojure-sound.core 10 | (:require [uncomplicate.clojure-sound.internal :refer [supported]]) 11 | (:import java.net.URL 12 | [java.io File InputStream OutputStream] 13 | [javax.sound.midi MidiSystem MidiDevice MidiDevice$Info MidiFileFormat MidiChannel 14 | Receiver MidiDeviceReceiver MidiDeviceTransmitter Sequencer Soundbank Synthesizer 15 | Transmitter ControllerEventListener MetaEventListener Instrument MetaMessage MidiEvent 16 | MidiMessage Patch Sequence Sequencer$SyncMode ShortMessage SoundbankResource 17 | SysexMessage Track VoiceStatus])) 18 | 19 | (defprotocol SoundSystemProcedures 20 | (file-format [this])) 21 | 22 | (defprotocol SoundInfoProvider 23 | (sound-info [this])) 24 | 25 | (defprotocol Open 26 | (open! [this!] [this! buffer-size] [this! format data offset buffer-size]) 27 | (open? [this!])) 28 | 29 | (defprotocol Timing 30 | (resolution [this]) 31 | (division [this]) 32 | (micro-length [this]) 33 | (micro-position [this]) 34 | (micro-position! [this microseconds])) 35 | 36 | (defprotocol Reset 37 | (re-set! [this!])) 38 | 39 | (defprotocol Broadcast 40 | (listen! [this! listener] [this! listener params]) 41 | (ignore! [this! listener] [this! listener params])) 42 | 43 | (defprotocol Activity 44 | (running? [this]) 45 | (active? [this]) 46 | (start! [this]) 47 | (stop! [this])) 48 | 49 | (defprotocol Type 50 | (itype [this])) 51 | 52 | (defprotocol Format 53 | (property [this key]) 54 | (properties [this]) 55 | (byte-length [this])) 56 | 57 | (defprotocol Available 58 | (available [this])) 59 | 60 | (defprotocol Channels 61 | (channels [this])) 62 | 63 | (defn supported? 64 | ([feature] 65 | (supported feature)) 66 | ([this feature] 67 | (supported feature this))) 68 | 69 | (defmulti write! (fn [& args] (mapv class args))) 70 | 71 | (defmethod write! :default [& args] 72 | (throw (ex-info "Unsupported write request." {:type :sound-error :args args}))) 73 | 74 | (defmulti read! (fn [& args] (mapv class args))) 75 | 76 | (defmethod read! :default [& args] 77 | (throw (ex-info "Unsupported read request." {:type :sound-error :args args}))) 78 | 79 | (defmulti connect! (fn [& args] (mapv class args))) 80 | 81 | (defmethod connect! :default [& args] 82 | (throw (ex-info "Unsupported connection between these endpoints." {:type :sound-error :args args}))) 83 | 84 | (defmethod print-method (Class/forName "[I") 85 | [this w] 86 | (print-method (seq this) w)) 87 | 88 | (defmethod print-method (Class/forName "[B") 89 | [this w] 90 | (print-method (seq this) w)) 91 | 92 | (defmethod print-method (Class/forName "[J") 93 | [this w] 94 | (print-method (seq this) w)) 95 | -------------------------------------------------------------------------------- /src/uncomplicate/clojure_sound/internal.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Dragan Djuric. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) or later 4 | ;; which can be found in the file LICENSE at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns uncomplicate.clojure-sound.internal 10 | (:require [clojure.string :as str] 11 | [uncomplicate.commons.core :refer [Info info]])) 12 | 13 | (defprotocol Support 14 | (supported [feature] [feature object])) 15 | 16 | (defprotocol SequenceSource 17 | (set-sequence [source sequencer!]) 18 | (get-sequence [source])) 19 | 20 | (defprotocol Load 21 | (load-instruments [source synth]) 22 | (unload-instruments [source synth])) 23 | 24 | (defprotocol ReceiverProvider 25 | (get-receiver [this])) 26 | 27 | (defprotocol GetFormat 28 | (get-format [this])) 29 | 30 | (defn name-key [s] 31 | (-> (str/trim s) 32 | (str/replace " " "-") 33 | str/lower-case 34 | (str/replace "_" "-") 35 | keyword)) 36 | 37 | (defn key-name [k] 38 | (-> (name k) 39 | (str/trim) 40 | (str/replace "-" "_"))) 41 | 42 | (defmethod print-method (Class/forName "[Ljava.lang.Object;") 43 | [objects ^java.io.Writer w] 44 | (.write w (pr-str (seq objects)))) 45 | 46 | (defn simple-name [^Class class] 47 | (.getSimpleName class)) 48 | 49 | (defmacro extend-array-info [array-type] 50 | `(extend-protocol Info 51 | ~array-type 52 | (info 53 | ([this#] 54 | (map info this#)) 55 | ([this# info-type#] 56 | (map #(info % info-type#) this#))))) 57 | -------------------------------------------------------------------------------- /src/uncomplicate/clojure_sound/midi.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Dragan Djuric. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) or later 4 | ;; which can be found in the file LICENSE at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns uncomplicate.clojure-sound.midi 10 | (:refer-clojure :exclude [sequence]) 11 | (:require [clojure 12 | [string :refer [trim join]] 13 | [walk :refer [keywordize-keys]]] 14 | [uncomplicate.commons.core :refer [Releaseable Closeable close! Info info]] 15 | [uncomplicate.clojure-sound 16 | [internal :refer [name-key Support SequenceSource set-sequence get-sequence 17 | Load load-instruments unload-instruments simple-name key-name 18 | ReceiverProvider get-receiver extend-array-info]] 19 | [core :refer [write! SoundInfoProvider Open Timing Reset Broadcast Activity Type 20 | Format active? connect! micro-length division resolution 21 | properties property itype SoundSystemProcedures Available Channels]]]) 22 | (:import [clojure.lang ILookup IFn] 23 | java.lang.reflect.Field 24 | java.net.URL 25 | java.util.Arrays 26 | [java.io File InputStream OutputStream] 27 | [java.nio ByteBuffer ByteOrder] 28 | [javax.sound.midi MidiSystem MidiDevice MidiDevice$Info MidiFileFormat MidiChannel 29 | Receiver MidiDeviceReceiver MidiDeviceTransmitter Sequencer Soundbank Synthesizer 30 | Transmitter ControllerEventListener MetaEventListener Instrument MetaMessage MidiEvent 31 | MidiMessage Patch Sequence Sequencer$SyncMode ShortMessage SoundbankResource 32 | SysexMessage Track VoiceStatus])) 33 | 34 | (defprotocol Instruments 35 | (instruments [this])) 36 | 37 | (defprotocol Program 38 | (program [this])) 39 | 40 | (defprotocol Data 41 | (data [this]) 42 | (message! [message arg] [message arg1 arg2 arg3] [message command channel data1 data2])) 43 | 44 | (defprotocol Tick 45 | (ticks [this])) 46 | 47 | (defprotocol Event 48 | (event [this arg])) 49 | 50 | (defprotocol Code 51 | (encode [this]) 52 | (decode [this])) 53 | 54 | ;; ===================== Keyword coding ================================================ 55 | 56 | (def sync-mode 57 | {:internal Sequencer$SyncMode/INTERNAL_CLOCK 58 | :midi-sync Sequencer$SyncMode/MIDI_SYNC 59 | :midi-time-code Sequencer$SyncMode/MIDI_TIME_CODE 60 | :no-sync Sequencer$SyncMode/NO_SYNC}) 61 | 62 | (def sync-mode-key 63 | {Sequencer$SyncMode/INTERNAL_CLOCK :internal 64 | Sequencer$SyncMode/MIDI_SYNC :midi-sync 65 | Sequencer$SyncMode/MIDI_TIME_CODE :midi-time-code 66 | Sequencer$SyncMode/NO_SYNC :no-sync}) 67 | 68 | (def ^:const timing-type 69 | {:ppq Sequence/PPQ 70 | :smpte24 Sequence/SMPTE_24 71 | :smpte25 Sequence/SMPTE_25 72 | :smpte30 Sequence/SMPTE_30 73 | :smpte30drop Sequence/SMPTE_30DROP}) 74 | 75 | (def ^:const timing-type-key 76 | {Sequence/PPQ :ppq 77 | Sequence/SMPTE_24 :smpte24 78 | Sequence/SMPTE_25 :smpte25 79 | Sequence/SMPTE_30 :smpte30 80 | Sequence/SMPTE_30DROP :smpte30drop}) 81 | 82 | (def ^:const command-type 83 | {:active-sensing ShortMessage/ACTIVE_SENSING 84 | :channel-pressure ShortMessage/CHANNEL_PRESSURE 85 | :continue ShortMessage/CONTINUE 86 | :control-change ShortMessage/CONTROL_CHANGE 87 | :end-of-exclusive ShortMessage/END_OF_EXCLUSIVE 88 | :time-code ShortMessage/MIDI_TIME_CODE 89 | :off ShortMessage/NOTE_OFF 90 | :on ShortMessage/NOTE_ON 91 | :bend ShortMessage/PITCH_BEND 92 | :poly-pressure ShortMessage/POLY_PRESSURE 93 | :program-change ShortMessage/PROGRAM_CHANGE 94 | :song-position ShortMessage/SONG_POSITION_POINTER 95 | :song-select ShortMessage/SONG_SELECT 96 | :start ShortMessage/START 97 | :stop ShortMessage/STOP 98 | :reset ShortMessage/SYSTEM_RESET 99 | :clock ShortMessage/TIMING_CLOCK 100 | :tune ShortMessage/TUNE_REQUEST 101 | :special-system-exclusive SysexMessage/SPECIAL_SYSTEM_EXCLUSIVE 102 | :system-exclusive SysexMessage/SYSTEM_EXCLUSIVE}) 103 | 104 | (def ^:const command-type-key 105 | {ShortMessage/ACTIVE_SENSING :active-sensing 106 | ShortMessage/CHANNEL_PRESSURE :channel-pressure 107 | ShortMessage/CONTINUE :continue 108 | ShortMessage/CONTROL_CHANGE :control-change 109 | ShortMessage/END_OF_EXCLUSIVE :end-of-exclusive 110 | ShortMessage/MIDI_TIME_CODE :time-code 111 | ShortMessage/NOTE_OFF :off 112 | ShortMessage/NOTE_ON :on 113 | ShortMessage/PITCH_BEND :bend 114 | ShortMessage/POLY_PRESSURE :poly-pressure 115 | ShortMessage/PROGRAM_CHANGE :program-change 116 | ShortMessage/SONG_POSITION_POINTER :song-position 117 | ShortMessage/SONG_SELECT :song-select 118 | ShortMessage/START :start 119 | ShortMessage/STOP :stop 120 | ShortMessage/SYSTEM_RESET :reset 121 | ShortMessage/TIMING_CLOCK :clock 122 | ShortMessage/TUNE_REQUEST :tune}) 123 | 124 | (def ^:const controller-type 125 | {0 :bank 126 | 1 :modulation-wheel 127 | 2 :breath-control 128 | 4 :foot-control 129 | 5 :portamento-time 130 | 6 :data-entry 131 | 7 :volume 132 | 8 :balance 133 | 10 :pan 134 | 11 :expression-control 135 | 12 :effect-control-1 136 | 13 :effect-control-2 137 | 16 :gp-control-1 138 | 17 :gp-control-2 139 | 18 :gp-control-3 140 | 19 :gp-control-4 141 | 32 :bank 142 | 33 :modulation-wheel 143 | 34 :breath-control 144 | 36 :foot-control 145 | 37 :portamento-time 146 | 38 :data-entry 147 | 39 :volume 148 | 40 :balance 149 | 42 :pan 150 | 43 :expression-control 151 | 44 :effect-control-1 152 | 45 :effect-control-2 153 | 48 :gp-control-1 154 | 49 :gp-control-2 155 | 50 :gp-control-3 156 | 51 :gp-control-4 157 | 64 :sustain 158 | 65 :portamento 159 | 66 :sostenuto 160 | 67 :soft-pedal 161 | 68 :legato-footswitch 162 | 69 :hold-2 163 | 70 :sc-sound-variation 164 | 71 :sc-timbre 165 | 72 :sc-release-time 166 | 73 :sc-attack-time 167 | 74 :sc-brightness 168 | 75 :sc-6 169 | 76 :sc-7 170 | 77 :sc-8 171 | 78 :sc-9 172 | 79 :sc-10 173 | 80 :gp-control-5 174 | 81 :gp-control-6 175 | 82 :gp-control-7 176 | 83 :gp-control-8 177 | 84 :portamento-control 178 | 91 :effects-1-depth 179 | 92 :effects-2-depth 180 | 93 :effects-3-depth 181 | 94 :effects-4-depth 182 | 95 :effects-5-depth 183 | 96 :data-inc 184 | 97 :data-dec 185 | 98 :non-registered-param-number-lsb 186 | 99 :non-registered-param-number-msb 187 | 100 :registered-param-number-lsb 188 | 101 :registered-param-number-msb 189 | 120 :all-sound-off 190 | 121 :reset-all-controllers 191 | 122 :local-controll-on-off 192 | 123 :all-notes-off 193 | 124 :omni-off 194 | 125 :omni-on 195 | 126 :poly-on-off 196 | 127 :poly-on}) 197 | 198 | (def ^:const sysex-status-key 199 | {SysexMessage/SPECIAL_SYSTEM_EXCLUSIVE :special-system-exclusive 200 | SysexMessage/SYSTEM_EXCLUSIVE :system-exclusive}) 201 | 202 | (def ^:const meta-message-type 203 | {:sequence-number 0x00 204 | :text 0x01 205 | :copyright-notice 0x02 206 | :track-name 0x03 207 | :instrument-name 0x04 208 | :lyrics 0x05 209 | :marker 0x06 210 | :cue-point 0x07 211 | :channel-prefix 0x20 212 | :end-of-track 0x2F 213 | :set-tempo 0x51 214 | :smpte-offset 0x54 215 | :time-signature 0x58 216 | :key-signature 0x59 217 | :sequencer-specific 0x7F}) 218 | 219 | (def ^:const meta-message-type-key 220 | {0x00 :sequence-number 221 | 0x01 :text 222 | 0x02 :copyright-notice 223 | 0x03 :track-name 224 | 0x04 :instrument-name 225 | 0x05 :lyrics 226 | 0x06 :marker 227 | 0x07 :cue-point 228 | 0x20 :channel-prefix 229 | 0x2F :end-of-track 230 | 0x51 :set-tempo 231 | 0x54 :smpte-offset 232 | 0x58 :time-signature 233 | 0x59 :key-signature 234 | 0x7F :sequencer-specific}) 235 | 236 | (def ^:const midi-file-type 237 | {:single 0 238 | :multi 1 239 | :collection 2}) 240 | 241 | (def ^:const midi-file-type-key 242 | {0 :single 243 | 1 :multi 244 | 2 :collection}) 245 | 246 | ;; =========================== MidiSystem ==================================== 247 | 248 | (defprotocol MidiSystemProcedures 249 | (soundbank [this]) 250 | (device [this])) 251 | 252 | (declare sequence tracks) 253 | 254 | (extend-protocol SequenceSource 255 | File 256 | (get-sequence [file] 257 | (MidiSystem/getSequence file)) 258 | InputStream 259 | (get-sequence [stream] 260 | (MidiSystem/getSequence stream)) 261 | URL 262 | (get-sequence [url] 263 | (MidiSystem/getSequence url)) 264 | Sequencer 265 | (get-sequence [sequencer] 266 | (.getSequence sequencer)) 267 | Sequence 268 | (get-sequence [sq] 269 | (sequence (division sq) (resolution sq) (tracks sq))) 270 | nil 271 | (ge-sequence [_] 272 | (throw (ex-info "nil is not allowed as a sequence source.")))) 273 | 274 | (extend-protocol SoundSystemProcedures 275 | File 276 | (file-format [file] 277 | (MidiSystem/getMidiFileFormat file)) 278 | InputStream 279 | (file-format [stream] 280 | (MidiSystem/getMidiFileFormat stream)) 281 | URL 282 | (file-format [url] 283 | (MidiSystem/getMidiFileFormat url))) 284 | 285 | (extend-protocol MidiSystemProcedures 286 | File 287 | (soundbank [file] 288 | (MidiSystem/getSoundbank file)) 289 | InputStream 290 | (file-format [stream] 291 | (MidiSystem/getMidiFileFormat stream)) 292 | (soundbank [stream] 293 | (MidiSystem/getSoundbank stream)) 294 | URL 295 | (soundbank [url] 296 | (MidiSystem/getSoundbank url)) 297 | MidiDeviceReceiver 298 | (device [receiver] 299 | (.getMidiDevice receiver)) 300 | MidiDeviceTransmitter 301 | (device [transmitter] 302 | (.getMidiDevice transmitter)) 303 | MidiDevice$Info 304 | (device [info] 305 | (MidiSystem/getMidiDevice info)) 306 | Synthesizer 307 | (soundbank [synth] 308 | (.getDefaultSoundbank synth)) 309 | SoundbankResource 310 | (soundbank [resource] 311 | (.getSoundbank resource))) 312 | 313 | (defn device-info 314 | ([] 315 | (MidiSystem/getMidiDeviceInfo)) 316 | ([^MidiDevice device] 317 | (.getDeviceInfo device))) 318 | 319 | (defn file-types 320 | (^ints [] 321 | (MidiSystem/getMidiFileTypes)) 322 | (^ints [sequence] 323 | (MidiSystem/getMidiFileTypes sequence))) 324 | 325 | (defn receiver 326 | ([] 327 | (MidiSystem/getReceiver)) 328 | ([provider] 329 | (get-receiver provider))) 330 | 331 | (defn sequencer 332 | ([] 333 | (MidiSystem/getSequencer)) 334 | ([connected?] 335 | (MidiSystem/getSequencer connected?))) 336 | 337 | (defn sequencer? [device] 338 | (instance? Sequencer device)) 339 | 340 | (defn synthesizer [] 341 | (MidiSystem/getSynthesizer)) 342 | 343 | (defn synthesizer? [device] 344 | (instance? Synthesizer device)) 345 | 346 | (defn transmitter 347 | ([] 348 | (MidiSystem/getTransmitter)) 349 | ([^MidiDevice device] 350 | (.getTransmitter device))) 351 | 352 | (extend-protocol Support 353 | Integer 354 | (supported [file-type] 355 | (MidiSystem/isFileTypeSupported file-type)) 356 | Long 357 | (supported [file-type] 358 | (MidiSystem/isFileTypeSupported file-type)) 359 | Short 360 | (supported [file-type] 361 | (MidiSystem/isFileTypeSupported file-type)) 362 | Byte 363 | (supported [file-type] 364 | (MidiSystem/isFileTypeSupported file-type)) 365 | Sequence 366 | (supported [sequence file-type] 367 | (MidiSystem/isFileTypeSupported file-type sequence))) 368 | 369 | (defmethod write! [Sequence File] 370 | [in out! file-type] 371 | (MidiSystem/write ^Sequence in ^long file-type ^File out!)) 372 | 373 | (defmethod write! [Sequence OutputStream] 374 | [in out! file-type] 375 | (MidiSystem/write ^Sequence in ^long file-type ^OutputStream out!)) 376 | 377 | ;; ============================= MidiDevice ================================ 378 | 379 | (extend-type MidiDevice$Info 380 | Info 381 | (info 382 | ([this] 383 | {:description (.getDescription this) 384 | :name (.getName this) 385 | :vendor (.getVendor this) 386 | :version (.getVersion this)}) 387 | ([this info-type] 388 | (case info-type 389 | :description (.getDescription this) 390 | :name (.getName this) 391 | :vendor (.getVendor this) 392 | :version (.getVersion this) 393 | nil))) 394 | SoundInfoProvider 395 | (sound-info [info] 396 | info)) 397 | 398 | (defn ^long max-receivers [^MidiDevice device] 399 | (.getMaxReceivers device)) 400 | 401 | (defn ^long max-transmitters [^MidiDevice device] 402 | (.getMaxTransmitters device)) 403 | 404 | (defn receiver? [device] 405 | (not= 0 (max-receivers device))) 406 | 407 | (defn transmitter? [device] 408 | (not= 0 (max-transmitters device))) 409 | 410 | (defn receivers [^MidiDevice device] 411 | (.getReceivers device)) 412 | 413 | (defn transmitters [^MidiDevice device] 414 | (.getTransmitters device)) 415 | 416 | (extend-type MidiDevice 417 | Info 418 | (info 419 | ([this] 420 | (merge {:class (simple-name (class this)) 421 | :status (if (.isOpen this) :open :closed) 422 | :micro-position (.getMicrosecondPosition this)} 423 | (info (.getDeviceInfo this)))) 424 | ([this info-type] 425 | (case info-type 426 | :class (simple-name (class this)) 427 | :status (if (.isOpen this) :open :closed) 428 | :micro-position (.getMicrosecondPosition this) 429 | (info (.getDeviceInfo this) info-type)))) 430 | Releaseable 431 | (release [this] 432 | (close! this) 433 | true) 434 | SoundInfoProvider 435 | (sound-info [device] 436 | (.getDeviceInfo device)) 437 | ReceiverProvider 438 | (get-receiver [device] 439 | (.getReceiver device)) 440 | Open 441 | (open! [device] 442 | (.open device) 443 | device) 444 | (open? [device] 445 | (.isOpen device)) 446 | Closeable 447 | (close! [device] 448 | (close! (transmitters device)) 449 | (close! (receivers device)) 450 | (.close device) 451 | device) 452 | Timing 453 | (micro-position [device] 454 | (.getMicrosecondPosition device))) 455 | 456 | ;; ============================= MidiChannel ================================ 457 | 458 | (extend-type MidiChannel 459 | Info 460 | (info 461 | ([this] 462 | {:mono (.getMono this) 463 | :mute (.getMute this) 464 | :omni (.getOmni this) 465 | :solo (.getSolo this) 466 | :program (.getProgram this)}) 467 | ([this info-type] 468 | (case info-type 469 | :mono (.getMono this) 470 | :mute (.getMute this) 471 | :omni (.getOmni this) 472 | :solo (.getSolo this) 473 | :program (.getProgram this) 474 | nil))) 475 | Reset 476 | (re-set! [channel!] 477 | (.resetAllControllers channel!) 478 | channel!) 479 | Program 480 | (program [channel] 481 | (.getProgram channel))) 482 | 483 | (defn off! 484 | ([^MidiChannel channel!] 485 | (.allNotesOff channel!) 486 | channel!) 487 | ([^MidiChannel channel! ^long note] 488 | (.noteOff channel! note) 489 | channel!) 490 | ([^MidiChannel channel! ^long note ^long velocity] 491 | (.noteOff channel! note velocity) 492 | channel!)) 493 | 494 | (defn on! [^MidiChannel channel! ^long note ^long velocity] 495 | (.noteOn channel! note velocity) 496 | channel!) 497 | 498 | (defn sound-off! [^MidiChannel channel!] 499 | (.allSoundOff channel!) 500 | channel!) 501 | 502 | (defn pressure 503 | (^long [^MidiChannel channel] 504 | (.getChannelPressure channel)) 505 | (^long [^MidiChannel channel ^long note] 506 | (.getPolyPressure channel note))) 507 | 508 | (defn pressure! [^MidiChannel channel! ^long pressure] 509 | (.setChannelPressure channel! pressure) 510 | channel!) 511 | 512 | (defn controller ^long [^MidiChannel channel controller] 513 | (.getController channel (get controller-type controller controller))) 514 | 515 | (defn mono [^MidiChannel channel] 516 | (.getMono channel)) 517 | 518 | (defn mono! 519 | ([^MidiChannel channel!] 520 | (.setMono channel! (not (.getMono channel!))) 521 | channel!) 522 | ([^MidiChannel channel! on] 523 | (.setMono channel! on) 524 | channel!)) 525 | 526 | (defn mute 527 | ([^MidiChannel channel] 528 | (.getMute channel)) 529 | ([^Sequencer sequencer track] 530 | (.getTrackMute sequencer track))) 531 | 532 | (defn mute! 533 | ([^MidiChannel channel!] 534 | (.setMute channel! (not (.getMute channel!))) 535 | channel!) 536 | ([this! arg] 537 | (if (instance? MidiChannel this!) 538 | (.setMute ^MidiChannel this! arg) 539 | (.setTrackMute ^Sequencer this! arg (not (.getTrackMute ^Sequencer this! arg)))) 540 | this!) 541 | ([^Sequencer sequencer! track mute] 542 | (.setTrackMute sequencer! track mute) 543 | sequencer!)) 544 | 545 | (defn omni [^MidiChannel channel] 546 | (.getOmni channel)) 547 | 548 | (defn omni! 549 | ([^MidiChannel channel!] 550 | (.setOmni channel! (not (.getOmni channel!))) 551 | channel!) 552 | ([^MidiChannel channel! on] 553 | (.setOmni channel! on) 554 | channel!)) 555 | 556 | (defn bend ^long [^MidiChannel channel] 557 | (.getPitchBend channel)) 558 | 559 | (defn bend! 560 | ([^MidiChannel channel!] 561 | (.setPitchBend channel! (not (.getPitchBend channel!))) 562 | channel!) 563 | ([^MidiChannel channel! bend] 564 | (.setPitchBend channel! bend) 565 | channel!)) 566 | 567 | (defn solo 568 | ([^MidiChannel channel] 569 | (.getSolo channel)) 570 | ([^Sequencer sequencer track] 571 | (.getTrackSolo sequencer track))) 572 | 573 | (defn solo! 574 | ([^MidiChannel channel!] 575 | (.setSolo channel! (not (.getSolo channel!))) 576 | channel!) 577 | ([this! arg] 578 | (if (instance? MidiChannel this!) 579 | (.setSolo ^MidiChannel this! arg) 580 | (.setTrackSolo ^Sequencer this! arg (not (.getTrackSolo ^Sequencer this! arg)))) 581 | this!) 582 | ([^Sequencer sequencer! track solo] 583 | (.setTrackSolo sequencer! track solo) 584 | sequencer!)) 585 | 586 | (defn control! 587 | ([^MidiChannel channel! on] 588 | (.localControl channel! on)) 589 | ([^MidiChannel channel! controller ^long val] 590 | (.controlChange channel! (get controller-type controller controller) val) 591 | channel!)) 592 | 593 | (defn program! 594 | ([^MidiChannel channel! ^long bank ^long program] 595 | (.programChange channel! bank program) 596 | channel!) 597 | ([^MidiChannel channel! ^long program] 598 | (.programChange channel! program) 599 | channel!)) 600 | 601 | ;; ============================= Receiver ================================ 602 | 603 | (extend-type Receiver 604 | Info 605 | (info 606 | ([this] 607 | {:class (simple-name (class this)) 608 | :id (System/identityHashCode this)}) 609 | ([this info-type] 610 | (case info-type 611 | :class (simple-name (class this)) 612 | :id (System/identityHashCode this) 613 | nil))) 614 | Releaseable 615 | (release [this] 616 | (close! this) 617 | true)) 618 | 619 | (deftype ReceiverFunction [f] 620 | Receiver 621 | (send [_ message timestamp] 622 | (f message timestamp))) 623 | 624 | (extend-type IFn 625 | ReceiverProvider 626 | (get-receiver [f] 627 | (->ReceiverFunction f))) 628 | 629 | (defn send! 630 | ([^Receiver receiver! ^MidiMessage message ^long timestamp] 631 | (.send receiver! message timestamp) 632 | receiver!) 633 | ([^Receiver receiver! ^MidiMessage message] 634 | (send! receiver! message -1))) 635 | 636 | ;; ============================= Transmitter ================================ 637 | 638 | (extend-type Transmitter 639 | Info 640 | (info 641 | ([this] 642 | {:class (simple-name (class this)) 643 | :id (System/identityHashCode this)}) 644 | ([this info-type] 645 | (case info-type 646 | :class (simple-name (class this)) 647 | :id (System/identityHashCode this) 648 | nil))) 649 | ReceiverProvider 650 | (get-receiver [transmitter] 651 | (.getReceiver transmitter)) 652 | Releaseable 653 | (release [this] 654 | (close! this) 655 | true)) 656 | 657 | (defmethod connect! [Transmitter Receiver] 658 | [^Transmitter transmitter! receiver] 659 | (.setReceiver transmitter! receiver) 660 | transmitter!) 661 | 662 | (defmethod connect! [MidiDevice Receiver] 663 | [device receiver] 664 | (let [tr (transmitter device)] 665 | (.setReceiver ^Transmitter tr receiver) 666 | tr)) 667 | 668 | (defmethod connect! [Transmitter MidiDevice] 669 | [^Transmitter transmitter! device] 670 | (let [rc (receiver device)] 671 | (.setReceiver transmitter! rc) 672 | transmitter!)) 673 | 674 | (defmethod connect! [MidiDevice MidiDevice] 675 | [in out] 676 | (let [tr (transmitter in) 677 | rc (receiver out)] 678 | (.setReceiver ^Transmitter tr rc) 679 | tr)) 680 | 681 | ;; ============================= Sequencer ================================ 682 | 683 | (deftype MetaEventListenerFunction [f ^int meta-type] 684 | Info 685 | (info [_] 686 | {:fn f 687 | :type (get meta-message-type-key meta-type meta-type)}) 688 | (info [_ info-type] 689 | (case info-type 690 | :fn f 691 | :type (get meta-message-type-key meta-type meta-type) 692 | nil)) 693 | MetaEventListener 694 | (meta [_ message] 695 | (when (or (< meta-type 0) (= meta-type (.getType message))) 696 | (f message)))) 697 | 698 | (deftype MetaEventListenerWrapper [^MetaEventListener f ^int meta-type] 699 | Info 700 | (info [_] 701 | {:fn f 702 | :type (get meta-message-type-key meta-type meta-type)}) 703 | (info [_ info-type] 704 | (case info-type 705 | :fn f 706 | :type (get meta-message-type-key meta-type meta-type) 707 | nil)) 708 | MetaEventListener 709 | (meta [_ message] 710 | (when (or (< meta-type 0) (= meta-type (.getType message))) 711 | (.meta f message)))) 712 | 713 | (defn meta-listener 714 | ([meta-type f] 715 | (let [meta-type (get meta-message-type meta-type meta-type)] 716 | (if (instance? MetaEventListener f) 717 | (->MetaEventListenerWrapper f meta-type) 718 | (->MetaEventListenerFunction f meta-type)))) 719 | ([f] 720 | (if (instance? MetaEventListener f) 721 | f 722 | (meta-listener -1 f)))) 723 | 724 | (deftype ControllerEventListenerFunction [f] 725 | Info 726 | (info [_] 727 | {:fn f}) 728 | (info [_ info-type] 729 | (case info-type 730 | :fn f 731 | nil)) 732 | ControllerEventListener 733 | (controlChange [_ message] 734 | (f message))) 735 | 736 | (defn ctrl-listener [f] 737 | (if (instance? ControllerEventListener f) 738 | f 739 | (->ControllerEventListenerFunction f))) 740 | 741 | (extend-type Sequencer 742 | Broadcast 743 | (listen! 744 | ([sequencer! listener] 745 | (let [listener (if (instance? MetaEventListener listener) 746 | listener 747 | (meta-listener listener))] 748 | (.addMetaEventListener sequencer! listener) 749 | listener)) 750 | ([sequencer! listener selection] 751 | (if (or (number? selection) (keyword? selection)) 752 | (let [listener (meta-listener selection listener)] 753 | (.addMetaEventListener sequencer! listener) 754 | listener) 755 | (let [listener (if (instance? ControllerEventListener listener) 756 | listener 757 | (ctrl-listener listener))] 758 | (.addControllerEventListener sequencer! listener 759 | (if (sequential? selection) 760 | (int-array selection) 761 | selection)) 762 | listener)))) 763 | (ignore! 764 | ([sequencer! listener] 765 | (.removeMetaEventListener sequencer! listener) 766 | sequencer!) 767 | ([sequencer! listener controllers] 768 | (.removeControllerEventListener sequencer! listener 769 | (if (sequential? controllers) 770 | (int-array (map (fn [c] 771 | (get controller-type c c)) 772 | controllers)) 773 | controllers)))) 774 | Timing 775 | (micro-length [sequencer] 776 | (.getMicrosecondLength sequencer)) 777 | (micro-position [sequencer] 778 | (.getMicrosecondPosition sequencer)) 779 | (micro-position! [sequencer! microseconds] 780 | (.setMicrosecondPosition sequencer! microseconds) 781 | sequencer!) 782 | Activity 783 | (running? [sequencer] 784 | (.isRunning sequencer)) 785 | (start! [sequencer!] 786 | (.start sequencer!) 787 | sequencer!) 788 | (stop! [sequencer!] 789 | (.stop sequencer!) 790 | sequencer!) 791 | Tick 792 | (ticks [sequencer] 793 | (.getTickLength sequencer))) 794 | 795 | (extend-protocol SequenceSource 796 | InputStream 797 | (set-sequence [stream sequencer!] 798 | (.setSequence ^Sequencer sequencer! stream)) 799 | Sequence 800 | (set-sequence [s sequencer!] 801 | (.setSequence ^Sequencer sequencer! s))) 802 | 803 | (defn sequence! 804 | ([^Sequencer sequencer!] 805 | (.setSequence sequencer! (.getSequence sequencer!)) 806 | sequencer!) 807 | ([sequencer! source] 808 | (set-sequence source sequencer!) 809 | sequencer!)) 810 | 811 | (defn loop-count ^long [^Sequencer sequencer] 812 | (.getLoopCount sequencer)) 813 | 814 | (defn loop-count! [^Sequencer sequencer! count] 815 | (.setLoopCount sequencer! count) 816 | sequencer!) 817 | 818 | (defn end-point ^long [^Sequencer sequencer] 819 | (.getLoopEndPoint sequencer)) 820 | 821 | (defn end-point! [^Sequencer sequencer! tick] 822 | (.setLoopEndPoint sequencer! tick) 823 | sequencer!) 824 | 825 | (defn start-point ^long [^Sequencer sequencer] 826 | (.getLoopStartPoint sequencer)) 827 | 828 | (defn start-point! [^Sequencer sequencer! tick] 829 | (.setLoopStartPoint sequencer! tick) 830 | sequencer!) 831 | 832 | (defn master-sync [^Sequencer sequencer] 833 | (get sync-mode-key (.getMasterSyncMode sequencer) 834 | (throw (ex-info "Unknown sync mode." {:type :sound-error 835 | :mode (.getMasterSyncMode sequencer) 836 | :supported (keys sync-mode-key)})))) 837 | 838 | (defn master-sync! [^Sequencer sequencer! sync] 839 | (.setMasterSyncMode sequencer! (get sync-mode sync sync)) 840 | sequencer!) 841 | 842 | (defn master-modes [^Sequencer sequencer] 843 | (map sync-mode-key (.getMasterSyncModes sequencer))) 844 | 845 | (defn slave-sync [^Sequencer sequencer] 846 | (get sync-mode-key (.getSlaveSyncMode sequencer) 847 | (throw (ex-info "Unknown sync mode." {:type :sound-error 848 | :mode (.getSlaveSyncMode sequencer) 849 | :supported (keys sync-mode-key)})))) 850 | 851 | (defn slave-sync! [^Sequencer sequencer! sync] 852 | (.setSlaveSyncMode sequencer! (get sync-mode sync sync)) 853 | sequencer!) 854 | 855 | (defn slave-modes [^Sequencer sequencer] 856 | (get sync-mode-key (.getSlaveSyncModes sequencer))) 857 | 858 | (defn tempo-factor ^double [^Sequencer sequencer] 859 | (.getTempoFactor sequencer)) 860 | 861 | (defn tempo-factor! [^Sequencer sequencer! factor] 862 | (.setTempoFactor sequencer! factor) 863 | sequencer!) 864 | 865 | (defn tempo-bpm ^double [^Sequencer sequencer] 866 | (.getTempoInBPM sequencer)) 867 | 868 | (defn tempo-bpm! [^Sequencer sequencer! bpm] 869 | (.setTempoInBPM sequencer! bpm) 870 | sequencer!) 871 | 872 | (defn tempo-mpq ^double [^Sequencer sequencer] 873 | (.getTempoInMPQ sequencer)) 874 | 875 | (defn tempo-mpq! [^Sequencer sequencer! mpq] 876 | (.setTempoInMPQ sequencer! mpq) 877 | sequencer!) 878 | 879 | (defn tick-position ^long [^Sequencer sequencer] 880 | (.getTickPosition sequencer)) 881 | 882 | (defn tick-position! [^Sequencer sequencer! position] 883 | (.setTickPosition sequencer! position) 884 | sequencer!) 885 | 886 | (defn recording? [^Sequencer sequencer] 887 | (.isRecording sequencer)) 888 | 889 | (defn rec! 890 | ([^Sequencer sequencer!] 891 | (.startRecording sequencer!) 892 | sequencer!) 893 | ([^Sequencer sequencer! track channel] 894 | (.recordEnable sequencer! track channel) 895 | sequencer!)) 896 | 897 | (defn stop-rec! 898 | ([^Sequencer sequencer!] 899 | (.stopRecording sequencer!) 900 | sequencer!) 901 | ([^Sequencer sequencer! track] 902 | (.recordDisable sequencer! track) 903 | sequencer!)) 904 | 905 | ;; =================== Synthesizer ===================================================== 906 | 907 | (extend-type Synthesizer 908 | Instruments 909 | (instruments [synth] 910 | (.getLoadedInstruments synth)) 911 | Load 912 | (load-instruments [source synth!] 913 | (every? identity (map #(.loadInstrument ^Synthesizer synth! %) 914 | (.getAvailableInstruments source)))) 915 | (unload-instruments [source synth!] 916 | (let [instruments (.getLoadedInstruments source)] 917 | (dotimes [i (alength instruments)] 918 | (.unloadInstrument ^Synthesizer synth! (aget instruments i))) 919 | synth!)) 920 | Available 921 | (available [synth] 922 | (.getAvailableInstruments synth)) 923 | Channels 924 | (channels [synth] 925 | (.getChannels synth))) 926 | 927 | (defn latency ^long [^Synthesizer synth] 928 | (.getLatency synth)) 929 | 930 | (defn max-polyphony ^long [^Synthesizer synth] 931 | (.getMaxPolyphony synth)) 932 | 933 | (defn voice-status [^Synthesizer synth] 934 | (.getVoiceStatus synth)) 935 | 936 | (defn load! 937 | ([synth!] 938 | (load-instruments synth! synth!)) 939 | ([synth! source] 940 | (if (seqable? source) 941 | (every? identity (map #(load-instruments % synth!) source)) 942 | (load-instruments source synth!))) 943 | ([^Synthesizer synth! soundbank patches] 944 | (.loadInstruments synth! soundbank 945 | (if (sequential? patches) (into-array Patch patches) patches)))) 946 | 947 | (defn unload! 948 | ([synth!] 949 | (unload-instruments synth! synth!) 950 | synth!) 951 | ([synth! source] 952 | (if (seqable? source) 953 | (doseq [instrument source] 954 | (unload-instruments source synth!)) 955 | (unload-instruments source synth!)) 956 | synth!)) 957 | 958 | (defn remap! [^Synthesizer synth! from to] 959 | (.remapInstrument synth! from to)) 960 | 961 | ;; ============================= Soundbank ================================ 962 | 963 | (extend-type Soundbank 964 | Info 965 | (info 966 | ([this] 967 | {:class (simple-name (class this)) 968 | :description (.getDescription this) 969 | :name (.getName this) 970 | :vendor (.getVendor this) 971 | :version (.getVersion this)}) 972 | ([this info-type] 973 | (case info-type 974 | :class (simple-name (class this)) 975 | :description (.getDescription this) 976 | :name (.getName this) 977 | :vendor (.getVendor this) 978 | :version (.getVersion this) 979 | nil))) 980 | Instruments 981 | (instruments [soundbank] 982 | (.getInstruments soundbank)) 983 | Load 984 | (load-instruments [soundbank synth!] 985 | (.loadAllInstruments ^Synthesizer synth! soundbank)) 986 | (unload-instruments [soundbank synth!] 987 | (.unloadAllInstruments ^Synthesizer synth! soundbank) 988 | synth!) 989 | Support 990 | (supported [soundbank synth] 991 | (.isSoundbankSupported ^Synthesizer synth soundbank))) 992 | 993 | (defn instrument 994 | ([^Soundbank soundbank patch] 995 | (.getInstrument soundbank patch))) 996 | 997 | (defn resources [^Soundbank soundbank] 998 | (.getResources soundbank)) 999 | 1000 | ;; =================== SoundbankResource ========================================== 1001 | 1002 | (extend-type SoundbankResource 1003 | Info 1004 | (info 1005 | ([this] 1006 | {:name (.getName this)}) 1007 | ([this info-type] 1008 | (case info-type 1009 | :name (.getName this) 1010 | nil))) 1011 | Data 1012 | (data [resource] 1013 | (.getData resource))) 1014 | 1015 | (defn data-class [^SoundbankResource resource] 1016 | (.getDataClass resource)) 1017 | 1018 | ;; =================== Instrument ====================================================== 1019 | 1020 | (extend-type Instrument 1021 | Load 1022 | (load-instruments [instrument synth!] 1023 | (.loadInstrument ^Synthesizer synth! instrument)) 1024 | (unload-instruments [instrument synth!] 1025 | (.unloadInstrument ^Synthesizer synth! instrument) 1026 | synth!)) 1027 | 1028 | ;; =================== Patch =========================================================== 1029 | 1030 | (extend-type Patch 1031 | Info 1032 | (info 1033 | ([this] 1034 | {:bank (.getBank this) 1035 | :program (.getProgram this)}) 1036 | ([this info-type] 1037 | (case info-type 1038 | :bank (.getBank this) 1039 | :program (.getProgram this) 1040 | nil))) 1041 | Program 1042 | (program [patch] 1043 | (.getProgram patch))) 1044 | 1045 | (defn patch 1046 | ([^Instrument instrument] 1047 | (.getPatch instrument)) 1048 | ([bank program] 1049 | (Patch. bank program))) 1050 | 1051 | (defn bank ^long [^Patch patch] 1052 | (.getBank patch)) 1053 | 1054 | ;; =================== MidiFileFormat ================================================== 1055 | 1056 | (extend-type MidiFileFormat 1057 | Info 1058 | (info 1059 | ([this] 1060 | (into {:type (itype this)} 1061 | (map (fn [[k v]] [(name-key k) v]) (properties this)))) 1062 | ([this info-type] 1063 | (case info-type 1064 | :type (itype this) 1065 | (property this (key-name info-type))))) 1066 | Timing 1067 | (micro-length [mff] 1068 | (.getMicrosecondLength mff)) 1069 | (division [mff] 1070 | (.getDivisionType mff)) 1071 | (resolution [mff] 1072 | (.getResolution mff)) 1073 | Format 1074 | (property [mff key] 1075 | (.getProperty mff (name key))) 1076 | (properties [mff] 1077 | (.properties mff)) 1078 | (byte-length [mff] 1079 | (.getByteLength mff)) 1080 | Type 1081 | (itype [mff] 1082 | (let [mff-type (.getType mff)] 1083 | (get midi-file-type mff-type mff-type)))) 1084 | 1085 | (defn midi-file-format 1086 | ([type division resolution bytes microseconds] 1087 | (MidiFileFormat. type (get timing-type division division) 1088 | resolution bytes microseconds)) 1089 | ([type division resolution bytes microseconds properties] 1090 | (MidiFileFormat. type (get timing-type division division) 1091 | resolution bytes microseconds properties))) 1092 | 1093 | ;; =================== MidiMessage ===================================================== 1094 | 1095 | (extend-type MidiMessage 1096 | Info 1097 | (info 1098 | ([this] 1099 | {:status (.getStatus this) 1100 | :length (.getLength this) 1101 | :bytes (.getMessage this)}) 1102 | ([this info-type] 1103 | (case info-type 1104 | :status (.getStatus this) 1105 | :length (.getLength this) 1106 | :bytes (.getMessage this) 1107 | nil))) 1108 | Event 1109 | (event [message tick] 1110 | (MidiEvent. message tick))) 1111 | 1112 | (defn message-bytes [^MidiMessage message] 1113 | (.getMessage message)) 1114 | 1115 | (defn message-length ^long [^MidiMessage message] 1116 | (.getLength message)) 1117 | 1118 | (defn status [^MidiMessage message] 1119 | (.getStatus message)) 1120 | 1121 | ;; =================== MetaMessage ===================================================== 1122 | 1123 | (defn decode-integer [^bytes data] 1124 | (case (alength data) 1125 | 1 (.get (ByteBuffer/wrap data) 0) 1126 | 2 (.getShort (ByteBuffer/wrap data) 0) 1127 | 3 (bit-or (bit-and (aget data 2) 0xFF) 1128 | (bit-shift-left (bit-and (aget data 1) 0xFF) 8) 1129 | (bit-shift-left (bit-and (aget data 0) 0xFF) 16)) 1130 | 4 (.getInt (ByteBuffer/wrap data) 0) 1131 | 8 (.getLong (ByteBuffer/wrap data) 0) 1132 | (throw (ex-info "Not an integer data buffer." {:type :sound-error 1133 | :length (alength data) :supported #{1 2 4 8}})))) 1134 | 1135 | (defn decode-smpte [^bytes data] 1136 | {:hours (aget data 0) 1137 | :minutes (aget data 1) 1138 | :seconds (aget data 2) 1139 | :frames (aget data 3) 1140 | :fractional-frames (aget data 4)}) 1141 | 1142 | (defn decode-time-signature [^bytes data] 1143 | {:numerator (aget data 0) 1144 | :denumerator (Math/pow 2 (aget data 1)) 1145 | :ticks-per-beat (aget data 2) 1146 | :notes-per-beat (aget data 3)}) 1147 | 1148 | (defn decode-key-signature [^bytes data] 1149 | {:mode (case (aget data 1) 1150 | 0 :major 1151 | 1 :minor 1152 | :unknown) 1153 | :key (aget data 0)}) 1154 | 1155 | (defn decode-vendor-specific [^bytes data] 1156 | (or (< 0 (alength data)) 1157 | (if (= 0 (aget data 0)) 1158 | {:vendor-id (decode-integer (Arrays/copyOfRange data 0 3)) 1159 | :data (Arrays/copyOfRange data 3 (alength data))} 1160 | {:vendor-id (aget data 0) 1161 | :data (Arrays/copyOfRange data 1 (alength data))}))) 1162 | 1163 | (extend-type MetaMessage 1164 | Info 1165 | (info 1166 | ([this] 1167 | {:status (.getStatus this) 1168 | :length (.getLength this) 1169 | :bytes (.getMessage this) 1170 | :type (itype this) 1171 | :data (decode this)}) 1172 | ([this info-type] 1173 | (case info-type 1174 | :status (.getStatus this) 1175 | :length (.getLength this) 1176 | :bytes (.getMessage this) 1177 | :type (itype this) 1178 | :data (decode this) 1179 | nil))) 1180 | Type 1181 | (itype [message] 1182 | (let [mt (.getType message)] 1183 | (get meta-message-type-key mt mt))) 1184 | Data 1185 | (data [message] 1186 | (.getData message)) 1187 | (message! [mm! type data] 1188 | (.setMessage mm! type data (alength data)) 1189 | mm!) 1190 | (message! [mm! type data length] 1191 | (.setMessage mm! type data length) 1192 | mm!) 1193 | Code 1194 | (decode [message] 1195 | (let [data (.getData message)] 1196 | (case (.getType message) 1197 | 0x00 (decode-integer data) 1198 | 0x01 (String. data) 1199 | 0x02 (String. data) 1200 | 0x03 (String. data) 1201 | 0x04 (String. data) 1202 | 0x05 (String. data) 1203 | 0x06 (String. data) 1204 | 0x07 (String. data) 1205 | 0x20 (aget data 0) 1206 | 0x2F nil 1207 | 0x51 (decode-integer data) 1208 | 0x54 (decode-smpte data) 1209 | 0x58 (decode-time-signature data) 1210 | 0x59 (decode-key-signature data) 1211 | 0x7F data 1212 | 0xFF (decode-vendor-specific data) 1213 | data)))) 1214 | 1215 | (defn meta-message 1216 | ([] 1217 | (MetaMessage.)) 1218 | ([type ^bytes data] 1219 | (MetaMessage. type data (alength data))) 1220 | ([type data length] 1221 | (MetaMessage. type data length))) 1222 | 1223 | ;; =================== ShortMessage ===================================================== 1224 | 1225 | (defn channel [^ShortMessage message] 1226 | (.getChannel message)) 1227 | 1228 | (defn command [^ShortMessage message] 1229 | (.getCommand message)) 1230 | 1231 | (defn data1 ^long [^ShortMessage message] 1232 | (.getData1 message)) 1233 | 1234 | (defn data2 ^long [^ShortMessage message] 1235 | (.getData2 message)) 1236 | 1237 | (defn short-little-endian 1238 | (^long [^long data1 ^long data2] 1239 | (+ (* 128 data2) data1)) 1240 | (^long [^bytes data] 1241 | (+ (* 128 (aget data 1)) (aget data 0)))) 1242 | 1243 | (defn decode-controller [^long controller ^long value] 1244 | (let [controller-type (controller-type controller)] 1245 | (merge {:controller controller 1246 | :value value} 1247 | (if controller-type 1248 | {:control controller-type} 1249 | nil)))) 1250 | 1251 | (extend-type ShortMessage 1252 | Info 1253 | (info 1254 | ([message] 1255 | (let [decoded-data (decode message)] 1256 | (merge {:channel (channel message) 1257 | :command (get command-type-key (command message))} 1258 | (if (map? decoded-data) 1259 | decoded-data 1260 | {:data decoded-data})))) 1261 | ([message info-type] 1262 | (case info-type 1263 | :status (status message) 1264 | :length (message-length message) 1265 | :bytes (message-bytes message) 1266 | :channel (channel message) 1267 | :command (get command-type-key (command message)) 1268 | :data1 (data1 message) 1269 | :data2 (data2 message) 1270 | :data (decode message) 1271 | nil))) 1272 | Data 1273 | (message! 1274 | ([sm! status] 1275 | (.setMessage sm! (get command-type status status)) 1276 | sm!) 1277 | ([sm! status data] 1278 | (.setMessage sm! (get command-type status status) (aget ^bytes data 0) (aget ^bytes data 1)) 1279 | sm!) 1280 | ([sm! status data1 data2] 1281 | (.setMessage sm! (get command-type status status) data1 data2) 1282 | sm!) 1283 | ([sm! command channel data1 data2] 1284 | (.setMessage sm! (get command-type command command) channel data1 data2) 1285 | sm!)) 1286 | Code 1287 | (decode [message] 1288 | (case (.getCommand message) 1289 | 128 {:key (data1 message) :velocity (data2 message)} 1290 | 144 {:key (data1 message) :velocity (data2 message)} 1291 | 160 {:key (data1 message) :velocity (data2 message)} 1292 | 176 (decode-controller (data1 message) (data2 message)) 1293 | 192 (data1 message) 1294 | 208 (data1 message) 1295 | 224 (short-little-endian (data1 message) (data2 message)) 1296 | 242 (short-little-endian (data1 message) (data2 message)) 1297 | 243 (data1 message) 1298 | 246 :tune 1299 | nil))) 1300 | 1301 | (defn short-message 1302 | ([] 1303 | (ShortMessage.)) 1304 | ([status] 1305 | (ShortMessage. (get command-type status status))) 1306 | ([status data1 data2] 1307 | (ShortMessage. (get command-type status status) data1 data2)) 1308 | ([command channel data1 data2] 1309 | (ShortMessage. (get command-type command command) channel data1 data2))) 1310 | 1311 | ;; =================== SysexMessage ===================================================== 1312 | 1313 | (extend-type SysexMessage 1314 | Info 1315 | (info 1316 | ([message] 1317 | {:status (status message) 1318 | :length (message-length message) 1319 | :bytes (message-bytes message) 1320 | :data (decode message)}) 1321 | ([message info-type] 1322 | (case info-type 1323 | :status (status message) 1324 | :length (message-length message) 1325 | :bytes (message-bytes message) 1326 | (get (decode message) info-type nil)))) 1327 | Data 1328 | (data [message] 1329 | (.getData message)) 1330 | (message! 1331 | ([sm! data] 1332 | (.setMessage sm! data (alength ^bytes data)) 1333 | sm!) 1334 | ([sm! arg1 arg2] 1335 | (if (integer? arg2) 1336 | (.setMessage sm! arg1 arg2) 1337 | (message! sm! arg1 arg2 (alength ^bytes arg2))) 1338 | sm!) 1339 | ([sm! status data length] 1340 | (.setMessage sm! (get command-type status status) data length) 1341 | sm!)) 1342 | Code 1343 | (decode [message] 1344 | (decode-vendor-specific (.getData message)))) 1345 | 1346 | (defn sysex-message 1347 | ([] 1348 | (SysexMessage.)) 1349 | ([data length] 1350 | (SysexMessage. data length)) 1351 | ([status data length] 1352 | (SysexMessage. (get command-type status status) data length))) 1353 | 1354 | ;; =================== MidiEvent ====================================================== 1355 | 1356 | (extend-type MidiEvent 1357 | Info 1358 | (info 1359 | ([this] 1360 | (assoc (info (.getMessage this)) 1361 | :tick (.getTick this))) 1362 | ([this info-type] 1363 | (case info-type 1364 | :tick (.getTick this) 1365 | (info (.getMessage this)))))) 1366 | 1367 | (defn message [^MidiEvent event] 1368 | (.getMessage event)) 1369 | 1370 | (defn tick [^MidiEvent event] 1371 | (.getTick event)) 1372 | 1373 | (defn tick! [^MidiEvent event! tick] 1374 | (.setTick event! tick) 1375 | event!) 1376 | 1377 | ;; =================== Sequence ======================================================== 1378 | 1379 | (extend-type Sequence 1380 | Info 1381 | (info 1382 | ([this] 1383 | {:ticks (.getTickLength this)}) 1384 | ([this info-type] 1385 | (case info-type 1386 | :id (System/identityHashCode this) 1387 | :ticks (.getTickLength this) 1388 | nil))) 1389 | Timing 1390 | (micro-length [this] 1391 | (.getMicrosecondLength this)) 1392 | (division [this] 1393 | (.getDivisionType this)) 1394 | (resolution [this] 1395 | (.getResolution this)) 1396 | Tick 1397 | (ticks [this] 1398 | (.getTickLength this))) 1399 | 1400 | (defn sequence 1401 | ([source] 1402 | (get-sequence source)) 1403 | ([division ^long resolution] 1404 | (Sequence. (get timing-type division division) resolution)) 1405 | ([division ^long resolution ^long num-tracks] 1406 | (Sequence. (get timing-type division division) resolution num-tracks))) 1407 | 1408 | (defn tracks [^Sequence sequence] 1409 | (.getTracks sequence)) 1410 | 1411 | (defn patches [^Sequence sequence] 1412 | (.getPatchList sequence)) 1413 | 1414 | (defn track! [^Sequence sequence!] 1415 | (.createTrack sequence!)) 1416 | 1417 | (defn delete! [^Sequence sequence! track] 1418 | (.deleteTrack sequence! track)) 1419 | 1420 | ;; =================== Track ========================================== 1421 | 1422 | (extend-type Track 1423 | Info 1424 | (info 1425 | ([this] 1426 | {:size (.size this) 1427 | :ticks (.ticks this)}) 1428 | ([this info-type] 1429 | (case info-type 1430 | :size (.size this) 1431 | :ticks (.ticks this) 1432 | nil))) 1433 | Event 1434 | (event [track i] 1435 | (.get track i)) 1436 | Tick 1437 | (ticks [track] 1438 | (.ticks track))) 1439 | 1440 | (defn add! [^Track track! event] 1441 | (.add track! event)) 1442 | 1443 | (defn remove! [^Track track! event] 1444 | (.remove track! event)) 1445 | 1446 | (defn events ^long [^Track track] 1447 | (.size track)) 1448 | 1449 | ;; =================== VoiceStatus ========================================== 1450 | 1451 | (defn voice-status-info 1452 | ([^VoiceStatus status] 1453 | (into {:class "VoiceStatus"} 1454 | (if (active? status) 1455 | (remove nil? 1456 | (map (fn [^Field field] 1457 | (try 1458 | (vector (name-key (.getName field)) (.get field status)) 1459 | (catch IllegalAccessException e nil))) 1460 | (.getDeclaredFields VoiceStatus))) 1461 | [[:active false]]))) 1462 | ([^VoiceStatus status key] 1463 | (if (active? status) 1464 | (case key 1465 | :class "VoiceStatus" 1466 | :active (.active status) 1467 | :bank (.bank status) 1468 | :channel (.channel status) 1469 | :note (.note status) 1470 | :program (.program status) 1471 | :volume (.volume status) 1472 | (try 1473 | (.get (.getDeclaredField (class status) (key-name key)) status) 1474 | (catch NoSuchFieldException e nil) 1475 | (catch IllegalAccessException e nil))) 1476 | (if (= key :active) false nil)))) 1477 | 1478 | (extend-type VoiceStatus 1479 | Info 1480 | (info 1481 | ([this] 1482 | (voice-status-info this)) 1483 | ([this info-type] 1484 | (voice-status-info this info-type))) 1485 | Activity 1486 | (active? [status] 1487 | (.active status))) 1488 | 1489 | ;; =================== Sequential info for sampled arrays ============================== 1490 | 1491 | (extend-array-info (Class/forName "[Ljavax.sound.midi.MidiDevice$Info;") ) 1492 | (extend-array-info (Class/forName "[Ljavax.sound.midi.MidiDevice;")) 1493 | (extend-array-info (Class/forName "[Ljavax.sound.midi.MidiChannel;")) 1494 | (extend-array-info (Class/forName "[Ljavax.sound.midi.Receiver;")) 1495 | (extend-array-info (Class/forName "[Ljavax.sound.midi.Transmitter;")) 1496 | (extend-array-info (Class/forName "[Ljavax.sound.midi.Soundbank;")) 1497 | (extend-array-info (Class/forName "[Ljavax.sound.midi.SoundbankResource;")) 1498 | (extend-array-info (Class/forName "[Ljavax.sound.midi.Instrument;")) 1499 | (extend-array-info (Class/forName "[Ljavax.sound.midi.Patch;")) 1500 | (extend-array-info (Class/forName "[Ljavax.sound.midi.MidiFileFormat;")) 1501 | (extend-array-info (Class/forName "[Ljavax.sound.midi.Sequencer$SyncMode;")) 1502 | (extend-array-info (Class/forName "[Ljavax.sound.midi.Sequence;")) 1503 | (extend-array-info (Class/forName "[Ljavax.sound.midi.VoiceStatus;")) 1504 | (extend-array-info (Class/forName "[Ljavax.sound.midi.MidiEvent;")) 1505 | (extend-array-info (Class/forName "[Ljavax.sound.midi.MidiMessage;")) 1506 | (extend-array-info (Class/forName "[Ljavax.sound.midi.MetaMessage;")) 1507 | (extend-array-info (Class/forName "[Ljavax.sound.midi.ShortMessage;")) 1508 | (extend-array-info (Class/forName "[Ljavax.sound.midi.SysexMessage;")) 1509 | (extend-array-info (Class/forName "[Ljavax.sound.midi.Track;")) 1510 | 1511 | ;; =================== User friendly printing ========================================== 1512 | 1513 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.MidiDevice$Info;") 1514 | [this w] 1515 | (print-method (seq this) w)) 1516 | 1517 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.MidiDevice;") 1518 | [this w] 1519 | (print-method (seq this) w)) 1520 | 1521 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.MidiChannel;") 1522 | [this w] 1523 | (print-method (seq this) w)) 1524 | 1525 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.Receiver;") 1526 | [this w] 1527 | (print-method (seq this) w)) 1528 | 1529 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.Transmitter;") 1530 | [this w] 1531 | (print-method (seq this) w)) 1532 | 1533 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.Soundbank;") 1534 | [this w] 1535 | (print-method (seq this) w)) 1536 | 1537 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.SoundbankResource;") 1538 | [this w] 1539 | (print-method (seq this) w)) 1540 | 1541 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.Instrument;") 1542 | [this w] 1543 | (print-method (seq this) w)) 1544 | 1545 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.Patch;") 1546 | [this w] 1547 | (print-method (seq this) w)) 1548 | 1549 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.MidiFileFormat;") 1550 | [this w] 1551 | (print-method (seq this) w)) 1552 | 1553 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.Sequencer$SyncMode;") 1554 | [this w] 1555 | (print-method (seq this) w)) 1556 | 1557 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.Sequence;") 1558 | [this w] 1559 | (print-method (seq this) w)) 1560 | 1561 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.VoiceStatus;") 1562 | [this w] 1563 | (print-method (seq this) w)) 1564 | 1565 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.MidiEvent;") 1566 | [this w] 1567 | (print-method (seq this) w)) 1568 | 1569 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.MidiMessage;") 1570 | [this w] 1571 | (print-method (seq this) w)) 1572 | 1573 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.MetaMessage;") 1574 | [this w] 1575 | (print-method (seq this) w)) 1576 | 1577 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.ShortMessage;") 1578 | [this w] 1579 | (print-method (seq this) w)) 1580 | 1581 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.SysexMessage;") 1582 | [this w] 1583 | (print-method (seq this) w)) 1584 | 1585 | (defmethod print-method (Class/forName "[Ljavax.sound.midi.Track;") 1586 | [this w] 1587 | (print-method (seq this) w)) 1588 | 1589 | (defmethod print-method MetaEventListener 1590 | [this ^java.io.Writer w] 1591 | (.write w (pr-str (info this)))) 1592 | 1593 | (defmethod print-method ControllerEventListener 1594 | [this ^java.io.Writer w] 1595 | (.write w (pr-str (info this)))) 1596 | 1597 | (defmethod print-method MidiDevice$Info 1598 | [this ^java.io.Writer w] 1599 | (.write w (pr-str (info this)))) 1600 | 1601 | (defmethod print-method MidiDevice 1602 | [this ^java.io.Writer w] 1603 | (.write w (pr-str (info this)))) 1604 | 1605 | (defmethod print-method MidiChannel 1606 | [this ^java.io.Writer w] 1607 | (.write w (pr-str (info this)))) 1608 | 1609 | (defmethod print-method Receiver 1610 | [this ^java.io.Writer w] 1611 | (.write w (pr-str (info this)))) 1612 | 1613 | (defmethod print-method Transmitter 1614 | [this ^java.io.Writer w] 1615 | (.write w (pr-str (info this)))) 1616 | 1617 | (defmethod print-method Soundbank 1618 | [this ^java.io.Writer w] 1619 | (.write w (pr-str (info this)))) 1620 | 1621 | (defmethod print-method SoundbankResource 1622 | [this ^java.io.Writer w] 1623 | (.write w (pr-str (info this)))) 1624 | 1625 | (defmethod print-method Patch 1626 | [this ^java.io.Writer w] 1627 | (.write w (pr-str (info this)))) 1628 | 1629 | (defmethod print-method MidiFileFormat 1630 | [this ^java.io.Writer w] 1631 | (.write w (pr-str (info this)))) 1632 | 1633 | (defmethod print-method MidiMessage 1634 | [this ^java.io.Writer w] 1635 | (.write w (pr-str (info this)))) 1636 | 1637 | (defmethod print-method MetaMessage 1638 | [this ^java.io.Writer w] 1639 | (.write w (pr-str (info this)))) 1640 | 1641 | (defmethod print-method MidiEvent 1642 | [this ^java.io.Writer w] 1643 | (.write w (pr-str (info this)))) 1644 | 1645 | (defmethod print-method Sequence 1646 | [this ^java.io.Writer w] 1647 | (.write w (pr-str (assoc (info this) :id (System/identityHashCode this))))) 1648 | 1649 | (defmethod print-method Track 1650 | [this ^java.io.Writer w] 1651 | (.write w (pr-str (assoc (info this) :id (System/identityHashCode this))))) 1652 | 1653 | (defmethod print-method VoiceStatus 1654 | [^VoiceStatus this ^java.io.Writer w] 1655 | (.write w (pr-str (info this)))) 1656 | -------------------------------------------------------------------------------- /src/uncomplicate/clojure_sound/sampled.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Dragan Djuric. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) or later 4 | ;; which can be found in the file LICENSE at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns uncomplicate.clojure-sound.sampled 10 | (:require [clojure.walk :refer [stringify-keys]] 11 | [uncomplicate.commons.core :refer [Releaseable close! Info info]] 12 | [uncomplicate.clojure-sound 13 | [internal :refer [name-key key-name Support supported simple-name GetFormat get-format 14 | extend-array-info]] 15 | [core :refer [write! read! SoundInfoProvider Open Timing Reset Broadcast Activity Type 16 | Format SoundSystemProcedures file-format itype properties 17 | sound-info Available Channels]]]) 18 | (:import java.net.URL 19 | [java.io File InputStream OutputStream] 20 | [java.security Permission] 21 | [javax.sound.sampled AudioSystem AudioFormat AudioInputStream AudioPermission 22 | AudioFormat AudioFormat$Encoding AudioFileFormat AudioFileFormat$Type Mixer Mixer$Info 23 | Line Line$Info DataLine DataLine$Info Port Port$Info SourceDataLine TargetDataLine Clip 24 | Control$Type Control BooleanControl BooleanControl$Type CompoundControl EnumControl 25 | EnumControl$Type FloatControl FloatControl$Type LineListener LineEvent LineEvent$Type 26 | ReverbType] 27 | [clojure.lang Sequential Keyword] )) 28 | 29 | (defprotocol Match 30 | (matches? [this other])) 31 | 32 | (defprotocol AudioSystemProcedures 33 | (audio-input-stream [this] [target-format source-stream]) 34 | (encodings [this] [target-format source-stream])) 35 | 36 | (defprotocol Frame 37 | (frame-length [this]) 38 | (frame-position [this])) 39 | 40 | (defprotocol Value 41 | (value [this]) 42 | (value! [this val])) 43 | 44 | ;; ===================== Keyword coding ================================================ 45 | 46 | (def ^:const global-const 47 | {:not-specified AudioSystem/NOT_SPECIFIED}) 48 | 49 | (def port-info 50 | {:microphone Port$Info/MICROPHONE 51 | :mic Port$Info/MICROPHONE 52 | :headphone Port$Info/HEADPHONE 53 | :phones Port$Info/HEADPHONE 54 | :line-in Port$Info/LINE_IN 55 | :in Port$Info/LINE_IN 56 | :line-out Port$Info/LINE_OUT 57 | :out Port$Info/LINE_OUT 58 | :speaker Port$Info/SPEAKER 59 | :compact-disc Port$Info/COMPACT_DISC 60 | :cd Port$Info/COMPACT_DISC}) 61 | 62 | (def port-info-key 63 | {Port$Info/MICROPHONE :microphone 64 | Port$Info/HEADPHONE :headphone 65 | Port$Info/LINE_IN :line-in 66 | Port$Info/LINE_OUT :line-out 67 | Port$Info/SPEAKER :speaker 68 | Port$Info/COMPACT_DISC :compact-disc}) 69 | 70 | (def line-key-class 71 | {:target TargetDataLine 72 | :target-data-line TargetDataLine 73 | :source SourceDataLine 74 | :source-data-line SourceDataLine 75 | :clip Clip 76 | :port Port 77 | :mixer Mixer 78 | :line Line 79 | :data-line DataLine}) 80 | 81 | (def line-class-key 82 | {TargetDataLine :target 83 | SourceDataLine :source 84 | Clip :clip 85 | Port :port 86 | Mixer :mixer 87 | Line :line 88 | DataLine :data-line}) 89 | 90 | (def audio-encoding 91 | {:alaw AudioFormat$Encoding/ALAW 92 | :ulaw AudioFormat$Encoding/ULAW 93 | :pcm-signed AudioFormat$Encoding/PCM_SIGNED 94 | :pcm-unsigned AudioFormat$Encoding/PCM_UNSIGNED 95 | :pcm-float AudioFormat$Encoding/PCM_FLOAT 96 | :signed AudioFormat$Encoding/PCM_SIGNED 97 | :unsigned AudioFormat$Encoding/PCM_UNSIGNED 98 | :float AudioFormat$Encoding/PCM_FLOAT 99 | :byte AudioFormat$Encoding/PCM_SIGNED}) 100 | 101 | (def ^:const big-endian? 102 | {:big-endian true 103 | :big true 104 | true true 105 | :little-endian false 106 | :little false 107 | false false}) 108 | 109 | (def ^:const signed? 110 | {:signed true 111 | true true 112 | :unsigned false 113 | false false}) 114 | 115 | (def audio-file-format-type 116 | {:aifc AudioFileFormat$Type/AIFC 117 | :aiff AudioFileFormat$Type/AIFF 118 | :au AudioFileFormat$Type/AU 119 | :snd AudioFileFormat$Type/SND 120 | :wave AudioFileFormat$Type/WAVE}) 121 | 122 | (def audio-file-format-type-key 123 | {AudioFileFormat$Type/AIFC :aifc 124 | AudioFileFormat$Type/AIFF :aiff 125 | AudioFileFormat$Type/AU :au 126 | AudioFileFormat$Type/SND :snd 127 | AudioFileFormat$Type/WAVE :wave}) 128 | 129 | (def boolean-control 130 | {:apply-reverb BooleanControl$Type/APPLY_REVERB 131 | :mute BooleanControl$Type/MUTE}) 132 | 133 | (def enum-control 134 | {:reverb EnumControl$Type/REVERB}) 135 | 136 | (def float-control 137 | {:aux-return FloatControl$Type/AUX_RETURN 138 | :aux-send FloatControl$Type/AUX_SEND 139 | :balance FloatControl$Type/BALANCE 140 | :master-gain FloatControl$Type/MASTER_GAIN 141 | :pan FloatControl$Type/PAN 142 | :reverb-return FloatControl$Type/REVERB_RETURN 143 | :reverb-send FloatControl$Type/REVERB_SEND 144 | :sample-rate FloatControl$Type/SAMPLE_RATE 145 | :volume FloatControl$Type/VOLUME 146 | :vol FloatControl$Type/VOLUME}) 147 | 148 | (def control-type 149 | (merge boolean-control enum-control float-control)) 150 | 151 | (def control-type-key 152 | {BooleanControl$Type/APPLY_REVERB :apply-reverb 153 | EnumControl$Type/REVERB :reverb 154 | BooleanControl$Type/MUTE :mute 155 | FloatControl$Type/AUX_RETURN :aux-return 156 | FloatControl$Type/AUX_SEND :aux-send 157 | FloatControl$Type/BALANCE :balance 158 | FloatControl$Type/MASTER_GAIN :master-gain 159 | FloatControl$Type/PAN :pan 160 | FloatControl$Type/REVERB_RETURN :reverb-return 161 | FloatControl$Type/REVERB_SEND :reverb-send 162 | FloatControl$Type/SAMPLE_RATE :sample-rate 163 | FloatControl$Type/VOLUME :volume}) 164 | 165 | (def control-class 166 | {:boolean BooleanControl$Type 167 | :enum EnumControl$Type 168 | :float FloatControl$Type}) 169 | 170 | (def control-class-key 171 | {BooleanControl$Type :boolean 172 | EnumControl$Type :enum 173 | FloatControl$Type :float}) 174 | 175 | (def line-event-type 176 | {:close LineEvent$Type/CLOSE 177 | :open LineEvent$Type/OPEN 178 | :start LineEvent$Type/START 179 | :stop LineEvent$Type/STOP}) 180 | 181 | (def line-event-type-key 182 | {LineEvent$Type/CLOSE :close 183 | LineEvent$Type/OPEN :open 184 | LineEvent$Type/START :start 185 | LineEvent$Type/STOP :stop}) 186 | 187 | (def sampled-type 188 | (merge port-info line-key-class audio-encoding audio-file-format-type 189 | control-type control-class line-event-type)) 190 | 191 | ;; =========================== AudioSystem ==================================== 192 | 193 | (extend-protocol SoundSystemProcedures 194 | File 195 | (file-format [file] 196 | (AudioSystem/getAudioFileFormat file)) 197 | InputStream 198 | (file-format [stream] 199 | (AudioSystem/getAudioFileFormat stream)) 200 | URL 201 | (afile-format [url] 202 | (AudioSystem/getAudioFileFormat url))) 203 | 204 | (extend-protocol AudioSystemProcedures 205 | File 206 | (audio-input-stream [file] 207 | (AudioSystem/getAudioInputStream file)) 208 | InputStream 209 | (audio-input-stream [stream] 210 | (AudioSystem/getAudioInputStream stream)) 211 | URL 212 | (audio-input-stream [url] 213 | (AudioSystem/getAudioInputStream url)) 214 | AudioFormat 215 | (audio-input-stream [target source] 216 | (AudioSystem/getAudioInputStream target ^AudioInputStream source)) 217 | (encodings [source] 218 | (AudioSystem/getTargetEncodings source)) 219 | AudioFormat$Encoding 220 | (audio-input-stream [target source] 221 | (AudioSystem/getAudioInputStream target ^AudioInputStream source)) 222 | (encodings [source] 223 | (AudioSystem/getTargetEncodings source))) 224 | 225 | (defn audio-file-types 226 | ([^AudioInputStream stream] 227 | (AudioSystem/getAudioFileTypes stream)) 228 | ([] 229 | (AudioSystem/getAudioFileTypes))) 230 | 231 | (defn target-data-line 232 | ([^AudioFormat format] 233 | (AudioSystem/getTargetDataLine format)) 234 | ([^AudioFormat format ^Mixer$Info mixer-info] 235 | (AudioSystem/getTargetDataLine format mixer-info))) 236 | 237 | (defn target-formats [encoding source-format] 238 | (AudioSystem/getTargetFormats encoding source-format)) 239 | 240 | (defn target-line-info [^Line$Info info] 241 | (AudioSystem/getTargetLineInfo info)) 242 | 243 | ;; =========================== Line ============================================ 244 | 245 | (deftype LineListenerFunction [f match-event?] 246 | Info 247 | (info [_] 248 | {:fn f}) 249 | (info [_ info-type] 250 | (case info-type 251 | :fn f 252 | nil)) 253 | LineListener 254 | (update [_ event] 255 | (when (match-event? (.getType event)) 256 | (f event)))) 257 | 258 | (deftype LineListenerWrapper [^LineListener f match-event?] 259 | Info 260 | (info [_] 261 | {:fn f}) 262 | (info [_ info-type] 263 | (case info-type 264 | :fn f 265 | nil)) 266 | LineListener 267 | (update [_ event] 268 | (when (match-event? (.getType event)) 269 | (.update f event)))) 270 | 271 | (defn line-listener 272 | ([selection f] 273 | (let [selection (cond (seqable? selection) 274 | (apply hash-set (map #(get line-event-type % %) selection)) 275 | selection (partial = (get line-event-type selection selection)) 276 | :default (constantly true))] 277 | (if (instance? LineListener f) 278 | (->LineListenerWrapper f selection) 279 | (->LineListenerFunction f selection)))) 280 | ([f] 281 | (if (instance? LineListener f) 282 | f 283 | (->LineListenerFunction f (constantly true))))) 284 | 285 | (extend-type Line 286 | Info 287 | (info 288 | ([this] 289 | {:class (simple-name (.getLineClass (.getLineInfo this))) 290 | :status (if (.isOpen this) :open :closed)}) 291 | ([this info-type] 292 | (case info-type 293 | :class (simple-name (.getLineClass (.getLineInfo this))) 294 | :status (if (.isOpen this) :open :closed)))) 295 | Releaseable 296 | (release [this] 297 | (close! this) 298 | true) 299 | SoundInfoProvider 300 | (sound-info [this] 301 | (.getLineInfo this)) 302 | Open 303 | (open! [line!] 304 | (.open line!) 305 | line!) 306 | (open? [line] 307 | (.isOpen line)) 308 | Broadcast 309 | (listen! 310 | ([line! listener] 311 | (let [listener (line-listener listener)] 312 | (.addLineListener line! (line-listener listener)) 313 | listener)) 314 | ([line! listener selection] 315 | (let [listener (line-listener selection listener)] 316 | (.addLineListener line! (line-listener listener)) 317 | listener))) 318 | (ignore! [line! listener] 319 | (.removeLineListener line! listener) 320 | line!)) 321 | 322 | (extend-type java.lang.Class 323 | SoundInfoProvider 324 | (sound-info [c] 325 | (Line$Info. c))) 326 | 327 | (extend-type Keyword 328 | SoundInfoProvider 329 | (sound-info [kw] 330 | (get sampled-type kw (ex-info "Unknown port info." {:type :sound-error 331 | :requested kw 332 | :supported (keys sampled-type)}))) 333 | Support 334 | (supported 335 | ([kw] 336 | (AudioSystem/isLineSupported (sound-info kw))) 337 | ([kw obj] 338 | (supported (get sampled-type kw kw) obj)))) 339 | 340 | (extend-type Line$Info 341 | Info 342 | (info 343 | ([this] 344 | {:class (simple-name (.getLineClass this))}) 345 | ([this info-type] 346 | (case info-type 347 | :class (simple-name (.getLineClass this)) 348 | nil))) 349 | SoundInfoProvider 350 | (sound-info [this] 351 | this) 352 | Match 353 | (matches? [this other] 354 | (.matches this other)) 355 | Support 356 | (supported 357 | ([info] 358 | (AudioSystem/isLineSupported info)) 359 | ([info mxr] 360 | (.isLineSupported ^Mixer mxr info)))) 361 | 362 | (extend-type Port$Info 363 | Info 364 | (info 365 | ([this] 366 | {:class (simple-name (.getLineClass this)) 367 | :name (.getName this)}) 368 | ([this info-type] 369 | (case info-type 370 | :class (simple-name (.getLineClass this)) 371 | :name (.getName this) 372 | nil)))) 373 | 374 | (defn source? [^Port$Info port] 375 | (.isSource port)) 376 | 377 | (defn line-info 378 | ([this] 379 | (sound-info (sound-info this))) 380 | ([line-kind format] 381 | (DataLine$Info. (get line-key-class line-kind line-kind) format)) 382 | ([line-kind format buffer-size] 383 | (if (number? buffer-size) 384 | (DataLine$Info. (get line-key-class line-kind line-kind) format buffer-size) 385 | (Port$Info. (get line-key-class line-kind line-kind) format buffer-size))) 386 | ([line-kind formats ^long min-buffer-size ^long max-buffer-size] 387 | (DataLine$Info. (get line-key-class line-kind line-kind) 388 | (if (sequential? formats) (into-array AudioFormat formats) formats) 389 | min-buffer-size max-buffer-size))) 390 | 391 | (defn control 392 | ([^Line line] 393 | (.getControls line)) 394 | ([^Line line ctrl-type] 395 | (.getControl line (get control-type ctrl-type ctrl-type)))) 396 | 397 | (defn line-class [info] 398 | (.getLineClass ^Line$Info (sound-info info))) 399 | 400 | (extend-type LineEvent$Type 401 | Info 402 | (info 403 | ([this] 404 | {:name (.toString this)}) 405 | ([this info-type] 406 | (case info-type 407 | :name (.toString this) 408 | nil)))) 409 | 410 | (extend-type LineEvent 411 | Info 412 | (info 413 | ([this] 414 | {:type (itype this) 415 | :position (frame-position this)}) 416 | ([this info-type] 417 | (case info-type 418 | :type (itype this) 419 | :position (frame-position this) 420 | nil))) 421 | Frame 422 | (frame-position [event] 423 | (.getFramePosition event)) 424 | Type 425 | (itype [event] 426 | (let [event-type (.getType event)] 427 | (get line-event-type-key event-type event-type)))) 428 | 429 | (defn line-event [line event-type ^long position] 430 | (LineEvent. line (line-event-type event-type event-type) position)) 431 | 432 | ;; =================== DataLine ========================================== 433 | 434 | (extend-type DataLine 435 | Info 436 | (info 437 | ([this] 438 | {:class (simple-name (.getLineClass (.getLineInfo this))) 439 | :status (if (.isOpen this) :open :closed) 440 | :level (.getLevel this) 441 | :active (.isActive this) 442 | :running (.isRunning this)}) 443 | ([this info-type] 444 | (case info-type 445 | :class (simple-name (.getLineClass (.getLineInfo this))) 446 | :status (if (.isOpen this) :open :closed) 447 | :level (.getLevel this) 448 | :active (.isActive this) 449 | :running (.isRunning this)))) 450 | GetFormat 451 | (get-format [line] 452 | (.getFormat line)) 453 | Frame 454 | (frame-position [line] 455 | (.getLongFramePosition line)) 456 | Available 457 | (available [line] 458 | (.available line)) 459 | Timing 460 | (micro-position [line] 461 | (.getMicrosecondPosition line)) 462 | Activity 463 | (running? [line] 464 | (.isRunning line)) 465 | (active? [line] 466 | (.isActive line)) 467 | (start! [line!] 468 | (.start line!) 469 | line!) 470 | (stop! [line!] 471 | (.stop line!) 472 | line!)) 473 | 474 | (defn formats [^DataLine$Info info] 475 | (.getFormats info)) 476 | 477 | (defn max-buffer-size ^long [^DataLine$Info info] 478 | (.getMaxBufferSize info)) 479 | 480 | (defn min-buffer-size ^long [^DataLine$Info info] 481 | (.getMinBufferSize info)) 482 | 483 | (defn drain! [^DataLine line] 484 | (.drain line) 485 | line) 486 | 487 | (defn flush! [^DataLine line] 488 | (.flush line) 489 | line) 490 | 491 | (defn buffer-size ^long [^DataLine line] 492 | (.getBufferSize line)) 493 | 494 | (defn level ^double [^DataLine line] 495 | (.getLevel line)) 496 | 497 | ;; ====================== Clip ================================================= 498 | 499 | (extend-type Clip 500 | Open 501 | (open! 502 | ([clip!] 503 | (try 504 | (.open clip!) 505 | (catch IllegalArgumentException e 506 | (ex-info "Clip does not support 1-argument open!." 507 | {:type :sound-error 508 | :cause e}))) 509 | clip!) 510 | ([clip! stream] 511 | (.open clip! ^AudioInputStream stream) 512 | clip!) 513 | ([clip! format data offset buffer-size] 514 | (.open clip! ^AudioFormat format data offset buffer-size) 515 | clip!)) 516 | (open? [clip] 517 | (.isOpen clip)) 518 | Frame 519 | (frame-length [clip] 520 | (.getFrameLength clip)) 521 | (frame-position [clip] 522 | (.getFramePosition clip)) 523 | Timing 524 | (micro-length [clip] 525 | (.getMicrosecondLength clip)) 526 | (micro-position [line] 527 | (.getMicrosecondPosition line)) 528 | (micro-position! [clip microseconds] 529 | (.setMicrosecondPosition clip microseconds) 530 | clip)) 531 | 532 | (defn clip 533 | ([] 534 | (AudioSystem/getClip)) 535 | ([^Mixer$Info mixer-info] 536 | (AudioSystem/getClip mixer-info))) 537 | 538 | (defn frame-position! [^Clip clip ^long frames] 539 | (.setFramePosition clip frames) 540 | clip) 541 | 542 | (defn loop-points! [^Clip clip ^long start ^long end] 543 | (.setLoopPoints clip start end) 544 | clip) 545 | 546 | (defn loop! 547 | ([^Clip clip] 548 | (.loop clip Clip/LOOP_CONTINUOUSLY) 549 | clip) 550 | ([^Clip clip ^long count] 551 | (.loop clip count) 552 | clip)) 553 | 554 | ;; ====================== SourceDataLine ================================================= 555 | 556 | (extend-type SourceDataLine 557 | Open 558 | (open! 559 | ([line!] 560 | (.open line!) 561 | line!) 562 | ([line! format-or-buffer-size] 563 | (if (number? format-or-buffer-size) 564 | (.open line! ^AudioFormat (get-format line!) format-or-buffer-size) 565 | (.open line! ^AudioFormat format-or-buffer-size)) 566 | line!) 567 | ([line! format buffer-size] 568 | (.open line! ^AudioFormat format buffer-size) 569 | line!)) 570 | (open? [line] 571 | (.isOpen line))) 572 | 573 | (defmethod write! [AudioInputStream File] 574 | [^AudioInputStream in ^File out! ^AudioFileFormat$Type file-type] 575 | (AudioSystem/write in file-type out!)) 576 | 577 | (defmethod write! [AudioInputStream OutputStream] 578 | [^AudioInputStream in ^OutputStream out! ^AudioFileFormat$Type file-type] 579 | (AudioSystem/write in file-type out!)) 580 | 581 | (defmethod write! [(Class/forName "[B") SourceDataLine Number Number] 582 | [byte-arr ^SourceDataLine line! offset length] 583 | (.write line! byte-arr offset length)) 584 | 585 | (defmethod write! [(Class/forName "[B") SourceDataLine Number] 586 | [^bytes byte-arr ^SourceDataLine line! ^long offset] 587 | (.write line! byte-arr offset (- (long (alength byte-arr)) offset))) 588 | 589 | (defmethod write! [(Class/forName "[B") SourceDataLine] 590 | [^bytes byte-arr ^SourceDataLine line!] 591 | (.write line! byte-arr 0 (alength byte-arr))) 592 | 593 | ;; ====================== TargetDataLine ================================================= 594 | 595 | (extend-type TargetDataLine 596 | Open 597 | (open! 598 | ([line!] 599 | (.open line!) 600 | line!) 601 | ([line! format-or-buffer-size] 602 | (if (number? format-or-buffer-size) 603 | (.open line! ^AudioFormat (get-format line!) format-or-buffer-size) 604 | (.open line! ^AudioFormat format-or-buffer-size)) 605 | line!) 606 | ([line! format buffer-size] 607 | (.open line! ^AudioFormat format buffer-size) 608 | line!)) 609 | (open? [line] 610 | (.isOpen line))) 611 | 612 | ;; =========================== Mixer =========================================== 613 | 614 | (extend-type Mixer$Info 615 | Info 616 | (info 617 | ([this] 618 | {:description (.getDescription this) 619 | :name (.getName this) 620 | :vendor (.getVendor this) 621 | :version (.getVersion this)}) 622 | ([this info-type] 623 | (case info-type 624 | :description (.getDescription this) 625 | :name (.getName this) 626 | :vendor (.getVendor this) 627 | :version (.getVersion this) 628 | nil))) 629 | SoundInfoProvider 630 | (sound-info [info] 631 | info)) 632 | 633 | (extend-type Mixer 634 | Info 635 | (info 636 | ([this] 637 | (merge {:class (simple-name (class this)) 638 | :status (if (.isOpen this) :open :closed)} 639 | (info (.getMixerInfo this)))) 640 | ([this info-type] 641 | (case info-type 642 | :class (simple-name (class this)) 643 | :status (if (.isOpen this) :open :closed) 644 | (info (.getMixerInfo this) info-type)))) 645 | SoundInfoProvider 646 | (sound-info [mixer] 647 | (.getMixerInfo mixer)) 648 | Support 649 | (supported [this info] 650 | (.isLineSupported this (sound-info info)))) 651 | 652 | (defn mixer-info 653 | ([] 654 | (AudioSystem/getMixerInfo)) 655 | ([^Mixer mixer] 656 | (.getMixerInfo mixer))) 657 | 658 | (defn mixer 659 | ([] 660 | (AudioSystem/getMixer nil)) 661 | ([^Mixer$Info info] 662 | (AudioSystem/getMixer info))) 663 | 664 | (defn max-lines [^Mixer mixer info] 665 | (.getMaxLines mixer (sound-info info))) 666 | 667 | (defn line 668 | ([obj] 669 | (if (instance? LineEvent obj) 670 | (.getLine ^LineEvent obj) 671 | (AudioSystem/getLine (sound-info obj)))) 672 | ([^Mixer mixer info] 673 | (.getLine mixer (sound-info info)))) 674 | 675 | (defn source-info 676 | ([this] 677 | (if (instance? Mixer this) 678 | (.getSourceLineInfo ^Mixer this) 679 | (AudioSystem/getSourceLineInfo (sound-info this)))) 680 | ([^Mixer mixer info] 681 | (.getSourceLineInfo mixer (sound-info info)))) 682 | 683 | (defn source 684 | ([this] 685 | (if (instance? Mixer this) 686 | (map (partial line this) (.getSourceLineInfo ^Mixer this)) 687 | (map line (AudioSystem/getSourceLineInfo this)))) 688 | ([mixer info] 689 | (map (partial line mixer) (source-info mixer info)))) 690 | 691 | (defn open-sources [^Mixer mixer] 692 | (.getSourceLines mixer)) 693 | 694 | (defn target-info 695 | ([this] 696 | (if (instance? Line$Info this) 697 | (AudioSystem/getTargetLineInfo (sound-info this)) 698 | (.getTargetLineInfo ^Mixer this))) 699 | ([^Mixer mixer info] 700 | (.getTargetLineInfo mixer (sound-info info)))) 701 | 702 | (defn target 703 | ([this] 704 | (if (instance? Mixer this) 705 | (map (partial line this) (.getTargetLineInfo ^Mixer this)) 706 | (map line (AudioSystem/getTargetLineInfo this)))) 707 | ([mixer info] 708 | (map (partial line mixer) (target-info mixer info)))) 709 | 710 | (defn open-targets [^Mixer mixer] 711 | (.getTargetLines mixer)) 712 | 713 | (defn sync-supported? 714 | ([mixer lines] 715 | (sync-supported? mixer lines true)) 716 | ([^Mixer mixer lines maintain-sync?] 717 | (.isSynchronizationSupported mixer (if (sequential? lines) (into-array Line lines) lines) 718 | maintain-sync?))) 719 | 720 | (extend-protocol Support 721 | (Class/forName "[Ljavax.sound.sampled.Line;") 722 | (supported [lines mixer] 723 | (sync-supported? mixer lines true)) 724 | Sequential 725 | (supported [lines mixer] 726 | (sync-supported? mixer (into-array Line lines) true))) 727 | 728 | (defn sync! 729 | ([^Mixer mixer! lines maintain-sync?] 730 | (.synchronize mixer! (if (sequential? lines) (into-array Line lines) lines) maintain-sync?)) 731 | ([mixer! lines] 732 | (sync! mixer! lines true))) 733 | 734 | (defn unsync! [^Mixer mixer! lines] 735 | (.unsynchronize mixer! (if (sequential? lines) (into-array Line lines) lines))) 736 | 737 | ;; ====================== AudioPermission ====================================== 738 | 739 | (extend-type AudioPermission 740 | Info 741 | (info 742 | ([this] 743 | {:name (.getName this)}) 744 | ([this info-type] 745 | (case info-type 746 | :name (.getName this) 747 | nil)))) 748 | 749 | (defn audio-permission [permission] 750 | (if (instance? AudioPermission permission) 751 | permission 752 | (try (AudioPermission. (#{"play" "record" "*"} (name permission))) 753 | (catch NullPointerException e 754 | (throw (ex-info (format "Unsupported permission: %s." permission) 755 | {:type :permission-error 756 | :requested permission :supported #{:play :record "play" "record"}})))))) 757 | 758 | (defn implies [^Permission this other] 759 | (.implies this (audio-permission other))) 760 | 761 | ;; ================== AudioFormat ====================================== 762 | 763 | (extend-type AudioFormat$Encoding 764 | Info 765 | (info 766 | ([this] 767 | {:name (name-key (.toString this))}) 768 | ([this info-type] 769 | (case info-type 770 | :name (name-key (.toString this))))) 771 | Support 772 | (supported [target source] 773 | (AudioSystem/isConversionSupported target ^AudioFormat source))) 774 | 775 | (extend-type AudioFormat 776 | Info 777 | (info 778 | ([this] 779 | (into {:encoding (name-key (.toString (.getEncoding this)))} 780 | (map (fn [[k v]] [(name-key k) v]) (properties this)))) 781 | ([this info-type] 782 | (case info-type 783 | :encoding (name-key (.toString (.getEncoding this))) 784 | (map (fn [[k v]] [(name-key k) v]) (properties this))))) 785 | Support 786 | (supported [af line] 787 | (.isFormatSupported ^DataLine$Info line af)) 788 | Match 789 | (matches? [af other] 790 | (.matches af other)) 791 | Format 792 | (property [af key] 793 | (.getProperty af (name key))) 794 | (properties [af] 795 | (.properties af)) 796 | GetFormat 797 | (get-format [this] 798 | this) 799 | Support 800 | (support [target source] 801 | (AudioSystem/isConversionSupported target ^AudioFormat source)) 802 | Channels 803 | (channels [format] 804 | (.getChannels format))) 805 | 806 | (defn audio-format 807 | ([from] 808 | (get-format from)) 809 | ([sample-rate sample-size-bits] 810 | (audio-format sample-rate sample-size-bits 1 :signed :little-endian)) 811 | ([sample-rate sample-size-bits channels] 812 | (audio-format sample-rate sample-size-bits channels :signed :little-endian)) 813 | ([sample-rate sample-size-bits channels signed endian] 814 | (AudioFormat. sample-rate sample-size-bits channels 815 | (signed? signed) (big-endian? endian))) 816 | ([encoding sample-rate sample-size-bits channels frame-size frame-rate endian] 817 | (AudioFormat. (get audio-encoding encoding encoding) 818 | sample-rate sample-size-bits channels frame-size frame-rate 819 | (big-endian? endian))) 820 | ([encoding sample-rate sample-size-bits channels frame-size frame-rate endian properties] 821 | (AudioFormat. (get audio-encoding encoding encoding) 822 | sample-rate sample-size-bits channels frame-size frame-rate 823 | (big-endian? endian) (stringify-keys properties)))) 824 | 825 | (extend-type java.util.Map 826 | Format 827 | (property [this key] 828 | (.get this (name key))) 829 | (properties [this] 830 | this) 831 | GetFormat 832 | (get-format [from] 833 | (let [{:keys [encoding sample-rate sample-size-bits channels 834 | frame-size frame-rate signed endian properties] 835 | :or {channels 1 sample-size-bits 16 endian :little-endian signed :signed}} from] 836 | (if encoding 837 | (if properties 838 | (audio-format encoding sample-rate sample-size-bits channels frame-size 839 | frame-rate endian properties) 840 | (audio-format encoding sample-rate sample-size-bits channels frame-size frame-rate)) 841 | (audio-format sample-rate sample-size-bits channels signed endian))))) 842 | 843 | (defn encoding [this] 844 | (if (instance? AudioFormat this) 845 | (.getEncoding ^AudioFormat this) 846 | (get audio-encoding this (AudioFormat$Encoding. (name this))))) 847 | 848 | (defn frame-rate ^double [^AudioFormat format] 849 | (.getFrameRate format)) 850 | 851 | (defn frame-size ^long [^AudioFormat format] 852 | (.getFrameSize format)) 853 | 854 | (defn sample-rate ^double [^AudioFormat format] 855 | (.getSampleRate format)) 856 | 857 | (defn sample-size-bits ^long [^AudioFormat format] 858 | (.getSampleSizeInBits format)) 859 | 860 | (defn big-endian? [^AudioFormat format] 861 | (.isBigEndian format)) 862 | 863 | ;; =================== AudioFileFormat ================================================= 864 | 865 | (extend-type AudioFileFormat$Type 866 | Info 867 | (info 868 | ([this] 869 | {:extension (.getExtension this) 870 | :name (.toString this)}) 871 | ([this info-type] 872 | (case info-type 873 | :extension (.getExtension this) 874 | :name (.toString this) 875 | nil))) 876 | Support 877 | (supported [feature stream] 878 | (AudioSystem/isFileTypeSupported (get audio-file-format-type feature feature) stream))) 879 | 880 | (defn extension [^AudioFileFormat$Type t] 881 | (.getExtension t)) 882 | 883 | (defn file-format-type 884 | ([aff] 885 | (if (instance? AudioFileFormat aff) 886 | (.getType ^AudioFileFormat aff) 887 | (get audio-file-format-type aff (file-format-type (name aff) aff)))) 888 | ([name extension] 889 | (AudioFileFormat$Type. (str name) (name extension)))) 890 | 891 | (extend-type AudioFileFormat 892 | Info 893 | (info 894 | ([this] 895 | (into {:type (extension (itype this))} 896 | (map (fn [[k v]] [(name-key k) v]) (properties this)))) 897 | ([this info-type] 898 | (case info-type 899 | :type (extension (itype this)) 900 | (map (fn [[k v]] [(name-key k) v]) (properties this))))) 901 | Frame 902 | (frame-length [aff] 903 | (.getFrameLength aff)) 904 | Type 905 | (itype [aff] 906 | (.getType aff)) 907 | GetFormat 908 | (get-format [aff] 909 | (.getFormat aff)) 910 | Format 911 | (property [aff key] 912 | (.getProperty aff (name key))) 913 | (properties [aff] 914 | (.properties aff)) 915 | (byte-length [aff] 916 | (.getByteLength aff))) 917 | 918 | (defn audio-file-format 919 | ([this] 920 | (file-format this)) 921 | ([type ^long byte-length format ^long frame-length] 922 | (AudioFileFormat. (file-format-type type) byte-length format frame-length)) 923 | ([type format ^long frame-length] 924 | (AudioFileFormat. (file-format-type type) format frame-length)) 925 | ([type args] 926 | (AudioFileFormat. (file-format-type type) 927 | (:format args) (:frame-length args) 928 | (stringify-keys (dissoc args :format :frame-length))))) 929 | 930 | ;; ========================== InputStream ================================================ 931 | 932 | (extend-type InputStream 933 | Available 934 | (available [stream] 935 | (.available stream)) 936 | Support 937 | (supported [stream] 938 | (.markSupported stream)) 939 | Reset 940 | (re-set! [stream!] 941 | (.reset stream!) 942 | stream!)) 943 | 944 | (defn mark! [^InputStream stream! ^long read-limit] 945 | (.mark stream! read-limit)) 946 | 947 | (defmethod read! [InputStream] 948 | [^InputStream stream] 949 | (.read stream)) 950 | 951 | (defmethod read! [InputStream (Class/forName "[B")] 952 | [^InputStream stream array!] 953 | (.read stream array!)) 954 | 955 | (defmethod read! [InputStream (Class/forName "[B") Number Number] 956 | [^InputStream stream array! offset length] 957 | (.read stream array! offset length)) 958 | 959 | (defmethod read! [InputStream (Class/forName "[B") Number] 960 | [^InputStream stream ^bytes array! ^long offset] 961 | (.read stream array! offset (- (long (alength array!)) offset))) 962 | 963 | (defmethod read! [(Class/forName "[B") InputStream] 964 | [array! ^InputStream stream] 965 | (.read stream array!)) 966 | 967 | (defmethod read! [(Class/forName "[B") InputStream Number Number] 968 | [array! ^InputStream stream offset length] 969 | (.read stream array! offset length)) 970 | 971 | (defmethod read! [(Class/forName "[B") InputStream Number] 972 | [^bytes array! ^InputStream stream ^long offset] 973 | (.read stream array! offset (- (long (alength array!)) offset))) 974 | 975 | (defmethod read! [TargetDataLine (Class/forName "[B") Number Number] 976 | [^TargetDataLine line ^bytes array! offset length] 977 | (.read line array! offset length)) 978 | 979 | (defmethod read! [TargetDataLine (Class/forName "[B") Number] 980 | [^TargetDataLine line ^bytes array! ^long offset] 981 | (.read line array! offset (- (long (alength array!)) offset))) 982 | 983 | (defmethod read! [TargetDataLine (Class/forName "[B")] 984 | [^TargetDataLine line ^bytes array!] 985 | (.read line array! 0 (long (alength array!)))) 986 | 987 | (defmethod read! [(Class/forName "[B") TargetDataLine Number Number] 988 | [^bytes array! ^TargetDataLine line offset length] 989 | (.read line array! offset length)) 990 | 991 | (defmethod read! [(Class/forName "[B") TargetDataLine Number] 992 | [^bytes array! ^TargetDataLine line ^long offset] 993 | (.read line array! offset (- (long (alength array!)) offset))) 994 | 995 | (defmethod read! [(Class/forName "[B") TargetDataLine] 996 | [^bytes array! ^TargetDataLine line] 997 | (.read line array! 0 (long (alength array!)))) 998 | 999 | 1000 | (defn skip! [^InputStream stream! ^long n] 1001 | (.skip stream! n)) 1002 | 1003 | ;; =================== AudioInputStream ================================================ 1004 | 1005 | (extend-type AudioInputStream 1006 | Info 1007 | (info 1008 | ([this] 1009 | {:format (info (.getFormat this)) 1010 | :length (.getFrameLength this)}) 1011 | ([this info-type] 1012 | (case info-type 1013 | :format (info (.getFormat this)) 1014 | :length (.getFrameLength this) 1015 | nil))) 1016 | Releaseable 1017 | (release [this] 1018 | (.close this) 1019 | true) 1020 | GetFormat 1021 | (get-format [this] 1022 | (.getFormat this)) 1023 | Frame 1024 | (frame-length [this] 1025 | (.getFrameLength this))) 1026 | 1027 | (defn audio-input 1028 | ([stream format ^long length] 1029 | (AudioInputStream. stream format length)) 1030 | ([line] 1031 | (AudioInputStream. line))) 1032 | 1033 | ;; =================== Control ================================================== 1034 | 1035 | (extend-type Control$Type 1036 | Info 1037 | (info 1038 | ([this] 1039 | {:name (get control-type-key this (.toString this)) 1040 | :type (let [clazz (class this)] 1041 | (get control-class-key clazz (simple-name clazz)))}) 1042 | ([this info-type] 1043 | (case info-type 1044 | :name (get control-type-key this (.toString this)) 1045 | :type (let [clazz (class this)] 1046 | (get control-class-key clazz (simple-name clazz))) 1047 | nil))) 1048 | Support 1049 | (supported [this line] 1050 | (.isControlSupported ^Line line this)) 1051 | Type 1052 | (itype [this] 1053 | (class this))) 1054 | 1055 | (extend-type Control 1056 | Info 1057 | (info 1058 | ([this] 1059 | {:type (let [ctrl-type (.getType this)] 1060 | (get control-type-key ctrl-type ctrl-type)) 1061 | :value (value this) 1062 | :kind (info (.getType this) :type)}) 1063 | ([this info-type] 1064 | (let [t (itype this)] 1065 | (case info-type 1066 | :type (let [ctrl-type (.getType this)] 1067 | (get control-type-key ctrl-type ctrl-type)) 1068 | :value (value this) 1069 | :kind (info (.getType this) :type) 1070 | nil)))) 1071 | Type 1072 | (itype [control] 1073 | (.getType control))) 1074 | 1075 | ;; =================== BooleanControl ================================================== 1076 | 1077 | (extend-type BooleanControl 1078 | Value 1079 | (value [this] 1080 | (.getValue this)) 1081 | (value! [this! val] 1082 | (.setValue this! val) 1083 | this!)) 1084 | 1085 | (defn state-label [^BooleanControl control state] 1086 | (.getStateLabel control state)) 1087 | 1088 | ;; =================== CompoundControl ================================================== 1089 | 1090 | (defn controls [^CompoundControl control] 1091 | (.getMemberControls control)) 1092 | 1093 | ;; =================== EnumControl ================================================== 1094 | 1095 | (extend-type EnumControl 1096 | Value 1097 | (value [this] 1098 | (.getValue this)) 1099 | (value! [this! val] 1100 | (.setValue this! val) 1101 | this!)) 1102 | 1103 | (defn values [^EnumControl control] 1104 | (.getValues control)) 1105 | 1106 | ;; =================== FloatControl ================================================== 1107 | 1108 | (extend-type FloatControl 1109 | Value 1110 | (value [this] 1111 | (.getValue this)) 1112 | (value! [this! val] 1113 | (.setValue this! val) 1114 | this!)) 1115 | 1116 | (defn maximum ^double [^FloatControl control] 1117 | (.getMaximum control)) 1118 | 1119 | (defn max-label [^FloatControl control] 1120 | (.getMaxLabel control)) 1121 | 1122 | (defn mid-label [^FloatControl control] 1123 | (.getMidLabel control)) 1124 | 1125 | (defn minimum ^double [^FloatControl control] 1126 | (.getMinimum control)) 1127 | 1128 | (defn min-label [^FloatControl control] 1129 | (.getMinLabel control)) 1130 | 1131 | (defn precision ^double [^FloatControl control] 1132 | (.getPrecision control)) 1133 | 1134 | (defn units [^FloatControl control] 1135 | (.getUnits control)) 1136 | 1137 | (defn update-period ^long [^FloatControl control] 1138 | (.getUpdatePeriod control)) 1139 | 1140 | (defn shift? [^FloatControl control] 1141 | (< -1 (.getUpdatePeriod control))) 1142 | 1143 | (defn shift! 1144 | ([control ^long microseconds] 1145 | (shift! control (minimum control) (maximum control) microseconds)) 1146 | ([control ^long to ^long microseconds] 1147 | (shift! control (value control) to microseconds)) 1148 | ([^FloatControl control ^double from ^double to ^long microseconds] 1149 | (.shift control from to microseconds))) 1150 | 1151 | ;; =================== ReverbType ===================================================== 1152 | 1153 | (defn decay-time ^long [^ReverbType reverb] 1154 | (.getDecayTime reverb)) 1155 | 1156 | (defn early-delay ^long [^ReverbType reverb] 1157 | (.getEarlyReflectionDelay reverb)) 1158 | 1159 | (defn early-intensity ^double [^ReverbType reverb] 1160 | (.getEarlyReflectionIntensity reverb)) 1161 | 1162 | (defn late-delay ^long [^ReverbType reverb] 1163 | (.getLateReflectionDelay reverb)) 1164 | 1165 | (defn late-intensity ^double [^ReverbType reverb] 1166 | (.getLateReflectionIntensity reverb)) 1167 | 1168 | (extend-type ReverbType 1169 | Info 1170 | (info 1171 | ([this] 1172 | {:name (.getName this) 1173 | :decay-time (decay-time this) 1174 | :early-delay (early-delay this) 1175 | :early-intensity (early-intensity this) 1176 | :late-delay (late-delay this) 1177 | :late-intensity (late-intensity this)}) 1178 | ([this info-type] 1179 | (case info-type 1180 | :name (.getName this) 1181 | :decay-time (decay-time this) 1182 | :early-delay (early-delay this) 1183 | :early-intensity (early-intensity this) 1184 | :late-delay (late-delay this) 1185 | :late-intensity (late-intensity this) 1186 | nil)))) 1187 | 1188 | ;; =================== Sequential info for sampled arrays ============================== 1189 | 1190 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.Mixer$Info;")) 1191 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.Line$Info;")) 1192 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.DataLine$Info;")) 1193 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.Port$Info;")) 1194 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.Line;")) 1195 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.DataLine;")) 1196 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.Clip;")) 1197 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.Clip;")) 1198 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.SourceDataLine;")) 1199 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.TargetDataLine;")) 1200 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.Mixer;")) 1201 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.AudioFormat;")) 1202 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.AudioFormat$Encoding;")) 1203 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.AudioFileFormat;")) 1204 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.AudioFileFormat$Type;")) 1205 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.Control;")) 1206 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.BooleanControl;")) 1207 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.CompoundControl;")) 1208 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.EnumControl;")) 1209 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.FloatControl;")) 1210 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.LineEvent;")) 1211 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.LineEvent$Type;")) 1212 | (extend-array-info (Class/forName "[Ljavax.sound.sampled.ReverbType;")) 1213 | (extend-array-info (Class/forName "[Ljava.lang.Object;")) 1214 | 1215 | ;; =================== User friendly printing ========================================== 1216 | 1217 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.Mixer$Info;") 1218 | [this ^java.io.Writer w] 1219 | (.write w (pr-str (seq this)))) 1220 | 1221 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.Line$Info;") 1222 | [this ^java.io.Writer w] 1223 | (.write w (pr-str (seq this)))) 1224 | 1225 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.DataLine$Info;") 1226 | [this ^java.io.Writer w] 1227 | (.write w (pr-str (seq this)))) 1228 | 1229 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.Port$Info;") 1230 | [this ^java.io.Writer w] 1231 | (.write w (pr-str (seq this)))) 1232 | 1233 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.Line;") 1234 | [this w] 1235 | (print-method (seq this) w)) 1236 | 1237 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.DataLine;") 1238 | [this w] 1239 | (print-method (seq this) w)) 1240 | 1241 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.Clip;") 1242 | [this w] 1243 | (print-method (seq this) w)) 1244 | 1245 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.SourceDataLine;") 1246 | [this w] 1247 | (print-method (seq this) w)) 1248 | 1249 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.TargetDataLine;") 1250 | [this w] 1251 | (print-method (seq this) w)) 1252 | 1253 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.Mixer;") 1254 | [this w] 1255 | (print-method (seq this) w)) 1256 | 1257 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.AudioFormat;") 1258 | [this w] 1259 | (print-method (seq this) w)) 1260 | 1261 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.AudioFormat$Encoding;") 1262 | [this w] 1263 | (print-method (seq this) w)) 1264 | 1265 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.AudioFileFormat;") 1266 | [this w] 1267 | (print-method (seq this) w)) 1268 | 1269 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.AudioFileFormat$Type;") 1270 | [this w] 1271 | (print-method (seq this) w)) 1272 | 1273 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.Control;") 1274 | [this w] 1275 | (print-method (seq this) w)) 1276 | 1277 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.BooleanControl;") 1278 | [this w] 1279 | (print-method (seq this) w)) 1280 | 1281 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.CompoundControl;") 1282 | [this w] 1283 | (print-method (seq this) w)) 1284 | 1285 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.EnumControl;") 1286 | [this w] 1287 | (print-method (seq this) w)) 1288 | 1289 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.FloatControl;") 1290 | [this w] 1291 | (print-method (seq this) w)) 1292 | 1293 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.LineEvent;") 1294 | [this w] 1295 | (print-method (seq this) w)) 1296 | 1297 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.LineEvent$Type;") 1298 | [this w] 1299 | (print-method (seq this) w)) 1300 | 1301 | (defmethod print-method (Class/forName "[Ljavax.sound.sampled.ReverbType;") 1302 | [this w] 1303 | (print-method (seq this) w)) 1304 | 1305 | (defmethod print-method (Class/forName "[Ljava.lang.Object;") 1306 | [this w] 1307 | (print-method (seq this) w)) 1308 | 1309 | (defmethod print-method LineListener 1310 | [this ^java.io.Writer w] 1311 | (.write w (pr-str (info this)))) 1312 | 1313 | (defmethod print-method Line 1314 | [this ^java.io.Writer w] 1315 | (.write w (pr-str (info this)))) 1316 | 1317 | (defmethod print-method Line$Info 1318 | [this ^java.io.Writer w] 1319 | (.write w (pr-str (info this)))) 1320 | 1321 | (defmethod print-method Mixer$Info 1322 | [this ^java.io.Writer w] 1323 | (.write w (pr-str (info this)))) 1324 | 1325 | (defmethod print-method AudioPermission 1326 | [this ^java.io.Writer w] 1327 | (.write w (pr-str (info this)))) 1328 | 1329 | (defmethod print-method AudioFormat 1330 | [this ^java.io.Writer w] 1331 | (.write w (pr-str (info this)))) 1332 | 1333 | (defmethod print-method AudioFormat$Encoding 1334 | [this ^java.io.Writer w] 1335 | (.write w (pr-str (name-key this)))) 1336 | 1337 | (defmethod print-method AudioFileFormat 1338 | [this ^java.io.Writer w] 1339 | (.write w (pr-str (info this)))) 1340 | 1341 | (defmethod print-method AudioFileFormat$Type 1342 | [this ^java.io.Writer w] 1343 | (.write w (pr-str (info this)))) 1344 | 1345 | (defmethod print-method AudioInputStream 1346 | [this ^java.io.Writer w] 1347 | (.write w (pr-str (info this)))) 1348 | 1349 | (defmethod print-method Control$Type 1350 | [this ^java.io.Writer w] 1351 | (.write w (pr-str (info this)))) 1352 | 1353 | (defmethod print-method Control 1354 | [this ^java.io.Writer w] 1355 | (.write w (pr-str (info this)))) 1356 | 1357 | (defmethod print-method LineEvent 1358 | [this ^java.io.Writer w] 1359 | (.write w (pr-str (info this)))) 1360 | 1361 | (defmethod print-method LineEvent$Type 1362 | [this ^java.io.Writer w] 1363 | (.write w (pr-str (info this)))) 1364 | 1365 | (defmethod print-method ReverbType 1366 | [this ^java.io.Writer w] 1367 | (.write w (pr-str (info this)))) 1368 | -------------------------------------------------------------------------------- /test/clojure/uncomplicate/clojure_sound/midi_test.clj: -------------------------------------------------------------------------------- 1 | (ns uncomplicate.clojure-sound.midi-test 2 | (:require [midje.sweet :refer [facts throws => =not=> roughly truthy]] 3 | [uncomplicate.commons.core :refer [close! info]] 4 | [uncomplicate.clojure-sound 5 | [core :refer :all] 6 | [midi :refer :all]])) 7 | 8 | (facts "Obtaining Default Devices." 9 | (info (sequencer) :name) => "Real Time Sequencer" 10 | (info (synthesizer) :name) => "Gervill" 11 | (close! (receiver)) => truthy 12 | (close! (transmitter)) => truthy 13 | (<= 2 (count (device-info))) => true 14 | (< 0 (count (filter (comp sequencer? device) (device-info)))) => true 15 | (< 0 (count (filter (comp synthesizer? device) (device-info)))) => true) 16 | 17 | (let [gervill (first (filter #(= "Gervill" (info % :name)) (device-info)))] 18 | (facts "Test Gervill: Info." 19 | (info gervill :description) => "Software MIDI Synthesizer" 20 | (info gervill :name) => "Gervill" 21 | (info gervill :vendor) => "OpenJDK" 22 | (<= 1.0 (Double/parseDouble (info gervill :version))) => true) 23 | 24 | (facts "Test Gervill: InfoProvider." 25 | (sound-info gervill) => gervill) 26 | 27 | (facts "Test Gervill: Opening Devices." 28 | (device gervill) => truthy 29 | (let [dev (device gervill)] 30 | (sequencer? dev) => false 31 | (synthesizer? dev) => true 32 | (max-polyphony dev) => 64 33 | (open? dev) => false 34 | (open! dev) => dev 35 | (open? dev) => true 36 | (close! dev) => dev 37 | (open? dev) => false))) 38 | 39 | (facts "Transmitting and receiving MIDI messages." 40 | (let [msg (short-message) 41 | rcvr (receiver)] 42 | (try 43 | (message! msg :on 0 60 93) => msg 44 | (send! rcvr msg -1) => rcvr 45 | (number? (micro-position (device rcvr))) => true 46 | (send! rcvr msg) => rcvr 47 | (device rcvr) => (device rcvr) 48 | (finally (close! rcvr) => rcvr)))) 49 | 50 | (facts "Connecting transmitters and receivers." 51 | (let [sq (sequencer) 52 | tr (transmitter) 53 | synth (synthesizer) 54 | rc (receiver)] 55 | (try 56 | (connect! tr rc) => tr 57 | (synthesizer? sq) => (sequencer? synth) 58 | (finally 59 | (close! rc) => rc 60 | (close! synth) => synth 61 | (close! tr) => tr 62 | (close! sq) => sq)))) 63 | 64 | (facts "Connecting to more than one device." 65 | (let [synth (synthesizer) 66 | sq (sequencer) 67 | input (first (remove (comp zero? max-transmitters) 68 | (drop 2 (map device (device-info))))) 69 | tr1 (transmitter input) 70 | tr2 (transmitter input) 71 | rc-synth (receiver synth) 72 | rc-sq (receiver sq)] 73 | (try 74 | tr1 =not=> tr2 75 | (max-transmitters input) => -1 76 | (max-receivers input) => 0 77 | (connect! tr1 rc-synth) => tr1 78 | (connect! tr2 rc-sq) => tr2 79 | (synthesizer? sq) => (sequencer? synth) 80 | (finally 81 | (close! input) => input 82 | (close! sq) => sq 83 | (close! synth) => synth)))) 84 | 85 | (facts "Connecting to more than one device with automatic receivers and transmitters." 86 | (let [synth (synthesizer) 87 | sq (sequencer) 88 | input (first (remove (comp zero? max-transmitters) 89 | (drop 2 (map device (device-info))))) 90 | tr-count (count (transmitters input))] 91 | (try 92 | (max-transmitters input) => -1 93 | (let [tr1 (connect! input synth) 94 | tr2 (connect! input sq)] 95 | (filter #{tr1 tr2} (transmitters input)) => [tr1 tr2] 96 | (- (count (transmitters input)) tr-count) => 2 97 | (close! tr1) 98 | (close! tr2) 99 | (count (transmitters input)) => tr-count 100 | (connect! input synth) 101 | (count (transmitters (close! input))) => 0 102 | (count (receivers synth)) => 2 103 | (count (receivers (close! synth))) => 0 104 | (count (receivers sq)) => 1 105 | (count (receivers (close! sq))) => 0) 106 | (finally 107 | (close! input) => input 108 | (close! sq) => sq 109 | (close! synth) => synth)))) 110 | 111 | (facts "Using Sequencer." 112 | (let [sqcr (sequencer) 113 | maple (sequence (clojure.java.io/resource "maple.mid")) 114 | cleanup (promise)] 115 | (try 116 | (info (device (receiver (first (transmitters (sequencer))))) :name) => "Gervill" 117 | (open! sqcr) => sqcr 118 | (sequence! sqcr maple) => sqcr 119 | (listen! sqcr (meta-listener :end-of-track 120 | (fn [_] 121 | (stop! sqcr) 122 | (close! sqcr) ;; Auto-close 123 | (deliver cleanup :confirmed)))) 124 | (start! sqcr) => sqcr 125 | (stop! sqcr) => sqcr 126 | (running? sqcr) => false 127 | (start! sqcr) => sqcr 128 | (running? sqcr) => true 129 | (deref cleanup) => :confirmed 130 | (finally ;; So I can interrupt the music while testing... 131 | (close! sqcr))))) 132 | 133 | ;;TODO 134 | #_(facts "Recording and saving sequences." 135 | (let [player (sequencer) 136 | maple (sequence (clojure.java.io/resource "maple.mid")) 137 | synth (synthesizer) 138 | recorder (sequencer) 139 | copy (sequence :ppq (resolution maple) (tracks maple))] 140 | (try 141 | (sequence! player maple) => player 142 | (sequence! recorder copy) => recorder 143 | (dotimes [i (tracks recorder)] 144 | (rec! recorder i)) 145 | (open! recorder) => recorder 146 | (open! player) => player 147 | (connect! player recorder) 148 | (rec! recorder) => recorder 149 | (recording? recorder) => true 150 | (start! player) => player 151 | (Thread/sleep 2000) 152 | (dotimes [i 16] 153 | (mute! player i true)) 154 | (stop! recorder) 155 | (stop-rec! recorder) => recorder 156 | (doseq [t (tracks copy)] 157 | (stop-rec! recorder t)) 158 | ;;(write! recorder (clojure.java.io/resource "maple-copy.mid") 0) 159 | (stop! player) => player 160 | (println "now") 161 | (sequence! recorder) 162 | (connect! recorder synth) 163 | (open! synth) => synth 164 | (open? recorder) => true 165 | (println ">" (tick-position recorder)) 166 | (println (start-point recorder)) 167 | (println (end-point recorder)) 168 | (println (loop-count recorder)) 169 | (tick-position! recorder 0) 170 | (start-point! recorder 0) 171 | (end-point! recorder (ticks recorder)) 172 | (loop-count! recorder -1) 173 | (println (map event-count (tracks copy))) 174 | (start! recorder) => recorder 175 | (running? recorder) => true 176 | (Thread/sleep 2000) 177 | (finally 178 | (close! synth) 179 | (close! player) 180 | (close! recorder))))) 181 | 182 | (let [sqcr (sequencer) 183 | maple (sequence (clojure.java.io/resource "maple.mid")) 184 | maple2 (sequence :ppq (resolution maple)) 185 | finished? (promise)] 186 | (try 187 | (facts "Editing a Sequence." 188 | (open! sqcr) => sqcr 189 | (let [t2 (track! maple2) 190 | t1 (first (tracks maple))] 191 | (String. ^bytes (data (message (event t1 0)))) => "control track" 192 | (events t1) => 6 193 | (take 3 (map #(String. ^bytes (data (message (event t1 %1)))) (range (events t1)))) 194 | => ["control track" "creator: " "GNU LilyPond 2.14.2 "] 195 | (delete! maple2 t2) => true) 196 | (map events (tracks maple)) => [6 2391 2751] 197 | (map ticks (tracks maple)) => [0 110592 110592] 198 | (doseq [t (tracks maple)] 199 | (let [t2 (track! maple2)] 200 | (dotimes [i (events t)] 201 | (add! t2 (event t i))))) 202 | (map ticks (tracks maple2)) => [0 110592 110592] 203 | (sequence! sqcr maple2) => sqcr) 204 | (facts "Using advanced Sequencer features." 205 | (tick-position sqcr) => 0 206 | (micro-position sqcr) => 0 207 | (tick-position! sqcr 110092) => sqcr 208 | (micro-position sqcr) => 143348958 209 | (tempo-factor sqcr) => 1.0 210 | (tempo-factor! sqcr 3.0) => sqcr 211 | (tempo-bpm sqcr) => 120.0 212 | (tempo-mpq sqcr) => 500000.0 213 | (tempo-bpm! sqcr 360) => sqcr 214 | (tempo-mpq sqcr) => 166666.671875 215 | (mute sqcr 1) => false 216 | (solo sqcr 1) => false 217 | (mute! sqcr 2) => sqcr 218 | (tempo-factor! sqcr 1.0) => sqcr 219 | (tempo-bpm! sqcr 120) => sqcr 220 | (listen! sqcr (partial deliver finished?) :end-of-track) 221 | (start! sqcr) 222 | (deref finished?) => truthy 223 | (tick-position sqcr) => 110592 224 | (micro-position! sqcr 0) => sqcr 225 | (tick-position sqcr) => 0) 226 | (finally 227 | (Thread/sleep 1000) 228 | (stop! sqcr) 229 | (close! sqcr)))) 230 | 231 | (let [synth (synthesizer) 232 | sqcr (sequencer) 233 | fluid (soundbank (clojure.java.io/resource "FluidR3_GM.sf2"));; Note: This soundbank is freely available on the internet. Please google it. 234 | maple (sequence (clojure.java.io/resource "maple.mid")) 235 | finished? (promise)] 236 | (try 237 | (facts "Managing instruments and sound banks." 238 | (count (instruments synth)) => 0 239 | (info (soundbank synth) :name) => "Emergency GM sound set" 240 | (count (available synth)) => 129 241 | (load! synth (first (available synth))) => false 242 | (open! synth) 243 | (load! synth (first (available synth))) => true 244 | (load! synth (available synth)) 245 | (count (instruments synth)) => 129 246 | (unload! synth) => synth 247 | (count (instruments synth)) => 0 248 | (load! synth) => true 249 | (unload! synth) => synth 250 | (info fluid :name) => "Fluid R3 GM" 251 | (load! synth fluid) => true 252 | (count (instruments synth)) => 189 253 | (info (soundbank synth) :name) => "Emergency GM sound set" 254 | (open! sqcr) 255 | (sequence! sqcr maple) 256 | (open! synth) 257 | (connect! sqcr synth) 258 | (tick-position! sqcr 110092) 259 | (listen! sqcr (partial deliver finished?) :end-of-track) 260 | (start! sqcr) 261 | @finished?) 262 | (facts "Querying the synthesizer's capabilities and current state." 263 | (latency synth) => 120000 264 | (max-polyphony synth) => 64 265 | (count (voice-status synth)) => 64) 266 | (facts "Using channels." 267 | (send! (receiver synth) (short-message :on 4 60 93)) 268 | (Thread/sleep 1000) 269 | (on! (first (drop 4 (channels synth))) 60 93) 270 | (Thread/sleep 1000)) 271 | (finally 272 | (Thread/sleep 1000) 273 | (close! sqcr) 274 | (close! synth)))) 275 | -------------------------------------------------------------------------------- /test/clojure/uncomplicate/clojure_sound/sampled_test.clj: -------------------------------------------------------------------------------- 1 | (ns uncomplicate.clojure-sound.sampled-test 2 | (:require [midje.sweet :refer [facts throws => =not=> roughly truthy]] 3 | [clojure.string :refer [includes?]] 4 | [uncomplicate.commons.core :refer [close! info with-release]] 5 | [uncomplicate.clojure-sound 6 | [core :refer :all] 7 | [sampled :refer :all]]) 8 | (:import [javax.sound.sampled SourceDataLine TargetDataLine Clip])) 9 | 10 | (facts "Getting a mixer." 11 | (pos? (count (mixer-info))) => true 12 | (keys (info (first (mixer-info)))) => [:description :name :vendor :version] 13 | (info (first (mixer-info)) :vendor) => "ALSA (http://www.alsa-project.org)" 14 | (mixer) => (mixer nil) 15 | (mixer (first (filter #(includes? (info % :name) "[default]") (mixer-info)))) => (mixer)) 16 | 17 | (with-release [noise (audio-input-stream (clojure.java.io/resource "Noise.wav"))] 18 | (let [noise-format (audio-format noise) 19 | mxr (mixer)] 20 | 21 | (facts "Getting a line directly from audio system." 22 | (info noise-format :encoding) => :pcm-signed 23 | (encoding noise-format) => (encoding :pcm-signed) 24 | (let [noise-info (line-info :target noise-format) 25 | noise-line (line (line-info :target noise-format))] 26 | (try 27 | (line-class noise-info) => javax.sound.sampled.TargetDataLine 28 | (open? noise-line) => false 29 | (open! noise-line noise-format) => noise-line 30 | (supported? :line-out) => false 31 | (supported? (port-info :line-out)) => false 32 | (line (line-info :line-out)) => (throws IllegalArgumentException) 33 | (filter identity (map supported? (vals port-info))) => [] 34 | (count (filter seq (map source-info (vals port-info)))) => 0 35 | (count (filter seq (map target-info (vals port-info)))) => 0 36 | (finally 37 | (close! noise-line))))) 38 | 39 | (facts "Audio format." 40 | (encoding noise-format) => (encoding :pcm-signed) 41 | (channels noise-format) => 1 42 | (frame-rate noise-format) => 48000.0 43 | (frame-size noise-format) => 2 44 | (sample-rate noise-format) => 48000.0 45 | (sample-size-bits noise-format) => 16 46 | (big-endian? noise-format) => false) 47 | 48 | (facts "Getting a line from a mixer." 49 | (try 50 | (filter identity (map (partial supported? mxr) (vals port-info))) => [] 51 | (open! mxr) 52 | (filter (partial supported? mxr) (vals port-info)) => [] 53 | (pos? (count (source-info mxr))) => true 54 | (pos? (count (target-info mxr))) => true 55 | (info (first (map line (target-info mxr))) :class) => "TargetDataLine" 56 | (map (comp line-class line) (source-info mxr)) => [SourceDataLine Clip] 57 | (count (open-sources mxr)) => 0 58 | (let [l (line (first (source-info mxr)))] 59 | (pos? (long (available l))) => false 60 | (open! l) => l 61 | (count (open-sources mxr)) => 1 62 | (open? l) => true 63 | (pos? (long (available l))) => true 64 | (encoding (audio-format l)) => (encoding :pcm-signed) 65 | (channels (audio-format l)) => 2 66 | (close! l) => l) 67 | (finally 68 | (close! mxr)))))) 69 | 70 | (facts "Using a Clip." 71 | (with-release [noise (audio-input-stream (clojure.java.io/resource "Noise.wav")) 72 | clip (line (line-info :clip (audio-format noise)))] 73 | (open? clip) => false 74 | (open! clip noise) => clip 75 | (level clip) => -1.0 76 | (active? clip) => false 77 | (start! clip) => clip 78 | (active? clip) => true 79 | (Thread/sleep 5) 80 | (stop! clip) => clip 81 | (close! clip) => clip)) 82 | 83 | (facts "Using a SourceDataLine." 84 | (with-release [src (line (line-info :source (audio-format 44100.0 16 2)))] 85 | (open? src) => false 86 | (open! src 767) => src 87 | (level src) => -1.0 88 | (running? src) => false 89 | (available src) => 764 90 | (start! src) => src 91 | (active? src) => false 92 | (running? src) => false 93 | (write! (byte-array (repeatedly 2756 (partial rand-int 77))) src 0) 94 | (active? src) => true 95 | (running? src) => true 96 | (stop! src) => src 97 | (running? src) => false 98 | (active? src) => false 99 | (start! src) => src 100 | (drain! src) => src 101 | (flush! src) => src 102 | (close! src))) 103 | 104 | (facts "Monitoring a line's status." 105 | (with-release [noise (audio-input-stream (clojure.java.io/resource "Noise.wav")) 106 | clip (line (line-info :clip (audio-format noise)))] 107 | (let [finished? (promise)] 108 | (listen! clip (partial deliver finished?) :stop) 109 | (open! clip noise) => clip 110 | (start! clip) => clip 111 | @finished?))) 112 | 113 | (facts "Synchronizing audio on multiple lines." 114 | (with-release [noise (audio-input-stream (clojure.java.io/resource "Noise.wav")) 115 | noise-format (audio-format noise) 116 | mxr (mixer) 117 | clip1 (line mxr (line-info :clip noise-format)) 118 | clip2 (line mxr (line-info :clip noise-format)) 119 | finished? (promise)] 120 | (listen! clip1 (partial deliver finished?) :stop) 121 | (supported? mxr [clip1 clip2]) => false 122 | (sync-supported? mxr [clip1 clip2]) => false 123 | (sync! mxr [clip1 clip2]) => (throws IllegalArgumentException) 124 | (open! clip1 noise) => clip1 125 | (open! clip2 noise) => clip2 126 | (start! clip1) => clip1 127 | (start! clip2) => clip2 128 | @finished?)) 129 | 130 | (facts "Capturing audio." 131 | (with-release [mxr (mixer) 132 | tgt (line mxr (line-info :target (audio-format 44100.0 16 2))) 133 | src (line (line-info :source (audio-format 44100.0 16 2))) 134 | finished? (promise)] 135 | (listen! tgt (partial deliver finished?) :stop) 136 | (open! tgt) => tgt 137 | (flush! tgt) => tgt 138 | (start! tgt) => tgt 139 | (read! tgt (byte-array 100)) => 100 140 | (read! tgt (byte-array 100) 1 3) => (throws IllegalArgumentException) 141 | (seq (doto (byte-array (repeat 8 99)) 142 | (read! tgt 2 4))) => [99 99 0 0 0 0 99 99] 143 | (stop! tgt) => tgt 144 | (drain! tgt) => tgt 145 | @finished?)) 146 | 147 | (facts "Getting a line that has the desired controls." 148 | (with-release [mxr (open! (mixer)) 149 | sdl (open! (first (source mxr))) 150 | gain-control (control sdl :master-gain)] 151 | (info gain-control :type) => :master-gain 152 | (info (map itype (control sdl)) :name) => [:master-gain :mute :balance :pan] 153 | (info (control sdl) :kind) => [:float :boolean :float :float] 154 | (value (control sdl :mute)) => false 155 | (value (value! (control sdl :mute) true)) => true 156 | (maximum gain-control) => (roughly 6 0.1) 157 | (max-label gain-control) => "Maximum" 158 | (minimum gain-control) => -80.0 159 | (min-label gain-control) => "Minimum" 160 | (units gain-control) => "dB" 161 | (precision gain-control) => 0.625 162 | (update-period gain-control) => -1 163 | (shift? gain-control) => false)) 164 | 165 | (facts "Getting a line that has the desired controls." 166 | (with-release [sdl (open! (first (source (line-info :source))))] 167 | (info (control sdl) :type) => [:master-gain :mute :balance :pan])) 168 | -------------------------------------------------------------------------------- /test/resources/Noise.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uncomplicate/clojure-sound/c07d75121a29fd225fd3f0f9e3f57b0fa3e0c262/test/resources/Noise.wav -------------------------------------------------------------------------------- /test/resources/maple.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uncomplicate/clojure-sound/c07d75121a29fd225fd3f0f9e3f57b0fa3e0c262/test/resources/maple.mid --------------------------------------------------------------------------------