Browse Source

add some stuff

Ondřej Hruška 1 year ago
commit
8820089289
Signed by: Ondřej Hruška <ondra@ondrovo.com> GPG key ID: 2C5FD5035250423D
10 changed files with 1355 additions and 0 deletions
  1. 1 0
      .gitignore
  2. 11 0
      .idea/beeptestrs.iml
  3. 8 0
      .idea/modules.xml
  4. 6 0
      .idea/vcs.xml
  5. 138 0
      .idea/workspace.xml
  6. 596 0
      Cargo.lock
  7. 12 0
      Cargo.toml
  8. 152 0
      src/beep.rs
  9. 424 0
      src/beep/osc.rs
  10. 7 0
      src/main.rs

+ 1 - 0
.gitignore View File

@@ -0,0 +1 @@
1
+/target

+ 11 - 0
.idea/beeptestrs.iml View File

@@ -0,0 +1,11 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<module type="CPP_MODULE" version="4">
3
+  <component name="NewModuleRootManager">
4
+    <content url="file://$MODULE_DIR$">
5
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
6
+      <excludeFolder url="file://$MODULE_DIR$/target" />
7
+    </content>
8
+    <orderEntry type="inheritedJdk" />
9
+    <orderEntry type="sourceFolder" forTests="false" />
10
+  </component>
11
+</module>

+ 8 - 0
.idea/modules.xml View File

@@ -0,0 +1,8 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project version="4">
3
+  <component name="ProjectModuleManager">
4
+    <modules>
5
+      <module fileurl="file://$PROJECT_DIR$/.idea/beeptestrs.iml" filepath="$PROJECT_DIR$/.idea/beeptestrs.iml" />
6
+    </modules>
7
+  </component>
8
+</project>

+ 6 - 0
.idea/vcs.xml View File

@@ -0,0 +1,6 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project version="4">
3
+  <component name="VcsDirectoryMappings">
4
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
5
+  </component>
6
+</project>

+ 138 - 0
.idea/workspace.xml View File

@@ -0,0 +1,138 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project version="4">
3
+  <component name="CMakeRunConfigurationManager" shouldGenerate="true" shouldDeleteObsolete="true">
4
+    <generated />
5
+  </component>
6
+  <component name="CMakeSettings">
7
+    <configurations>
8
+      <configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
9
+    </configurations>
10
+  </component>
11
+  <component name="CargoProjects">
12
+    <cargoProject FILE="$PROJECT_DIR$/Cargo.toml" />
13
+  </component>
14
+  <component name="ChangeListManager">
15
+    <list default="true" id="10cd9b57-3049-40f7-975a-a181479596b3" name="Default Changelist" comment="">
16
+      <change afterPath="$PROJECT_DIR$/src/beep/osc.rs" afterDir="false" />
17
+    </list>
18
+    <option name="SHOW_DIALOG" value="false" />
19
+    <option name="HIGHLIGHT_CONFLICTS" value="true" />
20
+    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
21
+    <option name="LAST_RESOLUTION" value="IGNORE" />
22
+  </component>
23
+  <component name="ClangdSettings">
24
+    <option name="formatViaClangd" value="false" />
25
+  </component>
26
+  <component name="FileColors">
27
+    <fileColor scope="Non-Project Files (Material Default)" color="2E3C43" />
28
+    <fileColor scope="Non-Project Files (Material Darker)" color="323232" />
29
+    <fileColor scope="Non-Project Files (Material Lighter)" color="eae8e8" />
30
+    <fileColor scope="Non-Project Files (Material Palenight)" color="2f2e43" />
31
+  </component>
32
+  <component name="FileTemplateManagerImpl">
33
+    <option name="RECENT_TEMPLATES">
34
+      <list>
35
+        <option value="Rust File" />
36
+      </list>
37
+    </option>
38
+  </component>
39
+  <component name="Git.Settings">
40
+    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
41
+  </component>
42
+  <component name="MacroExpansionManager">
43
+    <option name="directoryName" value="zqz5wsij" />
44
+  </component>
45
+  <component name="ProjectId" id="1jMxUiuWWUW9kd7fLhEhnNAF1kh" />
46
+  <component name="ProjectViewState">
47
+    <option name="hideEmptyMiddlePackages" value="true" />
48
+    <option name="showLibraryContents" value="true" />
49
+  </component>
50
+  <component name="PropertiesComponent">
51
+    <property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
52
+    <property name="WebServerToolWindowFactoryState" value="false" />
53
+    <property name="cf.first.check.clang-format" value="false" />
54
+    <property name="node.js.detected.package.eslint" value="true" />
55
+    <property name="node.js.detected.package.tslint" value="true" />
56
+    <property name="node.js.path.for.package.eslint" value="project" />
57
+    <property name="node.js.path.for.package.tslint" value="project" />
58
+    <property name="node.js.selected.package.eslint" value="(autodetect)" />
59
+    <property name="node.js.selected.package.tslint" value="(autodetect)" />
60
+    <property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
61
+    <property name="nodejs_npm_path_reset_for_default_project" value="true" />
62
+    <property name="org.rust.cargo.project.model.PROJECT_DISCOVERY" value="true" />
63
+    <property name="settings.editor.selected.configurable" value="editor.preferences.gutterIcons" />
64
+  </component>
65
+  <component name="RunManager">
66
+    <configuration name="Run" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
67
+      <option name="channel" value="DEFAULT" />
68
+      <option name="command" value="run --package beeptestrs --bin beeptestrs" />
69
+      <option name="allFeatures" value="false" />
70
+      <option name="emulateTerminal" value="false" />
71
+      <option name="backtrace" value="SHORT" />
72
+      <option name="workingDirectory" value="file://$PROJECT_DIR$" />
73
+      <envs />
74
+      <method v="2">
75
+        <option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
76
+      </method>
77
+    </configuration>
78
+    <configuration default="true" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
79
+      <option name="channel" value="DEFAULT" />
80
+      <option name="command" value="run" />
81
+      <option name="allFeatures" value="false" />
82
+      <option name="emulateTerminal" value="false" />
83
+      <option name="backtrace" value="SHORT" />
84
+      <envs />
85
+      <method v="2">
86
+        <option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
87
+      </method>
88
+    </configuration>
89
+  </component>
90
+  <component name="RustProjectSettings">
91
+    <option name="toolchainHomeDirectory" value="/usr/bin" />
92
+  </component>
93
+  <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
94
+  <component name="TaskManager">
95
+    <task active="true" id="Default" summary="Default task">
96
+      <changelist id="10cd9b57-3049-40f7-975a-a181479596b3" name="Default Changelist" comment="" />
97
+      <created>1603627779596</created>
98
+      <option name="number" value="Default" />
99
+      <option name="presentableId" value="Default" />
100
+      <updated>1603627779596</updated>
101
+      <workItem from="1603627780818" duration="18022000" />
102
+    </task>
103
+    <servers />
104
+  </component>
105
+  <component name="TypeScriptGeneratedFilesManager">
106
+    <option name="version" value="3" />
107
+  </component>
108
+  <component name="WindowStateProjectService">
109
+    <state x="769" y="331" key="#com.intellij.ide.util.MemberChooser" timestamp="1603643362566">
110
+      <screen x="0" y="32" width="1920" height="1168" />
111
+    </state>
112
+    <state x="769" y="331" key="#com.intellij.ide.util.MemberChooser/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1603643362566" />
113
+    <state width="1600" height="1109" key="GridCell.Tab.0.bottom" timestamp="1603650855702">
114
+      <screen x="0" y="32" width="1920" height="1168" />
115
+    </state>
116
+    <state width="1600" height="1109" key="GridCell.Tab.0.bottom/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1603650855702" />
117
+    <state width="1600" height="1109" key="GridCell.Tab.0.center" timestamp="1603650855702">
118
+      <screen x="0" y="32" width="1920" height="1168" />
119
+    </state>
120
+    <state width="1600" height="1109" key="GridCell.Tab.0.center/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1603650855702" />
121
+    <state width="1600" height="1109" key="GridCell.Tab.0.left" timestamp="1603650855702">
122
+      <screen x="0" y="32" width="1920" height="1168" />
123
+    </state>
124
+    <state width="1600" height="1109" key="GridCell.Tab.0.left/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1603650855702" />
125
+    <state width="1600" height="1109" key="GridCell.Tab.0.right" timestamp="1603650855702">
126
+      <screen x="0" y="32" width="1920" height="1168" />
127
+    </state>
128
+    <state width="1600" height="1109" key="GridCell.Tab.0.right/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1603650855702" />
129
+    <state x="651" y="325" width="635" height="580" key="find.popup" timestamp="1603633050478">
130
+      <screen x="0" y="32" width="1920" height="1168" />
131
+    </state>
132
+    <state x="651" y="325" width="635" height="580" key="find.popup/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1603633050478" />
133
+    <state width="1007" height="578" key="javadoc.popup.new" timestamp="1603640885345">
134
+      <screen x="0" y="32" width="1920" height="1168" />
135
+    </state>
136
+    <state width="1007" height="578" key="javadoc.popup.new/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1603640885345" />
137
+  </component>
138
+</project>

