expo-juce 0.3.3 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ios/BeeperAudioEngine.mm +25 -16
- package/ios/TransportEngine.mm +3 -2
- package/package.json +1 -1
package/ios/BeeperAudioEngine.mm
CHANGED
|
@@ -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
|
|
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 =
|
|
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,26 +154,26 @@ 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
|
-
|
|
157
|
+
auto synthNode = sGraph->addNode(std::move(synthProcessor));
|
|
148
158
|
|
|
149
159
|
// 7. Add output node
|
|
150
|
-
|
|
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
|
-
{
|
|
157
|
-
{
|
|
166
|
+
{synthNode->nodeID, 0},
|
|
167
|
+
{outputNode->nodeID, 0}
|
|
158
168
|
});
|
|
159
169
|
sGraph->addConnection({
|
|
160
|
-
{
|
|
161
|
-
{
|
|
170
|
+
{synthNode->nodeID, 1},
|
|
171
|
+
{outputNode->nodeID, 1}
|
|
162
172
|
});
|
|
163
173
|
|
|
164
174
|
// 9. Create player and attach to device
|
|
165
175
|
sPlayer = new GraphPlayer();
|
|
166
|
-
sPlayer->setProcessor(sGraph
|
|
176
|
+
sPlayer->setProcessor(sGraph);
|
|
167
177
|
sDeviceManager->addAudioCallback(sPlayer);
|
|
168
178
|
|
|
169
179
|
self.isReady = true;
|
|
@@ -195,10 +205,9 @@ static juce::AudioProcessorGraph::Node::Ptr sOutputNode;
|
|
|
195
205
|
sPlayer = nullptr;
|
|
196
206
|
}
|
|
197
207
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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();
|
package/ios/TransportEngine.mm
CHANGED
|
@@ -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::
|
|
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::
|
|
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;
|