Skip to content

Commit 99c357a

Browse files
committed
Fix hosting vst and clap on mac
1 parent 4c2bac7 commit 99c357a

23 files changed

Lines changed: 1891 additions & 170 deletions

cmake/yup_dependencies.cmake

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,19 @@ function (_yup_fetch_vst3sdk)
128128
get_target_property (vst3sdk_source_dir sdk SOURCE_DIR)
129129
endif()
130130

131-
if (vst3sdk_source_dir AND EXISTS "${vst3sdk_source_dir}/public.sdk/source/common/memorystream.cpp")
132-
target_sources (yup_audio_plugin_host_vst3sdk INTERFACE
133-
"${vst3sdk_source_dir}/public.sdk/source/common/memorystream.cpp")
131+
set (vst3sdk_memorystream_source "${vst3sdk_source_dir}/public.sdk/source/common/memorystream.cpp")
132+
if (vst3sdk_source_dir AND EXISTS "${vst3sdk_memorystream_source}")
133+
target_sources (yup_audio_plugin_host_vst3sdk INTERFACE "${vst3sdk_memorystream_source}")
134134
endif()
135135

136-
if (vst3sdk_source_dir AND EXISTS "${vst3sdk_source_dir}/public.sdk/source/vst/hosting/parameterchanges.cpp")
137-
target_sources (yup_audio_plugin_host_vst3sdk INTERFACE
138-
"${vst3sdk_source_dir}/public.sdk/source/vst/hosting/parameterchanges.cpp")
136+
set (vst3sdk_parameterchanges_source "${vst3sdk_source_dir}/public.sdk/source/vst/hosting/parameterchanges.cpp")
137+
if (vst3sdk_source_dir AND EXISTS "${vst3sdk_parameterchanges_source}")
138+
target_sources (yup_audio_plugin_host_vst3sdk INTERFACE "${vst3sdk_parameterchanges_source}")
139+
endif()
140+
141+
set (vst3sdk_eventlist_source "${vst3sdk_source_dir}/public.sdk/source/vst/hosting/eventlist.cpp")
142+
if (vst3sdk_source_dir AND EXISTS "${vst3sdk_eventlist_source}")
143+
target_sources (yup_audio_plugin_host_vst3sdk INTERFACE "${vst3sdk_eventlist_source}")
139144
endif()
140145
endif()
141146
endfunction()

modules/yup_audio_plugin_client/clap/yup_audio_plugin_client_CLAP.cpp

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -603,9 +603,16 @@ bool AudioPluginProcessorCLAP::initialise()
603603
auto wrapper = getWrapper (plugin);
604604
auto* audioProcessor = wrapper->audioProcessor.get();
605605

606-
return static_cast<uint32_t> (isInput
607-
? audioProcessor->getBusLayout().getInputBuses().size()
608-
: audioProcessor->getBusLayout().getOutputBuses().size());
606+
Span<const AudioBus> busses = isInput
607+
? audioProcessor->getBusLayout().getInputBuses()
608+
: audioProcessor->getBusLayout().getOutputBuses();
609+
610+
uint32_t count = 0;
611+
for (const auto& bus : busses)
612+
if (bus.getType() == AudioBus::Type::Audio)
613+
++count;
614+
615+
return count;
609616
};
610617

611618
extensionAudioPorts.get = [] (const clap_plugin_t* plugin, uint32_t index, bool isInput, clap_audio_port_info_t* info) -> bool
@@ -617,17 +624,32 @@ bool AudioPluginProcessorCLAP::initialise()
617624
? audioProcessor->getBusLayout().getInputBuses()
618625
: audioProcessor->getBusLayout().getOutputBuses();
619626

620-
if (index >= static_cast<uint32_t> (busses.size()))
621-
return false;
627+
const AudioBus* audioBus = nullptr;
628+
uint32_t audioBusIndex = 0;
622629

623-
const AudioBus& bus = busses[index];
630+
for (const auto& bus : busses)
631+
{
632+
if (bus.getType() != AudioBus::Type::Audio)
633+
continue;
634+
635+
if (audioBusIndex == index)
636+
{
637+
audioBus = &bus;
638+
break;
639+
}
640+
641+
++audioBusIndex;
642+
}
643+
644+
if (audioBus == nullptr)
645+
return false;
624646