+ 596 - 0
Cargo.lock View File

@@ -0,0 +1,596 @@
1
+# This file is automatically @generated by Cargo.
2
+# It is not intended for manual editing.
3
+[[package]]
4
+name = "alsa"
5
+version = "0.4.2"
6
+source = "registry+https://github.com/rust-lang/crates.io-index"
7
+checksum = "44581add1add74ade32aca327b550342359ec00191672c23c1caa3d492b85930"
8
+dependencies = [
9
+ "alsa-sys",
10
+ "bitflags",
11
+ "libc",
12
+ "nix",
13
+]
14
+
15
+[[package]]
16
+name = "alsa-sys"
17
+version = "0.3.0"
18
+source = "registry+https://github.com/rust-lang/crates.io-index"
19
+checksum = "d5a0559bcd3f7a482690d98be41c08a43e92f669b179433e95ddf5e8b8fd36a3"
20
+dependencies = [
21
+ "libc",
22
+ "pkg-config",
23
+]
24
+
25
+[[package]]
26
+name = "anyhow"
27
+version = "1.0.33"
28
+source = "registry+https://github.com/rust-lang/crates.io-index"
29
+checksum = "a1fd36ffbb1fb7c834eac128ea8d0e310c5aeb635548f9d58861e1308d46e71c"
30
+
31
+[[package]]
32
+name = "beeptestrs"
33
+version = "0.1.0"
34
+dependencies = [
35
+ "anyhow",
36
+ "cpal",
37
+ "parking_lot 0.11.0",
38
+]
39
+
40
+[[package]]
41
+name = "bindgen"
42
+version = "0.53.3"
43
+source = "registry+https://github.com/rust-lang/crates.io-index"
44
+checksum = "c72a978d268b1d70b0e963217e60fdabd9523a941457a6c42a7315d15c7e89e5"
45
+dependencies = [
46
+ "bitflags",
47
+ "cexpr",
48
+ "cfg-if 0.1.10",
49
+ "clang-sys",
50
+ "lazy_static",
51
+ "lazycell",
52
+ "peeking_take_while",
53
+ "proc-macro2",
54
+ "quote",
55
+ "regex",
56
+ "rustc-hash",
57
+ "shlex",
58
+]
59
+
60
+[[package]]
61
+name = "bitflags"
62
+version = "1.2.1"
63
+source = "registry+https://github.com/rust-lang/crates.io-index"
64
+checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
65
+
66
+[[package]]
67
+name = "bumpalo"
68
+version = "3.4.0"
69
+source = "registry+https://github.com/rust-lang/crates.io-index"
70
+checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
71
+
72
+[[package]]
73
+name = "cc"
74
+version = "1.0.61"
75
+source = "registry+https://github.com/rust-lang/crates.io-index"
76
+checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
77
+
78
+[[package]]
79
+name = "cexpr"
80
+version = "0.4.0"
81
+source = "registry+https://github.com/rust-lang/crates.io-index"
82
+checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
83
+dependencies = [
84
+ "nom",
85
+]
86
+
87
+[[package]]
88
+name = "cfg-if"
89
+version = "0.1.10"
90
+source = "registry+https://github.com/rust-lang/crates.io-index"
91
+checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
92
+
93
+[[package]]
94
+name = "cfg-if"
95
+version = "1.0.0"
96
+source = "registry+https://github.com/rust-lang/crates.io-index"
97
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
98
+
99
+[[package]]
100
+name = "clang-sys"
101
+version = "0.29.3"
102
+source = "registry+https://github.com/rust-lang/crates.io-index"
103
+checksum = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a"
104
+dependencies = [
105
+ "glob",
106
+ "libc",
107
+ "libloading",
108
+]
109
+
110
+[[package]]
111
+name = "cloudabi"
112
+version = "0.0.3"
113
+source = "registry+https://github.com/rust-lang/crates.io-index"
114
+checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
115
+dependencies = [
116
+ "bitflags",
117
+]
118
+
119
+[[package]]
120
+name = "cloudabi"
121
+version = "0.1.0"
122
+source = "registry+https://github.com/rust-lang/crates.io-index"
123
+checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467"
124
+dependencies = [
125
+ "bitflags",
126
+]
127
+
128
+[[package]]
129
+name = "core-foundation-sys"
130
+version = "0.6.2"
131
+source = "registry+https://github.com/rust-lang/crates.io-index"
132
+checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
133
+
134
+[[package]]
135
+name = "coreaudio-rs"
136
+version = "0.9.1"
137
+source = "registry+https://github.com/rust-lang/crates.io-index"
138
+checksum = "f229761965dad3e9b11081668a6ea00f1def7aa46062321b5ec245b834f6e491"
139
+dependencies = [
140
+ "bitflags",
141
+ "coreaudio-sys",
142
+]
143
+
144
+[[package]]
145
+name = "coreaudio-sys"
146
+version = "0.2.5"
147
+source = "registry+https://github.com/rust-lang/crates.io-index"
148
+checksum = "d6570ee6e089131e928d5ec9236db9e818aa3cf850f48b0eec6ef700571271d4"
149
+dependencies = [
150
+ "bindgen",
151
+]
152
+
153
+[[package]]
154
+name = "cpal"
155
+version = "0.12.1"
156
+source = "registry+https://github.com/rust-lang/crates.io-index"
157
+checksum = "be4410231e181e24e3e4612dfca601601e40942003e6a52a12d9d80f19fd9737"
158
+dependencies = [
159
+ "alsa",
160
+ "core-foundation-sys",
161
+ "coreaudio-rs",
162
+ "js-sys",
163
+ "lazy_static",
164
+ "libc",
165
+ "mach",
166
+ "nix",
167
+ "parking_lot 0.9.0",
168
+ "stdweb",
169
+ "thiserror",
170
+ "web-sys",
171
+ "winapi",
172
+]
173
+
174
+[[package]]
175
+name = "glob"
176
+version = "0.3.0"
177
+source = "registry+https://github.com/rust-lang/crates.io-index"
178
+checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
179
+
180
+[[package]]
181
+name = "instant"
182
+version = "0.1.8"
183
+source = "registry+https://github.com/rust-lang/crates.io-index"
184
+checksum = "cb1fc4429a33e1f80d41dc9fea4d108a88bec1de8053878898ae448a0b52f613"
185
+dependencies = [
186
+ "cfg-if 1.0.0",
187
+]
188
+
189
+[[package]]
190
+name = "js-sys"
191
+version = "0.3.45"
192
+source = "registry+https://github.com/rust-lang/crates.io-index"
193
+checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8"
194
+dependencies = [
195
+ "wasm-bindgen",
196
+]
197
+
198
+[[package]]
199
+name = "lazy_static"
200
+version = "1.4.0"
201
+source = "registry+https://github.com/rust-lang/crates.io-index"
202
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
203
+
204
+[[package]]
205
+name = "lazycell"
206
+version = "1.3.0"
207
+source = "registry+https://github.com/rust-lang/crates.io-index"
208
+checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
209
+
210
+[[package]]
211
+name = "libc"
212
+version = "0.2.79"
213
+source = "registry+https://github.com/rust-lang/crates.io-index"
214
+checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
215
+
216
+[[package]]
217
+name = "libloading"
218
+version = "0.5.2"
219
+source = "registry+https://github.com/rust-lang/crates.io-index"
220
+checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753"
221
+dependencies = [
222
+ "cc",
223
+ "winapi",
224
+]
225
+
226
+[[package]]
227
+name = "lock_api"
228
+version = "0.3.4"
229
+source = "registry+https://github.com/rust-lang/crates.io-index"
230
+checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
231
+dependencies = [
232
+ "scopeguard",
233
+]
234
+
235
+[[package]]
236
+name = "lock_api"
237
+version = "0.4.1"
238
+source = "registry+https://github.com/rust-lang/crates.io-index"
239
+checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c"
240
+dependencies = [
241
+ "scopeguard",
242
+]
243
+
244
+[[package]]
245
+name = "log"
246
+version = "0.4.11"
247
+source = "registry+https://github.com/rust-lang/crates.io-index"
248
+checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
249
+dependencies = [
250
+ "cfg-if 0.1.10",
251
+]
252
+
253
+[[package]]
254
+name = "mach"
255
+version = "0.3.2"
256
+source = "registry+https://github.com/rust-lang/crates.io-index"
257
+checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
258
+dependencies = [
259
+ "libc",
260
+]
261
+
262
+[[package]]
263
+name = "maybe-uninit"
264
+version = "2.0.0"
265
+source = "registry+https://github.com/rust-lang/crates.io-index"
266
+checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
267
+
268
+[[package]]
269
+name = "memchr"
270
+version = "2.3.3"
271
+source = "registry+https://github.com/rust-lang/crates.io-index"
272
+checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
273
+
274
+[[package]]
275
+name = "nix"
276
+version = "0.15.0"
277
+source = "registry+https://github.com/rust-lang/crates.io-index"
278
+checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229"
279
+dependencies = [
280
+ "bitflags",
281
+ "cc",
282
+ "cfg-if 0.1.10",
283
+ "libc",
284
+ "void",
285
+]
286
+
287
+[[package]]
288
+name = "nom"
289
+version = "5.1.2"
290
+source = "registry+https://github.com/rust-lang/crates.io-index"
291
+checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
292
+dependencies = [
293
+ "memchr",
294
+ "version_check",
295
+]
296
+
297
+[[package]]
298
+name = "parking_lot"
299
+version = "0.9.0"
300
+source = "registry+https://github.com/rust-lang/crates.io-index"
301
+checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252"
302
+dependencies = [
303
+ "lock_api 0.3.4",
304
+ "parking_lot_core 0.6.2",
305
+ "rustc_version",
306
+]
307
+
308
+[[package]]
309
+name = "parking_lot"
310
+version = "0.11.0"
311
+source = "registry+https://github.com/rust-lang/crates.io-index"
312
+checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733"
313
+dependencies = [
314
+ "instant",
315
+ "lock_api 0.4.1",
316
+ "parking_lot_core 0.8.0",
317
+]
318
+
319
+[[package]]
320
+name = "parking_lot_core"
321
+version = "0.6.2"
322
+source = "registry+https://github.com/rust-lang/crates.io-index"
323
+checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"
324
+dependencies = [
325
+ "cfg-if 0.1.10",
326
+ "cloudabi 0.0.3",
327
+ "libc",
328
+ "redox_syscall",
329
+ "rustc_version",
330
+ "smallvec 0.6.13",
331
+ "winapi",
332
+]
333
+
334
+[[package]]
335
+name = "parking_lot_core"
336
+version = "0.8.0"
337
+source = "registry+https://github.com/rust-lang/crates.io-index"
338
+checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b"
339
+dependencies = [
340
+ "cfg-if 0.1.10",
341
+ "cloudabi 0.1.0",
342
+ "instant",
343
+ "libc",
344
+ "redox_syscall",
345
+ "smallvec 1.4.2",
346
+ "winapi",
347
+]
348
+
349
+[[package]]
350
+name = "peeking_take_while"
351
+version = "0.1.2"
352
+source = "registry+https://github.com/rust-lang/crates.io-index"
353
+checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
354
+
355
+[[package]]
356
+name = "pkg-config"
357
+version = "0.3.19"
358
+source = "registry+https://github.com/rust-lang/crates.io-index"
359
+checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
360
+
361
+[[package]]
362
+name = "proc-macro2"
363
+version = "1.0.24"
364
+source = "registry+https://github.com/rust-lang/crates.io-index"
365
+checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
366
+dependencies = [
367
+ "unicode-xid",
368
+]
369
+
370
+[[package]]
371
+name = "quote"
372
+version = "1.0.7"
373
+source = "registry+https://github.com/rust-lang/crates.io-index"
374
+checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
375
+dependencies = [
376
+ "proc-macro2",
377
+]
378
+
379
+[[package]]
380
+name = "redox_syscall"
381
+version = "0.1.57"
382
+source = "registry+https://github.com/rust-lang/crates.io-index"
383
+checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
384
+
385
+[[package]]
386
+name = "regex"
387
+version = "1.4.1"
388
+source = "registry+https://github.com/rust-lang/crates.io-index"
389
+checksum = "8963b85b8ce3074fecffde43b4b0dded83ce2f367dc8d363afc56679f3ee820b"
390
+dependencies = [
391
+ "regex-syntax",
392
+]
393
+
394
+[[package]]
395
+name = "regex-syntax"
396
+version = "0.6.20"
397
+source = "registry+https://github.com/rust-lang/crates.io-index"
398
+checksum = "8cab7a364d15cde1e505267766a2d3c4e22a843e1a601f0fa7564c0f82ced11c"
399
+
400
+[[package]]
401
+name = "rustc-hash"
402
+version = "1.1.0"
403
+source = "registry+https://github.com/rust-lang/crates.io-index"
404
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
405
+
406
+[[package]]
407
+name = "rustc_version"
408
+version = "0.2.3"
409
+source = "registry+https://github.com/rust-lang/crates.io-index"
410
+checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
411
+dependencies = [
412
+ "semver",
413
+]
414
+
415
+[[package]]
416
+name = "scopeguard"
417
+version = "1.1.0"
418
+source = "registry+https://github.com/rust-lang/crates.io-index"
419
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
420
+
421
+[[package]]
422
+name = "semver"
423
+version = "0.9.0"
424
+source = "registry+https://github.com/rust-lang/crates.io-index"
425
+checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
426
+dependencies = [
427
+ "semver-parser",
428
+]
429
+
430
+[[package]]
431
+name = "semver-parser"
432
+version = "0.7.0"
433
+source = "registry+https://github.com/rust-lang/crates.io-index"
434
+checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
435
+
436
+[[package]]
437
+name = "shlex"
438
+version = "0.1.1"
439
+source = "registry+https://github.com/rust-lang/crates.io-index"
440
+checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
441
+
442
+[[package]]
443
+name = "smallvec"
444
+version = "0.6.13"
445
+source = "registry+https://github.com/rust-lang/crates.io-index"
446
+checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6"
447
+dependencies = [
448
+ "maybe-uninit",
449
+]
450
+
451
+[[package]]
452
+name = "smallvec"
453
+version = "1.4.2"
454
+source = "registry+https://github.com/rust-lang/crates.io-index"
455
+checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
456
+
457
+[[package]]
458
+name = "stdweb"
459
+version = "0.1.3"
460
+source = "registry+https://github.com/rust-lang/crates.io-index"
461
+checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e"
462
+
463
+[[package]]
464
+name = "syn"
465
+version = "1.0.48"
466
+source = "registry+https://github.com/rust-lang/crates.io-index"
467
+checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
468
+dependencies = [
469
+ "proc-macro2",
470
+ "quote",
471
+ "unicode-xid",
472
+]
473
+
474
+[[package]]
475
+name = "thiserror"
476
+version = "1.0.21"
477
+source = "registry+https://github.com/rust-lang/crates.io-index"
478
+checksum = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42"
479
+dependencies = [
480
+ "thiserror-impl",
481
+]
482
+
483
+[[package]]
484
+name = "thiserror-impl"
485
+version = "1.0.21"
486
+source = "registry+https://github.com/rust-lang/crates.io-index"
487
+checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab"
488
+dependencies = [
489
+ "proc-macro2",
490
+ "quote",
491
+ "syn",
492
+]
493
+
494
+[[package]]
495
+name = "unicode-xid"
496
+version = "0.2.1"
497
+source = "registry+https://github.com/rust-lang/crates.io-index"
498
+checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
499
+
500
+[[package]]
501
+name = "version_check"
502
+version = "0.9.2"
503
+source = "registry+https://github.com/rust-lang/crates.io-index"
504
+checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
505
+
506
+[[package]]
507
+name = "void"
508
+version = "1.0.2"
509
+source = "registry+https://github.com/rust-lang/crates.io-index"
510
+checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
511
+
512
+[[package]]
513
+name = "wasm-bindgen"
514
+version = "0.2.68"
515
+source = "registry+https://github.com/rust-lang/crates.io-index"
516
+checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42"
517
+dependencies = [
518
+ "cfg-if 0.1.10",
519
+ "wasm-bindgen-macro",
520
+]
521
+
522
+[[package]]
523
+name = "wasm-bindgen-backend"
524
+version = "0.2.68"
525
+source = "registry+https://github.com/rust-lang/crates.io-index"
526
+checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68"
527
+dependencies = [
528
+ "bumpalo",
529
+ "lazy_static",
530
+ "log",
531
+ "proc-macro2",
532
+ "quote",
533
+ "syn",
534
+ "wasm-bindgen-shared",
535
+]
536
+
537
+[[package]]
538
+name = "wasm-bindgen-macro"
539
+version = "0.2.68"
540
+source = "registry+https://github.com/rust-lang/crates.io-index"
541
+checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038"
542
+dependencies = [
543
+ "quote",
544
+ "wasm-bindgen-macro-support",
545
+]
546
+
547
+[[package]]
548
+name = "wasm-bindgen-macro-support"
549
+version = "0.2.68"
550
+source = "registry+https://github.com/rust-lang/crates.io-index"
551
+checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe"
552
+dependencies = [
553
+ "proc-macro2",
554
+ "quote",
555
+ "syn",
556
+ "wasm-bindgen-backend",
557
+ "wasm-bindgen-shared",
558
+]
559
+
560
+[[package]]
561
+name = "wasm-bindgen-shared"
562
+version = "0.2.68"
563
+source = "registry+https://github.com/rust-lang/crates.io-index"
564
+checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307"
565
+
566
+[[package]]
567
+name = "web-sys"
568
+version = "0.3.45"
569
+source = "registry+https://github.com/rust-lang/crates.io-index"
570
+checksum = "4bf6ef87ad7ae8008e15a355ce696bed26012b7caa21605188cfd8214ab51e2d"
571
+dependencies = [
572
+ "js-sys",
573
+ "wasm-bindgen",
574
+]
575
+
576
+[[package]]
577
+name = "winapi"
578
+version = "0.3.9"
579
+source = "registry+https://github.com/rust-lang/crates.io-index"
580
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
581
+dependencies = [
582
+ "winapi-i686-pc-windows-gnu",
583
+ "winapi-x86_64-pc-windows-gnu",
584
+]
585
+
586
+[[package]]
587
+name = "winapi-i686-pc-windows-gnu"
588
+version = "0.4.0"
589
+source = "registry+https://github.com/rust-lang/crates.io-index"
590
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
591
+
592
+[[package]]
593
+name = "winapi-x86_64-pc-windows-gnu"
594
+version = "0.4.0"
595
+source = "registry+https://github.com/rust-lang/crates.io-index"
596
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

