expo-juce 0.3.0 → 0.3.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.
@@ -6,9 +6,6 @@ public class ExpoJuceModule: Module {
6
6
  public func definition() -> ModuleDefinition {
7
7
  Name("ExpoJuce")
8
8
 
9
- // Audio engine is initialized lazily on first use, not at module load.
10
- // Initializing AVAudioEngine in OnCreate can crash before JS is ready.
11
-
12
9
  OnDestroy {
13
10
  ExpoJuceBridge.stopBeatTimer()
14
11
  ExpoJuceBridge.shutdown()
@@ -20,8 +17,6 @@ public class ExpoJuceModule: Module {
20
17
 
21
18
  Events("onChange", "onBeat")
22
19
 
23
- // Beat timer only runs while JS has active listeners.
24
- // This prevents sendEvent from firing before listeners are attached (crash).
25
20
  OnStartObserving {
26
21
  self.hasListeners = true
27
22
  ExpoJuceBridge.startBeatTimer()
@@ -37,8 +32,13 @@ public class ExpoJuceModule: Module {
37
32
  }
38
33
 
39
34
  // ── Synth ───────────────────────────────────────────────────────
35
+ // All audio methods use AsyncFunction dispatched on main queue.
36
+ // AVAudioEngine and CoreAudio must be accessed from the main thread.
37
+ // Expo's synchronous Function() runs on the JS/Hermes thread,
38
+ // which is NOT the main thread in production builds — this causes
39
+ // native crashes when calling CoreAudio APIs.
40
40
 
41
- Function("setup") { [weak self] in
41
+ AsyncFunction("setup") { [weak self] in
42
42
  ExpoJuceBridge.setup()
43
43
  ExpoJuceBridge.setBeatCallback { [weak self] beat, bpm in
44
44
  guard let self = self, self.hasListeners else { return }
@@ -47,61 +47,61 @@ public class ExpoJuceModule: Module {
47
47
  "bpm": bpm,
48
48
  ])
49
49
  }
50
- }
50
+ }.runOnQueue(.main)
51
51
 
52
- Function("playTone") { (frequency: Double, duration: Double) -> String in
52
+ AsyncFunction("playTone") { (frequency: Double, duration: Double) -> String in
53
53
  ExpoJuceBridge.playTone(withFrequency: frequency, duration: duration)
54
54
  return "Playing tone at \(frequency)Hz for \(duration)ms"
55
- }
55
+ }.runOnQueue(.main)
56
56
 
57
- Function("setFrequency") { (frequency: Double) in
57
+ AsyncFunction("setFrequency") { (frequency: Double) in
58
58
  ExpoJuceBridge.setFrequency(frequency)
59
- }
59
+ }.runOnQueue(.main)
60
60
 
61
- Function("setLevel") { (level: Double) in
61
+ AsyncFunction("setLevel") { (level: Double) in
62
62
  ExpoJuceBridge.setLevel(level)
63
- }
63
+ }.runOnQueue(.main)
64
64
 
65
- Function("setWaveform") { (waveform: String) in
65
+ AsyncFunction("setWaveform") { (waveform: String) in
66
66
  ExpoJuceBridge.setWaveform(waveform)
67
- }
67
+ }.runOnQueue(.main)
68
68
 
69
- Function("setHarmonicLevels") { (levels: [Double]) in
69
+ AsyncFunction("setHarmonicLevels") { (levels: [Double]) in
70
70
  let nsLevels = levels.map { NSNumber(value: $0) }
71
71
  ExpoJuceBridge.setHarmonicLevels(nsLevels)
72
- }
72
+ }.runOnQueue(.main)
73
73
 
74
- Function("noteOn") {
74
+ AsyncFunction("noteOn") {
75
75
  ExpoJuceBridge.noteOn()
76
- }
76
+ }.runOnQueue(.main)
77
77
 
78
- Function("noteOff") {
78
+ AsyncFunction("noteOff") {
79
79
  ExpoJuceBridge.noteOff()
80
- }
80
+ }.runOnQueue(.main)
81
81
 
82
82
  // ── DSP Params ──────────────────────────────────────────────────
83
83
 
84
- Function("setAttack") { (ms: Double) in
84
+ AsyncFunction("setAttack") { (ms: Double) in
85
85
  ExpoJuceBridge.setAttack(ms)
86
- }
86
+ }.runOnQueue(.main)
87
87
 
88
- Function("setRelease") { (ms: Double) in
88
+ AsyncFunction("setRelease") { (ms: Double) in
89
89
  ExpoJuceBridge.setRelease(ms)
90
- }
90
+ }.runOnQueue(.main)
91
91
 
92
- Function("setFilterCutoff") { (hz: Double) in
92
+ AsyncFunction("setFilterCutoff") { (hz: Double) in
93
93
  ExpoJuceBridge.setFilterCutoff(hz)
94
- }
94
+ }.runOnQueue(.main)
95
95
 
96
- Function("setFilterResonance") { (q: Double) in
96
+ AsyncFunction("setFilterResonance") { (q: Double) in
97
97
  ExpoJuceBridge.setFilterResonance(q)
98
- }
98
+ }.runOnQueue(.main)
99
99
 
100
- Function("setDetune") { (cents: Double) in
100
+ AsyncFunction("setDetune") { (cents: Double) in
101
101
  ExpoJuceBridge.setDetune(cents)
102
- }
102
+ }.runOnQueue(.main)
103
103
 
104
- Function("setDSPValue") { (param: String, value: Double) in
104
+ AsyncFunction("setDSPValue") { (param: String, value: Double) in
105
105
  switch param {
106
106
  case "frequency":
107
107
  ExpoJuceBridge.setFrequency(value)
@@ -120,37 +120,37 @@ public class ExpoJuceModule: Module {
120
120
  default:
121
121
  break
122
122
  }
123
- }
123
+ }.runOnQueue(.main)
124
124
 
125
125
  // ── Transport ───────────────────────────────────────────────────
126
126
 
127
- Function("startTransport") {
127
+ AsyncFunction("startTransport") {
128
128
  ExpoJuceBridge.startTransport()
129
- }
129
+ }.runOnQueue(.main)
130
130
 
131
- Function("stopTransport") {
131
+ AsyncFunction("stopTransport") {
132
132
  ExpoJuceBridge.stopTransport()
133
- }
133
+ }.runOnQueue(.main)
134
134
 
135
- Function("setTempo") { (bpm: Double) in
135
+ AsyncFunction("setTempo") { (bpm: Double) in
136
136
  ExpoJuceBridge.setTempo(bpm)
137
- }
137
+ }.runOnQueue(.main)
138
138
 
139
- Function("setTransportPosition") { (beat: Double) in
139
+ AsyncFunction("setTransportPosition") { (beat: Double) in
140
140
  ExpoJuceBridge.setTransportPosition(beat)
141
- }
141
+ }.runOnQueue(.main)
142
142
 
143
- Function("getTransportPosition") { () -> Double in
143
+ AsyncFunction("getTransportPosition") { () -> Double in
144
144
  return ExpoJuceBridge.getTransportPosition()
145
- }
145
+ }.runOnQueue(.main)
146
146
 
147
- Function("getTempo") { () -> Double in
147
+ AsyncFunction("getTempo") { () -> Double in
148
148
  return ExpoJuceBridge.getTempo()
149
- }
149
+ }.runOnQueue(.main)
150
150
 
151
- Function("isTransportPlaying") { () -> Bool in
151
+ AsyncFunction("isTransportPlaying") { () -> Bool in
152
152
  return ExpoJuceBridge.isTransportPlaying()
153
- }
153
+ }.runOnQueue(.main)
154
154
 
155
155
  // ── Async / View ────────────────────────────────────────────────
156
156
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-juce",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Realtime DSP w/C++ & JUCE",
5
5
  "type": "module",
6
6
  "main": "build/index.js",