RAM2118 commited on
Commit
4ad264f
·
verified ·
1 Parent(s): b1feaca

v0.4: CARemoteLayerServer architecture — crash-safe + GUI inside Ableton window

Browse files

COMPLETE REWRITE to proper solution:
- Plugin runs in Helper process (crash-safe, like v0.2)
- GUI renders in Helper via CARemoteLayerServer
- PluginBridge.vst3 displays it via CARemoteLayerClient (CALayer inside its own editor)
- Mach port transferred via NSMachBootstrapServer (named port per PID)
- Mouse/keyboard events forwarded from host → helper via Unix socket IPC
- Audio routes via shared memory (same as v0.2)

This gives BOTH crash safety AND GUI inside Ableton's window (including fullscreen).

Based on: Apple's CARemoteLayerServer/Client public API (macOS 10.7+)
Reference: krevis/RemoteLayerDemo on GitHub"

Files changed (1) hide show
  1. Source/Shared/IPCProtocol.h +36 -53
Source/Shared/IPCProtocol.h CHANGED
@@ -4,11 +4,15 @@
4
  #include <cstdint>
5
 
6
  // IPC Protocol between PluginBridge (parent) and PluginBridgeHelper (child)
7
- // Transport: Unix domain socket, newline-delimited JSON messages
8
- //
9
- // Flow:
10
- // Parent → Helper: commands (load plugin, get/set params, show GUI, quit)
11
- // Helper → Parent: responses + notifications (param changes from GUI, crash)
 
 
 
 
12
 
13
  namespace IPC {
14
 
@@ -18,7 +22,7 @@ inline std::string getSocketPath(int parentPid)
18
  return "/tmp/pluginbridge_" + std::to_string(parentPid) + ".sock";
19
  }
20
 
21
- // Shared memory name (unique per instance)
22
  inline std::string getShmName(int parentPid)
23
  {
24
  return "pluginbridge_audio_" + std::to_string(parentPid);
@@ -35,64 +39,43 @@ inline std::string getOutputSemName(int parentPid)
35
  return "pb_output_" + std::to_string(parentPid);
36
  }
37
 
38
- // --- Message Types (Parent → Helper) ---
39
-
40
- // Load a plugin by file path
41
- // {"cmd":"load","path":"/Library/Audio/Plug-Ins/VST3/Pro-Q 4.vst3"}
42
- // Response: {"ok":true,"name":"Pro-Q 4","params":737} or {"ok":false,"error":"..."}
43
 
44
- // Unload current plugin
 
45
  // {"cmd":"unload"}
46
- // Response: {"ok":true}
47
-
48
- // Search parameters by keyword
49
  // {"cmd":"search_param","keyword":"gain"}
50
- // Response: {"ok":true,"results":[{"i":12,"n":"Band 1 Gain","v":0.5,"t":"0.0 dB"},...]}
51
-
52
- // Get parameter values by index
53
- // {"cmd":"get_params","ids":[12,13,14]}
54
- // Response: {"ok":true,"values":{"12":0.5,"13":0.7,"14":0.0}}
55
-
56
- // Set parameter values
57
- // {"cmd":"set_params","values":{"12":0.75,"13":0.3}}
58
- // Response: {"ok":true}
59
-
60
- // Show/hide plugin GUI
61
  // {"cmd":"show_gui"}
62
  // {"cmd":"hide_gui"}
63
- // Response: {"ok":true}
64
-
65
- // Get plugin state (for save)
66
  // {"cmd":"get_state"}
67
- // Response: {"ok":true,"state":"<base64-encoded state>"}
68
-
69
- // Set plugin state (for restore)
70
- // {"cmd":"set_state","state":"<base64-encoded state>"}
71
- // Response: {"ok":true}
72
-
73
- // Configure audio
74
  // {"cmd":"configure","sampleRate":44100,"blockSize":512,"channels":2}
75
- // Response: {"ok":true}
76
-
77
- // Quit helper
78
  // {"cmd":"quit"}
79
- // No response (process exits)
80
-
81
- // --- Message Types (Helper → Parent, unsolicited notifications) ---
82
-
83
- // Parameter changed from GUI interaction
84
- // {"notify":"param_changed","index":12,"value":0.75}
85
-
86
- // Plugin crashed (sent if helper catches it before dying)
 
 
 
 
 
 
87
  // {"notify":"crashed","error":"..."}