+ 12 - 0
Cargo.toml View File

@@ -0,0 +1,12 @@
1
+[package]
2
+name = "beeptestrs"
3
+version = "0.1.0"
4
+authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
5
+edition = "2018"
6
+
7
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8
+
9
+[dependencies]
10
+cpal = "0.12.1"
11
+anyhow = "1.0.33"
12
+parking_lot = "0.11.0"

+ 152 - 0
src/beep.rs View File

@@ -0,0 +1,152 @@
1
+use std::sync::Arc;
2
+
3
+use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
4
+use std::ops::{Add, AddAssign, Sub};
5
+use parking_lot::RwLock;
6
+use std::time::Duration;
7
+use std::thread;
8
+use cpal::SampleFormat;
9
+use crate::beep::osc::{Instrument, Oscillator};
10
+
11
+pub mod osc;
12
+
13
+struct AudioContext {
14
+    device: cpal::Device,
15
+    sample_format: SampleFormat,
16
+    config: cpal::StreamConfig
17
+}
18
+
19
+fn al_init() -> Result<AudioContext, anyhow::Error> {
20
+    let host = cpal::default_host();
21
+
22
+    let device = host.default_output_device()
23
+        .ok_or_else(|| anyhow::anyhow!("No audio output device!"))?;
24
+
25
+    let config = device.default_output_config()?;
26
+
27
+    Ok(AudioContext {
28
+        device,
29
+        sample_format: config.sample_format(),
30
+        config: config.into()
31
+    })
32
+}
33
+
34
+pub fn beep() -> Result<(), anyhow::Error> {
35
+    let ac = al_init()?;
36
+
37
+    match ac.sample_format {
38
+        cpal::SampleFormat::F32 => run::<f32>(&ac.device, &ac.config.into())?,
39
+        cpal::SampleFormat::I16 => run::<i16>(&ac.device, &ac.config.into())?,
40
+        cpal::SampleFormat::U16 => run::<u16>(&ac.device, &ac.config.into())?,
41
+    }
42
+
43
+    Ok(())
44
+}
45
+
46
+
47
+fn run<T>(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyhow::Error>
48
+    where
49
+        T: cpal::Sample,
50
+{
51
+    let sample_rate = config.sample_rate.0 as f32;
52
+    let channels = config.channels as usize;
53
+
54
+    println!("SR={}",sample_rate);
55
+
56
+    // Produce a sinusoid of maximum amplitude.
57
+
58
+    let mut ins = Instrument::new(vec![
59
+        Oscillator::new(440.0, 1.0, sample_rate),
60
+        //Oscillator::new(441.0, 1.0, sample_rate),
61
+    ], sample_rate);
62
+
63
+    let instrument = Arc::new(parking_lot::RwLock::new(ins));
64
+
65
+    let err_fn = |err| eprintln!("an error occurred on stream: {}", err);
66
+
67
+    let m_instrument = instrument.clone();
68
+    let stream = device.build_output_stream(
69
+        config,
70
+        move |data: &mut [T], _: &cpal::OutputCallbackInfo| {
71
+            write_data(data, channels, &m_instrument)
72
+        },
73
+        err_fn,
74
+    )?;
75
+    stream.play()?;
76
+
77
+    // instrument.write().normalize(false);
78
+    // instrument.write().set_amp(1.0);
79
+    // thread::sleep(Duration::from_millis(500));
80
+    //
81
+    // instrument.write().set_amp(0.0);
82
+    // thread::sleep(Duration::from_millis(250));
83
+
84
+    instrument.write().set_amp(1.0);
85
+    instrument.write().normalize(true);
86
+    thread::sleep(Duration::from_millis(500));
87
+
88
+    instrument.write().set_amp(0.0);
89
+    thread::sleep(Duration::from_millis(250));
90
+
91
+    instrument.write().set_amp(0.5);
92
+    thread::sleep(Duration::from_millis(500));
93
+
94
+    instrument.write().set_amp(0.0);
95
+    thread::sleep(Duration::from_millis(250));
96
+
97
+    instrument.write().fade_amp(1.0, Duration::from_millis(10));
98
+    instrument.write().waveforms[0].fade_balance(-1.0, Duration::from_millis(10));
99
+    thread::sleep(Duration::from_millis(500));
100
+    instrument.write().fade_amp(0.0, Duration::from_millis(10));
101
+    thread::sleep(Duration::from_millis(500));
102
+
103
+
104
+    /*
105
+
106
+    //instrument.write().set_amp_fade(0.0, 4.0);
107
+
108
+    thread::sleep(Duration::from_millis(1000));
109
+    instrument.write().waveforms[1].fade_amp(0.0, Duration::from_millis(500));
110
+    thread::sleep(Duration::from_millis(500));
111
+
112
+    instrument.write().waveforms[0].fade_balance(-1.0, Duration::from_millis(1000));
113
+    thread::sleep(Duration::from_millis(1000));
114
+    instrument.write().waveforms[0].fade_balance(1.0, Duration::from_millis(2000));
115
+    instrument.write().waveforms[0].fade_freq(880.0, Duration::from_millis(1000));
116
+    thread::sleep(Duration::from_millis(2000));
117
+    instrument.write().waveforms[0].fade_balance(-1.0, Duration::from_millis(1000));
118
+    instrument.write().waveforms[0].fade_amp(0.0, Duration::from_millis(1000));
119
+    thread::sleep(Duration::from_millis(1000));
120
+*/
121
+
122
+    // for _ in 0..10
123
+    // {
124
+    //     instrument.write().harmonics[0].set_balance_fade(-1.0, 0.1);
125
+    //     thread::sleep(Duration::from_millis(100));
126
+    //     instrument.write().harmonics[0].set_balance_fade(1.0, 0.1);
127
+    //     thread::sleep(Duration::from_millis(100));
128
+    // }
129
+    //
130
+    // instrument.write().harmonics[0].set_balance_fade(0.0, 0.5);
131
+    // instrument.write().harmonics[0].set_amp_fade(0.0, 0.05);
132
+    // thread::sleep(Duration::from_millis(200));
133
+
134
+    Ok(())
135
+}
136
+
137
+fn write_data<T>(output: &mut [T], channels: usize, instrument: &Arc<RwLock<Instrument>>)
138
+    where
139
+        T: cpal::Sample,
140
+{
141
+    let mut instrument = instrument.write();
142
+    for frame in output.chunks_mut(channels) {
143
+        let sample = instrument.sample();
144
+
145
+        if channels == 1 {
146
+            frame[0] = cpal::Sample::from::<f32>(&((sample.left + sample.right) / 2.0));
147
+        } else {
148
+            frame[0] = cpal::Sample::from::<f32>(&sample.left);
149
+            frame[1] = cpal::Sample::from::<f32>(&sample.right);
150
+        }
151
+    }
152
+}

+ 424 - 0
src/beep/osc.rs View File

@@ -0,0 +1,424 @@
1
+use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign};
2
+use std::sync::Arc;
3
+use std::time::Duration;
4
+
5
+use parking_lot::RwLock;
6
+
7
+use hack::StableClamp;
8
+
9
+mod hack {
10
+    /// Work-around for `f32::clamp()` being unstable
11
+    pub(super) trait StableClamp {
12
+        fn clamp_(self, min: f32, max: f32) -> f32;
13
+    }
14
+
15
+    impl StableClamp for f32 {
16
+        fn clamp_(self, min: f32, max: f32) -> f32 {
17
+            assert!(min <= max);
18
+            let mut x = self;
19
+            if x < min {
20
+                x = min;
21
+            }
22
+            if x > max {
23
+                x = max;
24
+            }
25
+            x
26
+        }
27
+    }
28
+}
29
+
30
+/// Value fader
31
+#[derive(Clone,Debug)]
32
+struct Tween {
33
+    /// Actual value
34
+    actual: f32,
35
+    /// Target value, approached by adding "step" to :actual" every sample
36
+    target: f32,
37
+    /// Value added to "actual" per sample. None = set to target at the end of a cycle
38
+    step: Option<f32>,
39
+    // Min value
40
+    min: f32,
41
+    // Max value
42
+    max: f32,
43
+}
44
+
45
+/// Scale function amplitude by a gain
46
+trait GainScale {
47
+    fn scale_gain(self, gain: f32) -> Self;
48
+}
49
+
50
+impl GainScale for f32 {
51
+    /// Scale by gain 0-1
52
+    fn scale_gain(self, gain: f32) -> Self {
53
+        // TODO what scaling to use?
54
+        // self * (1.0 + gain * 9.0).log10()
55
+        // self * gain.sqrt()
56
+        self * gain
57
+    }
58
+}
59
+
60
+impl Tween {
61
+    /// Update values for the next sample
62
+    pub fn tick(&mut self) {
63
+        if let Some(step) = self.step {
64
+            let actual0 = self.actual;
65
+            self.actual += step;
66
+            if (self.target > actual0 && self.target <= self.actual)
67
+                || (self.target <= actual0 && self.target > self.actual)
68
+            {
69
+                self.step = None;
70
+                self.actual = self.target;
71
+            }
72
+        }
73
+    }
74
+
75
+    /// Get actual value
76
+    pub fn actual(&self) -> f32 {
77
+        self.actual
78
+    }
79
+
80
+    /// Check if the change from actual to target is scheduled to happen instantly
81
+    /// at the end of a cycle.
82
+    pub fn is_change_scheduled_at_crossover(&self) -> bool {
83
+        self.target != self.actual
84
+            && self.step.is_none()
85
+    }
86
+
87
+    /// Check if the fading is in progress
88
+    pub fn is_fading(&self) -> bool {
89
+        self.step.is_some()
90
+    }
91
+
92
+    /// Schedule change at crossover
93
+    fn set_at_crossover(&mut self, val: f32) {
94
+        self.target = val.clamp_(self.min, self.max);
95
+        self.step = None; // change at the earliest convenience
96
+    }
97
+
98
+    /// Start fading
99
+    fn fade(&mut self, val: f32, time: Duration, sample_rate: f32) {
100
+        self.target = val.clamp_(self.min, self.max);
101
+        let nsamples = (time.as_secs_f32() * sample_rate).ceil();
102
+        self.step = Some((self.target - self.actual) / nsamples);
103
+    }
104
+
105
+    /// Set value immediately
106
+    fn set_immediate(&mut self, val: f32) {
107
+        self.target = val;
108
+        self.actual = val;
109
+        self.step = None;
110
+    }
111
+}
112
+
113
+#[derive(Clone,Debug)]
114
+pub struct Oscillator {
115
+    /// Amplitude fader 0..1
116
+    gain: Tween,
117
+    /// Frequency fader 1..22050
118
+    freq: Tween,
119
+    /// Balance fader -1..1
120
+    balance: Tween,
121
+    /// Left channel multiplier
122
+    gain_left: f32,
123
+    /// Right channel multiplier
124
+    gain_right: f32,
125
+    /// Sample rate (Hz)
126
+    sample_rate: f32,
127
+    /// Phase increment per sample
128
+    phase_step: f32,
129
+    /// Phase 0-TAU
130
+    phase: f32,
131
+}
132
+
133
+impl Oscillator {
134
+    /// New oscillator with a frequency and amplitude
135
+    pub fn new(freq: f32, amp: f32, sample_rate : f32) -> Self {
136
+        let mut o = Self {
137
+            gain: Tween {
138
+                actual: amp,
139
+                target: amp,
140
+                min: 0.0,
141
+                max: 1.0,
142
+                step: None
143
+            },
144
+            freq: Tween {
145
+                actual: freq,
146
+                target: freq,
147
+                min: 1.0,
148
+                max: sample_rate / 2.0,
149
+                step: None
150
+            },
151
+            balance: Tween {
152
+                actual: 0.0,
153
+                target: 0.0,
154
+                min: -1.0,
155
+                max: 1.0,
156
+                step: None
157
+            },
158
+            phase_step: 0.0,
159
+            gain_left: 0.0,
160
+            gain_right: 0.0,
161
+            sample_rate,
162
+            phase: 0.0
163
+        };
164
+        o.update_step();
165
+        o.update_gains(1.0);
166
+        o
167
+    }
168
+
169
+    /// Update the step variable
170
+    fn update_step(&mut self) {
171
+        self.phase_step = (self.freq.actual / self.sample_rate) * std::f32::consts::TAU;
172
+    }
173
+
174
+    /// Update the pre-computed channel gain variables
175
+    fn update_gains(&mut self, gain_scale: f32) {
176
+        let angle = ((1.0 + self.balance.actual) / 2.0) * std::f32::consts::FRAC_PI_2;
177
+        let act = self.gain.actual * gain_scale;
178
+        self.gain_left = act.scale_gain(angle.sin());
179
+        self.gain_right = act.scale_gain(angle.cos());
180
+    }
181
+
182
+    /// Get actual amplitude (0..1)
183
+    pub fn get_amp(&self) -> f32 {
184
+        self.gain.actual
185
+    }
186
+
187
+    /// Set amplitude (0..1), change at the end of the current waveform to reduce popping
188
+    pub fn set_amp(&mut self, amp : f32) {
189
+        self.gain.set_at_crossover(amp);
190
+    }
191
+
192
+    /// Fade to amplitude (0..1) over a given time
193
+    pub fn fade_amp(&mut self, amp : f32, time: Duration) {
194
+        self.gain.fade(amp, time, self.sample_rate);
195
+    }
196
+
197
+    /// Get balance value (-1..1)
198
+    pub fn get_balance(&self) -> f32 {
199
+        self.balance.actual
200
+    }
201
+
202
+    /// Set balance (-1..1), change at the end of the current waveform to reduce popping
203
+    pub fn set_balance(&mut self, balance : f32) {
204
+        self.balance.set_at_crossover(balance);
205
+    }
206
+
207
+    /// Fade to balance (-1..1) over a given time
208
+    pub fn fade_balance(&mut self, balance : f32, time: Duration) {
209
+        self.balance.fade(balance, time, self.sample_rate);
210
+    }
211
+
212
+    /// Get frequency value (1..22050)
213
+    pub fn get_freq(&self) -> f32 {
214
+        self.freq.actual
215
+    }
216
+
217
+    /// Set frequency (1..22050), change at the end of the current waveform to reduce popping
218
+    pub fn set_freq(&mut self, freq : f32) {
219
+        self.freq.set_at_crossover(freq);
220
+    }
221
+
222
+    /// Fade to frequency (1..22050) over a given time
223
+    pub fn fade_freq(&mut self, freq : f32, time: Duration) {
224
+        self.freq.fade(freq, time, self.sample_rate);
225
+    }
226
+
227
+    /// Take a sample. Mutable to update faders.
228
+    pub fn sample(&mut self, gain_scale: f32) -> StereoSample {
229
+        if self.freq.is_fading() {
230
+            self.freq.tick();
231
+            self.update_step();
232
+        }
233
+
234
+        if self.balance.is_fading() || self.gain.is_fading() {
235
+            self.balance.tick();
236
+            self.gain.tick();
237
+            self.update_gains(gain_scale);
238
+        }
239
+
240
+        let ph0 = self.phase;
241
+        self.phase = (self.phase + self.phase_step) % std::f32::consts::TAU;
242
+
243
+        // "zero crossing detection"
244
+        if self.phase < ph0 {
245
+            if self.gain.is_change_scheduled_at_crossover() {
246
+                self.gain.actual = self.gain.target;
247
+                self.update_gains(gain_scale);
248
+            }
249
+
250
+            if self.balance.is_change_scheduled_at_crossover() {
251
+                self.balance.actual = self.balance.target;
252
+                self.update_gains(gain_scale);
253
+            }
254
+
255
+            if self.freq.is_change_scheduled_at_crossover() {
256
+                self.freq.actual = self.freq.target;
257
+            }
258
+        }
259
+
260
+        let y = self.phase.sin() * self.gain.actual;
261
+
262
+        StereoSample {
263
+            left: self.gain_left * y,
264
+            right: self.gain_right * y,
265
+        }
266
+    }
267
+}
268
+
269
+/// Waveform generator with multiple frequencies / waveforms
270
+#[derive(Clone,Debug)]
271
+pub struct Instrument {
272
+    /// Waveforms to combine to produce the final sound
273
+    pub waveforms: Vec<Oscillator>,
274
+    /// Normalize the output to produce constant amplitude of 1.0 (instead of clipping)
275
+    normalize: bool,
276
+    sample_rate : f32,
277
+    master: Tween,
278
+}
279
+
280
+impl Instrument {
281
+    pub fn new(waveforms: Vec<Oscillator>, sample_rate: f32) -> Self {
282
+        let mut o = Self {
283
+            waveforms,
284
+            normalize: false,
285
+            sample_rate,
286
+            master: Tween {
287
+                actual: 1.0,
288
+                target: 1.0,
289
+                step: None,
290
+                min: 0.0,
291
+                max: 1.0
292
+            }
293
+        };
294
+        o
295
+    }
296
+
297
+    pub fn normalize(&mut self, normalize: bool) {
298
+        self.normalize = normalize;
299
+
300
+        // This is an artifact of how Oscillator works internally:
301
+        // The left/right gains are pre-computed and updated only when they change
302
+        if normalize {
303
+            self.force_normalize();
304
+        } else {
305
+            self.waveforms.iter_mut()
306
+                .for_each(|item| item.update_gains(1.0));
307
+        }
308
+    }
309
+
310
+    pub fn get_amp(&self) -> f32 {
311
+        self.master.actual()
312
+    }
313
+
314
+    pub fn set_amp(&mut self, amp : f32) {
315
+        self.master.set_immediate(amp);
316
+    }
317
+
318
+    pub fn fade_amp(&mut self, amp : f32, time: Duration) {
319
+        self.master.fade(amp, time, self.sample_rate);
320
+    }
321
+
322
+    fn force_normalize(&mut self) {
323
+        let gain_scale = 1.0 / self.waveforms.iter()
324
+            .fold((0.0), |acc, item| acc + item.gain.actual);
325
+
326
+        println!("Gain scale {}", gain_scale);
327
+        self.waveforms.iter_mut()
328
+            .for_each(|item| item.update_gains(gain_scale));
329
+    }
330
+
331
+    /// Get a sample (left, right)
332
+    pub fn sample(&mut self) -> StereoSample {
333
+        self.master.tick();
334
+
335
+        let gain_scaling = if self.normalize {
336
+            1.0 / self.waveforms.iter()
337
+                .fold((0.0), |acc, item| acc + item.gain.actual)
338
+        } else {
339
+            1.0
340
+        };
341
+
342
+        let (mut value, gains)= self.waveforms.iter_mut()
343
+            .fold((StereoSample::default(), 0.0), |mut acc, item| {
344
+                acc.0 += item.sample(gain_scaling);
345
+                acc.1 += item.gain.actual;
346
+                acc
347
+            });
348
+
349
+        if self.normalize {
350
+            value.left /= gains;
351
+            value.right /= gains;
352
+        }
353
+
354
+        value.left = value.left.scale_gain(self.master.actual).clamp_(-1.0, 1.0);
355
+        value.right = value.right.scale_gain(self.master.actual).clamp_(-1.0, 1.0);
356
+        value.clip()
357
+    }
358
+}
359
+
360
+#[derive(Default, Clone, Copy)]
361
+pub struct StereoSample {
362
+    pub left: f32,
363
+    pub right: f32,
364
+}
365
+
366
+impl StereoSample {
367
+    pub fn clip(self) -> Self {
368
+        Self {
369
+            left: self.left.clamp_(-1.0, 1.0),
370
+            right: self.right.clamp_(-1.0, 1.0),
371
+        }
372
+    }
373
+}
374
+
375
+impl Add for StereoSample {
376
+    type Output = Self;
377
+
378
+    fn add(self, rhs: Self) -> Self::Output {
379
+        Self {
380
+            left: self.left + rhs.left,
381
+            right: self.right + rhs.right,
382
+        }
383
+    }
384
+}
385
+
386
+impl Div<f32> for StereoSample {
387
+    type Output = Self;
388
+
389
+    fn div(self, rhs: f32) -> Self::Output {
390
+        Self {
391
+            left: self.left / rhs,
392
+            right: self.right / rhs,
393
+        }
394
+    }
395
+}
396
+
397
+impl Mul<f32> for StereoSample {
398
+    type Output = Self;
399
+
400
+    fn mul(self, rhs: f32) -> Self::Output {
401
+        Self {
402
+            left: self.left * rhs,
403
+            right: self.right * rhs,
404
+        }
405
+    }
406
+}
407
+
408
+impl AddAssign<Self> for StereoSample {
409
+    fn add_assign(&mut self, rhs: Self) {
410
+        *self = *self + rhs
411
+    }
412
+}
413
+
414
+impl DivAssign<f32> for StereoSample {
415
+    fn div_assign(&mut self, rhs: f32) {
416
+        *self = *self / rhs
417
+    }
418
+}
419
+
420
+impl MulAssign<f32> for StereoSample {
421
+    fn mul_assign(&mut self, rhs: f32) {
422
+        *self = *self * rhs
423
+    }
424
+}

+ 7 - 0
src/main.rs View File

@@ -0,0 +1,7 @@
1
+mod beep;
2
+extern crate anyhow;
3
+extern crate cpal;
4
+
5
+fn main() -> Result<(), anyhow::Error> {
6
+    beep::beep()
7
+}