expo-juce 0.5.2 → 0.7.0-rc.0

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.
Files changed (51) hide show
  1. package/README.md +207 -9
  2. package/build/ExpoJuce.types.d.ts +175 -1
  3. package/build/ExpoJuce.types.d.ts.map +1 -1
  4. package/build/ExpoJuce.types.js.map +1 -1
  5. package/build/ExpoJuceModule.d.ts +46 -1
  6. package/build/ExpoJuceModule.d.ts.map +1 -1
  7. package/build/ExpoJuceModule.js.map +1 -1
  8. package/build/ExpoJuceModule.web.d.ts +11 -0
  9. package/build/ExpoJuceModule.web.d.ts.map +1 -1
  10. package/build/ExpoJuceModule.web.js +44 -0
  11. package/build/ExpoJuceModule.web.js.map +1 -1
  12. package/build/JucePadView.js.map +1 -1
  13. package/build/JucePadView.web.js.map +1 -1
  14. package/build/arpDefaults.d.ts +9 -0
  15. package/build/arpDefaults.d.ts.map +1 -0
  16. package/build/arpDefaults.js +23 -0
  17. package/build/arpDefaults.js.map +1 -0
  18. package/build/effectDefaults.d.ts +4 -0
  19. package/build/effectDefaults.d.ts.map +1 -0
  20. package/build/effectDefaults.js +22 -0
  21. package/build/effectDefaults.js.map +1 -0
  22. package/build/index.d.ts +15 -3
  23. package/build/index.d.ts.map +1 -1
  24. package/build/index.js +117 -0
  25. package/build/index.js.map +1 -1
  26. package/ios/AnalysisRing.h +133 -0
  27. package/ios/Arpeggiator.h +567 -0
  28. package/ios/AuxProcessor.h +183 -0
  29. package/ios/AuxProcessor.mm +431 -0
  30. package/ios/BackingTrack.h +192 -0
  31. package/ios/BackingTrack.mm +307 -0
  32. package/ios/ExpoJuce.podspec +7 -0
  33. package/ios/ExpoJuceAudioEngine.h +39 -0
  34. package/ios/ExpoJuceAudioEngine.mm +408 -7
  35. package/ios/ExpoJuceBridge.h +26 -0
  36. package/ios/ExpoJuceBridge.m +28 -0
  37. package/ios/ExpoJuceModule.swift +65 -0
  38. package/ios/FxChainProcessor.h +285 -0
  39. package/ios/FxChainProcessor.mm +644 -0
  40. package/ios/GraphPlayer.h +8 -0
  41. package/ios/JuceConfig.h +12 -2
  42. package/ios/JucePadView.swift +182 -123
  43. package/ios/NoteEventQueue.h +150 -143
  44. package/ios/PolySynthProcessor.h +25 -3
  45. package/ios/PolySynthProcessor.mm +103 -28
  46. package/ios/SpectrumAnalyser.h +84 -0
  47. package/ios/TransportEngine.h +18 -0
  48. package/ios/TransportEngine.mm +10 -3
  49. package/ios/Voice.h +66 -65
  50. package/ios/VoiceAllocator.h +2 -1
  51. package/package.json +1 -1
package/README.md CHANGED
@@ -23,10 +23,16 @@ iOS only for now; the Android and web modules are scaffolds.
23
23
  │
24
24
  juce::AudioDeviceManager ─► GraphPlayer (AudioIODeviceCallback: graph, peaks, NaNs, test tone)
25
25
  │
26
- juce::AudioProcessorGraph PolySynthProcessor ─► audio output node
26
+ juce::AudioProcessorGraph PolySynthProcessor ─► FxChainProcessor ─► audio output node
27
+ AuxProcessor (metronome + backing track) ───► audio output node (summed)
27
28
  ├─ NoteEventQueue: note-on ring (256) drained at block start; releases as marks
28
29
  ├─ 8 × Voice: dsp::Oscillator → StateVariableTPTFilter (LP) → ADSR
29
30
  └─ TransportEngine (tempo / position / beat crossings)
31
+ FxChainProcessor: drive → crush → eq → chorus → phaser → echo → reverb →
32
+ pump → compressor → safety limiter; settings in a process-lifetime store
33
+ AuxProcessor: clicks rendered at prepare; track buffer swapped in from
34
+ a process-lifetime BackingTrack holder (decoded on a background queue)
35
+ GraphPlayer also writes the output into an AnalysisRing (getAnalysis: peak / rms / scope)
30
36
  ```
31
37
 
32
38
  ## Roadmap: Full Audio Workstation
@@ -44,9 +50,9 @@ Replaced the mono SynthEngine + AVAudioEngine with a JUCE-owned audio stack buil
44
50
  `setup()` rejects with the failing step, `getDiagnostics()` exposes every layer from device callbacks to synth peak, `playTestTone()` bypasses the graph, an offline harness reproduces the device callback on Linux. Root causes of the first on-device silence (fixed 2026-10-22): a stale Metro bundle running the app on stubs, infinite recursion in `-setBeatCallback:`, `setup()` always resolving, and expo-av switching the shared session to Ambient. 0.5.2 adds: 128-sample buffer, no allocation on the audio thread, native `JucePadView` touch path, 2 ms default attack, session re-assert and interruption / media-reset recovery (keeping synth settings and transport state across rebuilds).
45
51
 
46
52
  ### Sub-project 2: Effects Chain
47
- **Status:** Planned
53
+ **Status:** In progress (0.6.0) — engine + JS API done and verified in the offline harness; not yet run on a device
48
54
 
49
- Add `AudioProcessor` nodes for standard effects: reverb, delay, distortion, EQ, compressor. Inserted into the graph after the synth node on a master effects bus. Each effect is an independent processor — can be bypassed, reordered, or removed.
55
+ One `FxChainProcessor` node between the synth and the output with nine effects in a fixed order: drive → crush → eq → chorus → phaser → echo → reverb → pump → compressor, then an always-on safety limiter (-0.3 dBFS). Each effect is enabled / bypassed independently (`setEffectEnabled`, `setEffectParam`, `getEffectsState`, see [Effects](#effects-060)); echo sync and pump follow the transport tempo and beat. Also in 0.6.0: an `AuxProcessor` node summed into the output after the effects, with a sample-accurate metronome (5 click styles, bar accent) and a backing track (off-thread decode + resample, gapless MP3 loop regions, lock-free swap, follows transport play / stop / seek; see [Metronome and backing track](#metronome-and-backing-track-060)); `getAnalysis()` (output peak / rms, 128-point scope, last note, transport) for HUDs; a `hitPolygon` touch shape on `JucePadView`. Later: reorderable / per-node effects via modular routing (sub-project 5).
50
56
 
51
57
  ### Sub-project 3: Sample Playback / Drum Machine
52
58
  **Status:** Planned — an earlier JUCE sampler prototype is the starting point
@@ -126,6 +132,185 @@ ExpoJuce.playTestTone(500); // 440 Hz into the device output, bypasses
126
132
  await ExpoJuce.shutdown();
127
133
  ```
128
134
 
135
+ ### Effects (0.6.0+)
136
+
137
+ A master effects chain sits between the synth and the output. All effects are off by
138
+ default; order is fixed (drive → crush → eq → chorus → phaser → echo → reverb → pump →
139
+ compressor), followed by a safety limiter (-0.3 dBFS ceiling, always on, not user-facing).
140
+
141
+ ```ts
142
+ ExpoJuce.setEffectEnabled('reverb', true); // boolean: false = unknown effect
143
+ ExpoJuce.setEffectParam('reverb', 'mix', 0.4); // boolean: false = unknown effect/param or NaN
144
+ ExpoJuce.setEffectParam('echo', 'division', 1); // synced echo, 1/8 at the transport tempo
145
+ const fx = ExpoJuce.getEffectsState(); // { reverb: { enabled, params: { size, … } }, … }
146
+ ```
147
+
148
+ - Synchronous and lock-free, any thread. Values are clamped to the ranges below (switches and
149
+ division indices rounded); `getEffectsState()` reports the stored values.
150
+ - Settings are remembered natively whether or not the engine is running: calls made before
151
+ `setup()`, or during an automatic engine rebuild, take effect when the chain runs.
152
+ - Turning an effect on or off crossfades it (10 ms); a disabled effect is skipped entirely and
153
+ an effect turned on starts clean (no stale echo or reverb tail). Echo and reverb spill over:
154
+ turning them off stops feeding them, and their tail keeps ringing out until it has faded
155
+ below -90 dBFS (at most 5 s, faded out over the last 50 ms); only then are they bypassed.
156
+ With every effect off (and tails ended) the output is the synth output, bit for bit (while
157
+ it stays under the limiter ceiling).
158
+ - Parameter changes are smoothed where they would click (mix, gains, delay time, EQ bands).
159
+ - Echo `sync` and `pump` follow the transport tempo; pump is locked to the transport beat
160
+ while it plays and free-runs at the tempo while it is stopped.
161
+ - Binaries older than 0.6.0: the setters return `false` and `getEffectsState()` returns the
162
+ defaults. Web: no-ops.
163
+
164
+ | effect | params (range, default) |
165
+ |---|---|
166
+ | `drive` | `amount` 0–1 (0.4), `tone` 0–1 (0.6), `mix` 0–1 (1) — pre-gain 1–40×, tanh, tone LP 800 Hz–20 kHz |
167
+ | `crush` | `bits` 2–16 (8), `downsample` 1–32 (4), `noise` 0–1 (0.1), `mix` 0–1 (1) |
168
+ | `eq` | `low` −12–12 dB (0, shelf 120 Hz), `mid` −12–12 dB (0, peak Q 0.9), `midFreq` 200–5000 Hz (1000), `high` −12–12 dB (0, shelf 6 kHz) |
169
+ | `chorus` | `rate` 0.05–5 Hz (0.8), `depth` 0–1 (0.4), `mix` 0–1 (0.5) |
170
+ | `phaser` | `rate` 0.05–5 Hz (0.4), `depth` 0–1 (0.6), `feedback` 0–0.9 (0.5), `mix` 0–1 (0.5) |
171
+ | `echo` | `time` 0.02–2 s (0.375), `sync` 0/1 (1), `division` 0–5 (2; 1/4, 1/8, 1/8 dotted, 1/16, 1/4 dotted, 1/2), `feedback` 0–0.95 (0.4), `tone` 0–1 (0.5), `pingPong` 0/1 (0), `mix` 0–1 (0.35) |
172
+ | `reverb` | `size` 0–1 (0.6), `damping` 0–1 (0.5), `width` 0–1 (1), `mix` 0–1 (0.3) |
173
+ | `pump` | `depth` 0–1 (0.6), `shape` 0–1 (0.5; soft → snappy), `division` 0–3 (0; 1/4, 1/8, 1/2, 1/1) |
174
+ | `compressor` | `threshold` −60–0 dB (−18), `ratio` 1–20 (4), `attack` 0.1–100 ms (10), `release` 10–1000 ms (120), `makeup` 0–24 dB (4) |
175
+
176
+ `mix` is a dry/wet crossfade (0 = dry). Synced echo time is capped at 2 s.
177
+
178
+ ### Metronome and backing track (0.6.0+)
179
+
180
+ An aux source next to the synth: its output is summed into the device output **after** the
181
+ effects chain (clicks and the track are not effected). Both follow the transport.
182
+
183
+ ```ts
184
+ ExpoJuce.setMetronome({ enabled: true, style: 'wood', level: 0.7, accent: true, beatsPerBar: 4 }); // false: unknown style
185
+
186
+ const info = await ExpoJuce.loadBackingTrack(fileUri, { // gapless info: optional, MP3 only
187
+ loopSamples: 682839, leadingSamples: 4561, trailingSamples: 1496,
188
+ });
189
+ // { durationSec, sampleRate, channels, loopStartSample, loopSamples, decodedSamples, regionRule, decoder }
190
+ ExpoJuce.setBackingTrackOptions({ level: 0.8, loop: true, enabled: true });
191
+ ExpoJuce.startTransport(); // the track and the clicks start
192
+ ExpoJuce.unloadBackingTrack();
193
+ ```
194
+
195
+ - **Metronome**: a click on every beat while the transport plays, starting on the exact
196
+ sample where the beat falls (any block size), accented on beat 1 of the bar. Styles
197
+ `classic` (sine blip 1.5 kHz / accent 2.5 kHz), `wood` (resonant noise burst), `beep`
198
+ (square 1 / 2 kHz), `cowbell` (two squares 540 / 800 Hz through a band-pass), `hat`
199
+ (high-passed noise). Clicks are rendered once when the engine prepares (no allocation while
200
+ playing). Options: `enabled` (false), `style` ('classic'), `level` 0–1 (0.8), `accent`
201
+ (true), `beatsPerBar` 1–16 (4). `setMetronome` returns `false` and applies nothing if
202
+ `style` is not one of the five.
203
+ - **Backing track**: `loadBackingTrack(uri, gapless?)` takes a local file (`file://` URI or
204
+ absolute path; download remote files first). It decodes WAV, AIFF, FLAC and MP3 (plus
205
+ CoreAudio formats such as M4A / AAC on iOS) on a background queue straight to the device
206
+ rate and swaps the buffer in without blocking or allocating on the audio thread; the old
207
+ buffer is freed on the main thread after the audio thread has let go of it. Only the
208
+ playable buffer stays in memory (after a device-rate change the file is decoded again).
209
+ Limit: 6 minutes (17 280 000 frames per channel at 48 kHz, about 138 MB; the same duration
210
+ at other rates), and the load is refused if the process has less memory available than the
211
+ decode needs (about twice the final buffer). A newer load or `unloadBackingTrack()`
212
+ supersedes a load in flight (it rejects with `ERR_BACKING_TRACK_SUPERSEDED`); other errors:
213
+ `ERR_BACKING_TRACK_URI`, `_FILE`, `_FORMAT`, `_TOO_LONG`, `_MEMORY`.
214
+ - **Gapless MP3 loops**: MP3 encoders pad the start and end. With `gapless` (the LAME / Xing
215
+ tag values, frames at the file's rate) the loop region is, by decoded length:
216
+ - `loopSamples` (the decoder honoured the tag) → `[0, loopSamples)`, `regionRule: 'tag'`;
217
+ - 1152 + leading + loopSamples + trailing (the decoder ignored the tag and decoded the tag
218
+ frame) → `[1152 + leading, +loopSamples)`, `'padded'`;
219
+ - leading + loopSamples + trailing (the decoder skipped the tag frame but kept the encoder
220
+ padding, as JUCE's own MP3 decoder does) → `[leading, +loopSamples)`, `'paddedNoTagFrame'`;
221
+ - anything else, or no `gapless` → the whole file, `'whole'` (with `gapless` given, this
222
+ means the fallback fired and the loop will include the padding).
223
+
224
+ The region is cut before resampling; `decodedSamples` / `decoder` tell what the decoder did.
225
+ - **Playback** follows the transport. Track sample 0 is beat 0: when the transport starts or
226
+ is moved (`setTransportPosition`), the track goes to beat × 60 / bpm seconds (modulo its
227
+ length when looping), so pause + resume continues where it stopped and seeks land on the
228
+ beat grid. While playing, the track simply advances with the audio: a tempo change does not
229
+ move or stretch it (it keeps its own speed). Starts away from the top, seeks, stops, and
230
+ replacing or unloading the track while it plays are crossfaded over 5 ms. With `loop` off it
231
+ plays once and then stays silent until the transport is moved back.
232
+ - Options: `level` 0–1 (0.8), `loop` (true), `enabled` (true).
233
+ - Metronome and backing-track options and the loaded track are kept natively for the life of
234
+ the app: they work before `setup()` and survive engine rebuilds.
235
+ - Binaries older than 0.6.0: the setters are no-ops (`setMetronome` returns `false`) and
236
+ `loadBackingTrack` rejects with `ERR_UNSUPPORTED`. Web: the same.
237
+
238
+ ### Arpeggiator (0.7.0+)
239
+
240
+ A sample-accurate arpeggiator between the note queue and the voices. While it is enabled, every
241
+ note the synth receives (pads, keys, `noteOn`) joins its held list instead of sounding, and the
242
+ arp plays the held notes step by step on the transport's grid; disabled, notes play directly as
243
+ before (voices that were sounding when the arp was enabled keep sounding until released).
244
+
245
+ ```ts
246
+ ExpoJuce.setArpeggiator({ enabled: true, mode: 'upDown', rate: '1/16', octaves: 2, gate: 0.6 });
247
+ ExpoJuce.setArpeggiator({ pattern: [true, false, true, true], swing: 0.2, ratchet: 2 }); // partial
248
+ const state = ExpoJuce.getArpeggiatorState(); // every option as stored (ArpOptions)
249
+ ExpoJuce.ARP_DEFAULTS; // the defaults below
250
+ ```
251
+
252
+ - Options (`ArpOptions`, omitted keys keep their value, numbers are clamped; `setArpeggiator`
253
+ returns `false` and applies nothing for an unknown `mode` / `rate` or a `pattern` that is not
254
+ 1–16 booleans):
255
+ - `enabled` (false)
256
+ - `mode` ('up'): `'up'` held notes ascending, `'down'`, `'upDown'` (up, then the interior
257
+ reversed: `[60, 64, 72, 76]` → `60 64 72 76 72 64`), `'played'` (press order), `'random'`
258
+ (a uniform pick per step), `'chord'` (every held note on every step)
259
+ - `rate` ('1/16'): `'1/4'`, `'1/8'`, `'1/8T'`, `'1/8D'`, `'1/16'`, `'1/16T'`, `'1/32'`
260
+ (1, 2, 3, 4/3, 4, 6, 8 steps per beat)
261
+ - `octaves` 1–4 (1): the cycle repeats +12 per extra octave
262
+ - `gate` 0.05–1.5 of a step (0.6): note length; above 1 the note overlaps the next step
263
+ (legato; a one-note cycle retriggers the same voice)
264
+ - `swing` 0–0.5 of a step (0): odd steps start that much later
265
+ - `latch` (false): the list is kept when every note is released; the next press replaces it
266
+ - `pattern` 1–16 booleans (16 × true), all or nothing: a false step is a rest and does not
267
+ advance the cycle
268
+ - `accent` 0–1 (0): steps other than the first of the pattern play at
269
+ velocity × (1 − 0.6 × accent)
270
+ - `ratchet` 1–4 (1): hits per step, each with `gate` of its share
271
+ - `transposeSteps` 0–8 (0) and `transposeDistance` −12..12 (0): each pass through the pattern
272
+ rises by `transposeDistance` semitones, wrapping after `transposeSteps` passes
273
+ - Timing: step `k` starts `k × L` after the arp starts (`L = 60 / bpm / stepsPerBeat`), on the
274
+ exact sample whatever the block size. While the transport plays the steps sit on the beat grid
275
+ (`beat × stepsPerBeat` integer; a chord landing up to ¼ step after a grid line plays at once,
276
+ later ones wait for the next line) and a tempo change re-derives the grid from the new bpm.
277
+ Stopped, the grid starts at the first note-on and runs free at the current tempo.
278
+ - The cycle restarts when the held list goes from empty to non-empty; adding or removing notes
279
+ while it plays takes effect at the next step. `allNotesOff()` stops the arp and clears its
280
+ list (a latched list included). Disabling stops the sounding arp notes at once.
281
+ - 16 voices (`MAX_VOICES`), so a 16-note `chord` or ratchets with long releases do not steal.
282
+ - Options are kept natively for the life of the app in one store the synth reads once per block:
283
+ they work before `setup()`, survive engine rebuilds and apply within a block even while a
284
+ rebuild is in progress. Binaries older than 0.7.0: `setArpeggiator` returns `false`,
285
+ `getArpeggiatorState` returns `ARP_DEFAULTS`. Web: the same.
286
+
287
+ ### Analysis (0.6.0+)
288
+
289
+ ```ts
290
+ const a = ExpoJuce.getAnalysis();
291
+ // { peak, rms, scope: number[128], bands: number[24], sampleRate, lastNote, bpm, beat, playing,
292
+ // arp: { active, step, steps, note } }
293
+ ```
294
+
295
+ Synchronous and lock-free, cheap enough to call every animation frame (from one place: each
296
+ call consumes the levels). `peak` (peak hold) and `rms` (both channels) cover the output
297
+ since the previous call (synth, effects, metronome and backing track as sent to the device);
298
+ `scope` is 128 points, oldest first, taken every 16th sample from the last 2048 output samples
299
+ (mono, may tear across a block; it is for display). `bpm` / `beat` / `playing` are the
300
+ transport as of the last rendered block. `lastNote` is the last note the synth started (-1 if
301
+ none). Zeros when the engine is not running.
302
+
303
+ 0.7.0+ adds `bands`: 24 log-spaced spectrum bands between 40 Hz and 16 kHz, each 0–1 for
304
+ −60..0 dBFS of the loudest FFT bin in the band, from a Hann-windowed 2048-point FFT of the same
305
+ output samples, computed on the calling thread (about 50 µs per call). Band edges are
306
+ `edge_0 = 40`, `edge_i = max(40 × 400^(i/24), edge_(i−1) + sampleRate / 2048)`,
307
+ `edge_24 = 16000`: the lowest bands are one FFT bin wide (23.4 Hz at 48 kHz) until the
308
+ geometric spacing takes over around 140 Hz, so no two bands read the same bin. `sampleRate` is
309
+ the device rate the bands were computed at. `arp` is the arpeggiator as of the last block:
310
+ `active` (enabled and holding notes), `step` (pattern slot of the last step started), `steps`
311
+ (pattern length), `note` (last note the arp started, -1 if none). Older binaries: 24 zeros,
312
+ `sampleRate` 0, `arp` inactive.
313
+
129
314
  ### `JucePadView` (0.5.2+)
130
315
 
131
316
  A native view that plays a note on touch from the iOS main thread, without the JS thread,
@@ -147,6 +332,15 @@ import { JucePadView } from 'expo-juce';
147
332
 
148
333
  Check the shipped type definitions for the exact event payloads once 0.5.2 is published.
149
334
 
335
+ `hitPolygon` (0.6.0+) makes the touch area non-rectangular: a flat `[x0, y0, x1, y1, …]`
336
+ polygon normalised to the view (0–1). Touches outside it (including on its children) pass
337
+ through to the views underneath, so pads with overlapping bounding boxes do not steal each
338
+ other's touches. Empty, odd-length or fewer than 3 points: the whole view.
339
+
340
+ ```tsx
341
+ <JucePadView note={62} hitPolygon={[0.5, 0, 1, 0.5, 0.5, 1, 0, 0.5]} style={{ width: 96, height: 96 }} />
342
+ ```
343
+
150
344
  ### Diagnostics
151
345
 
152
346
  `getDiagnostics()` returns setup state (`isReady`, `setupStep`, `lastError`), device
@@ -159,8 +353,11 @@ Check the shipped type definitions for the exact event payloads once 0.5.2 is pu
159
353
  `mediaResetCount`, `lastRecoveryError`, `engineState`. A debug screen in the app can show them
160
354
  as a pass/fail ladder — see [docs/latency.md](docs/latency.md#5-a-diagnostics-panel).
161
355
 
162
- Synth settings and transport state (tempo, position, playing) survive the engine's automatic
163
- rebuilds after a media-services reset or a stalled device; held notes do not.
356
+ 0.6.0+ adds `fxProcessCount`, `fxLimiterGain` (1 = the safety limiter is idle),
357
+ `fxNonFiniteResets`, `metronomeClicks`, `backingTrackLoaded` and `backingTrackPositionSec`.
358
+
359
+ Synth settings, effect, metronome and backing-track settings (and the loaded track) and transport state (tempo, position, playing) survive the
360
+ engine's automatic rebuilds after a media-services reset or a stalled device; held notes do not.
164
361
 
165
362
  ## Latency
166
363
 
@@ -195,11 +392,11 @@ bash and fail under npm on Windows, so build and publish by hand from Git Bash:
195
392
  npm install
196
393
  # 1. bump "version" in package.json, e.g. 0.5.2-diag.2
197
394
  CI=1 bash node_modules/expo-module-scripts/bin/expo-module-build # tsc → build/
198
- npm publish --tag diag --ignore-scripts # npm 2FA code required
395
+ npm publish --tag next --ignore-scripts --otp=<code> # npm 2FA code required
199
396
  ```
200
397
 
201
398
  `--ignore-scripts` skips `prepublishOnly`, so step 1's build is mandatory. Prereleases go
202
- under a dist-tag (`diag`) so `latest` is not moved.
399
+ under a dist-tag (`next`, earlier `diag`) so `latest` is not moved.
203
400
 
204
401
  Then in the consuming app: `npm install expo-juce@<version> --save-exact`, check that
205
402
  `package-lock.json` still has its macOS-only optional entries (a lockfile regenerated on
@@ -210,8 +407,9 @@ cannot resolve `file:` dependencies, which is why every native change goes throu
210
407
 
211
408
  1. **TypeScript:** `npx tsc --noEmit` here and in the consuming app.
212
409
  2. **Engine C++:** the offline harness compiles the vendored JUCE headless with `Voice`,
213
- `VoiceAllocator`, `TransportEngine`, `PolySynthProcessor` and a copy of `GraphPlayer`, replays the
214
- `doSetup` order and drives the callback with iOS-like block sizes. From `test/harness` in
410
+ `VoiceAllocator`, `TransportEngine`, `PolySynthProcessor`, `FxChainProcessor` and a copy of
411
+ `GraphPlayer`, replays the `doSetup` order (synth → fx → output) and drives the callback with
412
+ iOS-like block sizes; separate cases check every effect, bypass and echo sync. From `test/harness` in
215
413
  WSL (or any Linux shell):
216
414
  ```sh
217
415
  cmake -S . -B build && cmake --build build -j16 && ./build/harness
@@ -29,6 +29,12 @@ export type JucePadViewProps = {
29
29
  velocity?: number;
30
30
  /** When false, touches pass through and no note is played (default true). */
31
31
  enabled?: boolean;
32
+ /**
33
+ * Touch area as a polygon: flat `[x0, y0, x1, y1, …]` normalised to the view (0–1). Touches
34
+ * outside it pass through to the views underneath. Empty, odd-length or fewer than 3 points:
35
+ * the whole view (default). iOS, 0.6.0+.
36
+ */
37
+ hitPolygon?: number[];
32
38
  /** Fired on the first touch, after the note has been sent to the engine (visuals/haptics only). */
33
39
  onNoteOn?: (event: PadNoteOnEvent) => void;
34
40
  /** Fired when the last touch lifts or is cancelled, after the note-off was sent. */
@@ -36,6 +42,160 @@ export type JucePadViewProps = {
36
42
  style?: StyleProp<ViewStyle>;
37
43
  children?: React.ReactNode;
38
44
  };
45
+ /**
46
+ * Effects of the master chain (synth → effects → output), in processing order. A safety
47
+ * limiter (-0.3 dBFS ceiling) always runs after them and is not user-facing.
48
+ */
49
+ export type EffectId = 'drive' | 'crush' | 'eq' | 'chorus' | 'phaser' | 'echo' | 'reverb' | 'pump' | 'compressor';
50
+ /** Parameter names per effect (ranges and defaults: see README "Effects"). */
51
+ export type EffectParams = {
52
+ drive: 'amount' | 'tone' | 'mix';
53
+ crush: 'bits' | 'downsample' | 'noise' | 'mix';
54
+ eq: 'low' | 'mid' | 'midFreq' | 'high';
55
+ chorus: 'rate' | 'depth' | 'mix';
56
+ phaser: 'rate' | 'depth' | 'feedback' | 'mix';
57
+ echo: 'time' | 'sync' | 'division' | 'feedback' | 'tone' | 'pingPong' | 'mix';
58
+ reverb: 'size' | 'damping' | 'width' | 'mix';
59
+ pump: 'depth' | 'shape' | 'division';
60
+ compressor: 'threshold' | 'ratio' | 'attack' | 'release' | 'makeup';
61
+ };
62
+ /** One effect's state as stored natively (values after clamping). */
63
+ export type EffectState = {
64
+ enabled: boolean;
65
+ params: Record<string, number>;
66
+ };
67
+ /** Returned by `getEffectsState()`: every effect, enabled or not. */
68
+ export type EffectsState = Record<EffectId, EffectState>;
69
+ /** Metronome click sounds. */
70
+ export type MetronomeStyle = 'classic' | 'wood' | 'beep' | 'cowbell' | 'hat';
71
+ /** `setMetronome()` options; omitted keys keep their current value. */
72
+ export type MetronomeOptions = {
73
+ /** Clicks while the transport plays (default false). */
74
+ enabled?: boolean;
75
+ /** Click sound (default 'classic'). */
76
+ style?: MetronomeStyle;
77
+ /** 0–1 (default 0.8). */
78
+ level?: number;
79
+ /** Higher click on beat 1 of the bar (default true). */
80
+ accent?: boolean;
81
+ /** 1–16, rounded (default 4). */
82
+ beatsPerBar?: number;
83
+ };
84
+ /**
85
+ * Gapless info of an MP3 (frames at the file's own rate, from its LAME/Xing tag): lets
86
+ * `loadBackingTrack` cut the exact loop out of what the decoder returns.
87
+ */
88
+ export type BackingTrackGapless = {
89
+ loopSamples: number;
90
+ leadingSamples: number;
91
+ trailingSamples: number;
92
+ };
93
+ /** Loop region chosen by `loadBackingTrack()` (see `BackingTrackInfo.regionRule`). */
94
+ export type BackingTrackRegionRule = 'tag' | 'padded' | 'paddedNoTagFrame' | 'whole';
95
+ /** Resolved by `loadBackingTrack()`. Sample counts are frames at the file's own rate. */
96
+ export type BackingTrackInfo = {
97
+ /** Length of the loop region in seconds. */
98
+ durationSec: number;
99
+ /** The file's sample rate (playback is resampled to the device rate). */
100
+ sampleRate: number;
101
+ channels: number;
102
+ /** Where the loop region starts in the decoded audio. */
103
+ loopStartSample: number;
104
+ /** Length of the loop region. */
105
+ loopSamples: number;
106
+ /** Frames the decoder produced before the region was cut. */
107
+ decodedSamples?: number;
108
+ /**
109
+ * Which loop region was used (0.6.0-rc.1+): 'tag' (the decoder honoured the gapless tag),
110
+ * 'padded' (tag frame decoded as silence, padding cut), 'paddedNoTagFrame' (tag frame skipped,
111
+ * padding cut), 'whole' (no gapless info, or a length matching no case: the whole file).
112
+ */
113
+ regionRule?: BackingTrackRegionRule;
114
+ /** Decoder used (e.g. "CoreAudio", "MP3 file", "WAV file"). */
115
+ decoder?: string;
116
+ };
117
+ /** `setBackingTrackOptions()` options; omitted keys keep their current value. */
118
+ export type BackingTrackOptions = {
119
+ /** 0–1 (default 0.8). */
120
+ level?: number;
121
+ /** Loop the track (default true); otherwise it plays once and then is silent. */
122
+ loop?: boolean;
123
+ /** Plays with the transport (default true). */
124
+ enabled?: boolean;
125
+ };
126
+ /** Arpeggiator note order (0.7.0+). */
127
+ export type ArpMode = 'up' | 'down' | 'upDown' | 'played' | 'random' | 'chord';
128
+ /**
129
+ * Arpeggiator step rate (0.7.0+). Steps per beat: '1/4' 1, '1/8' 2, '1/8T' 3, '1/8D' 4/3,
130
+ * '1/16' 4, '1/16T' 6, '1/32' 8.
131
+ */
132
+ export type ArpRate = '1/4' | '1/8' | '1/8T' | '1/8D' | '1/16' | '1/16T' | '1/32';
133
+ /**
134
+ * `setArpeggiator()` options / `getArpeggiatorState()` result (0.7.0+). In `setArpeggiator`
135
+ * omitted keys keep their current value; numbers are clamped to their range; an unknown `mode`
136
+ * or `rate` rejects the whole call (nothing applied).
137
+ */
138
+ export type ArpOptions = {
139
+ /** Arpeggiate held notes instead of playing them directly (default false). */
140
+ enabled: boolean;
141
+ /** Note order (default 'up'). */
142
+ mode: ArpMode;
143
+ /** Step rate relative to the transport tempo (default '1/16'). */
144
+ rate: ArpRate;
145
+ /** 1–4 octaves of the held notes in the cycle (default 1). */
146
+ octaves: number;
147
+ /** Note length, 0.05–1.5 of a step (default 0.6); > 1 overlaps the next step (legato). */
148
+ gate: number;
149
+ /** 0–0.5 of a step added to odd steps (default 0). */
150
+ swing: number;
151
+ /** Keep playing after every note is released; the next press starts a new chord (default false). */
152
+ latch: boolean;
153
+ /** 1–16 steps, true = the step plays (default 16 × true). */
154
+ pattern: boolean[];
155
+ /** 0–1 (default 0): steps other than step 0 play at velocity × (1 − 0.6 × accent). */
156
+ accent: number;
157
+ /** 1–4 hits per step (default 1). */
158
+ ratchet: number;
159
+ /** 0–8 pattern passes before the transposition wraps (default 0 = off). */
160
+ transposeSteps: number;
161
+ /** −12..12 semitones added per pass (default 0). */
162
+ transposeDistance: number;
163
+ };
164
+ /** Arpeggiator state in `Analysis.arp` (0.7.0+), as of the last rendered block. */
165
+ export type ArpAnalysis = {
166
+ /** The arp is enabled and holds notes (a step is sounding or coming). */
167
+ active: boolean;
168
+ /** Pattern slot of the last step started (0-based). */
169
+ step: number;
170
+ /** Pattern length. */
171
+ steps: number;
172
+ /** Last note the arp started; -1 if none yet. */
173
+ note: number;
174
+ };
175
+ /** Returned by `getAnalysis()`: the device output (synth, effects, metronome, backing track). */
176
+ export type Analysis = {
177
+ /** Max |sample| since the previous getAnalysis() call (peak hold), 0–1+. */
178
+ peak: number;
179
+ /** RMS (both channels) since the previous getAnalysis() call. */
180
+ rms: number;
181
+ /** 128 points, oldest first, decimated (every 16th sample) from the last 2048 output samples (mono). */
182
+ scope: number[];
183
+ /**
184
+ * 24 log-spaced bands, 40 Hz – 16 kHz, each 0–1 (−60..0 dBFS of the loudest FFT bin in the
185
+ * band) from the last 2048 output samples (0.7.0+; 24 zeros on older binaries).
186
+ */
187
+ bands: number[];
188
+ /** Sample rate the bands were computed at (0 when the engine is not running / older binaries). */
189
+ sampleRate: number;
190
+ /** Last note the synth started; -1 if none yet. */
191
+ lastNote: number;
192
+ /** Transport tempo, position (beats) and play state as of the last rendered block. */
193
+ bpm: number;
194
+ beat: number;
195
+ playing: boolean;
196
+ /** Arpeggiator state (0.7.0+; inactive on older binaries). */
197
+ arp: ArpAnalysis;
198
+ };
39
199
  /** Native engine lifecycle state reported in `AudioDiagnostics.engineState`. */
40
200
  export type EngineState = 'ready' | 'recovering' | 'failed' | 'uninitialized';
41
201
  /**
@@ -69,9 +229,11 @@ export type AudioDiagnostics = {
69
229
  nanCount: number;
70
230
  /** Cumulative test-tone samples actually mixed into the device output (audio thread). */
71
231
  toneSamplesMixed: number;
232
+ /** Note-ons accepted into the note queue (cumulative since launch, across engine rebuilds). */
72
233
  notesQueued: number;
234
+ /** Note-ons the synth has started (cumulative since launch). */
73
235
  notesConsumed: number;
74
- /** noteOn events dropped because the event queue was full. */
236
+ /** Note-ons dropped because the note queue was full. Note releases are never dropped. */
75
237
  notesDropped: number;
76
238
  activeVoices: number;
77
239
  /** ms from noteOn enqueue to the synth consuming it; -1 if none yet. */
@@ -100,6 +262,18 @@ export type AudioDiagnostics = {
100
262
  lockBusyDrops?: number;
101
263
  /** Times the audio callback was found stalled and the device was reopened (0.5.2+). */
102
264
  deadDeviceRestarts?: number;
265
+ /** FxChainProcessor::processBlock calls (0.6.0+). */
266
+ fxProcessCount?: number;
267
+ /** Lowest safety-limiter gain in the last effects block, 1 = not limiting (0.6.0+). */
268
+ fxLimiterGain?: number;
269
+ /** Effect chunks whose output went non-finite and were silenced + reset (0.6.0+). */
270
+ fxNonFiniteResets?: number;
271
+ /** Metronome clicks started (cumulative in the current engine, 0.6.0+). */
272
+ metronomeClicks?: number;
273
+ /** A backing track is loaded (0.6.0+). */
274
+ backingTrackLoaded?: boolean;
275
+ /** Backing-track playback position in seconds; -1 when not playing (0.6.0+). */
276
+ backingTrackPositionSec?: number;
103
277
  sessionCategory: string;
104
278
  sessionSampleRate: number;
105
279
  sessionIOBufferMs: number;
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoJuce.types.d.ts","sourceRoot":"","sources":["../src/ExpoJuce.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ExpoJuce.types.d.ts","sourceRoot":"","sources":["../src/ExpoJuce.types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoJuce.types.js","sourceRoot":"","sources":["../src/ExpoJuce.types.ts"],"names":[],"mappings":"","sourcesContent":["import type * as React from 'react';\r\nimport type { StyleProp, ViewStyle } from 'react-native';\r\n\r\nexport type ChangeEventPayload = {\r\n value: string;\r\n};\r\n\r\nexport type BeatEventPayload = {\r\n beat: number;\r\n bpm: number;\r\n};\r\n\r\nexport type SetupOptions = {\r\n /** Preferred I/O buffer size in samples (default 128). The device grants the nearest size it supports. */\r\n bufferSize?: number;\r\n /** Preferred sample rate in Hz (default 48000). */\r\n sampleRate?: number;\r\n};\r\n\r\n/** Payload of `JucePadView` `onNoteOn`. */\r\nexport type PadNoteOnEvent = {\r\n note: number;\r\n velocity: number;\r\n};\r\n\r\n/** Payload of `JucePadView` `onNoteOff`. */\r\nexport type PadNoteOffEvent = {\r\n note: number;\r\n};\r\n\r\nexport type JucePadViewProps = {\r\n /** MIDI note triggered while the pad is touched. */\r\n note: number;\r\n /** MIDI velocity 0-127 (default 100). */\r\n velocity?: number;\r\n /** When false, touches pass through and no note is played (default true). */\r\n enabled?: boolean;\r\n /** Fired on the first touch, after the note has been sent to the engine (visuals/haptics only). */\r\n onNoteOn?: (event: PadNoteOnEvent) => void;\r\n /** Fired when the last touch lifts or is cancelled, after the note-off was sent. */\r\n onNoteOff?: (event: PadNoteOffEvent) => void;\r\n style?: StyleProp<ViewStyle>;\r\n children?: React.ReactNode;\r\n};\r\n\r\n/** Native engine lifecycle state reported in `AudioDiagnostics.engineState`. */\r\nexport type EngineState = 'ready' | 'recovering' | 'failed' | 'uninitialized';\r\n\r\n/**\r\n * Snapshot of native audio-engine state, returned by `getDiagnostics()`.\r\n * Counters are cumulative since setup; peaks are a decaying hold (~300 ms half-life)\r\n * of the max |sample| per block. All fields have safe defaults when not set up.\r\n */\r\nexport type AudioDiagnostics = {\r\n /** Native engine finished setup. */\r\n isReady: boolean;\r\n /** Last setup step reached (0-11); -1 if setup never ran. On failure, the failing step. */\r\n setupStep: number;\r\n /** Setup failure and/or non-fatal setup notes; empty string if none. */\r\n lastError: string;\r\n deviceName: string;\r\n deviceType: string;\r\n sampleRate: number;\r\n bufferSize: number;\r\n outputLatencySamples: number;\r\n /** Device IO callbacks received. */\r\n callbackCount: number;\r\n /** AudioProcessorGraph::processBlock calls made by the device callback. */\r\n graphProcessCount: number;\r\n /** PolySynthProcessor::processBlock calls. */\r\n synthProcessCount: number;\r\n /** Peak of the graph output written to the device (before test tone). */\r\n outputPeak: number;\r\n /** Peak of the synth output inside PolySynthProcessor. */\r\n synthPeak: number;\r\n /** Device blocks containing a non-finite (NaN/Inf) output sample. */\r\n nanCount: number;\r\n /** Cumulative test-tone samples actually mixed into the device output (audio thread). */\r\n toneSamplesMixed: number;\r\n notesQueued: number;\r\n notesConsumed: number;\r\n /** noteOn events dropped because the event queue was full. */\r\n notesDropped: number;\r\n activeVoices: number;\r\n /** ms from noteOn enqueue to the synth consuming it; -1 if none yet. */\r\n lastNoteToRenderMs: number;\r\n /** ms from the UITouch hardware timestamp (JucePadView) to the synth consuming the note; -1 if none yet. */\r\n lastTouchToRenderMs: number;\r\n /** Buffer size (samples) requested at setup; compare with `bufferSize` (granted). */\r\n preferredBufferSize: number;\r\n /** Sample rate (Hz) requested at setup; compare with `sampleRate` (granted). */\r\n preferredSampleRate: number;\r\n /** Times another component changed the session category and it was re-asserted to Playback. */\r\n sessionReassertCount: number;\r\n /** AVAudioSession interruptions (calls, Siri, alarms) begun since launch. */\r\n interruptionCount: number;\r\n /** AVAudioSession media-services resets handled (full engine rebuild) since launch. */\r\n mediaResetCount: number;\r\n /** Last session-recovery problem; empty string if none. */\r\n lastRecoveryError: string;\r\n /**\r\n * Engine lifecycle (0.5.2+; absent on older binaries): \"ready\", \"recovering\" (rebuild running,\r\n * retry scheduled, or a lifecycle section in progress), \"failed\" (setup failed or rebuild\r\n * retries exhausted), \"uninitialized\" (never set up, or shut down).\r\n */\r\n engineState?: EngineState;\r\n /** JS-thread calls dropped because the engine lifecycle lock was busy (0.5.2+). */\r\n lockBusyDrops?: number;\r\n /** Times the audio callback was found stalled and the device was reopened (0.5.2+). */\r\n deadDeviceRestarts?: number;\r\n sessionCategory: string;\r\n sessionSampleRate: number;\r\n sessionIOBufferMs: number;\r\n sessionOutputLatencyMs: number;\r\n otherAudioPlaying: boolean;\r\n /** AVAudioSession current route output port types. */\r\n routeOutputs: string[];\r\n};\r\n"]}
1
+ {"version":3,"file":"ExpoJuce.types.js","sourceRoot":"","sources":["../src/ExpoJuce.types.ts"],"names":[],"mappings":"","sourcesContent":["import type * as React from 'react';\r\nimport type { StyleProp, ViewStyle } from 'react-native';\r\n\r\nexport type ChangeEventPayload = {\r\n value: string;\r\n};\r\n\r\nexport type BeatEventPayload = {\r\n beat: number;\r\n bpm: number;\r\n};\r\n\r\nexport type SetupOptions = {\r\n /** Preferred I/O buffer size in samples (default 128). The device grants the nearest size it supports. */\r\n bufferSize?: number;\r\n /** Preferred sample rate in Hz (default 48000). */\r\n sampleRate?: number;\r\n};\r\n\r\n/** Payload of `JucePadView` `onNoteOn`. */\r\nexport type PadNoteOnEvent = {\r\n note: number;\r\n velocity: number;\r\n};\r\n\r\n/** Payload of `JucePadView` `onNoteOff`. */\r\nexport type PadNoteOffEvent = {\r\n note: number;\r\n};\r\n\r\nexport type JucePadViewProps = {\r\n /** MIDI note triggered while the pad is touched. */\r\n note: number;\r\n /** MIDI velocity 0-127 (default 100). */\r\n velocity?: number;\r\n /** When false, touches pass through and no note is played (default true). */\r\n enabled?: boolean;\r\n /**\r\n * Touch area as a polygon: flat `[x0, y0, x1, y1, …]` normalised to the view (0–1). Touches\r\n * outside it pass through to the views underneath. Empty, odd-length or fewer than 3 points:\r\n * the whole view (default). iOS, 0.6.0+.\r\n */\r\n hitPolygon?: number[];\r\n /** Fired on the first touch, after the note has been sent to the engine (visuals/haptics only). */\r\n onNoteOn?: (event: PadNoteOnEvent) => void;\r\n /** Fired when the last touch lifts or is cancelled, after the note-off was sent. */\r\n onNoteOff?: (event: PadNoteOffEvent) => void;\r\n style?: StyleProp<ViewStyle>;\r\n children?: React.ReactNode;\r\n};\r\n\r\n/**\r\n * Effects of the master chain (synth → effects → output), in processing order. A safety\r\n * limiter (-0.3 dBFS ceiling) always runs after them and is not user-facing.\r\n */\r\nexport type EffectId =\r\n | 'drive'\r\n | 'crush'\r\n | 'eq'\r\n | 'chorus'\r\n | 'phaser'\r\n | 'echo'\r\n | 'reverb'\r\n | 'pump'\r\n | 'compressor';\r\n\r\n/** Parameter names per effect (ranges and defaults: see README \"Effects\"). */\r\nexport type EffectParams = {\r\n drive: 'amount' | 'tone' | 'mix';\r\n crush: 'bits' | 'downsample' | 'noise' | 'mix';\r\n eq: 'low' | 'mid' | 'midFreq' | 'high';\r\n chorus: 'rate' | 'depth' | 'mix';\r\n phaser: 'rate' | 'depth' | 'feedback' | 'mix';\r\n echo: 'time' | 'sync' | 'division' | 'feedback' | 'tone' | 'pingPong' | 'mix';\r\n reverb: 'size' | 'damping' | 'width' | 'mix';\r\n pump: 'depth' | 'shape' | 'division';\r\n compressor: 'threshold' | 'ratio' | 'attack' | 'release' | 'makeup';\r\n};\r\n\r\n/** One effect's state as stored natively (values after clamping). */\r\nexport type EffectState = {\r\n enabled: boolean;\r\n params: Record<string, number>;\r\n};\r\n\r\n/** Returned by `getEffectsState()`: every effect, enabled or not. */\r\nexport type EffectsState = Record<EffectId, EffectState>;\r\n\r\n/** Metronome click sounds. */\r\nexport type MetronomeStyle = 'classic' | 'wood' | 'beep' | 'cowbell' | 'hat';\r\n\r\n/** `setMetronome()` options; omitted keys keep their current value. */\r\nexport type MetronomeOptions = {\r\n /** Clicks while the transport plays (default false). */\r\n enabled?: boolean;\r\n /** Click sound (default 'classic'). */\r\n style?: MetronomeStyle;\r\n /** 0–1 (default 0.8). */\r\n level?: number;\r\n /** Higher click on beat 1 of the bar (default true). */\r\n accent?: boolean;\r\n /** 1–16, rounded (default 4). */\r\n beatsPerBar?: number;\r\n};\r\n\r\n/**\r\n * Gapless info of an MP3 (frames at the file's own rate, from its LAME/Xing tag): lets\r\n * `loadBackingTrack` cut the exact loop out of what the decoder returns.\r\n */\r\nexport type BackingTrackGapless = {\r\n loopSamples: number;\r\n leadingSamples: number;\r\n trailingSamples: number;\r\n};\r\n\r\n/** Loop region chosen by `loadBackingTrack()` (see `BackingTrackInfo.regionRule`). */\r\nexport type BackingTrackRegionRule = 'tag' | 'padded' | 'paddedNoTagFrame' | 'whole';\r\n\r\n/** Resolved by `loadBackingTrack()`. Sample counts are frames at the file's own rate. */\r\nexport type BackingTrackInfo = {\r\n /** Length of the loop region in seconds. */\r\n durationSec: number;\r\n /** The file's sample rate (playback is resampled to the device rate). */\r\n sampleRate: number;\r\n channels: number;\r\n /** Where the loop region starts in the decoded audio. */\r\n loopStartSample: number;\r\n /** Length of the loop region. */\r\n loopSamples: number;\r\n /** Frames the decoder produced before the region was cut. */\r\n decodedSamples?: number;\r\n /**\r\n * Which loop region was used (0.6.0-rc.1+): 'tag' (the decoder honoured the gapless tag),\r\n * 'padded' (tag frame decoded as silence, padding cut), 'paddedNoTagFrame' (tag frame skipped,\r\n * padding cut), 'whole' (no gapless info, or a length matching no case: the whole file).\r\n */\r\n regionRule?: BackingTrackRegionRule;\r\n /** Decoder used (e.g. \"CoreAudio\", \"MP3 file\", \"WAV file\"). */\r\n decoder?: string;\r\n};\r\n\r\n/** `setBackingTrackOptions()` options; omitted keys keep their current value. */\r\nexport type BackingTrackOptions = {\r\n /** 0–1 (default 0.8). */\r\n level?: number;\r\n /** Loop the track (default true); otherwise it plays once and then is silent. */\r\n loop?: boolean;\r\n /** Plays with the transport (default true). */\r\n enabled?: boolean;\r\n};\r\n\r\n/** Arpeggiator note order (0.7.0+). */\r\nexport type ArpMode = 'up' | 'down' | 'upDown' | 'played' | 'random' | 'chord';\r\n\r\n/**\r\n * Arpeggiator step rate (0.7.0+). Steps per beat: '1/4' 1, '1/8' 2, '1/8T' 3, '1/8D' 4/3,\r\n * '1/16' 4, '1/16T' 6, '1/32' 8.\r\n */\r\nexport type ArpRate = '1/4' | '1/8' | '1/8T' | '1/8D' | '1/16' | '1/16T' | '1/32';\r\n\r\n/**\r\n * `setArpeggiator()` options / `getArpeggiatorState()` result (0.7.0+). In `setArpeggiator`\r\n * omitted keys keep their current value; numbers are clamped to their range; an unknown `mode`\r\n * or `rate` rejects the whole call (nothing applied).\r\n */\r\nexport type ArpOptions = {\r\n /** Arpeggiate held notes instead of playing them directly (default false). */\r\n enabled: boolean;\r\n /** Note order (default 'up'). */\r\n mode: ArpMode;\r\n /** Step rate relative to the transport tempo (default '1/16'). */\r\n rate: ArpRate;\r\n /** 1–4 octaves of the held notes in the cycle (default 1). */\r\n octaves: number;\r\n /** Note length, 0.05–1.5 of a step (default 0.6); > 1 overlaps the next step (legato). */\r\n gate: number;\r\n /** 0–0.5 of a step added to odd steps (default 0). */\r\n swing: number;\r\n /** Keep playing after every note is released; the next press starts a new chord (default false). */\r\n latch: boolean;\r\n /** 1–16 steps, true = the step plays (default 16 × true). */\r\n pattern: boolean[];\r\n /** 0–1 (default 0): steps other than step 0 play at velocity × (1 − 0.6 × accent). */\r\n accent: number;\r\n /** 1–4 hits per step (default 1). */\r\n ratchet: number;\r\n /** 0–8 pattern passes before the transposition wraps (default 0 = off). */\r\n transposeSteps: number;\r\n /** −12..12 semitones added per pass (default 0). */\r\n transposeDistance: number;\r\n};\r\n\r\n/** Arpeggiator state in `Analysis.arp` (0.7.0+), as of the last rendered block. */\r\nexport type ArpAnalysis = {\r\n /** The arp is enabled and holds notes (a step is sounding or coming). */\r\n active: boolean;\r\n /** Pattern slot of the last step started (0-based). */\r\n step: number;\r\n /** Pattern length. */\r\n steps: number;\r\n /** Last note the arp started; -1 if none yet. */\r\n note: number;\r\n};\r\n\r\n/** Returned by `getAnalysis()`: the device output (synth, effects, metronome, backing track). */\r\nexport type Analysis = {\r\n /** Max |sample| since the previous getAnalysis() call (peak hold), 0–1+. */\r\n peak: number;\r\n /** RMS (both channels) since the previous getAnalysis() call. */\r\n rms: number;\r\n /** 128 points, oldest first, decimated (every 16th sample) from the last 2048 output samples (mono). */\r\n scope: number[];\r\n /**\r\n * 24 log-spaced bands, 40 Hz – 16 kHz, each 0–1 (−60..0 dBFS of the loudest FFT bin in the\r\n * band) from the last 2048 output samples (0.7.0+; 24 zeros on older binaries).\r\n */\r\n bands: number[];\r\n /** Sample rate the bands were computed at (0 when the engine is not running / older binaries). */\r\n sampleRate: number;\r\n /** Last note the synth started; -1 if none yet. */\r\n lastNote: number;\r\n /** Transport tempo, position (beats) and play state as of the last rendered block. */\r\n bpm: number;\r\n beat: number;\r\n playing: boolean;\r\n /** Arpeggiator state (0.7.0+; inactive on older binaries). */\r\n arp: ArpAnalysis;\r\n};\r\n\r\n/** Native engine lifecycle state reported in `AudioDiagnostics.engineState`. */\r\nexport type EngineState = 'ready' | 'recovering' | 'failed' | 'uninitialized';\r\n\r\n/**\r\n * Snapshot of native audio-engine state, returned by `getDiagnostics()`.\r\n * Counters are cumulative since setup; peaks are a decaying hold (~300 ms half-life)\r\n * of the max |sample| per block. All fields have safe defaults when not set up.\r\n */\r\nexport type AudioDiagnostics = {\r\n /** Native engine finished setup. */\r\n isReady: boolean;\r\n /** Last setup step reached (0-11); -1 if setup never ran. On failure, the failing step. */\r\n setupStep: number;\r\n /** Setup failure and/or non-fatal setup notes; empty string if none. */\r\n lastError: string;\r\n deviceName: string;\r\n deviceType: string;\r\n sampleRate: number;\r\n bufferSize: number;\r\n outputLatencySamples: number;\r\n /** Device IO callbacks received. */\r\n callbackCount: number;\r\n /** AudioProcessorGraph::processBlock calls made by the device callback. */\r\n graphProcessCount: number;\r\n /** PolySynthProcessor::processBlock calls. */\r\n synthProcessCount: number;\r\n /** Peak of the graph output written to the device (before test tone). */\r\n outputPeak: number;\r\n /** Peak of the synth output inside PolySynthProcessor. */\r\n synthPeak: number;\r\n /** Device blocks containing a non-finite (NaN/Inf) output sample. */\r\n nanCount: number;\r\n /** Cumulative test-tone samples actually mixed into the device output (audio thread). */\r\n toneSamplesMixed: number;\r\n /** Note-ons accepted into the note queue (cumulative since launch, across engine rebuilds). */\r\n notesQueued: number;\r\n /** Note-ons the synth has started (cumulative since launch). */\r\n notesConsumed: number;\r\n /** Note-ons dropped because the note queue was full. Note releases are never dropped. */\r\n notesDropped: number;\r\n activeVoices: number;\r\n /** ms from noteOn enqueue to the synth consuming it; -1 if none yet. */\r\n lastNoteToRenderMs: number;\r\n /** ms from the UITouch hardware timestamp (JucePadView) to the synth consuming the note; -1 if none yet. */\r\n lastTouchToRenderMs: number;\r\n /** Buffer size (samples) requested at setup; compare with `bufferSize` (granted). */\r\n preferredBufferSize: number;\r\n /** Sample rate (Hz) requested at setup; compare with `sampleRate` (granted). */\r\n preferredSampleRate: number;\r\n /** Times another component changed the session category and it was re-asserted to Playback. */\r\n sessionReassertCount: number;\r\n /** AVAudioSession interruptions (calls, Siri, alarms) begun since launch. */\r\n interruptionCount: number;\r\n /** AVAudioSession media-services resets handled (full engine rebuild) since launch. */\r\n mediaResetCount: number;\r\n /** Last session-recovery problem; empty string if none. */\r\n lastRecoveryError: string;\r\n /**\r\n * Engine lifecycle (0.5.2+; absent on older binaries): \"ready\", \"recovering\" (rebuild running,\r\n * retry scheduled, or a lifecycle section in progress), \"failed\" (setup failed or rebuild\r\n * retries exhausted), \"uninitialized\" (never set up, or shut down).\r\n */\r\n engineState?: EngineState;\r\n /** JS-thread calls dropped because the engine lifecycle lock was busy (0.5.2+). */\r\n lockBusyDrops?: number;\r\n /** Times the audio callback was found stalled and the device was reopened (0.5.2+). */\r\n deadDeviceRestarts?: number;\r\n /** FxChainProcessor::processBlock calls (0.6.0+). */\r\n fxProcessCount?: number;\r\n /** Lowest safety-limiter gain in the last effects block, 1 = not limiting (0.6.0+). */\r\n fxLimiterGain?: number;\r\n /** Effect chunks whose output went non-finite and were silenced + reset (0.6.0+). */\r\n fxNonFiniteResets?: number;\r\n /** Metronome clicks started (cumulative in the current engine, 0.6.0+). */\r\n metronomeClicks?: number;\r\n /** A backing track is loaded (0.6.0+). */\r\n backingTrackLoaded?: boolean;\r\n /** Backing-track playback position in seconds; -1 when not playing (0.6.0+). */\r\n backingTrackPositionSec?: number;\r\n sessionCategory: string;\r\n sessionSampleRate: number;\r\n sessionIOBufferMs: number;\r\n sessionOutputLatencyMs: number;\r\n otherAudioPlaying: boolean;\r\n /** AVAudioSession current route output port types. */\r\n routeOutputs: string[];\r\n};\r\n"]}
@@ -1,3 +1,48 @@
1
- declare const _default: any;
1
+ import type { Analysis, ArpOptions, AudioDiagnostics, BackingTrackGapless, BackingTrackInfo, BackingTrackOptions, EffectsState, MetronomeOptions } from './ExpoJuce.types';
2
+ export type ExpoJuceNativeModule = {
3
+ PI: number;
4
+ hello(): string;
5
+ setup(options?: {
6
+ bufferSize?: number;
7
+ sampleRate?: number;
8
+ }): Promise<void>;
9
+ shutdown(): Promise<void>;
10
+ hasJucePadView?(): boolean;
11
+ getDiagnostics(): AudioDiagnostics;
12
+ playTestTone(durationMs: number): boolean;
13
+ noteOn(note: number, velocity: number): void;
14
+ noteOff(note: number): void;
15
+ allNotesOff(): void;
16
+ setWaveform(waveform: string): void;
17
+ setFilterCutoff(hz: number): void;
18
+ setFilterResonance(q: number): void;
19
+ setAttack(ms: number): void;
20
+ setDecay(ms: number): void;
21
+ setSustain(level: number): void;
22
+ setRelease(ms: number): void;
23
+ setDetune(cents: number): void;
24
+ setMasterLevel(level: number): void;
25
+ setEffectEnabled?(id: string, enabled: boolean): boolean;
26
+ setEffectParam?(id: string, param: string, value: number): boolean;
27
+ getEffectsState?(): EffectsState;
28
+ setMetronome?(options: MetronomeOptions): boolean;
29
+ setBackingTrackOptions?(options: BackingTrackOptions): void;
30
+ loadBackingTrack?(uri: string, gapless: BackingTrackGapless | null): Promise<BackingTrackInfo>;
31
+ unloadBackingTrack?(): void;
32
+ getAnalysis?(): Analysis;
33
+ setArpeggiator?(options: Partial<ArpOptions>): boolean;
34
+ getArpeggiatorState?(): ArpOptions;
35
+ startTransport(): void;
36
+ stopTransport(): void;
37
+ setTempo(bpm: number): void;
38
+ setTransportPosition(beat: number): void;
39
+ getTransportPosition(): number;
40
+ getTempo(): number;
41
+ isTransportPlaying(): boolean;
42
+ setValueAsync(value: string): Promise<void>;
43
+ addListener(eventName: string): void;
44
+ removeListeners(count: number): void;
45
+ };
46
+ declare const _default: ExpoJuceNativeModule;
2
47
  export default _default;
3
48
  //# sourceMappingURL=ExpoJuceModule.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoJuceModule.d.ts","sourceRoot":"","sources":["../src/ExpoJuceModule.ts"],"names":[],"mappings":";AAIA,wBAA+C"}
1
+ {"version":3,"file":"ExpoJuceModule.d.ts","sourceRoot":"","sources":["../src/ExpoJuceModule.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoJuceModule.js","sourceRoot":"","sources":["../src/ExpoJuceModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,kEAAkE;AAClE,4EAA4E;AAC5E,eAAe,mBAAmB,CAAC,UAAU,CAAC,CAAC","sourcesContent":["import { requireNativeModule } from 'expo-modules-core';\r\n\r\n// It loads the native module object from the JSI or falls back to\r\n// the bridge module (from NativeModulesProxy) if the remote debugger is on.\r\nexport default requireNativeModule('ExpoJuce');\r\n"]}
1
+ {"version":3,"file":"ExpoJuceModule.js","sourceRoot":"","sources":["../src/ExpoJuceModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AA0ExD,kEAAkE;AAClE,4EAA4E;AAC5E,eAAe,mBAAmB,CAAuB,UAAU,CAAC,CAAC","sourcesContent":["import { requireNativeModule } from 'expo-modules-core';\r\n\r\nimport type {\r\n Analysis,\r\n ArpOptions,\r\n AudioDiagnostics,\r\n BackingTrackGapless,\r\n BackingTrackInfo,\r\n BackingTrackOptions,\r\n EffectsState,\r\n MetronomeOptions,\r\n} from './ExpoJuce.types';\r\n\r\n// The native module (ios/ExpoJuceModule.swift). Functions added after 0.5.0 are optional,\r\n// because an older binary does not export them: callers check `typeof fn === 'function'`.\r\nexport type ExpoJuceNativeModule = {\r\n PI: number;\r\n hello(): string;\r\n\r\n // Lifecycle. Binaries older than 0.5.2 take no argument (see setup() in index.ts).\r\n setup(options?: { bufferSize?: number; sampleRate?: number }): Promise<void>;\r\n shutdown(): Promise<void>;\r\n hasJucePadView?(): boolean;\r\n\r\n // Diagnostics\r\n getDiagnostics(): AudioDiagnostics;\r\n playTestTone(durationMs: number): boolean;\r\n\r\n // Synth\r\n noteOn(note: number, velocity: number): void;\r\n noteOff(note: number): void;\r\n allNotesOff(): void;\r\n setWaveform(waveform: string): void;\r\n setFilterCutoff(hz: number): void;\r\n setFilterResonance(q: number): void;\r\n setAttack(ms: number): void;\r\n setDecay(ms: number): void;\r\n setSustain(level: number): void;\r\n setRelease(ms: number): void;\r\n setDetune(cents: number): void;\r\n setMasterLevel(level: number): void;\r\n\r\n // Effects (0.6.0+)\r\n setEffectEnabled?(id: string, enabled: boolean): boolean;\r\n setEffectParam?(id: string, param: string, value: number): boolean;\r\n getEffectsState?(): EffectsState;\r\n\r\n // Metronome, backing track, analysis (0.6.0+)\r\n setMetronome?(options: MetronomeOptions): boolean;\r\n setBackingTrackOptions?(options: BackingTrackOptions): void;\r\n loadBackingTrack?(uri: string, gapless: BackingTrackGapless | null): Promise<BackingTrackInfo>;\r\n unloadBackingTrack?(): void;\r\n getAnalysis?(): Analysis;\r\n\r\n // Arpeggiator (0.7.0+)\r\n setArpeggiator?(options: Partial<ArpOptions>): boolean;\r\n getArpeggiatorState?(): ArpOptions;\r\n\r\n // Transport\r\n startTransport(): void;\r\n stopTransport(): void;\r\n setTempo(bpm: number): void;\r\n setTransportPosition(beat: number): void;\r\n getTransportPosition(): number;\r\n getTempo(): number;\r\n isTransportPlaying(): boolean;\r\n\r\n // Events\r\n setValueAsync(value: string): Promise<void>;\r\n // Event-emitter hooks (keeps the type compatible with expo-modules-core's EventEmitter).\r\n addListener(eventName: string): void;\r\n removeListeners(count: number): void;\r\n};\r\n\r\n// It loads the native module object from the JSI or falls back to\r\n// the bridge module (from NativeModulesProxy) if the remote debugger is on.\r\nexport default requireNativeModule<ExpoJuceNativeModule>('ExpoJuce');\r\n"]}