88
 
89
- // Helper ready (sent after startup)
90
- // {"notify":"ready"}
91
-
92
  // --- Constants ---
93
-
94
- static constexpr int kMaxMessageSize = 65536; // 64KB max for state blobs
95
- static constexpr int kHelperTimeoutMs = 5000; // 5s to start up
96
  static constexpr int kProcessBlockTimeoutMs = 50; // 50ms max wait in processBlock
97
 
98
  } // namespace IPC
 
4
  #include <cstdint>
5
 
6
  // IPC Protocol between PluginBridge (parent) and PluginBridgeHelper (child)
7
+ //
8
+ // v0.4 Architecture:
9
+ // - Audio: POSIX shared memory (zero-copy, same as v0.2)
10
+ // - Sync: Named semaphores (same as v0.2)
11
+ // - Commands: Unix domain socket, newline-delimited JSON
12
+ // - GUI: CARemoteLayerServer in Helper → Mach port → CARemoteLayerClient in Host
13
+ //
14
+ // The Mach port for CARemoteLayerServer is transferred via NSMachBootstrapServer
15
+ // using the name "com.pluginbridge.layer.<parentPid>"
16
 
17
  namespace IPC {
18
 
 
22
  return "/tmp/pluginbridge_" + std::to_string(parentPid) + ".sock";
23
  }
24
 
25
+ // Shared memory name
26
  inline std::string getShmName(int parentPid)
27
  {
28
  return "pluginbridge_audio_" + std::to_string(parentPid);
 
39
  return "pb_output_" + std::to_string(parentPid);
40
  }
41
 
42
+ // Mach bootstrap port name for CARemoteLayerServer
43
+ inline std::string getLayerPortName(int parentPid)
44
+ {
45
+ return "com.pluginbridge.layer." + std::to_string(parentPid);
46
+ }
47
 
48
+ // --- Message Types (Parent → Helper) ---
49
+ // {"cmd":"load","path":"/path/to/plugin.vst3","sampleRate":44100,"blockSize":512}
50
  // {"cmd":"unload"}
 
 
 
51
  // {"cmd":"search_param","keyword":"gain"}
52
+ // {"cmd":"get_params","ids":[0,1,2]}
53
+ // {"cmd":"set_params","values":{"0":0.5,"1":0.75}}
 
 
 
 
 
 
 
 
 
54
  // {"cmd":"show_gui"}
55
  // {"cmd":"hide_gui"}
 
 
 
56
  // {"cmd":"get_state"}
57
+ // {"cmd":"set_state","state":"<base64>"}
 
 
 
 
 
 
58
  // {"cmd":"configure","sampleRate":44100,"blockSize":512,"channels":2}
 
 
 
59
  // {"cmd":"quit"}
60
+ //
61
+ // --- Mouse/Keyboard forwarding (Parent → Helper) ---
62
+ // {"cmd":"mouse","type":"down","x":120.5,"y":45.0,"button":0}
63
+ // {"cmd":"mouse","type":"up","x":120.5,"y":45.0,"button":0}
64
+ // {"cmd":"mouse","type":"move","x":130.0,"y":50.0}
65
+ // {"cmd":"mouse","type":"drag","x":135.0,"y":52.0,"button":0}
66
+ // {"cmd":"mouse","type":"scroll","x":120.5,"y":45.0,"dx":0,"dy":-3.0}
67
+ // {"cmd":"key","type":"down","keyCode":36,"chars":"a","modifiers":0}
68
+ // {"cmd":"key","type":"up","keyCode":36,"chars":"a","modifiers":0}
69
+ //
70
+ // --- Notifications (Helper → Parent) ---
71
+ // {"notify":"ready","layerPortName":"com.pluginbridge.layer.12345","width":800,"height":600}
72
+ // {"notify":"param_changed","index":5,"value":0.75}
73
+ // {"notify":"resized","width":900,"height":650}
74
  // {"notify":"crashed","error":"..."}
75
 
 
 
 
76
  // --- Constants ---
77
+ static constexpr int kMaxMessageSize = 65536;
78
+ static constexpr int kHelperTimeoutMs = 10000; // 10s for startup (CARemoteLayer init takes time)
 
79
  static constexpr int kProcessBlockTimeoutMs = 50; // 50ms max wait in processBlock
80
 
81
  } // namespace IPC