openaudio-suite 2.5.5 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +163 -109
- package/CONTRIBUTING.md +39 -324
- package/OpenAudio.js +314 -159
- package/OpenAudio_r.js +211 -164
- package/OpenAudio_s.js +420 -204
- package/README.md +142 -122
- package/docs/OPENAUDIO_R.md +135 -143
- package/docs/OPENAUDIO_S.md +162 -377
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,201 +2,255 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the OpenAudio suite are documented here.
|
|
4
4
|
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
|
-
|
|
7
|
-
## v2.5.0 – 2026-03-15
|
|
8
|
-
### Improvements
|
|
9
|
-
- Added consistent **semantic versioning** strategy across releases.
|
|
10
|
-
- Added **package-lock.json** to support reproducible installs and GitHub Actions CI.
|
|
11
|
-
- No changes to OpenAudio JS files; all improvements are tooling and release management.
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
12
6
|
|
|
13
7
|
---
|
|
14
8
|
|
|
15
9
|
## OpenAudio.js
|
|
16
10
|
|
|
17
|
-
### [1.
|
|
11
|
+
### [1.3.0] — 2026-03-16
|
|
12
|
+
|
|
13
|
+
#### Fixed
|
|
14
|
+
- **`#isUnlocked` flag** — The silent-MP3 unlock is now performed only once per instance. Previously, every `play()` call (including replays) re-ran the full unlock dance: discarding all buffered/preloaded audio, reassigning `src` to the silent MP3, awaiting the Promise, then restoring the real `src`. This meant the constructor's `preload = 'auto'` was silently wasted on every play. From 1.3.0, subsequent `play()` calls skip directly to `#playClip()` — preloaded data is preserved and replay latency is eliminated.
|
|
15
|
+
- **`#playCancelled` flag** — `stop()` now plants a cancellation token that the in-flight unlock `.then()` checks before calling `#playClip()`. Previously, calling `stop()` during an async unlock was a race: the `.then()` would resolve ~50–200 ms later and start the clip anyway, overriding the explicit stop.
|
|
16
|
+
- **`isPlaying` encapsulation** — `isPlaying` is now a private field (`#isPlaying`) exposed via a read-only getter. Previously a plain public property, it could be written by callers to silently corrupt the state machine.
|
|
17
|
+
- **`#isPlaying` race in `#playClip()`** — `#isPlaying` is now set `true` synchronously before `#audio.play()`, closing the double-play race window where a rapid second `play()` call between the `.play()` call and its `.then()` resolution could bypass the guard and attempt concurrent playback. `#isPlaying` is reverted in `.catch()` on failure.
|
|
18
|
+
- **`resume()` race in `#onVisibilityChange()`** — The visibility-restore `.play()` path now uses the same pre-set/revert pattern. Previously, a concurrent `stop()` in the `.then()` window could leave `isPlaying = true` after `stop()` had set it to `false`.
|
|
19
|
+
- **`destroy()` resource release** — Uses `removeAttribute('src')` + `load()` per the WHATWG HTMLMediaElement resource-release spec, replacing `src = ''` which can fire spurious `error` events on some browsers.
|
|
20
|
+
- **`#pausedByVisibility` in `destroy()`** — Now reset to `false` during teardown for consistent state.
|
|
21
|
+
- **Removed unreachable optional chain** — `this.#audio?.pause()` in `#playClip()` `.then()` was unreachable since `#isDestroyed` implies `destroy()` already paused; removed to avoid misleading readers.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
### [1.2.0] — 2026-03-16
|
|
26
|
+
|
|
27
|
+
#### Fixed
|
|
28
|
+
- **Unlock now plays on the shared `#audio` element** — Previously the silent MP3 was played on a throwaway `new Audio()`, blessing the wrong element. On iOS Safari this caused `NotAllowedError` for every clip after the first. The shared element is now unlocked directly.
|
|
29
|
+
- **`#isDestroyed` flag** — All public methods (`play`, `stop`, `destroy`) are safe no-ops after `destroy()` has been called, preventing throws on the nulled `#audio` element.
|
|
30
|
+
- **Double-`destroy()` safe** — Calling `destroy()` more than once no longer throws.
|
|
31
|
+
- **`ended` listener stored and removed** — `#endedHandler` is now stored as a named reference and removed in `destroy()`, preventing a callback-into-null race on teardown.
|
|
32
|
+
- **`canPlay()` input guard** — Returns `false` for non-string or empty input rather than passing `undefined` to `canPlayType()`.
|
|
33
|
+
|
|
34
|
+
---
|
|
18
35
|
|
|
36
|
+
### [1.1.0] — 2026-03-16
|
|
19
37
|
#### Added
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
38
|
+
- Background tab detection via Page Visibility API (`document.visibilitychange`)
|
|
39
|
+
- `pauseOnHidden` option — pause audio when tab hides, resume when tab returns
|
|
40
|
+
- `onHidden` callback — fires when tab becomes hidden
|
|
41
|
+
- `onVisible` callback — fires when tab becomes visible
|
|
42
|
+
- `#boundVisibility` bound listener — proper cleanup prevents stale listeners in SPAs
|
|
25
43
|
|
|
26
44
|
#### Fixed
|
|
27
|
-
- Stale visibilitychange listeners in SPA environments
|
|
45
|
+
- Stale `visibilitychange` listeners in SPA environments
|
|
28
46
|
- Listener cleanup in `destroy()` now removes the exact function reference
|
|
29
47
|
|
|
30
48
|
#### Changed
|
|
31
49
|
- Class renamed: `SingleAudio` → `OpenAudio` (matches filename)
|
|
32
|
-
- Improved documentation with background tab behavior examples
|
|
33
50
|
|
|
34
51
|
---
|
|
35
52
|
|
|
36
|
-
### [1.0.0] —
|
|
53
|
+
### [1.0.0] — 2026-03-16
|
|
37
54
|
|
|
38
55
|
#### Added
|
|
39
|
-
- Initial release:
|
|
56
|
+
- Initial release: single-clip, one-shot player
|
|
40
57
|
- Silent MP3 unlock for autoplay policy compliance
|
|
41
58
|
- `onPlay` and `onEnd` callbacks
|
|
42
59
|
- `destroy()` method for SPA cleanup
|
|
43
60
|
- `canPlay()` static format checking
|
|
44
|
-
- Complete API documentation
|
|
45
61
|
|
|
46
62
|
---
|
|
47
63
|
|
|
48
64
|
## OpenAudio_s.js
|
|
49
65
|
|
|
50
|
-
### [1.
|
|
66
|
+
### [1.3.0] — 2026-03-16
|
|
67
|
+
|
|
68
|
+
#### Fixed
|
|
69
|
+
- **`#playCancelled` flag** — `stop()` and `reset()` now plant a cancellation token that the in-flight silent-MP3 unlock `.then()` checks before calling `#playClip()`. Previously, calling `reset()` or `stop()` during the ~50–200 ms unlock window was silently ignored: the unlock `.then()` would fire unconditionally and start the first clip regardless. The fix mirrors the pattern introduced in `OpenAudio.js` 1.3.0.
|
|
70
|
+
|
|
71
|
+
#### Changed
|
|
72
|
+
- **Usage example** — `goto(getCurrentIndex() - 1)` in the guided-tour example now uses `Math.max(0, …)`, preventing a spurious out-of-range `console.warn` when the user is already on clip 0.
|
|
73
|
+
|
|
74
|
+
#### Documentation
|
|
75
|
+
- `stop()` method note updated to describe both the auto-advance timer cancellation (v1.2.0) and the new unlock cancellation (v1.3.0).
|
|
76
|
+
- `play()` JSDoc updated with a design note explaining that `#isStarted` becomes `true` after the unlock resolves (inside `#playClip()`), and why this intentionally differs from `OpenAudio_r.js`.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### [1.2.0] — 2026-03-16
|
|
81
|
+
|
|
82
|
+
#### Fixed
|
|
83
|
+
- **`isPlaying` + `isStarted` encapsulation** — Both are now private fields (`#isPlaying`, `#isStarted`) exposed via read-only getters. Previously plain public properties, they could be written by callers to silently corrupt the state machine and all navigation guards.
|
|
84
|
+
- **`#isPlaying` / `#isStarted` race in `#playClip()`** — Both flags are now set `true` synchronously before `#audio.play()`, closing the double-play race window where a rapid `next()` or `goto()` between the `.play()` call and its `.then()` resolution could bypass guards and abort a starting clip. `#isPlaying` is reverted in `.catch()` on failure.
|
|
85
|
+
- **`#advanceTimer`** — The auto-advance `setTimeout` handle is now stored and cleared by `stop()`, `reset()`, and `destroy()`. Previously, calling `stop()` or `reset()` during the advance delay window could not cancel the pending `next()`, causing the next clip to start after an explicit stop.
|
|
86
|
+
- **`resume()` race** — `#isPlaying` is now set `true` synchronously before `#audio.play()` in `resume()`, then reverted in `.catch()`. Previously the `.then()` could set `isPlaying = true` after a concurrent `stop()` had already set it to `false`.
|
|
87
|
+
- **`destroy()` resource release** — Uses `removeAttribute('src')` + `load()` per the WHATWG HTMLMediaElement resource-release spec, replacing `src = ''`.
|
|
88
|
+
- **`getCurrentClip()` live reference** — Now returns a shallow copy `({ ...clip })` instead of a direct reference to the internal clip object. Callers can no longer mutate the internal playlist via the returned object.
|
|
89
|
+
- **`getClips()` live references** — Now deep-copies inner objects via `map(c => ({ ...c }))`. Previously the shallow spread copied the array but left inner object references live.
|
|
90
|
+
- **Removed unreachable optional chain** — `this.#audio?.pause()` in `#playClip()` `.then()` removed.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
### [1.1.0] — 2026-03-16
|
|
95
|
+
|
|
96
|
+
#### Fixed
|
|
97
|
+
- **Unlock now plays on the shared `#audio` element** — Same root cause as `OpenAudio.js` 1.2.0.
|
|
98
|
+
- **`play()` guard extended** — Now checks `isStarted` in addition to `isPlaying` and `#isUnlocking`.
|
|
99
|
+
- **`next()` / `goto()` / `gotoLabel()` guard** — All return immediately if `!isStarted`.
|
|
100
|
+
- **`pause()` / `resume()` guard** — Both check `isStarted` before touching the Audio element.
|
|
101
|
+
- **`destroy()` teardown** — `ended` listener removed via stored `#endedHandler` reference before `#audio` is nulled.
|
|
102
|
+
- **`#isDestroyed` flag** — All public methods are safe no-ops after `destroy()`.
|
|
103
|
+
|
|
104
|
+
#### Added
|
|
105
|
+
- `advanceDelay` option (default `0.5s`) — replaces the hardcoded 500ms gap between auto-advance clips.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
### [1.0.0] — 2026-03-16
|
|
51
110
|
|
|
52
111
|
#### Added
|
|
53
|
-
- Initial release:
|
|
112
|
+
- Initial release: sequential playlist player
|
|
54
113
|
- Click-to-advance playback control
|
|
55
|
-
- Jump to clip by index or label
|
|
114
|
+
- Jump to clip by index (`goto()`) or label (`gotoLabel()`)
|
|
56
115
|
- Pause/resume support
|
|
57
|
-
- Progress tracking
|
|
58
|
-
- Complete API documentation with usage patterns
|
|
116
|
+
- Progress tracking (`getCurrentClip()`, `getCurrentIndex()`, `getClipCount()`)
|
|
59
117
|
|
|
60
118
|
---
|
|
61
119
|
|
|
62
120
|
## OpenAudio_r.js
|
|
63
121
|
|
|
64
|
-
### [2.
|
|
65
|
-
|
|
66
|
-
### Added
|
|
67
|
-
- **`#isUnlocking` flag** — Prevents duplicate unlock attempts when `start()` is called rapidly (spam-clicks). The unlock play() is async; without this flag, a race condition allowed multiple overlapping unlock sequences.
|
|
68
|
-
- **`destroy()` method** — Removes the `visibilitychange` listener. Essential for SPAs (React, Vue, Svelte, etc.) where engine instances are created and destroyed across component lifecycles. Without it, stale listeners accumulate on `document` and defunct engines wake up on every tab-focus event.
|
|
69
|
-
- **`AudioEngine.canPlay(type)` static method** — Wraps `HTMLAudioElement.canPlayType()` with a clean boolean return. Use to check browser support for `.ogg`, `.wav`, or `.flac` before constructing an engine, rather than discovering a silent failure at play() time.
|
|
70
|
-
- **Callback resilience for `onCycleReset`** — Wrapped in try/catch, matching the existing resilience applied to `onPlay` and `onEnd`. A throwing `onCycleReset` can no longer stall the engine loop.
|
|
71
|
-
- **Comprehensive documentation** — Added HTML5 AUDIO vs. WEB AUDIO API comparison, browser autoplay policy deep-dive, background tab throttling mitigation, and callback resilience guarantees.
|
|
122
|
+
### [2.6.0] — 2026-03-16
|
|
72
123
|
|
|
73
|
-
|
|
74
|
-
-
|
|
75
|
-
- Potential memory leak: `visibilitychange` listener now properly removed on `destroy()`.
|
|
76
|
-
|
|
77
|
-
### Changed
|
|
78
|
-
- Better error messages in console (include clip label and context).
|
|
124
|
+
#### Fixed
|
|
125
|
+
- **`TypeError` for invalid arguments** — All constructor and `addClip()` validation throws changed from `Error` to `TypeError`. This matches the ECMAScript convention that type-mismatch errors should be `TypeError`, and aligns with `OpenAudio.js` and `OpenAudio_s.js`. Callers catching errors via `instanceof TypeError` now correctly catch `AudioEngine` validation failures.
|
|
79
126
|
|
|
80
|
-
|
|
81
|
-
-
|
|
82
|
-
-
|
|
83
|
-
- Created CONTRIBUTING.md with PR guidelines and testing checklist
|
|
127
|
+
#### Changed
|
|
128
|
+
- **`#isStarted` timing** — `#isStarted` is now set `true` inside the unlock `.then()` (after the Audio element is confirmed usable), not at the top of `start()` before the unlock. The duplicate-start guard during the unlock window continues to be handled by `#isUnlocking`. This aligns the state machine semantics with `OpenAudio_s.js`.
|
|
129
|
+
- **`try/catch` formatting** — Three compact single-line `try/catch` blocks (in the `onended` handler, `#resetCycle()`, and `#playNext()` `.then()`) expanded to multi-line style, matching the formatting convention used throughout the suite.
|
|
84
130
|
|
|
85
131
|
---
|
|
86
132
|
|
|
87
|
-
|
|
133
|
+
### [2.5.0] — 2026-03-16
|
|
88
134
|
|
|
89
|
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
135
|
+
#### Fixed
|
|
136
|
+
- **`isStarted` + `isPlaying` encapsulation** — Both are now private fields (`#isStarted`, `#isPlaying`) exposed via read-only getters. Previously plain public properties, they could be written by callers to silently corrupt the scheduling state machine and all internal guards.
|
|
137
|
+
- **`#isDestroyed` flag** — All public methods (`start`, `stop`, `reset`, `setVolume`, `addClip`, `destroy`) now return immediately after `destroy()` has been called, making post-destroy calls safe no-ops. Previously `destroy()` documented post-destroy behaviour as "undefined"; in practice, calling `start()` after `destroy()` would silently re-run the engine without a `visibilitychange` listener, leaving it in a partially torn-down state.
|
|
138
|
+
- **`destroy()` resource release** — Now releases the Audio element via `removeAttribute('src')` + `load()` per the WHATWG HTMLMediaElement resource-release spec, then nulls `#audio`. Previously the element was kept live with its src intact after `destroy()`.
|
|
139
|
+
- **`#isPlaying` race in `#playNext()`** — `#isPlaying` is now set `true` synchronously before `#audio.play()`, closing the narrow race window where `isPlaying` reported `false` while audio was starting. Reverted in `.catch()` on failure.
|
|
140
|
+
|
|
141
|
+
---
|
|
93
142
|
|
|
94
|
-
###
|
|
95
|
-
- Network latency no longer visible as gaps between clips (prefetch eliminates fetch delay).
|
|
96
|
-
- Background tab throttling no longer causes clips to bunch together on tab return (recalculation uses wall-clock time, not timer promises).
|
|
143
|
+
### [2.4.1] — 2026-03-16
|
|
97
144
|
|
|
98
|
-
|
|
99
|
-
-
|
|
145
|
+
#### Changed
|
|
146
|
+
- Licence changed from GPL-3.0-or-later to Apache-2.0 across the entire suite.
|
|
100
147
|
|
|
101
148
|
---
|
|
102
149
|
|
|
103
|
-
|
|
150
|
+
### [2.4.0] — 2026-03-16
|
|
104
151
|
|
|
105
|
-
|
|
106
|
-
-
|
|
107
|
-
-
|
|
152
|
+
#### Added
|
|
153
|
+
- `#isUnlocking` flag — prevents duplicate unlock attempts on rapid `start()` calls.
|
|
154
|
+
- `destroy()` method — removes the `visibilitychange` listener.
|
|
155
|
+
- `AudioEngine.canPlay(type)` static method.
|
|
156
|
+
- `onCycleReset` callback wrapped in try/catch.
|
|
108
157
|
|
|
109
|
-
|
|
110
|
-
-
|
|
158
|
+
#### Fixed
|
|
159
|
+
- Race condition on `start()` spam.
|
|
160
|
+
- Potential memory leak: `visibilitychange` listener now properly removed.
|
|
111
161
|
|
|
112
162
|
---
|
|
113
163
|
|
|
114
|
-
|
|
164
|
+
### [2.3.0] — 2026-03-16
|
|
115
165
|
|
|
116
|
-
|
|
117
|
-
-
|
|
118
|
-
-
|
|
166
|
+
#### Added
|
|
167
|
+
- Clip `src` validation in constructor and `addClip()`.
|
|
168
|
+
- Next-clip prefetch to eliminate network fetch delay.
|
|
169
|
+
- Background tab throttling mitigation via wall-clock correction.
|
|
119
170
|
|
|
120
|
-
|
|
121
|
-
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
### [2.2.0] — 2026-03-16
|
|
174
|
+
|
|
175
|
+
#### Added
|
|
176
|
+
- `reset()` method.
|
|
177
|
+
- `setVolume()` method with clamping.
|
|
178
|
+
- True private fields (`#`).
|
|
179
|
+
- NotAllowedError handling — engine halts rather than silent-loops.
|
|
180
|
+
- Silent base64 unlock in `start()`.
|
|
122
181
|
|
|
123
182
|
---
|
|
124
183
|
|
|
125
|
-
|
|
184
|
+
### [2.1.1] — 2026-03-16
|
|
126
185
|
|
|
127
|
-
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
-
|
|
132
|
-
|
|
133
|
-
|
|
186
|
+
#### Fixed
|
|
187
|
+
- Constructor validates `lowTime` and `maxTime`.
|
|
188
|
+
- `#scheduleNext()` clears existing timer before setting a new one.
|
|
189
|
+
- Cycle-boundary repeat fix.
|
|
190
|
+
- `stop()` no longer resets `currentTime`.
|
|
191
|
+
|
|
192
|
+
---
|
|
134
193
|
|
|
135
|
-
###
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
-
|
|
139
|
-
-
|
|
194
|
+
### [2.1.0] — 2026-03-16
|
|
195
|
+
|
|
196
|
+
#### Added
|
|
197
|
+
- `addClip()` method for runtime updates.
|
|
198
|
+
- Cycle-boundary repeat prevention.
|
|
199
|
+
- Single reusable Audio element created once in constructor.
|
|
140
200
|
|
|
141
201
|
---
|
|
142
202
|
|
|
143
|
-
|
|
203
|
+
### [2.0.0] — 2026-03-16
|
|
144
204
|
|
|
145
|
-
|
|
146
|
-
-
|
|
147
|
-
- No dependencies, no external libraries
|
|
148
|
-
- HTML5 `<audio>` element-based scheduling
|
|
149
|
-
- Basic shuffle bag implementation
|
|
150
|
-
- Mobile-compatible (respects autoplay policies)
|
|
205
|
+
#### Added
|
|
206
|
+
- Initial public release. Shuffle bag algorithm. `onPlay` / `onEnd` / `onCycleReset` callbacks. `addClip()` runtime insertion.
|
|
151
207
|
|
|
152
208
|
---
|
|
153
209
|
|
|
154
210
|
## Upgrade Guide
|
|
155
211
|
|
|
156
|
-
###
|
|
157
|
-
|
|
158
|
-
- **Recommended:** Call `engine.destroy()` when tearing down in SPAs to prevent listener accumulation.
|
|
159
|
-
- **New:** Use `AudioEngine.canPlay('audio/ogg')` to check format support before constructing.
|
|
212
|
+
### OpenAudio_s.js 1.2 → 1.3
|
|
213
|
+
No breaking changes. `stop()` and `reset()` now cancel in-flight unlocks via `#playCancelled`; callers that relied on the previous (unintended) behaviour of the clip starting despite a `stop()` during the unlock window should review their flow, but this was never a documented or safe pattern.
|
|
160
214
|
|
|
161
|
-
### 2.
|
|
162
|
-
|
|
163
|
-
- **Benefit:** Faster clip transitions due to network prefetch during inter-clip gap.
|
|
215
|
+
### OpenAudio_r.js 2.5 → 2.6
|
|
216
|
+
No breaking changes. `TypeError` is now thrown instead of `Error` for constructor and `addClip()` validation failures. If you catch these via `catch (e)` and check `e.message`, nothing changes. If you check `e instanceof Error` it still holds. Only `instanceof TypeError` (which previously returned `false`) now returns `true`.
|
|
164
217
|
|
|
165
|
-
###
|
|
166
|
-
|
|
167
|
-
- **New:** Cycle-boundary repeat prevention automatically enabled.
|
|
218
|
+
### OpenAudio.js 1.2 → 1.3
|
|
219
|
+
No breaking changes. `player.isPlaying` is now read-only (getter); existing read access is unaffected. Write access (`player.isPlaying = ...`) was never a supported pattern.
|
|
168
220
|
|
|
169
|
-
### 1.
|
|
170
|
-
|
|
171
|
-
- **Migration:** Wrap old code in a new `AudioEngine` constructor.
|
|
221
|
+
### OpenAudio_s.js 1.1 → 1.2
|
|
222
|
+
No breaking changes. `player.isPlaying` and `player.isStarted` are now read-only getters; existing read access is unaffected.
|
|
172
223
|
|
|
173
|
-
|
|
224
|
+
### OpenAudio_r.js 2.4 → 2.5
|
|
225
|
+
No breaking changes. `engine.isStarted` and `engine.isPlaying` are now read-only getters; existing read access is unaffected.
|
|
226
|
+
|
|
227
|
+
### OpenAudio.js 1.1 → 1.2
|
|
228
|
+
No breaking changes. Fixes are internal.
|
|
174
229
|
|
|
175
|
-
|
|
230
|
+
### OpenAudio_s.js 1.0 → 1.1
|
|
231
|
+
No breaking changes. `advanceDelay` is now configurable.
|
|
176
232
|
|
|
177
|
-
|
|
233
|
+
### OpenAudio_r.js 2.4 → 2.4.1
|
|
234
|
+
Licence header update only.
|
|
178
235
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
- Real-time DSP effects (reverb, EQ, compression)
|
|
182
|
-
- Frequency analysis or visualization
|
|
236
|
+
### OpenAudio_r.js 2.3 → 2.4
|
|
237
|
+
Additive features only.
|
|
183
238
|
|
|
184
|
-
|
|
239
|
+
### OpenAudio_r.js 1.0 → 2.0
|
|
240
|
+
**Breaking:** Complete rewrite with methods and callbacks.
|
|
185
241
|
|
|
186
242
|
---
|
|
187
243
|
|
|
188
244
|
## Contributing
|
|
189
245
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
All contributions will be credited in the changelog.
|
|
246
|
+
See [CONTRIBUTING.md](https://github.com/Rexore/OpenAudio/blob/main/CONTRIBUTING.md).
|
|
193
247
|
|
|
194
248
|
---
|
|
195
249
|
|
|
196
250
|
## License
|
|
197
251
|
|
|
198
|
-
|
|
252
|
+
Licensed under the **Apache License 2.0**.
|
|
199
253
|
|
|
200
254
|
---
|
|
201
255
|
|
|
202
|
-
*Last updated: March
|
|
256
|
+
*Last updated: March 16, 2026*
|