mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Updated old code where the 'volatile' keyword was still being used
This commit is contained in:
parent
5faef29f3e
commit
e2dcc32f88
17 changed files with 580 additions and 649 deletions
|
|
@ -120,7 +120,7 @@ public:
|
||||||
|
|
||||||
bool isRecording() const
|
bool isRecording() const
|
||||||
{
|
{
|
||||||
return activeWriter != nullptr;
|
return activeWriter.load() != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
@ -140,9 +140,9 @@ public:
|
||||||
{
|
{
|
||||||
const ScopedLock sl (writerLock);
|
const ScopedLock sl (writerLock);
|
||||||
|
|
||||||
if (activeWriter != nullptr && numInputChannels >= thumbnail.getNumChannels())
|
if (activeWriter.load() != nullptr && numInputChannels >= thumbnail.getNumChannels())
|
||||||
{
|
{
|
||||||
activeWriter->write (inputChannelData, numSamples);
|
activeWriter.load()->write (inputChannelData, numSamples);
|
||||||
|
|
||||||
// Create an AudioBuffer to wrap our incoming data, note that this does no allocations or copies, it simply references our input data
|
// Create an AudioBuffer to wrap our incoming data, note that this does no allocations or copies, it simply references our input data
|
||||||
AudioBuffer<float> buffer (const_cast<float**> (inputChannelData), thumbnail.getNumChannels(), numSamples);
|
AudioBuffer<float> buffer (const_cast<float**> (inputChannelData), thumbnail.getNumChannels(), numSamples);
|
||||||
|
|
@ -158,13 +158,13 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AudioThumbnail& thumbnail;
|
AudioThumbnail& thumbnail;
|
||||||
TimeSliceThread backgroundThread { "Audio Recorder Thread" }; // the thread that will write our audio data to disk
|
TimeSliceThread backgroundThread { "Audio Recorder Thread" }; // the thread that will write our audio data to disk
|
||||||
std::unique_ptr<AudioFormatWriter::ThreadedWriter> threadedWriter; // the FIFO used to buffer the incoming data
|
std::unique_ptr<AudioFormatWriter::ThreadedWriter> threadedWriter; // the FIFO used to buffer the incoming data
|
||||||
double sampleRate = 0.0;
|
double sampleRate = 0.0;
|
||||||
int64 nextSampleNum = 0;
|
int64 nextSampleNum = 0;
|
||||||
|
|
||||||
CriticalSection writerLock;
|
CriticalSection writerLock;
|
||||||
AudioFormatWriter::ThreadedWriter* volatile activeWriter = nullptr;
|
std::atomic<AudioFormatWriter::ThreadedWriter*> activeWriter { nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
|
||||||
|
|
@ -25,20 +25,14 @@ namespace juce
|
||||||
|
|
||||||
BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* s,
|
BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* s,
|
||||||
TimeSliceThread& thread,
|
TimeSliceThread& thread,
|
||||||
const bool deleteSourceWhenDeleted,
|
bool deleteSourceWhenDeleted,
|
||||||
const int bufferSizeSamples,
|
int bufferSizeSamples,
|
||||||
const int numChannels,
|
int numChannels,
|
||||||
bool prefillBufferOnPrepareToPlay)
|
bool prefillBufferOnPrepareToPlay)
|
||||||
: source (s, deleteSourceWhenDeleted),
|
: source (s, deleteSourceWhenDeleted),
|
||||||
backgroundThread (thread),
|
backgroundThread (thread),
|
||||||
numberOfSamplesToBuffer (jmax (1024, bufferSizeSamples)),
|
numberOfSamplesToBuffer (jmax (1024, bufferSizeSamples)),
|
||||||
numberOfChannels (numChannels),
|
numberOfChannels (numChannels),
|
||||||
bufferValidStart (0),
|
|
||||||
bufferValidEnd (0),
|
|
||||||
nextPlayPos (0),
|
|
||||||
sampleRate (0),
|
|
||||||
wasSourceLooping (false),
|
|
||||||
isPrepared (false),
|
|
||||||
prefillBuffer (prefillBufferOnPrepareToPlay)
|
prefillBuffer (prefillBufferOnPrepareToPlay)
|
||||||
{
|
{
|
||||||
jassert (source != nullptr);
|
jassert (source != nullptr);
|
||||||
|
|
@ -55,7 +49,7 @@ BufferingAudioSource::~BufferingAudioSource()
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double newSampleRate)
|
void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double newSampleRate)
|
||||||
{
|
{
|
||||||
const int bufferSizeNeeded = jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer);
|
auto bufferSizeNeeded = jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer);
|
||||||
|
|
||||||
if (newSampleRate != sampleRate
|
if (newSampleRate != sampleRate
|
||||||
|| bufferSizeNeeded != buffer.getNumSamples()
|
|| bufferSizeNeeded != buffer.getNumSamples()
|
||||||
|
|
@ -104,8 +98,12 @@ void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info
|
||||||
{
|
{
|
||||||
const ScopedLock sl (bufferStartPosLock);
|
const ScopedLock sl (bufferStartPosLock);
|
||||||
|
|
||||||
const int validStart = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos);
|
auto start = bufferValidStart.load();
|
||||||
const int validEnd = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos);
|
auto end = bufferValidEnd.load();
|
||||||
|
auto pos = nextPlayPos.load();
|
||||||
|
|
||||||
|
auto validStart = (int) (jlimit (start, end, pos) - pos);
|
||||||
|
auto validEnd = (int) (jlimit (start, end, pos + info.numSamples) - pos);
|
||||||
|
|
||||||
if (validStart == validEnd)
|
if (validStart == validEnd)
|
||||||
{
|
{
|
||||||
|
|
@ -126,8 +124,8 @@ void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info
|
||||||
for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;)
|
for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;)
|
||||||
{
|
{
|
||||||
jassert (buffer.getNumSamples() > 0);
|
jassert (buffer.getNumSamples() > 0);
|
||||||
const int startBufferIndex = (int) ((validStart + nextPlayPos) % buffer.getNumSamples());
|
auto startBufferIndex = (int) ((validStart + nextPlayPos) % buffer.getNumSamples());
|
||||||
const int endBufferIndex = (int) ((validEnd + nextPlayPos) % buffer.getNumSamples());
|
auto endBufferIndex = (int) ((validEnd + nextPlayPos) % buffer.getNumSamples());
|
||||||
|
|
||||||
if (startBufferIndex < endBufferIndex)
|
if (startBufferIndex < endBufferIndex)
|
||||||
{
|
{
|
||||||
|
|
@ -138,7 +136,7 @@ void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const int initialSize = buffer.getNumSamples() - startBufferIndex;
|
auto initialSize = buffer.getNumSamples() - startBufferIndex;
|
||||||
|
|
||||||
info.buffer->copyFrom (chan, info.startSample + validStart,
|
info.buffer->copyFrom (chan, info.startSample + validStart,
|
||||||
buffer,
|
buffer,
|
||||||
|
|
@ -157,7 +155,7 @@ void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BufferingAudioSource::waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, const uint32 timeout)
|
bool BufferingAudioSource::waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, uint32 timeout)
|
||||||
{
|
{
|
||||||
if (!source || source->getTotalLength() <= 0)
|
if (!source || source->getTotalLength() <= 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -168,26 +166,28 @@ bool BufferingAudioSource::waitForNextAudioBlockReady (const AudioSourceChannelI
|
||||||
if (! isLooping() && nextPlayPos > getTotalLength())
|
if (! isLooping() && nextPlayPos > getTotalLength())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
uint32 now = Time::getMillisecondCounter();
|
auto now = Time::getMillisecondCounter();
|
||||||
const uint32 startTime = now;
|
auto startTime = now;
|
||||||
|
|
||||||
uint32 elapsed = (now >= startTime ? now - startTime
|
auto elapsed = (now >= startTime ? now - startTime
|
||||||
: (std::numeric_limits<uint32>::max() - startTime) + now);
|
: (std::numeric_limits<uint32>::max() - startTime) + now);
|
||||||
|
|
||||||
while (elapsed <= timeout)
|
while (elapsed <= timeout)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
const ScopedLock sl (bufferStartPosLock);
|
const ScopedLock sl (bufferStartPosLock);
|
||||||
|
|
||||||
const int validStart = static_cast<int> (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos);
|
auto start = bufferValidStart.load();
|
||||||
const int validEnd = static_cast<int> (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos);
|
auto end = bufferValidEnd.load();
|
||||||
|
auto pos = nextPlayPos.load();
|
||||||
|
|
||||||
|
auto validStart = static_cast<int> (jlimit (start, end, pos) - pos);
|
||||||
|
auto validEnd = static_cast<int> (jlimit (start, end, pos + info.numSamples) - pos);
|
||||||
|
|
||||||
if (validStart <= 0 && validStart < validEnd && validEnd >= info.numSamples)
|
if (validStart <= 0 && validStart < validEnd && validEnd >= info.numSamples)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (elapsed < timeout && (! bufferReadyEvent.wait (static_cast<int> (timeout - elapsed))))
|
if (elapsed < timeout && (! bufferReadyEvent.wait (static_cast<int> (timeout - elapsed))))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -202,9 +202,11 @@ bool BufferingAudioSource::waitForNextAudioBlockReady (const AudioSourceChannelI
|
||||||
int64 BufferingAudioSource::getNextReadPosition() const
|
int64 BufferingAudioSource::getNextReadPosition() const
|
||||||
{
|
{
|
||||||
jassert (source->getTotalLength() > 0);
|
jassert (source->getTotalLength() > 0);
|
||||||
|
auto pos = nextPlayPos.load();
|
||||||
|
|
||||||
return (source->isLooping() && nextPlayPos > 0)
|
return (source->isLooping() && nextPlayPos > 0)
|
||||||
? nextPlayPos % source->getTotalLength()
|
? pos % source->getTotalLength()
|
||||||
: nextPlayPos;
|
: pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferingAudioSource::setNextReadPosition (int64 newPosition)
|
void BufferingAudioSource::setNextReadPosition (int64 newPosition)
|
||||||
|
|
@ -229,7 +231,7 @@ bool BufferingAudioSource::readNextBufferChunk()
|
||||||
bufferValidEnd = 0;
|
bufferValidEnd = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
newBVS = jmax ((int64) 0, nextPlayPos);
|
newBVS = jmax ((int64) 0, nextPlayPos.load());
|
||||||
newBVE = newBVS + buffer.getNumSamples() - 4;
|
newBVE = newBVS + buffer.getNumSamples() - 4;
|
||||||
sectionToReadStart = 0;
|
sectionToReadStart = 0;
|
||||||
sectionToReadEnd = 0;
|
sectionToReadEnd = 0;
|
||||||
|
|
@ -255,7 +257,7 @@ bool BufferingAudioSource::readNextBufferChunk()
|
||||||
sectionToReadEnd = newBVE;
|
sectionToReadEnd = newBVE;
|
||||||
|
|
||||||
bufferValidStart = newBVS;
|
bufferValidStart = newBVS;
|
||||||
bufferValidEnd = jmin (bufferValidEnd, newBVE);
|
bufferValidEnd = jmin (bufferValidEnd.load(), newBVE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,8 +265,8 @@ bool BufferingAudioSource::readNextBufferChunk()
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
jassert (buffer.getNumSamples() > 0);
|
jassert (buffer.getNumSamples() > 0);
|
||||||
const int bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples());
|
auto bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples());
|
||||||
const int bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples());
|
auto bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples());
|
||||||
|
|
||||||
if (bufferIndexStart < bufferIndexEnd)
|
if (bufferIndexStart < bufferIndexEnd)
|
||||||
{
|
{
|
||||||
|
|
@ -274,7 +276,7 @@ bool BufferingAudioSource::readNextBufferChunk()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const int initialSize = buffer.getNumSamples() - bufferIndexStart;
|
auto initialSize = buffer.getNumSamples() - bufferIndexStart;
|
||||||
|
|
||||||
readBufferSection (sectionToReadStart,
|
readBufferSection (sectionToReadStart,
|
||||||
initialSize,
|
initialSize,
|
||||||
|
|
@ -293,11 +295,10 @@ bool BufferingAudioSource::readNextBufferChunk()
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferReadyEvent.signal();
|
bufferReadyEvent.signal();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferingAudioSource::readBufferSection (const int64 start, const int length, const int bufferOffset)
|
void BufferingAudioSource::readBufferSection (int64 start, int length, int bufferOffset)
|
||||||
{
|
{
|
||||||
if (source->getNextReadPosition() != start)
|
if (source->getNextReadPosition() != start)
|
||||||
source->setNextReadPosition (start);
|
source->setNextReadPosition (start);
|
||||||
|
|
|
||||||
|
|
@ -105,9 +105,9 @@ private:
|
||||||
AudioBuffer<float> buffer;
|
AudioBuffer<float> buffer;
|
||||||
CriticalSection bufferStartPosLock;
|
CriticalSection bufferStartPosLock;
|
||||||
WaitableEvent bufferReadyEvent;
|
WaitableEvent bufferReadyEvent;
|
||||||
int64 volatile bufferValidStart, bufferValidEnd, nextPlayPos;
|
std::atomic<int64> bufferValidStart { 0 }, bufferValidEnd { 0 }, nextPlayPos { 0 };
|
||||||
double volatile sampleRate;
|
double sampleRate = 0;
|
||||||
bool wasSourceLooping, isPrepared, prefillBuffer;
|
bool wasSourceLooping = false, isPrepared = false, prefillBuffer;
|
||||||
|
|
||||||
bool readNextBufferChunk();
|
bool readNextBufferChunk();
|
||||||
void readBufferSection (int64 start, int length, int bufferOffset);
|
void readBufferSection (int64 start, int length, int bufferOffset);
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ private:
|
||||||
CriticalSection lock;
|
CriticalSection lock;
|
||||||
OptionalScopedPointer<AudioSource> input;
|
OptionalScopedPointer<AudioSource> input;
|
||||||
Reverb reverb;
|
Reverb reverb;
|
||||||
volatile bool bypass;
|
std::atomic<bool> bypass;
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ReverbAudioSource)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ReverbAudioSource)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ namespace juce
|
||||||
|
|
||||||
namespace CoreMidiHelpers
|
namespace CoreMidiHelpers
|
||||||
{
|
{
|
||||||
static bool checkError (const OSStatus err, const int lineNum)
|
static bool checkError (OSStatus err, int lineNum)
|
||||||
{
|
{
|
||||||
if (err == noErr)
|
if (err == noErr)
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -48,10 +48,10 @@ namespace CoreMidiHelpers
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
struct ScopedCFString
|
struct ScopedCFString
|
||||||
{
|
{
|
||||||
ScopedCFString() noexcept : cfString (nullptr) {}
|
ScopedCFString() noexcept {}
|
||||||
~ScopedCFString() noexcept { if (cfString != nullptr) CFRelease (cfString); }
|
~ScopedCFString() noexcept { if (cfString != nullptr) CFRelease (cfString); }
|
||||||
|
|
||||||
CFStringRef cfString;
|
CFStringRef cfString = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
static String getMidiObjectName (MIDIObjectRef entity)
|
static String getMidiObjectName (MIDIObjectRef entity)
|
||||||
|
|
@ -87,7 +87,7 @@ namespace CoreMidiHelpers
|
||||||
|
|
||||||
static String getEndpointName (MIDIEndpointRef endpoint, bool isExternal)
|
static String getEndpointName (MIDIEndpointRef endpoint, bool isExternal)
|
||||||
{
|
{
|
||||||
String result (getMidiObjectName (endpoint));
|
auto result = getMidiObjectName (endpoint);
|
||||||
|
|
||||||
MIDIEntityRef entity = 0; // NB: don't attempt to use nullptr for refs - it fails in some types of build.
|
MIDIEntityRef entity = 0; // NB: don't attempt to use nullptr for refs - it fails in some types of build.
|
||||||
MIDIEndpointGetEntity (endpoint, &entity);
|
MIDIEndpointGetEntity (endpoint, &entity);
|
||||||
|
|
@ -104,7 +104,7 @@ namespace CoreMidiHelpers
|
||||||
|
|
||||||
if (device != 0)
|
if (device != 0)
|
||||||
{
|
{
|
||||||
const String deviceName (getMidiObjectName (device));
|
auto deviceName = getMidiObjectName (device);
|
||||||
|
|
||||||
if (deviceName.isNotEmpty())
|
if (deviceName.isNotEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -141,14 +141,14 @@ namespace CoreMidiHelpers
|
||||||
|
|
||||||
if (numConnections > 0)
|
if (numConnections > 0)
|
||||||
{
|
{
|
||||||
const SInt32* pid = reinterpret_cast<const SInt32*> (CFDataGetBytePtr (connections));
|
auto pid = reinterpret_cast<const SInt32*> (CFDataGetBytePtr (connections));
|
||||||
|
|
||||||
for (int i = 0; i < numConnections; ++i, ++pid)
|
for (int i = 0; i < numConnections; ++i, ++pid)
|
||||||
{
|
{
|
||||||
MIDIUniqueID uid = (MIDIUniqueID) ByteOrder::swapIfLittleEndian ((uint32) *pid);
|
auto uid = (MIDIUniqueID) ByteOrder::swapIfLittleEndian ((uint32) *pid);
|
||||||
MIDIObjectRef connObject;
|
MIDIObjectRef connObject;
|
||||||
MIDIObjectType connObjectType;
|
MIDIObjectType connObjectType;
|
||||||
OSStatus err = MIDIObjectFindByUniqueID (uid, &connObject, &connObjectType);
|
auto err = MIDIObjectFindByUniqueID (uid, &connObject, &connObjectType);
|
||||||
|
|
||||||
if (err == noErr)
|
if (err == noErr)
|
||||||
{
|
{
|
||||||
|
|
@ -192,11 +192,12 @@ namespace CoreMidiHelpers
|
||||||
#if defined (JucePlugin_CFBundleIdentifier)
|
#if defined (JucePlugin_CFBundleIdentifier)
|
||||||
portUniqueId = JUCE_STRINGIFY (JucePlugin_CFBundleIdentifier);
|
portUniqueId = JUCE_STRINGIFY (JucePlugin_CFBundleIdentifier);
|
||||||
#else
|
#else
|
||||||
File appBundle (File::getSpecialLocation (File::currentApplicationFile));
|
auto appBundle = File::getSpecialLocation (File::currentApplicationFile);
|
||||||
CFURLRef bundleURL = CFURLCreateWithFileSystemPath (kCFAllocatorDefault, appBundle.getFullPathName().toCFString(), kCFURLPOSIXPathStyle, true);
|
|
||||||
if (bundleURL != nullptr)
|
if (auto bundleURL = CFURLCreateWithFileSystemPath (kCFAllocatorDefault, appBundle.getFullPathName().toCFString(),
|
||||||
|
kCFURLPOSIXPathStyle, true))
|
||||||
{
|
{
|
||||||
CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, bundleURL);
|
auto bundleRef = CFBundleCreate (kCFAllocatorDefault, bundleURL);
|
||||||
CFRelease (bundleURL);
|
CFRelease (bundleURL);
|
||||||
|
|
||||||
if (bundleRef != nullptr)
|
if (bundleRef != nullptr)
|
||||||
|
|
@ -211,31 +212,30 @@ namespace CoreMidiHelpers
|
||||||
|
|
||||||
if (portUniqueId.isNotEmpty())
|
if (portUniqueId.isNotEmpty())
|
||||||
{
|
{
|
||||||
portUniqueId += (String ("." + portName + String (isInput ? ".input" : ".output")));
|
portUniqueId += "." + portName + (isInput ? ".input" : ".output");
|
||||||
|
|
||||||
CHECK_ERROR (MIDIObjectSetStringProperty (device, kMIDIPropertyUniqueID, portUniqueId.toCFString()));
|
CHECK_ERROR (MIDIObjectSetStringProperty (device, kMIDIPropertyUniqueID, portUniqueId.toCFString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static StringArray findDevices (const bool forInput)
|
static StringArray findDevices (bool forInput)
|
||||||
{
|
{
|
||||||
// It seems that OSX can be a bit picky about the thread that's first used to
|
// It seems that OSX can be a bit picky about the thread that's first used to
|
||||||
// search for devices. It's safest to use the message thread for calling this.
|
// search for devices. It's safest to use the message thread for calling this.
|
||||||
jassert (MessageManager::getInstance()->isThisTheMessageThread());
|
jassert (MessageManager::getInstance()->isThisTheMessageThread());
|
||||||
|
|
||||||
|
StringArray s;
|
||||||
enableSimulatorMidiSession();
|
enableSimulatorMidiSession();
|
||||||
|
|
||||||
const ItemCount num = forInput ? MIDIGetNumberOfSources()
|
auto num = forInput ? MIDIGetNumberOfSources()
|
||||||
: MIDIGetNumberOfDestinations();
|
: MIDIGetNumberOfDestinations();
|
||||||
StringArray s;
|
|
||||||
|
|
||||||
for (ItemCount i = 0; i < num; ++i)
|
for (ItemCount i = 0; i < num; ++i)
|
||||||
{
|
{
|
||||||
MIDIEndpointRef dest = forInput ? MIDIGetSource (i)
|
|
||||||
: MIDIGetDestination (i);
|
|
||||||
String name;
|
String name;
|
||||||
|
|
||||||
if (dest != 0)
|
if (auto dest = forInput ? MIDIGetSource (i)
|
||||||
|
: MIDIGetDestination (i))
|
||||||
name = getConnectedEndpointName (dest);
|
name = getConnectedEndpointName (dest);
|
||||||
|
|
||||||
if (name.isEmpty())
|
if (name.isEmpty())
|
||||||
|
|
@ -298,7 +298,7 @@ namespace CoreMidiHelpers
|
||||||
MIDIEndpointDispose (endPoint);
|
MIDIEndpointDispose (endPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void send (const MIDIPacketList* const packets) noexcept
|
void send (const MIDIPacketList* packets) noexcept
|
||||||
{
|
{
|
||||||
if (port != 0)
|
if (port != 0)
|
||||||
MIDISend (port, endPoint, packets);
|
MIDISend (port, endPoint, packets);
|
||||||
|
|
@ -311,16 +311,13 @@ namespace CoreMidiHelpers
|
||||||
};
|
};
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
class MidiPortAndCallback;
|
struct MidiPortAndCallback;
|
||||||
CriticalSection callbackLock;
|
CriticalSection callbackLock;
|
||||||
Array<MidiPortAndCallback*> activeCallbacks;
|
Array<MidiPortAndCallback*> activeCallbacks;
|
||||||
|
|
||||||
class MidiPortAndCallback
|
struct MidiPortAndCallback
|
||||||
{
|
{
|
||||||
public:
|
MidiPortAndCallback (MidiInputCallback& cb) : callback (cb) {}
|
||||||
MidiPortAndCallback (MidiInputCallback& cb) : callback (cb)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~MidiPortAndCallback()
|
~MidiPortAndCallback()
|
||||||
{
|
{
|
||||||
|
|
@ -335,7 +332,7 @@ namespace CoreMidiHelpers
|
||||||
CHECK_ERROR (MIDIPortDisconnectSource (portAndEndpoint->port, portAndEndpoint->endPoint));
|
CHECK_ERROR (MIDIPortDisconnectSource (portAndEndpoint->port, portAndEndpoint->endPoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
void handlePackets (const MIDIPacketList* const pktlist)
|
void handlePackets (const MIDIPacketList* pktlist)
|
||||||
{
|
{
|
||||||
auto time = Time::getMillisecondCounterHiRes() * 0.001;
|
auto time = Time::getMillisecondCounterHiRes() * 0.001;
|
||||||
|
|
||||||
|
|
@ -357,7 +354,7 @@ namespace CoreMidiHelpers
|
||||||
|
|
||||||
MidiInput* input = nullptr;
|
MidiInput* input = nullptr;
|
||||||
std::unique_ptr<MidiPortAndEndpoint> portAndEndpoint;
|
std::unique_ptr<MidiPortAndEndpoint> portAndEndpoint;
|
||||||
volatile bool active = false;
|
std::atomic<bool> active { false };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MidiInputCallback& callback;
|
MidiInputCallback& callback;
|
||||||
|
|
@ -380,15 +377,15 @@ MidiOutput* MidiOutput::openDevice (int index)
|
||||||
|
|
||||||
if (isPositiveAndBelow (index, MIDIGetNumberOfDestinations()))
|
if (isPositiveAndBelow (index, MIDIGetNumberOfDestinations()))
|
||||||
{
|
{
|
||||||
MIDIEndpointRef endPoint = MIDIGetDestination ((ItemCount) index);
|
auto endPoint = MIDIGetDestination ((ItemCount) index);
|
||||||
|
|
||||||
CoreMidiHelpers::ScopedCFString pname;
|
CoreMidiHelpers::ScopedCFString pname;
|
||||||
|
|
||||||
if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname.cfString)))
|
if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname.cfString)))
|
||||||
{
|
{
|
||||||
MIDIClientRef client = CoreMidiHelpers::getGlobalMidiClient();
|
auto client = CoreMidiHelpers::getGlobalMidiClient();
|
||||||
MIDIPortRef port;
|
MIDIPortRef port;
|
||||||
String deviceName = CoreMidiHelpers::getConnectedEndpointName (endPoint);
|
auto deviceName = CoreMidiHelpers::getConnectedEndpointName (endPoint);
|
||||||
|
|
||||||
if (client != 0 && CHECK_ERROR (MIDIOutputPortCreate (client, pname.cfString, &port)))
|
if (client != 0 && CHECK_ERROR (MIDIOutputPortCreate (client, pname.cfString, &port)))
|
||||||
{
|
{
|
||||||
|
|
@ -413,7 +410,7 @@ MidiOutput* MidiOutput::createNewDevice (const String& deviceName)
|
||||||
{
|
{
|
||||||
CoreMidiHelpers::setUniqueIdForMidiPort (endPoint, deviceName, false);
|
CoreMidiHelpers::setUniqueIdForMidiPort (endPoint, deviceName, false);
|
||||||
|
|
||||||
MidiOutput* mo = new MidiOutput (deviceName);
|
auto mo = new MidiOutput (deviceName);
|
||||||
mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (0, endPoint);
|
mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (0, endPoint);
|
||||||
return mo;
|
return mo;
|
||||||
}
|
}
|
||||||
|
|
@ -438,8 +435,8 @@ void MidiOutput::sendMessageNow (const MidiMessage& message)
|
||||||
|
|
||||||
HeapBlock<MIDIPacketList> allocatedPackets;
|
HeapBlock<MIDIPacketList> allocatedPackets;
|
||||||
MIDIPacketList stackPacket;
|
MIDIPacketList stackPacket;
|
||||||
MIDIPacketList* packetToSend = &stackPacket;
|
auto* packetToSend = &stackPacket;
|
||||||
const size_t dataSize = (size_t) message.getRawDataSize();
|
auto dataSize = (size_t) message.getRawDataSize();
|
||||||
|
|
||||||
if (message.isSysEx())
|
if (message.isSysEx())
|
||||||
{
|
{
|
||||||
|
|
@ -450,7 +447,7 @@ void MidiOutput::sendMessageNow (const MidiMessage& message)
|
||||||
packetToSend = allocatedPackets;
|
packetToSend = allocatedPackets;
|
||||||
packetToSend->numPackets = (UInt32) numPackets;
|
packetToSend->numPackets = (UInt32) numPackets;
|
||||||
|
|
||||||
MIDIPacket* p = packetToSend->packet;
|
auto* p = packetToSend->packet;
|
||||||
|
|
||||||
for (int i = 0; i < numPackets; ++i)
|
for (int i = 0; i < numPackets; ++i)
|
||||||
{
|
{
|
||||||
|
|
@ -464,7 +461,7 @@ void MidiOutput::sendMessageNow (const MidiMessage& message)
|
||||||
}
|
}
|
||||||
else if (dataSize < 65536) // max packet size
|
else if (dataSize < 65536) // max packet size
|
||||||
{
|
{
|
||||||
const size_t stackCapacity = sizeof (stackPacket.packet->data);
|
auto stackCapacity = sizeof (stackPacket.packet->data);
|
||||||
|
|
||||||
if (dataSize > stackCapacity)
|
if (dataSize > stackCapacity)
|
||||||
{
|
{
|
||||||
|
|
@ -473,7 +470,7 @@ void MidiOutput::sendMessageNow (const MidiMessage& message)
|
||||||
}
|
}
|
||||||
|
|
||||||
packetToSend->numPackets = 1;
|
packetToSend->numPackets = 1;
|
||||||
MIDIPacket& p = *(packetToSend->packet);
|
auto& p = *(packetToSend->packet);
|
||||||
p.timeStamp = timeStamp;
|
p.timeStamp = timeStamp;
|
||||||
p.length = (UInt16) dataSize;
|
p.length = (UInt16) dataSize;
|
||||||
memcpy (p.data, message.getRawData(), dataSize);
|
memcpy (p.data, message.getRawData(), dataSize);
|
||||||
|
|
@ -500,13 +497,13 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback)
|
||||||
|
|
||||||
if (isPositiveAndBelow (index, MIDIGetNumberOfSources()))
|
if (isPositiveAndBelow (index, MIDIGetNumberOfSources()))
|
||||||
{
|
{
|
||||||
if (MIDIEndpointRef endPoint = MIDIGetSource ((ItemCount) index))
|
if (auto endPoint = MIDIGetSource ((ItemCount) index))
|
||||||
{
|
{
|
||||||
ScopedCFString name;
|
ScopedCFString name;
|
||||||
|
|
||||||
if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &name.cfString)))
|
if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &name.cfString)))
|
||||||
{
|
{
|
||||||
if (MIDIClientRef client = getGlobalMidiClient())
|
if (auto client = getGlobalMidiClient())
|
||||||
{
|
{
|
||||||
MIDIPortRef port;
|
MIDIPortRef port;
|
||||||
std::unique_ptr<MidiPortAndCallback> mpc (new MidiPortAndCallback (*callback));
|
std::unique_ptr<MidiPortAndCallback> mpc (new MidiPortAndCallback (*callback));
|
||||||
|
|
@ -540,11 +537,9 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback)
|
||||||
MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback)
|
MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback)
|
||||||
{
|
{
|
||||||
jassert (callback != nullptr);
|
jassert (callback != nullptr);
|
||||||
|
|
||||||
using namespace CoreMidiHelpers;
|
using namespace CoreMidiHelpers;
|
||||||
MidiInput* mi = nullptr;
|
|
||||||
|
|
||||||
if (MIDIClientRef client = getGlobalMidiClient())
|
if (auto client = getGlobalMidiClient())
|
||||||
{
|
{
|
||||||
std::unique_ptr<MidiPortAndCallback> mpc (new MidiPortAndCallback (*callback));
|
std::unique_ptr<MidiPortAndCallback> mpc (new MidiPortAndCallback (*callback));
|
||||||
mpc->active = false;
|
mpc->active = false;
|
||||||
|
|
@ -555,20 +550,22 @@ MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallba
|
||||||
|
|
||||||
if (CHECK_ERROR (MIDIDestinationCreate (client, name.cfString, midiInputProc, mpc.get(), &endPoint)))
|
if (CHECK_ERROR (MIDIDestinationCreate (client, name.cfString, midiInputProc, mpc.get(), &endPoint)))
|
||||||
{
|
{
|
||||||
CoreMidiHelpers::setUniqueIdForMidiPort (endPoint, deviceName, true);
|
setUniqueIdForMidiPort (endPoint, deviceName, true);
|
||||||
|
|
||||||
mpc->portAndEndpoint.reset (new MidiPortAndEndpoint (0, endPoint));
|
mpc->portAndEndpoint.reset (new MidiPortAndEndpoint (0, endPoint));
|
||||||
|
|
||||||
mi = new MidiInput (deviceName);
|
auto mi = new MidiInput (deviceName);
|
||||||
mpc->input = mi;
|
mpc->input = mi;
|
||||||
mi->internal = mpc.get();
|
mi->internal = mpc.get();
|
||||||
|
|
||||||
const ScopedLock sl (callbackLock);
|
const ScopedLock sl (callbackLock);
|
||||||
activeCallbacks.add (mpc.release());
|
activeCallbacks.add (mpc.release());
|
||||||
|
|
||||||
|
return mi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mi;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
MidiInput::MidiInput (const String& nm) : name (nm)
|
MidiInput::MidiInput (const String& nm) : name (nm)
|
||||||
|
|
|
||||||
|
|
@ -77,11 +77,7 @@ struct ASIOSampleFormat
|
||||||
{
|
{
|
||||||
ASIOSampleFormat() noexcept {}
|
ASIOSampleFormat() noexcept {}
|
||||||
|
|
||||||
ASIOSampleFormat (const long type) noexcept
|
ASIOSampleFormat (long type) noexcept
|
||||||
: bitDepth (24),
|
|
||||||
littleEndian (true),
|
|
||||||
formatIsFloat (false),
|
|
||||||
byteStride (4)
|
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
|
|
@ -114,7 +110,7 @@ struct ASIOSampleFormat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void convertToFloat (const void* const src, float* const dst, const int samps) const noexcept
|
void convertToFloat (const void* src, float* dst, int samps) const noexcept
|
||||||
{
|
{
|
||||||
if (formatIsFloat)
|
if (formatIsFloat)
|
||||||
{
|
{
|
||||||
|
|
@ -132,7 +128,7 @@ struct ASIOSampleFormat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void convertFromFloat (const float* const src, void* const dst, const int samps) const noexcept
|
void convertFromFloat (const float* src, void* dst, int samps) const noexcept
|
||||||
{
|
{
|
||||||
if (formatIsFloat)
|
if (formatIsFloat)
|
||||||
{
|
{
|
||||||
|
|
@ -150,18 +146,18 @@ struct ASIOSampleFormat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear (void* dst, const int numSamps) noexcept
|
void clear (void* dst, int numSamps) noexcept
|
||||||
{
|
{
|
||||||
if (dst != nullptr)
|
if (dst != nullptr)
|
||||||
zeromem (dst, numSamps * byteStride);
|
zeromem (dst, numSamps * byteStride);
|
||||||
}
|
}
|
||||||
|
|
||||||
int bitDepth, byteStride;
|
int bitDepth = 24, byteStride = 4;
|
||||||
bool formatIsFloat, littleEndian;
|
bool formatIsFloat = false, littleEndian = true;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void convertInt16ToFloat (const char* src, float* dest, const int srcStrideBytes,
|
static void convertInt16ToFloat (const char* src, float* dest, int srcStrideBytes,
|
||||||
int numSamples, const bool littleEndian) noexcept
|
int numSamples, bool littleEndian) noexcept
|
||||||
{
|
{
|
||||||
const double g = 1.0 / 32768.0;
|
const double g = 1.0 / 32768.0;
|
||||||
|
|
||||||
|
|
@ -183,8 +179,8 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void convertFloatToInt16 (const float* src, char* dest, const int dstStrideBytes,
|
static void convertFloatToInt16 (const float* src, char* dest, int dstStrideBytes,
|
||||||
int numSamples, const bool littleEndian) noexcept
|
int numSamples, bool littleEndian) noexcept
|
||||||
{
|
{
|
||||||
const double maxVal = (double) 0x7fff;
|
const double maxVal = (double) 0x7fff;
|
||||||
|
|
||||||
|
|
@ -206,8 +202,8 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void convertInt24ToFloat (const char* src, float* dest, const int srcStrideBytes,
|
static void convertInt24ToFloat (const char* src, float* dest, int srcStrideBytes,
|
||||||
int numSamples, const bool littleEndian) noexcept
|
int numSamples, bool littleEndian) noexcept
|
||||||
{
|
{
|
||||||
const double g = 1.0 / 0x7fffff;
|
const double g = 1.0 / 0x7fffff;
|
||||||
|
|
||||||
|
|
@ -229,8 +225,8 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void convertFloatToInt24 (const float* src, char* dest, const int dstStrideBytes,
|
static void convertFloatToInt24 (const float* src, char* dest, int dstStrideBytes,
|
||||||
int numSamples, const bool littleEndian) noexcept
|
int numSamples, bool littleEndian) noexcept
|
||||||
{
|
{
|
||||||
const double maxVal = (double) 0x7fffff;
|
const double maxVal = (double) 0x7fffff;
|
||||||
|
|
||||||
|
|
@ -252,8 +248,8 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void convertInt32ToFloat (const char* src, float* dest, const int srcStrideBytes,
|
static void convertInt32ToFloat (const char* src, float* dest, int srcStrideBytes,
|
||||||
int numSamples, const bool littleEndian) noexcept
|
int numSamples, bool littleEndian) noexcept
|
||||||
{
|
{
|
||||||
const double g = 1.0 / 0x7fffffff;
|
const double g = 1.0 / 0x7fffffff;
|
||||||
|
|
||||||
|
|
@ -275,8 +271,8 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void convertFloatToInt32 (const float* src, char* dest, const int dstStrideBytes,
|
static void convertFloatToInt32 (const float* src, char* dest, int dstStrideBytes,
|
||||||
int numSamples, const bool littleEndian) noexcept
|
int numSamples, bool littleEndian) noexcept
|
||||||
{
|
{
|
||||||
const double maxVal = (double) 0x7fffffff;
|
const double maxVal = (double) 0x7fffffff;
|
||||||
|
|
||||||
|
|
@ -300,8 +296,9 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
constexpr int maxNumASIODevices = 16;
|
||||||
class ASIOAudioIODevice;
|
class ASIOAudioIODevice;
|
||||||
static ASIOAudioIODevice* volatile currentASIODev[16] = { 0 };
|
static ASIOAudioIODevice* currentASIODev[maxNumASIODevices] = {};
|
||||||
|
|
||||||
extern HWND juce_messageWindowHandle;
|
extern HWND juce_messageWindowHandle;
|
||||||
|
|
||||||
|
|
@ -314,33 +311,10 @@ class ASIOAudioIODevice : public AudioIODevice,
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ASIOAudioIODevice (ASIOAudioIODeviceType* ownerType, const String& devName,
|
ASIOAudioIODevice (ASIOAudioIODeviceType* ownerType, const String& devName,
|
||||||
const CLSID clsID, const int slotNumber)
|
CLSID clsID, int slotNumber)
|
||||||
: AudioIODevice (devName, "ASIO"),
|
: AudioIODevice (devName, "ASIO"),
|
||||||
owner (ownerType),
|
owner (ownerType),
|
||||||
asioObject (nullptr),
|
classId (clsID)
|
||||||
classId (clsID),
|
|
||||||
inputLatency (0),
|
|
||||||
outputLatency (0),
|
|
||||||
minBufferSize (0), maxBufferSize (0),
|
|
||||||
preferredBufferSize (0),
|
|
||||||
bufferGranularity (0),
|
|
||||||
numClockSources (0),
|
|
||||||
currentBlockSizeSamples (0),
|
|
||||||
currentBitDepth (16),
|
|
||||||
currentSampleRate (0),
|
|
||||||
currentCallback (nullptr),
|
|
||||||
bufferIndex (0),
|
|
||||||
numActiveInputChans (0),
|
|
||||||
numActiveOutputChans (0),
|
|
||||||
deviceIsOpen (false),
|
|
||||||
isStarted (false),
|
|
||||||
buffersCreated (false),
|
|
||||||
calledback (false),
|
|
||||||
littleEndian (false),
|
|
||||||
postOutput (true),
|
|
||||||
needToReset (false),
|
|
||||||
insideControlPanelModalLoop (false),
|
|
||||||
shouldUsePreferredSize (false)
|
|
||||||
{
|
{
|
||||||
::CoInitialize (nullptr);
|
::CoInitialize (nullptr);
|
||||||
|
|
||||||
|
|
@ -348,15 +322,15 @@ public:
|
||||||
inBuffers.calloc (4);
|
inBuffers.calloc (4);
|
||||||
outBuffers.calloc (4);
|
outBuffers.calloc (4);
|
||||||
|
|
||||||
jassert (currentASIODev [slotNumber] == nullptr);
|
jassert (currentASIODev[slotNumber] == nullptr);
|
||||||
currentASIODev [slotNumber] = this;
|
currentASIODev[slotNumber] = this;
|
||||||
|
|
||||||
openDevice();
|
openDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
~ASIOAudioIODevice()
|
~ASIOAudioIODevice()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < numElementsInArray (currentASIODev); ++i)
|
for (int i = 0; i < maxNumASIODevices; ++i)
|
||||||
if (currentASIODev[i] == this)
|
if (currentASIODev[i] == this)
|
||||||
currentASIODev[i] = nullptr;
|
currentASIODev[i] = nullptr;
|
||||||
|
|
||||||
|
|
@ -381,7 +355,7 @@ public:
|
||||||
|
|
||||||
if (newRates.isEmpty())
|
if (newRates.isEmpty())
|
||||||
{
|
{
|
||||||
double cr = getSampleRate();
|
auto cr = getSampleRate();
|
||||||
JUCE_ASIO_LOG ("No sample rates supported - current rate: " + String ((int) cr));
|
JUCE_ASIO_LOG ("No sample rates supported - current rate: " + String ((int) cr));
|
||||||
|
|
||||||
if (cr > 0)
|
if (cr > 0)
|
||||||
|
|
@ -394,8 +368,9 @@ public:
|
||||||
|
|
||||||
#if JUCE_ASIO_DEBUGGING
|
#if JUCE_ASIO_DEBUGGING
|
||||||
StringArray s;
|
StringArray s;
|
||||||
for (int i = 0; i < sampleRates.size(); ++i)
|
|
||||||
s.add (String (sampleRates.getUnchecked(i)));
|
for (auto r : sampleRates)
|
||||||
|
s.add (String (r));
|
||||||
|
|
||||||
JUCE_ASIO_LOG ("Rates: " + s.joinIntoString (" "));
|
JUCE_ASIO_LOG ("Rates: " + s.joinIntoString (" "));
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -425,21 +400,20 @@ public:
|
||||||
|
|
||||||
if (asioObject == nullptr)
|
if (asioObject == nullptr)
|
||||||
{
|
{
|
||||||
const String openingError (openDevice());
|
auto openingError = openDevice();
|
||||||
|
|
||||||
if (asioObject == nullptr)
|
if (asioObject == nullptr)
|
||||||
return openingError;
|
return openingError;
|
||||||
}
|
}
|
||||||
|
|
||||||
isStarted = false;
|
isStarted = false;
|
||||||
bufferIndex = -1;
|
|
||||||
|
|
||||||
long err = asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans);
|
auto err = asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans);
|
||||||
jassert (err == ASE_OK);
|
jassert (err == ASE_OK);
|
||||||
|
|
||||||
bufferSizeSamples = readBufferSizes (bufferSizeSamples);
|
bufferSizeSamples = readBufferSizes (bufferSizeSamples);
|
||||||
|
|
||||||
double sampleRate = sr;
|
auto sampleRate = sr;
|
||||||
currentSampleRate = sampleRate;
|
currentSampleRate = sampleRate;
|
||||||
currentBlockSizeSamples = bufferSizeSamples;
|
currentBlockSizeSamples = bufferSizeSamples;
|
||||||
currentChansOut.clear();
|
currentChansOut.clear();
|
||||||
|
|
@ -450,9 +424,11 @@ public:
|
||||||
if (sampleRate == 0 || (sampleRates.size() > 0 && ! sampleRates.contains (sampleRate)))
|
if (sampleRate == 0 || (sampleRates.size() > 0 && ! sampleRates.contains (sampleRate)))
|
||||||
sampleRate = sampleRates[0];
|
sampleRate = sampleRates[0];
|
||||||
|
|
||||||
jassert (sampleRate != 0);
|
|
||||||
if (sampleRate == 0)
|
if (sampleRate == 0)
|
||||||
|
{
|
||||||
|
jassertfalse;
|
||||||
sampleRate = 44100.0;
|
sampleRate = 44100.0;
|
||||||
|
}
|
||||||
|
|
||||||
updateClockSources();
|
updateClockSources();
|
||||||
currentSampleRate = getSampleRate();
|
currentSampleRate = getSampleRate();
|
||||||
|
|
@ -486,7 +462,7 @@ public:
|
||||||
needToReset = false;
|
needToReset = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int totalBuffers = resetBuffers (inputChannels, outputChannels);
|
auto totalBuffers = resetBuffers (inputChannels, outputChannels);
|
||||||
|
|
||||||
setCallbackFunctions();
|
setCallbackFunctions();
|
||||||
|
|
||||||
|
|
@ -508,7 +484,7 @@ public:
|
||||||
if (err == ASE_OK)
|
if (err == ASE_OK)
|
||||||
{
|
{
|
||||||
buffersCreated = true;
|
buffersCreated = true;
|
||||||
tempBuffer.calloc (totalBuffers * currentBlockSizeSamples + 32);
|
ioBufferSpace.calloc (totalBuffers * currentBlockSizeSamples + 32);
|
||||||
|
|
||||||
int n = 0;
|
int n = 0;
|
||||||
Array<int> types;
|
Array<int> types;
|
||||||
|
|
@ -518,7 +494,7 @@ public:
|
||||||
{
|
{
|
||||||
if (inputChannels[i])
|
if (inputChannels[i])
|
||||||
{
|
{
|
||||||
inBuffers[n] = tempBuffer + (currentBlockSizeSamples * n);
|
inBuffers[n] = ioBufferSpace + (currentBlockSizeSamples * n);
|
||||||
|
|
||||||
ASIOChannelInfo channelInfo = { 0 };
|
ASIOChannelInfo channelInfo = { 0 };
|
||||||
channelInfo.channel = i;
|
channelInfo.channel = i;
|
||||||
|
|
@ -540,7 +516,7 @@ public:
|
||||||
{
|
{
|
||||||
if (outputChannels[i])
|
if (outputChannels[i])
|
||||||
{
|
{
|
||||||
outBuffers[n] = tempBuffer + (currentBlockSizeSamples * (numActiveInputChans + n));
|
outBuffers[n] = ioBufferSpace + (currentBlockSizeSamples * (numActiveInputChans + n));
|
||||||
|
|
||||||
ASIOChannelInfo channelInfo = { 0 };
|
ASIOChannelInfo channelInfo = { 0 };
|
||||||
channelInfo.channel = i;
|
channelInfo.channel = i;
|
||||||
|
|
@ -564,8 +540,8 @@ public:
|
||||||
|
|
||||||
for (int i = 0; i < numActiveOutputChans; ++i)
|
for (int i = 0; i < numActiveOutputChans; ++i)
|
||||||
{
|
{
|
||||||
outputFormat[i].clear (bufferInfos [numActiveInputChans + i].buffers[0], currentBlockSizeSamples);
|
outputFormat[i].clear (bufferInfos[numActiveInputChans + i].buffers[0], currentBlockSizeSamples);
|
||||||
outputFormat[i].clear (bufferInfos [numActiveInputChans + i].buffers[1], currentBlockSizeSamples);
|
outputFormat[i].clear (bufferInfos[numActiveInputChans + i].buffers[1], currentBlockSizeSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
readLatencies();
|
readLatencies();
|
||||||
|
|
@ -615,7 +591,7 @@ public:
|
||||||
isStarted = false;
|
isStarted = false;
|
||||||
deviceIsOpen = false;
|
deviceIsOpen = false;
|
||||||
|
|
||||||
const String errorCopy (error);
|
auto errorCopy = error;
|
||||||
close(); // (this resets the error string)
|
close(); // (this resets the error string)
|
||||||
error = errorCopy;
|
error = errorCopy;
|
||||||
}
|
}
|
||||||
|
|
@ -678,7 +654,7 @@ public:
|
||||||
|
|
||||||
void stop() override
|
void stop() override
|
||||||
{
|
{
|
||||||
AudioIODeviceCallback* const lastCallback = currentCallback;
|
auto* lastCallback = currentCallback;
|
||||||
|
|
||||||
{
|
{
|
||||||
const ScopedLock sl (callbackLock);
|
const ScopedLock sl (callbackLock);
|
||||||
|
|
@ -698,15 +674,13 @@ public:
|
||||||
|
|
||||||
bool done = false;
|
bool done = false;
|
||||||
insideControlPanelModalLoop = true;
|
insideControlPanelModalLoop = true;
|
||||||
|
auto started = Time::getMillisecondCounter();
|
||||||
const uint32 started = Time::getMillisecondCounter();
|
|
||||||
|
|
||||||
if (asioObject != nullptr)
|
if (asioObject != nullptr)
|
||||||
{
|
{
|
||||||
asioObject->controlPanel();
|
asioObject->controlPanel();
|
||||||
|
|
||||||
const int spent = (int) Time::getMillisecondCounter() - (int) started;
|
auto spent = (int) (Time::getMillisecondCounter() - started);
|
||||||
|
|
||||||
JUCE_ASIO_LOG ("spent: " + String (spent));
|
JUCE_ASIO_LOG ("spent: " + String (spent));
|
||||||
|
|
||||||
if (spent > 300)
|
if (spent > 300)
|
||||||
|
|
@ -730,13 +704,10 @@ public:
|
||||||
if (! insideControlPanelModalLoop)
|
if (! insideControlPanelModalLoop)
|
||||||
{
|
{
|
||||||
stopTimer();
|
stopTimer();
|
||||||
|
|
||||||
JUCE_ASIO_LOG ("restart request!");
|
JUCE_ASIO_LOG ("restart request!");
|
||||||
|
|
||||||
AudioIODeviceCallback* const oldCallback = currentCallback;
|
auto* oldCallback = currentCallback;
|
||||||
|
|
||||||
close();
|
close();
|
||||||
|
|
||||||
needToReset = true;
|
needToReset = true;
|
||||||
open (BigInteger (currentChansIn), BigInteger (currentChansOut),
|
open (BigInteger (currentChansIn), BigInteger (currentChansOut),
|
||||||
currentSampleRate, currentBlockSizeSamples);
|
currentSampleRate, currentBlockSizeSamples);
|
||||||
|
|
@ -757,51 +728,49 @@ public:
|
||||||
private:
|
private:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
WeakReference<ASIOAudioIODeviceType> owner;
|
WeakReference<ASIOAudioIODeviceType> owner;
|
||||||
IASIO* volatile asioObject;
|
IASIO* asioObject = {};
|
||||||
ASIOCallbacks callbacks;
|
ASIOCallbacks callbacks;
|
||||||
|
|
||||||
CLSID classId;
|
CLSID classId;
|
||||||
String error;
|
String error;
|
||||||
|
|
||||||
long totalNumInputChans, totalNumOutputChans;
|
long totalNumInputChans = 0, totalNumOutputChans = 0;
|
||||||
StringArray inputChannelNames, outputChannelNames;
|
StringArray inputChannelNames, outputChannelNames;
|
||||||
|
|
||||||
Array<double> sampleRates;
|
Array<double> sampleRates;
|
||||||
Array<int> bufferSizes;
|
Array<int> bufferSizes;
|
||||||
long inputLatency, outputLatency;
|
long inputLatency = 0, outputLatency = 0;
|
||||||
long minBufferSize, maxBufferSize, preferredBufferSize, bufferGranularity;
|
long minBufferSize = 0, maxBufferSize = 0, preferredBufferSize = 0, bufferGranularity = 0;
|
||||||
ASIOClockSource clocks[32];
|
ASIOClockSource clocks[32] = {};
|
||||||
int numClockSources;
|
int numClockSources = 0;
|
||||||
|
|
||||||
int volatile currentBlockSizeSamples;
|
int currentBlockSizeSamples = 0;
|
||||||
int volatile currentBitDepth;
|
int currentBitDepth = 16;
|
||||||
double volatile currentSampleRate;
|
double currentSampleRate = 0;
|
||||||
BigInteger currentChansOut, currentChansIn;
|
BigInteger currentChansOut, currentChansIn;
|
||||||
AudioIODeviceCallback* volatile currentCallback;
|
AudioIODeviceCallback* currentCallback = {};
|
||||||
CriticalSection callbackLock;
|
CriticalSection callbackLock;
|
||||||
|
|
||||||
HeapBlock<ASIOBufferInfo> bufferInfos;
|
HeapBlock<ASIOBufferInfo> bufferInfos;
|
||||||
HeapBlock<float*> inBuffers, outBuffers;
|
HeapBlock<float*> inBuffers, outBuffers;
|
||||||
|
HeapBlock<float> ioBufferSpace;
|
||||||
HeapBlock<ASIOSampleFormat> inputFormat, outputFormat;
|
HeapBlock<ASIOSampleFormat> inputFormat, outputFormat;
|
||||||
|
int numActiveInputChans = 0, numActiveOutputChans = 0;
|
||||||
|
|
||||||
WaitableEvent event1;
|
bool deviceIsOpen = false, isStarted = false, buffersCreated = false;
|
||||||
HeapBlock<float> tempBuffer;
|
std::atomic<bool> calledback { false };
|
||||||
int volatile bufferIndex, numActiveInputChans, numActiveOutputChans;
|
bool littleEndian = false, postOutput = true, needToReset = false;
|
||||||
|
bool insideControlPanelModalLoop = false;
|
||||||
bool deviceIsOpen, isStarted, buffersCreated;
|
bool shouldUsePreferredSize = false;
|
||||||
bool volatile calledback;
|
|
||||||
bool volatile littleEndian, postOutput, needToReset;
|
|
||||||
bool volatile insideControlPanelModalLoop;
|
|
||||||
bool volatile shouldUsePreferredSize;
|
|
||||||
int xruns = 0;
|
int xruns = 0;
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
static String convertASIOString (char* const text, int length)
|
static String convertASIOString (char* text, int length)
|
||||||
{
|
{
|
||||||
if (CharPointer_UTF8::isValidString (text, length))
|
if (CharPointer_UTF8::isValidString (text, length))
|
||||||
return String::fromUTF8 (text, length);
|
return String::fromUTF8 (text, length);
|
||||||
|
|
||||||
WCHAR wideVersion [64] = { 0 };
|
WCHAR wideVersion[64] = {};
|
||||||
MultiByteToWideChar (CP_ACP, 0, text, length, wideVersion, numElementsInArray (wideVersion));
|
MultiByteToWideChar (CP_ACP, 0, text, length, wideVersion, numElementsInArray (wideVersion));
|
||||||
return wideVersion;
|
return wideVersion;
|
||||||
}
|
}
|
||||||
|
|
@ -867,7 +836,7 @@ private:
|
||||||
if (shouldUsePreferredSize)
|
if (shouldUsePreferredSize)
|
||||||
{
|
{
|
||||||
JUCE_ASIO_LOG ("Using preferred size for buffer..");
|
JUCE_ASIO_LOG ("Using preferred size for buffer..");
|
||||||
long err = refreshBufferSizes();
|
auto err = refreshBufferSizes();
|
||||||
|
|
||||||
if (err == ASE_OK)
|
if (err == ASE_OK)
|
||||||
{
|
{
|
||||||
|
|
@ -890,8 +859,8 @@ private:
|
||||||
{
|
{
|
||||||
numActiveInputChans = 0;
|
numActiveInputChans = 0;
|
||||||
numActiveOutputChans = 0;
|
numActiveOutputChans = 0;
|
||||||
|
auto* info = bufferInfos.get();
|
||||||
|
|
||||||
ASIOBufferInfo* info = bufferInfos;
|
|
||||||
for (int i = 0; i < totalNumInputChans; ++i)
|
for (int i = 0; i < totalNumInputChans; ++i)
|
||||||
{
|
{
|
||||||
if (inputChannels[i])
|
if (inputChannels[i])
|
||||||
|
|
@ -952,7 +921,7 @@ private:
|
||||||
double getSampleRate() const
|
double getSampleRate() const
|
||||||
{
|
{
|
||||||
double cr = 0;
|
double cr = 0;
|
||||||
long err = asioObject->getSampleRate (&cr);
|
auto err = asioObject->getSampleRate (&cr);
|
||||||
JUCE_ASIO_LOG_ERROR ("getSampleRate", err);
|
JUCE_ASIO_LOG_ERROR ("getSampleRate", err);
|
||||||
return cr;
|
return cr;
|
||||||
}
|
}
|
||||||
|
|
@ -962,7 +931,7 @@ private:
|
||||||
if (currentSampleRate != newRate)
|
if (currentSampleRate != newRate)
|
||||||
{
|
{
|
||||||
JUCE_ASIO_LOG ("rate change: " + String (currentSampleRate) + " to " + String (newRate));
|
JUCE_ASIO_LOG ("rate change: " + String (currentSampleRate) + " to " + String (newRate));
|
||||||
long err = asioObject->setSampleRate (newRate);
|
auto err = asioObject->setSampleRate (newRate);
|
||||||
|
|
||||||
if (err == ASE_NoClock && numClockSources > 0)
|
if (err == ASE_NoClock && numClockSources > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -970,7 +939,6 @@ private:
|
||||||
Thread::sleep (10);
|
Thread::sleep (10);
|
||||||
err = asioObject->setClockSource (clocks[0].index);
|
err = asioObject->setClockSource (clocks[0].index);
|
||||||
JUCE_ASIO_LOG_ERROR ("setClockSource2", err);
|
JUCE_ASIO_LOG_ERROR ("setClockSource2", err);
|
||||||
|
|
||||||
Thread::sleep (10);
|
Thread::sleep (10);
|
||||||
err = asioObject->setSampleRate (newRate);
|
err = asioObject->setSampleRate (newRate);
|
||||||
}
|
}
|
||||||
|
|
@ -1009,7 +977,7 @@ private:
|
||||||
if (numClockSources > 1 && ! isSourceSet)
|
if (numClockSources > 1 && ! isSourceSet)
|
||||||
{
|
{
|
||||||
JUCE_ASIO_LOG ("setting clock source");
|
JUCE_ASIO_LOG ("setting clock source");
|
||||||
long err = asioObject->setClockSource (clocks[0].index);
|
auto err = asioObject->setClockSource (clocks[0].index);
|
||||||
JUCE_ASIO_LOG_ERROR ("setClockSource1", err);
|
JUCE_ASIO_LOG_ERROR ("setClockSource1", err);
|
||||||
Thread::sleep (20);
|
Thread::sleep (20);
|
||||||
}
|
}
|
||||||
|
|
@ -1035,7 +1003,7 @@ private:
|
||||||
numActiveInputChans = 0;
|
numActiveInputChans = 0;
|
||||||
numActiveOutputChans = 0;
|
numActiveOutputChans = 0;
|
||||||
|
|
||||||
ASIOBufferInfo* info = bufferInfos;
|
auto* info = bufferInfos.get();
|
||||||
int numChans = 0;
|
int numChans = 0;
|
||||||
|
|
||||||
for (int i = 0; i < jmin (2, (int) totalNumInputChans); ++i)
|
for (int i = 0; i < jmin (2, (int) totalNumInputChans); ++i)
|
||||||
|
|
@ -1064,7 +1032,7 @@ private:
|
||||||
|
|
||||||
if (preferredSize > 0)
|
if (preferredSize > 0)
|
||||||
{
|
{
|
||||||
long err = asioObject->createBuffers (bufferInfos, numChans, preferredSize, &callbacks);
|
auto err = asioObject->createBuffers (bufferInfos, numChans, preferredSize, &callbacks);
|
||||||
JUCE_ASIO_LOG_ERROR ("dummy buffers", err);
|
JUCE_ASIO_LOG_ERROR ("dummy buffers", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1094,8 +1062,8 @@ private:
|
||||||
if (i < 2)
|
if (i < 2)
|
||||||
{
|
{
|
||||||
// clear the channels that are used with the dummy stuff
|
// clear the channels that are used with the dummy stuff
|
||||||
outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[0], preferredBufferSize);
|
outputFormat[i].clear (bufferInfos[outputBufferIndex + i].buffers[0], preferredBufferSize);
|
||||||
outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[1], preferredBufferSize);
|
outputFormat[i].clear (bufferInfos[outputBufferIndex + i].buffers[1], preferredBufferSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1140,7 +1108,7 @@ private:
|
||||||
String getLastDriverError() const
|
String getLastDriverError() const
|
||||||
{
|
{
|
||||||
jassert (asioObject != nullptr);
|
jassert (asioObject != nullptr);
|
||||||
char buffer [512] = { 0 };
|
char buffer[512] = {};
|
||||||
asioObject->getErrorMessage (buffer);
|
asioObject->getErrorMessage (buffer);
|
||||||
return String (buffer, sizeof (buffer) - 1);
|
return String (buffer, sizeof (buffer) - 1);
|
||||||
}
|
}
|
||||||
|
|
@ -1163,7 +1131,7 @@ private:
|
||||||
|
|
||||||
if (driverError.isEmpty())
|
if (driverError.isEmpty())
|
||||||
{
|
{
|
||||||
char buffer [512];
|
char buffer[512] = {};
|
||||||
asioObject->getDriverName (buffer); // just in case any flimsy drivers expect this to be called..
|
asioObject->getDriverName (buffer); // just in case any flimsy drivers expect this to be called..
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1187,7 +1155,6 @@ private:
|
||||||
numActiveOutputChans = 0;
|
numActiveOutputChans = 0;
|
||||||
xruns = 0;
|
xruns = 0;
|
||||||
currentCallback = nullptr;
|
currentCallback = nullptr;
|
||||||
|
|
||||||
error.clear();
|
error.clear();
|
||||||
|
|
||||||
if (getName().isEmpty())
|
if (getName().isEmpty())
|
||||||
|
|
@ -1220,7 +1187,7 @@ private:
|
||||||
{
|
{
|
||||||
addBufferSizes (minBufferSize, maxBufferSize, preferredBufferSize, bufferGranularity);
|
addBufferSizes (minBufferSize, maxBufferSize, preferredBufferSize, bufferGranularity);
|
||||||
|
|
||||||
double currentRate = getSampleRate();
|
auto currentRate = getSampleRate();
|
||||||
|
|
||||||
if (currentRate < 1.0 || currentRate > 192001.0)
|
if (currentRate < 1.0 || currentRate > 192001.0)
|
||||||
{
|
{
|
||||||
|
|
@ -1232,13 +1199,12 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
currentSampleRate = currentRate;
|
currentSampleRate = currentRate;
|
||||||
|
|
||||||
postOutput = (asioObject->outputReady() == 0);
|
postOutput = (asioObject->outputReady() == 0);
|
||||||
|
|
||||||
if (postOutput)
|
if (postOutput)
|
||||||
JUCE_ASIO_LOG ("outputReady true");
|
JUCE_ASIO_LOG ("outputReady true");
|
||||||
|
|
||||||
updateSampleRates();
|
updateSampleRates();
|
||||||
|
|
||||||
readLatencies(); // ..doing these steps because cubase does so at this stage
|
readLatencies(); // ..doing these steps because cubase does so at this stage
|
||||||
createDummyBuffers (preferredBufferSize); // in initialisation, and some devices fail if we don't.
|
createDummyBuffers (preferredBufferSize); // in initialisation, and some devices fail if we don't.
|
||||||
readLatencies();
|
readLatencies();
|
||||||
|
|
@ -1294,12 +1260,11 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
void JUCE_ASIOCALLBACK callback (const long index)
|
void JUCE_ASIOCALLBACK callback (long index)
|
||||||
{
|
{
|
||||||
if (isStarted)
|
if (isStarted)
|
||||||
{
|
{
|
||||||
bufferIndex = index;
|
processBuffer (index);
|
||||||
processBuffer();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -1310,23 +1275,21 @@ private:
|
||||||
calledback = true;
|
calledback = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void processBuffer()
|
void processBuffer (long bufferIndex)
|
||||||
{
|
{
|
||||||
const ASIOBufferInfo* const infos = bufferInfos;
|
|
||||||
const int bi = bufferIndex;
|
|
||||||
|
|
||||||
const ScopedLock sl (callbackLock);
|
const ScopedLock sl (callbackLock);
|
||||||
|
|
||||||
if (bi >= 0)
|
if (bufferIndex >= 0)
|
||||||
{
|
{
|
||||||
const int samps = currentBlockSizeSamples;
|
auto* infos = bufferInfos.get();
|
||||||
|
auto samps = currentBlockSizeSamples;
|
||||||
|
|
||||||
if (currentCallback != nullptr)
|
if (currentCallback != nullptr)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < numActiveInputChans; ++i)
|
for (int i = 0; i < numActiveInputChans; ++i)
|
||||||
{
|
{
|
||||||
jassert (inBuffers[i] != nullptr);
|
jassert (inBuffers[i] != nullptr);
|
||||||
inputFormat[i].convertToFloat (infos[i].buffers[bi], inBuffers[i], samps);
|
inputFormat[i].convertToFloat (infos[i].buffers[bufferIndex], inBuffers[i], samps);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentCallback->audioDeviceIOCallback (const_cast<const float**> (inBuffers.getData()), numActiveInputChans,
|
currentCallback->audioDeviceIOCallback (const_cast<const float**> (inBuffers.getData()), numActiveInputChans,
|
||||||
|
|
@ -1335,13 +1298,13 @@ private:
|
||||||
for (int i = 0; i < numActiveOutputChans; ++i)
|
for (int i = 0; i < numActiveOutputChans; ++i)
|
||||||
{
|
{
|
||||||
jassert (outBuffers[i] != nullptr);
|
jassert (outBuffers[i] != nullptr);
|
||||||
outputFormat[i].convertFromFloat (outBuffers[i], infos [numActiveInputChans + i].buffers[bi], samps);
|
outputFormat[i].convertFromFloat (outBuffers[i], infos[numActiveInputChans + i].buffers[bufferIndex], samps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < numActiveOutputChans; ++i)
|
for (int i = 0; i < numActiveOutputChans; ++i)
|
||||||
outputFormat[i].clear (infos[numActiveInputChans + i].buffers[bi], samps);
|
outputFormat[i].clear (infos[numActiveInputChans + i].buffers[bufferIndex], samps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1359,15 +1322,15 @@ private:
|
||||||
return 1;
|
return 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case kAsioBufferSizeChange: JUCE_ASIO_LOG ("kAsioBufferSizeChange"); resetRequest(); return 1;
|
case kAsioBufferSizeChange: JUCE_ASIO_LOG ("kAsioBufferSizeChange"); resetRequest(); return 1;
|
||||||
case kAsioResetRequest: JUCE_ASIO_LOG ("kAsioResetRequest"); resetRequest(); return 1;
|
case kAsioResetRequest: JUCE_ASIO_LOG ("kAsioResetRequest"); resetRequest(); return 1;
|
||||||
case kAsioResyncRequest: JUCE_ASIO_LOG ("kAsioResyncRequest"); resetRequest(); return 1;
|
case kAsioResyncRequest: JUCE_ASIO_LOG ("kAsioResyncRequest"); resetRequest(); return 1;
|
||||||
case kAsioLatenciesChanged: JUCE_ASIO_LOG ("kAsioLatenciesChanged"); return 1;
|
case kAsioLatenciesChanged: JUCE_ASIO_LOG ("kAsioLatenciesChanged"); return 1;
|
||||||
case kAsioEngineVersion: return 2;
|
case kAsioEngineVersion: return 2;
|
||||||
|
|
||||||
case kAsioSupportsTimeInfo:
|
case kAsioSupportsTimeInfo:
|
||||||
case kAsioSupportsTimeCode: return 0;
|
case kAsioSupportsTimeCode: return 0;
|
||||||
case kAsioOverload: xruns++; return 1;
|
case kAsioOverload: ++xruns; return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1379,29 +1342,30 @@ private:
|
||||||
{
|
{
|
||||||
static ASIOTime* JUCE_ASIOCALLBACK bufferSwitchTimeInfoCallback (ASIOTime*, long index, long)
|
static ASIOTime* JUCE_ASIOCALLBACK bufferSwitchTimeInfoCallback (ASIOTime*, long index, long)
|
||||||
{
|
{
|
||||||
if (currentASIODev[deviceIndex] != nullptr)
|
if (auto* d = currentASIODev[deviceIndex])
|
||||||
currentASIODev[deviceIndex]->callback (index);
|
d->callback (index);
|
||||||
|
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void JUCE_ASIOCALLBACK bufferSwitchCallback (long index, long)
|
static void JUCE_ASIOCALLBACK bufferSwitchCallback (long index, long)
|
||||||
{
|
{
|
||||||
if (currentASIODev[deviceIndex] != nullptr)
|
if (auto* d = currentASIODev[deviceIndex])
|
||||||
currentASIODev[deviceIndex]->callback (index);
|
d->callback (index);
|
||||||
}
|
}
|
||||||
|
|
||||||
static long JUCE_ASIOCALLBACK asioMessagesCallback (long selector, long value, void*, double*)
|
static long JUCE_ASIOCALLBACK asioMessagesCallback (long selector, long value, void*, double*)
|
||||||
{
|
{
|
||||||
return currentASIODev[deviceIndex] != nullptr
|
if (auto* d = currentASIODev[deviceIndex])
|
||||||
? currentASIODev[deviceIndex]->asioMessagesCallback (selector, value)
|
return d->asioMessagesCallback (selector, value);
|
||||||
: 0;
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void JUCE_ASIOCALLBACK sampleRateChangedCallback (ASIOSampleRate)
|
static void JUCE_ASIOCALLBACK sampleRateChangedCallback (ASIOSampleRate)
|
||||||
{
|
{
|
||||||
if (currentASIODev[deviceIndex] != nullptr)
|
if (auto* d = currentASIODev[deviceIndex])
|
||||||
currentASIODev[deviceIndex]->resetRequest();
|
d->resetRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setCallbacks (ASIOCallbacks& callbacks) noexcept
|
static void setCallbacks (ASIOCallbacks& callbacks) noexcept
|
||||||
|
|
@ -1430,7 +1394,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct ASIOAudioIODevice::ASIOCallbackFunctions <sizeof(currentASIODev) / sizeof(currentASIODev[0])>
|
struct ASIOAudioIODevice::ASIOCallbackFunctions<maxNumASIODevices>
|
||||||
{
|
{
|
||||||
static void setCallbacksForDevice (ASIOCallbacks&, ASIOAudioIODevice*) noexcept {}
|
static void setCallbacksForDevice (ASIOCallbacks&, ASIOAudioIODevice*) noexcept {}
|
||||||
};
|
};
|
||||||
|
|
@ -1442,10 +1406,9 @@ public:
|
||||||
ASIOAudioIODeviceType() : AudioIODeviceType ("ASIO") {}
|
ASIOAudioIODeviceType() : AudioIODeviceType ("ASIO") {}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
void scanForDevices()
|
void scanForDevices() override
|
||||||
{
|
{
|
||||||
hasScanned = true;
|
hasScanned = true;
|
||||||
|
|
||||||
deviceNames.clear();
|
deviceNames.clear();
|
||||||
classIds.clear();
|
classIds.clear();
|
||||||
|
|
||||||
|
|
@ -1454,7 +1417,7 @@ public:
|
||||||
|
|
||||||
if (RegOpenKey (HKEY_LOCAL_MACHINE, _T("software\\asio"), &hk) == ERROR_SUCCESS)
|
if (RegOpenKey (HKEY_LOCAL_MACHINE, _T("software\\asio"), &hk) == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
TCHAR name [256];
|
TCHAR name[256] = {};
|
||||||
|
|
||||||
while (RegEnumKey (hk, index++, name, numElementsInArray (name)) == ERROR_SUCCESS)
|
while (RegEnumKey (hk, index++, name, numElementsInArray (name)) == ERROR_SUCCESS)
|
||||||
addDriverInfo (name, hk);
|
addDriverInfo (name, hk);
|
||||||
|
|
@ -1463,14 +1426,13 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StringArray getDeviceNames (bool /*wantInputNames*/) const
|
StringArray getDeviceNames (bool /*wantInputNames*/) const override
|
||||||
{
|
{
|
||||||
jassert (hasScanned); // need to call scanForDevices() before doing this
|
jassert (hasScanned); // need to call scanForDevices() before doing this
|
||||||
|
|
||||||
return deviceNames;
|
return deviceNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getDefaultDeviceIndex (bool) const
|
int getDefaultDeviceIndex (bool) const override
|
||||||
{
|
{
|
||||||
jassert (hasScanned); // need to call scanForDevices() before doing this
|
jassert (hasScanned); // need to call scanForDevices() before doing this
|
||||||
|
|
||||||
|
|
@ -1489,8 +1451,8 @@ public:
|
||||||
|
|
||||||
static int findFreeSlot()
|
static int findFreeSlot()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < numElementsInArray (currentASIODev); ++i)
|
for (int i = 0; i < maxNumASIODevices; ++i)
|
||||||
if (currentASIODev[i] == 0)
|
if (currentASIODev[i] == nullptr)
|
||||||
return i;
|
return i;
|
||||||
|
|
||||||
jassertfalse; // unfortunately you can only have a finite number
|
jassertfalse; // unfortunately you can only have a finite number
|
||||||
|
|
@ -1498,29 +1460,29 @@ public:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getIndexOfDevice (AudioIODevice* d, bool /*asInput*/) const
|
int getIndexOfDevice (AudioIODevice* d, bool /*asInput*/) const override
|
||||||
{
|
{
|
||||||
jassert (hasScanned); // need to call scanForDevices() before doing this
|
jassert (hasScanned); // need to call scanForDevices() before doing this
|
||||||
|
|
||||||
return d == nullptr ? -1 : deviceNames.indexOf (d->getName());
|
return d == nullptr ? -1 : deviceNames.indexOf (d->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasSeparateInputsAndOutputs() const { return false; }
|
bool hasSeparateInputsAndOutputs() const override { return false; }
|
||||||
|
|
||||||
AudioIODevice* createDevice (const String& outputDeviceName,
|
AudioIODevice* createDevice (const String& outputDeviceName,
|
||||||
const String& inputDeviceName)
|
const String& inputDeviceName) override
|
||||||
{
|
{
|
||||||
// ASIO can't open two different devices for input and output - they must be the same one.
|
// ASIO can't open two different devices for input and output - they must be the same one.
|
||||||
jassert (inputDeviceName == outputDeviceName || outputDeviceName.isEmpty() || inputDeviceName.isEmpty());
|
jassert (inputDeviceName == outputDeviceName || outputDeviceName.isEmpty() || inputDeviceName.isEmpty());
|
||||||
jassert (hasScanned); // need to call scanForDevices() before doing this
|
jassert (hasScanned); // need to call scanForDevices() before doing this
|
||||||
|
|
||||||
const String deviceName (outputDeviceName.isNotEmpty() ? outputDeviceName
|
auto deviceName = outputDeviceName.isNotEmpty() ? outputDeviceName
|
||||||
: inputDeviceName);
|
: inputDeviceName;
|
||||||
const int index = deviceNames.indexOf (deviceName);
|
auto index = deviceNames.indexOf (deviceName);
|
||||||
|
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
{
|
{
|
||||||
const int freeSlot = findFreeSlot();
|
auto freeSlot = findFreeSlot();
|
||||||
|
|
||||||
if (freeSlot >= 0)
|
if (freeSlot >= 0)
|
||||||
return new ASIOAudioIODevice (this, deviceName,
|
return new ASIOAudioIODevice (this, deviceName,
|
||||||
|
|
@ -1552,7 +1514,7 @@ private:
|
||||||
if (RegOpenKey (HKEY_CLASSES_ROOT, _T("clsid"), &hk) == ERROR_SUCCESS)
|
if (RegOpenKey (HKEY_CLASSES_ROOT, _T("clsid"), &hk) == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
int index = 0;
|
int index = 0;
|
||||||
TCHAR name [512];
|
TCHAR name[512] = {};
|
||||||
|
|
||||||
while (RegEnumKey (hk, index++, name, numElementsInArray (name)) == ERROR_SUCCESS)
|
while (RegEnumKey (hk, index++, name, numElementsInArray (name)) == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
|
|
@ -1564,7 +1526,7 @@ private:
|
||||||
{
|
{
|
||||||
if (RegOpenKeyEx (subKey, _T("InprocServer32"), 0, KEY_READ, &pathKey) == ERROR_SUCCESS)
|
if (RegOpenKeyEx (subKey, _T("InprocServer32"), 0, KEY_READ, &pathKey) == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
TCHAR pathName [1024] = { 0 };
|
TCHAR pathName[1024] = {};
|
||||||
DWORD dtype = REG_SZ;
|
DWORD dtype = REG_SZ;
|
||||||
DWORD dsize = sizeof (pathName);
|
DWORD dsize = sizeof (pathName);
|
||||||
|
|
||||||
|
|
@ -1596,7 +1558,7 @@ private:
|
||||||
|
|
||||||
if (RegOpenKeyEx (hk, keyName.toWideCharPointer(), 0, KEY_READ, &subKey) == ERROR_SUCCESS)
|
if (RegOpenKeyEx (hk, keyName.toWideCharPointer(), 0, KEY_READ, &subKey) == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
TCHAR buf [256] = { 0 };
|
TCHAR buf[256] = {};
|
||||||
DWORD dtype = REG_SZ;
|
DWORD dtype = REG_SZ;
|
||||||
DWORD dsize = sizeof (buf);
|
DWORD dsize = sizeof (buf);
|
||||||
|
|
||||||
|
|
@ -1605,6 +1567,7 @@ private:
|
||||||
if (dsize > 0 && checkClassIsOk (buf))
|
if (dsize > 0 && checkClassIsOk (buf))
|
||||||
{
|
{
|
||||||
CLSID classId;
|
CLSID classId;
|
||||||
|
|
||||||
if (CLSIDFromString ((LPOLESTR) buf, &classId) == S_OK)
|
if (CLSIDFromString ((LPOLESTR) buf, &classId) == S_OK)
|
||||||
{
|
{
|
||||||
dtype = REG_SZ;
|
dtype = REG_SZ;
|
||||||
|
|
|
||||||
|
|
@ -192,7 +192,7 @@ JUCE_IUNKNOWNCLASS (IMMDeviceEnumerator, "A95664D2-9614-4F35-A746-DE8DB63617E6")
|
||||||
|
|
||||||
JUCE_COMCLASS (MMDeviceEnumerator, "BCDE0395-E52F-467C-8E3D-C4579291692E");
|
JUCE_COMCLASS (MMDeviceEnumerator, "BCDE0395-E52F-467C-8E3D-C4579291692E");
|
||||||
|
|
||||||
typedef LONGLONG REFERENCE_TIME;
|
using REFERENCE_TIME = LONGLONG;
|
||||||
|
|
||||||
enum AVRT_PRIORITY
|
enum AVRT_PRIORITY
|
||||||
{
|
{
|
||||||
|
|
@ -308,7 +308,7 @@ JUCE_IUNKNOWNCLASS (IAudioSessionControl, "F4B1A599-7266-4319-A8CA-E70ACB11E8CD"
|
||||||
namespace WasapiClasses
|
namespace WasapiClasses
|
||||||
{
|
{
|
||||||
|
|
||||||
String getDeviceID (IMMDevice* const device)
|
String getDeviceID (IMMDevice* device)
|
||||||
{
|
{
|
||||||
String s;
|
String s;
|
||||||
WCHAR* deviceId = nullptr;
|
WCHAR* deviceId = nullptr;
|
||||||
|
|
@ -332,17 +332,17 @@ EDataFlow getDataFlow (const ComSmartPtr<IMMDevice>& device)
|
||||||
return flow;
|
return flow;
|
||||||
}
|
}
|
||||||
|
|
||||||
int refTimeToSamples (const REFERENCE_TIME& t, const double sampleRate) noexcept
|
int refTimeToSamples (const REFERENCE_TIME& t, double sampleRate) noexcept
|
||||||
{
|
{
|
||||||
return roundToInt (sampleRate * ((double) t) * 0.0000001);
|
return roundToInt (sampleRate * ((double) t) * 0.0000001);
|
||||||
}
|
}
|
||||||
|
|
||||||
REFERENCE_TIME samplesToRefTime (const int numSamples, const double sampleRate) noexcept
|
REFERENCE_TIME samplesToRefTime (int numSamples, double sampleRate) noexcept
|
||||||
{
|
{
|
||||||
return (REFERENCE_TIME) ((numSamples * 10000.0 * 1000.0 / sampleRate) + 0.5);
|
return (REFERENCE_TIME) ((numSamples * 10000.0 * 1000.0 / sampleRate) + 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* const src) noexcept
|
void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* src) noexcept
|
||||||
{
|
{
|
||||||
memcpy (&dest, src, src->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? sizeof (WAVEFORMATEXTENSIBLE)
|
memcpy (&dest, src, src->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? sizeof (WAVEFORMATEXTENSIBLE)
|
||||||
: sizeof (WAVEFORMATEX));
|
: sizeof (WAVEFORMATEX));
|
||||||
|
|
@ -352,21 +352,8 @@ void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* const src) n
|
||||||
class WASAPIDeviceBase
|
class WASAPIDeviceBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WASAPIDeviceBase (const ComSmartPtr<IMMDevice>& d, const bool exclusiveMode)
|
WASAPIDeviceBase (const ComSmartPtr<IMMDevice>& d, bool exclusiveMode)
|
||||||
: device (d),
|
: device (d), useExclusiveMode (exclusiveMode)
|
||||||
sampleRate (0),
|
|
||||||
defaultSampleRate (0),
|
|
||||||
numChannels (0),
|
|
||||||
actualNumChannels (0),
|
|
||||||
minBufferSize (0),
|
|
||||||
defaultBufferSize (0),
|
|
||||||
latencySamples (0),
|
|
||||||
useExclusiveMode (exclusiveMode),
|
|
||||||
actualBufferSize (0),
|
|
||||||
bytesPerSample (0),
|
|
||||||
bytesPerFrame (0),
|
|
||||||
sampleRateHasChanged (false),
|
|
||||||
shouldClose (false)
|
|
||||||
{
|
{
|
||||||
clientEvent = CreateEvent (nullptr, false, false, nullptr);
|
clientEvent = CreateEvent (nullptr, false, false, nullptr);
|
||||||
|
|
||||||
|
|
@ -443,20 +430,19 @@ public:
|
||||||
{
|
{
|
||||||
sampleRateHasChanged = false;
|
sampleRateHasChanged = false;
|
||||||
shouldClose = false;
|
shouldClose = false;
|
||||||
|
|
||||||
channelMaps.clear();
|
channelMaps.clear();
|
||||||
|
|
||||||
for (int i = 0; i <= channels.getHighestBit(); ++i)
|
for (int i = 0; i <= channels.getHighestBit(); ++i)
|
||||||
if (channels[i])
|
if (channels[i])
|
||||||
channelMaps.add (i);
|
channelMaps.add (i);
|
||||||
|
|
||||||
REFERENCE_TIME latency;
|
REFERENCE_TIME latency;
|
||||||
|
|
||||||
if (check (client->GetStreamLatency (&latency)))
|
if (check (client->GetStreamLatency (&latency)))
|
||||||
latencySamples = refTimeToSamples (latency, sampleRate);
|
latencySamples = refTimeToSamples (latency, sampleRate);
|
||||||
|
|
||||||
(void) check (client->GetBufferSize (&actualBufferSize));
|
(void) check (client->GetBufferSize (&actualBufferSize));
|
||||||
|
|
||||||
createSessionEventCallback();
|
createSessionEventCallback();
|
||||||
|
|
||||||
return check (client->SetEventHandle (clientEvent));
|
return check (client->SetEventHandle (clientEvent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -486,26 +472,25 @@ public:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
ComSmartPtr<IMMDevice> device;
|
ComSmartPtr<IMMDevice> device;
|
||||||
ComSmartPtr<IAudioClient> client;
|
ComSmartPtr<IAudioClient> client;
|
||||||
double sampleRate, defaultSampleRate;
|
double sampleRate = 0, defaultSampleRate = 0;
|
||||||
int numChannels, actualNumChannels;
|
int numChannels = 0, actualNumChannels = 0;
|
||||||
int minBufferSize, defaultBufferSize, latencySamples;
|
int minBufferSize = 0, defaultBufferSize = 0, latencySamples = 0;
|
||||||
DWORD mixFormatChannelMask;
|
DWORD mixFormatChannelMask = 0;
|
||||||
const bool useExclusiveMode;
|
const bool useExclusiveMode;
|
||||||
Array<double> rates;
|
Array<double> rates;
|
||||||
HANDLE clientEvent;
|
HANDLE clientEvent = {};
|
||||||
BigInteger channels;
|
BigInteger channels;
|
||||||
Array<int> channelMaps;
|
Array<int> channelMaps;
|
||||||
UINT32 actualBufferSize;
|
UINT32 actualBufferSize = 0;
|
||||||
int bytesPerSample, bytesPerFrame;
|
int bytesPerSample = 0, bytesPerFrame = 0;
|
||||||
bool sampleRateHasChanged, shouldClose;
|
bool sampleRateHasChanged = false, shouldClose = false;
|
||||||
|
|
||||||
virtual void updateFormat (bool isFloat) = 0;
|
virtual void updateFormat (bool isFloat) = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
class SessionEventCallback : public ComBaseClassHelper<IAudioSessionEvents>
|
struct SessionEventCallback : public ComBaseClassHelper<IAudioSessionEvents>
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
SessionEventCallback (WASAPIDeviceBase& d) : owner (d) {}
|
SessionEventCallback (WASAPIDeviceBase& d) : owner (d) {}
|
||||||
|
|
||||||
JUCE_COMRESULT OnDisplayNameChanged (LPCWSTR, LPCGUID) { return S_OK; }
|
JUCE_COMRESULT OnDisplayNameChanged (LPCWSTR, LPCGUID) { return S_OK; }
|
||||||
|
|
@ -513,6 +498,7 @@ private:
|
||||||
JUCE_COMRESULT OnSimpleVolumeChanged (float, BOOL, LPCGUID) { return S_OK; }
|
JUCE_COMRESULT OnSimpleVolumeChanged (float, BOOL, LPCGUID) { return S_OK; }
|
||||||
JUCE_COMRESULT OnChannelVolumeChanged (DWORD, float*, DWORD, LPCGUID) { return S_OK; }
|
JUCE_COMRESULT OnChannelVolumeChanged (DWORD, float*, DWORD, LPCGUID) { return S_OK; }
|
||||||
JUCE_COMRESULT OnGroupingParamChanged (LPCGUID, LPCGUID) { return S_OK; }
|
JUCE_COMRESULT OnGroupingParamChanged (LPCGUID, LPCGUID) { return S_OK; }
|
||||||
|
|
||||||
JUCE_COMRESULT OnStateChanged(AudioSessionState state)
|
JUCE_COMRESULT OnStateChanged(AudioSessionState state)
|
||||||
{
|
{
|
||||||
if (state == AudioSessionStateInactive || state == AudioSessionStateExpired)
|
if (state == AudioSessionStateInactive || state == AudioSessionStateExpired)
|
||||||
|
|
@ -530,9 +516,7 @@ private:
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
WASAPIDeviceBase& owner;
|
WASAPIDeviceBase& owner;
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SessionEventCallback)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SessionEventCallback)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -644,7 +628,7 @@ private:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tryInitialisingWithBufferSize (const int bufferSizeSamples)
|
bool tryInitialisingWithBufferSize (int bufferSizeSamples)
|
||||||
{
|
{
|
||||||
WAVEFORMATEXTENSIBLE format;
|
WAVEFORMATEXTENSIBLE format;
|
||||||
|
|
||||||
|
|
@ -701,9 +685,8 @@ private:
|
||||||
class WASAPIInputDevice : public WASAPIDeviceBase
|
class WASAPIInputDevice : public WASAPIDeviceBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WASAPIInputDevice (const ComSmartPtr<IMMDevice>& d, const bool exclusiveMode)
|
WASAPIInputDevice (const ComSmartPtr<IMMDevice>& d, bool exclusiveMode)
|
||||||
: WASAPIDeviceBase (d, exclusiveMode),
|
: WASAPIDeviceBase (d, exclusiveMode)
|
||||||
reservoir (1, 1)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -712,7 +695,7 @@ public:
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool open (const double newSampleRate, const BigInteger& newChannels, int bufferSizeSamples)
|
bool open (double newSampleRate, const BigInteger& newChannels, int bufferSizeSamples)
|
||||||
{
|
{
|
||||||
return openClient (newSampleRate, newChannels, bufferSizeSamples)
|
return openClient (newSampleRate, newChannels, bufferSizeSamples)
|
||||||
&& (numChannels == 0 || check (client->GetService (__uuidof (IAudioCaptureClient),
|
&& (numChannels == 0 || check (client->GetService (__uuidof (IAudioCaptureClient),
|
||||||
|
|
@ -724,13 +707,14 @@ public:
|
||||||
closeClient();
|
closeClient();
|
||||||
captureClient = nullptr;
|
captureClient = nullptr;
|
||||||
reservoir.reset();
|
reservoir.reset();
|
||||||
reservoirReadPos = reservoirWritePos = 0;
|
reservoirReadPos = 0;
|
||||||
|
reservoirWritePos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class SourceType>
|
template<class SourceType>
|
||||||
void updateFormatWithType (SourceType*) noexcept
|
void updateFormatWithType (SourceType*) noexcept
|
||||||
{
|
{
|
||||||
typedef AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> NativeType;
|
using NativeType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst>;
|
||||||
converter.reset (new AudioData::ConverterInstance<AudioData::Pointer<SourceType, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const>, NativeType> (actualNumChannels, 1));
|
converter.reset (new AudioData::ConverterInstance<AudioData::Pointer<SourceType, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const>, NativeType> (actualNumChannels, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -742,12 +726,13 @@ public:
|
||||||
else updateFormatWithType ((AudioData::Int16*) nullptr);
|
else updateFormatWithType ((AudioData::Int16*) nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool start (const int userBufferSize)
|
bool start (int userBufferSize)
|
||||||
{
|
{
|
||||||
reservoirSize = actualBufferSize + userBufferSize;
|
reservoirSize = actualBufferSize + userBufferSize;
|
||||||
reservoirMask = nextPowerOfTwo (reservoirSize) - 1;
|
reservoirMask = nextPowerOfTwo (reservoirSize) - 1;
|
||||||
reservoir.setSize ((reservoirMask + 1) * bytesPerFrame, true);
|
reservoir.setSize ((reservoirMask + 1) * bytesPerFrame, true);
|
||||||
reservoirReadPos = reservoirWritePos = 0;
|
reservoirReadPos = 0;
|
||||||
|
reservoirWritePos = 0;
|
||||||
xruns = 0;
|
xruns = 0;
|
||||||
|
|
||||||
if (! check (client->Start()))
|
if (! check (client->Start()))
|
||||||
|
|
@ -768,7 +753,7 @@ public:
|
||||||
captureClient->ReleaseBuffer (numSamplesAvailable);
|
captureClient->ReleaseBuffer (numSamplesAvailable);
|
||||||
}
|
}
|
||||||
|
|
||||||
int getNumSamplesInReservoir() const noexcept { return reservoirWritePos - reservoirReadPos; }
|
int getNumSamplesInReservoir() const noexcept { return reservoirWritePos.load() - reservoirReadPos.load(); }
|
||||||
|
|
||||||
void handleDeviceBuffer()
|
void handleDeviceBuffer()
|
||||||
{
|
{
|
||||||
|
|
@ -788,9 +773,9 @@ public:
|
||||||
|
|
||||||
while (samplesLeft > 0)
|
while (samplesLeft > 0)
|
||||||
{
|
{
|
||||||
const int localWrite = reservoirWritePos & reservoirMask;
|
auto localWrite = reservoirWritePos.load() & reservoirMask;
|
||||||
const int samplesToDo = jmin (samplesLeft, reservoirMask + 1 - localWrite);
|
auto samplesToDo = jmin (samplesLeft, reservoirMask + 1 - localWrite);
|
||||||
const int samplesToDoBytes = samplesToDo * bytesPerFrame;
|
auto samplesToDoBytes = samplesToDo * bytesPerFrame;
|
||||||
|
|
||||||
void* reservoirPtr = addBytesToPointer (reservoir.getData(), localWrite * bytesPerFrame);
|
void* reservoirPtr = addBytesToPointer (reservoir.getData(), localWrite * bytesPerFrame);
|
||||||
|
|
||||||
|
|
@ -805,7 +790,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getNumSamplesInReservoir() > reservoirSize)
|
if (getNumSamplesInReservoir() > reservoirSize)
|
||||||
reservoirReadPos = reservoirWritePos - reservoirSize;
|
reservoirReadPos = reservoirWritePos.load() - reservoirSize;
|
||||||
|
|
||||||
captureClient->ReleaseBuffer (numSamplesAvailable);
|
captureClient->ReleaseBuffer (numSamplesAvailable);
|
||||||
}
|
}
|
||||||
|
|
@ -829,13 +814,13 @@ public:
|
||||||
|
|
||||||
while (bufferSize > 0)
|
while (bufferSize > 0)
|
||||||
{
|
{
|
||||||
const int localRead = reservoirReadPos & reservoirMask;
|
auto localRead = reservoirReadPos.load() & reservoirMask;
|
||||||
|
auto samplesToDo = jmin (bufferSize, getNumSamplesInReservoir(), reservoirMask + 1 - localRead);
|
||||||
|
|
||||||
const int samplesToDo = jmin (bufferSize, getNumSamplesInReservoir(), reservoirMask + 1 - localRead);
|
|
||||||
if (samplesToDo <= 0)
|
if (samplesToDo <= 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
const int reservoirOffset = localRead * bytesPerFrame;
|
auto reservoirOffset = localRead * bytesPerFrame;
|
||||||
|
|
||||||
for (int i = 0; i < numDestBuffers; ++i)
|
for (int i = 0; i < numDestBuffers; ++i)
|
||||||
converter->convertSamples (destBuffers[i] + offset, 0, addBytesToPointer (reservoir.getData(), reservoirOffset), channelMaps.getUnchecked(i), samplesToDo);
|
converter->convertSamples (destBuffers[i] + offset, 0, addBytesToPointer (reservoir.getData(), reservoirOffset), channelMaps.getUnchecked(i), samplesToDo);
|
||||||
|
|
@ -849,7 +834,7 @@ public:
|
||||||
ComSmartPtr<IAudioCaptureClient> captureClient;
|
ComSmartPtr<IAudioCaptureClient> captureClient;
|
||||||
MemoryBlock reservoir;
|
MemoryBlock reservoir;
|
||||||
int reservoirSize, reservoirMask, xruns;
|
int reservoirSize, reservoirMask, xruns;
|
||||||
volatile int reservoirReadPos, reservoirWritePos;
|
std::atomic<int> reservoirReadPos, reservoirWritePos;
|
||||||
|
|
||||||
std::unique_ptr<AudioData::Converter> converter;
|
std::unique_ptr<AudioData::Converter> converter;
|
||||||
|
|
||||||
|
|
@ -861,7 +846,7 @@ private:
|
||||||
class WASAPIOutputDevice : public WASAPIDeviceBase
|
class WASAPIOutputDevice : public WASAPIDeviceBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WASAPIOutputDevice (const ComSmartPtr<IMMDevice>& d, const bool exclusiveMode)
|
WASAPIOutputDevice (const ComSmartPtr<IMMDevice>& d, bool exclusiveMode)
|
||||||
: WASAPIDeviceBase (d, exclusiveMode)
|
: WASAPIDeviceBase (d, exclusiveMode)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -871,7 +856,7 @@ public:
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool open (const double newSampleRate, const BigInteger& newChannels, int bufferSizeSamples)
|
bool open (double newSampleRate, const BigInteger& newChannels, int bufferSizeSamples)
|
||||||
{
|
{
|
||||||
return openClient (newSampleRate, newChannels, bufferSizeSamples)
|
return openClient (newSampleRate, newChannels, bufferSizeSamples)
|
||||||
&& (numChannels == 0 || check (client->GetService (__uuidof (IAudioRenderClient),
|
&& (numChannels == 0 || check (client->GetService (__uuidof (IAudioRenderClient),
|
||||||
|
|
@ -887,7 +872,7 @@ public:
|
||||||
template<class DestType>
|
template<class DestType>
|
||||||
void updateFormatWithType (DestType*)
|
void updateFormatWithType (DestType*)
|
||||||
{
|
{
|
||||||
typedef AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> NativeType;
|
using NativeType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const>;
|
||||||
converter.reset (new AudioData::ConverterInstance<NativeType, AudioData::Pointer<DestType, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst>> (1, actualNumChannels));
|
converter.reset (new AudioData::ConverterInstance<NativeType, AudioData::Pointer<DestType, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst>> (1, actualNumChannels));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -901,7 +886,7 @@ public:
|
||||||
|
|
||||||
bool start()
|
bool start()
|
||||||
{
|
{
|
||||||
int samplesToDo = getNumSamplesAvailableToCopy();
|
auto samplesToDo = getNumSamplesAvailableToCopy();
|
||||||
uint8* outputData;
|
uint8* outputData;
|
||||||
|
|
||||||
if (check (renderClient->GetBuffer (samplesToDo, &outputData)))
|
if (check (renderClient->GetBuffer (samplesToDo, &outputData)))
|
||||||
|
|
@ -918,6 +903,7 @@ public:
|
||||||
if (! useExclusiveMode)
|
if (! useExclusiveMode)
|
||||||
{
|
{
|
||||||
UINT32 padding = 0;
|
UINT32 padding = 0;
|
||||||
|
|
||||||
if (check (client->GetCurrentPadding (&padding)))
|
if (check (client->GetCurrentPadding (&padding)))
|
||||||
return actualBufferSize - (int) padding;
|
return actualBufferSize - (int) padding;
|
||||||
}
|
}
|
||||||
|
|
@ -925,7 +911,7 @@ public:
|
||||||
return actualBufferSize;
|
return actualBufferSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void copyBuffers (const float** const srcBuffers, const int numSrcBuffers, int bufferSize,
|
void copyBuffers (const float** srcBuffers, int numSrcBuffers, int bufferSize,
|
||||||
WASAPIInputDevice* inputDevice, Thread& thread)
|
WASAPIInputDevice* inputDevice, Thread& thread)
|
||||||
{
|
{
|
||||||
if (numChannels <= 0)
|
if (numChannels <= 0)
|
||||||
|
|
@ -985,18 +971,12 @@ public:
|
||||||
const String& typeName,
|
const String& typeName,
|
||||||
const String& outputDeviceID,
|
const String& outputDeviceID,
|
||||||
const String& inputDeviceID,
|
const String& inputDeviceID,
|
||||||
const bool exclusiveMode)
|
bool exclusiveMode)
|
||||||
: AudioIODevice (deviceName, typeName),
|
: AudioIODevice (deviceName, typeName),
|
||||||
Thread ("JUCE WASAPI"),
|
Thread ("JUCE WASAPI"),
|
||||||
outputDeviceId (outputDeviceID),
|
outputDeviceId (outputDeviceID),
|
||||||
inputDeviceId (inputDeviceID),
|
inputDeviceId (inputDeviceID),
|
||||||
useExclusiveMode (exclusiveMode),
|
useExclusiveMode (exclusiveMode)
|
||||||
isOpen_ (false),
|
|
||||||
isStarted (false),
|
|
||||||
currentBufferSizeSamples (0),
|
|
||||||
currentSampleRate (0),
|
|
||||||
callback (nullptr),
|
|
||||||
deviceBecameInactive (false)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1085,7 +1065,7 @@ public:
|
||||||
BigInteger getActiveOutputChannels() const override { return outputDevice != nullptr ? outputDevice->channels : BigInteger(); }
|
BigInteger getActiveOutputChannels() const override { return outputDevice != nullptr ? outputDevice->channels : BigInteger(); }
|
||||||
BigInteger getActiveInputChannels() const override { return inputDevice != nullptr ? inputDevice->channels : BigInteger(); }
|
BigInteger getActiveInputChannels() const override { return inputDevice != nullptr ? inputDevice->channels : BigInteger(); }
|
||||||
String getLastError() override { return lastError; }
|
String getLastError() override { return lastError; }
|
||||||
int getXRunCount () const noexcept override { return inputDevice != nullptr ? inputDevice->xruns : -1; }
|
int getXRunCount() const noexcept override { return inputDevice != nullptr ? inputDevice->xruns : -1; }
|
||||||
|
|
||||||
String open (const BigInteger& inputChannels, const BigInteger& outputChannels,
|
String open (const BigInteger& inputChannels, const BigInteger& outputChannels,
|
||||||
double sampleRate, int bufferSizeSamples) override
|
double sampleRate, int bufferSizeSamples) override
|
||||||
|
|
@ -1209,7 +1189,7 @@ public:
|
||||||
{
|
{
|
||||||
if (isStarted)
|
if (isStarted)
|
||||||
{
|
{
|
||||||
AudioIODeviceCallback* const callbackLocal = callback;
|
auto* callbackLocal = callback;
|
||||||
|
|
||||||
{
|
{
|
||||||
const ScopedLock sl (startStopLock);
|
const ScopedLock sl (startStopLock);
|
||||||
|
|
@ -1230,9 +1210,8 @@ public:
|
||||||
if (avSetMmThreadCharacteristics != 0 && avSetMmThreadPriority != 0)
|
if (avSetMmThreadCharacteristics != 0 && avSetMmThreadPriority != 0)
|
||||||
{
|
{
|
||||||
DWORD dummy = 0;
|
DWORD dummy = 0;
|
||||||
HANDLE h = avSetMmThreadCharacteristics (L"Pro Audio", &dummy);
|
|
||||||
|
|
||||||
if (h != 0)
|
if (auto h = avSetMmThreadCharacteristics (L"Pro Audio", &dummy))
|
||||||
avSetMmThreadPriority (h, AVRT_PRIORITY_NORMAL);
|
avSetMmThreadPriority (h, AVRT_PRIORITY_NORMAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1241,15 +1220,15 @@ public:
|
||||||
{
|
{
|
||||||
setMMThreadPriority();
|
setMMThreadPriority();
|
||||||
|
|
||||||
const int bufferSize = currentBufferSizeSamples;
|
auto bufferSize = currentBufferSizeSamples;
|
||||||
const int numInputBuffers = getActiveInputChannels().countNumberOfSetBits();
|
auto numInputBuffers = getActiveInputChannels().countNumberOfSetBits();
|
||||||
const int numOutputBuffers = getActiveOutputChannels().countNumberOfSetBits();
|
auto numOutputBuffers = getActiveOutputChannels().countNumberOfSetBits();
|
||||||
bool sampleRateHasChanged = false;
|
bool sampleRateHasChanged = false;
|
||||||
|
|
||||||
AudioBuffer<float> ins (jmax (1, numInputBuffers), bufferSize + 32);
|
AudioBuffer<float> ins (jmax (1, numInputBuffers), bufferSize + 32);
|
||||||
AudioBuffer<float> outs (jmax (1, numOutputBuffers), bufferSize + 32);
|
AudioBuffer<float> outs (jmax (1, numOutputBuffers), bufferSize + 32);
|
||||||
float** const inputBuffers = ins.getArrayOfWritePointers();
|
auto inputBuffers = ins.getArrayOfWritePointers();
|
||||||
float** const outputBuffers = outs.getArrayOfWritePointers();
|
auto outputBuffers = outs.getArrayOfWritePointers();
|
||||||
ins.clear();
|
ins.clear();
|
||||||
outs.clear();
|
outs.clear();
|
||||||
|
|
||||||
|
|
@ -1329,21 +1308,21 @@ private:
|
||||||
std::unique_ptr<WASAPIInputDevice> inputDevice;
|
std::unique_ptr<WASAPIInputDevice> inputDevice;
|
||||||
std::unique_ptr<WASAPIOutputDevice> outputDevice;
|
std::unique_ptr<WASAPIOutputDevice> outputDevice;
|
||||||
const bool useExclusiveMode;
|
const bool useExclusiveMode;
|
||||||
double defaultSampleRate;
|
double defaultSampleRate = 0;
|
||||||
int minBufferSize, defaultBufferSize;
|
int minBufferSize = 0, defaultBufferSize = 0;
|
||||||
int latencyIn, latencyOut;
|
int latencyIn = 0, latencyOut = 0;
|
||||||
Array<double> sampleRates;
|
Array<double> sampleRates;
|
||||||
Array<int> bufferSizes;
|
Array<int> bufferSizes;
|
||||||
|
|
||||||
// Active state...
|
// Active state...
|
||||||
bool isOpen_, isStarted;
|
bool isOpen_ = false, isStarted = false;
|
||||||
int currentBufferSizeSamples;
|
int currentBufferSizeSamples = 0;
|
||||||
double currentSampleRate;
|
double currentSampleRate = 0;
|
||||||
|
|
||||||
AudioIODeviceCallback* callback;
|
AudioIODeviceCallback* callback = {};
|
||||||
CriticalSection startStopLock;
|
CriticalSection startStopLock;
|
||||||
|
|
||||||
bool sampleRateChangedByOutput, deviceBecameInactive;
|
bool sampleRateChangedByOutput = false, deviceBecameInactive = false;
|
||||||
|
|
||||||
BigInteger lastKnownInputChannels, lastKnownOutputChannels;
|
BigInteger lastKnownInputChannels, lastKnownOutputChannels;
|
||||||
|
|
||||||
|
|
@ -1351,28 +1330,33 @@ private:
|
||||||
bool createDevices()
|
bool createDevices()
|
||||||
{
|
{
|
||||||
ComSmartPtr<IMMDeviceEnumerator> enumerator;
|
ComSmartPtr<IMMDeviceEnumerator> enumerator;
|
||||||
|
|
||||||
if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
|
if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ComSmartPtr<IMMDeviceCollection> deviceCollection;
|
ComSmartPtr<IMMDeviceCollection> deviceCollection;
|
||||||
|
|
||||||
if (! check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress())))
|
if (! check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress())))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
UINT32 numDevices = 0;
|
UINT32 numDevices = 0;
|
||||||
|
|
||||||
if (! check (deviceCollection->GetCount (&numDevices)))
|
if (! check (deviceCollection->GetCount (&numDevices)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (UINT32 i = 0; i < numDevices; ++i)
|
for (UINT32 i = 0; i < numDevices; ++i)
|
||||||
{
|
{
|
||||||
ComSmartPtr<IMMDevice> device;
|
ComSmartPtr<IMMDevice> device;
|
||||||
|
|
||||||
if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress())))
|
if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress())))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const String deviceId (getDeviceID (device));
|
auto deviceId = getDeviceID (device);
|
||||||
|
|
||||||
if (deviceId.isEmpty())
|
if (deviceId.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const EDataFlow flow = getDataFlow (device);
|
auto flow = getDataFlow (device);
|
||||||
|
|
||||||
if (deviceId == inputDeviceId && flow == eCapture)
|
if (deviceId == inputDeviceId && flow == eCapture)
|
||||||
inputDevice.reset (new WASAPIInputDevice (device, useExclusiveMode));
|
inputDevice.reset (new WASAPIInputDevice (device, useExclusiveMode));
|
||||||
|
|
@ -1381,7 +1365,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
return (outputDeviceId.isEmpty() || (outputDevice != nullptr && outputDevice->isOk()))
|
return (outputDeviceId.isEmpty() || (outputDevice != nullptr && outputDevice->isOk()))
|
||||||
&& (inputDeviceId.isEmpty() || (inputDevice != nullptr && inputDevice->isOk()));
|
&& (inputDeviceId.isEmpty() || (inputDevice != nullptr && inputDevice->isOk()));
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
@ -1428,8 +1412,7 @@ public:
|
||||||
WASAPIAudioIODeviceType (bool exclusive)
|
WASAPIAudioIODeviceType (bool exclusive)
|
||||||
: AudioIODeviceType (exclusive ? "Windows Audio (Exclusive Mode)" : "Windows Audio"),
|
: AudioIODeviceType (exclusive ? "Windows Audio (Exclusive Mode)" : "Windows Audio"),
|
||||||
DeviceChangeDetector (L"Windows Audio"),
|
DeviceChangeDetector (L"Windows Audio"),
|
||||||
exclusiveMode (exclusive),
|
exclusiveMode (exclusive)
|
||||||
hasScanned (false)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1471,7 +1454,7 @@ public:
|
||||||
{
|
{
|
||||||
jassert (hasScanned); // need to call scanForDevices() before doing this
|
jassert (hasScanned); // need to call scanForDevices() before doing this
|
||||||
|
|
||||||
if (WASAPIAudioIODevice* const d = dynamic_cast<WASAPIAudioIODevice*> (device))
|
if (auto d = dynamic_cast<WASAPIAudioIODevice*> (device))
|
||||||
return asInput ? inputDeviceIds.indexOf (d->inputDeviceId)
|
return asInput ? inputDeviceIds.indexOf (d->inputDeviceId)
|
||||||
: outputDeviceIds.indexOf (d->outputDeviceId);
|
: outputDeviceIds.indexOf (d->outputDeviceId);
|
||||||
|
|
||||||
|
|
@ -1487,8 +1470,8 @@ public:
|
||||||
|
|
||||||
std::unique_ptr<WASAPIAudioIODevice> device;
|
std::unique_ptr<WASAPIAudioIODevice> device;
|
||||||
|
|
||||||
const int outputIndex = outputDeviceNames.indexOf (outputDeviceName);
|
auto outputIndex = outputDeviceNames.indexOf (outputDeviceName);
|
||||||
const int inputIndex = inputDeviceNames.indexOf (inputDeviceName);
|
auto inputIndex = inputDeviceNames.indexOf (inputDeviceName);
|
||||||
|
|
||||||
if (outputIndex >= 0 || inputIndex >= 0)
|
if (outputIndex >= 0 || inputIndex >= 0)
|
||||||
{
|
{
|
||||||
|
|
@ -1511,7 +1494,8 @@ public:
|
||||||
StringArray inputDeviceNames, inputDeviceIds;
|
StringArray inputDeviceNames, inputDeviceIds;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool exclusiveMode, hasScanned;
|
const bool exclusiveMode;
|
||||||
|
bool hasScanned = false;
|
||||||
ComSmartPtr<IMMDeviceEnumerator> enumerator;
|
ComSmartPtr<IMMDeviceEnumerator> enumerator;
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
@ -1538,7 +1522,7 @@ private:
|
||||||
ComSmartPtr<ChangeNotificationClient> notifyClient;
|
ComSmartPtr<ChangeNotificationClient> notifyClient;
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
static String getDefaultEndpoint (IMMDeviceEnumerator* const enumerator, const bool forCapture)
|
static String getDefaultEndpoint (IMMDeviceEnumerator* enumerator, bool forCapture)
|
||||||
{
|
{
|
||||||
String s;
|
String s;
|
||||||
IMMDevice* dev = nullptr;
|
IMMDevice* dev = nullptr;
|
||||||
|
|
@ -1575,8 +1559,8 @@ private:
|
||||||
enumerator->RegisterEndpointNotificationCallback (notifyClient);
|
enumerator->RegisterEndpointNotificationCallback (notifyClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
const String defaultRenderer (getDefaultEndpoint (enumerator, false));
|
auto defaultRenderer = getDefaultEndpoint (enumerator, false);
|
||||||
const String defaultCapture (getDefaultEndpoint (enumerator, true));
|
auto defaultCapture = getDefaultEndpoint (enumerator, true);
|
||||||
|
|
||||||
ComSmartPtr<IMMDeviceCollection> deviceCollection;
|
ComSmartPtr<IMMDeviceCollection> deviceCollection;
|
||||||
UINT32 numDevices = 0;
|
UINT32 numDevices = 0;
|
||||||
|
|
@ -1588,18 +1572,21 @@ private:
|
||||||
for (UINT32 i = 0; i < numDevices; ++i)
|
for (UINT32 i = 0; i < numDevices; ++i)
|
||||||
{
|
{
|
||||||
ComSmartPtr<IMMDevice> device;
|
ComSmartPtr<IMMDevice> device;
|
||||||
|
|
||||||
if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress())))
|
if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress())))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
DWORD state = 0;
|
DWORD state = 0;
|
||||||
|
|
||||||
if (! (check (device->GetState (&state)) && state == DEVICE_STATE_ACTIVE))
|
if (! (check (device->GetState (&state)) && state == DEVICE_STATE_ACTIVE))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const String deviceId (getDeviceID (device));
|
auto deviceId = getDeviceID (device);
|
||||||
String name;
|
String name;
|
||||||
|
|
||||||
{
|
{
|
||||||
ComSmartPtr<IPropertyStore> properties;
|
ComSmartPtr<IPropertyStore> properties;
|
||||||
|
|
||||||
if (! check (device->OpenPropertyStore (STGM_READ, properties.resetAndGetPointerAddress())))
|
if (! check (device->OpenPropertyStore (STGM_READ, properties.resetAndGetPointerAddress())))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
@ -1615,7 +1602,7 @@ private:
|
||||||
PropVariantClear (&value);
|
PropVariantClear (&value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const EDataFlow flow = getDataFlow (device);
|
auto flow = getDataFlow (device);
|
||||||
|
|
||||||
if (flow == eRender)
|
if (flow == eRender)
|
||||||
{
|
{
|
||||||
|
|
@ -1666,9 +1653,11 @@ struct MMDeviceMasterVolume
|
||||||
MMDeviceMasterVolume()
|
MMDeviceMasterVolume()
|
||||||
{
|
{
|
||||||
ComSmartPtr<IMMDeviceEnumerator> enumerator;
|
ComSmartPtr<IMMDeviceEnumerator> enumerator;
|
||||||
|
|
||||||
if (check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
|
if (check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
|
||||||
{
|
{
|
||||||
ComSmartPtr<IMMDevice> device;
|
ComSmartPtr<IMMDevice> device;
|
||||||
|
|
||||||
if (check (enumerator->GetDefaultAudioEndpoint (eRender, eConsole, device.resetAndGetPointerAddress())))
|
if (check (enumerator->GetDefaultAudioEndpoint (eRender, eConsole, device.resetAndGetPointerAddress())))
|
||||||
check (device->Activate (__uuidof (IAudioEndpointVolume), CLSCTX_INPROC_SERVER, nullptr,
|
check (device->Activate (__uuidof (IAudioEndpointVolume), CLSCTX_INPROC_SERVER, nullptr,
|
||||||
(void**) endpointVolume.resetAndGetPointerAddress()));
|
(void**) endpointVolume.resetAndGetPointerAddress()));
|
||||||
|
|
@ -1678,6 +1667,7 @@ struct MMDeviceMasterVolume
|
||||||
float getGain() const
|
float getGain() const
|
||||||
{
|
{
|
||||||
float vol = 0.0f;
|
float vol = 0.0f;
|
||||||
|
|
||||||
if (endpointVolume != nullptr)
|
if (endpointVolume != nullptr)
|
||||||
check (endpointVolume->GetMasterVolumeLevelScalar (&vol));
|
check (endpointVolume->GetMasterVolumeLevelScalar (&vol));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -166,11 +166,11 @@ private:
|
||||||
AudioSource* masterSource = nullptr;
|
AudioSource* masterSource = nullptr;
|
||||||
|
|
||||||
CriticalSection callbackLock;
|
CriticalSection callbackLock;
|
||||||
float volatile gain = 1.0f, lastGain = 1.0f;
|
float gain = 1.0f, lastGain = 1.0f;
|
||||||
bool volatile playing = false, stopped = true;
|
bool playing = false, stopped = true;
|
||||||
double sampleRate = 44100.0, sourceSampleRate = 0;
|
double sampleRate = 44100.0, sourceSampleRate = 0;
|
||||||
int blockSize = 128, readAheadBufferSize = 0;
|
int blockSize = 128, readAheadBufferSize = 0;
|
||||||
bool volatile isPrepared = false, inputStreamEOF = false;
|
bool isPrepared = false, inputStreamEOF = false;
|
||||||
|
|
||||||
void releaseMasterResources();
|
void releaseMasterResources();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -93,8 +93,8 @@ private:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
OptionalScopedPointer<AudioFormatReader> reader;
|
OptionalScopedPointer<AudioFormatReader> reader;
|
||||||
|
|
||||||
int64 volatile nextPlayPos;
|
int64 nextPlayPos;
|
||||||
bool volatile looping;
|
bool looping;
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatReaderSource)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatReaderSource)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ bool AudioFormatWriter::writeFromFloatArrays (const float* const* channels, int
|
||||||
|
|
||||||
while (numSamples > 0)
|
while (numSamples > 0)
|
||||||
{
|
{
|
||||||
const int numToDo = jmin (numSamples, maxSamples);
|
auto numToDo = jmin (numSamples, maxSamples);
|
||||||
|
|
||||||
for (int i = 0; i < numSourceChannels; ++i)
|
for (int i = 0; i < numSourceChannels; ++i)
|
||||||
convertFloatsToInts (chans[i], channels[i] + startSample, numToDo);
|
convertFloatsToInts (chans[i], channels[i] + startSample, numToDo);
|
||||||
|
|
@ -217,12 +217,7 @@ public:
|
||||||
: fifo (numSamples),
|
: fifo (numSamples),
|
||||||
buffer (channels, numSamples),
|
buffer (channels, numSamples),
|
||||||
timeSliceThread (tst),
|
timeSliceThread (tst),
|
||||||
writer (w),
|
writer (w)
|
||||||
receiver (nullptr),
|
|
||||||
samplesWritten (0),
|
|
||||||
samplesPerFlush (0),
|
|
||||||
flushSampleCounter (0),
|
|
||||||
isRunning (true)
|
|
||||||
{
|
{
|
||||||
timeSliceThread.addTimeSliceClient (this);
|
timeSliceThread.addTimeSliceClient (this);
|
||||||
}
|
}
|
||||||
|
|
@ -267,7 +262,7 @@ public:
|
||||||
|
|
||||||
int writePendingData()
|
int writePendingData()
|
||||||
{
|
{
|
||||||
const int numToDo = fifo.getTotalSize() / 4;
|
auto numToDo = fifo.getTotalSize() / 4;
|
||||||
|
|
||||||
int start1, size1, start2, size2;
|
int start1, size1, start2, size2;
|
||||||
fifo.prepareToRead (numToDo, start1, size1, start2, size2);
|
fifo.prepareToRead (numToDo, start1, size1, start2, size2);
|
||||||
|
|
@ -278,6 +273,7 @@ public:
|
||||||
writer->writeFromAudioSampleBuffer (buffer, start1, size1);
|
writer->writeFromAudioSampleBuffer (buffer, start1, size1);
|
||||||
|
|
||||||
const ScopedLock sl (thumbnailLock);
|
const ScopedLock sl (thumbnailLock);
|
||||||
|
|
||||||
if (receiver != nullptr)
|
if (receiver != nullptr)
|
||||||
receiver->addBlock (samplesWritten, buffer, start1, size1);
|
receiver->addBlock (samplesWritten, buffer, start1, size1);
|
||||||
|
|
||||||
|
|
@ -330,10 +326,10 @@ private:
|
||||||
TimeSliceThread& timeSliceThread;
|
TimeSliceThread& timeSliceThread;
|
||||||
std::unique_ptr<AudioFormatWriter> writer;
|
std::unique_ptr<AudioFormatWriter> writer;
|
||||||
CriticalSection thumbnailLock;
|
CriticalSection thumbnailLock;
|
||||||
IncomingDataReceiver* receiver;
|
IncomingDataReceiver* receiver = {};
|
||||||
int64 samplesWritten;
|
int64 samplesWritten = 0;
|
||||||
int samplesPerFlush, flushSampleCounter;
|
int samplesPerFlush = 0, flushSampleCounter = 0;
|
||||||
volatile bool isRunning;
|
std::atomic<bool> isRunning { true };
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE (Buffer)
|
JUCE_DECLARE_NON_COPYABLE (Buffer)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,169 +1,169 @@
|
||||||
$$CameraApi21
|
$$CameraApi21
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
public class CameraDeviceStateCallback extends CameraDevice.StateCallback
|
public class CameraDeviceStateCallback extends CameraDevice.StateCallback
|
||||||
{
|
{
|
||||||
private native void cameraDeviceStateClosed (long host, CameraDevice camera);
|
private native void cameraDeviceStateClosed (long host, CameraDevice camera);
|
||||||
private native void cameraDeviceStateDisconnected (long host, CameraDevice camera);
|
private native void cameraDeviceStateDisconnected (long host, CameraDevice camera);
|
||||||
private native void cameraDeviceStateError (long host, CameraDevice camera, int error);
|
private native void cameraDeviceStateError (long host, CameraDevice camera, int error);
|
||||||
private native void cameraDeviceStateOpened (long host, CameraDevice camera);
|
private native void cameraDeviceStateOpened (long host, CameraDevice camera);
|
||||||
|
|
||||||
CameraDeviceStateCallback (long hostToUse)
|
CameraDeviceStateCallback (long hostToUse)
|
||||||
{
|
{
|
||||||
host = hostToUse;
|
host = hostToUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClosed (CameraDevice camera)
|
public void onClosed (CameraDevice camera)
|
||||||
{
|
{
|
||||||
cameraDeviceStateClosed (host, camera);
|
cameraDeviceStateClosed (host, camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisconnected (CameraDevice camera)
|
public void onDisconnected (CameraDevice camera)
|
||||||
{
|
{
|
||||||
cameraDeviceStateDisconnected (host, camera);
|
cameraDeviceStateDisconnected (host, camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError (CameraDevice camera, int error)
|
public void onError (CameraDevice camera, int error)
|
||||||
{
|
{
|
||||||
cameraDeviceStateError (host, camera, error);
|
cameraDeviceStateError (host, camera, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOpened (CameraDevice camera)
|
public void onOpened (CameraDevice camera)
|
||||||
{
|
{
|
||||||
cameraDeviceStateOpened (host, camera);
|
cameraDeviceStateOpened (host, camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long host;
|
private long host;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
public class CameraCaptureSessionStateCallback extends CameraCaptureSession.StateCallback
|
public class CameraCaptureSessionStateCallback extends CameraCaptureSession.StateCallback
|
||||||
{
|
{
|
||||||
private native void cameraCaptureSessionActive (long host, CameraCaptureSession session);
|
private native void cameraCaptureSessionActive (long host, CameraCaptureSession session);
|
||||||
private native void cameraCaptureSessionClosed (long host, CameraCaptureSession session);
|
private native void cameraCaptureSessionClosed (long host, CameraCaptureSession session);
|
||||||
private native void cameraCaptureSessionConfigureFailed (long host, CameraCaptureSession session);
|
private native void cameraCaptureSessionConfigureFailed (long host, CameraCaptureSession session);
|
||||||
private native void cameraCaptureSessionConfigured (long host, CameraCaptureSession session);
|
private native void cameraCaptureSessionConfigured (long host, CameraCaptureSession session);
|
||||||
private native void cameraCaptureSessionReady (long host, CameraCaptureSession session);
|
private native void cameraCaptureSessionReady (long host, CameraCaptureSession session);
|
||||||
|
|
||||||
CameraCaptureSessionStateCallback (long hostToUse)
|
CameraCaptureSessionStateCallback (long hostToUse)
|
||||||
{
|
{
|
||||||
host = hostToUse;
|
host = hostToUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActive (CameraCaptureSession session)
|
public void onActive (CameraCaptureSession session)
|
||||||
{
|
{
|
||||||
cameraCaptureSessionActive (host, session);
|
cameraCaptureSessionActive (host, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClosed (CameraCaptureSession session)
|
public void onClosed (CameraCaptureSession session)
|
||||||
{
|
{
|
||||||
cameraCaptureSessionClosed (host, session);
|
cameraCaptureSessionClosed (host, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConfigureFailed (CameraCaptureSession session)
|
public void onConfigureFailed (CameraCaptureSession session)
|
||||||
{
|
{
|
||||||
cameraCaptureSessionConfigureFailed (host, session);
|
cameraCaptureSessionConfigureFailed (host, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConfigured (CameraCaptureSession session)
|
public void onConfigured (CameraCaptureSession session)
|
||||||
{
|
{
|
||||||
cameraCaptureSessionConfigured (host, session);
|
cameraCaptureSessionConfigured (host, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReady (CameraCaptureSession session)
|
public void onReady (CameraCaptureSession session)
|
||||||
{
|
{
|
||||||
cameraCaptureSessionReady (host, session);
|
cameraCaptureSessionReady (host, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long host;
|
private long host;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
public class CameraCaptureSessionCaptureCallback extends CameraCaptureSession.CaptureCallback
|
public class CameraCaptureSessionCaptureCallback extends CameraCaptureSession.CaptureCallback
|
||||||
{
|
{
|
||||||
private native void cameraCaptureSessionCaptureCompleted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result);
|
private native void cameraCaptureSessionCaptureCompleted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result);
|
||||||
private native void cameraCaptureSessionCaptureFailed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureFailure failure);
|
private native void cameraCaptureSessionCaptureFailed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureFailure failure);
|
||||||
private native void cameraCaptureSessionCaptureProgressed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult);
|
private native void cameraCaptureSessionCaptureProgressed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult);
|
||||||
private native void cameraCaptureSessionCaptureStarted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber);
|
private native void cameraCaptureSessionCaptureStarted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber);
|
||||||
private native void cameraCaptureSessionCaptureSequenceAborted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId);
|
private native void cameraCaptureSessionCaptureSequenceAborted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId);
|
||||||
private native void cameraCaptureSessionCaptureSequenceCompleted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId, long frameNumber);
|
private native void cameraCaptureSessionCaptureSequenceCompleted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId, long frameNumber);
|
||||||
|
|
||||||
CameraCaptureSessionCaptureCallback (long hostToUse, boolean shouldBePreview)
|
CameraCaptureSessionCaptureCallback (long hostToUse, boolean shouldBePreview)
|
||||||
{
|
{
|
||||||
host = hostToUse;
|
host = hostToUse;
|
||||||
preview = shouldBePreview;
|
preview = shouldBePreview;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCaptureCompleted (CameraCaptureSession session, CaptureRequest request,
|
public void onCaptureCompleted (CameraCaptureSession session, CaptureRequest request,
|
||||||
TotalCaptureResult result)
|
TotalCaptureResult result)
|
||||||
{
|
{
|
||||||
cameraCaptureSessionCaptureCompleted (host, preview, session, request, result);
|
cameraCaptureSessionCaptureCompleted (host, preview, session, request, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCaptureFailed (CameraCaptureSession session, CaptureRequest request, CaptureFailure failure)
|
public void onCaptureFailed (CameraCaptureSession session, CaptureRequest request, CaptureFailure failure)
|
||||||
{
|
{
|
||||||
cameraCaptureSessionCaptureFailed (host, preview, session, request, failure);
|
cameraCaptureSessionCaptureFailed (host, preview, session, request, failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCaptureProgressed (CameraCaptureSession session, CaptureRequest request,
|
public void onCaptureProgressed (CameraCaptureSession session, CaptureRequest request,
|
||||||
CaptureResult partialResult)
|
CaptureResult partialResult)
|
||||||
{
|
{
|
||||||
cameraCaptureSessionCaptureProgressed (host, preview, session, request, partialResult);
|
cameraCaptureSessionCaptureProgressed (host, preview, session, request, partialResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCaptureSequenceAborted (CameraCaptureSession session, int sequenceId)
|
public void onCaptureSequenceAborted (CameraCaptureSession session, int sequenceId)
|
||||||
{
|
{
|
||||||
cameraCaptureSessionCaptureSequenceAborted (host, preview, session, sequenceId);
|
cameraCaptureSessionCaptureSequenceAborted (host, preview, session, sequenceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCaptureSequenceCompleted (CameraCaptureSession session, int sequenceId, long frameNumber)
|
public void onCaptureSequenceCompleted (CameraCaptureSession session, int sequenceId, long frameNumber)
|
||||||
{
|
{
|
||||||
cameraCaptureSessionCaptureSequenceCompleted (host, preview, session, sequenceId, frameNumber);
|
cameraCaptureSessionCaptureSequenceCompleted (host, preview, session, sequenceId, frameNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCaptureStarted (CameraCaptureSession session, CaptureRequest request, long timestamp,
|
public void onCaptureStarted (CameraCaptureSession session, CaptureRequest request, long timestamp,
|
||||||
long frameNumber)
|
long frameNumber)
|
||||||
{
|
{
|
||||||
cameraCaptureSessionCaptureStarted (host, preview, session, request, timestamp, frameNumber);
|
cameraCaptureSessionCaptureStarted (host, preview, session, request, timestamp, frameNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long host;
|
private long host;
|
||||||
private boolean preview;
|
private boolean preview;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
public class JuceOrientationEventListener extends OrientationEventListener
|
public class JuceOrientationEventListener extends OrientationEventListener
|
||||||
{
|
{
|
||||||
private native void deviceOrientationChanged (long host, int orientation);
|
private native void deviceOrientationChanged (long host, int orientation);
|
||||||
|
|
||||||
public JuceOrientationEventListener (long hostToUse, Context context, int rate)
|
public JuceOrientationEventListener (long hostToUse, Context context, int rate)
|
||||||
{
|
{
|
||||||
super (context, rate);
|
super (context, rate);
|
||||||
|
|
||||||
host = hostToUse;
|
host = hostToUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOrientationChanged (int orientation)
|
public void onOrientationChanged (int orientation)
|
||||||
{
|
{
|
||||||
deviceOrientationChanged (host, orientation);
|
deviceOrientationChanged (host, orientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long host;
|
private long host;
|
||||||
}
|
}
|
||||||
|
|
||||||
CameraApi21$$
|
CameraApi21$$
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ bool CriticalSection::tryEnter() const noexcept { return pthread_mutex_trylo
|
||||||
void CriticalSection::exit() const noexcept { pthread_mutex_unlock (&lock); }
|
void CriticalSection::exit() const noexcept { pthread_mutex_unlock (&lock); }
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
WaitableEvent::WaitableEvent (const bool useManualReset) noexcept
|
WaitableEvent::WaitableEvent (bool useManualReset) noexcept
|
||||||
: triggered (false), manualReset (useManualReset)
|
: triggered (false), manualReset (useManualReset)
|
||||||
{
|
{
|
||||||
pthread_cond_init (&condition, 0);
|
pthread_cond_init (&condition, 0);
|
||||||
|
|
@ -61,7 +61,7 @@ WaitableEvent::~WaitableEvent() noexcept
|
||||||
pthread_mutex_destroy (&mutex);
|
pthread_mutex_destroy (&mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WaitableEvent::wait (const int timeOutMillisecs) const noexcept
|
bool WaitableEvent::wait (int timeOutMillisecs) const noexcept
|
||||||
{
|
{
|
||||||
pthread_mutex_lock (&mutex);
|
pthread_mutex_lock (&mutex);
|
||||||
|
|
||||||
|
|
@ -152,6 +152,7 @@ void JUCE_CALLTYPE Process::terminate()
|
||||||
bool Process::setMaxNumberOfFileHandles (int newMaxNumber) noexcept
|
bool Process::setMaxNumberOfFileHandles (int newMaxNumber) noexcept
|
||||||
{
|
{
|
||||||
rlimit lim;
|
rlimit lim;
|
||||||
|
|
||||||
if (getrlimit (RLIMIT_NOFILE, &lim) == 0)
|
if (getrlimit (RLIMIT_NOFILE, &lim) == 0)
|
||||||
{
|
{
|
||||||
if (newMaxNumber <= 0 && lim.rlim_cur == RLIM_INFINITY && lim.rlim_max == RLIM_INFINITY)
|
if (newMaxNumber <= 0 && lim.rlim_cur == RLIM_INFINITY && lim.rlim_max == RLIM_INFINITY)
|
||||||
|
|
@ -201,8 +202,8 @@ File File::getCurrentWorkingDirectory()
|
||||||
{
|
{
|
||||||
HeapBlock<char> heapBuffer;
|
HeapBlock<char> heapBuffer;
|
||||||
|
|
||||||
char localBuffer [1024];
|
char localBuffer[1024];
|
||||||
char* cwd = getcwd (localBuffer, sizeof (localBuffer) - 1);
|
auto cwd = getcwd (localBuffer, sizeof (localBuffer) - 1);
|
||||||
size_t bufferSize = 4096;
|
size_t bufferSize = 4096;
|
||||||
|
|
||||||
while (cwd == nullptr && errno == ERANGE)
|
while (cwd == nullptr && errno == ERANGE)
|
||||||
|
|
@ -221,9 +222,9 @@ bool File::setAsCurrentWorkingDirectory() const
|
||||||
}
|
}
|
||||||
|
|
||||||
#if JUCE_ANDROID
|
#if JUCE_ANDROID
|
||||||
typedef unsigned long juce_sigactionflags_type;
|
using juce_sigactionflags_type = unsigned long;
|
||||||
#else
|
#else
|
||||||
typedef int juce_sigactionflags_type;
|
using juce_sigactionflags_type = int;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
@ -245,11 +246,11 @@ int juce_siginterrupt (int sig, int flag)
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
#if JUCE_LINUX || (JUCE_IOS && ! __DARWIN_ONLY_64_BIT_INO_T) // (this iOS stuff is to avoid a simulator bug)
|
#if JUCE_LINUX || (JUCE_IOS && ! __DARWIN_ONLY_64_BIT_INO_T) // (this iOS stuff is to avoid a simulator bug)
|
||||||
typedef struct stat64 juce_statStruct;
|
using juce_statStruct = struct stat64;
|
||||||
#define JUCE_STAT stat64
|
#define JUCE_STAT stat64
|
||||||
#else
|
#else
|
||||||
typedef struct stat juce_statStruct;
|
using juce_statStruct = struct stat;
|
||||||
#define JUCE_STAT stat
|
#define JUCE_STAT stat
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool juce_stat (const String& fileName, juce_statStruct& info)
|
bool juce_stat (const String& fileName, juce_statStruct& info)
|
||||||
|
|
@ -278,8 +279,8 @@ namespace
|
||||||
static int64 getCreationTime (const juce_statStruct& s) noexcept { return (int64) s.st_ctime; }
|
static int64 getCreationTime (const juce_statStruct& s) noexcept { return (int64) s.st_ctime; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void updateStatInfoForFile (const String& path, bool* const isDir, int64* const fileSize,
|
void updateStatInfoForFile (const String& path, bool* isDir, int64* fileSize,
|
||||||
Time* const modTime, Time* const creationTime, bool* const isReadOnly)
|
Time* modTime, Time* creationTime, bool* isReadOnly)
|
||||||
{
|
{
|
||||||
if (isDir != nullptr || fileSize != nullptr || modTime != nullptr || creationTime != nullptr)
|
if (isDir != nullptr || fileSize != nullptr || modTime != nullptr || creationTime != nullptr)
|
||||||
{
|
{
|
||||||
|
|
@ -344,7 +345,7 @@ uint64 File::getFileIdentifier() const
|
||||||
static bool hasEffectiveRootFilePermissions()
|
static bool hasEffectiveRootFilePermissions()
|
||||||
{
|
{
|
||||||
#if JUCE_LINUX
|
#if JUCE_LINUX
|
||||||
return (geteuid() == 0);
|
return geteuid() == 0;
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -475,7 +476,7 @@ int64 juce_fileSetPosition (void* handle, int64 pos)
|
||||||
|
|
||||||
void FileInputStream::openHandle()
|
void FileInputStream::openHandle()
|
||||||
{
|
{
|
||||||
const int f = open (file.getFullPathName().toUTF8(), O_RDONLY, 00644);
|
auto f = open (file.getFullPathName().toUTF8(), O_RDONLY, 00644);
|
||||||
|
|
||||||
if (f != -1)
|
if (f != -1)
|
||||||
fileHandle = fdToVoidPointer (f);
|
fileHandle = fdToVoidPointer (f);
|
||||||
|
|
@ -489,7 +490,7 @@ FileInputStream::~FileInputStream()
|
||||||
close (getFD (fileHandle));
|
close (getFD (fileHandle));
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t FileInputStream::readInternal (void* const buffer, const size_t numBytes)
|
size_t FileInputStream::readInternal (void* buffer, size_t numBytes)
|
||||||
{
|
{
|
||||||
ssize_t result = 0;
|
ssize_t result = 0;
|
||||||
|
|
||||||
|
|
@ -512,7 +513,7 @@ void FileOutputStream::openHandle()
|
||||||
{
|
{
|
||||||
if (file.exists())
|
if (file.exists())
|
||||||
{
|
{
|
||||||
const int f = open (file.getFullPathName().toUTF8(), O_RDWR, 00644);
|
auto f = open (file.getFullPathName().toUTF8(), O_RDWR, 00644);
|
||||||
|
|
||||||
if (f != -1)
|
if (f != -1)
|
||||||
{
|
{
|
||||||
|
|
@ -535,7 +536,7 @@ void FileOutputStream::openHandle()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const int f = open (file.getFullPathName().toUTF8(), O_RDWR + O_CREAT, 00644);
|
auto f = open (file.getFullPathName().toUTF8(), O_RDWR + O_CREAT, 00644);
|
||||||
|
|
||||||
if (f != -1)
|
if (f != -1)
|
||||||
fileHandle = fdToVoidPointer (f);
|
fileHandle = fdToVoidPointer (f);
|
||||||
|
|
@ -553,19 +554,17 @@ void FileOutputStream::closeHandle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t FileOutputStream::writeInternal (const void* const data, const size_t numBytes)
|
ssize_t FileOutputStream::writeInternal (const void* data, size_t numBytes)
|
||||||
{
|
{
|
||||||
ssize_t result = 0;
|
if (fileHandle == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (fileHandle != 0)
|
auto result = ::write (getFD (fileHandle), data, numBytes);
|
||||||
{
|
|
||||||
result = ::write (getFD (fileHandle), data, numBytes);
|
|
||||||
|
|
||||||
if (result == -1)
|
if (result == -1)
|
||||||
status = getResultForErrno();
|
status = getResultForErrno();
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return (ssize_t) result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef JUCE_ANDROID
|
#ifndef JUCE_ANDROID
|
||||||
|
|
@ -588,7 +587,7 @@ Result FileOutputStream::truncate()
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
String SystemStats::getEnvironmentVariable (const String& name, const String& defaultValue)
|
String SystemStats::getEnvironmentVariable (const String& name, const String& defaultValue)
|
||||||
{
|
{
|
||||||
if (const char* s = ::getenv (name.toUTF8()))
|
if (auto s = ::getenv (name.toUTF8()))
|
||||||
return String::fromUTF8 (s);
|
return String::fromUTF8 (s);
|
||||||
|
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
|
|
@ -601,7 +600,7 @@ void MemoryMappedFile::openInternal (const File& file, AccessMode mode, bool exc
|
||||||
|
|
||||||
if (range.getStart() > 0)
|
if (range.getStart() > 0)
|
||||||
{
|
{
|
||||||
const long pageSize = sysconf (_SC_PAGE_SIZE);
|
auto pageSize = sysconf (_SC_PAGE_SIZE);
|
||||||
range.setStart (range.getStart() - (range.getStart() % pageSize));
|
range.setStart (range.getStart() - (range.getStart() % pageSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -649,7 +648,7 @@ File juce_getExecutableFile()
|
||||||
{
|
{
|
||||||
Dl_info exeInfo;
|
Dl_info exeInfo;
|
||||||
|
|
||||||
void* localSymbol = (void*) juce_getExecutableFile;
|
auto localSymbol = (void*) juce_getExecutableFile;
|
||||||
dladdr (localSymbol, &exeInfo);
|
dladdr (localSymbol, &exeInfo);
|
||||||
return CharPointer_UTF8 (exeInfo.dli_fname);
|
return CharPointer_UTF8 (exeInfo.dli_fname);
|
||||||
}
|
}
|
||||||
|
|
@ -664,6 +663,7 @@ File juce_getExecutableFile()
|
||||||
int64 File::getBytesFreeOnVolume() const
|
int64 File::getBytesFreeOnVolume() const
|
||||||
{
|
{
|
||||||
struct statfs buf;
|
struct statfs buf;
|
||||||
|
|
||||||
if (juce_doStatFS (*this, buf))
|
if (juce_doStatFS (*this, buf))
|
||||||
return (int64) buf.f_bsize * (int64) buf.f_bavail; // Note: this returns space available to non-super user
|
return (int64) buf.f_bsize * (int64) buf.f_bavail; // Note: this returns space available to non-super user
|
||||||
|
|
||||||
|
|
@ -673,6 +673,7 @@ int64 File::getBytesFreeOnVolume() const
|
||||||
int64 File::getVolumeTotalSize() const
|
int64 File::getVolumeTotalSize() const
|
||||||
{
|
{
|
||||||
struct statfs buf;
|
struct statfs buf;
|
||||||
|
|
||||||
if (juce_doStatFS (*this, buf))
|
if (juce_doStatFS (*this, buf))
|
||||||
return (int64) buf.f_bsize * (int64) buf.f_blocks;
|
return (int64) buf.f_bsize * (int64) buf.f_blocks;
|
||||||
|
|
||||||
|
|
@ -686,11 +687,11 @@ String File::getVolumeLabel() const
|
||||||
{
|
{
|
||||||
u_int32_t length;
|
u_int32_t length;
|
||||||
attrreference_t mountPointRef;
|
attrreference_t mountPointRef;
|
||||||
char mountPointSpace [MAXPATHLEN];
|
char mountPointSpace[MAXPATHLEN];
|
||||||
} attrBuf;
|
} attrBuf;
|
||||||
|
|
||||||
struct attrlist attrList;
|
struct attrlist attrList;
|
||||||
zerostruct (attrList); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct)
|
zerostruct (attrList); // (can't use "= {}" on this object because it's a C struct)
|
||||||
attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
|
attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
|
||||||
attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_NAME;
|
attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_NAME;
|
||||||
|
|
||||||
|
|
@ -702,7 +703,7 @@ String File::getVolumeLabel() const
|
||||||
return String::fromUTF8 (((const char*) &attrBuf.mountPointRef) + attrBuf.mountPointRef.attr_dataoffset,
|
return String::fromUTF8 (((const char*) &attrBuf.mountPointRef) + attrBuf.mountPointRef.attr_dataoffset,
|
||||||
(int) attrBuf.mountPointRef.attr_length);
|
(int) attrBuf.mountPointRef.attr_length);
|
||||||
|
|
||||||
const File parent (f.getParentDirectory());
|
auto parent = f.getParentDirectory();
|
||||||
|
|
||||||
if (f == parent)
|
if (f == parent)
|
||||||
break;
|
break;
|
||||||
|
|
@ -719,7 +720,7 @@ int File::getVolumeSerialNumber() const
|
||||||
int result = 0;
|
int result = 0;
|
||||||
/* int fd = open (getFullPathName().toUTF8(), O_RDONLY | O_NONBLOCK);
|
/* int fd = open (getFullPathName().toUTF8(), O_RDONLY | O_NONBLOCK);
|
||||||
|
|
||||||
char info [512];
|
char info[512];
|
||||||
|
|
||||||
#ifndef HDIO_GET_IDENTITY
|
#ifndef HDIO_GET_IDENTITY
|
||||||
#define HDIO_GET_IDENTITY 0x030d
|
#define HDIO_GET_IDENTITY 0x030d
|
||||||
|
|
@ -748,12 +749,12 @@ String juce_getOutputFromCommand (const String&);
|
||||||
String juce_getOutputFromCommand (const String& command)
|
String juce_getOutputFromCommand (const String& command)
|
||||||
{
|
{
|
||||||
// slight bodge here, as we just pipe the output into a temp file and read it...
|
// slight bodge here, as we just pipe the output into a temp file and read it...
|
||||||
const File tempFile (File::getSpecialLocation (File::tempDirectory)
|
auto tempFile = File::getSpecialLocation (File::tempDirectory)
|
||||||
.getNonexistentChildFile (String::toHexString (Random::getSystemRandom().nextInt()), ".tmp", false));
|
.getNonexistentChildFile (String::toHexString (Random::getSystemRandom().nextInt()), ".tmp", false);
|
||||||
|
|
||||||
juce_runSystemCommand (command + " > " + tempFile.getFullPathName());
|
juce_runSystemCommand (command + " > " + tempFile.getFullPathName());
|
||||||
|
|
||||||
String result (tempFile.loadFileAsString());
|
auto result = tempFile.loadFileAsString();
|
||||||
tempFile.deleteFile();
|
tempFile.deleteFile();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -764,12 +765,9 @@ String juce_getOutputFromCommand (const String& command)
|
||||||
class InterProcessLock::Pimpl
|
class InterProcessLock::Pimpl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Pimpl (const String&, int)
|
Pimpl (const String&, int) {}
|
||||||
: handle (1), refCount (1) // On iOS just fake success..
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int handle, refCount;
|
int handle = 1, refCount = 1; // On iOS just fake success..
|
||||||
};
|
};
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
@ -777,8 +775,7 @@ public:
|
||||||
class InterProcessLock::Pimpl
|
class InterProcessLock::Pimpl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Pimpl (const String& lockName, const int timeOutMillisecs)
|
Pimpl (const String& lockName, int timeOutMillisecs)
|
||||||
: handle (0), refCount (1)
|
|
||||||
{
|
{
|
||||||
#if JUCE_MAC
|
#if JUCE_MAC
|
||||||
if (! createLockFile (File ("~/Library/Caches/com.juce.locks").getChildFile (lockName), timeOutMillisecs))
|
if (! createLockFile (File ("~/Library/Caches/com.juce.locks").getChildFile (lockName), timeOutMillisecs))
|
||||||
|
|
@ -787,6 +784,7 @@ public:
|
||||||
|
|
||||||
#else
|
#else
|
||||||
File tempFolder ("/var/tmp");
|
File tempFolder ("/var/tmp");
|
||||||
|
|
||||||
if (! tempFolder.isDirectory())
|
if (! tempFolder.isDirectory())
|
||||||
tempFolder = "/tmp";
|
tempFolder = "/tmp";
|
||||||
|
|
||||||
|
|
@ -799,7 +797,7 @@ public:
|
||||||
closeFile();
|
closeFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool createLockFile (const File& file, const int timeOutMillisecs)
|
bool createLockFile (const File& file, int timeOutMillisecs)
|
||||||
{
|
{
|
||||||
file.create();
|
file.create();
|
||||||
handle = open (file.getFullPathName().toUTF8(), O_RDWR);
|
handle = open (file.getFullPathName().toUTF8(), O_RDWR);
|
||||||
|
|
@ -812,16 +810,16 @@ public:
|
||||||
fl.l_whence = SEEK_SET;
|
fl.l_whence = SEEK_SET;
|
||||||
fl.l_type = F_WRLCK;
|
fl.l_type = F_WRLCK;
|
||||||
|
|
||||||
const int64 endTime = Time::currentTimeMillis() + timeOutMillisecs;
|
auto endTime = Time::currentTimeMillis() + timeOutMillisecs;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
const int result = fcntl (handle, F_SETLK, &fl);
|
auto result = fcntl (handle, F_SETLK, &fl);
|
||||||
|
|
||||||
if (result >= 0)
|
if (result >= 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const int error = errno;
|
auto error = errno;
|
||||||
|
|
||||||
if (error != EINTR)
|
if (error != EINTR)
|
||||||
{
|
{
|
||||||
|
|
@ -859,7 +857,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int handle, refCount;
|
int handle = 0, refCount = 1;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -871,7 +869,7 @@ InterProcessLock::~InterProcessLock()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InterProcessLock::enter (const int timeOutMillisecs)
|
bool InterProcessLock::enter (int timeOutMillisecs)
|
||||||
{
|
{
|
||||||
const ScopedLock sl (lock);
|
const ScopedLock sl (lock);
|
||||||
|
|
||||||
|
|
@ -964,7 +962,6 @@ void Thread::launchThread()
|
||||||
if (pthread_attr_init (&attr) == 0)
|
if (pthread_attr_init (&attr) == 0)
|
||||||
{
|
{
|
||||||
attrPtr = &attr;
|
attrPtr = &attr;
|
||||||
|
|
||||||
pthread_attr_setstacksize (attrPtr, threadStackSize);
|
pthread_attr_setstacksize (attrPtr, threadStackSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1054,7 +1051,7 @@ void JUCE_CALLTYPE Thread::yield()
|
||||||
#define SUPPORT_AFFINITIES 1
|
#define SUPPORT_AFFINITIES 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask (const uint32 affinityMask)
|
void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask (uint32 affinityMask)
|
||||||
{
|
{
|
||||||
#if SUPPORT_AFFINITIES
|
#if SUPPORT_AFFINITIES
|
||||||
cpu_set_t affinity;
|
cpu_set_t affinity;
|
||||||
|
|
@ -1109,7 +1106,7 @@ void* DynamicLibrary::getFunction (const String& functionName) noexcept
|
||||||
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
static inline String readPosixConfigFileValue (const char* file, const char* const key)
|
static inline String readPosixConfigFileValue (const char* file, const char* key)
|
||||||
{
|
{
|
||||||
StringArray lines;
|
StringArray lines;
|
||||||
File (file).readLines (lines);
|
File (file).readLines (lines);
|
||||||
|
|
@ -1267,7 +1264,7 @@ bool ChildProcess::start (const StringArray& args, int streamFlags)
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
struct HighResolutionTimer::Pimpl
|
struct HighResolutionTimer::Pimpl
|
||||||
{
|
{
|
||||||
Pimpl (HighResolutionTimer& t) : owner (t), thread (0), destroyThread (false), isRunning (false)
|
Pimpl (HighResolutionTimer& t) : owner (t)
|
||||||
{
|
{
|
||||||
pthread_condattr_t attr;
|
pthread_condattr_t attr;
|
||||||
pthread_condattr_init (&attr);
|
pthread_condattr_init (&attr);
|
||||||
|
|
@ -1338,15 +1335,13 @@ struct HighResolutionTimer::Pimpl
|
||||||
}
|
}
|
||||||
|
|
||||||
HighResolutionTimer& owner;
|
HighResolutionTimer& owner;
|
||||||
int volatile periodMs;
|
std::atomic<int> periodMs;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pthread_t thread;
|
pthread_t thread = {};
|
||||||
pthread_cond_t stopCond;
|
pthread_cond_t stopCond;
|
||||||
pthread_mutex_t timerMutex;
|
pthread_mutex_t timerMutex;
|
||||||
|
std::atomic<bool> destroyThread { false }, isRunning { false };
|
||||||
bool volatile destroyThread;
|
|
||||||
bool volatile isRunning;
|
|
||||||
|
|
||||||
static void* timerThread (void* param)
|
static void* timerThread (void* param)
|
||||||
{
|
{
|
||||||
|
|
@ -1369,7 +1364,7 @@ private:
|
||||||
|
|
||||||
void timerThread()
|
void timerThread()
|
||||||
{
|
{
|
||||||
int lastPeriod = periodMs;
|
auto lastPeriod = periodMs.load();
|
||||||
Clock clock (lastPeriod);
|
Clock clock (lastPeriod);
|
||||||
|
|
||||||
pthread_mutex_lock (&timerMutex);
|
pthread_mutex_lock (&timerMutex);
|
||||||
|
|
@ -1385,15 +1380,16 @@ private:
|
||||||
if (isRunning)
|
if (isRunning)
|
||||||
owner.hiResTimerCallback();
|
owner.hiResTimerCallback();
|
||||||
|
|
||||||
if (lastPeriod != periodMs)
|
auto newPeriod = periodMs.load();
|
||||||
|
|
||||||
|
if (lastPeriod != newPeriod)
|
||||||
{
|
{
|
||||||
lastPeriod = periodMs;
|
lastPeriod = newPeriod;
|
||||||
clock = Clock (lastPeriod);
|
clock = Clock (lastPeriod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
periodMs = 0;
|
periodMs = 0;
|
||||||
|
|
||||||
pthread_mutex_unlock (&timerMutex);
|
pthread_mutex_unlock (&timerMutex);
|
||||||
pthread_exit (nullptr);
|
pthread_exit (nullptr);
|
||||||
}
|
}
|
||||||
|
|
@ -1421,7 +1417,7 @@ private:
|
||||||
uint64_t time, delta;
|
uint64_t time, delta;
|
||||||
mach_timebase_info_data_t timebase;
|
mach_timebase_info_data_t timebase;
|
||||||
|
|
||||||
bool hasExpired(struct timespec& time_left) noexcept
|
bool hasExpired (struct timespec& time_left) noexcept
|
||||||
{
|
{
|
||||||
uint64_t now = mach_absolute_time();
|
uint64_t now = mach_absolute_time();
|
||||||
|
|
||||||
|
|
@ -1457,11 +1453,11 @@ private:
|
||||||
|
|
||||||
uint64 time, delta;
|
uint64 time, delta;
|
||||||
|
|
||||||
bool hasExpired(struct timespec& expiryTime) noexcept
|
bool hasExpired (struct timespec& expiryTime) noexcept
|
||||||
{
|
{
|
||||||
struct timespec t;
|
struct timespec t;
|
||||||
clock_gettime (CLOCK_MONOTONIC, &t);
|
clock_gettime (CLOCK_MONOTONIC, &t);
|
||||||
uint64 now = (uint64) (1000000000 * (int64) t.tv_sec + (int64) t.tv_nsec);
|
auto now = (uint64) (1000000000 * (int64) t.tv_sec + (int64) t.tv_nsec);
|
||||||
|
|
||||||
if (now < time)
|
if (now < time)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -94,10 +94,10 @@ namespace SocketHelpers
|
||||||
: setOption (handle, IPPROTO_TCP, TCP_NODELAY, (int) 1));
|
: setOption (handle, IPPROTO_TCP, TCP_NODELAY, (int) 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void closeSocket (volatile int& handle, CriticalSection& readLock,
|
static void closeSocket (std::atomic<int>& handle, CriticalSection& readLock,
|
||||||
bool isListener, int portNumber, bool& connected) noexcept
|
bool isListener, int portNumber, std::atomic<bool>& connected) noexcept
|
||||||
{
|
{
|
||||||
const SocketHandle h = handle;
|
const SocketHandle h = handle.load();
|
||||||
handle = -1;
|
handle = -1;
|
||||||
|
|
||||||
#if JUCE_WINDOWS
|
#if JUCE_WINDOWS
|
||||||
|
|
@ -187,8 +187,8 @@ namespace SocketHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
static int readSocket (SocketHandle handle,
|
static int readSocket (SocketHandle handle,
|
||||||
void* const destBuffer, const int maxBytesToRead,
|
void* destBuffer, int maxBytesToRead,
|
||||||
bool volatile& connected,
|
std::atomic<bool>& connected,
|
||||||
bool blockUntilSpecifiedAmountHasArrived,
|
bool blockUntilSpecifiedAmountHasArrived,
|
||||||
CriticalSection& readLock,
|
CriticalSection& readLock,
|
||||||
String* senderIP = nullptr,
|
String* senderIP = nullptr,
|
||||||
|
|
@ -242,8 +242,8 @@ namespace SocketHelpers
|
||||||
return (int) bytesRead;
|
return (int) bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int waitForReadiness (const volatile int& handle, CriticalSection& readLock,
|
static int waitForReadiness (std::atomic<int>& handle, CriticalSection& readLock,
|
||||||
const bool forReading, const int timeoutMsecs) noexcept
|
bool forReading, int timeoutMsecs) noexcept
|
||||||
{
|
{
|
||||||
// avoid race-condition
|
// avoid race-condition
|
||||||
CriticalSection::ScopedTryLockType lock (readLock);
|
CriticalSection::ScopedTryLockType lock (readLock);
|
||||||
|
|
@ -251,7 +251,7 @@ namespace SocketHelpers
|
||||||
if (! lock.isLocked())
|
if (! lock.isLocked())
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
int h = handle;
|
int h = handle.load();
|
||||||
|
|
||||||
struct timeval timeout;
|
struct timeval timeout;
|
||||||
struct timeval* timeoutp;
|
struct timeval* timeoutp;
|
||||||
|
|
@ -293,7 +293,7 @@ namespace SocketHelpers
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// we are closing
|
// we are closing
|
||||||
if (handle < 0)
|
if (handle.load() < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -308,7 +308,7 @@ namespace SocketHelpers
|
||||||
return FD_ISSET (h, forReading ? &rset : &wset) ? 1 : 0;
|
return FD_ISSET (h, forReading ? &rset : &wset) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool setSocketBlockingState (SocketHandle handle, const bool shouldBlock) noexcept
|
static bool setSocketBlockingState (SocketHandle handle, bool shouldBlock) noexcept
|
||||||
{
|
{
|
||||||
#if JUCE_WINDOWS
|
#if JUCE_WINDOWS
|
||||||
u_long nonBlocking = shouldBlock ? 0 : (u_long) 1;
|
u_long nonBlocking = shouldBlock ? 0 : (u_long) 1;
|
||||||
|
|
@ -328,7 +328,7 @@ namespace SocketHelpers
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static addrinfo* getAddressInfo (const bool isDatagram, const String& hostName, int portNumber)
|
static addrinfo* getAddressInfo (bool isDatagram, const String& hostName, int portNumber)
|
||||||
{
|
{
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
zerostruct (hints);
|
zerostruct (hints);
|
||||||
|
|
@ -345,11 +345,11 @@ namespace SocketHelpers
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool connectSocket (int volatile& handle,
|
static bool connectSocket (std::atomic<int>& handle,
|
||||||
CriticalSection& readLock,
|
CriticalSection& readLock,
|
||||||
const String& hostName,
|
const String& hostName,
|
||||||
const int portNumber,
|
int portNumber,
|
||||||
const int timeOutMillisecs) noexcept
|
int timeOutMillisecs) noexcept
|
||||||
{
|
{
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
|
|
@ -373,7 +373,7 @@ namespace SocketHelpers
|
||||||
if (errno == EINPROGRESS)
|
if (errno == EINPROGRESS)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
const volatile int cvHandle = (int) newHandle;
|
std::atomic<int> cvHandle { (int) newHandle };
|
||||||
|
|
||||||
if (waitForReadiness (cvHandle, readLock, false, timeOutMillisecs) == 1)
|
if (waitForReadiness (cvHandle, readLock, false, timeOutMillisecs) == 1)
|
||||||
success = true;
|
success = true;
|
||||||
|
|
@ -454,14 +454,14 @@ StreamingSocket::~StreamingSocket()
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
int StreamingSocket::read (void* destBuffer, const int maxBytesToRead, bool shouldBlock)
|
int StreamingSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock)
|
||||||
{
|
{
|
||||||
return (connected && ! isListener) ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead,
|
return (connected && ! isListener) ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead,
|
||||||
connected, shouldBlock, readLock)
|
connected, shouldBlock, readLock)
|
||||||
: -1;
|
: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int StreamingSocket::write (const void* sourceBuffer, const int numBytesToWrite)
|
int StreamingSocket::write (const void* sourceBuffer, int numBytesToWrite)
|
||||||
{
|
{
|
||||||
if (isListener || ! connected)
|
if (isListener || ! connected)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -470,20 +470,19 @@ int StreamingSocket::write (const void* sourceBuffer, const int numBytesToWrite)
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
int StreamingSocket::waitUntilReady (const bool readyForReading,
|
int StreamingSocket::waitUntilReady (bool readyForReading, int timeoutMsecs)
|
||||||
const int timeoutMsecs) const
|
|
||||||
{
|
{
|
||||||
return connected ? SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs)
|
return connected ? SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs)
|
||||||
: -1;
|
: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
bool StreamingSocket::bindToPort (const int port)
|
bool StreamingSocket::bindToPort (int port)
|
||||||
{
|
{
|
||||||
return bindToPort (port, String());
|
return bindToPort (port, String());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StreamingSocket::bindToPort (const int port, const String& addr)
|
bool StreamingSocket::bindToPort (int port, const String& addr)
|
||||||
{
|
{
|
||||||
jassert (SocketHelpers::isValidPortNumber (port));
|
jassert (SocketHelpers::isValidPortNumber (port));
|
||||||
|
|
||||||
|
|
@ -495,9 +494,7 @@ int StreamingSocket::getBoundPort() const noexcept
|
||||||
return SocketHelpers::getBoundPort (handle);
|
return SocketHelpers::getBoundPort (handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StreamingSocket::connect (const String& remoteHostName,
|
bool StreamingSocket::connect (const String& remoteHostName, int remotePortNumber, int timeOutMillisecs)
|
||||||
const int remotePortNumber,
|
|
||||||
const int timeOutMillisecs)
|
|
||||||
{
|
{
|
||||||
jassert (SocketHelpers::isValidPortNumber (remotePortNumber));
|
jassert (SocketHelpers::isValidPortNumber (remotePortNumber));
|
||||||
|
|
||||||
|
|
@ -537,7 +534,7 @@ void StreamingSocket::close()
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
bool StreamingSocket::createListener (const int newPortNumber, const String& localHostName)
|
bool StreamingSocket::createListener (int newPortNumber, const String& localHostName)
|
||||||
{
|
{
|
||||||
jassert (SocketHelpers::isValidPortNumber (newPortNumber));
|
jassert (SocketHelpers::isValidPortNumber (newPortNumber));
|
||||||
|
|
||||||
|
|
@ -633,18 +630,18 @@ void DatagramSocket::shutdown()
|
||||||
if (handle < 0)
|
if (handle < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto copyOfHandle = handle;
|
std::atomic<int> handleCopy { handle.load() };
|
||||||
handle = -1;
|
handle = -1;
|
||||||
bool connected = false;
|
std::atomic<bool> connected { false };
|
||||||
SocketHelpers::closeSocket (copyOfHandle, readLock, false, 0, connected);
|
SocketHelpers::closeSocket (handleCopy, readLock, false, 0, connected);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatagramSocket::bindToPort (const int port)
|
bool DatagramSocket::bindToPort (int port)
|
||||||
{
|
{
|
||||||
return bindToPort (port, String());
|
return bindToPort (port, String());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatagramSocket::bindToPort (const int port, const String& addr)
|
bool DatagramSocket::bindToPort (int port, const String& addr)
|
||||||
{
|
{
|
||||||
jassert (SocketHelpers::isValidPortNumber (port));
|
jassert (SocketHelpers::isValidPortNumber (port));
|
||||||
|
|
||||||
|
|
@ -664,8 +661,7 @@ int DatagramSocket::getBoundPort() const noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
int DatagramSocket::waitUntilReady (const bool readyForReading,
|
int DatagramSocket::waitUntilReady (bool readyForReading, int timeoutMsecs)
|
||||||
const int timeoutMsecs) const
|
|
||||||
{
|
{
|
||||||
if (handle < 0)
|
if (handle < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -678,7 +674,7 @@ int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock
|
||||||
if (handle < 0 || ! isBound)
|
if (handle < 0 || ! isBound)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
bool connected = true;
|
std::atomic<bool> connected { true };
|
||||||
|
|
||||||
SocketHelpers::setSocketBlockingState (handle, shouldBlock);
|
SocketHelpers::setSocketBlockingState (handle, shouldBlock);
|
||||||
return SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead,
|
return SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead,
|
||||||
|
|
@ -690,7 +686,7 @@ int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock
|
||||||
if (handle < 0 || ! isBound)
|
if (handle < 0 || ! isBound)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
bool connected = true;
|
std::atomic<bool> connected { true };
|
||||||
|
|
||||||
SocketHelpers::setSocketBlockingState (handle, shouldBlock);
|
SocketHelpers::setSocketBlockingState (handle, shouldBlock);
|
||||||
return SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected,
|
return SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected,
|
||||||
|
|
|
||||||
|
|
@ -122,8 +122,7 @@ public:
|
||||||
If the socket is ready on return, this returns 1. If it times-out before
|
If the socket is ready on return, this returns 1. If it times-out before
|
||||||
the socket becomes ready, it returns 0. If an error occurs, it returns -1.
|
the socket becomes ready, it returns 0. If an error occurs, it returns -1.
|
||||||
*/
|
*/
|
||||||
int waitUntilReady (bool readyForReading,
|
int waitUntilReady (bool readyForReading, int timeoutMsecs);
|
||||||
int timeoutMsecs) const;
|
|
||||||
|
|
||||||
/** Reads bytes from the socket.
|
/** Reads bytes from the socket.
|
||||||
|
|
||||||
|
|
@ -177,8 +176,9 @@ public:
|
||||||
private:
|
private:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
String hostName;
|
String hostName;
|
||||||
int volatile portNumber = 0, handle = -1;
|
std::atomic<int> portNumber { 0 }, handle { -1 };
|
||||||
bool connected = false, isListener = false;
|
std::atomic<bool> connected { false };
|
||||||
|
bool isListener = false;
|
||||||
mutable CriticalSection readLock;
|
mutable CriticalSection readLock;
|
||||||
|
|
||||||
StreamingSocket (const String& hostname, int portNumber, int handle);
|
StreamingSocket (const String& hostname, int portNumber, int handle);
|
||||||
|
|
@ -263,8 +263,7 @@ public:
|
||||||
If the socket is ready on return, this returns 1. If it times-out before
|
If the socket is ready on return, this returns 1. If it times-out before
|
||||||
the socket becomes ready, it returns 0. If an error occurs, it returns -1.
|
the socket becomes ready, it returns 0. If an error occurs, it returns -1.
|
||||||
*/
|
*/
|
||||||
int waitUntilReady (bool readyForReading,
|
int waitUntilReady (bool readyForReading, int timeoutMsecs);
|
||||||
int timeoutMsecs) const;
|
|
||||||
|
|
||||||
/** Reads bytes from the socket.
|
/** Reads bytes from the socket.
|
||||||
|
|
||||||
|
|
@ -320,19 +319,16 @@ public:
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
/** Join a multicast group.
|
/** Join a multicast group.
|
||||||
|
|
||||||
@returns true if it succeeds.
|
@returns true if it succeeds.
|
||||||
*/
|
*/
|
||||||
bool joinMulticast (const String& multicastIPAddress);
|
bool joinMulticast (const String& multicastIPAddress);
|
||||||
|
|
||||||
/** Leave a multicast group.
|
/** Leave a multicast group.
|
||||||
|
|
||||||
@returns true if it succeeds.
|
@returns true if it succeeds.
|
||||||
*/
|
*/
|
||||||
bool leaveMulticast (const String& multicastIPAddress);
|
bool leaveMulticast (const String& multicastIPAddress);
|
||||||
|
|
||||||
/** Enables or disables multicast loopback.
|
/** Enables or disables multicast loopback.
|
||||||
|
|
||||||
@returns true if it succeeds.
|
@returns true if it succeeds.
|
||||||
*/
|
*/
|
||||||
bool setMulticastLoopbackEnabled (bool enableLoopback);
|
bool setMulticastLoopbackEnabled (bool enableLoopback);
|
||||||
|
|
@ -350,7 +346,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
int handle = -1;
|
std::atomic<int> handle { -1 };
|
||||||
bool isBound = false;
|
bool isBound = false;
|
||||||
String lastBindAddress, lastServerHost;
|
String lastBindAddress, lastServerHost;
|
||||||
int lastServerPort = -1;
|
int lastServerPort = -1;
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,8 @@
|
||||||
namespace juce
|
namespace juce
|
||||||
{
|
{
|
||||||
|
|
||||||
class ThreadPool::ThreadPoolThread : public Thread
|
struct ThreadPool::ThreadPoolThread : public Thread
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
ThreadPoolThread (ThreadPool& p, size_t stackSize)
|
ThreadPoolThread (ThreadPool& p, size_t stackSize)
|
||||||
: Thread ("Pool", stackSize), pool (p)
|
: Thread ("Pool", stackSize), pool (p)
|
||||||
{
|
{
|
||||||
|
|
@ -38,7 +37,7 @@ public:
|
||||||
wait (500);
|
wait (500);
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadPoolJob* volatile currentJob = nullptr;
|
std::atomic<ThreadPoolJob*> currentJob { nullptr };
|
||||||
ThreadPool& pool;
|
ThreadPool& pool;
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolThread)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolThread)
|
||||||
|
|
@ -85,7 +84,7 @@ void ThreadPoolJob::removeListener (Thread::Listener* listener)
|
||||||
ThreadPoolJob* ThreadPoolJob::getCurrentThreadPoolJob()
|
ThreadPoolJob* ThreadPoolJob::getCurrentThreadPoolJob()
|
||||||
{
|
{
|
||||||
if (auto* t = dynamic_cast<ThreadPool::ThreadPoolThread*> (Thread::getCurrentThread()))
|
if (auto* t = dynamic_cast<ThreadPool::ThreadPoolThread*> (Thread::getCurrentThread()))
|
||||||
return t->currentJob;
|
return t->currentJob.load();
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,6 @@ namespace juce
|
||||||
{
|
{
|
||||||
|
|
||||||
class ThreadPool;
|
class ThreadPool;
|
||||||
class ThreadPoolThread;
|
|
||||||
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
/**
|
/**
|
||||||
|
|
@ -134,7 +132,6 @@ public:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
private:
|
private:
|
||||||
friend class ThreadPool;
|
friend class ThreadPool;
|
||||||
friend class ThreadPoolThread;
|
|
||||||
String jobName;
|
String jobName;
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
bool shouldStop = false, isActive = false, shouldBeDeleted = false;
|
bool shouldStop = false, isActive = false, shouldBeDeleted = false;
|
||||||
|
|
@ -323,9 +320,9 @@ private:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
Array<ThreadPoolJob*> jobs;
|
Array<ThreadPoolJob*> jobs;
|
||||||
|
|
||||||
class ThreadPoolThread;
|
struct ThreadPoolThread;
|
||||||
friend class ThreadPoolJob;
|
friend class ThreadPoolJob;
|
||||||
friend class ThreadPoolThread;
|
friend struct ThreadPoolThread;
|
||||||
friend struct ContainerDeletePolicy<ThreadPoolThread>;
|
friend struct ContainerDeletePolicy<ThreadPoolThread>;
|
||||||
OwnedArray<ThreadPoolThread> threads;
|
OwnedArray<ThreadPoolThread> threads;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
WaitableEvent finished;
|
WaitableEvent finished;
|
||||||
void* volatile result = nullptr;
|
std::atomic<void*> result { nullptr };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MessageCallbackFunction* const func;
|
MessageCallbackFunction* const func;
|
||||||
|
|
@ -177,7 +177,7 @@ void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* cons
|
||||||
if (message->post())
|
if (message->post())
|
||||||
{
|
{
|
||||||
message->finished.wait();
|
message->finished.wait();
|
||||||
return message->result;
|
return message->result.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
jassertfalse; // the OS message queue failed to send the message!
|
jassertfalse; // the OS message queue failed to send the message!
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue