mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-26 02:14:22 +00:00
CoreMidi: Use RAII to avoid potential leaks of MIDI ports/endpoints
This commit is contained in:
parent
a48a564f9e
commit
69b630a2c0
1 changed files with 80 additions and 24 deletions
|
|
@ -313,36 +313,89 @@ namespace CoreMidiHelpers
|
|||
|
||||
using SenderToUse = Sender<implementationStrategy>;
|
||||
|
||||
template <typename Resource, typename Destructor>
|
||||
class ScopedMidiResource
|
||||
{
|
||||
public:
|
||||
ScopedMidiResource() = default;
|
||||
|
||||
explicit ScopedMidiResource (Resource r) : contents (r, {}) {}
|
||||
|
||||
~ScopedMidiResource() noexcept
|
||||
{
|
||||
auto ref = std::get<0> (contents);
|
||||
|
||||
if (ref != 0)
|
||||
std::get<1> (contents) (ref);
|
||||
}
|
||||
|
||||
ScopedMidiResource (const ScopedMidiResource& other) = delete;
|
||||
ScopedMidiResource& operator= (const ScopedMidiResource& other) = delete;
|
||||
|
||||
ScopedMidiResource (ScopedMidiResource&& other) noexcept { swap (other); }
|
||||
|
||||
ScopedMidiResource& operator= (ScopedMidiResource&& other) noexcept
|
||||
{
|
||||
swap (other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap (ScopedMidiResource& other) noexcept { std::swap (other.contents, contents); }
|
||||
|
||||
Resource operator*() const noexcept { return std::get<0> (contents); }
|
||||
|
||||
Resource release() noexcept
|
||||
{
|
||||
auto old = std::get<0> (contents);
|
||||
std::get<0> (contents) = 0;
|
||||
return old;
|
||||
}
|
||||
|
||||
private:
|
||||
std::tuple<Resource, Destructor> contents { {}, {} };
|
||||
};
|
||||
|
||||
struct PortRefDestructor
|
||||
{
|
||||
void operator() (MIDIPortRef p) const noexcept { MIDIPortDispose (p); }
|
||||
};
|
||||
|
||||
using ScopedPortRef = ScopedMidiResource<MIDIPortRef, PortRefDestructor>;
|
||||
|
||||
struct EndpointRefDestructor
|
||||
{
|
||||
void operator() (MIDIEndpointRef p) const noexcept { MIDIEndpointDispose (p); }
|
||||
};
|
||||
|
||||
using ScopedEndpointRef = ScopedMidiResource<MIDIEndpointRef, EndpointRefDestructor>;
|
||||
|
||||
//==============================================================================
|
||||
class MidiPortAndEndpoint
|
||||
{
|
||||
public:
|
||||
MidiPortAndEndpoint (MIDIPortRef p, MIDIEndpointRef ep) noexcept
|
||||
: port (p), endpoint (ep), sender (ep)
|
||||
MidiPortAndEndpoint (ScopedPortRef p, ScopedEndpointRef ep) noexcept
|
||||
: port (std::move (p)), endpoint (std::move (ep)), sender (*endpoint)
|
||||
{}
|
||||
|
||||
~MidiPortAndEndpoint() noexcept
|
||||
{
|
||||
if (port != 0)
|
||||
MIDIPortDispose (port);
|
||||
|
||||
// if port == nullptr, it means we created the endpoint, so it's safe to delete it
|
||||
if (port == 0 && endpoint != 0)
|
||||
MIDIEndpointDispose (endpoint);
|
||||
// if port != 0, it means we didn't create the endpoint, so it's not safe to delete it
|
||||
if (*port != 0)
|
||||
endpoint.release();
|
||||
}
|
||||
|
||||
void send (const MidiMessage& m)
|
||||
{
|
||||
sender.send (port, endpoint, m);
|
||||
sender.send (*port, *endpoint, m);
|
||||
}
|
||||
|
||||
void send (ump::Iterator b, ump::Iterator e)
|
||||
{
|
||||
sender.send (port, endpoint, b, e);
|
||||
sender.send (*port, *endpoint, b, e);
|
||||
}
|
||||
|
||||
bool canStop() const noexcept { return port != 0; }
|
||||
void stop() const { CHECK_ERROR (MIDIPortDisconnectSource (port, endpoint)); }
|
||||
bool canStop() const noexcept { return *port != 0; }
|
||||
void stop() const { CHECK_ERROR (MIDIPortDisconnectSource (*port, *endpoint)); }
|
||||
|
||||
ump::MidiProtocol getProtocol() const noexcept
|
||||
{
|
||||
|
|
@ -350,8 +403,8 @@ namespace CoreMidiHelpers
|
|||
}
|
||||
|
||||
private:
|
||||
MIDIPortRef port;
|
||||
MIDIEndpointRef endpoint;
|
||||
ScopedPortRef port;
|
||||
ScopedEndpointRef endpoint;
|
||||
|
||||
SenderToUse sender;
|
||||
};
|
||||
|
|
@ -1017,13 +1070,12 @@ public:
|
|||
if (! CHECK_ERROR (CreatorFunctionsToUse::createInputPort (protocol, client, cfName.cfString, input->internal.get(), &port)))
|
||||
continue;
|
||||
|
||||
if (! CHECK_ERROR (MIDIPortConnectSource (port, endpoint, nullptr)))
|
||||
{
|
||||
CHECK_ERROR (MIDIPortDispose (port));
|
||||
continue;
|
||||
}
|
||||
ScopedPortRef scopedPort { port };
|
||||
|
||||
input->internal->portAndEndpoint = std::make_unique<MidiPortAndEndpoint> (port, endpoint);
|
||||
if (! CHECK_ERROR (MIDIPortConnectSource (*scopedPort, endpoint, nullptr)))
|
||||
continue;
|
||||
|
||||
input->internal->portAndEndpoint = std::make_unique<MidiPortAndEndpoint> (std::move (scopedPort), ScopedEndpointRef { endpoint });
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
|
@ -1049,6 +1101,7 @@ public:
|
|||
ScopedCFString name (deviceName);
|
||||
|
||||
auto err = CreatorFunctionsToUse::createDestination (protocol, client, name.cfString, input->internal.get(), &endpoint);
|
||||
ScopedEndpointRef scopedEndpoint { endpoint };
|
||||
|
||||
#if JUCE_IOS
|
||||
if (err == kMIDINotPermitted)
|
||||
|
|
@ -1066,7 +1119,7 @@ public:
|
|||
if (! CHECK_ERROR (MIDIObjectSetIntegerProperty (endpoint, kMIDIPropertyUniqueID, (SInt32) deviceIdentifier)))
|
||||
return {};
|
||||
|
||||
input->internal->portAndEndpoint = std::make_unique<MidiPortAndEndpoint> ((MIDIPortRef) 0, endpoint);
|
||||
input->internal->portAndEndpoint = std::make_unique<MidiPortAndEndpoint> (ScopedPortRef{}, std::move (scopedEndpoint));
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
|
@ -1185,8 +1238,10 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (const String& deviceIdentifi
|
|||
if (! CHECK_ERROR (MIDIOutputPortCreate (client, cfName.cfString, &port)))
|
||||
continue;
|
||||
|
||||
ScopedPortRef scopedPort { port };
|
||||
|
||||
auto midiOutput = rawToUniquePtr (new MidiOutput (endpointInfo.name, endpointInfo.identifier));
|
||||
midiOutput->internal = std::make_unique<Pimpl> (port, endpoint);
|
||||
midiOutput->internal = std::make_unique<Pimpl> (std::move (scopedPort), ScopedEndpointRef { endpoint });
|
||||
|
||||
return midiOutput;
|
||||
}
|
||||
|
|
@ -1206,6 +1261,7 @@ std::unique_ptr<MidiOutput> MidiOutput::createNewDevice (const String& deviceNam
|
|||
ScopedCFString name (deviceName);
|
||||
|
||||
auto err = CreatorFunctionsToUse::createSource (ump::PacketProtocol::MIDI_1_0, client, name.cfString, &endpoint);
|
||||
ScopedEndpointRef scopedEndpoint { endpoint };
|
||||
|
||||
#if JUCE_IOS
|
||||
if (err == kMIDINotPermitted)
|
||||
|
|
@ -1222,11 +1278,11 @@ std::unique_ptr<MidiOutput> MidiOutput::createNewDevice (const String& deviceNam
|
|||
|
||||
auto deviceIdentifier = createUniqueIDForMidiPort (deviceName, false);
|
||||
|
||||
if (! CHECK_ERROR (MIDIObjectSetIntegerProperty (endpoint, kMIDIPropertyUniqueID, (SInt32) deviceIdentifier)))
|
||||
if (! CHECK_ERROR (MIDIObjectSetIntegerProperty (*scopedEndpoint, kMIDIPropertyUniqueID, (SInt32) deviceIdentifier)))
|
||||
return {};
|
||||
|
||||
auto midiOutput = rawToUniquePtr (new MidiOutput (deviceName, String (deviceIdentifier)));
|
||||
midiOutput->internal = std::make_unique<Pimpl> ((UInt32) 0, endpoint);
|
||||
midiOutput->internal = std::make_unique<Pimpl> (ScopedPortRef{}, std::move (scopedEndpoint));
|
||||
|
||||
return midiOutput;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue