| @@ -32,10 +32,11 @@ public: | |||||
| void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); | void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); | ||||
| //============================================================================== | //============================================================================== | ||||
| bool hasEditor() const { return true; } | |||||
| AudioProcessorEditor* createEditor(); | AudioProcessorEditor* createEditor(); | ||||
| //============================================================================== | //============================================================================== | ||||
| const String getName() const { return JucePlugin_Name; } | |||||
| const String getName() const { return JucePlugin_Name; } | |||||
| int getNumParameters(); | int getNumParameters(); | ||||
| float getParameter (int index); | float getParameter (int index); | ||||
| @@ -352,34 +352,20 @@ public: | |||||
| void open() | 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() | 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(); | stopTimer(); | ||||
| deleteEditor (false); | |||||
| if (MessageManager::getInstance()->isThisTheMessageThread()) | |||||
| deleteEditor (false); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -439,8 +425,10 @@ public: | |||||
| { | { | ||||
| result = 1; | 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; | result = -1; | ||||
| } | } | ||||
| @@ -770,48 +758,22 @@ public: | |||||
| switch (ti->smpteFrameRate) | switch (ti->smpteFrameRate) | ||||
| { | { | ||||
| 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 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.. | |||||
| 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 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.. | |||||
| } | } | ||||
| info.frameRate = rate; | info.frameRate = rate; | ||||
| @@ -1068,6 +1030,7 @@ public: | |||||
| { | { | ||||
| recursionCheck = true; | recursionCheck = true; | ||||
| JUCE_AUTORELEASEPOOL | |||||
| juce_callAnyTimersSynchronously(); | juce_callAnyTimersSynchronously(); | ||||
| for (int i = ComponentPeer::getNumPeers(); --i >= 0;) | for (int i = ComponentPeer::getNumPeers(); --i >= 0;) | ||||
| @@ -1105,6 +1068,7 @@ public: | |||||
| void deleteEditor (bool canDeleteLaterIfModal) | void deleteEditor (bool canDeleteLaterIfModal) | ||||
| { | { | ||||
| JUCE_AUTORELEASEPOOL | |||||
| PopupMenu::dismissAllActiveMenus(); | PopupMenu::dismissAllActiveMenus(); | ||||
| jassert (! recursionCheck); | jassert (! recursionCheck); | ||||
| @@ -1161,10 +1125,12 @@ public: | |||||
| } | } | ||||
| else if (opCode == effEditOpen) | else if (opCode == effEditOpen) | ||||
| { | { | ||||
| checkWhetherWavelabHasChangedThread(); | |||||
| checkWhetherMessageThreadIsCorrect(); | |||||
| const MessageManagerLock mmLock; | const MessageManagerLock mmLock; | ||||
| jassert (! recursionCheck); | jassert (! recursionCheck); | ||||
| startTimer (1000 / 4); // performs misc housekeeping chores | |||||
| deleteEditor (true); | deleteEditor (true); | ||||
| createEditorComp(); | createEditorComp(); | ||||
| @@ -1191,14 +1157,14 @@ public: | |||||
| } | } | ||||
| else if (opCode == effEditClose) | else if (opCode == effEditClose) | ||||
| { | { | ||||
| checkWhetherWavelabHasChangedThread(); | |||||
| checkWhetherMessageThreadIsCorrect(); | |||||
| const MessageManagerLock mmLock; | const MessageManagerLock mmLock; | ||||
| deleteEditor (true); | deleteEditor (true); | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| else if (opCode == effEditGetRect) | else if (opCode == effEditGetRect) | ||||
| { | { | ||||
| checkWhetherWavelabHasChangedThread(); | |||||
| checkWhetherMessageThreadIsCorrect(); | |||||
| const MessageManagerLock mmLock; | const MessageManagerLock mmLock; | ||||
| createEditorComp(); | createEditorComp(); | ||||
| @@ -1436,18 +1402,9 @@ private: | |||||
| bool shouldDeleteEditor; | 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()) | if (getHostType().isWavelab() || getHostType().isCubaseBridged()) | ||||
| { | { | ||||
| @@ -1476,8 +1433,7 @@ private: | |||||
| } | } | ||||
| } | } | ||||
| #else | #else | ||||
| typedef MessageManagerLock NonWavelabMMLock; | |||||
| static void checkWhetherWavelabHasChangedThread() {} | |||||
| static void checkWhetherMessageThreadIsCorrect() {} | |||||
| #endif | #endif | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -64,7 +64,7 @@ | |||||
| */ | */ | ||||
| #define JUCE_MAJOR_VERSION 1 | #define JUCE_MAJOR_VERSION 1 | ||||
| #define JUCE_MINOR_VERSION 52 | #define JUCE_MINOR_VERSION 52 | ||||
| #define JUCE_BUILDNUMBER 72 | |||||
| #define JUCE_BUILDNUMBER 73 | |||||
| /** Current Juce version number. | /** Current Juce version number. | ||||
| @@ -2594,6 +2594,11 @@ private: | |||||
| void createInternal (const juce_wchar* text, size_t numChars); | void createInternal (const juce_wchar* text, size_t numChars); | ||||
| void appendInternal (const juce_wchar* text, int numExtraChars); | 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. */ | /** Concatenates two strings. */ | ||||
| @@ -39875,7 +39880,8 @@ public: | |||||
| a generic UI that lets the user twiddle the parameters directly. | 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 | 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 | 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 | 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. | 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. | - 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. | - It's safe to assume that an editor will be deleted before its filter. | ||||
| @see hasEditor | |||||
| */ | */ | ||||
| virtual AudioProcessorEditor* createEditor() = 0; | 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. | /** Returns the active editor, if there is one. | ||||
| Bear in mind this can return 0, even if an editor has previously been | Bear in mind this can return 0, even if an editor has previously been | ||||
| @@ -42011,6 +42024,7 @@ public: | |||||
| bool acceptsMidi() const; | bool acceptsMidi() const; | ||||
| bool producesMidi() const; | bool producesMidi() const; | ||||
| bool hasEditor() const; | |||||
| AudioProcessorEditor* createEditor(); | AudioProcessorEditor* createEditor(); | ||||
| int getNumParameters(); | int getNumParameters(); | ||||
| @@ -42057,6 +42071,7 @@ public: | |||||
| bool acceptsMidi() const; | bool acceptsMidi() const; | ||||
| bool producesMidi() const; | bool producesMidi() const; | ||||
| bool hasEditor() const { return false; } | |||||
| AudioProcessorEditor* createEditor() { return 0; } | AudioProcessorEditor* createEditor() { return 0; } | ||||
| int getNumParameters() { 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 | /* 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. | 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 | flag in juce_Config.h | ||||
| */ | */ | ||||
| #include <Movies.h> | #include <Movies.h> | ||||
| @@ -70,7 +70,7 @@ BEGIN_JUCE_NAMESPACE | |||||
| bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle& dataHandle); | bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle& dataHandle); | ||||
| static const char* const quickTimeFormatName = "QuickTime file"; | 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 | class QTAudioReader : public AudioFormatReader | ||||
| @@ -31,6 +31,7 @@ BEGIN_JUCE_NAMESPACE | |||||
| #include "../../gui/components/juce_Desktop.h" | #include "../../gui/components/juce_Desktop.h" | ||||
| #include "../../text/juce_LocalisedStrings.h" | #include "../../text/juce_LocalisedStrings.h" | ||||
| #include "../dsp/juce_AudioSampleBuffer.h" | #include "../dsp/juce_AudioSampleBuffer.h" | ||||
| #include "../../core/juce_SystemStats.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| @@ -231,6 +231,7 @@ public: | |||||
| void processBlock (AudioSampleBuffer& buffer, | void processBlock (AudioSampleBuffer& buffer, | ||||
| MidiBuffer& midiMessages); | MidiBuffer& midiMessages); | ||||
| bool hasEditor() const; | |||||
| AudioProcessorEditor* createEditor(); | AudioProcessorEditor* createEditor(); | ||||
| const String getInputChannelName (int index) const; | const String getInputChannelName (int index) const; | ||||
| @@ -1087,6 +1088,11 @@ private: | |||||
| #endif | #endif | ||||
| //============================================================================== | //============================================================================== | ||||
| bool AudioUnitPluginInstance::hasEditor() const | |||||
| { | |||||
| return true; | |||||
| } | |||||
| AudioProcessorEditor* AudioUnitPluginInstance::createEditor() | AudioProcessorEditor* AudioUnitPluginInstance::createEditor() | ||||
| { | { | ||||
| ScopedPointer<AudioProcessorEditor> w (new AudioUnitPluginWindowCocoa (*this, false)); | ScopedPointer<AudioProcessorEditor> w (new AudioUnitPluginWindowCocoa (*this, false)); | ||||
| @@ -729,6 +729,7 @@ public: | |||||
| void processBlock (AudioSampleBuffer& buffer, | void processBlock (AudioSampleBuffer& buffer, | ||||
| MidiBuffer& midiMessages); | MidiBuffer& midiMessages); | ||||
| bool hasEditor() const { return effect != 0 && (effect->flags & effFlagsHasEditor) != 0; } | |||||
| AudioProcessorEditor* createEditor(); | AudioProcessorEditor* createEditor(); | ||||
| const String getInputChannelName (int index) const; | const String getInputChannelName (int index) const; | ||||
| @@ -805,7 +806,6 @@ private: | |||||
| const String getVersion() const; | const String getVersion() const; | ||||
| const String getCategory() const; | const String getCategory() const; | ||||
| bool hasEditor() const throw() { return effect != 0 && (effect->flags & effFlagsHasEditor) != 0; } | |||||
| void setPower (const bool on); | void setPower (const bool on); | ||||
| VSTPluginInstance (const ReferenceCountedObjectPtr <ModuleHandle>& module); | VSTPluginInstance (const ReferenceCountedObjectPtr <ModuleHandle>& module); | ||||
| @@ -231,6 +231,9 @@ AudioProcessorEditor* AudioProcessor::createEditorIfNeeded() | |||||
| AudioProcessorEditor* const ed = createEditor(); | AudioProcessorEditor* const ed = createEditor(); | ||||
| // You must make your hasEditor() method return a consistent result! | |||||
| jassert (hasEditor() == (ed != 0)); | |||||
| if (ed != 0) | if (ed != 0) | ||||
| { | { | ||||
| // you must give your editor comp a size before returning it.. | // 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. | 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 | 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 | 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 | 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. | 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. | - 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. | - It's safe to assume that an editor will be deleted before its filter. | ||||
| @see hasEditor | |||||
| */ | */ | ||||
| virtual AudioProcessorEditor* createEditor() = 0; | 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. | /** Returns the active editor, if there is one. | ||||
| @@ -1276,10 +1276,8 @@ bool AudioProcessorGraph::AudioGraphIOProcessor::isOutput() const | |||||
| return type == audioOutputNode || type == midiOutputNode; | 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; } | int AudioProcessorGraph::AudioGraphIOProcessor::getNumParameters() { return 0; } | ||||
| const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterName (int) { return String::empty; } | const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterName (int) { return String::empty; } | ||||
| @@ -335,6 +335,7 @@ public: | |||||
| bool acceptsMidi() const; | bool acceptsMidi() const; | ||||
| bool producesMidi() const; | bool producesMidi() const; | ||||
| bool hasEditor() const; | |||||
| AudioProcessorEditor* createEditor(); | AudioProcessorEditor* createEditor(); | ||||
| int getNumParameters(); | int getNumParameters(); | ||||
| @@ -382,6 +383,7 @@ public: | |||||
| bool acceptsMidi() const; | bool acceptsMidi() const; | ||||
| bool producesMidi() const; | bool producesMidi() const; | ||||
| bool hasEditor() const { return false; } | |||||
| AudioProcessorEditor* createEditor() { return 0; } | AudioProcessorEditor* createEditor() { return 0; } | ||||
| int getNumParameters() { return 0; } | int getNumParameters() { return 0; } | ||||
| @@ -33,7 +33,7 @@ | |||||
| */ | */ | ||||
| #define JUCE_MAJOR_VERSION 1 | #define JUCE_MAJOR_VERSION 1 | ||||
| #define JUCE_MINOR_VERSION 52 | #define JUCE_MINOR_VERSION 52 | ||||
| #define JUCE_BUILDNUMBER 72 | |||||
| #define JUCE_BUILDNUMBER 73 | |||||
| /** Current Juce version number. | /** Current Juce version number. | ||||
| @@ -30,153 +30,315 @@ | |||||
| #if JUCE_MAC | #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; | |||||
| static bool logError (const OSStatus err, const int lineNum) | |||||
| { | |||||
| if (err == noErr) | |||||
| return true; | |||||
| log ("CoreMidi error: " + String (lineNum) + " - " + String::toHexString ((int) err)); | |||||
| jassertfalse; | |||||
| return false; | |||||
| } | |||||
| Logger::writeToLog ("CoreMidi error: " + String (lineNum) + " - " + String::toHexString ((int) err)); | |||||
| jassertfalse; | |||||
| return false; | |||||
| } | |||||
| #undef OK | |||||
| #define OK(a) logAnyErrorsMidi(a, __LINE__) | |||||
| #undef CHECK_ERROR | |||||
| #define CHECK_ERROR(a) CoreMidiHelpers::logError (a, __LINE__) | |||||
| //============================================================================== | |||||
| static const String getEndpointName (MIDIEndpointRef endpoint, bool isExternal) | |||||
| { | |||||
| String result; | |||||
| CFStringRef str = 0; | |||||
| //============================================================================== | |||||
| static const String getEndpointName (MIDIEndpointRef endpoint, bool isExternal) | |||||
| { | |||||
| String result; | |||||
| CFStringRef str = 0; | |||||
| MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &str); | |||||
| MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &str); | |||||
| if (str != 0) | |||||
| { | |||||
| result = PlatformUtilities::cfStringToJuceString (str); | |||||
| CFRelease (str); | |||||
| str = 0; | |||||
| } | |||||
| if (str != 0) | |||||
| { | |||||
| result = PlatformUtilities::cfStringToJuceString (str); | |||||
| CFRelease (str); | |||||
| str = 0; | |||||
| } | |||||
| MIDIEntityRef entity = 0; | |||||
| MIDIEndpointGetEntity (endpoint, &entity); | |||||
| MIDIEntityRef entity = 0; | |||||
| MIDIEndpointGetEntity (endpoint, &entity); | |||||
| if (entity == 0) | |||||
| return result; // probably virtual | |||||
| if (entity == 0) | |||||
| return result; // probably virtual | |||||
| if (result.isEmpty()) | |||||
| { | |||||
| // endpoint name has zero length - try the entity | |||||
| MIDIObjectGetStringProperty (entity, kMIDIPropertyName, &str); | |||||
| if (result.isEmpty()) | |||||
| { | |||||
| // endpoint name has zero length - try the entity | |||||
| MIDIObjectGetStringProperty (entity, kMIDIPropertyName, &str); | |||||
| if (str != 0) | |||||
| { | |||||
| 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; | |||||
| MIDIObjectGetStringProperty (device, kMIDIPropertyName, &str); | |||||
| if (str != 0) | if (str != 0) | ||||
| { | { | ||||
| result += PlatformUtilities::cfStringToJuceString (str); | |||||
| const String s (PlatformUtilities::cfStringToJuceString (str)); | |||||
| CFRelease (str); | CFRelease (str); | ||||
| str = 0; | |||||
| // 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(); | |||||
| } | |||||
| } | } | ||||
| } | |||||
| // now consider the device's name | |||||
| MIDIDeviceRef device = 0; | |||||
| MIDIEntityGetDevice (entity, &device); | |||||
| if (device == 0) | |||||
| return result; | return result; | ||||
| } | |||||
| MIDIObjectGetStringProperty (device, kMIDIPropertyName, &str); | |||||
| if (str != 0) | |||||
| static const String getConnectedEndpointName (MIDIEndpointRef endpoint) | |||||
| { | { | ||||
| const String s (PlatformUtilities::cfStringToJuceString (str)); | |||||
| CFRelease (str); | |||||
| String result; | |||||
| // Does the endpoint have connections? | |||||
| CFDataRef connections = 0; | |||||
| int numConnections = 0; | |||||
| MIDIObjectGetDataProperty (endpoint, kMIDIPropertyConnectionUniqueID, &connections); | |||||
| // 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 (connections != 0) | |||||
| { | { | ||||
| result = s; | |||||
| 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); | |||||
| } | } | ||||
| else if (! result.startsWithIgnoreCase (s)) | |||||
| 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) | |||||
| { | { | ||||
| // prepend the device name to the entity name | |||||
| result = (s + " " + result).trimEnd(); | |||||
| 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; | |||||
| } | } | ||||
| return result; | |||||
| } | |||||
| //============================================================================== | |||||
| class MidiPortAndEndpoint | |||||
| { | |||||
| public: | |||||
| MidiPortAndEndpoint (MIDIPortRef port_, MIDIEndpointRef endPoint_) | |||||
| : port (port_), endPoint (endPoint_) | |||||
| { | |||||
| } | |||||
| static const String getConnectedEndpointName (MIDIEndpointRef endpoint) | |||||
| { | |||||
| String result; | |||||
| ~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; | |||||
| } | |||||
| } | |||||
| // Does the endpoint have connections? | |||||
| CFDataRef connections = 0; | |||||
| int numConnections = 0; | |||||
| if (totalMessage [pendingBytes - 1] == 0xf7) | |||||
| { | |||||
| callback->handleIncomingMidiMessage (input, MidiMessage (totalMessage, pendingBytes, pendingDataTime)); | |||||
| pendingBytes = 0; | |||||
| } | |||||
| else | |||||
| { | |||||
| callback->handlePartialSysexMessage (input, totalMessage, pendingBytes, pendingDataTime); | |||||
| } | |||||
| } | |||||
| }; | |||||
| MIDIObjectGetDataProperty (endpoint, kMIDIPropertyConnectionUniqueID, &connections); | |||||
| static CriticalSection callbackLock; | |||||
| static Array<void*> activeCallbacks; | |||||
| if (connections != 0) | |||||
| static void midiInputProc (const MIDIPacketList* pktlist, | |||||
| void* readProcRefCon, | |||||
| void* /*srcConnRefCon*/) | |||||
| { | { | ||||
| numConnections = (int) (CFDataGetLength (connections) / sizeof (MIDIUniqueID)); | |||||
| double time = Time::getMillisecondCounterHiRes() * 0.001; | |||||
| const double originalTime = time; | |||||
| if (numConnections > 0) | |||||
| MidiPortAndCallback* const mpc = (MidiPortAndCallback*) readProcRefCon; | |||||
| const ScopedLock sl (CoreMidiHelpers::callbackLock); | |||||
| if (CoreMidiHelpers::activeCallbacks.contains (mpc) && mpc->active) | |||||
| { | { | ||||
| const SInt32* pid = reinterpret_cast <const SInt32*> (CFDataGetBytePtr (connections)); | |||||
| const MIDIPacket* packet = &pktlist->packet[0]; | |||||
| for (int i = 0; i < numConnections; ++i, ++pid) | |||||
| for (unsigned int i = 0; i < pktlist->numPackets; ++i) | |||||
| { | { | ||||
| MIDIUniqueID uid = EndianS32_BtoN (*pid); | |||||
| MIDIObjectRef connObject; | |||||
| MIDIObjectType connObjectType; | |||||
| OSStatus err = MIDIObjectFindByUniqueID (uid, &connObject, &connObjectType); | |||||
| const uint8* d = (const uint8*) (packet->data); | |||||
| int size = packet->length; | |||||
| if (err == noErr) | |||||
| while (size > 0) | |||||
| { | { | ||||
| String s; | |||||
| time = originalTime; | |||||
| if (connObjectType == kMIDIObjectType_ExternalSource | |||||
| || connObjectType == kMIDIObjectType_ExternalDestination) | |||||
| if (mpc->pendingBytes > 0 || d[0] == 0xf0) | |||||
| { | { | ||||
| // Connected to an external device's endpoint (10.3 and later). | |||||
| s = getEndpointName (static_cast <MIDIEndpointRef> (connObject), true); | |||||
| mpc->processSysex (d, size, time); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| // Connected to an external device (10.2) (or something else, catch-all) | |||||
| CFStringRef str = 0; | |||||
| MIDIObjectGetStringProperty (connObject, kMIDIPropertyName, &str); | |||||
| int used = 0; | |||||
| const MidiMessage m (d, size, used, 0, time); | |||||
| if (str != 0) | |||||
| if (used <= 0) | |||||
| { | { | ||||
| s = PlatformUtilities::cfStringToJuceString (str); | |||||
| CFRelease (str); | |||||
| jassertfalse; // malformed midi message | |||||
| break; | |||||
| } | |||||
| else | |||||
| { | |||||
| mpc->callback->handleIncomingMidiMessage (mpc->input, m); | |||||
| } | } | ||||
| } | |||||
| if (s.isNotEmpty()) | |||||
| { | |||||
| if (result.isNotEmpty()) | |||||
| result += ", "; | |||||
| result += s; | |||||
| size -= used; | |||||
| d += used; | |||||
| } | } | ||||
| } | } | ||||
| packet = MIDIPacketNext (packet); | |||||
| } | } | ||||
| } | } | ||||
| 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); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -191,7 +353,7 @@ const StringArray MidiOutput::getDevices() | |||||
| if (dest != 0) | if (dest != 0) | ||||
| { | { | ||||
| String name (getConnectedEndpointName (dest)); | |||||
| String name (CoreMidiHelpers::getConnectedEndpointName (dest)); | |||||
| if (name.isEmpty()) | if (name.isEmpty()) | ||||
| name = "<error>"; | name = "<error>"; | ||||
| @@ -212,48 +374,6 @@ int MidiOutput::getDefaultDeviceIndex() | |||||
| return 0; | 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* MidiOutput::openDevice (int index) | ||||
| { | { | ||||
| MidiOutput* mo = 0; | MidiOutput* mo = 0; | ||||
| @@ -263,19 +383,15 @@ MidiOutput* MidiOutput::openDevice (int index) | |||||
| MIDIEndpointRef endPoint = MIDIGetDestination (index); | MIDIEndpointRef endPoint = MIDIGetDestination (index); | ||||
| CFStringRef pname; | 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); | CFRelease (pname); | ||||
| @@ -288,27 +404,24 @@ MidiOutput* MidiOutput::openDevice (int index) | |||||
| MidiOutput* MidiOutput::createNewDevice (const String& deviceName) | MidiOutput* MidiOutput::createNewDevice (const String& deviceName) | ||||
| { | { | ||||
| MidiOutput* mo = 0; | MidiOutput* mo = 0; | ||||
| MIDIClientRef client = CoreMidiHelpers::getGlobalMidiClient(); | |||||
| if (makeSureClientExists()) | |||||
| { | |||||
| MIDIEndpointRef endPoint; | |||||
| CFStringRef name = PlatformUtilities::juceStringToCFString (deviceName); | |||||
| if (OK (MIDISourceCreate (globalMidiClient, name, &endPoint))) | |||||
| { | |||||
| mo = new MidiOutput(); | |||||
| mo->internal = new MidiPortAndEndpoint (0, endPoint); | |||||
| } | |||||
| MIDIEndpointRef endPoint; | |||||
| CFStringRef name = PlatformUtilities::juceStringToCFString (deviceName); | |||||
| CFRelease (name); | |||||
| if (client != 0 && CHECK_ERROR (MIDISourceCreate (client, name, &endPoint))) | |||||
| { | |||||
| mo = new MidiOutput(); | |||||
| mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (0, endPoint); | |||||
| } | } | ||||
| CFRelease (name); | |||||
| return mo; | return mo; | ||||
| } | } | ||||
| MidiOutput::~MidiOutput() | MidiOutput::~MidiOutput() | ||||
| { | { | ||||
| delete static_cast<MidiPortAndEndpoint*> (internal); | |||||
| delete static_cast<CoreMidiHelpers::MidiPortAndEndpoint*> (internal); | |||||
| } | } | ||||
| void MidiOutput::reset() | void MidiOutput::reset() | ||||
| @@ -326,7 +439,7 @@ void MidiOutput::setVolume (float /*leftVol*/, float /*rightVol*/) | |||||
| void MidiOutput::sendMessageNow (const MidiMessage& message) | 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()) | if (message.isSysEx()) | ||||
| { | { | ||||
| @@ -381,7 +494,7 @@ const StringArray MidiInput::getDevices() | |||||
| if (source != 0) | if (source != 0) | ||||
| { | { | ||||
| String name (getConnectedEndpointName (source)); | |||||
| String name (CoreMidiHelpers::getConnectedEndpointName (source)); | |||||
| if (name.isEmpty()) | if (name.isEmpty()) | ||||
| name = "<error>"; | name = "<error>"; | ||||
| @@ -402,132 +515,9 @@ int MidiInput::getDefaultDeviceIndex() | |||||
| return 0; | 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) | MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) | ||||
| { | { | ||||
| using namespace CoreMidiHelpers; | |||||
| MidiInput* mi = 0; | MidiInput* mi = 0; | ||||
| if (((unsigned int) index) < (unsigned int) MIDIGetNumberOfSources()) | if (((unsigned int) index) < (unsigned int) MIDIGetNumberOfSources()) | ||||
| @@ -538,20 +528,20 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) | |||||
| { | { | ||||
| CFStringRef pname; | 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; | MIDIPortRef port; | ||||
| ScopedPointer <MidiPortAndCallback> mpc (new MidiPortAndCallback()); | ScopedPointer <MidiPortAndCallback> mpc (new MidiPortAndCallback()); | ||||
| mpc->active = false; | 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->portAndEndpoint = new MidiPortAndEndpoint (port, endPoint); | ||||
| mpc->callback = callback; | mpc->callback = callback; | ||||
| @@ -562,12 +552,12 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) | |||||
| mpc->input = mi; | mpc->input = mi; | ||||
| mi->internal = mpc; | mi->internal = mpc; | ||||
| const ScopedLock sl (CoreMidiCallbacks::callbackLock); | |||||
| CoreMidiCallbacks::activeCallbacks.add (mpc.release()); | |||||
| const ScopedLock sl (callbackLock); | |||||
| activeCallbacks.add (mpc.release()); | |||||
| } | } | ||||
| else | 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) | MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) | ||||
| { | { | ||||
| using namespace CoreMidiHelpers; | |||||
| MidiInput* mi = 0; | MidiInput* mi = 0; | ||||
| MIDIClientRef client = getGlobalMidiClient(); | |||||
| if (makeSureClientExists()) | |||||
| if (client != 0) | |||||
| { | { | ||||
| ScopedPointer <MidiPortAndCallback> mpc (new MidiPortAndCallback()); | ScopedPointer <MidiPortAndCallback> mpc (new MidiPortAndCallback()); | ||||
| mpc->active = false; | mpc->active = false; | ||||
| MIDIEndpointRef endPoint; | MIDIEndpointRef endPoint; | ||||
| CFStringRef name = PlatformUtilities::juceStringToCFString(deviceName); | 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->portAndEndpoint = new MidiPortAndEndpoint (0, endPoint); | ||||
| mpc->callback = callback; | mpc->callback = callback; | ||||
| @@ -602,8 +594,8 @@ MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallba | |||||
| mpc->input = mi; | mpc->input = mi; | ||||
| mi->internal = mpc; | mi->internal = mpc; | ||||
| const ScopedLock sl (CoreMidiCallbacks::callbackLock); | |||||
| CoreMidiCallbacks::activeCallbacks.add (mpc.release()); | |||||
| const ScopedLock sl (callbackLock); | |||||
| activeCallbacks.add (mpc.release()); | |||||
| } | } | ||||
| CFRelease (name); | CFRelease (name); | ||||
| @@ -619,16 +611,18 @@ MidiInput::MidiInput (const String& name_) | |||||
| MidiInput::~MidiInput() | MidiInput::~MidiInput() | ||||
| { | { | ||||
| using namespace CoreMidiHelpers; | |||||
| MidiPortAndCallback* const mpc = static_cast<MidiPortAndCallback*> (internal); | MidiPortAndCallback* const mpc = static_cast<MidiPortAndCallback*> (internal); | ||||
| mpc->active = false; | mpc->active = false; | ||||
| { | { | ||||
| const ScopedLock sl (CoreMidiCallbacks::callbackLock); | |||||
| CoreMidiCallbacks::activeCallbacks.removeValue (mpc); | |||||
| const ScopedLock sl (callbackLock); | |||||
| activeCallbacks.removeValue (mpc); | |||||
| } | } | ||||
| if (mpc->portAndEndpoint->port != 0) | 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->portAndEndpoint; | ||||
| delete mpc; | delete mpc; | ||||
| @@ -636,61 +630,31 @@ MidiInput::~MidiInput() | |||||
| void MidiInput::start() | 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() | void MidiInput::stop() | ||||
| { | { | ||||
| const ScopedLock sl (CoreMidiCallbacks::callbackLock); | |||||
| static_cast<MidiPortAndCallback*> (internal)->active = false; | |||||
| } | |||||
| #undef log | |||||
| #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 ScopedLock sl (CoreMidiHelpers::callbackLock); | |||||
| static_cast<CoreMidiHelpers::MidiPortAndCallback*> (internal)->active = false; | |||||
| } | } | ||||
| const StringArray MidiInput::getDevices() | |||||
| { | |||||
| return StringArray(); | |||||
| } | |||||
| #undef CHECK_ERROR | |||||
| 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 | #endif | ||||
| @@ -27,67 +27,74 @@ | |||||
| // compiled on its own). | // compiled on its own). | ||||
| #if JUCE_INCLUDED_FILE && JUCE_WASAPI | #if JUCE_INCLUDED_FILE && JUCE_WASAPI | ||||
| #ifndef WASAPI_ENABLE_LOGGING | |||||
| #define WASAPI_ENABLE_LOGGING 1 | |||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| #if 1 | |||||
| namespace WasapiClasses | |||||
| { | |||||
| const String getAudioErrorDesc (HRESULT hr) | |||||
| static void logFailure (HRESULT hr) | |||||
| { | { | ||||
| const char* e = 0; | |||||
| (void) hr; | |||||
| switch (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; | |||||
| } | |||||
| #define logFailure(hr) { if (FAILED (hr)) { DBG ("WASAPI FAIL! " + getAudioErrorDesc (hr)); jassertfalse; } } | |||||
| #define OK(a) wasapi_checkResult(a) | |||||
| DBG (e); | |||||
| jassertfalse; | |||||
| } | |||||
| #endif | |||||
| } | |||||
| static bool wasapi_checkResult (HRESULT hr) | |||||
| static bool check (HRESULT hr) | |||||
| { | { | ||||
| logFailure (hr); | logFailure (hr); | ||||
| return SUCCEEDED (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; | String s; | ||||
| WCHAR* deviceId = 0; | WCHAR* deviceId = 0; | ||||
| if (OK (device->GetId (&deviceId))) | |||||
| if (check (device->GetId (&deviceId))) | |||||
| { | { | ||||
| s = String (deviceId); | s = String (deviceId); | ||||
| CoTaskMemFree (deviceId); | CoTaskMemFree (deviceId); | ||||
| @@ -96,27 +103,28 @@ static const String wasapi_getDeviceID (IMMDevice* const device) | |||||
| return s; | return s; | ||||
| } | } | ||||
| static EDataFlow wasapi_getDataFlow (IMMDevice* const device) | |||||
| static EDataFlow getDataFlow (IMMDevice* const device) | |||||
| { | { | ||||
| EDataFlow flow = eRender; | EDataFlow flow = eRender; | ||||
| ComSmartPtr <IMMEndpoint> endPoint; | 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; | 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); | 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) | memcpy (&dest, src, src->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? sizeof (WAVEFORMATEXTENSIBLE) | ||||
| : sizeof (WAVEFORMATEX)); | : sizeof (WAVEFORMATEX)); | ||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| class WASAPIDeviceBase | class WASAPIDeviceBase | ||||
| { | { | ||||
| @@ -139,21 +147,21 @@ public: | |||||
| return; | return; | ||||
| REFERENCE_TIME defaultPeriod, minPeriod; | REFERENCE_TIME defaultPeriod, minPeriod; | ||||
| if (! OK (tempClient->GetDevicePeriod (&defaultPeriod, &minPeriod))) | |||||
| if (! check (tempClient->GetDevicePeriod (&defaultPeriod, &minPeriod))) | |||||
| return; | return; | ||||
| WAVEFORMATEX* mixFormat = 0; | WAVEFORMATEX* mixFormat = 0; | ||||
| if (! OK (tempClient->GetMixFormat (&mixFormat))) | |||||
| if (! check (tempClient->GetMixFormat (&mixFormat))) | |||||
| return; | return; | ||||
| WAVEFORMATEXTENSIBLE format; | WAVEFORMATEXTENSIBLE format; | ||||
| wasapi_copyWavFormat (format, mixFormat); | |||||
| copyWavFormat (format, mixFormat); | |||||
| CoTaskMemFree (mixFormat); | CoTaskMemFree (mixFormat); | ||||
| actualNumChannels = numChannels = format.Format.nChannels; | actualNumChannels = numChannels = format.Format.nChannels; | ||||
| defaultSampleRate = format.Format.nSamplesPerSec; | 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); | rates.addUsingDefaultSort (defaultSampleRate); | ||||
| @@ -203,12 +211,12 @@ public: | |||||
| channelMaps.add (i); | channelMaps.add (i); | ||||
| REFERENCE_TIME latency; | 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; | return false; | ||||
| @@ -293,7 +301,7 @@ private: | |||||
| if (hr == S_FALSE && format.Format.nSamplesPerSec == nearestFormat->Format.nSamplesPerSec) | if (hr == S_FALSE && format.Format.nSamplesPerSec == nearestFormat->Format.nSamplesPerSec) | ||||
| { | { | ||||
| wasapi_copyWavFormat (format, (WAVEFORMATEX*) nearestFormat); | |||||
| copyWavFormat (format, (WAVEFORMATEX*) nearestFormat); | |||||
| hr = S_OK; | hr = S_OK; | ||||
| } | } | ||||
| @@ -301,13 +309,13 @@ private: | |||||
| REFERENCE_TIME defaultPeriod = 0, minPeriod = 0; | REFERENCE_TIME defaultPeriod = 0, minPeriod = 0; | ||||
| if (useExclusiveMode) | if (useExclusiveMode) | ||||
| OK (client->GetDevicePeriod (&defaultPeriod, &minPeriod)); | |||||
| check (client->GetDevicePeriod (&defaultPeriod, &minPeriod)); | |||||
| GUID session; | GUID session; | ||||
| if (hr == S_OK | 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; | actualNumChannels = format.Format.nChannels; | ||||
| const bool isFloat = format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; | const bool isFloat = format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; | ||||
| @@ -345,7 +353,7 @@ public: | |||||
| reservoirCapacity = 16384; | reservoirCapacity = 16384; | ||||
| reservoir.setSize (actualNumChannels * reservoirCapacity * sizeof (float)); | reservoir.setSize (actualNumChannels * reservoirCapacity * sizeof (float)); | ||||
| return openClient (newSampleRate, newChannels) | return openClient (newSampleRate, newChannels) | ||||
| && (numChannels == 0 || OK (client->GetService (__uuidof (IAudioCaptureClient), (void**) &captureClient))); | |||||
| && (numChannels == 0 || check (client->GetService (__uuidof (IAudioCaptureClient), (void**) &captureClient))); | |||||
| } | } | ||||
| void close() | void close() | ||||
| @@ -392,7 +400,7 @@ public: | |||||
| else | else | ||||
| { | { | ||||
| UINT32 packetLength = 0; | UINT32 packetLength = 0; | ||||
| if (! OK (captureClient->GetNextPacketSize (&packetLength))) | |||||
| if (! check (captureClient->GetNextPacketSize (&packetLength))) | |||||
| break; | break; | ||||
| if (packetLength == 0) | if (packetLength == 0) | ||||
| @@ -408,7 +416,7 @@ public: | |||||
| UINT32 numSamplesAvailable; | UINT32 numSamplesAvailable; | ||||
| DWORD flags; | 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); | const int samplesToDo = jmin (bufferSize, (int) numSamplesAvailable); | ||||
| @@ -458,7 +466,7 @@ public: | |||||
| bool open (const double newSampleRate, const BigInteger& newChannels) | bool open (const double newSampleRate, const BigInteger& newChannels) | ||||
| { | { | ||||
| return openClient (newSampleRate, 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() | void close() | ||||
| @@ -491,7 +499,7 @@ public: | |||||
| while (bufferSize > 0) | while (bufferSize > 0) | ||||
| { | { | ||||
| UINT32 padding = 0; | UINT32 padding = 0; | ||||
| if (! OK (client->GetCurrentPadding (&padding))) | |||||
| if (! check (client->GetCurrentPadding (&padding))) | |||||
| return; | return; | ||||
| int samplesToDo = useExclusiveMode ? bufferSize | int samplesToDo = useExclusiveMode ? bufferSize | ||||
| @@ -507,7 +515,7 @@ public: | |||||
| } | } | ||||
| uint8* outputData = 0; | uint8* outputData = 0; | ||||
| if (OK (renderClient->GetBuffer (samplesToDo, &outputData))) | |||||
| if (check (renderClient->GetBuffer (samplesToDo, &outputData))) | |||||
| { | { | ||||
| for (int i = 0; i < numSrcBuffers; ++i) | for (int i = 0; i < numSrcBuffers; ++i) | ||||
| converter->convertSamples (outputData, channelMaps.getUnchecked(i), srcBuffers[i], offset, samplesToDo); | converter->convertSamples (outputData, channelMaps.getUnchecked(i), srcBuffers[i], offset, samplesToDo); | ||||
| @@ -866,28 +874,28 @@ private: | |||||
| bool createDevices() | bool createDevices() | ||||
| { | { | ||||
| ComSmartPtr <IMMDeviceEnumerator> enumerator; | ComSmartPtr <IMMDeviceEnumerator> enumerator; | ||||
| if (! OK (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) | |||||
| if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) | |||||
| return false; | return false; | ||||
| ComSmartPtr <IMMDeviceCollection> deviceCollection; | ComSmartPtr <IMMDeviceCollection> deviceCollection; | ||||
| if (! OK (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, &deviceCollection))) | |||||
| if (! check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, &deviceCollection))) | |||||
| return false; | return false; | ||||
| UINT32 numDevices = 0; | UINT32 numDevices = 0; | ||||
| if (! OK (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 (! OK (deviceCollection->Item (i, &device))) | |||||
| if (! check (deviceCollection->Item (i, &device))) | |||||
| continue; | continue; | ||||
| const String deviceId (wasapi_getDeviceID (device)); | |||||
| const String deviceId (getDeviceID (device)); | |||||
| if (deviceId.isEmpty()) | if (deviceId.isEmpty()) | ||||
| continue; | continue; | ||||
| const EDataFlow flow = wasapi_getDataFlow (device); | |||||
| const EDataFlow flow = getDataFlow (device); | |||||
| if (deviceId == inputDeviceId && flow == eCapture) | if (deviceId == inputDeviceId && flow == eCapture) | ||||
| inputDevice = new WASAPIInputDevice (device, useExclusiveMode); | inputDevice = new WASAPIInputDevice (device, useExclusiveMode); | ||||
| @@ -930,7 +938,7 @@ public: | |||||
| inputDeviceIds.clear(); | inputDeviceIds.clear(); | ||||
| ComSmartPtr <IMMDeviceEnumerator> enumerator; | ComSmartPtr <IMMDeviceEnumerator> enumerator; | ||||
| if (! OK (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) | |||||
| if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) | |||||
| return; | return; | ||||
| const String defaultRenderer = getDefaultEndpoint (enumerator, false); | const String defaultRenderer = getDefaultEndpoint (enumerator, false); | ||||
| @@ -939,20 +947,20 @@ public: | |||||
| ComSmartPtr <IMMDeviceCollection> deviceCollection; | ComSmartPtr <IMMDeviceCollection> deviceCollection; | ||||
| UINT32 numDevices = 0; | 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; | return; | ||||
| for (UINT32 i = 0; i < numDevices; ++i) | for (UINT32 i = 0; i < numDevices; ++i) | ||||
| { | { | ||||
| ComSmartPtr <IMMDevice> device; | ComSmartPtr <IMMDevice> device; | ||||
| if (! OK (deviceCollection->Item (i, &device))) | |||||
| if (! check (deviceCollection->Item (i, &device))) | |||||
| continue; | continue; | ||||
| const String deviceId (wasapi_getDeviceID (device)); | |||||
| const String deviceId (getDeviceID (device)); | |||||
| DWORD state = 0; | DWORD state = 0; | ||||
| if (! OK (device->GetState (&state))) | |||||
| if (! check (device->GetState (&state))) | |||||
| continue; | continue; | ||||
| if (state != DEVICE_STATE_ACTIVE) | if (state != DEVICE_STATE_ACTIVE) | ||||
| @@ -962,18 +970,18 @@ public: | |||||
| { | { | ||||
| ComSmartPtr <IPropertyStore> properties; | ComSmartPtr <IPropertyStore> properties; | ||||
| if (! OK (device->OpenPropertyStore (STGM_READ, &properties))) | |||||
| if (! check (device->OpenPropertyStore (STGM_READ, &properties))) | |||||
| continue; | continue; | ||||
| PROPVARIANT value; | PROPVARIANT value; | ||||
| PropVariantInit (&value); | PropVariantInit (&value); | ||||
| if (OK (properties->GetValue (PKEY_Device_FriendlyName, &value))) | |||||
| if (check (properties->GetValue (PKEY_Device_FriendlyName, &value))) | |||||
| name = value.pwszVal; | name = value.pwszVal; | ||||
| PropVariantClear (&value); | PropVariantClear (&value); | ||||
| } | } | ||||
| const EDataFlow flow = wasapi_getDataFlow (device); | |||||
| const EDataFlow flow = getDataFlow (device); | |||||
| if (flow == eRender) | if (flow == eRender) | ||||
| { | { | ||||
| @@ -1057,11 +1065,11 @@ private: | |||||
| { | { | ||||
| String s; | String s; | ||||
| IMMDevice* dev = 0; | IMMDevice* dev = 0; | ||||
| if (OK (enumerator->GetDefaultAudioEndpoint (forCapture ? eCapture : eRender, | |||||
| eMultimedia, &dev))) | |||||
| if (check (enumerator->GetDefaultAudioEndpoint (forCapture ? eCapture : eRender, | |||||
| eMultimedia, &dev))) | |||||
| { | { | ||||
| WCHAR* deviceId = 0; | WCHAR* deviceId = 0; | ||||
| if (OK (dev->GetId (&deviceId))) | |||||
| if (check (dev->GetId (&deviceId))) | |||||
| { | { | ||||
| s = String (deviceId); | s = String (deviceId); | ||||
| CoTaskMemFree (deviceId); | CoTaskMemFree (deviceId); | ||||
| @@ -1078,13 +1086,12 @@ private: | |||||
| WASAPIAudioIODeviceType& operator= (const WASAPIAudioIODeviceType&); | WASAPIAudioIODeviceType& operator= (const WASAPIAudioIODeviceType&); | ||||
| }; | }; | ||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| AudioIODeviceType* juce_createAudioIODeviceType_WASAPI() | AudioIODeviceType* juce_createAudioIODeviceType_WASAPI() | ||||
| { | { | ||||
| return new WASAPIAudioIODeviceType(); | |||||
| return new WasapiClasses::WASAPIAudioIODeviceType(); | |||||
| } | } | ||||
| #undef logFailure | |||||
| #undef OK | |||||
| #endif | #endif | ||||
| @@ -1052,6 +1052,11 @@ private: | |||||
| void createInternal (const juce_wchar* text, size_t numChars); | void createInternal (const juce_wchar* text, size_t numChars); | ||||
| void appendInternal (const juce_wchar* text, int numExtraChars); | 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; } | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||