625647
info->id = index;
626-
info->channel_count = bus.getNumChannels();
648+
info->channel_count = audioBus->getNumChannels();
627649
info->flags = (index == 0) ? CLAP_AUDIO_PORT_IS_MAIN : 0;
628-
info->port_type = bus.isStereo() ? CLAP_PORT_STEREO : CLAP_PORT_MONO;
650+
info->port_type = audioBus->isStereo() ? CLAP_PORT_STEREO : CLAP_PORT_MONO;
629651
info->in_place_pair = CLAP_INVALID_ID;
630-
bus.getName().copyToUTF8 (info->name, sizeof (info->name));
652+
audioBus->getName().copyToUTF8 (info->name, sizeof (info->name));
631653

632654
return true;
633655
};

modules/yup_audio_plugin_host/host/yup_AudioPluginDescription.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ struct AudioPluginDescription
6868
/** Reported output channel count (sum across main audio output buses). */
6969
int numOutputChannels = 0;
7070

71+
/** Reported MIDI input port count. */
72+
int numMidiInputPorts = 0;
73+
74+
/** Reported MIDI output port count. */
75+
int numMidiOutputPorts = 0;
76+
7177
bool operator== (const AudioPluginDescription& other) const noexcept
7278
{
7379
return formatType == other.formatType
@@ -80,7 +86,9 @@ struct AudioPluginDescription
8086
&& isInstrument == other.isInstrument
8187
&& isEffect == other.isEffect
8288
&& numInputChannels == other.numInputChannels
83-
&& numOutputChannels == other.numOutputChannels;
89+
&& numOutputChannels == other.numOutputChannels
90+
&& numMidiInputPorts == other.numMidiInputPorts
91+
&& numMidiOutputPorts == other.numMidiOutputPorts;
8492
}
8593

8694
bool operator!= (const AudioPluginDescription& other) const noexcept

modules/yup_audio_plugin_host/host/yup_AudioPluginFormat.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,21 @@ class AudioPluginFormat
4646
/** Returns a human-readable name for the format (e.g. "VST3"). */
4747
virtual String getFormatName() const = 0;
4848

49+
/**
50+
Returns the file extensions this format recognises, including the leading dot
51+
(e.g. { ".vst3" } or { ".clap" }).
52+
53+
The scanner uses these extensions to filter filesystem entries before calling
54+
scanFile(). Return an empty array for registry-based formats (e.g. AUv2) that
55+
do not perform file-system scanning — the scanner will call scanFile() with an
56+
invalid File instead.
57+
58+
Platform-specific extensions (e.g. ".dll" on Windows, ".so" on Linux) should
59+
be returned conditionally so only the relevant extensions are active on each
60+
platform.
61+
*/
62+
virtual StringArray getFileExtensions() const = 0;
63+
4964
//==============================================================================
5065

5166
/**

modules/yup_audio_plugin_host/host/yup_AudioPluginHostContext.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ struct AudioPluginHostContext
4040
/** True to prefer double-precision processing where the plugin supports it. */
4141
bool preferDoublePrecision = false;
4242

43+
/** True when the host is preparing the plugin for offline/non-realtime rendering. */
44+
bool isNonRealtime = false;
45+
4346
/** Optional playhead — the host sets this before processing. */
4447
AudioPlayHead* playHead = nullptr;
4548

modules/yup_audio_plugin_host/host/yup_AudioPluginInstance.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,34 @@
2222
namespace yup
2323
{
2424

25+
namespace
26+
{
27+
28+
template <typename FloatType>
29+
void processPluginBypassedBlock (const AudioPluginDescription& description,
30+
AudioBuffer<FloatType>& audioBuffer) noexcept
31+
{
32+
const int numSamples = audioBuffer.getNumSamples();
33+
const int numBufferChannels = audioBuffer.getNumChannels();
34+
const int numInputs = jmin (description.numInputChannels, numBufferChannels);
35+
const int numOutputs = jmin (description.numOutputChannels, numBufferChannels);
36+
const int numChannelsToCopy = jmin (numInputs, numOutputs);
37+
38+
for (int channel = 0; channel < numChannelsToCopy; ++channel)
39+
{
40+
const auto* source = audioBuffer.getReadPointer (channel);
41+
auto* destination = audioBuffer.getWritePointer (channel);
42+
43+
if (source != destination)
44+
FloatVectorOperations::copy (destination, source, numSamples);
45+
}
46+
47+
for (int channel = numChannelsToCopy; channel < numOutputs; ++channel)
48+
audioBuffer.clear (channel, 0, numSamples);
49+
}
50+
51+
} // namespace
52+
2553
AudioPluginInstance::AudioPluginInstance (const AudioPluginDescription& description,
2654
AudioBusLayout busLayout)
2755
: AudioProcessor (description.name, std::move (busLayout))
@@ -41,4 +69,44 @@ AudioPluginFormatType AudioPluginInstance::getFormatType() const noexcept
4169
return pluginDescription.formatType;
4270
}
4371

