expo-juce 0.3.2 → 0.4.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.
package/build/index.d.ts CHANGED
@@ -3,25 +3,25 @@ import ExpoJuceView from './ExpoJuceView';
3
3
  import { ChangeEventPayload, BeatEventPayload, ExpoJuceViewProps } from './ExpoJuce.types';
4
4
  export declare function setup(): Promise<void>;
5
5
  export declare function shutdown(): Promise<void>;
6
- export declare function noteOn(note: number, velocity: number): Promise<void>;
7
- export declare function noteOff(note: number): Promise<void>;
8
- export declare function allNotesOff(): Promise<void>;
9
- export declare function setWaveform(waveform: 'sine' | 'square' | 'saw' | 'triangle'): Promise<void>;
10
- export declare function setFilterCutoff(hz: number): Promise<void>;
11
- export declare function setFilterResonance(q: number): Promise<void>;
12
- export declare function setAttack(ms: number): Promise<void>;
13
- export declare function setDecay(ms: number): Promise<void>;
14
- export declare function setSustain(level: number): Promise<void>;
15
- export declare function setRelease(ms: number): Promise<void>;
16
- export declare function setDetune(cents: number): Promise<void>;
17
- export declare function setMasterLevel(level: number): Promise<void>;
18
- export declare function startTransport(): Promise<void>;
19
- export declare function stopTransport(): Promise<void>;
20
- export declare function setTempo(bpm: number): Promise<void>;
21
- export declare function setTransportPosition(beat: number): Promise<void>;
22
- export declare function getTransportPosition(): Promise<number>;
23
- export declare function getTempo(): Promise<number>;
24
- export declare function isTransportPlaying(): Promise<boolean>;
6
+ export declare function noteOn(note: number, velocity: number): void;
7
+ export declare function noteOff(note: number): void;
8
+ export declare function allNotesOff(): void;
9
+ export declare function setWaveform(waveform: 'sine' | 'square' | 'saw' | 'triangle'): void;
10
+ export declare function setFilterCutoff(hz: number): void;
11
+ export declare function setFilterResonance(q: number): void;
12
+ export declare function setAttack(ms: number): void;
13
+ export declare function setDecay(ms: number): void;
14
+ export declare function setSustain(level: number): void;
15
+ export declare function setRelease(ms: number): void;
16
+ export declare function setDetune(cents: number): void;
17
+ export declare function setMasterLevel(level: number): void;
18
+ export declare function startTransport(): void;
19
+ export declare function stopTransport(): void;
20
+ export declare function setTempo(bpm: number): void;
21
+ export declare function setTransportPosition(beat: number): void;
22
+ export declare function getTransportPosition(): number;
23
+ export declare function getTempo(): number;
24
+ export declare function isTransportPlaying(): boolean;
25
25
  export declare function setValueAsync(value: string): Promise<any>;
26
26
  export declare function addChangeListener(listener: (event: ChangeEventPayload) => void): Subscription;
27
27
  export declare function addBeatListener(listener: (event: BeatEventPayload) => void): Subscription;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGnF,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAI3F,wBAAsB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3C;AAED,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAE9C;AAID,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1E;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzD;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAEjD;AAID,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjG;AAED,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/D;AAED,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjE;AAED,wBAAsB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzD;AAED,wBAAsB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAExD;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE7D;AAED,wBAAsB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1D;AAED,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5D;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjE;AAID,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAEpD;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAEnD;AAED,wBAAsB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzD;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC,CAE5D;AAED,wBAAsB,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,CAEhD;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC,CAE3D;AAID,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,gBAEhD;AAID,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,GAAG,YAAY,CAE7F;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,YAAY,CAEzF;AAED,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGnF,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAI3F,wBAAsB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3C;AAED,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAE9C;AAKD,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE3D;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED,wBAAgB,WAAW,IAAI,IAAI,CAElC;AAKD,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,UAAU,GAAG,IAAI,CAElF;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAEhD;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAElD;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAEzC;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAElD;AAID,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEvD;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,wBAAgB,QAAQ,IAAI,MAAM,CAEjC;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAID,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,gBAEhD;AAID,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,GAAG,YAAY,CAE7F;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,YAAY,CAEzF;AAED,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,CAAC"}
