From ad0e8ae105f492e3ba545a5de57d11992e6e1c5c Mon Sep 17 00:00:00 2001 From: jules Date: Thu, 17 Jul 2008 08:51:56 +0000 Subject: [PATCH] --- .../juce_mac_CoreAudio.cpp | 2582 ++++++++--------- .../juce_mac_CoreMidi.cpp | 1184 ++++---- .../juce_mac_FileChooser.mm | 12 +- .../juce_mac_Messaging.mm | 847 +++--- .../juce_mac_NativeHeaders.h | 13 +- .../juce_mac_SystemStats.mm | 3 +- .../juce_mac_Windowing.mm | 8 +- juce_amalgamated.cpp | 287 +- juce_amalgamated.h | 6 +- .../gui/components/controls/juce_Slider.h | 2 +- .../filebrowser/juce_FileListComponent.cpp | 6 + .../filebrowser/juce_FileListComponent.h | 2 + .../special/juce_QuickTimeMovieComponent.cpp | 3 + src/juce_core/basics/juce_SystemStats.h | 2 +- 14 files changed, 2651 insertions(+), 2306 deletions(-) diff --git a/build/macosx/platform_specific_code/juce_mac_CoreAudio.cpp b/build/macosx/platform_specific_code/juce_mac_CoreAudio.cpp index c8e326c029..196c69de2a 100644 --- a/build/macosx/platform_specific_code/juce_mac_CoreAudio.cpp +++ b/build/macosx/platform_specific_code/juce_mac_CoreAudio.cpp @@ -1,1291 +1,1291 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-7 by Raw Material Software ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the - GNU General Public License, as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later version. - - JUCE is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with JUCE; if not, visit www.gnu.org/licenses or write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - ------------------------------------------------------------------------------ - - If you'd like to release a closed-source product which uses JUCE, commercial - licenses are also available: visit www.rawmaterialsoftware.com/juce for - more information. - - ============================================================================== -*/ - -#include "../../../src/juce_core/basics/juce_StandardHeader.h" -#include - -BEGIN_JUCE_NAMESPACE - - -#include "../../../src/juce_appframework/audio/devices/juce_AudioIODeviceType.h" -#include "../../../src/juce_appframework/events/juce_Timer.h" -#include "../../../src/juce_core/threads/juce_ScopedLock.h" -#include "../../../src/juce_core/threads/juce_Thread.h" -#include "../../../src/juce_core/text/juce_LocalisedStrings.h" -#include "../../../src/juce_appframework/gui/components/buttons/juce_TextButton.h" -#include "../../../src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.h" -#include "../../../src/juce_appframework/gui/components/controls/juce_ComboBox.h" -#include "../../../src/juce_appframework/gui/components/special/juce_AudioDeviceSelectorComponent.h" -#include "../../../src/juce_appframework/gui/components/windows/juce_AlertWindow.h" - - -//============================================================================== -#ifndef JUCE_COREAUDIO_ERROR_LOGGING_ENABLED - #define JUCE_COREAUDIO_ERROR_LOGGING_ENABLED 1 -#endif - -//============================================================================== -#undef log -#if JUCE_COREAUDIO_LOGGING_ENABLED - #define log(a) Logger::writeToLog (a) -#else - #define log(a) -#endif - -#undef OK -#if JUCE_COREAUDIO_ERROR_LOGGING_ENABLED - static bool logAnyErrors_CoreAudio (const OSStatus err, const int lineNum) - { - if (err == noErr) - return true; - - Logger::writeToLog (T("CoreAudio error: ") + String (lineNum) + T(" - ") + String::toHexString ((int)err)); - jassertfalse - return false; - } - - #define OK(a) logAnyErrors_CoreAudio (a, __LINE__) -#else - #define OK(a) (a == noErr) -#endif - -//============================================================================== -static const int maxNumChans = 96; - - -//============================================================================== -class CoreAudioInternal : public Timer -{ -public: - //============================================================================== - CoreAudioInternal (AudioDeviceID id) - : deviceID (id), - started (false), - audioBuffer (0), - numInputChans (0), - numOutputChans (0), - callbacksAllowed (true), - numInputChannelInfos (0), - numOutputChannelInfos (0), - inputLatency (0), - outputLatency (0), - callback (0), - inputDevice (0), - isSlaveDevice (false) - { - sampleRate = 0; - bufferSize = 512; - - if (deviceID == 0) - { - error = TRANS("can't open device"); - } - else - { - updateDetailsFromDevice(); - - AudioDeviceAddPropertyListener (deviceID, - kAudioPropertyWildcardChannel, - kAudioPropertyWildcardSection, - kAudioPropertyWildcardPropertyID, - deviceListenerProc, this); - } - } - - ~CoreAudioInternal() - { - AudioDeviceRemovePropertyListener (deviceID, - kAudioPropertyWildcardChannel, - kAudioPropertyWildcardSection, - kAudioPropertyWildcardPropertyID, - deviceListenerProc); - - stop (false); - - juce_free (audioBuffer); - delete inputDevice; - } - - void setTempBufferSize (const int numChannels, const int numSamples) - { - juce_free (audioBuffer); - - audioBuffer = (float*) juce_calloc (32 + numChannels * numSamples * sizeof (float)); - - zeromem (tempInputBuffers, sizeof (tempInputBuffers)); - zeromem (tempOutputBuffers, sizeof (tempOutputBuffers)); - - int count = 0; - int i; - for (i = 0; i < numInputChans; ++i) - tempInputBuffers[i] = audioBuffer + count++ * numSamples; - - for (i = 0; i < numOutputChans; ++i) - tempOutputBuffers[i] = audioBuffer + count++ * numSamples; - } - - // returns the number of actual available channels - void fillInChannelInfo (bool input) - { - int chanNum = 0, activeChans = 0; - UInt32 size; - - if (OK (AudioDeviceGetPropertyInfo (deviceID, 0, input, kAudioDevicePropertyStreamConfiguration, &size, 0))) - { - AudioBufferList* const bufList = (AudioBufferList*) juce_calloc (size); - - if (OK (AudioDeviceGetProperty (deviceID, 0, input, kAudioDevicePropertyStreamConfiguration, &size, bufList))) - { - const int numStreams = bufList->mNumberBuffers; - - for (int i = 0; i < numStreams; ++i) - { - const AudioBuffer& b = bufList->mBuffers[i]; - - for (unsigned int j = 0; j < b.mNumberChannels; ++j) - { - if (input) - { - if (activeInputChans[chanNum]) - { - inputChannelInfo [activeChans].sourceChannelNum = chanNum; - inputChannelInfo [activeChans].streamNum = i; - inputChannelInfo [activeChans].dataOffsetSamples = j; - inputChannelInfo [activeChans].dataStrideSamples = b.mNumberChannels; - ++activeChans; - numInputChannelInfos = activeChans; - } - - inChanNames.add (T("input ") + String (chanNum + 1)); - } - else - { - if (activeOutputChans[chanNum]) - { - outputChannelInfo [activeChans].sourceChannelNum = chanNum; - outputChannelInfo [activeChans].streamNum = i; - outputChannelInfo [activeChans].dataOffsetSamples = j; - outputChannelInfo [activeChans].dataStrideSamples = b.mNumberChannels; - ++activeChans; - numOutputChannelInfos = activeChans; - } - - outChanNames.add (T("output ") + String (chanNum + 1)); - } - - ++chanNum; - } - } - } - - juce_free (bufList); - } - } - - void updateDetailsFromDevice() - { - stopTimer(); - - if (deviceID == 0) - return; - - const ScopedLock sl (callbackLock); - - Float64 sr; - UInt32 size = sizeof (Float64); - if (OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyNominalSampleRate, &size, &sr))) - sampleRate = sr; - - UInt32 framesPerBuf; - size = sizeof (framesPerBuf); - - if (OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyBufferFrameSize, &size, &framesPerBuf))) - { - bufferSize = framesPerBuf; - - if (bufferSize > 0) - setTempBufferSize (numInputChans + numOutputChans, bufferSize); - } - - bufferSizes.clear(); - - if (OK (AudioDeviceGetPropertyInfo (deviceID, 0, false, kAudioDevicePropertyBufferFrameSizeRange, &size, 0))) - { - AudioValueRange* ranges = (AudioValueRange*) juce_calloc (size); - - if (OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyBufferFrameSizeRange, &size, ranges))) - { - bufferSizes.add ((int) ranges[0].mMinimum); - - for (int i = 32; i < 8192; i += 32) - { - for (int j = size / sizeof (AudioValueRange); --j >= 0;) - { - if (i >= ranges[j].mMinimum && i <= ranges[j].mMaximum) - { - bufferSizes.addIfNotAlreadyThere (i); - break; - } - } - } - - if (bufferSize > 0) - bufferSizes.addIfNotAlreadyThere (bufferSize); - } - - juce_free (ranges); - } - - if (bufferSizes.size() == 0 && bufferSize > 0) - bufferSizes.add (bufferSize); - - sampleRates.clear(); - const double possibleRates[] = { 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0 }; - String rates; - - if (OK (AudioDeviceGetPropertyInfo (deviceID, 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size, 0))) - { - AudioValueRange* ranges = (AudioValueRange*) juce_calloc (size); - - if (OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size, ranges))) - { - for (int i = 0; i < numElementsInArray (possibleRates); ++i) - { - bool ok = false; - - for (int j = size / sizeof (AudioValueRange); --j >= 0;) - if (possibleRates[i] >= ranges[j].mMinimum - 2 && possibleRates[i] <= ranges[j].mMaximum + 2) - ok = true; - - if (ok) - { - sampleRates.add (possibleRates[i]); - rates << possibleRates[i] << T(" "); - } - } - } - - juce_free (ranges); - } - - if (sampleRates.size() == 0 && sampleRate > 0) - { - sampleRates.add (sampleRate); - rates << sampleRate; - } - - log (T("sr: ") + rates); - - inputLatency = 0; - outputLatency = 0; - UInt32 lat; - size = sizeof (UInt32); - if (AudioDeviceGetProperty (deviceID, 0, true, kAudioDevicePropertyLatency, &size, &lat) == noErr) - inputLatency = (int) lat; - - if (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyLatency, &size, &lat) == noErr) - outputLatency = (int) lat; - - log (T("lat: ") + String (inputLatency) + T(" ") + String (outputLatency)); - - inChanNames.clear(); - outChanNames.clear(); - - zeromem (inputChannelInfo, sizeof (inputChannelInfo)); - zeromem (outputChannelInfo, sizeof (outputChannelInfo)); - - fillInChannelInfo (true); - fillInChannelInfo (false); - } - - //============================================================================== - const StringArray getSources (bool input) - { - StringArray s; - int num = 0; - OSType* types = getAllDataSourcesForDevice (deviceID, input, num); - - if (types != 0) - { - for (int i = 0; i < num; ++i) - { - AudioValueTranslation avt; - char buffer[256]; - - avt.mInputData = (void*) &(types[i]); - avt.mInputDataSize = sizeof (UInt32); - avt.mOutputData = buffer; - avt.mOutputDataSize = 256; - - UInt32 transSize = sizeof (avt); - if (OK (AudioDeviceGetProperty (deviceID, 0, input, kAudioDevicePropertyDataSourceNameForID, &transSize, &avt))) - { - DBG (buffer); - s.add (buffer); - } - } - - juce_free (types); - } - - return s; - } - - int getCurrentSourceIndex (bool input) const - { - OSType currentSourceID = 0; - UInt32 size = 0; - int result = -1; - - if (deviceID != 0 - && OK (AudioDeviceGetPropertyInfo (deviceID, 0, input, kAudioDevicePropertyDataSource, &size, 0))) - { - if (OK (AudioDeviceGetProperty (deviceID, 0, input, kAudioDevicePropertyDataSource, &size, ¤tSourceID))) - { - int num = 0; - OSType* const types = getAllDataSourcesForDevice (deviceID, input, num); - - if (types != 0) - { - for (int i = 0; i < num; ++i) - { - if (types[num] == currentSourceID) - { - result = i; - break; - } - } - - juce_free (types); - } - } - } - - return result; - } - - void setCurrentSourceIndex (int index, bool input) - { - if (deviceID != 0) - { - int num = 0; - OSType* types = getAllDataSourcesForDevice (deviceID, input, num); - - if (types != 0) - { - if (((unsigned int) index) < num) - { - OSType typeId = types[index]; - AudioDeviceSetProperty (deviceID, 0, 0, input, kAudioDevicePropertyDataSource, sizeof (typeId), &typeId); - } - - juce_free (types); - } - } - } - - //============================================================================== - const String reopen (const BitArray& inputChannels, - const BitArray& outputChannels, - double newSampleRate, - int bufferSizeSamples) - { - error = String::empty; - log ("CoreAudio reopen"); - callbacksAllowed = false; - stopTimer(); - - stop (false); - - activeInputChans = inputChannels; - activeOutputChans = outputChannels; - - activeInputChans.setRange (inChanNames.size(), - activeInputChans.getHighestBit() + 1 - inChanNames.size(), - false); - - activeOutputChans.setRange (outChanNames.size(), - activeOutputChans.getHighestBit() + 1 - outChanNames.size(), - false); - - numInputChans = activeInputChans.countNumberOfSetBits(); - numOutputChans = activeOutputChans.countNumberOfSetBits(); - - // set sample rate - Float64 sr = newSampleRate; - UInt32 size = sizeof (sr); - OK (AudioDeviceSetProperty (deviceID, 0, 0, false, kAudioDevicePropertyNominalSampleRate, size, &sr)); - OK (AudioDeviceSetProperty (deviceID, 0, 0, true, kAudioDevicePropertyNominalSampleRate, size, &sr)); - - // change buffer size - UInt32 framesPerBuf = bufferSizeSamples; - size = sizeof (framesPerBuf); - - OK (AudioDeviceSetProperty (deviceID, 0, 0, false, kAudioDevicePropertyBufferFrameSize, size, &framesPerBuf)); - OK (AudioDeviceSetProperty (deviceID, 0, 0, true, kAudioDevicePropertyBufferFrameSize, size, &framesPerBuf)); - - // wait for the changes to happen (on some devices) - int i = 30; - while (--i >= 0) - { - updateDetailsFromDevice(); - - if (sampleRate == newSampleRate && bufferSizeSamples == bufferSize) - break; - - Thread::sleep (100); - } - - if (i < 0) - error = "Couldn't change sample rate/buffer size"; - - if (sampleRates.size() == 0) - error = "Device has no available sample-rates"; - - if (bufferSizes.size() == 0) - error = "Device has no available buffer-sizes"; - - if (inputDevice != 0 && error.isEmpty()) - error = inputDevice->reopen (inputChannels, - outputChannels, - newSampleRate, - bufferSizeSamples); - - callbacksAllowed = true; - - return error; - } - - bool start (AudioIODeviceCallback* cb) - { - if (! started) - { - callback = 0; - - if (deviceID != 0) - { - if (OK (AudioDeviceAddIOProc (deviceID, audioIOProc, (void*) this))) - { - if (OK (AudioDeviceStart (deviceID, audioIOProc))) - { - started = true; - } - else - { - OK (AudioDeviceRemoveIOProc (deviceID, audioIOProc)); - } - } - } - } - - if (started) - { - const ScopedLock sl (callbackLock); - callback = cb; - } - - if (inputDevice != 0) - return started && inputDevice->start (cb); - else - return started; - } - - void stop (bool leaveInterruptRunning) - { - callbackLock.enter(); - callback = 0; - callbackLock.exit(); - - if (started - && (deviceID != 0) - && ! leaveInterruptRunning) - { - OK (AudioDeviceStop (deviceID, audioIOProc)); - OK (AudioDeviceRemoveIOProc (deviceID, audioIOProc)); - started = false; - - callbackLock.enter(); - callbackLock.exit(); - - // wait until it's definately stopped calling back.. - for (int i = 40; --i >= 0;) - { - Thread::sleep (50); - - UInt32 running = 0; - UInt32 size = sizeof (running); - OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyDeviceIsRunning, &size, &running)); - if (running == 0) - break; - } - - callbackLock.enter(); - callbackLock.exit(); - } - - if (inputDevice != 0) - inputDevice->stop (leaveInterruptRunning); - } - - double getSampleRate() const - { - return sampleRate; - } - - int getBufferSize() const - { - return bufferSize; - } - - void audioCallback (const AudioBufferList* inInputData, - AudioBufferList* outOutputData) - { - int i; - const ScopedLock sl (callbackLock); - - if (callback != 0) - { - if (inputDevice == 0) - { - for (i = numInputChans; --i >= 0;) - { - const CallbackDetailsForChannel& info = inputChannelInfo[i]; - float* dest = tempInputBuffers [info.sourceChannelNum]; - const float* src = ((const float*) inInputData->mBuffers[info.streamNum].mData) - + info.dataOffsetSamples; - const int stride = info.dataStrideSamples; - - if (stride != 0) // if this is zero, info is invalid - { - for (int j = bufferSize; --j >= 0;) - { - *dest++ = *src; - src += stride; - } - } - } - } - - if (! isSlaveDevice) - { - if (inputDevice == 0) - { - callback->audioDeviceIOCallback ((const float**) tempInputBuffers, - numInputChans, - tempOutputBuffers, - numOutputChans, - bufferSize); - } - else - { - jassert (inputDevice->bufferSize == bufferSize); - - callback->audioDeviceIOCallback ((const float**) inputDevice->tempInputBuffers, - inputDevice->numInputChans, - tempOutputBuffers, - numOutputChans, - bufferSize); - } - - for (i = numOutputChans; --i >= 0;) - { - const CallbackDetailsForChannel& info = outputChannelInfo[i]; - const float* src = tempOutputBuffers [i]; - float* dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) - + info.dataOffsetSamples; - const int stride = info.dataStrideSamples; - - if (stride != 0) // if this is zero, info is invalid - { - for (int j = bufferSize; --j >= 0;) - { - *dest = *src++; - dest += stride; - } - } - } - } - } - else - { - for (i = jmin (numOutputChans, numOutputChannelInfos); --i >= 0;) - { - const CallbackDetailsForChannel& info = outputChannelInfo[i]; - float* dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) - + info.dataOffsetSamples; - const int stride = info.dataStrideSamples; - - if (stride != 0) // if this is zero, info is invalid - { - for (int j = bufferSize; --j >= 0;) - { - *dest = 0.0f; - dest += stride; - } - } - } - } - } - - // called by callbacks - void deviceDetailsChanged() - { - if (callbacksAllowed) - startTimer (100); - } - - void timerCallback() - { - stopTimer(); - log ("CoreAudio device changed callback"); - - const double oldSampleRate = sampleRate; - const int oldBufferSize = bufferSize; - updateDetailsFromDevice(); - - if (oldBufferSize != bufferSize || oldSampleRate != sampleRate) - { - callbacksAllowed = false; - stop (false); - updateDetailsFromDevice(); - callbacksAllowed = true; - } - } - - CoreAudioInternal* getRelatedDevice() const - { - UInt32 size = 0; - CoreAudioInternal* result = 0; - - if (deviceID != 0 - && AudioDeviceGetPropertyInfo (deviceID, 0, false, kAudioDevicePropertyRelatedDevices, &size, 0) == noErr - && size > 0) - { - AudioDeviceID* devs = (AudioDeviceID*) juce_calloc (size); - - if (OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyRelatedDevices, &size, devs))) - { - for (unsigned int i = 0; i < size / sizeof (AudioDeviceID); ++i) - { - if (devs[i] != deviceID && devs[i] != 0) - { - result = new CoreAudioInternal (devs[i]); - - if (result->error.isEmpty()) - { - const bool thisIsInput = inChanNames.size() > 0 && outChanNames.size() == 0; - const bool otherIsInput = result->inChanNames.size() > 0 && result->outChanNames.size() == 0; - - if (thisIsInput != otherIsInput - || (inChanNames.size() + outChanNames.size() == 0) - || (result->inChanNames.size() + result->outChanNames.size()) == 0) - break; - } - - deleteAndZero (result); - } - } - } - - juce_free (devs); - } - - return result; - } - - //============================================================================== - juce_UseDebuggingNewOperator - - String error; - int inputLatency, outputLatency; - BitArray activeInputChans, activeOutputChans; - StringArray inChanNames, outChanNames; - Array sampleRates; - Array bufferSizes; - AudioIODeviceCallback* callback; - - CoreAudioInternal* inputDevice; - bool isSlaveDevice; - -private: - CriticalSection callbackLock; - AudioDeviceID deviceID; - bool started; - double sampleRate; - int bufferSize; - float* audioBuffer; - int numInputChans, numOutputChans; - bool callbacksAllowed; - - struct CallbackDetailsForChannel - { - int sourceChannelNum; - int streamNum; - int dataOffsetSamples; - int dataStrideSamples; - }; - - int numInputChannelInfos, numOutputChannelInfos; - CallbackDetailsForChannel inputChannelInfo [maxNumChans]; - CallbackDetailsForChannel outputChannelInfo [maxNumChans]; - float* tempInputBuffers [maxNumChans]; - float* tempOutputBuffers [maxNumChans]; - - CoreAudioInternal (const CoreAudioInternal&); - const CoreAudioInternal& operator= (const CoreAudioInternal&); - - //============================================================================== - static OSStatus audioIOProc (AudioDeviceID inDevice, - const AudioTimeStamp* inNow, - const AudioBufferList* inInputData, - const AudioTimeStamp* inInputTime, - AudioBufferList* outOutputData, - const AudioTimeStamp* inOutputTime, - void* device) - { - ((CoreAudioInternal*) device)->audioCallback (inInputData, outOutputData); - return noErr; - } - - static OSStatus deviceListenerProc (AudioDeviceID inDevice, - UInt32 inLine, - Boolean isInput, - AudioDevicePropertyID inPropertyID, - void* inClientData) - { - CoreAudioInternal* const intern = (CoreAudioInternal*) inClientData; - - switch (inPropertyID) - { - case kAudioDevicePropertyBufferSize: - case kAudioDevicePropertyBufferFrameSize: - case kAudioDevicePropertyNominalSampleRate: - case kAudioDevicePropertyStreamFormat: - case kAudioDevicePropertyDeviceIsAlive: - intern->deviceDetailsChanged(); - break; - - case kAudioDevicePropertyBufferSizeRange: - case kAudioDevicePropertyVolumeScalar: - case kAudioDevicePropertyMute: - case kAudioDevicePropertyPlayThru: - case kAudioDevicePropertyDataSource: - case kAudioDevicePropertyDeviceIsRunning: - break; - } - - return noErr; - } - - //============================================================================== - static OSType* getAllDataSourcesForDevice (AudioDeviceID deviceID, const bool input, int& num) - { - OSType* types = 0; - UInt32 size = 0; - num = 0; - - if (deviceID != 0 - && OK (AudioDeviceGetPropertyInfo (deviceID, 0, input, kAudioDevicePropertyDataSources, &size, 0))) - { - types = (OSType*) juce_calloc (size); - - if (OK (AudioDeviceGetProperty (deviceID, 0, input, kAudioDevicePropertyDataSources, &size, types))) - { - num = size / sizeof (OSType); - } - else - { - juce_free (types); - types = 0; - } - } - - return types; - } -}; - - -//============================================================================== -class CoreAudioIODevice : public AudioIODevice -{ -public: - CoreAudioIODevice (const String& deviceName, - AudioDeviceID inputDeviceId, - const int inputIndex_, - AudioDeviceID outputDeviceId, - const int outputIndex_) - : AudioIODevice (deviceName, "CoreAudio"), - inputIndex (inputIndex_), - outputIndex (outputIndex_), - isOpen_ (false), - isStarted (false) - { - internal = 0; - CoreAudioInternal* device = 0; - - if (outputDeviceId == 0 || outputDeviceId == inputDeviceId) - { - jassert (inputDeviceId != 0); - - device = new CoreAudioInternal (inputDeviceId); - lastError = device->error; - - if (lastError.isNotEmpty()) - deleteAndZero (device); - } - else - { - device = new CoreAudioInternal (outputDeviceId); - lastError = device->error; - - if (lastError.isNotEmpty()) - { - deleteAndZero (device); - } - else if (inputDeviceId != 0) - { - CoreAudioInternal* secondDevice = new CoreAudioInternal (inputDeviceId); - lastError = device->error; - - if (lastError.isNotEmpty()) - { - delete secondDevice; - } - else - { - device->inputDevice = secondDevice; - secondDevice->isSlaveDevice = true; - } - } - } - - internal = device; - - AudioHardwareAddPropertyListener (kAudioPropertyWildcardPropertyID, - hardwareListenerProc, internal); - } - - ~CoreAudioIODevice() - { - AudioHardwareRemovePropertyListener (kAudioPropertyWildcardPropertyID, - hardwareListenerProc); - - delete internal; - } - - const StringArray getOutputChannelNames() - { - return internal->outChanNames; - } - - const StringArray getInputChannelNames() - { - if (internal->inputDevice != 0) - return internal->inputDevice->inChanNames; - else - return internal->inChanNames; - } - - int getNumSampleRates() - { - return internal->sampleRates.size(); - } - - double getSampleRate (int index) - { - return internal->sampleRates [index]; - } - - int getNumBufferSizesAvailable() - { - return internal->bufferSizes.size(); - } - - int getBufferSizeSamples (int index) - { - return internal->bufferSizes [index]; - } - - int getDefaultBufferSize() - { - for (int i = 0; i < getNumBufferSizesAvailable(); ++i) - if (getBufferSizeSamples(i) >= 512) - return getBufferSizeSamples(i); - - return 512; - } - - const String open (const BitArray& inputChannels, - const BitArray& outputChannels, - double sampleRate, - int bufferSizeSamples) - { - isOpen_ = true; - - if (bufferSizeSamples <= 0) - bufferSizeSamples = getDefaultBufferSize(); - - internal->reopen (inputChannels, outputChannels, sampleRate, bufferSizeSamples); - lastError = internal->error; - return lastError; - } - - void close() - { - isOpen_ = false; - } - - bool isOpen() - { - return isOpen_; - } - - int getCurrentBufferSizeSamples() - { - return internal != 0 ? internal->getBufferSize() : 512; - } - - double getCurrentSampleRate() - { - return internal != 0 ? internal->getSampleRate() : 0; - } - - int getCurrentBitDepth() - { - return 32; // no way to find out, so just assume it's high.. - } - - const BitArray getActiveOutputChannels() const - { - return internal != 0 ? internal->activeOutputChans : BitArray(); - } - - const BitArray getActiveInputChannels() const - { - BitArray chans; - - if (internal != 0) - { - chans = internal->activeInputChans; - - if (internal->inputDevice != 0) - chans.orWith (internal->inputDevice->activeInputChans); - } - - return chans; - } - - int getOutputLatencyInSamples() - { - if (internal == 0) - return 0; - - // this seems like a good guess at getting the latency right - comparing - // this with a round-trip measurement, it gets it to within a few millisecs - // for the built-in mac soundcard - return internal->outputLatency + internal->getBufferSize() * 2; - } - - int getInputLatencyInSamples() - { - if (internal == 0) - return 0; - - return internal->inputLatency + internal->getBufferSize() * 2; - } - - void start (AudioIODeviceCallback* callback) - { - if (internal != 0 && ! isStarted) - { - if (callback != 0) - callback->audioDeviceAboutToStart (this); - - isStarted = true; - internal->start (callback); - } - } - - void stop() - { - if (isStarted && internal != 0) - { - AudioIODeviceCallback* const lastCallback = internal->callback; - - isStarted = false; - internal->stop (true); - - if (lastCallback != 0) - lastCallback->audioDeviceStopped(); - } - } - - bool isPlaying() - { - if (internal->callback == 0) - isStarted = false; - - return isStarted; - } - - const String getLastError() - { - return lastError; - } - - int inputIndex, outputIndex; - - juce_UseDebuggingNewOperator - -private: - CoreAudioInternal* internal; - bool isOpen_, isStarted; - String lastError; - - static OSStatus hardwareListenerProc (AudioHardwarePropertyID inPropertyID, void* inClientData) - { - CoreAudioInternal* const intern = (CoreAudioInternal*) inClientData; - - switch (inPropertyID) - { - case kAudioHardwarePropertyDevices: - intern->deviceDetailsChanged(); - break; - - case kAudioHardwarePropertyDefaultOutputDevice: - case kAudioHardwarePropertyDefaultInputDevice: - case kAudioHardwarePropertyDefaultSystemOutputDevice: - break; - } - - return noErr; - } - - CoreAudioIODevice (const CoreAudioIODevice&); - const CoreAudioIODevice& operator= (const CoreAudioIODevice&); -}; - -//============================================================================== -class CoreAudioIODeviceType : public AudioIODeviceType -{ -public: - //============================================================================== - CoreAudioIODeviceType() - : AudioIODeviceType (T("CoreAudio")), - hasScanned (false) - { - } - - ~CoreAudioIODeviceType() - { - } - - //============================================================================== - void scanForDevices() - { - hasScanned = true; - - inputDeviceNames.clear(); - outputDeviceNames.clear(); - inputIds.clear(); - outputIds.clear(); - - UInt32 size; - if (OK (AudioHardwareGetPropertyInfo (kAudioHardwarePropertyDevices, &size, 0))) - { - AudioDeviceID* const devs = (AudioDeviceID*) juce_calloc (size); - - if (OK (AudioHardwareGetProperty (kAudioHardwarePropertyDevices, &size, devs))) - { - static bool alreadyLogged = false; - const int num = size / sizeof (AudioDeviceID); - for (int i = 0; i < num; ++i) - { - char name[1024]; - size = sizeof (name); - if (OK (AudioDeviceGetProperty (devs[i], 0, false, kAudioDevicePropertyDeviceName, &size, name))) - { - const String nameString (String::fromUTF8 ((const uint8*) name, strlen (name))); - - if (! alreadyLogged) - log (T("CoreAudio device: ") + nameString); - - const int numIns = getNumChannels (devs[i], true); - const int numOuts = getNumChannels (devs[i], false); - - if (numIns > 0) - { - inputDeviceNames.add (nameString); - inputIds.add (devs[i]); - } - - if (numOuts > 0) - { - outputDeviceNames.add (nameString); - outputIds.add (devs[i]); - } - } - } - - alreadyLogged = true; - } - - juce_free (devs); - } - - inputDeviceNames.appendNumbersToDuplicates (false, true); - outputDeviceNames.appendNumbersToDuplicates (false, true); - } - - const StringArray getDeviceNames (const bool wantInputNames) const - { - jassert (hasScanned); // need to call scanForDevices() before doing this - - if (wantInputNames) - return inputDeviceNames; - else - return outputDeviceNames; - } - - int getDefaultDeviceIndex (const bool forInput) const - { - jassert (hasScanned); // need to call scanForDevices() before doing this - - AudioDeviceID deviceID; - UInt32 size = sizeof (deviceID); - - // if they're asking for any input channels at all, use the default input, so we - // get the built-in mic rather than the built-in output with no inputs.. - if (AudioHardwareGetProperty (forInput ? kAudioHardwarePropertyDefaultInputDevice - : kAudioHardwarePropertyDefaultOutputDevice, - &size, &deviceID) == noErr) - { - if (forInput) - { - for (int i = inputIds.size(); --i >= 0;) - if (inputIds[i] == deviceID) - return i; - } - else - { - for (int i = outputIds.size(); --i >= 0;) - if (outputIds[i] == deviceID) - return i; - } - } - - return 0; - } - - int getIndexOfDevice (AudioIODevice* device, const bool asInput) const - { - jassert (hasScanned); // need to call scanForDevices() before doing this - - CoreAudioIODevice* const d = dynamic_cast (device); - if (d == 0) - return -1; - - return asInput ? d->inputIndex - : d->outputIndex; - } - - bool hasSeparateInputsAndOutputs() const { return true; } - - AudioIODevice* createDevice (const String& outputDeviceName, - const String& inputDeviceName) - { - jassert (hasScanned); // need to call scanForDevices() before doing this - - const int inputIndex = inputDeviceNames.indexOf (inputDeviceName); - const int outputIndex = outputDeviceNames.indexOf (outputDeviceName); - - String deviceName (outputDeviceName); - if (deviceName.isEmpty()) - deviceName = inputDeviceName; - - if (index >= 0) - return new CoreAudioIODevice (deviceName, - inputIds [inputIndex], - inputIndex, - outputIds [outputIndex], - outputIndex); - - return 0; - } - - //============================================================================== - juce_UseDebuggingNewOperator - -private: - StringArray inputDeviceNames, outputDeviceNames; - Array inputIds, outputIds; - - bool hasScanned; - - static int getNumChannels (AudioDeviceID deviceID, bool input) - { - int total = 0; - UInt32 size; - - if (OK (AudioDeviceGetPropertyInfo (deviceID, 0, input, kAudioDevicePropertyStreamConfiguration, &size, 0))) - { - AudioBufferList* const bufList = (AudioBufferList*) juce_calloc (size); - - if (OK (AudioDeviceGetProperty (deviceID, 0, input, kAudioDevicePropertyStreamConfiguration, &size, bufList))) - { - const int numStreams = bufList->mNumberBuffers; - - for (int i = 0; i < numStreams; ++i) - { - const AudioBuffer& b = bufList->mBuffers[i]; - total += b.mNumberChannels; - } - } - - juce_free (bufList); - } - - return total; - } - - CoreAudioIODeviceType (const CoreAudioIODeviceType&); - const CoreAudioIODeviceType& operator= (const CoreAudioIODeviceType&); -}; - -//============================================================================== -AudioIODeviceType* juce_createDefaultAudioIODeviceType() -{ - return new CoreAudioIODeviceType(); -} - -#undef log - -END_JUCE_NAMESPACE +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../src/juce_core/basics/juce_StandardHeader.h" +#include + +BEGIN_JUCE_NAMESPACE + + +#include "../../../src/juce_appframework/audio/devices/juce_AudioIODeviceType.h" +#include "../../../src/juce_appframework/events/juce_Timer.h" +#include "../../../src/juce_core/threads/juce_ScopedLock.h" +#include "../../../src/juce_core/threads/juce_Thread.h" +#include "../../../src/juce_core/text/juce_LocalisedStrings.h" +#include "../../../src/juce_appframework/gui/components/buttons/juce_TextButton.h" +#include "../../../src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.h" +#include "../../../src/juce_appframework/gui/components/controls/juce_ComboBox.h" +#include "../../../src/juce_appframework/gui/components/special/juce_AudioDeviceSelectorComponent.h" +#include "../../../src/juce_appframework/gui/components/windows/juce_AlertWindow.h" + + +//============================================================================== +#ifndef JUCE_COREAUDIO_ERROR_LOGGING_ENABLED + #define JUCE_COREAUDIO_ERROR_LOGGING_ENABLED 1 +#endif + +//============================================================================== +#undef log +#if JUCE_COREAUDIO_LOGGING_ENABLED + #define log(a) Logger::writeToLog (a) +#else + #define log(a) +#endif + +#undef OK +#if JUCE_COREAUDIO_ERROR_LOGGING_ENABLED + static bool logAnyErrors_CoreAudio (const OSStatus err, const int lineNum) + { + if (err == noErr) + return true; + + Logger::writeToLog (T("CoreAudio error: ") + String (lineNum) + T(" - ") + String::toHexString ((int)err)); + jassertfalse + return false; + } + + #define OK(a) logAnyErrors_CoreAudio (a, __LINE__) +#else + #define OK(a) (a == noErr) +#endif + +//============================================================================== +static const int maxNumChans = 96; + + +//============================================================================== +class CoreAudioInternal : public Timer +{ +public: + //============================================================================== + CoreAudioInternal (AudioDeviceID id) + : deviceID (id), + started (false), + audioBuffer (0), + numInputChans (0), + numOutputChans (0), + callbacksAllowed (true), + numInputChannelInfos (0), + numOutputChannelInfos (0), + inputLatency (0), + outputLatency (0), + callback (0), + inputDevice (0), + isSlaveDevice (false) + { + sampleRate = 0; + bufferSize = 512; + + if (deviceID == 0) + { + error = TRANS("can't open device"); + } + else + { + updateDetailsFromDevice(); + + AudioDeviceAddPropertyListener (deviceID, + kAudioPropertyWildcardChannel, + kAudioPropertyWildcardSection, + kAudioPropertyWildcardPropertyID, + deviceListenerProc, this); + } + } + + ~CoreAudioInternal() + { + AudioDeviceRemovePropertyListener (deviceID, + kAudioPropertyWildcardChannel, + kAudioPropertyWildcardSection, + kAudioPropertyWildcardPropertyID, + deviceListenerProc); + + stop (false); + + juce_free (audioBuffer); + delete inputDevice; + } + + void setTempBufferSize (const int numChannels, const int numSamples) + { + juce_free (audioBuffer); + + audioBuffer = (float*) juce_calloc (32 + numChannels * numSamples * sizeof (float)); + + zeromem (tempInputBuffers, sizeof (tempInputBuffers)); + zeromem (tempOutputBuffers, sizeof (tempOutputBuffers)); + + int count = 0; + int i; + for (i = 0; i < numInputChans; ++i) + tempInputBuffers[i] = audioBuffer + count++ * numSamples; + + for (i = 0; i < numOutputChans; ++i) + tempOutputBuffers[i] = audioBuffer + count++ * numSamples; + } + + // returns the number of actual available channels + void fillInChannelInfo (bool input) + { + int chanNum = 0, activeChans = 0; + UInt32 size; + + if (OK (AudioDeviceGetPropertyInfo (deviceID, 0, input, kAudioDevicePropertyStreamConfiguration, &size, 0))) + { + AudioBufferList* const bufList = (AudioBufferList*) juce_calloc (size); + + if (OK (AudioDeviceGetProperty (deviceID, 0, input, kAudioDevicePropertyStreamConfiguration, &size, bufList))) + { + const int numStreams = bufList->mNumberBuffers; + + for (int i = 0; i < numStreams; ++i) + { + const AudioBuffer& b = bufList->mBuffers[i]; + + for (unsigned int j = 0; j < b.mNumberChannels; ++j) + { + if (input) + { + if (activeInputChans[chanNum]) + { + inputChannelInfo [activeChans].sourceChannelNum = chanNum; + inputChannelInfo [activeChans].streamNum = i; + inputChannelInfo [activeChans].dataOffsetSamples = j; + inputChannelInfo [activeChans].dataStrideSamples = b.mNumberChannels; + ++activeChans; + numInputChannelInfos = activeChans; + } + + inChanNames.add (T("input ") + String (chanNum + 1)); + } + else + { + if (activeOutputChans[chanNum]) + { + outputChannelInfo [activeChans].sourceChannelNum = chanNum; + outputChannelInfo [activeChans].streamNum = i; + outputChannelInfo [activeChans].dataOffsetSamples = j; + outputChannelInfo [activeChans].dataStrideSamples = b.mNumberChannels; + ++activeChans; + numOutputChannelInfos = activeChans; + } + + outChanNames.add (T("output ") + String (chanNum + 1)); + } + + ++chanNum; + } + } + } + + juce_free (bufList); + } + } + + void updateDetailsFromDevice() + { + stopTimer(); + + if (deviceID == 0) + return; + + const ScopedLock sl (callbackLock); + + Float64 sr; + UInt32 size = sizeof (Float64); + if (OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyNominalSampleRate, &size, &sr))) + sampleRate = sr; + + UInt32 framesPerBuf; + size = sizeof (framesPerBuf); + + if (OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyBufferFrameSize, &size, &framesPerBuf))) + { + bufferSize = framesPerBuf; + + if (bufferSize > 0) + setTempBufferSize (numInputChans + numOutputChans, bufferSize); + } + + bufferSizes.clear(); + + if (OK (AudioDeviceGetPropertyInfo (deviceID, 0, false, kAudioDevicePropertyBufferFrameSizeRange, &size, 0))) + { + AudioValueRange* ranges = (AudioValueRange*) juce_calloc (size); + + if (OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyBufferFrameSizeRange, &size, ranges))) + { + bufferSizes.add ((int) ranges[0].mMinimum); + + for (int i = 32; i < 8192; i += 32) + { + for (int j = size / sizeof (AudioValueRange); --j >= 0;) + { + if (i >= ranges[j].mMinimum && i <= ranges[j].mMaximum) + { + bufferSizes.addIfNotAlreadyThere (i); + break; + } + } + } + + if (bufferSize > 0) + bufferSizes.addIfNotAlreadyThere (bufferSize); + } + + juce_free (ranges); + } + + if (bufferSizes.size() == 0 && bufferSize > 0) + bufferSizes.add (bufferSize); + + sampleRates.clear(); + const double possibleRates[] = { 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0 }; + String rates; + + if (OK (AudioDeviceGetPropertyInfo (deviceID, 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size, 0))) + { + AudioValueRange* ranges = (AudioValueRange*) juce_calloc (size); + + if (OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size, ranges))) + { + for (int i = 0; i < numElementsInArray (possibleRates); ++i) + { + bool ok = false; + + for (int j = size / sizeof (AudioValueRange); --j >= 0;) + if (possibleRates[i] >= ranges[j].mMinimum - 2 && possibleRates[i] <= ranges[j].mMaximum + 2) + ok = true; + + if (ok) + { + sampleRates.add (possibleRates[i]); + rates << possibleRates[i] << T(" "); + } + } + } + + juce_free (ranges); + } + + if (sampleRates.size() == 0 && sampleRate > 0) + { + sampleRates.add (sampleRate); + rates << sampleRate; + } + + log (T("sr: ") + rates); + + inputLatency = 0; + outputLatency = 0; + UInt32 lat; + size = sizeof (UInt32); + if (AudioDeviceGetProperty (deviceID, 0, true, kAudioDevicePropertyLatency, &size, &lat) == noErr) + inputLatency = (int) lat; + + if (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyLatency, &size, &lat) == noErr) + outputLatency = (int) lat; + + log (T("lat: ") + String (inputLatency) + T(" ") + String (outputLatency)); + + inChanNames.clear(); + outChanNames.clear(); + + zeromem (inputChannelInfo, sizeof (inputChannelInfo)); + zeromem (outputChannelInfo, sizeof (outputChannelInfo)); + + fillInChannelInfo (true); + fillInChannelInfo (false); + } + + //============================================================================== + const StringArray getSources (bool input) + { + StringArray s; + int num = 0; + OSType* types = getAllDataSourcesForDevice (deviceID, input, num); + + if (types != 0) + { + for (int i = 0; i < num; ++i) + { + AudioValueTranslation avt; + char buffer[256]; + + avt.mInputData = (void*) &(types[i]); + avt.mInputDataSize = sizeof (UInt32); + avt.mOutputData = buffer; + avt.mOutputDataSize = 256; + + UInt32 transSize = sizeof (avt); + if (OK (AudioDeviceGetProperty (deviceID, 0, input, kAudioDevicePropertyDataSourceNameForID, &transSize, &avt))) + { + DBG (buffer); + s.add (buffer); + } + } + + juce_free (types); + } + + return s; + } + + int getCurrentSourceIndex (bool input) const + { + OSType currentSourceID = 0; + UInt32 size = 0; + int result = -1; + + if (deviceID != 0 + && OK (AudioDeviceGetPropertyInfo (deviceID, 0, input, kAudioDevicePropertyDataSource, &size, 0))) + { + if (OK (AudioDeviceGetProperty (deviceID, 0, input, kAudioDevicePropertyDataSource, &size, ¤tSourceID))) + { + int num = 0; + OSType* const types = getAllDataSourcesForDevice (deviceID, input, num); + + if (types != 0) + { + for (int i = 0; i < num; ++i) + { + if (types[num] == currentSourceID) + { + result = i; + break; + } + } + + juce_free (types); + } + } + } + + return result; + } + + void setCurrentSourceIndex (int index, bool input) + { + if (deviceID != 0) + { + int num = 0; + OSType* types = getAllDataSourcesForDevice (deviceID, input, num); + + if (types != 0) + { + if (((unsigned int) index) < num) + { + OSType typeId = types[index]; + AudioDeviceSetProperty (deviceID, 0, 0, input, kAudioDevicePropertyDataSource, sizeof (typeId), &typeId); + } + + juce_free (types); + } + } + } + + //============================================================================== + const String reopen (const BitArray& inputChannels, + const BitArray& outputChannels, + double newSampleRate, + int bufferSizeSamples) + { + error = String::empty; + log ("CoreAudio reopen"); + callbacksAllowed = false; + stopTimer(); + + stop (false); + + activeInputChans = inputChannels; + activeOutputChans = outputChannels; + + activeInputChans.setRange (inChanNames.size(), + activeInputChans.getHighestBit() + 1 - inChanNames.size(), + false); + + activeOutputChans.setRange (outChanNames.size(), + activeOutputChans.getHighestBit() + 1 - outChanNames.size(), + false); + + numInputChans = activeInputChans.countNumberOfSetBits(); + numOutputChans = activeOutputChans.countNumberOfSetBits(); + + // set sample rate + Float64 sr = newSampleRate; + UInt32 size = sizeof (sr); + OK (AudioDeviceSetProperty (deviceID, 0, 0, false, kAudioDevicePropertyNominalSampleRate, size, &sr)); + OK (AudioDeviceSetProperty (deviceID, 0, 0, true, kAudioDevicePropertyNominalSampleRate, size, &sr)); + + // change buffer size + UInt32 framesPerBuf = bufferSizeSamples; + size = sizeof (framesPerBuf); + + OK (AudioDeviceSetProperty (deviceID, 0, 0, false, kAudioDevicePropertyBufferFrameSize, size, &framesPerBuf)); + OK (AudioDeviceSetProperty (deviceID, 0, 0, true, kAudioDevicePropertyBufferFrameSize, size, &framesPerBuf)); + + // wait for the changes to happen (on some devices) + int i = 30; + while (--i >= 0) + { + updateDetailsFromDevice(); + + if (sampleRate == newSampleRate && bufferSizeSamples == bufferSize) + break; + + Thread::sleep (100); + } + + if (i < 0) + error = "Couldn't change sample rate/buffer size"; + + if (sampleRates.size() == 0) + error = "Device has no available sample-rates"; + + if (bufferSizes.size() == 0) + error = "Device has no available buffer-sizes"; + + if (inputDevice != 0 && error.isEmpty()) + error = inputDevice->reopen (inputChannels, + outputChannels, + newSampleRate, + bufferSizeSamples); + + callbacksAllowed = true; + + return error; + } + + bool start (AudioIODeviceCallback* cb) + { + if (! started) + { + callback = 0; + + if (deviceID != 0) + { + if (OK (AudioDeviceAddIOProc (deviceID, audioIOProc, (void*) this))) + { + if (OK (AudioDeviceStart (deviceID, audioIOProc))) + { + started = true; + } + else + { + OK (AudioDeviceRemoveIOProc (deviceID, audioIOProc)); + } + } + } + } + + if (started) + { + const ScopedLock sl (callbackLock); + callback = cb; + } + + if (inputDevice != 0) + return started && inputDevice->start (cb); + else + return started; + } + + void stop (bool leaveInterruptRunning) + { + callbackLock.enter(); + callback = 0; + callbackLock.exit(); + + if (started + && (deviceID != 0) + && ! leaveInterruptRunning) + { + OK (AudioDeviceStop (deviceID, audioIOProc)); + OK (AudioDeviceRemoveIOProc (deviceID, audioIOProc)); + started = false; + + callbackLock.enter(); + callbackLock.exit(); + + // wait until it's definately stopped calling back.. + for (int i = 40; --i >= 0;) + { + Thread::sleep (50); + + UInt32 running = 0; + UInt32 size = sizeof (running); + OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyDeviceIsRunning, &size, &running)); + if (running == 0) + break; + } + + callbackLock.enter(); + callbackLock.exit(); + } + + if (inputDevice != 0) + inputDevice->stop (leaveInterruptRunning); + } + + double getSampleRate() const + { + return sampleRate; + } + + int getBufferSize() const + { + return bufferSize; + } + + void audioCallback (const AudioBufferList* inInputData, + AudioBufferList* outOutputData) + { + int i; + const ScopedLock sl (callbackLock); + + if (callback != 0) + { + if (inputDevice == 0) + { + for (i = numInputChans; --i >= 0;) + { + const CallbackDetailsForChannel& info = inputChannelInfo[i]; + float* dest = tempInputBuffers [info.sourceChannelNum]; + const float* src = ((const float*) inInputData->mBuffers[info.streamNum].mData) + + info.dataOffsetSamples; + const int stride = info.dataStrideSamples; + + if (stride != 0) // if this is zero, info is invalid + { + for (int j = bufferSize; --j >= 0;) + { + *dest++ = *src; + src += stride; + } + } + } + } + + if (! isSlaveDevice) + { + if (inputDevice == 0) + { + callback->audioDeviceIOCallback ((const float**) tempInputBuffers, + numInputChans, + tempOutputBuffers, + numOutputChans, + bufferSize); + } + else + { + jassert (inputDevice->bufferSize == bufferSize); + + callback->audioDeviceIOCallback ((const float**) inputDevice->tempInputBuffers, + inputDevice->numInputChans, + tempOutputBuffers, + numOutputChans, + bufferSize); + } + + for (i = numOutputChans; --i >= 0;) + { + const CallbackDetailsForChannel& info = outputChannelInfo[i]; + const float* src = tempOutputBuffers [i]; + float* dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) + + info.dataOffsetSamples; + const int stride = info.dataStrideSamples; + + if (stride != 0) // if this is zero, info is invalid + { + for (int j = bufferSize; --j >= 0;) + { + *dest = *src++; + dest += stride; + } + } + } + } + } + else + { + for (i = jmin (numOutputChans, numOutputChannelInfos); --i >= 0;) + { + const CallbackDetailsForChannel& info = outputChannelInfo[i]; + float* dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) + + info.dataOffsetSamples; + const int stride = info.dataStrideSamples; + + if (stride != 0) // if this is zero, info is invalid + { + for (int j = bufferSize; --j >= 0;) + { + *dest = 0.0f; + dest += stride; + } + } + } + } + } + + // called by callbacks + void deviceDetailsChanged() + { + if (callbacksAllowed) + startTimer (100); + } + + void timerCallback() + { + stopTimer(); + log ("CoreAudio device changed callback"); + + const double oldSampleRate = sampleRate; + const int oldBufferSize = bufferSize; + updateDetailsFromDevice(); + + if (oldBufferSize != bufferSize || oldSampleRate != sampleRate) + { + callbacksAllowed = false; + stop (false); + updateDetailsFromDevice(); + callbacksAllowed = true; + } + } + + CoreAudioInternal* getRelatedDevice() const + { + UInt32 size = 0; + CoreAudioInternal* result = 0; + + if (deviceID != 0 + && AudioDeviceGetPropertyInfo (deviceID, 0, false, kAudioDevicePropertyRelatedDevices, &size, 0) == noErr + && size > 0) + { + AudioDeviceID* devs = (AudioDeviceID*) juce_calloc (size); + + if (OK (AudioDeviceGetProperty (deviceID, 0, false, kAudioDevicePropertyRelatedDevices, &size, devs))) + { + for (unsigned int i = 0; i < size / sizeof (AudioDeviceID); ++i) + { + if (devs[i] != deviceID && devs[i] != 0) + { + result = new CoreAudioInternal (devs[i]); + + if (result->error.isEmpty()) + { + const bool thisIsInput = inChanNames.size() > 0 && outChanNames.size() == 0; + const bool otherIsInput = result->inChanNames.size() > 0 && result->outChanNames.size() == 0; + + if (thisIsInput != otherIsInput + || (inChanNames.size() + outChanNames.size() == 0) + || (result->inChanNames.size() + result->outChanNames.size()) == 0) + break; + } + + deleteAndZero (result); + } + } + } + + juce_free (devs); + } + + return result; + } + + //============================================================================== + juce_UseDebuggingNewOperator + + String error; + int inputLatency, outputLatency; + BitArray activeInputChans, activeOutputChans; + StringArray inChanNames, outChanNames; + Array sampleRates; + Array bufferSizes; + AudioIODeviceCallback* callback; + + CoreAudioInternal* inputDevice; + bool isSlaveDevice; + +private: + CriticalSection callbackLock; + AudioDeviceID deviceID; + bool started; + double sampleRate; + int bufferSize; + float* audioBuffer; + int numInputChans, numOutputChans; + bool callbacksAllowed; + + struct CallbackDetailsForChannel + { + int sourceChannelNum; + int streamNum; + int dataOffsetSamples; + int dataStrideSamples; + }; + + int numInputChannelInfos, numOutputChannelInfos; + CallbackDetailsForChannel inputChannelInfo [maxNumChans]; + CallbackDetailsForChannel outputChannelInfo [maxNumChans]; + float* tempInputBuffers [maxNumChans]; + float* tempOutputBuffers [maxNumChans]; + + CoreAudioInternal (const CoreAudioInternal&); + const CoreAudioInternal& operator= (const CoreAudioInternal&); + + //============================================================================== + static OSStatus audioIOProc (AudioDeviceID inDevice, + const AudioTimeStamp* inNow, + const AudioBufferList* inInputData, + const AudioTimeStamp* inInputTime, + AudioBufferList* outOutputData, + const AudioTimeStamp* inOutputTime, + void* device) + { + ((CoreAudioInternal*) device)->audioCallback (inInputData, outOutputData); + return noErr; + } + + static OSStatus deviceListenerProc (AudioDeviceID inDevice, + UInt32 inLine, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + void* inClientData) + { + CoreAudioInternal* const intern = (CoreAudioInternal*) inClientData; + + switch (inPropertyID) + { + case kAudioDevicePropertyBufferSize: + case kAudioDevicePropertyBufferFrameSize: + case kAudioDevicePropertyNominalSampleRate: + case kAudioDevicePropertyStreamFormat: + case kAudioDevicePropertyDeviceIsAlive: + intern->deviceDetailsChanged(); + break; + + case kAudioDevicePropertyBufferSizeRange: + case kAudioDevicePropertyVolumeScalar: + case kAudioDevicePropertyMute: + case kAudioDevicePropertyPlayThru: + case kAudioDevicePropertyDataSource: + case kAudioDevicePropertyDeviceIsRunning: + break; + } + + return noErr; + } + + //============================================================================== + static OSType* getAllDataSourcesForDevice (AudioDeviceID deviceID, const bool input, int& num) + { + OSType* types = 0; + UInt32 size = 0; + num = 0; + + if (deviceID != 0 + && OK (AudioDeviceGetPropertyInfo (deviceID, 0, input, kAudioDevicePropertyDataSources, &size, 0))) + { + types = (OSType*) juce_calloc (size); + + if (OK (AudioDeviceGetProperty (deviceID, 0, input, kAudioDevicePropertyDataSources, &size, types))) + { + num = size / sizeof (OSType); + } + else + { + juce_free (types); + types = 0; + } + } + + return types; + } +}; + + +//============================================================================== +class CoreAudioIODevice : public AudioIODevice +{ +public: + CoreAudioIODevice (const String& deviceName, + AudioDeviceID inputDeviceId, + const int inputIndex_, + AudioDeviceID outputDeviceId, + const int outputIndex_) + : AudioIODevice (deviceName, "CoreAudio"), + inputIndex (inputIndex_), + outputIndex (outputIndex_), + isOpen_ (false), + isStarted (false) + { + internal = 0; + CoreAudioInternal* device = 0; + + if (outputDeviceId == 0 || outputDeviceId == inputDeviceId) + { + jassert (inputDeviceId != 0); + + device = new CoreAudioInternal (inputDeviceId); + lastError = device->error; + + if (lastError.isNotEmpty()) + deleteAndZero (device); + } + else + { + device = new CoreAudioInternal (outputDeviceId); + lastError = device->error; + + if (lastError.isNotEmpty()) + { + deleteAndZero (device); + } + else if (inputDeviceId != 0) + { + CoreAudioInternal* secondDevice = new CoreAudioInternal (inputDeviceId); + lastError = device->error; + + if (lastError.isNotEmpty()) + { + delete secondDevice; + } + else + { + device->inputDevice = secondDevice; + secondDevice->isSlaveDevice = true; + } + } + } + + internal = device; + + AudioHardwareAddPropertyListener (kAudioPropertyWildcardPropertyID, + hardwareListenerProc, internal); + } + + ~CoreAudioIODevice() + { + AudioHardwareRemovePropertyListener (kAudioPropertyWildcardPropertyID, + hardwareListenerProc); + + delete internal; + } + + const StringArray getOutputChannelNames() + { + return internal->outChanNames; + } + + const StringArray getInputChannelNames() + { + if (internal->inputDevice != 0) + return internal->inputDevice->inChanNames; + else + return internal->inChanNames; + } + + int getNumSampleRates() + { + return internal->sampleRates.size(); + } + + double getSampleRate (int index) + { + return internal->sampleRates [index]; + } + + int getNumBufferSizesAvailable() + { + return internal->bufferSizes.size(); + } + + int getBufferSizeSamples (int index) + { + return internal->bufferSizes [index]; + } + + int getDefaultBufferSize() + { + for (int i = 0; i < getNumBufferSizesAvailable(); ++i) + if (getBufferSizeSamples(i) >= 512) + return getBufferSizeSamples(i); + + return 512; + } + + const String open (const BitArray& inputChannels, + const BitArray& outputChannels, + double sampleRate, + int bufferSizeSamples) + { + isOpen_ = true; + + if (bufferSizeSamples <= 0) + bufferSizeSamples = getDefaultBufferSize(); + + internal->reopen (inputChannels, outputChannels, sampleRate, bufferSizeSamples); + lastError = internal->error; + return lastError; + } + + void close() + { + isOpen_ = false; + } + + bool isOpen() + { + return isOpen_; + } + + int getCurrentBufferSizeSamples() + { + return internal != 0 ? internal->getBufferSize() : 512; + } + + double getCurrentSampleRate() + { + return internal != 0 ? internal->getSampleRate() : 0; + } + + int getCurrentBitDepth() + { + return 32; // no way to find out, so just assume it's high.. + } + + const BitArray getActiveOutputChannels() const + { + return internal != 0 ? internal->activeOutputChans : BitArray(); + } + + const BitArray getActiveInputChannels() const + { + BitArray chans; + + if (internal != 0) + { + chans = internal->activeInputChans; + + if (internal->inputDevice != 0) + chans.orWith (internal->inputDevice->activeInputChans); + } + + return chans; + } + + int getOutputLatencyInSamples() + { + if (internal == 0) + return 0; + + // this seems like a good guess at getting the latency right - comparing + // this with a round-trip measurement, it gets it to within a few millisecs + // for the built-in mac soundcard + return internal->outputLatency + internal->getBufferSize() * 2; + } + + int getInputLatencyInSamples() + { + if (internal == 0) + return 0; + + return internal->inputLatency + internal->getBufferSize() * 2; + } + + void start (AudioIODeviceCallback* callback) + { + if (internal != 0 && ! isStarted) + { + if (callback != 0) + callback->audioDeviceAboutToStart (this); + + isStarted = true; + internal->start (callback); + } + } + + void stop() + { + if (isStarted && internal != 0) + { + AudioIODeviceCallback* const lastCallback = internal->callback; + + isStarted = false; + internal->stop (true); + + if (lastCallback != 0) + lastCallback->audioDeviceStopped(); + } + } + + bool isPlaying() + { + if (internal->callback == 0) + isStarted = false; + + return isStarted; + } + + const String getLastError() + { + return lastError; + } + + int inputIndex, outputIndex; + + juce_UseDebuggingNewOperator + +private: + CoreAudioInternal* internal; + bool isOpen_, isStarted; + String lastError; + + static OSStatus hardwareListenerProc (AudioHardwarePropertyID inPropertyID, void* inClientData) + { + CoreAudioInternal* const intern = (CoreAudioInternal*) inClientData; + + switch (inPropertyID) + { + case kAudioHardwarePropertyDevices: + intern->deviceDetailsChanged(); + break; + + case kAudioHardwarePropertyDefaultOutputDevice: + case kAudioHardwarePropertyDefaultInputDevice: + case kAudioHardwarePropertyDefaultSystemOutputDevice: + break; + } + + return noErr; + } + + CoreAudioIODevice (const CoreAudioIODevice&); + const CoreAudioIODevice& operator= (const CoreAudioIODevice&); +}; + +//============================================================================== +class CoreAudioIODeviceType : public AudioIODeviceType +{ +public: + //============================================================================== + CoreAudioIODeviceType() + : AudioIODeviceType (T("CoreAudio")), + hasScanned (false) + { + } + + ~CoreAudioIODeviceType() + { + } + + //============================================================================== + void scanForDevices() + { + hasScanned = true; + + inputDeviceNames.clear(); + outputDeviceNames.clear(); + inputIds.clear(); + outputIds.clear(); + + UInt32 size; + if (OK (AudioHardwareGetPropertyInfo (kAudioHardwarePropertyDevices, &size, 0))) + { + AudioDeviceID* const devs = (AudioDeviceID*) juce_calloc (size); + + if (OK (AudioHardwareGetProperty (kAudioHardwarePropertyDevices, &size, devs))) + { + static bool alreadyLogged = false; + const int num = size / sizeof (AudioDeviceID); + for (int i = 0; i < num; ++i) + { + char name[1024]; + size = sizeof (name); + if (OK (AudioDeviceGetProperty (devs[i], 0, false, kAudioDevicePropertyDeviceName, &size, name))) + { + const String nameString (String::fromUTF8 ((const uint8*) name, strlen (name))); + + if (! alreadyLogged) + log (T("CoreAudio device: ") + nameString); + + const int numIns = getNumChannels (devs[i], true); + const int numOuts = getNumChannels (devs[i], false); + + if (numIns > 0) + { + inputDeviceNames.add (nameString); + inputIds.add (devs[i]); + } + + if (numOuts > 0) + { + outputDeviceNames.add (nameString); + outputIds.add (devs[i]); + } + } + } + + alreadyLogged = true; + } + + juce_free (devs); + } + + inputDeviceNames.appendNumbersToDuplicates (false, true); + outputDeviceNames.appendNumbersToDuplicates (false, true); + } + + const StringArray getDeviceNames (const bool wantInputNames) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + if (wantInputNames) + return inputDeviceNames; + else + return outputDeviceNames; + } + + int getDefaultDeviceIndex (const bool forInput) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + AudioDeviceID deviceID; + UInt32 size = sizeof (deviceID); + + // if they're asking for any input channels at all, use the default input, so we + // get the built-in mic rather than the built-in output with no inputs.. + if (AudioHardwareGetProperty (forInput ? kAudioHardwarePropertyDefaultInputDevice + : kAudioHardwarePropertyDefaultOutputDevice, + &size, &deviceID) == noErr) + { + if (forInput) + { + for (int i = inputIds.size(); --i >= 0;) + if (inputIds[i] == deviceID) + return i; + } + else + { + for (int i = outputIds.size(); --i >= 0;) + if (outputIds[i] == deviceID) + return i; + } + } + + return 0; + } + + int getIndexOfDevice (AudioIODevice* device, const bool asInput) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + CoreAudioIODevice* const d = dynamic_cast (device); + if (d == 0) + return -1; + + return asInput ? d->inputIndex + : d->outputIndex; + } + + bool hasSeparateInputsAndOutputs() const { return true; } + + AudioIODevice* createDevice (const String& outputDeviceName, + const String& inputDeviceName) + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + const int inputIndex = inputDeviceNames.indexOf (inputDeviceName); + const int outputIndex = outputDeviceNames.indexOf (outputDeviceName); + + String deviceName (outputDeviceName); + if (deviceName.isEmpty()) + deviceName = inputDeviceName; + + if (index >= 0) + return new CoreAudioIODevice (deviceName, + inputIds [inputIndex], + inputIndex, + outputIds [outputIndex], + outputIndex); + + return 0; + } + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + StringArray inputDeviceNames, outputDeviceNames; + Array inputIds, outputIds; + + bool hasScanned; + + static int getNumChannels (AudioDeviceID deviceID, bool input) + { + int total = 0; + UInt32 size; + + if (OK (AudioDeviceGetPropertyInfo (deviceID, 0, input, kAudioDevicePropertyStreamConfiguration, &size, 0))) + { + AudioBufferList* const bufList = (AudioBufferList*) juce_calloc (size); + + if (OK (AudioDeviceGetProperty (deviceID, 0, input, kAudioDevicePropertyStreamConfiguration, &size, bufList))) + { + const int numStreams = bufList->mNumberBuffers; + + for (int i = 0; i < numStreams; ++i) + { + const AudioBuffer& b = bufList->mBuffers[i]; + total += b.mNumberChannels; + } + } + + juce_free (bufList); + } + + return total; + } + + CoreAudioIODeviceType (const CoreAudioIODeviceType&); + const CoreAudioIODeviceType& operator= (const CoreAudioIODeviceType&); +}; + +//============================================================================== +AudioIODeviceType* juce_createDefaultAudioIODeviceType() +{ + return new CoreAudioIODeviceType(); +} + +#undef log + +END_JUCE_NAMESPACE diff --git a/build/macosx/platform_specific_code/juce_mac_CoreMidi.cpp b/build/macosx/platform_specific_code/juce_mac_CoreMidi.cpp index bd6c5366e0..d585d8bc02 100644 --- a/build/macosx/platform_specific_code/juce_mac_CoreMidi.cpp +++ b/build/macosx/platform_specific_code/juce_mac_CoreMidi.cpp @@ -1,592 +1,592 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-7 by Raw Material Software ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the - GNU General Public License, as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later version. - - JUCE is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with JUCE; if not, visit www.gnu.org/licenses or write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - ------------------------------------------------------------------------------ - - If you'd like to release a closed-source product which uses JUCE, commercial - licenses are also available: visit www.rawmaterialsoftware.com/juce for - more information. - - ============================================================================== -*/ - -#include -#include "../../../src/juce_core/basics/juce_StandardHeader.h" - -BEGIN_JUCE_NAMESPACE - -#include "../../../src/juce_appframework/audio/devices/juce_MidiOutput.h" -#include "../../../src/juce_appframework/audio/devices/juce_MidiInput.h" -#include "../../../src/juce_appframework/application/juce_Application.h" -#include "../../../src/juce_core/basics/juce_Time.h" -#include "../../../src/juce_core/containers/juce_MemoryBlock.h" -#include "../../../src/juce_core/misc/juce_PlatformUtilities.h" -#include "../../../src/juce_core/threads/juce_ScopedLock.h" - -//============================================================================== -#undef log -#define log(a) Logger::writeToLog(a) - -static bool logAnyErrorsMidi (const OSStatus err, const int lineNum) -{ - if (err == noErr) - return true; - - log (T("CoreMidi error: ") + String (lineNum) + T(" - ") + 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) - { - result = PlatformUtilities::cfStringToJuceString (str); - CFRelease (str); - str = 0; - } - - MIDIEntityRef entity = 0; - MIDIEndpointGetEntity (endpoint, &entity); - - if (entity == 0) - return result; // probably virtual - - 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) - { - 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 + T(" ") + result).trimEnd(); - } - } - - 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 = CFDataGetLength (connections) / sizeof (MIDIUniqueID); - - if (numConnections > 0) - { - const SInt32* pid = reinterpret_cast (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 (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); -} - -//============================================================================== -const StringArray MidiOutput::getDevices() -{ - StringArray s; - - const ItemCount num = MIDIGetNumberOfDestinations(); - for (ItemCount i = 0; i < num; ++i) - { - MIDIEndpointRef dest = MIDIGetDestination (i); - - if (dest != 0) - { - String name (getConnectedEndpointName (dest)); - - if (name.isEmpty()) - name = ""; - - s.add (name); - } - else - { - s.add (""); - } - } - - return s; -} - -int MidiOutput::getDefaultDeviceIndex() -{ - return 0; -} - -static MIDIClientRef globalMidiClient; -static bool hasGlobalClientBeenCreated = false; - -static bool makeSureClientExists() -{ - if (! hasGlobalClientBeenCreated) - { - String name (T("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; -} - -struct MidiPortAndEndpoint -{ - MIDIPortRef port; - MIDIEndpointRef endPoint; -}; - -MidiOutput* MidiOutput::openDevice (int index) -{ - MidiOutput* mo = 0; - - if (((unsigned int) index) < (unsigned int) MIDIGetNumberOfDestinations()) - { - MIDIEndpointRef endPoint = MIDIGetDestination (index); - - CFStringRef pname; - if (OK (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname))) - { - log (T("CoreMidi - opening out: ") + PlatformUtilities::cfStringToJuceString (pname)); - - if (makeSureClientExists()) - { - MIDIPortRef port; - - if (OK (MIDIOutputPortCreate (globalMidiClient, pname, &port))) - { - MidiPortAndEndpoint* mpe = new MidiPortAndEndpoint(); - mpe->port = port; - mpe->endPoint = endPoint; - - mo = new MidiOutput(); - mo->internal = (void*)mpe; - } - } - - CFRelease (pname); - } - } - - return mo; -} - -MidiOutput::~MidiOutput() -{ - MidiPortAndEndpoint* const mpe = (MidiPortAndEndpoint*)internal; - MIDIPortDispose (mpe->port); - delete mpe; -} - -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) -{ - MidiPortAndEndpoint* const mpe = (MidiPortAndEndpoint*)internal; - - if (message.isSysEx()) - { - MIDIPacketList* const packets = (MIDIPacketList*) juce_malloc (32 + message.getRawDataSize()); - packets->numPackets = 1; - packets->packet[0].timeStamp = 0; - packets->packet[0].length = message.getRawDataSize(); - memcpy (packets->packet[0].data, message.getRawData(), message.getRawDataSize()); - - MIDISend (mpe->port, mpe->endPoint, packets); - juce_free (packets); - } - else - { - MIDIPacketList packets; - packets.numPackets = 1; - packets.packet[0].timeStamp = 0; - packets.packet[0].length = message.getRawDataSize(); - *(int*) (packets.packet[0].data) = *(const int*) message.getRawData(); - - MIDISend (mpe->port, mpe->endPoint, &packets); - } -} - -//============================================================================== -const StringArray MidiInput::getDevices() -{ - StringArray s; - - const ItemCount num = MIDIGetNumberOfSources(); - for (ItemCount i = 0; i < num; ++i) - { - MIDIEndpointRef source = MIDIGetSource (i); - - if (source != 0) - { - String name (getConnectedEndpointName (source)); - - if (name.isEmpty()) - name = ""; - - s.add (name); - } - else - { - s.add (""); - } - } - - return s; -} - -int MidiInput::getDefaultDeviceIndex() -{ - return 0; -} - -//============================================================================== -struct MidiPortAndCallback -{ - MidiInput* input; - MIDIPortRef port; - MIDIEndpointRef endPoint; - MidiInputCallback* callback; - MemoryBlock pendingData; - int pendingBytes; - double pendingDataTime; - bool active; -}; - -static CriticalSection callbackLock; -static VoidArray activeCallbacks; - -static void processSysex (MidiPortAndCallback* const mpe, const uint8*& d, int& size, const double time) -{ - if (*d == 0xf0) - { - mpe->pendingBytes = 0; - mpe->pendingDataTime = time; - } - - mpe->pendingData.ensureSize (mpe->pendingBytes + size, false); - uint8* totalMessage = (uint8*) mpe->pendingData.getData(); - - uint8* dest = totalMessage + mpe->pendingBytes; - - while (size > 0) - { - if (mpe->pendingBytes > 0 && *d >= 0x80) - { - if (*d >= 0xfa || *d == 0xf8) - { - mpe->callback->handleIncomingMidiMessage (mpe->input, MidiMessage (*d, time)); - ++d; - --size; - } - else - { - if (*d == 0xf7) - { - *dest++ = *d++; - mpe->pendingBytes++; - --size; - } - - break; - } - } - else - { - *dest++ = *d++; - mpe->pendingBytes++; - --size; - } - } - - if (totalMessage [mpe->pendingBytes - 1] == 0xf7) - { - mpe->callback->handleIncomingMidiMessage (mpe->input, MidiMessage (totalMessage, - mpe->pendingBytes, - mpe->pendingDataTime)); - mpe->pendingBytes = 0; - } - else - { - mpe->callback->handlePartialSysexMessage (mpe->input, - totalMessage, - mpe->pendingBytes, - mpe->pendingDataTime); - } -} - -static void midiInputProc (const MIDIPacketList* pktlist, - void* readProcRefCon, - void* srcConnRefCon) -{ - double time = Time::getMillisecondCounterHiRes() * 0.001; - const double originalTime = time; - - MidiPortAndCallback* const mpe = (MidiPortAndCallback*) readProcRefCon; - const ScopedLock sl (callbackLock); - - if (activeCallbacks.contains (mpe) && mpe->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 (mpe->pendingBytes > 0 || d[0] == 0xf0) - { - processSysex (mpe, d, size, time); - } - else - { - int used = 0; - const MidiMessage m (d, size, used, 0, time); - - if (used <= 0) - { - jassertfalse // malformed midi message - break; - } - else - { - mpe->callback->handleIncomingMidiMessage (mpe->input, m); - } - - size -= used; - d += used; - } - } - - packet = MIDIPacketNext (packet); - } - } -} - -MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) -{ - MidiInput* mi = 0; - - if (((unsigned int) index) < (unsigned int) MIDIGetNumberOfSources()) - { - MIDIEndpointRef endPoint = MIDIGetSource (index); - - if (endPoint != 0) - { - CFStringRef pname; - - if (OK (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname))) - { - log (T("CoreMidi - opening inp: ") + PlatformUtilities::cfStringToJuceString (pname)); - - if (makeSureClientExists()) - { - MIDIPortRef port; - - MidiPortAndCallback* const mpe = new MidiPortAndCallback(); - mpe->active = false; - - if (OK (MIDIInputPortCreate (globalMidiClient, pname, midiInputProc, mpe, &port))) - { - if (OK (MIDIPortConnectSource (port, endPoint, 0))) - { - mpe->port = port; - mpe->endPoint = endPoint; - mpe->callback = callback; - mpe->pendingBytes = 0; - mpe->pendingData.ensureSize (128); - - mi = new MidiInput (getDevices() [index]); - mpe->input = mi; - mi->internal = (void*) mpe; - - const ScopedLock sl (callbackLock); - activeCallbacks.add (mpe); - } - else - { - OK (MIDIPortDispose (port)); - delete mpe; - } - } - else - { - delete mpe; - } - } - } - - CFRelease (pname); - } - } - - return mi; -} - -MidiInput::MidiInput (const String& name_) - : name (name_) -{ -} - -MidiInput::~MidiInput() -{ - MidiPortAndCallback* const mpe = (MidiPortAndCallback*) internal; - mpe->active = false; - - callbackLock.enter(); - activeCallbacks.removeValue (mpe); - callbackLock.exit(); - - OK (MIDIPortDisconnectSource (mpe->port, mpe->endPoint)); - OK (MIDIPortDispose (mpe->port)); - delete mpe; -} - -void MidiInput::start() -{ - MidiPortAndCallback* const mpe = (MidiPortAndCallback*) internal; - const ScopedLock sl (callbackLock); - mpe->active = true; -} - -void MidiInput::stop() -{ - MidiPortAndCallback* const mpe = (MidiPortAndCallback*) internal; - const ScopedLock sl (callbackLock); - mpe->active = false; -} - -#undef log - -END_JUCE_NAMESPACE +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include +#include "../../../src/juce_core/basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "../../../src/juce_appframework/audio/devices/juce_MidiOutput.h" +#include "../../../src/juce_appframework/audio/devices/juce_MidiInput.h" +#include "../../../src/juce_appframework/application/juce_Application.h" +#include "../../../src/juce_core/basics/juce_Time.h" +#include "../../../src/juce_core/containers/juce_MemoryBlock.h" +#include "../../../src/juce_core/misc/juce_PlatformUtilities.h" +#include "../../../src/juce_core/threads/juce_ScopedLock.h" + +//============================================================================== +#undef log +#define log(a) Logger::writeToLog(a) + +static bool logAnyErrorsMidi (const OSStatus err, const int lineNum) +{ + if (err == noErr) + return true; + + log (T("CoreMidi error: ") + String (lineNum) + T(" - ") + 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) + { + result = PlatformUtilities::cfStringToJuceString (str); + CFRelease (str); + str = 0; + } + + MIDIEntityRef entity = 0; + MIDIEndpointGetEntity (endpoint, &entity); + + if (entity == 0) + return result; // probably virtual + + 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) + { + 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 + T(" ") + result).trimEnd(); + } + } + + 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 = CFDataGetLength (connections) / sizeof (MIDIUniqueID); + + if (numConnections > 0) + { + const SInt32* pid = reinterpret_cast (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 (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); +} + +//============================================================================== +const StringArray MidiOutput::getDevices() +{ + StringArray s; + + const ItemCount num = MIDIGetNumberOfDestinations(); + for (ItemCount i = 0; i < num; ++i) + { + MIDIEndpointRef dest = MIDIGetDestination (i); + + if (dest != 0) + { + String name (getConnectedEndpointName (dest)); + + if (name.isEmpty()) + name = ""; + + s.add (name); + } + else + { + s.add (""); + } + } + + return s; +} + +int MidiOutput::getDefaultDeviceIndex() +{ + return 0; +} + +static MIDIClientRef globalMidiClient; +static bool hasGlobalClientBeenCreated = false; + +static bool makeSureClientExists() +{ + if (! hasGlobalClientBeenCreated) + { + String name (T("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; +} + +struct MidiPortAndEndpoint +{ + MIDIPortRef port; + MIDIEndpointRef endPoint; +}; + +MidiOutput* MidiOutput::openDevice (int index) +{ + MidiOutput* mo = 0; + + if (((unsigned int) index) < (unsigned int) MIDIGetNumberOfDestinations()) + { + MIDIEndpointRef endPoint = MIDIGetDestination (index); + + CFStringRef pname; + if (OK (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname))) + { + log (T("CoreMidi - opening out: ") + PlatformUtilities::cfStringToJuceString (pname)); + + if (makeSureClientExists()) + { + MIDIPortRef port; + + if (OK (MIDIOutputPortCreate (globalMidiClient, pname, &port))) + { + MidiPortAndEndpoint* mpe = new MidiPortAndEndpoint(); + mpe->port = port; + mpe->endPoint = endPoint; + + mo = new MidiOutput(); + mo->internal = (void*)mpe; + } + } + + CFRelease (pname); + } + } + + return mo; +} + +MidiOutput::~MidiOutput() +{ + MidiPortAndEndpoint* const mpe = (MidiPortAndEndpoint*)internal; + MIDIPortDispose (mpe->port); + delete mpe; +} + +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) +{ + MidiPortAndEndpoint* const mpe = (MidiPortAndEndpoint*)internal; + + if (message.isSysEx()) + { + MIDIPacketList* const packets = (MIDIPacketList*) juce_malloc (32 + message.getRawDataSize()); + packets->numPackets = 1; + packets->packet[0].timeStamp = 0; + packets->packet[0].length = message.getRawDataSize(); + memcpy (packets->packet[0].data, message.getRawData(), message.getRawDataSize()); + + MIDISend (mpe->port, mpe->endPoint, packets); + juce_free (packets); + } + else + { + MIDIPacketList packets; + packets.numPackets = 1; + packets.packet[0].timeStamp = 0; + packets.packet[0].length = message.getRawDataSize(); + *(int*) (packets.packet[0].data) = *(const int*) message.getRawData(); + + MIDISend (mpe->port, mpe->endPoint, &packets); + } +} + +//============================================================================== +const StringArray MidiInput::getDevices() +{ + StringArray s; + + const ItemCount num = MIDIGetNumberOfSources(); + for (ItemCount i = 0; i < num; ++i) + { + MIDIEndpointRef source = MIDIGetSource (i); + + if (source != 0) + { + String name (getConnectedEndpointName (source)); + + if (name.isEmpty()) + name = ""; + + s.add (name); + } + else + { + s.add (""); + } + } + + return s; +} + +int MidiInput::getDefaultDeviceIndex() +{ + return 0; +} + +//============================================================================== +struct MidiPortAndCallback +{ + MidiInput* input; + MIDIPortRef port; + MIDIEndpointRef endPoint; + MidiInputCallback* callback; + MemoryBlock pendingData; + int pendingBytes; + double pendingDataTime; + bool active; +}; + +static CriticalSection callbackLock; +static VoidArray activeCallbacks; + +static void processSysex (MidiPortAndCallback* const mpe, const uint8*& d, int& size, const double time) +{ + if (*d == 0xf0) + { + mpe->pendingBytes = 0; + mpe->pendingDataTime = time; + } + + mpe->pendingData.ensureSize (mpe->pendingBytes + size, false); + uint8* totalMessage = (uint8*) mpe->pendingData.getData(); + + uint8* dest = totalMessage + mpe->pendingBytes; + + while (size > 0) + { + if (mpe->pendingBytes > 0 && *d >= 0x80) + { + if (*d >= 0xfa || *d == 0xf8) + { + mpe->callback->handleIncomingMidiMessage (mpe->input, MidiMessage (*d, time)); + ++d; + --size; + } + else + { + if (*d == 0xf7) + { + *dest++ = *d++; + mpe->pendingBytes++; + --size; + } + + break; + } + } + else + { + *dest++ = *d++; + mpe->pendingBytes++; + --size; + } + } + + if (totalMessage [mpe->pendingBytes - 1] == 0xf7) + { + mpe->callback->handleIncomingMidiMessage (mpe->input, MidiMessage (totalMessage, + mpe->pendingBytes, + mpe->pendingDataTime)); + mpe->pendingBytes = 0; + } + else + { + mpe->callback->handlePartialSysexMessage (mpe->input, + totalMessage, + mpe->pendingBytes, + mpe->pendingDataTime); + } +} + +static void midiInputProc (const MIDIPacketList* pktlist, + void* readProcRefCon, + void* srcConnRefCon) +{ + double time = Time::getMillisecondCounterHiRes() * 0.001; + const double originalTime = time; + + MidiPortAndCallback* const mpe = (MidiPortAndCallback*) readProcRefCon; + const ScopedLock sl (callbackLock); + + if (activeCallbacks.contains (mpe) && mpe->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 (mpe->pendingBytes > 0 || d[0] == 0xf0) + { + processSysex (mpe, d, size, time); + } + else + { + int used = 0; + const MidiMessage m (d, size, used, 0, time); + + if (used <= 0) + { + jassertfalse // malformed midi message + break; + } + else + { + mpe->callback->handleIncomingMidiMessage (mpe->input, m); + } + + size -= used; + d += used; + } + } + + packet = MIDIPacketNext (packet); + } + } +} + +MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) +{ + MidiInput* mi = 0; + + if (((unsigned int) index) < (unsigned int) MIDIGetNumberOfSources()) + { + MIDIEndpointRef endPoint = MIDIGetSource (index); + + if (endPoint != 0) + { + CFStringRef pname; + + if (OK (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname))) + { + log (T("CoreMidi - opening inp: ") + PlatformUtilities::cfStringToJuceString (pname)); + + if (makeSureClientExists()) + { + MIDIPortRef port; + + MidiPortAndCallback* const mpe = new MidiPortAndCallback(); + mpe->active = false; + + if (OK (MIDIInputPortCreate (globalMidiClient, pname, midiInputProc, mpe, &port))) + { + if (OK (MIDIPortConnectSource (port, endPoint, 0))) + { + mpe->port = port; + mpe->endPoint = endPoint; + mpe->callback = callback; + mpe->pendingBytes = 0; + mpe->pendingData.ensureSize (128); + + mi = new MidiInput (getDevices() [index]); + mpe->input = mi; + mi->internal = (void*) mpe; + + const ScopedLock sl (callbackLock); + activeCallbacks.add (mpe); + } + else + { + OK (MIDIPortDispose (port)); + delete mpe; + } + } + else + { + delete mpe; + } + } + } + + CFRelease (pname); + } + } + + return mi; +} + +MidiInput::MidiInput (const String& name_) + : name (name_) +{ +} + +MidiInput::~MidiInput() +{ + MidiPortAndCallback* const mpe = (MidiPortAndCallback*) internal; + mpe->active = false; + + callbackLock.enter(); + activeCallbacks.removeValue (mpe); + callbackLock.exit(); + + OK (MIDIPortDisconnectSource (mpe->port, mpe->endPoint)); + OK (MIDIPortDispose (mpe->port)); + delete mpe; +} + +void MidiInput::start() +{ + MidiPortAndCallback* const mpe = (MidiPortAndCallback*) internal; + const ScopedLock sl (callbackLock); + mpe->active = true; +} + +void MidiInput::stop() +{ + MidiPortAndCallback* const mpe = (MidiPortAndCallback*) internal; + const ScopedLock sl (callbackLock); + mpe->active = false; +} + +#undef log + +END_JUCE_NAMESPACE diff --git a/build/macosx/platform_specific_code/juce_mac_FileChooser.mm b/build/macosx/platform_specific_code/juce_mac_FileChooser.mm index f0d021699a..f11acd18ca 100644 --- a/build/macosx/platform_specific_code/juce_mac_FileChooser.mm +++ b/build/macosx/platform_specific_code/juce_mac_FileChooser.mm @@ -36,15 +36,6 @@ BEGIN_JUCE_NAMESPACE #include "../../../src/juce_appframework/gui/components/filebrowser/juce_FileChooser.h" END_JUCE_NAMESPACE -static const JUCE_NAMESPACE::String nsStringToJuce (NSString* s) -{ - return JUCE_NAMESPACE::String::fromUTF8 ((JUCE_NAMESPACE::uint8*) [s UTF8String]); -} - -static NSString* juceStringToNS (const JUCE_NAMESPACE::String& s) -{ - return [NSString stringWithUTF8String: (const char*) s.toUTF8()]; -} //============================================================================== @interface JuceFileChooserDelegate : NSObject @@ -84,8 +75,7 @@ static NSString* juceStringToNS (const JUCE_NAMESPACE::String& s) return true; } - return JUCE_NAMESPACE::File (JUCE_NAMESPACE::String::fromUTF8 ((const uint8*) filenameUTF8)) - .isDirectory(); + return JUCE_NAMESPACE::File (nsStringToJuce (filename)).isDirectory(); } @end diff --git a/build/macosx/platform_specific_code/juce_mac_Messaging.mm b/build/macosx/platform_specific_code/juce_mac_Messaging.mm index 8f822f7e6f..18c5f2eb7d 100644 --- a/build/macosx/platform_specific_code/juce_mac_Messaging.mm +++ b/build/macosx/platform_specific_code/juce_mac_Messaging.mm @@ -1,344 +1,503 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-7 by Raw Material Software ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the - GNU General Public License, as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later version. - - JUCE is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with JUCE; if not, visit www.gnu.org/licenses or write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - ------------------------------------------------------------------------------ - - If you'd like to release a closed-source product which uses JUCE, commercial - licenses are also available: visit www.rawmaterialsoftware.com/juce for - more information. - - ============================================================================== -*/ - -#include "../../../src/juce_core/basics/juce_StandardHeader.h" -#include - -BEGIN_JUCE_NAMESPACE - -#include "../../../src/juce_appframework/events/juce_MessageManager.h" -#include "../../../src/juce_appframework/application/juce_Application.h" -#include "../../../src/juce_appframework/gui/components/juce_Desktop.h" -#include "../../../src/juce_core/text/juce_StringArray.h" -#include "../../../src/juce_core/threads/juce_Thread.h" -#include "../../../src/juce_core/misc/juce_PlatformUtilities.h" - -#undef Point - -static int kJUCEClass = FOUR_CHAR_CODE ('JUCE'); -const int kJUCEKind = 1; -const int kCallbackKind = 2; - -extern void juce_HandleProcessFocusChange(); -extern void juce_maximiseAllMinimisedWindows(); -extern void juce_InvokeMainMenuCommand (const HICommand& command); -extern void juce_MainMenuAboutToBeUsed(); - -static pascal OSStatus EventHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData) -{ - void* event = 0; - GetEventParameter (theEvent, 'mess', typeVoidPtr, 0, sizeof (void*), 0, &event); - - if (event != 0) - MessageManager::getInstance()->deliverMessage (event); - - return noErr; -} - -struct CallbackMessagePayload -{ - MessageCallbackFunction* function; - void* parameter; - void* volatile result; - bool volatile hasBeenExecuted; -}; - -static pascal OSStatus CallbackHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData) -{ - CallbackMessagePayload* pl = 0; - GetEventParameter (theEvent, 'mess', typeVoidPtr, 0, sizeof(pl), 0, &pl); - - if (pl != 0) - { - pl->result = (*pl->function) (pl->parameter); - pl->hasBeenExecuted = true; - } - - return noErr; -} - -static pascal OSStatus MouseClickHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData) -{ - ::Point where; - GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, 0, sizeof(::Point), 0, &where); - WindowRef window; - if (FindWindow (where, &window) == inMenuBar) - { - // turn off the wait cursor before going in here.. - const int oldTimeBeforeWaitCursor = MessageManager::getInstance()->getTimeBeforeShowingWaitCursor(); - MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (0); - - if (Component::getCurrentlyModalComponent() != 0) - Component::getCurrentlyModalComponent()->inputAttemptWhenModal(); - - juce_MainMenuAboutToBeUsed(); - MenuSelect (where); - HiliteMenu (0); - - MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (oldTimeBeforeWaitCursor); - return noErr; - } - - return eventNotHandledErr; -} - -static pascal OSErr QuitAppleEventHandler (const AppleEvent *appleEvt, AppleEvent* reply, long refcon) -{ - if (JUCEApplication::getInstance() != 0) - JUCEApplication::getInstance()->systemRequestedQuit(); - - return noErr; -} - -static pascal OSErr OpenDocEventHandler (const AppleEvent *appleEvt, AppleEvent* reply, long refcon) -{ - AEDescList docs; - StringArray files; - - if (AEGetParamDesc (appleEvt, keyDirectObject, typeAEList, &docs) == noErr) - { - long num; - if (AECountItems (&docs, &num) == noErr) - { - for (int i = 1; i <= num; ++i) - { - FSRef file; - AEKeyword keyword; - DescType type; - Size size; - - if (AEGetNthPtr (&docs, i, typeFSRef, &keyword, &type, - &file, sizeof (file), &size) == noErr) - { - const String path (PlatformUtilities::makePathFromFSRef (&file)); - - if (path.isNotEmpty()) - files.add (path.quoted()); - } - } - - if (files.size() > 0 - && JUCEApplication::getInstance() != 0) - { - JUCE_TRY - { - JUCEApplication::getInstance() - ->anotherInstanceStarted (files.joinIntoString (T(" "))); - } - JUCE_CATCH_ALL - } - } - - AEDisposeDesc (&docs); - }; - - return noErr; -} - -static pascal OSStatus AppEventHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData) -{ - const UInt32 eventClass = GetEventClass (theEvent); - - if (eventClass == kEventClassCommand) - { - HICommand command; - - if (GetEventParameter (theEvent, kEventParamHICommand, typeHICommand, 0, sizeof (command), 0, &command) == noErr - || GetEventParameter (theEvent, kEventParamDirectObject, typeHICommand, 0, sizeof (command), 0, &command) == noErr) - { - if (command.commandID == kHICommandQuit) - { - if (JUCEApplication::getInstance() != 0) - JUCEApplication::getInstance()->systemRequestedQuit(); - - return noErr; - } - else if (command.commandID == kHICommandMaximizeAll - || command.commandID == kHICommandMaximizeWindow - || command.commandID == kHICommandBringAllToFront) - { - juce_maximiseAllMinimisedWindows(); - return noErr; - } - else - { - juce_InvokeMainMenuCommand (command); - } - } - } - else if (eventClass == kEventClassApplication) - { - if (GetEventKind (theEvent) == kEventAppFrontSwitched) - { - juce_HandleProcessFocusChange(); - } - else if (GetEventKind (theEvent) == kEventAppShown) - { - // this seems to blank the windows, so we need to do a repaint.. - for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;) - { - Component* const c = Desktop::getInstance().getComponent (i); - - if (c != 0) - c->repaint(); - } - } - } - - return eventNotHandledErr; -} - -static EventQueueRef mainQueue; -static EventHandlerRef juceEventHandler = 0; -static EventHandlerRef callbackEventHandler = 0; - -//============================================================================== -void MessageManager::doPlatformSpecificInitialisation() -{ - static bool initialised = false; - - if (! initialised) - { - initialised = true; - -#if MACOS_10_3_OR_EARLIER - // work-around for a bug in MacOS 10.2.. - ProcessSerialNumber junkPSN; - (void) GetCurrentProcess (&junkPSN); -#endif - - mainQueue = GetMainEventQueue(); - - // if we're linking a Juce app to one or more dynamic libraries, we'll need different values - // for this so each module doesn't interfere with the others. - UnsignedWide t; - Microseconds (&t); - kJUCEClass ^= t.lo; - } - - const EventTypeSpec type1 = { kJUCEClass, kJUCEKind }; - InstallApplicationEventHandler (NewEventHandlerUPP (EventHandlerProc), 1, &type1, 0, &juceEventHandler); - - const EventTypeSpec type2 = { kJUCEClass, kCallbackKind }; - InstallApplicationEventHandler (NewEventHandlerUPP (CallbackHandlerProc), 1, &type2, 0, &callbackEventHandler); - - // only do this stuff if we're running as an application rather than a library.. - if (JUCEApplication::getInstance() != 0) - { - const EventTypeSpec type3 = { kEventClassMouse, kEventMouseDown }; - InstallApplicationEventHandler (NewEventHandlerUPP (MouseClickHandlerProc), 1, &type3, 0, 0); - - const EventTypeSpec type4[] = { { kEventClassApplication, kEventAppShown }, - { kEventClassApplication, kEventAppFrontSwitched }, - { kEventClassCommand, kEventProcessCommand } }; - - InstallApplicationEventHandler (NewEventHandlerUPP (AppEventHandlerProc), 3, type4, 0, 0); - - AEInstallEventHandler (kCoreEventClass, kAEQuitApplication, - NewAEEventHandlerUPP (QuitAppleEventHandler), 0, false); - - AEInstallEventHandler (kCoreEventClass, kAEOpenDocuments, - NewAEEventHandlerUPP (OpenDocEventHandler), 0, false); - } -} - -void MessageManager::doPlatformSpecificShutdown() -{ - if (juceEventHandler != 0) - { - RemoveEventHandler (juceEventHandler); - juceEventHandler = 0; - } - - if (callbackEventHandler != 0) - { - RemoveEventHandler (callbackEventHandler); - callbackEventHandler = 0; - } -} - -bool juce_postMessageToSystemQueue (void* message) -{ - jassert (mainQueue == GetMainEventQueue()); - - EventRef event; - if (CreateEvent (0, kJUCEClass, kJUCEKind, 0, kEventAttributeUserEvent, &event) == noErr) - { - SetEventParameter (event, 'mess', typeVoidPtr, sizeof (void*), &message); - const bool ok = PostEventToQueue (mainQueue, event, kEventPriorityStandard) == noErr; - ReleaseEvent (event); - return ok; - } - - return false; -} - -void MessageManager::broadcastMessage (const String& value) throw() -{ -} - -void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback, - void* data) -{ - if (isThisTheMessageThread()) - { - return (*callback) (data); - } - else - { - jassert (mainQueue == GetMainEventQueue()); - - CallbackMessagePayload cmp; - cmp.function = callback; - cmp.parameter = data; - cmp.result = 0; - cmp.hasBeenExecuted = false; - - EventRef event; - if (CreateEvent (0, kJUCEClass, kCallbackKind, 0, kEventAttributeUserEvent, &event) == noErr) - { - void* v = &cmp; - SetEventParameter (event, 'mess', typeVoidPtr, sizeof (void*), &v); - - if (PostEventToQueue (mainQueue, event, kEventPriorityStandard) == noErr) - { - while (! cmp.hasBeenExecuted) - Thread::yield(); - - return cmp.result; - } - } - - return 0; - } -} - -END_JUCE_NAMESPACE +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "juce_mac_NativeHeaders.h" +#include + +BEGIN_JUCE_NAMESPACE + +#include "../../../src/juce_appframework/events/juce_MessageManager.h" +#include "../../../src/juce_appframework/application/juce_Application.h" +#include "../../../src/juce_appframework/gui/components/juce_Desktop.h" +#include "../../../src/juce_core/text/juce_StringArray.h" +#include "../../../src/juce_core/threads/juce_Thread.h" +#include "../../../src/juce_core/misc/juce_PlatformUtilities.h" + +#undef Point + +extern void juce_HandleProcessFocusChange(); +extern void juce_maximiseAllMinimisedWindows(); +extern void juce_InvokeMainMenuCommand (const HICommand& command); +extern void juce_MainMenuAboutToBeUsed(); + +struct CallbackMessagePayload +{ + MessageCallbackFunction* function; + void* parameter; + void* volatile result; + bool volatile hasBeenExecuted; +}; + +END_JUCE_NAMESPACE + +#if JUCE_COCOA + +NSString* juceMessageName = 0; + +@interface JuceAppDelegate : NSObject +id oldDelegate; +- (JuceAppDelegate*) init; +- (void) dealloc; +- (BOOL) application: (NSApplication*) theApplication openFile: (NSString*) filename; +- (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames; +- (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app; +- (void) applicationDidBecomeActive: (NSNotification*) aNotification; +- (void) applicationDidResignActive: (NSNotification*) aNotification; +- (void) applicationWillUnhide: (NSNotification*) aNotification; +- (void) customEvent: (NSNotification*) aNotification; +- (void) performCallback: (id) info; +@end + +@implementation JuceAppDelegate + +- (JuceAppDelegate*) init +{ + [super init]; + + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + + if (JUCE_NAMESPACE::JUCEApplication::getInstance() != 0) + { + oldDelegate = [NSApp delegate]; + [NSApp setDelegate: self]; + } + else + { + oldDelegate = 0; + [center addObserver: self selector: @selector (applicationDidResignActive:) + name: NSApplicationDidResignActiveNotification object: NSApp]; + + [center addObserver: self selector: @selector (applicationDidBecomeActive:) + name: NSApplicationDidBecomeActiveNotification object: NSApp]; + + [center addObserver: self selector: @selector (applicationWillUnhide:) + name: NSApplicationWillUnhideNotification object: NSApp]; + + } + + [center addObserver: self selector: @selector (customEvent:) + name: juceMessageName object: nil]; + + return self; +} + +- (void) dealloc +{ + if (oldDelegate != 0) + [NSApp setDelegate: oldDelegate]; + + [[NSNotificationCenter defaultCenter] removeObserver: self]; + [super dealloc]; +} + +- (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app +{ + if (JUCE_NAMESPACE::JUCEApplication::getInstance() != 0) + JUCE_NAMESPACE::JUCEApplication::getInstance()->systemRequestedQuit(); + + return NSTerminateLater; +} + +- (BOOL) application: (NSApplication*) app openFile: (NSString*) filename +{ + if (JUCE_NAMESPACE::JUCEApplication::getInstance() != 0) + { + JUCE_NAMESPACE::JUCEApplication::getInstance()->anotherInstanceStarted (nsStringToJuce (filename)); + return YES; + } + + return NO; +} + +- (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames +{ + JUCE_NAMESPACE::StringArray files; + for (int i = 0; i < [filenames count]; ++i) + files.add (nsStringToJuce ((NSString*) [filenames objectAtIndex: i])); + + if (files.size() > 0 && JUCE_NAMESPACE::JUCEApplication::getInstance() != 0) + JUCE_NAMESPACE::JUCEApplication::getInstance()->anotherInstanceStarted (files.joinIntoString (T(" "))); +} + +- (void) applicationDidBecomeActive: (NSNotification*) aNotification +{ + JUCE_NAMESPACE::juce_HandleProcessFocusChange(); +} + +- (void) applicationDidResignActive: (NSNotification*) aNotification +{ + JUCE_NAMESPACE::juce_HandleProcessFocusChange(); +} + +- (void) applicationWillUnhide: (NSNotification*) aNotification +{ + JUCE_NAMESPACE::juce_maximiseAllMinimisedWindows(); +} + +- (void) customEvent: (NSNotification*) n +{ + void* message = 0; + [((NSData*) [n object]) getBytes: &message length: sizeof (message)]; + + if (message != 0) + JUCE_NAMESPACE::MessageManager::getInstance()->deliverMessage (message); +} + +- (void) performCallback: (id) info +{ + JUCE_NAMESPACE::CallbackMessagePayload* pl = (JUCE_NAMESPACE::CallbackMessagePayload*) info; + + if (pl != 0) + { + pl->result = (*pl->function) (pl->parameter); + pl->hasBeenExecuted = true; + } +} + +@end +#endif + +BEGIN_JUCE_NAMESPACE + + +#if JUCE_COCOA +static JuceAppDelegate* juceAppDelegate = 0; + +#else +static int kJUCEClass = FOUR_CHAR_CODE ('JUCE'); +const int kJUCEKind = 1; +const int kCallbackKind = 2; + + +static pascal OSStatus EventHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData) +{ + void* event = 0; + GetEventParameter (theEvent, 'mess', typeVoidPtr, 0, sizeof (void*), 0, &event); + + if (event != 0) + MessageManager::getInstance()->deliverMessage (event); + + return noErr; +} + +static pascal OSStatus CallbackHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData) +{ + CallbackMessagePayload* pl = 0; + GetEventParameter (theEvent, 'mess', typeVoidPtr, 0, sizeof(pl), 0, &pl); + + if (pl != 0) + { + pl->result = (*pl->function) (pl->parameter); + pl->hasBeenExecuted = true; + } + + return noErr; +} + +static pascal OSStatus MouseClickHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData) +{ + ::Point where; + GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, 0, sizeof(::Point), 0, &where); + WindowRef window; + if (FindWindow (where, &window) == inMenuBar) + { + // turn off the wait cursor before going in here.. + const int oldTimeBeforeWaitCursor = MessageManager::getInstance()->getTimeBeforeShowingWaitCursor(); + MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (0); + + if (Component::getCurrentlyModalComponent() != 0) + Component::getCurrentlyModalComponent()->inputAttemptWhenModal(); + + juce_MainMenuAboutToBeUsed(); + MenuSelect (where); + HiliteMenu (0); + + MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (oldTimeBeforeWaitCursor); + return noErr; + } + + return eventNotHandledErr; +} + +static pascal OSErr QuitAppleEventHandler (const AppleEvent *appleEvt, AppleEvent* reply, long refcon) +{ + if (JUCEApplication::getInstance() != 0) + JUCEApplication::getInstance()->systemRequestedQuit(); + + return noErr; +} + +static pascal OSErr OpenDocEventHandler (const AppleEvent *appleEvt, AppleEvent* reply, long refcon) +{ + AEDescList docs; + StringArray files; + + if (AEGetParamDesc (appleEvt, keyDirectObject, typeAEList, &docs) == noErr) + { + long num; + if (AECountItems (&docs, &num) == noErr) + { + for (int i = 1; i <= num; ++i) + { + FSRef file; + AEKeyword keyword; + DescType type; + Size size; + + if (AEGetNthPtr (&docs, i, typeFSRef, &keyword, &type, + &file, sizeof (file), &size) == noErr) + { + const String path (PlatformUtilities::makePathFromFSRef (&file)); + + if (path.isNotEmpty()) + files.add (path.quoted()); + } + } + + if (files.size() > 0 + && JUCEApplication::getInstance() != 0) + { + JUCE_TRY + { + JUCEApplication::getInstance() + ->anotherInstanceStarted (files.joinIntoString (T(" "))); + } + JUCE_CATCH_ALL + } + } + + AEDisposeDesc (&docs); + }; + + return noErr; +} + +static pascal OSStatus AppEventHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData) +{ + const UInt32 eventClass = GetEventClass (theEvent); + + if (eventClass == kEventClassCommand) + { + HICommand command; + + if (GetEventParameter (theEvent, kEventParamHICommand, typeHICommand, 0, sizeof (command), 0, &command) == noErr + || GetEventParameter (theEvent, kEventParamDirectObject, typeHICommand, 0, sizeof (command), 0, &command) == noErr) + { + if (command.commandID == kHICommandQuit) + { + if (JUCEApplication::getInstance() != 0) + JUCEApplication::getInstance()->systemRequestedQuit(); + + return noErr; + } + else if (command.commandID == kHICommandMaximizeAll + || command.commandID == kHICommandMaximizeWindow + || command.commandID == kHICommandBringAllToFront) + { + juce_maximiseAllMinimisedWindows(); + return noErr; + } + else + { + juce_InvokeMainMenuCommand (command); + } + } + } + else if (eventClass == kEventClassApplication) + { + if (GetEventKind (theEvent) == kEventAppFrontSwitched) + { + juce_HandleProcessFocusChange(); + } + else if (GetEventKind (theEvent) == kEventAppShown) + { + // this seems to blank the windows, so we need to do a repaint.. + for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;) + { + Component* const c = Desktop::getInstance().getComponent (i); + + if (c != 0) + c->repaint(); + } + } + } + + return eventNotHandledErr; +} + +static EventQueueRef mainQueue; +static EventHandlerRef juceEventHandler = 0; +static EventHandlerRef callbackEventHandler = 0; + +#endif + +//============================================================================== +void MessageManager::doPlatformSpecificInitialisation() +{ + static bool initialised = false; + + if (! initialised) + { + initialised = true; + +#if JUCE_COCOA + // if we're linking a Juce app to one or more dynamic libraries, we'll need different values + // for this so each module doesn't interfere with the others. + UnsignedWide t; + Microseconds (&t); + kJUCEClass ^= t.lo; + + juceMessageName = juceStringToNS ("juce_" + String::toHexString ((int) t.lo)); + + juceAppDelegate = [[JuceAppDelegate alloc] init]; +#else + mainQueue = GetMainEventQueue(); +#endif + } + +#if ! JUCE_COCOA + const EventTypeSpec type1 = { kJUCEClass, kJUCEKind }; + InstallApplicationEventHandler (NewEventHandlerUPP (EventHandlerProc), 1, &type1, 0, &juceEventHandler); + + const EventTypeSpec type2 = { kJUCEClass, kCallbackKind }; + InstallApplicationEventHandler (NewEventHandlerUPP (CallbackHandlerProc), 1, &type2, 0, &callbackEventHandler); + + // only do this stuff if we're running as an application rather than a library.. + if (JUCEApplication::getInstance() != 0) + { + const EventTypeSpec type3 = { kEventClassMouse, kEventMouseDown }; + InstallApplicationEventHandler (NewEventHandlerUPP (MouseClickHandlerProc), 1, &type3, 0, 0); + + const EventTypeSpec type4[] = { { kEventClassApplication, kEventAppShown }, + { kEventClassApplication, kEventAppFrontSwitched }, + { kEventClassCommand, kEventProcessCommand } }; + + InstallApplicationEventHandler (NewEventHandlerUPP (AppEventHandlerProc), 3, type4, 0, 0); + + AEInstallEventHandler (kCoreEventClass, kAEQuitApplication, + NewAEEventHandlerUPP (QuitAppleEventHandler), 0, false); + + AEInstallEventHandler (kCoreEventClass, kAEOpenDocuments, + NewAEEventHandlerUPP (OpenDocEventHandler), 0, false); + } +#endif +} + +void MessageManager::doPlatformSpecificShutdown() +{ + if (juceEventHandler != 0) + { + RemoveEventHandler (juceEventHandler); + juceEventHandler = 0; + } + + if (callbackEventHandler != 0) + { + RemoveEventHandler (callbackEventHandler); + callbackEventHandler = 0; + } +} + +bool juce_postMessageToSystemQueue (void* message) +{ +#if JUCE_COCOA + [[NSNotificationCenter defaultCenter] postNotificationName: juceMessageName + object: [NSData dataWithBytes: &message + length: (int) sizeof (message)]]; + + return true; + +#else + jassert (mainQueue == GetMainEventQueue()); + + EventRef event; + if (CreateEvent (0, kJUCEClass, kJUCEKind, 0, kEventAttributeUserEvent, &event) == noErr) + { + SetEventParameter (event, 'mess', typeVoidPtr, sizeof (void*), &message); + const bool ok = PostEventToQueue (mainQueue, event, kEventPriorityStandard) == noErr; + ReleaseEvent (event); + return ok; + } + + return false; +#endif +} + +void MessageManager::broadcastMessage (const String& value) throw() +{ +} + +void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback, + void* data) +{ + if (isThisTheMessageThread()) + { + return (*callback) (data); + } + else + { + CallbackMessagePayload cmp; + cmp.function = callback; + cmp.parameter = data; + cmp.result = 0; + cmp.hasBeenExecuted = false; + +#if JUCE_COCOA + [juceAppDelegate performSelectorOnMainThread: @selector (performCallback:) + withObject: (id) &cmp + waitUntilDone: YES]; + + return cmp.result; + +#else + jassert (mainQueue == GetMainEventQueue()); + + EventRef event; + if (CreateEvent (0, kJUCEClass, kCallbackKind, 0, kEventAttributeUserEvent, &event) == noErr) + { + void* v = &cmp; + SetEventParameter (event, 'mess', typeVoidPtr, sizeof (void*), &v); + + if (PostEventToQueue (mainQueue, event, kEventPriorityStandard) == noErr) + { + while (! cmp.hasBeenExecuted) + Thread::yield(); + + return cmp.result; + } + } + + return 0; +#endif + } +} + +END_JUCE_NAMESPACE diff --git a/build/macosx/platform_specific_code/juce_mac_NativeHeaders.h b/build/macosx/platform_specific_code/juce_mac_NativeHeaders.h index dc5938fd00..21923aa8ae 100644 --- a/build/macosx/platform_specific_code/juce_mac_NativeHeaders.h +++ b/build/macosx/platform_specific_code/juce_mac_NativeHeaders.h @@ -50,7 +50,18 @@ private: NSAutoreleasePool* pool; }; - END_JUCE_NAMESPACE +//============================================================================== +static const JUCE_NAMESPACE::String nsStringToJuce (NSString* s) +{ + return JUCE_NAMESPACE::String::fromUTF8 ((JUCE_NAMESPACE::uint8*) [s UTF8String]); +} + +static NSString* juceStringToNS (const JUCE_NAMESPACE::String& s) +{ + return [NSString stringWithUTF8String: (const char*) s.toUTF8()]; +} + + #endif diff --git a/build/macosx/platform_specific_code/juce_mac_SystemStats.mm b/build/macosx/platform_specific_code/juce_mac_SystemStats.mm index 3cefd29bb5..89ef2fb974 100644 --- a/build/macosx/platform_specific_code/juce_mac_SystemStats.mm +++ b/build/macosx/platform_specific_code/juce_mac_SystemStats.mm @@ -29,7 +29,7 @@ ============================================================================== */ -#include "../../../src/juce_core/basics/juce_StandardHeader.h" +#include "juce_mac_NativeHeaders.h" #include #include #include @@ -145,6 +145,7 @@ void SystemStats::initialiseStats() throw() { initialised = true; + [NSApplication sharedApplication]; NSApplicationLoad(); #if JUCE_INTEL diff --git a/build/macosx/platform_specific_code/juce_mac_Windowing.mm b/build/macosx/platform_specific_code/juce_mac_Windowing.mm index 1c7facc259..16c9ea718d 100644 --- a/build/macosx/platform_specific_code/juce_mac_Windowing.mm +++ b/build/macosx/platform_specific_code/juce_mac_Windowing.mm @@ -29,9 +29,8 @@ ============================================================================== */ -#include "../../../src/juce_core/basics/juce_StandardHeader.h" +#include "juce_mac_NativeHeaders.h" #include -#include #include #include #include @@ -2139,6 +2138,11 @@ bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) if (juce_currentMouseTrackingPeer != 0) trackNextMouseEvent(); +/* [[NSRunLoop mainRunLoop] acceptInputForMode: NSDefaultRunLoopMode + beforeDate: returnIfNoPendingMessages + ? [[NSDate date] addTimeInterval: 0.01] + : [NSDate distantFuture]]; +*/ EventRef theEvent; if (ReceiveNextEvent (0, 0, (returnIfNoPendingMessages) ? kEventDurationNoWait diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 84aaff3e5d..8ac533fd34 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -51496,6 +51496,12 @@ void FileListComponent::scrollToTop() void FileListComponent::changeListenerCallback (void*) { updateContent(); + + if (lastDirectory != fileList.getDirectory()) + { + lastDirectory = fileList.getDirectory(); + deselectAllRows(); + } } class FileListItemComponent : public Component, @@ -252706,6 +252712,39 @@ END_JUCE_NAMESPACE /********* Start of inlined file: juce_mac_SystemStats.mm *********/ +/********* Start of inlined file: juce_mac_NativeHeaders.h *********/ +#ifndef __JUCE_MAC_NATIVEHEADERS_JUCEHEADER__ +#define __JUCE_MAC_NATIVEHEADERS_JUCEHEADER__ + +#include + +BEGIN_JUCE_NAMESPACE + +class AutoPool +{ +public: + AutoPool() { pool = [[NSAutoreleasePool alloc] init]; } + ~AutoPool() { [pool release]; } + +private: + NSAutoreleasePool* pool; +}; + +END_JUCE_NAMESPACE + +static const JUCE_NAMESPACE::String nsStringToJuce (NSString* s) +{ + return JUCE_NAMESPACE::String::fromUTF8 ((JUCE_NAMESPACE::uint8*) [s UTF8String]); +} + +static NSString* juceStringToNS (const JUCE_NAMESPACE::String& s) +{ + return [NSString stringWithUTF8String: (const char*) s.toUTF8()]; +} + +#endif +/********* End of inlined file: juce_mac_NativeHeaders.h *********/ + #include #include #include @@ -252813,7 +252852,8 @@ void SystemStats::initialiseStats() throw() { initialised = true; - NSApplicationLoad(); + [NSApplication sharedApplication]; +// NSApplicationLoad(); #if JUCE_INTEL { @@ -253113,29 +253153,6 @@ END_JUCE_NAMESPACE /********* Start of inlined file: juce_mac_Network.mm *********/ -/********* Start of inlined file: juce_mac_NativeHeaders.h *********/ -#ifndef __JUCE_MAC_NATIVEHEADERS_JUCEHEADER__ -#define __JUCE_MAC_NATIVEHEADERS_JUCEHEADER__ - -#include - -BEGIN_JUCE_NAMESPACE - -class AutoPool -{ -public: - AutoPool() { pool = [[NSAutoreleasePool alloc] init]; } - ~AutoPool() { [pool release]; } - -private: - NSAutoreleasePool* pool; -}; - -END_JUCE_NAMESPACE - -#endif -/********* End of inlined file: juce_mac_NativeHeaders.h *********/ - #include #include #include @@ -255844,16 +255861,6 @@ BEGIN_JUCE_NAMESPACE END_JUCE_NAMESPACE -static const JUCE_NAMESPACE::String nsStringToJuce (NSString* s) -{ - return JUCE_NAMESPACE::String::fromUTF8 ((JUCE_NAMESPACE::uint8*) [s UTF8String]); -} - -static NSString* juceStringToNS (const JUCE_NAMESPACE::String& s) -{ - return [NSString stringWithUTF8String: (const char*) s.toUTF8()]; -} - @interface JuceFileChooserDelegate : NSObject { JUCE_NAMESPACE::StringArray* filters; @@ -255891,8 +255898,7 @@ static NSString* juceStringToNS (const JUCE_NAMESPACE::String& s) return true; } - return JUCE_NAMESPACE::File (JUCE_NAMESPACE::String::fromUTF8 ((const uint8*) filenameUTF8)) - .isDirectory(); + return JUCE_NAMESPACE::File (nsStringToJuce (filename)).isDirectory(); } @end @@ -256466,15 +256472,158 @@ BEGIN_JUCE_NAMESPACE #undef Point -static int kJUCEClass = FOUR_CHAR_CODE ('JUCE'); -const int kJUCEKind = 1; -const int kCallbackKind = 2; - extern void juce_HandleProcessFocusChange(); extern void juce_maximiseAllMinimisedWindows(); extern void juce_InvokeMainMenuCommand (const HICommand& command); extern void juce_MainMenuAboutToBeUsed(); +struct CallbackMessagePayload +{ + MessageCallbackFunction* function; + void* parameter; + void* volatile result; + bool volatile hasBeenExecuted; +}; + +END_JUCE_NAMESPACE + +#if JUCE_COCOA + +NSString* juceMessageName = 0; + +@interface JuceAppDelegate : NSObject +id oldDelegate; +- (JuceAppDelegate*) init; +- (void) dealloc; +- (BOOL) application: (NSApplication*) theApplication openFile: (NSString*) filename; +- (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames; +- (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app; +- (void) applicationDidBecomeActive: (NSNotification*) aNotification; +- (void) applicationDidResignActive: (NSNotification*) aNotification; +- (void) applicationWillUnhide: (NSNotification*) aNotification; +- (void) customEvent: (NSNotification*) aNotification; +- (void) performCallback: (id) info; +@end + +@implementation JuceAppDelegate + +- (JuceAppDelegate*) init +{ + [super init]; + + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + + if (JUCE_NAMESPACE::JUCEApplication::getInstance() != 0) + { + oldDelegate = [NSApp delegate]; + [NSApp setDelegate: self]; + } + else + { + oldDelegate = 0; + [center addObserver: self selector: @selector (applicationDidResignActive:) + name: NSApplicationDidResignActiveNotification object: NSApp]; + + [center addObserver: self selector: @selector (applicationDidBecomeActive:) + name: NSApplicationDidBecomeActiveNotification object: NSApp]; + + [center addObserver: self selector: @selector (applicationWillUnhide:) + name: NSApplicationWillUnhideNotification object: NSApp]; + + } + + [center addObserver: self selector: @selector (customEvent:) + name: juceMessageName object: nil]; + + return self; +} + +- (void) dealloc +{ + if (oldDelegate != 0) + [NSApp setDelegate: oldDelegate]; + + [[NSNotificationCenter defaultCenter] removeObserver: self]; + [super dealloc]; +} + +- (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app +{ + if (JUCE_NAMESPACE::JUCEApplication::getInstance() != 0) + JUCE_NAMESPACE::JUCEApplication::getInstance()->systemRequestedQuit(); + + return NSTerminateLater; +} + +- (BOOL) application: (NSApplication*) app openFile: (NSString*) filename +{ + if (JUCE_NAMESPACE::JUCEApplication::getInstance() != 0) + { + JUCE_NAMESPACE::JUCEApplication::getInstance()->anotherInstanceStarted (nsStringToJuce (filename)); + return YES; + } + + return NO; +} + +- (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames +{ + JUCE_NAMESPACE::StringArray files; + for (int i = 0; i < [filenames count]; ++i) + files.add (nsStringToJuce ((NSString*) [filenames objectAtIndex: i])); + + if (files.size() > 0 && JUCE_NAMESPACE::JUCEApplication::getInstance() != 0) + JUCE_NAMESPACE::JUCEApplication::getInstance()->anotherInstanceStarted (files.joinIntoString (T(" "))); +} + +- (void) applicationDidBecomeActive: (NSNotification*) aNotification +{ + JUCE_NAMESPACE::juce_HandleProcessFocusChange(); +} + +- (void) applicationDidResignActive: (NSNotification*) aNotification +{ + JUCE_NAMESPACE::juce_HandleProcessFocusChange(); +} + +- (void) applicationWillUnhide: (NSNotification*) aNotification +{ + JUCE_NAMESPACE::juce_maximiseAllMinimisedWindows(); +} + +- (void) customEvent: (NSNotification*) n +{ + void* message = 0; + [((NSData*) [n object]) getBytes: &message length: sizeof (message)]; + + if (message != 0) + JUCE_NAMESPACE::MessageManager::getInstance()->deliverMessage (message); +} + +- (void) performCallback: (id) info +{ + JUCE_NAMESPACE::CallbackMessagePayload* pl = (JUCE_NAMESPACE::CallbackMessagePayload*) info; + + if (pl != 0) + { + pl->result = (*pl->function) (pl->parameter); + pl->hasBeenExecuted = true; + } +} + +@end +#endif + +BEGIN_JUCE_NAMESPACE + +#if JUCE_COCOA +static JuceAppDelegate* juceAppDelegate = 0; + +#else +static int kJUCEClass = FOUR_CHAR_CODE ('JUCE'); +const int kJUCEKind = 1; +const int kCallbackKind = 2; + static pascal OSStatus EventHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData) { void* event = 0; @@ -256486,14 +256635,6 @@ static pascal OSStatus EventHandlerProc (EventHandlerCallRef, EventRef theEvent, return noErr; } -struct CallbackMessagePayload -{ - MessageCallbackFunction* function; - void* parameter; - void* volatile result; - bool volatile hasBeenExecuted; -}; - static pascal OSStatus CallbackHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData) { CallbackMessagePayload* pl = 0; @@ -256643,6 +256784,8 @@ static EventQueueRef mainQueue; static EventHandlerRef juceEventHandler = 0; static EventHandlerRef callbackEventHandler = 0; +#endif + void MessageManager::doPlatformSpecificInitialisation() { static bool initialised = false; @@ -256651,21 +256794,22 @@ void MessageManager::doPlatformSpecificInitialisation() { initialised = true; -#if MACOS_10_3_OR_EARLIER - // work-around for a bug in MacOS 10.2.. - ProcessSerialNumber junkPSN; - (void) GetCurrentProcess (&junkPSN); -#endif - - mainQueue = GetMainEventQueue(); - +#if JUCE_COCOA // if we're linking a Juce app to one or more dynamic libraries, we'll need different values // for this so each module doesn't interfere with the others. UnsignedWide t; Microseconds (&t); kJUCEClass ^= t.lo; + + juceMessageName = juceStringToNS ("juce_" + String::toHexString ((int) t.lo)); + + juceAppDelegate = [[JuceAppDelegate alloc] init]; +#else + mainQueue = GetMainEventQueue(); +#endif } +#if ! JUCE_COCOA const EventTypeSpec type1 = { kJUCEClass, kJUCEKind }; InstallApplicationEventHandler (NewEventHandlerUPP (EventHandlerProc), 1, &type1, 0, &juceEventHandler); @@ -256690,6 +256834,7 @@ void MessageManager::doPlatformSpecificInitialisation() AEInstallEventHandler (kCoreEventClass, kAEOpenDocuments, NewAEEventHandlerUPP (OpenDocEventHandler), 0, false); } +#endif } void MessageManager::doPlatformSpecificShutdown() @@ -256709,6 +256854,14 @@ void MessageManager::doPlatformSpecificShutdown() bool juce_postMessageToSystemQueue (void* message) { +#if JUCE_COCOA + [[NSNotificationCenter defaultCenter] postNotificationName: juceMessageName + object: [NSData dataWithBytes: &message + length: (int) sizeof (message)]]; + + return true; + +#else jassert (mainQueue == GetMainEventQueue()); EventRef event; @@ -256721,6 +256874,7 @@ bool juce_postMessageToSystemQueue (void* message) } return false; +#endif } void MessageManager::broadcastMessage (const String& value) throw() @@ -256736,14 +256890,22 @@ void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* call } else { - jassert (mainQueue == GetMainEventQueue()); - CallbackMessagePayload cmp; cmp.function = callback; cmp.parameter = data; cmp.result = 0; cmp.hasBeenExecuted = false; +#if JUCE_COCOA + [juceAppDelegate performSelectorOnMainThread: @selector (performCallback:) + withObject: (id) &cmp + waitUntilDone: YES]; + + return cmp.result; + +#else + jassert (mainQueue == GetMainEventQueue()); + EventRef event; if (CreateEvent (0, kJUCEClass, kCallbackKind, 0, kEventAttributeUserEvent, &event) == noErr) { @@ -256760,6 +256922,7 @@ void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* call } return 0; +#endif } } @@ -257176,7 +257339,6 @@ END_JUCE_NAMESPACE /********* Start of inlined file: juce_mac_Windowing.mm *********/ #include -#include #include #include #include @@ -259233,6 +259395,11 @@ bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) if (juce_currentMouseTrackingPeer != 0) trackNextMouseEvent(); +/* [[NSRunLoop mainRunLoop] acceptInputForMode: NSDefaultRunLoopMode + beforeDate: returnIfNoPendingMessages + ? [[NSDate date] addTimeInterval: 0.01] + : [NSDate distantFuture]]; +*/ EventRef theEvent; if (ReceiveNextEvent (0, 0, (returnIfNoPendingMessages) ? kEventDurationNoWait @@ -259255,7 +259422,7 @@ bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) return true; } - return false; + return true; } ComponentPeer* Component::createNewPeer (int styleFlags, void* windowToAttachTo) diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 0f22a12f33..b067fa3ff3 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -7464,7 +7464,7 @@ public: /** Returns the type of operating system we're running on. - @returns one of the values from the OSType enum. + @returns one of the values from the OperatingSystemType enum. @see getOperatingSystemName */ static OperatingSystemType getOperatingSystemType() throw(); @@ -44393,7 +44393,7 @@ public: key to toggle velocity-sensitive mode */ void setVelocityModeParameters (const double sensitivity = 1.0, - const int threshold = 1.0, + const int threshold = 1, const double offset = 0.0, const bool userCanPressKeyToSwapMode = true) throw(); @@ -49163,6 +49163,8 @@ public: private: FileListComponent (const FileListComponent&); const FileListComponent& operator= (const FileListComponent&); + + File lastDirectory; }; #endif // __JUCE_FILELISTCOMPONENT_JUCEHEADER__ diff --git a/src/juce_appframework/gui/components/controls/juce_Slider.h b/src/juce_appframework/gui/components/controls/juce_Slider.h index 98b9f22690..d8f8d77b66 100644 --- a/src/juce_appframework/gui/components/controls/juce_Slider.h +++ b/src/juce_appframework/gui/components/controls/juce_Slider.h @@ -171,7 +171,7 @@ public: key to toggle velocity-sensitive mode */ void setVelocityModeParameters (const double sensitivity = 1.0, - const int threshold = 1.0, + const int threshold = 1, const double offset = 0.0, const bool userCanPressKeyToSwapMode = true) throw(); diff --git a/src/juce_appframework/gui/components/filebrowser/juce_FileListComponent.cpp b/src/juce_appframework/gui/components/filebrowser/juce_FileListComponent.cpp index 6bc39fb742..f5aa47f6e3 100644 --- a/src/juce_appframework/gui/components/filebrowser/juce_FileListComponent.cpp +++ b/src/juce_appframework/gui/components/filebrowser/juce_FileListComponent.cpp @@ -70,6 +70,12 @@ void FileListComponent::scrollToTop() void FileListComponent::changeListenerCallback (void*) { updateContent(); + + if (lastDirectory != fileList.getDirectory()) + { + lastDirectory = fileList.getDirectory(); + deselectAllRows(); + } } //============================================================================== diff --git a/src/juce_appframework/gui/components/filebrowser/juce_FileListComponent.h b/src/juce_appframework/gui/components/filebrowser/juce_FileListComponent.h index 03429cac72..92e89a984c 100644 --- a/src/juce_appframework/gui/components/filebrowser/juce_FileListComponent.h +++ b/src/juce_appframework/gui/components/filebrowser/juce_FileListComponent.h @@ -96,6 +96,8 @@ public: private: FileListComponent (const FileListComponent&); const FileListComponent& operator= (const FileListComponent&); + + File lastDirectory; }; diff --git a/src/juce_appframework/gui/components/special/juce_QuickTimeMovieComponent.cpp b/src/juce_appframework/gui/components/special/juce_QuickTimeMovieComponent.cpp index 3f9930ecac..bf289963d1 100644 --- a/src/juce_appframework/gui/components/special/juce_QuickTimeMovieComponent.cpp +++ b/src/juce_appframework/gui/components/special/juce_QuickTimeMovieComponent.cpp @@ -326,7 +326,10 @@ void QuickTimeMovieComponent::setSpeed (const float newSpeed) void QuickTimeMovieComponent::setMovieVolume (const float newVolume) { if (qtMovie != 0) + { qtMovie->PutAudioVolume (newVolume); + qtMovie->PutAudioMute (newVolume <= 0); + } } float QuickTimeMovieComponent::getMovieVolume() const diff --git a/src/juce_core/basics/juce_SystemStats.h b/src/juce_core/basics/juce_SystemStats.h index adb8d157fb..4489a833a9 100644 --- a/src/juce_core/basics/juce_SystemStats.h +++ b/src/juce_core/basics/juce_SystemStats.h @@ -75,7 +75,7 @@ public: /** Returns the type of operating system we're running on. - @returns one of the values from the OSType enum. + @returns one of the values from the OperatingSystemType enum. @see getOperatingSystemName */ static OperatingSystemType getOperatingSystemType() throw();