72+
void AudioPluginInstance::setNonRealtime (bool shouldBeNonRealtime) noexcept
73+
{
74+
if (nonRealtime == shouldBeNonRealtime)
75+
return;
76+
77+
nonRealtime = shouldBeNonRealtime;
78+
nonRealtimeStateChanged();
79+
}
80+
81+
bool AudioPluginInstance::isNonRealtime() const noexcept
82+
{
83+
return nonRealtime;
84+
}
85+
86+
void AudioPluginInstance::setBypassed (bool shouldBeBypassed) noexcept
87+
{
88+
if (bypassed == shouldBeBypassed)
89+
return;
90+
91+
bypassed = shouldBeBypassed;
92+
bypassStateChanged();
93+
}
94+
95+
bool AudioPluginInstance::isBypassed() const noexcept
96+
{
97+
return bypassed;
98+
}
99+
100+
void AudioPluginInstance::processBlockBypassed (AudioBuffer<float>& audioBuffer, MidiBuffer& midiBuffer)
101+
{
102+
ignoreUnused (midiBuffer);
103+
processPluginBypassedBlock (pluginDescription, audioBuffer);
104+
}
105+
106+
void AudioPluginInstance::processBlockBypassed (AudioBuffer<double>& audioBuffer, MidiBuffer& midiBuffer)
107+
{
108+
ignoreUnused (midiBuffer);
109+
processPluginBypassedBlock (pluginDescription, audioBuffer);
110+
}
111+
44112
} // namespace yup

modules/yup_audio_plugin_host/host/yup_AudioPluginInstance.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,43 @@ class AudioPluginInstance : public AudioProcessor
5252
/** Returns the format type of this instance. */
5353
AudioPluginFormatType getFormatType() const noexcept;
5454

55+
//==============================================================================
56+
57+
/** Sets whether this instance should render in offline/non-realtime mode. */
58+
void setNonRealtime (bool shouldBeNonRealtime) noexcept;
59+
60+
/** Returns true when this instance is rendering in offline/non-realtime mode. */
61+
bool isNonRealtime() const noexcept;
62+
63+
/** Sets whether processBlock() should render the hosted plugin bypassed. */
64+
void setBypassed (bool shouldBeBypassed) noexcept;
65+
66+
/** Returns true when this instance is currently bypassed. */
67+
bool isBypassed() const noexcept;
68+
69+
/** Processes a bypassed single-precision block by copying matching inputs to
70+
outputs and clearing outputs without matching inputs.
71+
*/
72+
void processBlockBypassed (AudioBuffer<float>& audioBuffer, MidiBuffer& midiBuffer) override;
73+
74+
/** Processes a bypassed double-precision block by copying matching inputs to
75+
outputs and clearing outputs without matching inputs.
76+
*/
77+
void processBlockBypassed (AudioBuffer<double>& audioBuffer, MidiBuffer& midiBuffer) override;
78+
5579
protected:
5680
AudioPluginDescription pluginDescription;
5781

82+
/** Called after setNonRealtime() changes state. */
83+
virtual void nonRealtimeStateChanged() {}
84+
85+
/** Called after setBypassed() changes state. */
86+
virtual void bypassStateChanged() {}
87+
5888
private:
89+
bool nonRealtime = false;
90+
bool bypassed = false;
91+
5992
YUP_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginInstance)
6093
};
6194

modules/yup_audio_plugin_host/host/yup_AudioPluginScanner.cpp

Lines changed: 27 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,6 @@ namespace yup
2525
namespace
2626
{
2727

28-
bool canScanFileWithFormat (AudioPluginFormatType type, const File& file)
29-
{
30-
switch (type)
31-
{
32-
case AudioPluginFormatType::vst3:
33-
return file.hasFileExtension (".vst3");
34-
35-
case AudioPluginFormatType::clap:
36-
return file.hasFileExtension (".clap");
37-
38-
case AudioPluginFormatType::audioUnit:
39-
return false;
40-
41-
default:
42-
break;
43-
}
44-
45-
return false;
46-
}
47-
4828
struct FileScanTask
4929
{
5030
File file;
@@ -128,35 +108,45 @@ AudioPluginScanner::ScanResult AudioPluginScanner::scan (const FileSearchPath& s
128108
{
129109
ScanResult result;
130110

131-
// AUv2 does not use file system scanning; delegate directly if registered.
132-
if (auto* auFormat = getFormatForType (AudioPluginFormatType::audioUnit))
111+
for (const auto& format : formats)
133112
{
134-
// AUv2 scanFile() with an invalid File triggers registry enumeration
135-
auto auResult = auFormat->scanFile (File {});
136-
if (auResult.wasOk())
113+
if (format->getFileExtensions().isEmpty())
137114
{
138-
auto descriptions = auResult.getValue();
139-
result.discovered.insert (result.discovered.end(),
140-
descriptions.begin(),
141-
descriptions.end());
115+
auto scanResult = format->scanFile (File {});
116+
if (scanResult.wasOk())
117+
{
118+
auto descriptions = scanResult.getValue();
119+
result.discovered.insert (result.discovered.end(),
120+
descriptions.begin(),
121+
descriptions.end());
122+
}
142123
}
143124
}
144125

145126
std::vector<FileScanTask> tasks;
146127
const int numPaths = searchPath.getNumPaths();
147128

148-
for (int p = 0; p < numPaths; ++p)
129+
for (const auto& format : formats)
149130
{
150-
Array<File> files;
151-
searchPath[p].findChildFiles (files, File::findFilesAndDirectories, true, "*", File::FollowSymlinks::noCycles);
131+
String wildcardPattern;
132+
for (const auto& ext : format->getFileExtensions())
133+
{
134+
if (wildcardPattern.isNotEmpty())
135+
wildcardPattern += ";";
152136

153-
for (const auto& file : files)
137+
wildcardPattern += "*" + ext;
138+
}
139+
140+
if (wildcardPattern.isEmpty())
141+
continue;
142+
143+
for (int p = 0; p < numPaths; ++p)
154144
{
155-
for (const auto& format : formats)
156-
{
157-
if (! canScanFileWithFormat (format->getFormatType(), file))
158-
continue;
145+
Array<File> files;
146+
searchPath[p].findChildFiles (files, File::findFilesAndDirectories, true, wildcardPattern, File::FollowSymlinks::noCycles);
159147

148+
for (const auto& file : files)
149+
{
160150
FileScanTask task;
161151
task.file = file;
162152
task.format = format.get();

modules/yup_audio_plugin_host/host/yup_AudioPluginScanner.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ class AudioPluginScanner
8585
matches the format. If a format returns a success, its results are
8686
appended; if it fails, the path is added to ScanResult::failedPaths.
8787
88-
AUv2 scanning enumerates the AudioComponent registry and does not walk
89-
the search path — pass an empty FileSearchPath when only AUv2 is registered.
88+
Formats that return an empty array from getFileExtensions() (e.g. AUv2) use
89+
registry-based discovery and are scanned independently of the search path.
9090
*/
9191
ScanResult scan (const FileSearchPath& searchPath);
9292

modules/yup_audio_plugin_host/native/yup_AudioPluginInstance_AUv2.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class AUv2Format : public AudioPluginFormat
3838

3939
AudioPluginFormatType getFormatType() const override;
4040
String getFormatName() const override;
41+
StringArray getFileExtensions() const override;
4142

4243
/** Returns an empty FileSearchPath — AUv2 uses AudioComponent registry. */
4344
FileSearchPath getDefaultSearchPaths() const override;

0 commit comments

Comments
 (0)