├── .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 |
4 |
5 |
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
--------------------------------------------------------------------------------