package/build/index.js CHANGED
@@ -9,63 +9,65 @@ export async function shutdown() {
9
9
  return ExpoJuceModule.shutdown();
10
10
  }
11
11
  // ── Polyphonic Synth (MIDI-style) ────────────────────────────────
12
- export async function noteOn(note, velocity) {
13
- return ExpoJuceModule.noteOn(note, velocity);
12
+ // Synchronous uses lock-free SPSC queue, no main thread required
13
+ export function noteOn(note, velocity) {
14
+ ExpoJuceModule.noteOn(note, velocity);
14
15
  }
15
- export async function noteOff(note) {
16
- return ExpoJuceModule.noteOff(note);
16
+ export function noteOff(note) {
17
+ ExpoJuceModule.noteOff(note);
17
18
  }
18
- export async function allNotesOff() {
19
- return ExpoJuceModule.allNotesOff();
19
+ export function allNotesOff() {
20
+ ExpoJuceModule.allNotesOff();
20
21
  }
21
22
  // ── Global Synth Params ──────────────────────────────────────────
22
- export async function setWaveform(waveform) {
23
- return ExpoJuceModule.setWaveform(waveform);
23
+ // Synchronous all params are std::atomic
24
+ export function setWaveform(waveform) {
25
+ ExpoJuceModule.setWaveform(waveform);
24
26
  }
25
- export async function setFilterCutoff(hz) {
26
- return ExpoJuceModule.setFilterCutoff(hz);
27
+ export function setFilterCutoff(hz) {
28
+ ExpoJuceModule.setFilterCutoff(hz);
27
29
  }
28
- export async function setFilterResonance(q) {
29
- return ExpoJuceModule.setFilterResonance(q);
30
+ export function setFilterResonance(q) {
31
+ ExpoJuceModule.setFilterResonance(q);
30
32
  }
31
- export async function setAttack(ms) {
32
- return ExpoJuceModule.setAttack(ms);
33
+ export function setAttack(ms) {
34
+ ExpoJuceModule.setAttack(ms);
33
35
  }
34
- export async function setDecay(ms) {
35
- return ExpoJuceModule.setDecay(ms);
36
+ export function setDecay(ms) {
37
+ ExpoJuceModule.setDecay(ms);
36
38
  }
37
- export async function setSustain(level) {
38
- return ExpoJuceModule.setSustain(level);
39
+ export function setSustain(level) {
40
+ ExpoJuceModule.setSustain(level);
39
41
  }
40
- export async function setRelease(ms) {
41
- return ExpoJuceModule.setRelease(ms);
42
+ export function setRelease(ms) {
43
+ ExpoJuceModule.setRelease(ms);
42
44
  }
43
- export async function setDetune(cents) {
44
- return ExpoJuceModule.setDetune(cents);
45
+ export function setDetune(cents) {
46
+ ExpoJuceModule.setDetune(cents);
45
47
  }
46
- export async function setMasterLevel(level) {
47
- return ExpoJuceModule.setMasterLevel(level);
48
+ export function setMasterLevel(level) {
49
+ ExpoJuceModule.setMasterLevel(level);
48
50
  }
49
51
  // ── Transport ────────────────────────────────────────────────────
50
- export async function startTransport() {
51
- return ExpoJuceModule.startTransport();
52
+ export function startTransport() {
53
+ ExpoJuceModule.startTransport();
52
54
  }
53
- export async function stopTransport() {
54
- return ExpoJuceModule.stopTransport();
55
+ export function stopTransport() {
56
+ ExpoJuceModule.stopTransport();
55
57
  }
56
- export async function setTempo(bpm) {
57
- return ExpoJuceModule.setTempo(bpm);
58
+ export function setTempo(bpm) {
59
+ ExpoJuceModule.setTempo(bpm);
58
60
  }
59
- export async function setTransportPosition(beat) {
60
- return ExpoJuceModule.setTransportPosition(beat);
61
+ export function setTransportPosition(beat) {
62
+ ExpoJuceModule.setTransportPosition(beat);
61
63
  }
62
- export async function getTransportPosition() {
64
+ export function getTransportPosition() {
63
65
  return ExpoJuceModule.getTransportPosition();
64
66
  }
65
- export async function getTempo() {
67
+ export function getTempo() {
66
68
  return ExpoJuceModule.getTempo();
67
69
  }
68
- export async function isTransportPlaying() {
70
+ export function isTransportPlaying() {
69
71
  return ExpoJuceModule.isTransportPlaying();
70
72
  }
71
73
  // ── Events ───────────────────────────────────────────────────────
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModulesProxy, EventEmitter, Subscription } from 'expo-modules-core';\n\nimport ExpoJuceModule from './ExpoJuceModule';\nimport ExpoJuceView from './ExpoJuceView';\nimport { ChangeEventPayload, BeatEventPayload, ExpoJuceViewProps } from './ExpoJuce.types';\n\n// ── Lifecycle ────────────────────────────────────────────────────\n\nexport async function setup(): Promise<void> {\n return ExpoJuceModule.setup();\n}\n\nexport async function shutdown(): Promise<void> {\n return ExpoJuceModule.shutdown();\n}\n\n// ── Polyphonic Synth (MIDI-style) ────────────────────────────────\n\nexport async function noteOn(note: number, velocity: number): Promise<void> {\n return ExpoJuceModule.noteOn(note, velocity);\n}\n\nexport async function noteOff(note: number): Promise<void> {\n return ExpoJuceModule.noteOff(note);\n}\n\nexport async function allNotesOff(): Promise<void> {\n return ExpoJuceModule.allNotesOff();\n}\n\n// ── Global Synth Params ──────────────────────────────────────────\n\nexport async function setWaveform(waveform: 'sine' | 'square' | 'saw' | 'triangle'): Promise<void> {\n return ExpoJuceModule.setWaveform(waveform);\n}\n\nexport async function setFilterCutoff(hz: number): Promise<void> {\n return ExpoJuceModule.setFilterCutoff(hz);\n}\n\nexport async function setFilterResonance(q: number): Promise<void> {\n return ExpoJuceModule.setFilterResonance(q);\n}\n\nexport async function setAttack(ms: number): Promise<void> {\n return ExpoJuceModule.setAttack(ms);\n}\n\nexport async function setDecay(ms: number): Promise<void> {\n return ExpoJuceModule.setDecay(ms);\n}\n\nexport async function setSustain(level: number): Promise<void> {\n return ExpoJuceModule.setSustain(level);\n}\n\nexport async function setRelease(ms: number): Promise<void> {\n return ExpoJuceModule.setRelease(ms);\n}\n\nexport async function setDetune(cents: number): Promise<void> {\n return ExpoJuceModule.setDetune(cents);\n}\n\nexport async function setMasterLevel(level: number): Promise<void> {\n return ExpoJuceModule.setMasterLevel(level);\n}\n\n// ── Transport ────────────────────────────────────────────────────\n\nexport async function startTransport(): Promise<void> {\n return ExpoJuceModule.startTransport();\n}\n\nexport async function stopTransport(): Promise<void> {\n return ExpoJuceModule.stopTransport();\n}\n\nexport async function setTempo(bpm: number): Promise<void> {\n return ExpoJuceModule.setTempo(bpm);\n}\n\nexport async function setTransportPosition(beat: number): Promise<void> {\n return ExpoJuceModule.setTransportPosition(beat);\n}\n\nexport async function getTransportPosition(): Promise<number> {\n return ExpoJuceModule.getTransportPosition();\n}\n\nexport async function getTempo(): Promise<number> {\n return ExpoJuceModule.getTempo();\n}\n\nexport async function isTransportPlaying(): Promise<boolean> {\n return ExpoJuceModule.isTransportPlaying();\n}\n\n// ── Events ───────────────────────────────────────────────────────\n\nexport async function setValueAsync(value: string) {\n return await ExpoJuceModule.setValueAsync(value);\n}\n\nconst emitter = new EventEmitter(ExpoJuceModule ?? NativeModulesProxy.ExpoJuce);\n\nexport function addChangeListener(listener: (event: ChangeEventPayload) => void): Subscription {\n return emitter.addListener<ChangeEventPayload>('onChange', listener);\n}\n\nexport function addBeatListener(listener: (event: BeatEventPayload) => void): Subscription {\n return emitter.addListener<BeatEventPayload>('onBeat', listener);\n}\n\nexport { ExpoJuceView, ExpoJuceViewProps, ChangeEventPayload, BeatEventPayload };\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModulesProxy, EventEmitter, Subscription } from 'expo-modules-core';\n\nimport ExpoJuceModule from './ExpoJuceModule';\nimport ExpoJuceView from './ExpoJuceView';\nimport { ChangeEventPayload, BeatEventPayload, ExpoJuceViewProps } from './ExpoJuce.types';\n\n// ── Lifecycle ────────────────────────────────────────────────────\n\nexport async function setup(): Promise<void> {\n return ExpoJuceModule.setup();\n}\n\nexport async function shutdown(): Promise<void> {\n return ExpoJuceModule.shutdown();\n}\n\n// ── Polyphonic Synth (MIDI-style) ────────────────────────────────\n// Synchronous — uses lock-free SPSC queue, no main thread required\n\nexport function noteOn(note: number, velocity: number): void {\n ExpoJuceModule.noteOn(note, velocity);\n}\n\nexport function noteOff(note: number): void {\n ExpoJuceModule.noteOff(note);\n}\n\nexport function allNotesOff(): void {\n ExpoJuceModule.allNotesOff();\n}\n\n// ── Global Synth Params ──────────────────────────────────────────\n// Synchronous — all params are std::atomic\n\nexport function setWaveform(waveform: 'sine' | 'square' | 'saw' | 'triangle'): void {\n ExpoJuceModule.setWaveform(waveform);\n}\n\nexport function setFilterCutoff(hz: number): void {\n ExpoJuceModule.setFilterCutoff(hz);\n}\n\nexport function setFilterResonance(q: number): void {\n ExpoJuceModule.setFilterResonance(q);\n}\n\nexport function setAttack(ms: number): void {\n ExpoJuceModule.setAttack(ms);\n}\n\nexport function setDecay(ms: number): void {\n ExpoJuceModule.setDecay(ms);\n}\n\nexport function setSustain(level: number): void {\n ExpoJuceModule.setSustain(level);\n}\n\nexport function setRelease(ms: number): void {\n ExpoJuceModule.setRelease(ms);\n}\n\nexport function setDetune(cents: number): void {\n ExpoJuceModule.setDetune(cents);\n}\n\nexport function setMasterLevel(level: number): void {\n ExpoJuceModule.setMasterLevel(level);\n}\n\n// ── Transport ────────────────────────────────────────────────────\n\nexport function startTransport(): void {\n ExpoJuceModule.startTransport();\n}\n\nexport function stopTransport(): void {\n ExpoJuceModule.stopTransport();\n}\n\nexport function setTempo(bpm: number): void {\n ExpoJuceModule.setTempo(bpm);\n}\n\nexport function setTransportPosition(beat: number): void {\n ExpoJuceModule.setTransportPosition(beat);\n}\n\nexport function getTransportPosition(): number {\n return ExpoJuceModule.getTransportPosition();\n}\n\nexport function getTempo(): number {\n return ExpoJuceModule.getTempo();\n}\n\nexport function isTransportPlaying(): boolean {\n return ExpoJuceModule.isTransportPlaying();\n}\n\n// ── Events ───────────────────────────────────────────────────────\n\nexport async function setValueAsync(value: string) {\n return await ExpoJuceModule.setValueAsync(value);\n}\n\nconst emitter = new EventEmitter(ExpoJuceModule ?? NativeModulesProxy.ExpoJuce);\n\nexport function addChangeListener(listener: (event: ChangeEventPayload) => void): Subscription {\n return emitter.addListener<ChangeEventPayload>('onChange', listener);\n}\n\nexport function addBeatListener(listener: (event: BeatEventPayload) => void): Subscription {\n return emitter.addListener<BeatEventPayload>('onBeat', listener);\n}\n\nexport { ExpoJuceView, ExpoJuceViewProps, ChangeEventPayload, BeatEventPayload };\n"]}
@@ -1,11 +1,21 @@
1
1
  #import "BeeperAudioEngine.h"
2
2
  #import <AVFoundation/AVFoundation.h>
3
3
  #include "JuceConfig.h"
4
+ #include <juce_core/juce_core.h>
5
+ #include <juce_events/juce_events.h>
4
6
  #include <juce_audio_devices/juce_audio_devices.h>
5
7
  #include <juce_audio_processors/juce_audio_processors.h>
6
8
  #include "PolySynthProcessor.h"
7
9
  #include "TransportEngine.h"
8
10
 
11
+ // Initialize JUCE at dylib load time — BEFORE any static C++ objects run.
12
+ // This is critical: without MessageManager, JUCE statics crash.
13
+ __attribute__((constructor))
14
+ static void initJuceEarly() {
15
+ juce::MessageManager::getInstance();
16
+ juce::initialiseJuce_GUI();
17
+ }
18
+
9
19
  // Lightweight AudioIODeviceCallback that drives an AudioProcessorGraph.
10
20
  // Replaces juce::AudioProcessorPlayer (from juce_audio_utils, which we don't use).
11
21
  class GraphPlayer : public juce::AudioIODeviceCallback {
@@ -59,14 +69,14 @@ private:
59
69
  @property (nonatomic, strong) dispatch_source_t beatTimer;
60
70
  @end
61
71
 
62
- // C++ members stored as raw pointers (ObjC class can't have C++ members inline)
72
+ // C++ members stored as raw pointers (ObjC class can't have C++ members inline).
73
+ // IMPORTANT: No JUCE smart pointers or std::unique_ptr at file scope —
74
+ // their constructors/destructors engage JUCE internals before MessageManager is ready.
63
75
  static juce::AudioDeviceManager* sDeviceManager = nullptr;
64
76
  static GraphPlayer* sPlayer = nullptr;
65
- static std::unique_ptr<juce::AudioProcessorGraph> sGraph;
77
+ static juce::AudioProcessorGraph* sGraph = nullptr;
66
78
  static PolySynthProcessor* sSynth = nullptr; // owned by graph
67
79
  static TransportEngine* sTransport = nullptr;
68
- static juce::AudioProcessorGraph::Node::Ptr sSynthNode;
69
- static juce::AudioProcessorGraph::Node::Ptr sOutputNode;
70
80
 
71
81
  @implementation BeeperAudioEngine
72
82
 
@@ -134,7 +144,7 @@ static juce::AudioProcessorGraph::Node::Ptr sOutputNode;
134
144
  }
135
145
 
136
146
  // 5. Create AudioProcessorGraph
137
- sGraph = std::make_unique<juce::AudioProcessorGraph>();
147
+ sGraph = new juce::AudioProcessorGraph();
138
148
  sGraph->setPlayConfigDetails(0, 2, sDeviceManager->getAudioDeviceSetup().sampleRate,
139
149
  sDeviceManager->getAudioDeviceSetup().bufferSize);
140
150
  sGraph->prepareToPlay(sDeviceManager->getAudioDeviceSetup().sampleRate,
@@ -144,21 +154,21 @@ static juce::AudioProcessorGraph::Node::Ptr sOutputNode;
144
154
  auto synthProcessor = std::make_unique<PolySynthProcessor>();
145
155
  sSynth = synthProcessor.get();
146
156
  sSynth->setTransport(sTransport);
147
- sSynthNode = sGraph->addNode(std::move(synthProcessor));
157
+ auto synthNode = sGraph->addNode(std::move(synthProcessor));
148
158
 
149
159
  // 7. Add output node
150
- sOutputNode = sGraph->addNode(
160
+ auto outputNode = sGraph->addNode(
151
161
  std::make_unique<juce::AudioProcessorGraph::AudioGraphIOProcessor>(
152
162
  juce::AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode));
153
163
 
154
164
  // 8. Connect synth → output (stereo: channels 0 and 1)
155
165
  sGraph->addConnection({
156
- {sSynthNode->nodeID, 0},
157
- {sOutputNode->nodeID, 0}
166
+ {synthNode->nodeID, 0},
167
+ {outputNode->nodeID, 0}
158
168
  });
159
169
  sGraph->addConnection({
160
- {sSynthNode->nodeID, 1},
161
- {sOutputNode->nodeID, 1}
170
+ {synthNode->nodeID, 1},
171
+ {outputNode->nodeID, 1}
162
172
  });
163
173
 
164
174
  // 9. Create player and attach to device
@@ -195,10 +205,9 @@ static juce::AudioProcessorGraph::Node::Ptr sOutputNode;
195
205
  sPlayer = nullptr;
196
206
  }
197
207
 
198
- sSynthNode = nullptr;
199
- sOutputNode = nullptr;
200
- sSynth = nullptr;
201
- sGraph.reset();
208
+ sSynth = nullptr; // owned by graph, don't delete
209
+ delete sGraph;
210
+ sGraph = nullptr;
202
211
 
203
212
  if (sDeviceManager) {
204
213
  sDeviceManager->closeAudioDevice();
@@ -33,102 +33,107 @@ public class ExpoJuceModule: Module {
33
33
 
34
34
  // ── Lifecycle ──────────────────────────────────────────────────
35
35
 
36
- AsyncFunction("setup") { [weak self] in
37
- ExpoJuceBridge.setup()
38
- ExpoJuceBridge.setBeatCallback { [weak self] beat, bpm in
39
- guard let self = self, self.hasListeners else { return }
40
- self.sendEvent("onBeat", [
41
- "beat": beat,
42
- "bpm": bpm,
43
- ])
36
+ AsyncFunction("setup") { [weak self] (promise: Promise) in
37
+ DispatchQueue.main.async {
38
+ ExpoJuceBridge.setup()
39
+ ExpoJuceBridge.setBeatCallback { [weak self] beat, bpm in
40
+ guard let self = self, self.hasListeners else { return }
41
+ self.sendEvent("onBeat", [
42
+ "beat": beat,
43
+ "bpm": bpm,
44
+ ])
45
+ }
46
+ promise.resolve(NSNull())
44
47
  }
45
- }.runOnQueue(.main)
48
+ }
46
49
 
47
50
  AsyncFunction("shutdown") {
48
51
  ExpoJuceBridge.shutdown()
49
52
  }.runOnQueue(.main)
50
53
 
51
54
  // ── Synth (MIDI-style) ─────────────────────────────────────────
55
+ // Thread-safe: PolySynthProcessor uses a lock-free SPSC queue
52
56
 
53
- AsyncFunction("noteOn") { (note: Int, velocity: Int) in
57
+ Function("noteOn") { (note: Int, velocity: Int) in
54
58
  ExpoJuceBridge.note(on: Int32(note), velocity: Int32(velocity))
55
- }.runOnQueue(.main)
59
+ }
56
60
 
57
- AsyncFunction("noteOff") { (note: Int) in
61
+ Function("noteOff") { (note: Int) in
58
62
  ExpoJuceBridge.noteOff(Int32(note))
59
- }.runOnQueue(.main)
63
+ }
60
64
 
61
- AsyncFunction("allNotesOff") {
65
+ Function("allNotesOff") {
62
66
  ExpoJuceBridge.allNotesOff()
63
- }.runOnQueue(.main)
67
+ }
64
68
 
65
69
  // ── Global Synth Params ────────────────────────────────────────
70
+ // Thread-safe: all params are std::atomic
66
71
 
67
- AsyncFunction("setWaveform") { (waveform: String) in
72
+ Function("setWaveform") { (waveform: String) in
68
73
  ExpoJuceBridge.setWaveform(waveform)
69
- }.runOnQueue(.main)
74
+ }
70
75
 
71
- AsyncFunction("setFilterCutoff") { (hz: Double) in
76
+ Function("setFilterCutoff") { (hz: Double) in
72
77
  ExpoJuceBridge.setFilterCutoff(hz)
73
- }.runOnQueue(.main)
78
+ }
74
79
 
75
- AsyncFunction("setFilterResonance") { (q: Double) in
80
+ Function("setFilterResonance") { (q: Double) in
76
81
  ExpoJuceBridge.setFilterResonance(q)
77
- }.runOnQueue(.main)
82
+ }
78
83
 
79
- AsyncFunction("setAttack") { (ms: Double) in
84
+ Function("setAttack") { (ms: Double) in
80
85
  ExpoJuceBridge.setAttack(ms)
81
- }.runOnQueue(.main)
86
+ }
82
87
 
83
- AsyncFunction("setDecay") { (ms: Double) in
88
+ Function("setDecay") { (ms: Double) in
84
89
  ExpoJuceBridge.setDecay(ms)
85
- }.runOnQueue(.main)
90
+ }
86
91
 
87
- AsyncFunction("setSustain") { (level: Double) in
92
+ Function("setSustain") { (level: Double) in
88
93
  ExpoJuceBridge.setSustain(level)
89
- }.runOnQueue(.main)
94
+ }
90
95
 
91
- AsyncFunction("setRelease") { (ms: Double) in
96
+ Function("setRelease") { (ms: Double) in
92
97
  ExpoJuceBridge.setRelease(ms)
93
- }.runOnQueue(.main)
98
+ }
94
99
 
95
- AsyncFunction("setDetune") { (cents: Double) in
100
+ Function("setDetune") { (cents: Double) in
96
101
  ExpoJuceBridge.setDetune(cents)
97
- }.runOnQueue(.main)
102
+ }
98
103
 
99
- AsyncFunction("setMasterLevel") { (level: Double) in
104
+ Function("setMasterLevel") { (level: Double) in
100
105
  ExpoJuceBridge.setMasterLevel(level)
101
- }.runOnQueue(.main)
106
+ }
102
107
 
103
108
  // ── Transport ──────────────────────────────────────────────────
104
109
 
105
- AsyncFunction("startTransport") {
110
+ Function("startTransport") {
106
111
  ExpoJuceBridge.startTransport()
107
- }.runOnQueue(.main)
112
+ }
108
113
 
109
- AsyncFunction("stopTransport") {
114
+ Function("stopTransport") {
110
115
  ExpoJuceBridge.stopTransport()
111
- }.runOnQueue(.main)
116
+ }
112
117
 
113
- AsyncFunction("setTempo") { (bpm: Double) in
118
+ Function("setTempo") { (bpm: Double) in
114
119
  ExpoJuceBridge.setTempo(bpm)
115
- }.runOnQueue(.main)
120
+ }
116
121
 
117
- AsyncFunction("setTransportPosition") { (beat: Double) in
122
+ Function("setTransportPosition") { (beat: Double) in
118
123
  ExpoJuceBridge.setTransportPosition(beat)
119
- }.runOnQueue(.main)
124
+ }
120
125
 
121
- AsyncFunction("getTransportPosition") { () -> Double in
126
+ Function("getTransportPosition") { () -> Double in
122
127
  return ExpoJuceBridge.getTransportPosition()
123
- }.runOnQueue(.main)
128
+ }
124
129
 
125
- AsyncFunction("getTempo") { () -> Double in
130
+ Function("getTempo") { () -> Double in
126
131
  return ExpoJuceBridge.getTempo()
127
- }.runOnQueue(.main)
132
+ }
128
133
 
129
- AsyncFunction("isTransportPlaying") { () -> Bool in
134
+ Function("isTransportPlaying") { () -> Bool in
130
135
  return ExpoJuceBridge.isTransportPlaying()
131
- }.runOnQueue(.main)
136
+ }
132
137
 
133
138
  // ── Async / View ───────────────────────────────────────────────
134
139
 
@@ -1,10 +1,11 @@
1
1
  #include "TransportEngine.h"
2
2
  #include <algorithm>
3
3
  #include <cmath>
4
+ #include <limits>
4
5
 
5
6
  TransportEngine::TransportEngine()
6
7
  : bpm(120.0), playing(false),
7
- pendingPosition(std::nan("")), positionSnapshot(0.0) {}
8
+ pendingPosition(std::numeric_limits<double>::quiet_NaN()), positionSnapshot(0.0) {}
8
9
 
9
10
  void TransportEngine::setTempo(double newBpm) {
10
11
  bpm.store(std::clamp(newBpm, 20.0, 400.0));
@@ -23,7 +24,7 @@ void TransportEngine::stop() { playing.store(false); }
23
24
  bool TransportEngine::isPlaying() { return playing.load(); }
24
25
 
25
26
  bool TransportEngine::advanceBlock(int numSamples, double sampleRate) {
26
- double pending = pendingPosition.exchange(std::nan(""));
27
+ double pending = pendingPosition.exchange(std::numeric_limits<double>::quiet_NaN());
27
28
  if (!std::isnan(pending)) {
28
29
  beatPosition = pending;
29
30
  lastBeatInt = (int)beatPosition;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-juce",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Realtime DSP w/C++ & JUCE",
5
5
  "type": "module",
6
6
  "main": "build/index.js",