mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-30 02:50:05 +00:00
Added a hasEditor() virtual method to the AudioProcessor class, which you'll need to implement in your plugins so that we can work around non-standard threading behaviour in Wavelab.
This commit is contained in:
parent
38c64a7840
commit
9168728a7e
16 changed files with 1305 additions and 1400 deletions
|
|
@ -32,10 +32,11 @@ public:
|
|||
void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages);
|
||||
|
||||
//==============================================================================
|
||||
bool hasEditor() const { return true; }
|
||||
AudioProcessorEditor* createEditor();
|
||||
|
||||
//==============================================================================
|
||||
const String getName() const { return JucePlugin_Name; }
|
||||
const String getName() const { return JucePlugin_Name; }
|
||||
|
||||
int getNumParameters();
|
||||
float getParameter (int index);
|
||||
|
|
|
|||
|
|
@ -352,34 +352,20 @@ public:
|
|||
|
||||
void open()
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
if (editorComp == 0)
|
||||
{
|
||||
checkWhetherWavelabHasChangedThread();
|
||||
const MessageManagerLock mmLock;
|
||||
|
||||
AudioProcessorEditor* const ed = filter->createEditorIfNeeded();
|
||||
|
||||
if (ed != 0)
|
||||
cEffect.flags |= effFlagsHasEditor;
|
||||
else
|
||||
cEffect.flags &= ~effFlagsHasEditor;
|
||||
|
||||
filter->editorBeingDeleted (ed);
|
||||
delete ed;
|
||||
}
|
||||
|
||||
startTimer (1000 / 4);
|
||||
// Note: most hosts call this on the UI thread, but wavelab doesn't, so be careful in here.
|
||||
if (filter->hasEditor())
|
||||
cEffect.flags |= effFlagsHasEditor;
|
||||
else
|
||||
cEffect.flags &= ~effFlagsHasEditor;
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
const NonWavelabMMLock mmLock;
|
||||
jassert (! recursionCheck);
|
||||
|
||||
// Note: most hosts call this on the UI thread, but wavelab doesn't, so be careful in here.
|
||||
stopTimer();
|
||||
deleteEditor (false);
|
||||
|
||||
if (MessageManager::getInstance()->isThisTheMessageThread())
|
||||
deleteEditor (false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -439,8 +425,10 @@ public:
|
|||
{
|
||||
result = 1;
|
||||
}
|
||||
else if (strcmp (text, "openCloseAnyThread" == 0)
|
||||
else if (strcmp (text, "openCloseAnyThread") == 0)
|
||||
{
|
||||
// This tells Wavelab to use the UI thread to invoke open/close,
|
||||
// like all other hosts do.
|
||||
result = -1;
|
||||
}
|
||||
|
||||
|
|
@ -770,48 +758,22 @@ public:
|
|||
|
||||
switch (ti->smpteFrameRate)
|
||||
{
|
||||
case kVstSmpte24fps:
|
||||
rate = AudioPlayHead::fps24;
|
||||
fps = 24.0;
|
||||
break;
|
||||
case kVstSmpte24fps: rate = AudioPlayHead::fps24; fps = 24.0; break;
|
||||
case kVstSmpte25fps: rate = AudioPlayHead::fps25; fps = 25.0; break;
|
||||
case kVstSmpte2997fps: rate = AudioPlayHead::fps2997; fps = 29.97; break;
|
||||
case kVstSmpte30fps: rate = AudioPlayHead::fps30; fps = 30.0; break;
|
||||
case kVstSmpte2997dfps: rate = AudioPlayHead::fps2997drop; fps = 29.97; break;
|
||||
case kVstSmpte30dfps: rate = AudioPlayHead::fps30drop; fps = 30.0; break;
|
||||
|
||||
case kVstSmpte25fps:
|
||||
rate = AudioPlayHead::fps25;
|
||||
fps = 25.0;
|
||||
break;
|
||||
case kVstSmpteFilm16mm:
|
||||
case kVstSmpteFilm35mm: fps = 24.0; break;
|
||||
|
||||
case kVstSmpte2997fps:
|
||||
rate = AudioPlayHead::fps2997;
|
||||
fps = 29.97;
|
||||
break;
|
||||
case kVstSmpte239fps: fps = 23.976; break;
|
||||
case kVstSmpte249fps: fps = 24.976; break;
|
||||
case kVstSmpte599fps: fps = 59.94; break;
|
||||
case kVstSmpte60fps: fps = 60; break;
|
||||
|
||||
case kVstSmpte30fps:
|
||||
rate = AudioPlayHead::fps30;
|
||||
fps = 30.0;
|
||||
break;
|
||||
|
||||
case kVstSmpte2997dfps:
|
||||
rate = AudioPlayHead::fps2997drop;
|
||||
fps = 29.97;
|
||||
break;
|
||||
|
||||
case kVstSmpte30dfps:
|
||||
rate = AudioPlayHead::fps30drop;
|
||||
fps = 30.0;
|
||||
break;
|
||||
|
||||
case kVstSmpteFilm16mm:
|
||||
case kVstSmpteFilm35mm:
|
||||
fps = 24.0;
|
||||
break;
|
||||
|
||||
case kVstSmpte239fps: fps = 23.976; break;
|
||||
case kVstSmpte249fps: fps = 24.976; break;
|
||||
case kVstSmpte599fps: fps = 59.94; break;
|
||||
case kVstSmpte60fps: fps = 60; break;
|
||||
|
||||
default:
|
||||
jassertfalse // unknown frame-rate..
|
||||
default: jassertfalse; // unknown frame-rate..
|
||||
}
|
||||
|
||||
info.frameRate = rate;
|
||||
|
|
@ -1068,6 +1030,7 @@ public:
|
|||
{
|
||||
recursionCheck = true;
|
||||
|
||||
JUCE_AUTORELEASEPOOL
|
||||
juce_callAnyTimersSynchronously();
|
||||
|
||||
for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
|
||||
|
|
@ -1105,6 +1068,7 @@ public:
|
|||
|
||||
void deleteEditor (bool canDeleteLaterIfModal)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
PopupMenu::dismissAllActiveMenus();
|
||||
|
||||
jassert (! recursionCheck);
|
||||
|
|
@ -1161,10 +1125,12 @@ public:
|
|||
}
|
||||
else if (opCode == effEditOpen)
|
||||
{
|
||||
checkWhetherWavelabHasChangedThread();
|
||||
checkWhetherMessageThreadIsCorrect();
|
||||
const MessageManagerLock mmLock;
|
||||
jassert (! recursionCheck);
|
||||
|
||||
startTimer (1000 / 4); // performs misc housekeeping chores
|
||||
|
||||
deleteEditor (true);
|
||||
createEditorComp();
|
||||
|
||||
|
|
@ -1191,14 +1157,14 @@ public:
|
|||
}
|
||||
else if (opCode == effEditClose)
|
||||
{
|
||||
checkWhetherWavelabHasChangedThread();
|
||||
checkWhetherMessageThreadIsCorrect();
|
||||
const MessageManagerLock mmLock;
|
||||
deleteEditor (true);
|
||||
return 0;
|
||||
}
|
||||
else if (opCode == effEditGetRect)
|
||||
{
|
||||
checkWhetherWavelabHasChangedThread();
|
||||
checkWhetherMessageThreadIsCorrect();
|
||||
const MessageManagerLock mmLock;
|
||||
createEditorComp();
|
||||
|
||||
|
|
@ -1436,18 +1402,9 @@ private:
|
|||
bool shouldDeleteEditor;
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_WINDOWS // Workarounds for Wavelab's happy-go-lucky use of threads.
|
||||
class NonWavelabMMLock
|
||||
{
|
||||
public:
|
||||
NonWavelabMMLock() : mm (getHostType().isWavelab() || getHostType().isCubaseBridged() ? 0 : new MessageManagerLock()) {}
|
||||
~NonWavelabMMLock() {}
|
||||
|
||||
private:
|
||||
ScopedPointer <MessageManagerLock> mm;
|
||||
};
|
||||
|
||||
static void checkWhetherWavelabHasChangedThread()
|
||||
#if JUCE_WINDOWS
|
||||
// Workarounds for Wavelab's happy-go-lucky use of threads.
|
||||
static void checkWhetherMessageThreadIsCorrect()
|
||||
{
|
||||
if (getHostType().isWavelab() || getHostType().isCubaseBridged())
|
||||
{
|
||||
|
|
@ -1476,8 +1433,7 @@ private:
|
|||
}
|
||||
}
|
||||
#else
|
||||
typedef MessageManagerLock NonWavelabMMLock;
|
||||
static void checkWhetherWavelabHasChangedThread() {}
|
||||
static void checkWhetherMessageThreadIsCorrect() {}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
1623
juce_amalgamated.cpp
1623
juce_amalgamated.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -64,7 +64,7 @@
|
|||
*/
|
||||
#define JUCE_MAJOR_VERSION 1
|
||||
#define JUCE_MINOR_VERSION 52
|
||||
#define JUCE_BUILDNUMBER 72
|
||||
#define JUCE_BUILDNUMBER 73
|
||||
|
||||
/** Current Juce version number.
|
||||
|
||||
|
|
@ -2594,6 +2594,11 @@ private:
|
|||
|
||||
void createInternal (const juce_wchar* text, size_t numChars);
|
||||
void appendInternal (const juce_wchar* text, int numExtraChars);
|
||||
|
||||
// This private cast operator should prevent strings being accidentally cast
|
||||
// to bools (this is possible because the compiler can add an implicit cast
|
||||
// via a const char*)
|
||||
operator bool() const throw() { return false; }
|
||||
};
|
||||
|
||||
/** Concatenates two strings. */
|
||||
|
|
@ -39875,7 +39880,8 @@ public:
|
|||
a generic UI that lets the user twiddle the parameters directly.
|
||||
|
||||
If you do want to pass back a component, the component should be created and set to
|
||||
the correct size before returning it.
|
||||
the correct size before returning it. If you implement this method, you must
|
||||
also implement the hasEditor() method and make it return true.
|
||||
|
||||
Remember not to do anything silly like allowing your filter to keep a pointer to
|
||||
the component that gets created - it could be deleted later without any warning, which
|
||||
|
|
@ -39892,9 +39898,16 @@ public:
|
|||
not open one at all. Your filter mustn't rely on it being there.
|
||||
- An editor object may be deleted and a replacement one created again at any time.
|
||||
- It's safe to assume that an editor will be deleted before its filter.
|
||||
|
||||
@see hasEditor
|
||||
*/
|
||||
virtual AudioProcessorEditor* createEditor() = 0;
|
||||
|
||||
/** Your filter must override this and return true if it can create an editor component.
|
||||
@see createEditor
|
||||
*/
|
||||
virtual bool hasEditor() const = 0;
|
||||
|
||||
/** Returns the active editor, if there is one.
|
||||
|
||||
Bear in mind this can return 0, even if an editor has previously been
|
||||
|
|
@ -42011,6 +42024,7 @@ public:
|
|||
bool acceptsMidi() const;
|
||||
bool producesMidi() const;
|
||||
|
||||
bool hasEditor() const;
|
||||
AudioProcessorEditor* createEditor();
|
||||
|
||||
int getNumParameters();
|
||||
|
|
@ -42057,6 +42071,7 @@ public:
|
|||
bool acceptsMidi() const;
|
||||
bool producesMidi() const;
|
||||
|
||||
bool hasEditor() const { return false; }
|
||||
AudioProcessorEditor* createEditor() { return 0; }
|
||||
|
||||
int getNumParameters() { return 0; }
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
/* If you've got an include error here, you probably need to install the QuickTime SDK and
|
||||
add its header directory to your include path.
|
||||
|
||||
Alternatively, if you don't need any QuickTime services, just turn off the JUC_QUICKTIME
|
||||
Alternatively, if you don't need any QuickTime services, just turn off the JUCE_QUICKTIME
|
||||
flag in juce_Config.h
|
||||
*/
|
||||
#include <Movies.h>
|
||||
|
|
@ -70,7 +70,7 @@ BEGIN_JUCE_NAMESPACE
|
|||
bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle& dataHandle);
|
||||
|
||||
static const char* const quickTimeFormatName = "QuickTime file";
|
||||
static const char* const quickTimeExtensions[] = { ".mov", ".mp3", ".mp4", 0 };
|
||||
static const char* const quickTimeExtensions[] = { ".mov", ".mp3", ".mp4", ".m4a", 0 };
|
||||
|
||||
//==============================================================================
|
||||
class QTAudioReader : public AudioFormatReader
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ BEGIN_JUCE_NAMESPACE
|
|||
#include "../../gui/components/juce_Desktop.h"
|
||||
#include "../../text/juce_LocalisedStrings.h"
|
||||
#include "../dsp/juce_AudioSampleBuffer.h"
|
||||
#include "../../core/juce_SystemStats.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -231,6 +231,7 @@ public:
|
|||
void processBlock (AudioSampleBuffer& buffer,
|
||||
MidiBuffer& midiMessages);
|
||||
|
||||
bool hasEditor() const;
|
||||
AudioProcessorEditor* createEditor();
|
||||
|
||||
const String getInputChannelName (int index) const;
|
||||
|
|
@ -1087,6 +1088,11 @@ private:
|
|||
#endif
|
||||
|
||||
//==============================================================================
|
||||
bool AudioUnitPluginInstance::hasEditor() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
AudioProcessorEditor* AudioUnitPluginInstance::createEditor()
|
||||
{
|
||||
ScopedPointer<AudioProcessorEditor> w (new AudioUnitPluginWindowCocoa (*this, false));
|
||||
|
|
|
|||
|
|
@ -729,6 +729,7 @@ public:
|
|||
void processBlock (AudioSampleBuffer& buffer,
|
||||
MidiBuffer& midiMessages);
|
||||
|
||||
bool hasEditor() const { return effect != 0 && (effect->flags & effFlagsHasEditor) != 0; }
|
||||
AudioProcessorEditor* createEditor();
|
||||
|
||||
const String getInputChannelName (int index) const;
|
||||
|
|
@ -805,7 +806,6 @@ private:
|
|||
const String getVersion() const;
|
||||
const String getCategory() const;
|
||||
|
||||
bool hasEditor() const throw() { return effect != 0 && (effect->flags & effFlagsHasEditor) != 0; }
|
||||
void setPower (const bool on);
|
||||
|
||||
VSTPluginInstance (const ReferenceCountedObjectPtr <ModuleHandle>& module);
|
||||
|
|
|
|||
|
|
@ -231,6 +231,9 @@ AudioProcessorEditor* AudioProcessor::createEditorIfNeeded()
|
|||
|
||||
AudioProcessorEditor* const ed = createEditor();
|
||||
|
||||
// You must make your hasEditor() method return a consistent result!
|
||||
jassert (hasEditor() == (ed != 0));
|
||||
|
||||
if (ed != 0)
|
||||
{
|
||||
// you must give your editor comp a size before returning it..
|
||||
|
|
|
|||
|
|
@ -318,7 +318,8 @@ public:
|
|||
a generic UI that lets the user twiddle the parameters directly.
|
||||
|
||||
If you do want to pass back a component, the component should be created and set to
|
||||
the correct size before returning it.
|
||||
the correct size before returning it. If you implement this method, you must
|
||||
also implement the hasEditor() method and make it return true.
|
||||
|
||||
Remember not to do anything silly like allowing your filter to keep a pointer to
|
||||
the component that gets created - it could be deleted later without any warning, which
|
||||
|
|
@ -335,9 +336,16 @@ public:
|
|||
not open one at all. Your filter mustn't rely on it being there.
|
||||
- An editor object may be deleted and a replacement one created again at any time.
|
||||
- It's safe to assume that an editor will be deleted before its filter.
|
||||
|
||||
@see hasEditor
|
||||
*/
|
||||
virtual AudioProcessorEditor* createEditor() = 0;
|
||||
|
||||
/** Your filter must override this and return true if it can create an editor component.
|
||||
@see createEditor
|
||||
*/
|
||||
virtual bool hasEditor() const = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the active editor, if there is one.
|
||||
|
||||
|
|
|
|||
|
|
@ -1276,10 +1276,8 @@ bool AudioProcessorGraph::AudioGraphIOProcessor::isOutput() const
|
|||
return type == audioOutputNode || type == midiOutputNode;
|
||||
}
|
||||
|
||||
AudioProcessorEditor* AudioProcessorGraph::AudioGraphIOProcessor::createEditor()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
bool AudioProcessorGraph::AudioGraphIOProcessor::hasEditor() const { return false; }
|
||||
AudioProcessorEditor* AudioProcessorGraph::AudioGraphIOProcessor::createEditor() { return 0; }
|
||||
|
||||
int AudioProcessorGraph::AudioGraphIOProcessor::getNumParameters() { return 0; }
|
||||
const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterName (int) { return String::empty; }
|
||||
|
|
|
|||
|
|
@ -335,6 +335,7 @@ public:
|
|||
bool acceptsMidi() const;
|
||||
bool producesMidi() const;
|
||||
|
||||
bool hasEditor() const;
|
||||
AudioProcessorEditor* createEditor();
|
||||
|
||||
int getNumParameters();
|
||||
|
|
@ -382,6 +383,7 @@ public:
|
|||
bool acceptsMidi() const;
|
||||
bool producesMidi() const;
|
||||
|
||||
bool hasEditor() const { return false; }
|
||||
AudioProcessorEditor* createEditor() { return 0; }
|
||||
|
||||
int getNumParameters() { return 0; }
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
*/
|
||||
#define JUCE_MAJOR_VERSION 1
|
||||
#define JUCE_MINOR_VERSION 52
|
||||
#define JUCE_BUILDNUMBER 72
|
||||
#define JUCE_BUILDNUMBER 73
|
||||
|
||||
/** Current Juce version number.
|
||||
|
||||
|
|
|
|||
|
|
@ -30,153 +30,315 @@
|
|||
#if JUCE_MAC
|
||||
|
||||
//==============================================================================
|
||||
#undef log
|
||||
#define log(a) Logger::writeToLog(a)
|
||||
|
||||
static bool logAnyErrorsMidi (const OSStatus err, const int lineNum)
|
||||
namespace CoreMidiHelpers
|
||||
{
|
||||
if (err == noErr)
|
||||
return true;
|
||||
|
||||
log ("CoreMidi error: " + String (lineNum) + " - " + String::toHexString ((int) err));
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
|
||||
#undef OK
|
||||
#define OK(a) logAnyErrorsMidi(a, __LINE__)
|
||||
|
||||
|
||||
//==============================================================================
|
||||
static const String getEndpointName (MIDIEndpointRef endpoint, bool isExternal)
|
||||
{
|
||||
String result;
|
||||
CFStringRef str = 0;
|
||||
|
||||
MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &str);
|
||||
|
||||
if (str != 0)
|
||||
static bool logError (const OSStatus err, const int lineNum)
|
||||
{
|
||||
result = PlatformUtilities::cfStringToJuceString (str);
|
||||
CFRelease (str);
|
||||
str = 0;
|
||||
if (err == noErr)
|
||||
return true;
|
||||
|
||||
Logger::writeToLog ("CoreMidi error: " + String (lineNum) + " - " + String::toHexString ((int) err));
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
|
||||
MIDIEntityRef entity = 0;
|
||||
MIDIEndpointGetEntity (endpoint, &entity);
|
||||
#undef CHECK_ERROR
|
||||
#define CHECK_ERROR(a) CoreMidiHelpers::logError (a, __LINE__)
|
||||
|
||||
if (entity == 0)
|
||||
return result; // probably virtual
|
||||
|
||||
if (result.isEmpty())
|
||||
//==============================================================================
|
||||
static const String getEndpointName (MIDIEndpointRef endpoint, bool isExternal)
|
||||
{
|
||||
// endpoint name has zero length - try the entity
|
||||
MIDIObjectGetStringProperty (entity, kMIDIPropertyName, &str);
|
||||
String result;
|
||||
CFStringRef str = 0;
|
||||
|
||||
MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &str);
|
||||
|
||||
if (str != 0)
|
||||
{
|
||||
result += PlatformUtilities::cfStringToJuceString (str);
|
||||
result = PlatformUtilities::cfStringToJuceString (str);
|
||||
CFRelease (str);
|
||||
str = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// now consider the device's name
|
||||
MIDIDeviceRef device = 0;
|
||||
MIDIEntityGetDevice (entity, &device);
|
||||
if (device == 0)
|
||||
return result;
|
||||
MIDIEntityRef entity = 0;
|
||||
MIDIEndpointGetEntity (endpoint, &entity);
|
||||
|
||||
MIDIObjectGetStringProperty (device, kMIDIPropertyName, &str);
|
||||
if (entity == 0)
|
||||
return result; // probably virtual
|
||||
|
||||
if (str != 0)
|
||||
{
|
||||
const String s (PlatformUtilities::cfStringToJuceString (str));
|
||||
CFRelease (str);
|
||||
|
||||
// if an external device has only one entity, throw away
|
||||
// the endpoint name and just use the device name
|
||||
if (isExternal && MIDIDeviceGetNumberOfEntities (device) < 2)
|
||||
if (result.isEmpty())
|
||||
{
|
||||
result = s;
|
||||
}
|
||||
else if (! result.startsWithIgnoreCase (s))
|
||||
{
|
||||
// prepend the device name to the entity name
|
||||
result = (s + " " + result).trimEnd();
|
||||
}
|
||||
}
|
||||
// endpoint name has zero length - try the entity
|
||||
MIDIObjectGetStringProperty (entity, kMIDIPropertyName, &str);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static const String getConnectedEndpointName (MIDIEndpointRef endpoint)
|
||||
{
|
||||
String result;
|
||||
|
||||
// Does the endpoint have connections?
|
||||
CFDataRef connections = 0;
|
||||
int numConnections = 0;
|
||||
|
||||
MIDIObjectGetDataProperty (endpoint, kMIDIPropertyConnectionUniqueID, &connections);
|
||||
|
||||
if (connections != 0)
|
||||
{
|
||||
numConnections = (int) (CFDataGetLength (connections) / sizeof (MIDIUniqueID));
|
||||
|
||||
if (numConnections > 0)
|
||||
{
|
||||
const SInt32* pid = reinterpret_cast <const SInt32*> (CFDataGetBytePtr (connections));
|
||||
|
||||
for (int i = 0; i < numConnections; ++i, ++pid)
|
||||
if (str != 0)
|
||||
{
|
||||
MIDIUniqueID uid = EndianS32_BtoN (*pid);
|
||||
MIDIObjectRef connObject;
|
||||
MIDIObjectType connObjectType;
|
||||
OSStatus err = MIDIObjectFindByUniqueID (uid, &connObject, &connObjectType);
|
||||
|
||||
if (err == noErr)
|
||||
{
|
||||
String s;
|
||||
|
||||
if (connObjectType == kMIDIObjectType_ExternalSource
|
||||
|| connObjectType == kMIDIObjectType_ExternalDestination)
|
||||
{
|
||||
// Connected to an external device's endpoint (10.3 and later).
|
||||
s = getEndpointName (static_cast <MIDIEndpointRef> (connObject), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Connected to an external device (10.2) (or something else, catch-all)
|
||||
CFStringRef str = 0;
|
||||
MIDIObjectGetStringProperty (connObject, kMIDIPropertyName, &str);
|
||||
|
||||
if (str != 0)
|
||||
{
|
||||
s = PlatformUtilities::cfStringToJuceString (str);
|
||||
CFRelease (str);
|
||||
}
|
||||
}
|
||||
|
||||
if (s.isNotEmpty())
|
||||
{
|
||||
if (result.isNotEmpty())
|
||||
result += ", ";
|
||||
|
||||
result += s;
|
||||
}
|
||||
}
|
||||
result += PlatformUtilities::cfStringToJuceString (str);
|
||||
CFRelease (str);
|
||||
str = 0;
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease (connections);
|
||||
// now consider the device's name
|
||||
MIDIDeviceRef device = 0;
|
||||
MIDIEntityGetDevice (entity, &device);
|
||||
if (device == 0)
|
||||
return result;
|
||||
|
||||
MIDIObjectGetStringProperty (device, kMIDIPropertyName, &str);
|
||||
|
||||
if (str != 0)
|
||||
{
|
||||
const String s (PlatformUtilities::cfStringToJuceString (str));
|
||||
CFRelease (str);
|
||||
|
||||
// if an external device has only one entity, throw away
|
||||
// the endpoint name and just use the device name
|
||||
if (isExternal && MIDIDeviceGetNumberOfEntities (device) < 2)
|
||||
{
|
||||
result = s;
|
||||
}
|
||||
else if (! result.startsWithIgnoreCase (s))
|
||||
{
|
||||
// prepend the device name to the entity name
|
||||
result = (s + " " + result).trimEnd();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (result.isNotEmpty())
|
||||
return result;
|
||||
static const String getConnectedEndpointName (MIDIEndpointRef endpoint)
|
||||
{
|
||||
String result;
|
||||
|
||||
// Here, either the endpoint had no connections, or we failed to obtain names for any of them.
|
||||
return getEndpointName (endpoint, false);
|
||||
// Does the endpoint have connections?
|
||||
CFDataRef connections = 0;
|
||||
int numConnections = 0;
|
||||
|
||||
MIDIObjectGetDataProperty (endpoint, kMIDIPropertyConnectionUniqueID, &connections);
|
||||
|
||||
if (connections != 0)
|
||||
{
|
||||
numConnections = (int) (CFDataGetLength (connections) / sizeof (MIDIUniqueID));
|
||||
|
||||
if (numConnections > 0)
|
||||
{
|
||||
const SInt32* pid = reinterpret_cast <const SInt32*> (CFDataGetBytePtr (connections));
|
||||
|
||||
for (int i = 0; i < numConnections; ++i, ++pid)
|
||||
{
|
||||
MIDIUniqueID uid = EndianS32_BtoN (*pid);
|
||||
MIDIObjectRef connObject;
|
||||
MIDIObjectType connObjectType;
|
||||
OSStatus err = MIDIObjectFindByUniqueID (uid, &connObject, &connObjectType);
|
||||
|
||||
if (err == noErr)
|
||||
{
|
||||
String s;
|
||||
|
||||
if (connObjectType == kMIDIObjectType_ExternalSource
|
||||
|| connObjectType == kMIDIObjectType_ExternalDestination)
|
||||
{
|
||||
// Connected to an external device's endpoint (10.3 and later).
|
||||
s = getEndpointName (static_cast <MIDIEndpointRef> (connObject), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Connected to an external device (10.2) (or something else, catch-all)
|
||||
CFStringRef str = 0;
|
||||
MIDIObjectGetStringProperty (connObject, kMIDIPropertyName, &str);
|
||||
|
||||
if (str != 0)
|
||||
{
|
||||
s = PlatformUtilities::cfStringToJuceString (str);
|
||||
CFRelease (str);
|
||||
}
|
||||
}
|
||||
|
||||
if (s.isNotEmpty())
|
||||
{
|
||||
if (result.isNotEmpty())
|
||||
result += ", ";
|
||||
|
||||
result += s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease (connections);
|
||||
}
|
||||
|
||||
if (result.isNotEmpty())
|
||||
return result;
|
||||
|
||||
// Here, either the endpoint had no connections, or we failed to obtain names for any of them.
|
||||
return getEndpointName (endpoint, false);
|
||||
}
|
||||
|
||||
static MIDIClientRef getGlobalMidiClient()
|
||||
{
|
||||
static MIDIClientRef globalMidiClient = 0;
|
||||
|
||||
if (globalMidiClient == 0)
|
||||
{
|
||||
String name ("JUCE");
|
||||
|
||||
if (JUCEApplication::getInstance() != 0)
|
||||
name = JUCEApplication::getInstance()->getApplicationName();
|
||||
|
||||
CFStringRef appName = PlatformUtilities::juceStringToCFString (name);
|
||||
CHECK_ERROR (MIDIClientCreate (appName, 0, 0, &globalMidiClient));
|
||||
CFRelease (appName);
|
||||
}
|
||||
|
||||
return globalMidiClient;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class MidiPortAndEndpoint
|
||||
{
|
||||
public:
|
||||
MidiPortAndEndpoint (MIDIPortRef port_, MIDIEndpointRef endPoint_)
|
||||
: port (port_), endPoint (endPoint_)
|
||||
{
|
||||
}
|
||||
|
||||
~MidiPortAndEndpoint()
|
||||
{
|
||||
if (port != 0)
|
||||
MIDIPortDispose (port);
|
||||
|
||||
if (port == 0 && endPoint != 0) // if port == 0, it means we created the endpoint, so it's safe to delete it
|
||||
MIDIEndpointDispose (endPoint);
|
||||
}
|
||||
|
||||
MIDIPortRef port;
|
||||
MIDIEndpointRef endPoint;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class MidiPortAndCallback
|
||||
{
|
||||
public:
|
||||
MidiInput* input;
|
||||
MidiPortAndEndpoint* portAndEndpoint;
|
||||
MidiInputCallback* callback;
|
||||
MemoryBlock pendingData;
|
||||
int pendingBytes;
|
||||
double pendingDataTime;
|
||||
bool active;
|
||||
|
||||
void processSysex (const uint8*& d, int& size, const double time)
|
||||
{
|
||||
if (*d == 0xf0)
|
||||
{
|
||||
pendingBytes = 0;
|
||||
pendingDataTime = time;
|
||||
}
|
||||
|
||||
pendingData.ensureSize (pendingBytes + size, false);
|
||||
uint8* totalMessage = (uint8*) pendingData.getData();
|
||||
|
||||
uint8* dest = totalMessage + pendingBytes;
|
||||
|
||||
while (size > 0)
|
||||
{
|
||||
if (pendingBytes > 0 && *d >= 0x80)
|
||||
{
|
||||
if (*d >= 0xfa || *d == 0xf8)
|
||||
{
|
||||
callback->handleIncomingMidiMessage (input, MidiMessage (*d, time));
|
||||
++d;
|
||||
--size;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*d == 0xf7)
|
||||
{
|
||||
*dest++ = *d++;
|
||||
pendingBytes++;
|
||||
--size;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*dest++ = *d++;
|
||||
pendingBytes++;
|
||||
--size;
|
||||
}
|
||||
}
|
||||
|
||||
if (totalMessage [pendingBytes - 1] == 0xf7)
|
||||
{
|
||||
callback->handleIncomingMidiMessage (input, MidiMessage (totalMessage, pendingBytes, pendingDataTime));
|
||||
pendingBytes = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
callback->handlePartialSysexMessage (input, totalMessage, pendingBytes, pendingDataTime);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static CriticalSection callbackLock;
|
||||
static Array<void*> activeCallbacks;
|
||||
|
||||
static void midiInputProc (const MIDIPacketList* pktlist,
|
||||
void* readProcRefCon,
|
||||
void* /*srcConnRefCon*/)
|
||||
{
|
||||
double time = Time::getMillisecondCounterHiRes() * 0.001;
|
||||
const double originalTime = time;
|
||||
|
||||
MidiPortAndCallback* const mpc = (MidiPortAndCallback*) readProcRefCon;
|
||||
const ScopedLock sl (CoreMidiHelpers::callbackLock);
|
||||
|
||||
if (CoreMidiHelpers::activeCallbacks.contains (mpc) && mpc->active)
|
||||
{
|
||||
const MIDIPacket* packet = &pktlist->packet[0];
|
||||
|
||||
for (unsigned int i = 0; i < pktlist->numPackets; ++i)
|
||||
{
|
||||
const uint8* d = (const uint8*) (packet->data);
|
||||
int size = packet->length;
|
||||
|
||||
while (size > 0)
|
||||
{
|
||||
time = originalTime;
|
||||
|
||||
if (mpc->pendingBytes > 0 || d[0] == 0xf0)
|
||||
{
|
||||
mpc->processSysex (d, size, time);
|
||||
}
|
||||
else
|
||||
{
|
||||
int used = 0;
|
||||
const MidiMessage m (d, size, used, 0, time);
|
||||
|
||||
if (used <= 0)
|
||||
{
|
||||
jassertfalse; // malformed midi message
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
mpc->callback->handleIncomingMidiMessage (mpc->input, m);
|
||||
}
|
||||
|
||||
size -= used;
|
||||
d += used;
|
||||
}
|
||||
}
|
||||
|
||||
packet = MIDIPacketNext (packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -191,7 +353,7 @@ const StringArray MidiOutput::getDevices()
|
|||
|
||||
if (dest != 0)
|
||||
{
|
||||
String name (getConnectedEndpointName (dest));
|
||||
String name (CoreMidiHelpers::getConnectedEndpointName (dest));
|
||||
|
||||
if (name.isEmpty())
|
||||
name = "<error>";
|
||||
|
|
@ -212,48 +374,6 @@ int MidiOutput::getDefaultDeviceIndex()
|
|||
return 0;
|
||||
}
|
||||
|
||||
static MIDIClientRef globalMidiClient;
|
||||
static bool hasGlobalClientBeenCreated = false;
|
||||
|
||||
static bool makeSureClientExists()
|
||||
{
|
||||
if (! hasGlobalClientBeenCreated)
|
||||
{
|
||||
String name ("JUCE");
|
||||
|
||||
if (JUCEApplication::getInstance() != 0)
|
||||
name = JUCEApplication::getInstance()->getApplicationName();
|
||||
|
||||
CFStringRef appName = PlatformUtilities::juceStringToCFString (name);
|
||||
|
||||
hasGlobalClientBeenCreated = OK (MIDIClientCreate (appName, 0, 0, &globalMidiClient));
|
||||
CFRelease (appName);
|
||||
}
|
||||
|
||||
return hasGlobalClientBeenCreated;
|
||||
}
|
||||
|
||||
class MidiPortAndEndpoint
|
||||
{
|
||||
public:
|
||||
MidiPortAndEndpoint (MIDIPortRef port_, MIDIEndpointRef endPoint_)
|
||||
: port (port_), endPoint (endPoint_)
|
||||
{
|
||||
}
|
||||
|
||||
~MidiPortAndEndpoint()
|
||||
{
|
||||
if (port != 0)
|
||||
MIDIPortDispose (port);
|
||||
|
||||
if (port == 0 && endPoint != 0) // if port == 0, it means we created the endpoint, so it's safe to delete it
|
||||
MIDIEndpointDispose (endPoint);
|
||||
}
|
||||
|
||||
MIDIPortRef port;
|
||||
MIDIEndpointRef endPoint;
|
||||
};
|
||||
|
||||
MidiOutput* MidiOutput::openDevice (int index)
|
||||
{
|
||||
MidiOutput* mo = 0;
|
||||
|
|
@ -263,19 +383,15 @@ MidiOutput* MidiOutput::openDevice (int index)
|
|||
MIDIEndpointRef endPoint = MIDIGetDestination (index);
|
||||
|
||||
CFStringRef pname;
|
||||
if (OK (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname)))
|
||||
if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname)))
|
||||
{
|
||||
log ("CoreMidi - opening out: " + PlatformUtilities::cfStringToJuceString (pname));
|
||||
MIDIClientRef client = CoreMidiHelpers::getGlobalMidiClient();
|
||||
MIDIPortRef port;
|
||||
|
||||
if (makeSureClientExists())
|
||||
if (client != 0 && CHECK_ERROR (MIDIOutputPortCreate (client, pname, &port)))
|
||||
{
|
||||
MIDIPortRef port;
|
||||
|
||||
if (OK (MIDIOutputPortCreate (globalMidiClient, pname, &port)))
|
||||
{
|
||||
mo = new MidiOutput();
|
||||
mo->internal = new MidiPortAndEndpoint (port, endPoint);
|
||||
}
|
||||
mo = new MidiOutput();
|
||||
mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (port, endPoint);
|
||||
}
|
||||
|
||||
CFRelease (pname);
|
||||
|
|
@ -288,27 +404,24 @@ MidiOutput* MidiOutput::openDevice (int index)
|
|||
MidiOutput* MidiOutput::createNewDevice (const String& deviceName)
|
||||
{
|
||||
MidiOutput* mo = 0;
|
||||
MIDIClientRef client = CoreMidiHelpers::getGlobalMidiClient();
|
||||
|
||||
if (makeSureClientExists())
|
||||
MIDIEndpointRef endPoint;
|
||||
CFStringRef name = PlatformUtilities::juceStringToCFString (deviceName);
|
||||
|
||||
if (client != 0 && CHECK_ERROR (MIDISourceCreate (client, name, &endPoint)))
|
||||
{
|
||||
MIDIEndpointRef endPoint;
|
||||
CFStringRef name = PlatformUtilities::juceStringToCFString (deviceName);
|
||||
|
||||
if (OK (MIDISourceCreate (globalMidiClient, name, &endPoint)))
|
||||
{
|
||||
mo = new MidiOutput();
|
||||
mo->internal = new MidiPortAndEndpoint (0, endPoint);
|
||||
}
|
||||
|
||||
CFRelease (name);
|
||||
mo = new MidiOutput();
|
||||
mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (0, endPoint);
|
||||
}
|
||||
|
||||
CFRelease (name);
|
||||
return mo;
|
||||
}
|
||||
|
||||
MidiOutput::~MidiOutput()
|
||||
{
|
||||
delete static_cast<MidiPortAndEndpoint*> (internal);
|
||||
delete static_cast<CoreMidiHelpers::MidiPortAndEndpoint*> (internal);
|
||||
}
|
||||
|
||||
void MidiOutput::reset()
|
||||
|
|
@ -326,7 +439,7 @@ void MidiOutput::setVolume (float /*leftVol*/, float /*rightVol*/)
|
|||
|
||||
void MidiOutput::sendMessageNow (const MidiMessage& message)
|
||||
{
|
||||
MidiPortAndEndpoint* const mpe = static_cast<MidiPortAndEndpoint*> (internal);
|
||||
CoreMidiHelpers::MidiPortAndEndpoint* const mpe = static_cast<CoreMidiHelpers::MidiPortAndEndpoint*> (internal);
|
||||
|
||||
if (message.isSysEx())
|
||||
{
|
||||
|
|
@ -381,7 +494,7 @@ const StringArray MidiInput::getDevices()
|
|||
|
||||
if (source != 0)
|
||||
{
|
||||
String name (getConnectedEndpointName (source));
|
||||
String name (CoreMidiHelpers::getConnectedEndpointName (source));
|
||||
|
||||
if (name.isEmpty())
|
||||
name = "<error>";
|
||||
|
|
@ -402,132 +515,9 @@ int MidiInput::getDefaultDeviceIndex()
|
|||
return 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct MidiPortAndCallback
|
||||
{
|
||||
MidiInput* input;
|
||||
MidiPortAndEndpoint* portAndEndpoint;
|
||||
MidiInputCallback* callback;
|
||||
MemoryBlock pendingData;
|
||||
int pendingBytes;
|
||||
double pendingDataTime;
|
||||
bool active;
|
||||
|
||||
void processSysex (const uint8*& d, int& size, const double time)
|
||||
{
|
||||
if (*d == 0xf0)
|
||||
{
|
||||
pendingBytes = 0;
|
||||
pendingDataTime = time;
|
||||
}
|
||||
|
||||
pendingData.ensureSize (pendingBytes + size, false);
|
||||
uint8* totalMessage = (uint8*) pendingData.getData();
|
||||
|
||||
uint8* dest = totalMessage + pendingBytes;
|
||||
|
||||
while (size > 0)
|
||||
{
|
||||
if (pendingBytes > 0 && *d >= 0x80)
|
||||
{
|
||||
if (*d >= 0xfa || *d == 0xf8)
|
||||
{
|
||||
callback->handleIncomingMidiMessage (input, MidiMessage (*d, time));
|
||||
++d;
|
||||
--size;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*d == 0xf7)
|
||||
{
|
||||
*dest++ = *d++;
|
||||
pendingBytes++;
|
||||
--size;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*dest++ = *d++;
|
||||
pendingBytes++;
|
||||
--size;
|
||||
}
|
||||
}
|
||||
|
||||
if (totalMessage [pendingBytes - 1] == 0xf7)
|
||||
{
|
||||
callback->handleIncomingMidiMessage (input, MidiMessage (totalMessage, pendingBytes, pendingDataTime));
|
||||
pendingBytes = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
callback->handlePartialSysexMessage (input, totalMessage, pendingBytes, pendingDataTime);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
namespace CoreMidiCallbacks
|
||||
{
|
||||
static CriticalSection callbackLock;
|
||||
static Array<void*> activeCallbacks;
|
||||
}
|
||||
|
||||
static void midiInputProc (const MIDIPacketList* pktlist,
|
||||
void* readProcRefCon,
|
||||
void* /*srcConnRefCon*/)
|
||||
{
|
||||
double time = Time::getMillisecondCounterHiRes() * 0.001;
|
||||
const double originalTime = time;
|
||||
|
||||
MidiPortAndCallback* const mpc = (MidiPortAndCallback*) readProcRefCon;
|
||||
const ScopedLock sl (CoreMidiCallbacks::callbackLock);
|
||||
|
||||
if (CoreMidiCallbacks::activeCallbacks.contains (mpc) && mpc->active)
|
||||
{
|
||||
const MIDIPacket* packet = &pktlist->packet[0];
|
||||
|
||||
for (unsigned int i = 0; i < pktlist->numPackets; ++i)
|
||||
{
|
||||
const uint8* d = (const uint8*) (packet->data);
|
||||
int size = packet->length;
|
||||
|
||||
while (size > 0)
|
||||
{
|
||||
time = originalTime;
|
||||
|
||||
if (mpc->pendingBytes > 0 || d[0] == 0xf0)
|
||||
{
|
||||
mpc->processSysex (d, size, time);
|
||||
}
|
||||
else
|
||||
{
|
||||
int used = 0;
|
||||
const MidiMessage m (d, size, used, 0, time);
|
||||
|
||||
if (used <= 0)
|
||||
{
|
||||
jassertfalse; // malformed midi message
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
mpc->callback->handleIncomingMidiMessage (mpc->input, m);
|
||||
}
|
||||
|
||||
size -= used;
|
||||
d += used;
|
||||
}
|
||||
}
|
||||
|
||||
packet = MIDIPacketNext (packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback)
|
||||
{
|
||||
using namespace CoreMidiHelpers;
|
||||
MidiInput* mi = 0;
|
||||
|
||||
if (((unsigned int) index) < (unsigned int) MIDIGetNumberOfSources())
|
||||
|
|
@ -538,20 +528,20 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback)
|
|||
{
|
||||
CFStringRef pname;
|
||||
|
||||
if (OK (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname)))
|
||||
if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname)))
|
||||
{
|
||||
log ("CoreMidi - opening inp: " + PlatformUtilities::cfStringToJuceString (pname));
|
||||
MIDIClientRef client = getGlobalMidiClient();
|
||||
|
||||
if (makeSureClientExists())
|
||||
if (client != 0)
|
||||
{
|
||||
MIDIPortRef port;
|
||||
|
||||
ScopedPointer <MidiPortAndCallback> mpc (new MidiPortAndCallback());
|
||||
mpc->active = false;
|
||||
|
||||
if (OK (MIDIInputPortCreate (globalMidiClient, pname, midiInputProc, mpc, &port)))
|
||||
if (CHECK_ERROR (MIDIInputPortCreate (client, pname, midiInputProc, mpc, &port)))
|
||||
{
|
||||
if (OK (MIDIPortConnectSource (port, endPoint, 0)))
|
||||
if (CHECK_ERROR (MIDIPortConnectSource (port, endPoint, 0)))
|
||||
{
|
||||
mpc->portAndEndpoint = new MidiPortAndEndpoint (port, endPoint);
|
||||
mpc->callback = callback;
|
||||
|
|
@ -562,12 +552,12 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback)
|
|||
mpc->input = mi;
|
||||
mi->internal = mpc;
|
||||
|
||||
const ScopedLock sl (CoreMidiCallbacks::callbackLock);
|
||||
CoreMidiCallbacks::activeCallbacks.add (mpc.release());
|
||||
const ScopedLock sl (callbackLock);
|
||||
activeCallbacks.add (mpc.release());
|
||||
}
|
||||
else
|
||||
{
|
||||
OK (MIDIPortDispose (port));
|
||||
CHECK_ERROR (MIDIPortDispose (port));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -582,16 +572,18 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback)
|
|||
|
||||
MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback)
|
||||
{
|
||||
using namespace CoreMidiHelpers;
|
||||
MidiInput* mi = 0;
|
||||
MIDIClientRef client = getGlobalMidiClient();
|
||||
|
||||
if (makeSureClientExists())
|
||||
if (client != 0)
|
||||
{
|
||||
ScopedPointer <MidiPortAndCallback> mpc (new MidiPortAndCallback());
|
||||
mpc->active = false;
|
||||
|
||||
MIDIEndpointRef endPoint;
|
||||
CFStringRef name = PlatformUtilities::juceStringToCFString(deviceName);
|
||||
if (OK (MIDIDestinationCreate (globalMidiClient, name, midiInputProc, mpc, &endPoint)))
|
||||
if (CHECK_ERROR (MIDIDestinationCreate (client, name, midiInputProc, mpc, &endPoint)))
|
||||
{
|
||||
mpc->portAndEndpoint = new MidiPortAndEndpoint (0, endPoint);
|
||||
mpc->callback = callback;
|
||||
|
|
@ -602,8 +594,8 @@ MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallba
|
|||
mpc->input = mi;
|
||||
mi->internal = mpc;
|
||||
|
||||
const ScopedLock sl (CoreMidiCallbacks::callbackLock);
|
||||
CoreMidiCallbacks::activeCallbacks.add (mpc.release());
|
||||
const ScopedLock sl (callbackLock);
|
||||
activeCallbacks.add (mpc.release());
|
||||
}
|
||||
|
||||
CFRelease (name);
|
||||
|
|
@ -619,16 +611,18 @@ MidiInput::MidiInput (const String& name_)
|
|||
|
||||
MidiInput::~MidiInput()
|
||||
{
|
||||
using namespace CoreMidiHelpers;
|
||||
|
||||
MidiPortAndCallback* const mpc = static_cast<MidiPortAndCallback*> (internal);
|
||||
mpc->active = false;
|
||||
|
||||
{
|
||||
const ScopedLock sl (CoreMidiCallbacks::callbackLock);
|
||||
CoreMidiCallbacks::activeCallbacks.removeValue (mpc);
|
||||
const ScopedLock sl (callbackLock);
|
||||
activeCallbacks.removeValue (mpc);
|
||||
}
|
||||
|
||||
if (mpc->portAndEndpoint->port != 0)
|
||||
OK (MIDIPortDisconnectSource (mpc->portAndEndpoint->port, mpc->portAndEndpoint->endPoint));
|
||||
CHECK_ERROR (MIDIPortDisconnectSource (mpc->portAndEndpoint->port, mpc->portAndEndpoint->endPoint));
|
||||
|
||||
delete mpc->portAndEndpoint;
|
||||
delete mpc;
|
||||
|
|
@ -636,61 +630,31 @@ MidiInput::~MidiInput()
|
|||
|
||||
void MidiInput::start()
|
||||
{
|
||||
const ScopedLock sl (CoreMidiCallbacks::callbackLock);
|
||||
static_cast<MidiPortAndCallback*> (internal)->active = true;
|
||||
const ScopedLock sl (CoreMidiHelpers::callbackLock);
|
||||
static_cast<CoreMidiHelpers::MidiPortAndCallback*> (internal)->active = true;
|
||||
}
|
||||
|
||||
void MidiInput::stop()
|
||||
{
|
||||
const ScopedLock sl (CoreMidiCallbacks::callbackLock);
|
||||
static_cast<MidiPortAndCallback*> (internal)->active = false;
|
||||
const ScopedLock sl (CoreMidiHelpers::callbackLock);
|
||||
static_cast<CoreMidiHelpers::MidiPortAndCallback*> (internal)->active = false;
|
||||
}
|
||||
|
||||
#undef log
|
||||
#undef CHECK_ERROR
|
||||
|
||||
#else
|
||||
|
||||
MidiOutput::~MidiOutput()
|
||||
{
|
||||
}
|
||||
|
||||
void MidiOutput::reset()
|
||||
{
|
||||
}
|
||||
|
||||
bool MidiOutput::getVolume (float& /*leftVol*/, float& /*rightVol*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void MidiOutput::setVolume (float /*leftVol*/, float /*rightVol*/)
|
||||
{
|
||||
}
|
||||
|
||||
void MidiOutput::sendMessageNow (const MidiMessage& message)
|
||||
{
|
||||
}
|
||||
|
||||
const StringArray MidiOutput::getDevices()
|
||||
{
|
||||
return StringArray();
|
||||
}
|
||||
|
||||
MidiOutput* MidiOutput::openDevice (int index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const StringArray MidiInput::getDevices()
|
||||
{
|
||||
return StringArray();
|
||||
}
|
||||
|
||||
MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
//==============================================================================
|
||||
#else // Stubs for iOS...
|
||||
|
||||
MidiOutput::~MidiOutput() {}
|
||||
void MidiOutput::reset() {}
|
||||
bool MidiOutput::getVolume (float& /*leftVol*/, float& /*rightVol*/) { return false; }
|
||||
void MidiOutput::setVolume (float /*leftVol*/, float /*rightVol*/) {}
|
||||
void MidiOutput::sendMessageNow (const MidiMessage& message) {}
|
||||
const StringArray MidiOutput::getDevices() { return StringArray(); }
|
||||
MidiOutput* MidiOutput::openDevice (int index) { return 0; }
|
||||
const StringArray MidiInput::getDevices() { return StringArray(); }
|
||||
MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) { return 0; }
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -27,67 +27,74 @@
|
|||
// compiled on its own).
|
||||
#if JUCE_INCLUDED_FILE && JUCE_WASAPI
|
||||
|
||||
#ifndef WASAPI_ENABLE_LOGGING
|
||||
#define WASAPI_ENABLE_LOGGING 1
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#if 1
|
||||
|
||||
const String getAudioErrorDesc (HRESULT hr)
|
||||
namespace WasapiClasses
|
||||
{
|
||||
const char* e = 0;
|
||||
|
||||
switch (hr)
|
||||
static void logFailure (HRESULT hr)
|
||||
{
|
||||
(void) hr;
|
||||
|
||||
#if WASAPI_ENABLE_LOGGING
|
||||
if (FAILED (hr))
|
||||
{
|
||||
case E_POINTER: e = "E_POINTER"; break;
|
||||
case E_INVALIDARG: e = "E_INVALIDARG"; break;
|
||||
case AUDCLNT_E_NOT_INITIALIZED: e = "AUDCLNT_E_NOT_INITIALIZED"; break;
|
||||
case AUDCLNT_E_ALREADY_INITIALIZED: e = "AUDCLNT_E_ALREADY_INITIALIZED"; break;
|
||||
case AUDCLNT_E_WRONG_ENDPOINT_TYPE: e = "AUDCLNT_E_WRONG_ENDPOINT_TYPE"; break;
|
||||
case AUDCLNT_E_DEVICE_INVALIDATED: e = "AUDCLNT_E_DEVICE_INVALIDATED"; break;
|
||||
case AUDCLNT_E_NOT_STOPPED: e = "AUDCLNT_E_NOT_STOPPED"; break;
|
||||
case AUDCLNT_E_BUFFER_TOO_LARGE: e = "AUDCLNT_E_BUFFER_TOO_LARGE"; break;
|
||||
case AUDCLNT_E_OUT_OF_ORDER: e = "AUDCLNT_E_OUT_OF_ORDER"; break;
|
||||
case AUDCLNT_E_UNSUPPORTED_FORMAT: e = "AUDCLNT_E_UNSUPPORTED_FORMAT"; break;
|
||||
case AUDCLNT_E_INVALID_SIZE: e = "AUDCLNT_E_INVALID_SIZE"; break;
|
||||
case AUDCLNT_E_DEVICE_IN_USE: e = "AUDCLNT_E_DEVICE_IN_USE"; break;
|
||||
case AUDCLNT_E_BUFFER_OPERATION_PENDING: e = "AUDCLNT_E_BUFFER_OPERATION_PENDING"; break;
|
||||
case AUDCLNT_E_THREAD_NOT_REGISTERED: e = "AUDCLNT_E_THREAD_NOT_REGISTERED"; break;
|
||||
case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: e = "AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; break;
|
||||
case AUDCLNT_E_ENDPOINT_CREATE_FAILED: e = "AUDCLNT_E_ENDPOINT_CREATE_FAILED"; break;
|
||||
case AUDCLNT_E_SERVICE_NOT_RUNNING: e = "AUDCLNT_E_SERVICE_NOT_RUNNING"; break;
|
||||
case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: e = "AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; break;
|
||||
case AUDCLNT_E_EXCLUSIVE_MODE_ONLY: e = "AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; break;
|
||||
case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: e = "AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; break;
|
||||
case AUDCLNT_E_EVENTHANDLE_NOT_SET: e = "AUDCLNT_E_EVENTHANDLE_NOT_SET"; break;
|
||||
case AUDCLNT_E_INCORRECT_BUFFER_SIZE: e = "AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break;
|
||||
case AUDCLNT_E_BUFFER_SIZE_ERROR: e = "AUDCLNT_E_BUFFER_SIZE_ERROR"; break;
|
||||
case AUDCLNT_S_BUFFER_EMPTY: e = "AUDCLNT_S_BUFFER_EMPTY"; break;
|
||||
case AUDCLNT_S_THREAD_ALREADY_REGISTERED: e = "AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break;
|
||||
default: return String::toHexString ((int) hr);
|
||||
}
|
||||
String e;
|
||||
e << Time::getCurrentTime().toString (true, true, true, true)
|
||||
<< " -- WASAPI error: ";
|
||||
|
||||
return e;
|
||||
switch (hr)
|
||||
{
|
||||
case E_POINTER: e << "E_POINTER"; break;
|
||||
case E_INVALIDARG: e << "E_INVALIDARG"; break;
|
||||
case AUDCLNT_E_NOT_INITIALIZED: e << "AUDCLNT_E_NOT_INITIALIZED"; break;
|
||||
case AUDCLNT_E_ALREADY_INITIALIZED: e << "AUDCLNT_E_ALREADY_INITIALIZED"; break;
|
||||
case AUDCLNT_E_WRONG_ENDPOINT_TYPE: e << "AUDCLNT_E_WRONG_ENDPOINT_TYPE"; break;
|
||||
case AUDCLNT_E_DEVICE_INVALIDATED: e << "AUDCLNT_E_DEVICE_INVALIDATED"; break;
|
||||
case AUDCLNT_E_NOT_STOPPED: e << "AUDCLNT_E_NOT_STOPPED"; break;
|
||||
case AUDCLNT_E_BUFFER_TOO_LARGE: e << "AUDCLNT_E_BUFFER_TOO_LARGE"; break;
|
||||
case AUDCLNT_E_OUT_OF_ORDER: e << "AUDCLNT_E_OUT_OF_ORDER"; break;
|
||||
case AUDCLNT_E_UNSUPPORTED_FORMAT: e << "AUDCLNT_E_UNSUPPORTED_FORMAT"; break;
|
||||
case AUDCLNT_E_INVALID_SIZE: e << "AUDCLNT_E_INVALID_SIZE"; break;
|
||||
case AUDCLNT_E_DEVICE_IN_USE: e << "AUDCLNT_E_DEVICE_IN_USE"; break;
|
||||
case AUDCLNT_E_BUFFER_OPERATION_PENDING: e << "AUDCLNT_E_BUFFER_OPERATION_PENDING"; break;
|
||||
case AUDCLNT_E_THREAD_NOT_REGISTERED: e << "AUDCLNT_E_THREAD_NOT_REGISTERED"; break;
|
||||
case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: e << "AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; break;
|
||||
case AUDCLNT_E_ENDPOINT_CREATE_FAILED: e << "AUDCLNT_E_ENDPOINT_CREATE_FAILED"; break;
|
||||
case AUDCLNT_E_SERVICE_NOT_RUNNING: e << "AUDCLNT_E_SERVICE_NOT_RUNNING"; break;
|
||||
case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: e << "AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; break;
|
||||
case AUDCLNT_E_EXCLUSIVE_MODE_ONLY: e << "AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; break;
|
||||
case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: e << "AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; break;
|
||||
case AUDCLNT_E_EVENTHANDLE_NOT_SET: e << "AUDCLNT_E_EVENTHANDLE_NOT_SET"; break;
|
||||
case AUDCLNT_E_INCORRECT_BUFFER_SIZE: e << "AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break;
|
||||
case AUDCLNT_E_BUFFER_SIZE_ERROR: e << "AUDCLNT_E_BUFFER_SIZE_ERROR"; break;
|
||||
case AUDCLNT_S_BUFFER_EMPTY: e << "AUDCLNT_S_BUFFER_EMPTY"; break;
|
||||
case AUDCLNT_S_THREAD_ALREADY_REGISTERED: e << "AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break;
|
||||
default: e << String::toHexString ((int) hr); break;
|
||||
}
|
||||
|
||||
DBG (e);
|
||||
jassertfalse;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#define logFailure(hr) { if (FAILED (hr)) { DBG ("WASAPI FAIL! " + getAudioErrorDesc (hr)); jassertfalse; } }
|
||||
#define OK(a) wasapi_checkResult(a)
|
||||
|
||||
static bool wasapi_checkResult (HRESULT hr)
|
||||
static bool check (HRESULT hr)
|
||||
{
|
||||
logFailure (hr);
|
||||
return SUCCEEDED (hr);
|
||||
}
|
||||
|
||||
#else
|
||||
#define logFailure(hr) {}
|
||||
#define OK(a) SUCCEEDED(a)
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
static const String wasapi_getDeviceID (IMMDevice* const device)
|
||||
static const String getDeviceID (IMMDevice* const device)
|
||||
{
|
||||
String s;
|
||||
WCHAR* deviceId = 0;
|
||||
|
||||
if (OK (device->GetId (&deviceId)))
|
||||
if (check (device->GetId (&deviceId)))
|
||||
{
|
||||
s = String (deviceId);
|
||||
CoTaskMemFree (deviceId);
|
||||
|
|
@ -96,27 +103,28 @@ static const String wasapi_getDeviceID (IMMDevice* const device)
|
|||
return s;
|
||||
}
|
||||
|
||||
static EDataFlow wasapi_getDataFlow (IMMDevice* const device)
|
||||
static EDataFlow getDataFlow (IMMDevice* const device)
|
||||
{
|
||||
EDataFlow flow = eRender;
|
||||
ComSmartPtr <IMMEndpoint> endPoint;
|
||||
if (OK (device->QueryInterface (__uuidof (IMMEndpoint), (void**) &endPoint)))
|
||||
(void) OK (endPoint->GetDataFlow (&flow));
|
||||
if (check (device->QueryInterface (__uuidof (IMMEndpoint), (void**) &endPoint)))
|
||||
(void) check (endPoint->GetDataFlow (&flow));
|
||||
|
||||
return flow;
|
||||
}
|
||||
|
||||
static int wasapi_refTimeToSamples (const REFERENCE_TIME& t, const double sampleRate) throw()
|
||||
static int refTimeToSamples (const REFERENCE_TIME& t, const double sampleRate) throw()
|
||||
{
|
||||
return roundDoubleToInt (sampleRate * ((double) t) * 0.0000001);
|
||||
}
|
||||
|
||||
static void wasapi_copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* const src) throw()
|
||||
static void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* const src) throw()
|
||||
{
|
||||
memcpy (&dest, src, src->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? sizeof (WAVEFORMATEXTENSIBLE)
|
||||
: sizeof (WAVEFORMATEX));
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class WASAPIDeviceBase
|
||||
{
|
||||
|
|
@ -139,21 +147,21 @@ public:
|
|||
return;
|
||||
|
||||
REFERENCE_TIME defaultPeriod, minPeriod;
|
||||
if (! OK (tempClient->GetDevicePeriod (&defaultPeriod, &minPeriod)))
|
||||
if (! check (tempClient->GetDevicePeriod (&defaultPeriod, &minPeriod)))
|
||||
return;
|
||||
|
||||
WAVEFORMATEX* mixFormat = 0;
|
||||
if (! OK (tempClient->GetMixFormat (&mixFormat)))
|
||||
if (! check (tempClient->GetMixFormat (&mixFormat)))
|
||||
return;
|
||||
|
||||
WAVEFORMATEXTENSIBLE format;
|
||||
wasapi_copyWavFormat (format, mixFormat);
|
||||
copyWavFormat (format, mixFormat);
|
||||
CoTaskMemFree (mixFormat);
|
||||
|
||||
actualNumChannels = numChannels = format.Format.nChannels;
|
||||
defaultSampleRate = format.Format.nSamplesPerSec;
|
||||
minBufferSize = wasapi_refTimeToSamples (minPeriod, defaultSampleRate);
|
||||
defaultBufferSize = wasapi_refTimeToSamples (defaultPeriod, defaultSampleRate);
|
||||
minBufferSize = refTimeToSamples (minPeriod, defaultSampleRate);
|
||||
defaultBufferSize = refTimeToSamples (defaultPeriod, defaultSampleRate);
|
||||
|
||||
rates.addUsingDefaultSort (defaultSampleRate);
|
||||
|
||||
|
|
@ -203,12 +211,12 @@ public:
|
|||
channelMaps.add (i);
|
||||
|
||||
REFERENCE_TIME latency;
|
||||
if (OK (client->GetStreamLatency (&latency)))
|
||||
latencySamples = wasapi_refTimeToSamples (latency, sampleRate);
|
||||
if (check (client->GetStreamLatency (&latency)))
|
||||
latencySamples = refTimeToSamples (latency, sampleRate);
|
||||
|
||||
(void) OK (client->GetBufferSize (&actualBufferSize));
|
||||
(void) check (client->GetBufferSize (&actualBufferSize));
|
||||
|
||||
return OK (client->SetEventHandle (clientEvent));
|
||||
return check (client->SetEventHandle (clientEvent));
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -293,7 +301,7 @@ private:
|
|||
|
||||
if (hr == S_FALSE && format.Format.nSamplesPerSec == nearestFormat->Format.nSamplesPerSec)
|
||||
{
|
||||
wasapi_copyWavFormat (format, (WAVEFORMATEX*) nearestFormat);
|
||||
copyWavFormat (format, (WAVEFORMATEX*) nearestFormat);
|
||||
hr = S_OK;
|
||||
}
|
||||
|
||||
|
|
@ -301,13 +309,13 @@ private:
|
|||
|
||||
REFERENCE_TIME defaultPeriod = 0, minPeriod = 0;
|
||||
if (useExclusiveMode)
|
||||
OK (client->GetDevicePeriod (&defaultPeriod, &minPeriod));
|
||||
check (client->GetDevicePeriod (&defaultPeriod, &minPeriod));
|
||||
|
||||
GUID session;
|
||||
if (hr == S_OK
|
||||
&& OK (client->Initialize (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED,
|
||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||
defaultPeriod, defaultPeriod, (WAVEFORMATEX*) &format, &session)))
|
||||
&& check (client->Initialize (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED,
|
||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||
defaultPeriod, defaultPeriod, (WAVEFORMATEX*) &format, &session)))
|
||||
{
|
||||
actualNumChannels = format.Format.nChannels;
|
||||
const bool isFloat = format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
|
|
@ -345,7 +353,7 @@ public:
|
|||
reservoirCapacity = 16384;
|
||||
reservoir.setSize (actualNumChannels * reservoirCapacity * sizeof (float));
|
||||
return openClient (newSampleRate, newChannels)
|
||||
&& (numChannels == 0 || OK (client->GetService (__uuidof (IAudioCaptureClient), (void**) &captureClient)));
|
||||
&& (numChannels == 0 || check (client->GetService (__uuidof (IAudioCaptureClient), (void**) &captureClient)));
|
||||
}
|
||||
|
||||
void close()
|
||||
|
|
@ -392,7 +400,7 @@ public:
|
|||
else
|
||||
{
|
||||
UINT32 packetLength = 0;
|
||||
if (! OK (captureClient->GetNextPacketSize (&packetLength)))
|
||||
if (! check (captureClient->GetNextPacketSize (&packetLength)))
|
||||
break;
|
||||
|
||||
if (packetLength == 0)
|
||||
|
|
@ -408,7 +416,7 @@ public:
|
|||
UINT32 numSamplesAvailable;
|
||||
DWORD flags;
|
||||
|
||||
if (OK (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, 0, 0)))
|
||||
if (check (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, 0, 0)))
|
||||
{
|
||||
const int samplesToDo = jmin (bufferSize, (int) numSamplesAvailable);
|
||||
|
||||
|
|
@ -458,7 +466,7 @@ public:
|
|||
bool open (const double newSampleRate, const BigInteger& newChannels)
|
||||
{
|
||||
return openClient (newSampleRate, newChannels)
|
||||
&& (numChannels == 0 || OK (client->GetService (__uuidof (IAudioRenderClient), (void**) &renderClient)));
|
||||
&& (numChannels == 0 || check (client->GetService (__uuidof (IAudioRenderClient), (void**) &renderClient)));
|
||||
}
|
||||
|
||||
void close()
|
||||
|
|
@ -491,7 +499,7 @@ public:
|
|||
while (bufferSize > 0)
|
||||
{
|
||||
UINT32 padding = 0;
|
||||
if (! OK (client->GetCurrentPadding (&padding)))
|
||||
if (! check (client->GetCurrentPadding (&padding)))
|
||||
return;
|
||||
|
||||
int samplesToDo = useExclusiveMode ? bufferSize
|
||||
|
|
@ -507,7 +515,7 @@ public:
|
|||
}
|
||||
|
||||
uint8* outputData = 0;
|
||||
if (OK (renderClient->GetBuffer (samplesToDo, &outputData)))
|
||||
if (check (renderClient->GetBuffer (samplesToDo, &outputData)))
|
||||
{
|
||||
for (int i = 0; i < numSrcBuffers; ++i)
|
||||
converter->convertSamples (outputData, channelMaps.getUnchecked(i), srcBuffers[i], offset, samplesToDo);
|
||||
|
|
@ -866,28 +874,28 @@ private:
|
|||
bool createDevices()
|
||||
{
|
||||
ComSmartPtr <IMMDeviceEnumerator> enumerator;
|
||||
if (! OK (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
|
||||
if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
|
||||
return false;
|
||||
|
||||
ComSmartPtr <IMMDeviceCollection> deviceCollection;
|
||||
if (! OK (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, &deviceCollection)))
|
||||
if (! check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, &deviceCollection)))
|
||||
return false;
|
||||
|
||||
UINT32 numDevices = 0;
|
||||
if (! OK (deviceCollection->GetCount (&numDevices)))
|
||||
if (! check (deviceCollection->GetCount (&numDevices)))
|
||||
return false;
|
||||
|
||||
for (UINT32 i = 0; i < numDevices; ++i)
|
||||
{
|
||||
ComSmartPtr <IMMDevice> device;
|
||||
if (! OK (deviceCollection->Item (i, &device)))
|
||||
if (! check (deviceCollection->Item (i, &device)))
|
||||
continue;
|
||||
|
||||
const String deviceId (wasapi_getDeviceID (device));
|
||||
const String deviceId (getDeviceID (device));
|
||||
if (deviceId.isEmpty())
|
||||
continue;
|
||||
|
||||
const EDataFlow flow = wasapi_getDataFlow (device);
|
||||
const EDataFlow flow = getDataFlow (device);
|
||||
|
||||
if (deviceId == inputDeviceId && flow == eCapture)
|
||||
inputDevice = new WASAPIInputDevice (device, useExclusiveMode);
|
||||
|
|
@ -930,7 +938,7 @@ public:
|
|||
inputDeviceIds.clear();
|
||||
|
||||
ComSmartPtr <IMMDeviceEnumerator> enumerator;
|
||||
if (! OK (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
|
||||
if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
|
||||
return;
|
||||
|
||||
const String defaultRenderer = getDefaultEndpoint (enumerator, false);
|
||||
|
|
@ -939,20 +947,20 @@ public:
|
|||
ComSmartPtr <IMMDeviceCollection> deviceCollection;
|
||||
UINT32 numDevices = 0;
|
||||
|
||||
if (! (OK (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, &deviceCollection))
|
||||
&& OK (deviceCollection->GetCount (&numDevices))))
|
||||
if (! (check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, &deviceCollection))
|
||||
&& check (deviceCollection->GetCount (&numDevices))))
|
||||
return;
|
||||
|
||||
for (UINT32 i = 0; i < numDevices; ++i)
|
||||
{
|
||||
ComSmartPtr <IMMDevice> device;
|
||||
if (! OK (deviceCollection->Item (i, &device)))
|
||||
if (! check (deviceCollection->Item (i, &device)))
|
||||
continue;
|
||||
|
||||
const String deviceId (wasapi_getDeviceID (device));
|
||||
const String deviceId (getDeviceID (device));
|
||||
|
||||
DWORD state = 0;
|
||||
if (! OK (device->GetState (&state)))
|
||||
if (! check (device->GetState (&state)))
|
||||
continue;
|
||||
|
||||
if (state != DEVICE_STATE_ACTIVE)
|
||||
|
|
@ -962,18 +970,18 @@ public:
|
|||
|
||||
{
|
||||
ComSmartPtr <IPropertyStore> properties;
|
||||
if (! OK (device->OpenPropertyStore (STGM_READ, &properties)))
|
||||
if (! check (device->OpenPropertyStore (STGM_READ, &properties)))
|
||||
continue;
|
||||
|
||||
PROPVARIANT value;
|
||||
PropVariantInit (&value);
|
||||
if (OK (properties->GetValue (PKEY_Device_FriendlyName, &value)))
|
||||
if (check (properties->GetValue (PKEY_Device_FriendlyName, &value)))
|
||||
name = value.pwszVal;
|
||||
|
||||
PropVariantClear (&value);
|
||||
}
|
||||
|
||||
const EDataFlow flow = wasapi_getDataFlow (device);
|
||||
const EDataFlow flow = getDataFlow (device);
|
||||
|
||||
if (flow == eRender)
|
||||
{
|
||||
|
|
@ -1057,11 +1065,11 @@ private:
|
|||
{
|
||||
String s;
|
||||
IMMDevice* dev = 0;
|
||||
if (OK (enumerator->GetDefaultAudioEndpoint (forCapture ? eCapture : eRender,
|
||||
eMultimedia, &dev)))
|
||||
if (check (enumerator->GetDefaultAudioEndpoint (forCapture ? eCapture : eRender,
|
||||
eMultimedia, &dev)))
|
||||
{
|
||||
WCHAR* deviceId = 0;
|
||||
if (OK (dev->GetId (&deviceId)))
|
||||
if (check (dev->GetId (&deviceId)))
|
||||
{
|
||||
s = String (deviceId);
|
||||
CoTaskMemFree (deviceId);
|
||||
|
|
@ -1078,13 +1086,12 @@ private:
|
|||
WASAPIAudioIODeviceType& operator= (const WASAPIAudioIODeviceType&);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
AudioIODeviceType* juce_createAudioIODeviceType_WASAPI()
|
||||
{
|
||||
return new WASAPIAudioIODeviceType();
|
||||
return new WasapiClasses::WASAPIAudioIODeviceType();
|
||||
}
|
||||
|
||||
#undef logFailure
|
||||
#undef OK
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1052,6 +1052,11 @@ private:
|
|||
|
||||
void createInternal (const juce_wchar* text, size_t numChars);
|
||||
void appendInternal (const juce_wchar* text, int numExtraChars);
|
||||
|
||||
// This private cast operator should prevent strings being accidentally cast
|
||||
// to bools (this is possible because the compiler can add an implicit cast
|
||||
// via a const char*)
|
||||
operator bool() const throw() { return false; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue