From facb48b04c9f00b7fc89504523a1b798f2fc3301 Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Thu, 29 Oct 2009 13:36:47 +0000 Subject: [PATCH] tweak to RTAS shutdown for PT8; added tooltips for the TableListBox; started adding some iphone audio code (not yet usable) --- .../wrapper/RTAS/juce_RTAS_MacUtilities.mm | 3 + juce_amalgamated.cpp | 558 ++++++++++++++---- juce_amalgamated.h | 8 +- .../components/controls/juce_TableListBox.cpp | 16 +- .../components/controls/juce_TableListBox.h | 4 + src/gui/components/controls/juce_TreeView.cpp | 4 +- .../menus/juce_MenuBarComponent.cpp | 2 +- .../special/juce_QuickTimeMovieComponent.h | 2 +- .../graphics/fonts/juce_GlyphArrangement.h | 2 +- src/native/mac/juce_iphone_Audio.cpp | 476 +++++++++++---- src/native/mac/juce_iphone_MiscUtilities.mm | 69 ++- src/native/mac/juce_mac_NativeIncludes.h | 2 - src/native/windows/juce_win32_ASIO.cpp | 5 +- 13 files changed, 905 insertions(+), 246 deletions(-) diff --git a/extras/audio plugins/wrapper/RTAS/juce_RTAS_MacUtilities.mm b/extras/audio plugins/wrapper/RTAS/juce_RTAS_MacUtilities.mm index 47282f09d5..c17c44075c 100644 --- a/extras/audio plugins/wrapper/RTAS/juce_RTAS_MacUtilities.mm +++ b/extras/audio plugins/wrapper/RTAS/juce_RTAS_MacUtilities.mm @@ -108,6 +108,9 @@ void removeSubWindow (void* nsWindow, Component* comp) [hostWindow removeChildWindow: pluginWindow]; comp->removeFromDesktop(); [hostWindow release]; + + for (int i = 20; --i >= 0;) + MessageManager::getInstance()->runDispatchLoopUntil (1); } static bool isJuceWindow (WindowRef w) throw() diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 7e99cb879b..10ae7596da 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -735,8 +735,6 @@ public: #if MACOS_10_4_OR_EARLIER #include - typedef int NSInteger; - typedef unsigned int NSUInteger; #endif #endif // __JUCE_MAC_NATIVEINCLUDES_JUCEHEADER__ @@ -47555,7 +47553,8 @@ BEGIN_JUCE_NAMESPACE static const tchar* const tableColumnPropertyTag = T("_tableColumnID"); -class TableListRowComp : public Component +class TableListRowComp : public Component, + public TooltipClient { public: TableListRowComp (TableListBox& owner_) @@ -47759,6 +47758,19 @@ public: owner.getModel()->cellDoubleClicked (row, columnId, e); } + const String getTooltip() + { + int x, y; + getMouseXYRelative (x, y); + + const int columnId = owner.getHeader()->getColumnIdAtX (x); + + if (columnId != 0 && owner.getModel() != 0) + return owner.getModel()->getCellTooltip (row, columnId); + + return String::empty; + } + juce_UseDebuggingNewOperator private: @@ -244743,11 +244755,12 @@ public: AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName) { + // ASIO can't open two different devices for input and output - they must be the same one. jassert (inputDeviceName == outputDeviceName || outputDeviceName.isEmpty() || inputDeviceName.isEmpty()); - (void) inputDeviceName; jassert (hasScanned); // need to call scanForDevices() before doing this - const int index = deviceNames.indexOf (outputDeviceName); + const int index = deviceNames.indexOf (outputDeviceName.isNotEmpty() ? outputDeviceName + : inputDeviceName); if (index >= 0) { @@ -258363,33 +258376,76 @@ void PlatformUtilities::addItemToDock (const File& file) #if ! JUCE_ONLY_BUILD_CORE_LIBRARY -bool AlertWindow::showNativeDialogBox (const String& title, - const String& bodyText, - bool isOkCancel) +END_JUCE_NAMESPACE + +@interface JuceAlertBoxDelegate : NSObject +{ +@public + bool clickedOk; +} + +- (void) alertView: (UIAlertView*) alertView clickedButtonAtIndex: (NSInteger) buttonIndex; + +@end + +@implementation JuceAlertBoxDelegate + +- (void) alertView: (UIAlertView*) alertView clickedButtonAtIndex: (NSInteger) buttonIndex +{ + clickedOk = (buttonIndex == 0); + alertView.hidden = true; +} + +@end + +BEGIN_JUCE_NAMESPACE + +// (This function is used directly by other bits of code) +bool juce_iPhoneShowModalAlert (const String& title, + const String& bodyText, + NSString* okButtonText, + NSString* cancelButtonText) { const ScopedAutoReleasePool pool; - UIAlertView *alert = [[[UIAlertView alloc] initWithTitle: juceStringToNS (title) - message: juceStringToNS (title) - delegate: nil - cancelButtonTitle: @"OK" - otherButtonTitles: (isOkCancel ? @"Cancel" : nil), nil] autorelease]; - alert.cancelButtonIndex = alert.firstOtherButtonIndex; + JuceAlertBoxDelegate* callback = [[JuceAlertBoxDelegate alloc] init]; + + UIAlertView* alert = [[UIAlertView alloc] initWithTitle: juceStringToNS (title) + message: juceStringToNS (bodyText) + delegate: callback + cancelButtonTitle: okButtonText + otherButtonTitles: cancelButtonText, nil]; + [alert retain]; [alert show]; - // xxx need to use a delegate to find which button was clicked - return false; + while (! alert.hidden && alert.superview != nil) + [[NSRunLoop mainRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; + + const bool result = callback->clickedOk; + [alert release]; + [callback release]; + + return result; +} + +bool AlertWindow::showNativeDialogBox (const String& title, + const String& bodyText, + bool isOkCancel) +{ + return juce_iPhoneShowModalAlert (title, bodyText, + @"OK", + isOkCancel ? @"Cancel" : nil); } bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles) { - jassertfalse // not implemented! + jassertfalse // no such thing on the iphone! return false; } bool DragAndDropContainer::performExternalDragDropOfText (const String& text) { - jassertfalse // not implemented! + jassertfalse // no such thing on the iphone! return false; } @@ -261169,37 +261225,43 @@ bool WebBrowserComponent::pageAboutToLoad (const String& url) // compiled on its own). #if JUCE_INCLUDED_FILE -class IPhoneAudioIODevice : public AudioIODeviceType +class IPhoneAudioIODevice : public AudioIODevice { public: - IPhoneAudioIODevice (const String& deviceName, const bool isInput_) + IPhoneAudioIODevice (const String& deviceName) : AudioIODevice (deviceName, T("Audio")), - isInput (isInput_), - isOpen_ (false), - isStarted (false) + audioUnit (0), + isRunning (false), + callback (0), + actualBufferSize (0), + floatData (1, 2) { + numInputChannels = 2; + numOutputChannels = 2; + preferredBufferSize = 0; + + AudioSessionInitialize (0, 0, interruptionListenerStatic, this); + updateDeviceInfo(); } - ~IPhoneAudioIODeviceType() + ~IPhoneAudioIODevice() { + close(); } const StringArray getOutputChannelNames() { StringArray s; - if (! isInput) - { - s.add ("Left"); - s.add ("Right"); - } + s.add ("Left"); + s.add ("Right"); return s; } const StringArray getInputChannelNames() { StringArray s; - if (isInput) + if (audioInputIsAvailable) { s.add ("Left"); s.add ("Right"); @@ -261209,134 +261271,150 @@ public: int getNumSampleRates() { - return sampleRates.size(); + return 1; } double getSampleRate (int index) { - return sampleRates [index]; + return sampleRate; } int getNumBufferSizesAvailable() { - return bufferSizes.size(); + return 1; } int getBufferSizeSamples (int index) { - return bufferSizes [index]; + return getDefaultBufferSize(); } int getDefaultBufferSize() { - for (int i = 0; i < getNumBufferSizesAvailable(); ++i) - if (getBufferSizeSamples(i) >= 512) - return getBufferSizeSamples(i); - - return 512; + return 1024; } const String open (const BitArray& inputChannels, const BitArray& outputChannels, double sampleRate, - int bufferSizeSamples) + int bufferSize) { - isOpen_ = true; - - if (bufferSizeSamples <= 0) - bufferSizeSamples = getDefaultBufferSize(); + close(); lastError = String::empty; + preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; - isOpen_ = lastError.isEmpty(); + // xxx set up channel mapping + + activeOutputChans = outputChannels; + activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false); + numOutputChannels = activeOutputChans.countNumberOfSetBits(); + monoOutputChannelNumber = activeOutputChans.findNextSetBit (0); + + activeInputChans = inputChannels; + activeInputChans.setRange (2, activeInputChans.getHighestBit(), false); + numInputChannels = activeInputChans.countNumberOfSetBits(); + monoInputChannelNumber = activeInputChans.findNextSetBit (0); + + AudioSessionSetActive (true); + + UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord; + AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (audioCategory), &audioCategory); + AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, propertyChangedStatic, this); + + fixAudioRouteIfSetToReceiver(); + updateDeviceInfo(); + + Float32 bufferDuration = preferredBufferSize / sampleRate; + AudioSessionSetProperty (kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof (bufferDuration), &bufferDuration); + actualBufferSize = preferredBufferSize; + + prepareFloatBuffers(); + + isRunning = true; + propertyChanged (0, 0, 0); // creates and starts the AU + + lastError = audioUnit != 0 ? String::empty + : T("Couldn't open the device"); return lastError; } void close() { - isOpen_ = false; + if (isRunning) + { + isRunning = false; + AudioSessionSetActive (false); + if (audioUnit != 0) + { + AudioComponentInstanceDispose (audioUnit); + audioUnit = 0; + } + } } bool isOpen() { - return isOpen_; + return isRunning; } int getCurrentBufferSizeSamples() { - return internal != 0 ? internal->getBufferSize() : 512; + return actualBufferSize; } double getCurrentSampleRate() { - return internal != 0 ? internal->getSampleRate() : 0; + return sampleRate; } int getCurrentBitDepth() { - return 32; // no way to find out, so just assume it's high.. + return 16; } const BitArray getActiveOutputChannels() const { - return internal != 0 ? internal->activeOutputChans : BitArray(); + return activeOutputChans; } const BitArray getActiveInputChannels() const { - BitArray chans; - - if (internal != 0) - { - chans = internal->activeInputChans; - - if (internal->inputDevice != 0) - chans.orWith (internal->inputDevice->activeInputChans); - } - - return chans; + return activeInputChans; } 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; + return 0; //xxx } int getInputLatencyInSamples() { - if (internal == 0) - return 0; - - return internal->inputLatency + internal->getBufferSize() * 2; + return 0; //xxx } - void start (AudioIODeviceCallback* callback) + void start (AudioIODeviceCallback* callback_) { - if (internal != 0 && ! isStarted) + if (isRunning && callback != callback_) { - if (callback != 0) - callback->audioDeviceAboutToStart (this); + if (callback_ != 0) + callback_->audioDeviceAboutToStart (this); - isStarted = true; - internal->start (callback); + callbackLock.enter(); + callback = callback_; + callbackLock.exit(); } } void stop() { - if (isStarted && internal != 0) + if (isRunning) { - AudioIODeviceCallback* const lastCallback = internal->callback; - - isStarted = false; - internal->stop (true); + callbackLock.enter(); + AudioIODeviceCallback* const lastCallback = callback; + callback = 0; + callbackLock.exit(); if (lastCallback != 0) lastCallback->audioDeviceStopped(); @@ -261345,10 +261423,7 @@ public: bool isPlaying() { - if (internal->callback == 0) - isStarted = false; - - return isStarted; + return isRunning && callback != 0; } const String getLastError() @@ -261356,36 +261431,286 @@ public: return lastError; } - int inputIndex, outputIndex; - - juce_UseDebuggingNewOperator - private: - CoreAudioInternal* internal; - bool isOpen_, isStarted; + + CriticalSection callbackLock; + Float64 sampleRate; + int numInputChannels, numOutputChannels; + int preferredBufferSize; + int actualBufferSize; + bool isRunning; String lastError; - static OSStatus hardwareListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, const AudioObjectPropertyAddress* pa, void* inClientData) + AudioStreamBasicDescription format; + AudioUnit audioUnit; + UInt32 audioInputIsAvailable; + AudioIODeviceCallback* callback; + BitArray activeOutputChans, activeInputChans; + + AudioSampleBuffer floatData; + float* inputChannels[3]; + float* outputChannels[3]; + bool monoInputChannelNumber, monoOutputChannelNumber; + + void prepareFloatBuffers() { - CoreAudioInternal* const intern = (CoreAudioInternal*) inClientData; + floatData.setSize (numInputChannels + numOutputChannels, actualBufferSize); + zerostruct (inputChannels); + zerostruct (outputChannels); - switch (pa->mSelector) + for (int i = 0; i < numInputChannels; ++i) + inputChannels[i] = floatData.getSampleData (i); + + for (int i = 0; i < numOutputChannels; ++i) + outputChannels[i] = floatData.getSampleData (i + numInputChannels); + } + + OSStatus process (AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) throw() + { + OSStatus err = noErr; + + if (audioInputIsAvailable) + err = AudioUnitRender (audioUnit, ioActionFlags, inTimeStamp, 1, inNumberFrames, ioData); + + const ScopedLock sl (callbackLock); + + if (callback != 0) { - case kAudioHardwarePropertyDevices: - intern->deviceDetailsChanged(); - break; + if (audioInputIsAvailable && numInputChannels > 0) + { + short* shortData = (short*) ioData->mBuffers[0].mData; - case kAudioHardwarePropertyDefaultOutputDevice: - case kAudioHardwarePropertyDefaultInputDevice: - case kAudioHardwarePropertyDefaultSystemOutputDevice: - break; + if (numInputChannels >= 2) + { + for (UInt32 i = 0; i < inNumberFrames; ++i) + { + inputChannels[0][i] = *shortData++ * (1.0f / 32768.0f); + inputChannels[1][i] = *shortData++ * (1.0f / 32768.0f); + } + } + else + { + if (monoInputChannelNumber > 0) + ++shortData; + + for (UInt32 i = 0; i < inNumberFrames; ++i) + { + inputChannels[0][i] = *shortData++ * (1.0f / 32768.0f); + ++shortData; + } + } + } + else + { + zeromem (inputChannels[0], sizeof (float) * inNumberFrames); + zeromem (inputChannels[1], sizeof (float) * inNumberFrames); + } + + callback->audioDeviceIOCallback ((const float**) inputChannels, numInputChannels, + outputChannels, numOutputChannels, + (int) inNumberFrames); + + short* shortData = (short*) ioData->mBuffers[0].mData; + int n = 0; + + if (numOutputChannels >= 2) + { + for (UInt32 i = 0; i < inNumberFrames; ++i) + { + shortData [n++] = (short) (outputChannels[0][i] * 32767.0f); + shortData [n++] = (short) (outputChannels[1][i] * 32767.0f); + } + } + else if (numOutputChannels == 1) + { + for (UInt32 i = 0; i < inNumberFrames; ++i) + { + const short s = (short) (outputChannels[monoOutputChannelNumber][i] * 32767.0f); + shortData [n++] = s; + shortData [n++] = s; + } + } + else + { + zeromem (ioData->mBuffers[0].mData, 2 * sizeof (short) * inNumberFrames); + } + } + else + { + zeromem (ioData->mBuffers[0].mData, 2 * sizeof (short) * inNumberFrames); } - return noErr; + return err; } - CoreAudioIODevice (const CoreAudioIODevice&); - const CoreAudioIODevice& operator= (const CoreAudioIODevice&); + void updateDeviceInfo() throw() + { + UInt32 size = sizeof (sampleRate); + AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareSampleRate, &size, &sampleRate); + + size = sizeof (audioInputIsAvailable); + AudioSessionGetProperty (kAudioSessionProperty_AudioInputAvailable, &size, &audioInputIsAvailable); + } + + void propertyChanged (AudioSessionPropertyID inID, UInt32 inDataSize, const void* inPropertyValue) + { + if (! isRunning) + return; + + if (inPropertyValue != 0) + { + CFDictionaryRef routeChangeDictionary = (CFDictionaryRef) inPropertyValue; + CFNumberRef routeChangeReasonRef = (CFNumberRef) CFDictionaryGetValue (routeChangeDictionary, + CFSTR (kAudioSession_AudioRouteChangeKey_Reason)); + + SInt32 routeChangeReason; + CFNumberGetValue (routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason); + + if (routeChangeReason == kAudioSessionRouteChangeReason_OldDeviceUnavailable) + fixAudioRouteIfSetToReceiver(); + } + + updateDeviceInfo(); + createAudioUnit(); + + AudioSessionSetActive (true); + + if (audioUnit != 0) + { + UInt32 formatSize = sizeof (format); + AudioUnitGetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, &formatSize); + + Float32 bufferDuration = preferredBufferSize / sampleRate; + UInt32 bufferDurationSize = sizeof (bufferDuration); + AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareIOBufferDuration, &bufferDurationSize, &bufferDurationSize); + actualBufferSize = (int) (sampleRate * bufferDuration + 0.5); + + AudioOutputUnitStart (audioUnit); + } + } + + void interruptionListener (UInt32 inInterruption) + { + if (inInterruption == kAudioSessionBeginInterruption) + { + isRunning = false; + AudioOutputUnitStop (audioUnit); + + if (juce_iPhoneShowModalAlert ("Audio Interrupted", + "This could have been interrupted by another application or by unplugging a headset", + @"Resume", + @"Cancel")) + { + isRunning = true; + propertyChanged (0, 0, 0); + } + } + + if (inInterruption == kAudioSessionEndInterruption) + { + isRunning = true; + AudioSessionSetActive (true); + AudioOutputUnitStart (audioUnit); + } + } + + static OSStatus processStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) + { + return ((IPhoneAudioIODevice*) inRefCon)->process (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); + } + + static void propertyChangedStatic (void* inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void* inPropertyValue) + { + ((IPhoneAudioIODevice*) inClientData)->propertyChanged (inID, inDataSize, inPropertyValue); + } + + static void interruptionListenerStatic (void* inClientData, UInt32 inInterruption) + { + ((IPhoneAudioIODevice*) inClientData)->interruptionListener (inInterruption); + } + + void resetFormat (const int numChannels) + { + memset (&format, 0, sizeof (format)); + format.mFormatID = kAudioFormatLinearPCM; + format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; + format.mBitsPerChannel = 8 * sizeof (short); + format.mChannelsPerFrame = 2; + format.mFramesPerPacket = 1; + format.mBytesPerFrame = format.mBytesPerPacket = 2 * sizeof (short); + } + + bool createAudioUnit() + { + if (audioUnit != 0) + { + AudioComponentInstanceDispose (audioUnit); + audioUnit = 0; + } + + resetFormat (2); + + AudioComponentDescription desc; + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_RemoteIO; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + AudioComponent comp = AudioComponentFindNext (0, &desc); + AudioComponentInstanceNew (comp, &audioUnit); + + if (audioUnit == 0) + return false; + + const UInt32 one = 1; + AudioUnitSetProperty (audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &one, sizeof (one)); + + AudioChannelLayout layout; + layout.mChannelBitmap = 0; + layout.mNumberChannelDescriptions = 0; + layout.mChannelLayoutTag = kAudioChannelLayoutTag_StereoHeadphones; + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, 0, &layout, sizeof (layout)); + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Output, 0, &layout, sizeof (layout)); + + AURenderCallbackStruct inputProc; + inputProc.inputProc = processStatic; + inputProc.inputProcRefCon = this; + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inputProc, sizeof (inputProc)); + + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof (format)); + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, sizeof (format)); + + AudioUnitInitialize (audioUnit); + return true; + } + + // If the routing is set to go through the receiver (i.e. the speaker, but quiet), this re-routes it + // to make it loud. Needed because by default when using an input + output, the output is kept quiet. + static void fixAudioRouteIfSetToReceiver() throw() + { + CFStringRef audioRoute = 0; + UInt32 propertySize = sizeof (audioRoute); + if (AudioSessionGetProperty (kAudioSessionProperty_AudioRoute, &propertySize, &audioRoute) == noErr) + { + NSString* route = (NSString*) audioRoute; + + //printf ("audio route: %s\n", [route cString]); + + if ([route hasPrefix: @"Receiver"]) + { + UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker; + AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute, sizeof (audioRouteOverride), &audioRouteOverride); + } + + CFRelease (audioRoute); + } + } + + IPhoneAudioIODevice (const IPhoneAudioIODevice&); + const IPhoneAudioIODevice& operator= (const IPhoneAudioIODevice&); }; class IPhoneAudioIODeviceType : public AudioIODeviceType @@ -261393,8 +261718,7 @@ class IPhoneAudioIODeviceType : public AudioIODeviceType public: IPhoneAudioIODeviceType() - : AudioIODeviceType (T("iPhone Audio")), - hasScanned (false) + : AudioIODeviceType (T("iPhone Audio")) { } @@ -261409,6 +261733,7 @@ public: const StringArray getDeviceNames (const bool wantInputNames) const { StringArray s; + s.add (wantInputNames ? "Microphone" : "Speaker"); return s; } @@ -261419,20 +261744,19 @@ public: int getIndexOfDevice (AudioIODevice* device, const bool asInput) const { - return 0; + return device != 0 ? 0 : -1; } - bool hasSeparateInputsAndOutputs() const { return true; } + bool hasSeparateInputsAndOutputs() const { return false; } AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName) { - if (outputDeviceName.isNotEmpty() && inputDeviceName.isNotEmpty()) - return new CoreAudioIODevice (deviceName, - inputIds [inputIndex], - inputIndex, - outputIds [outputIndex], - outputIndex); + if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty()) + { + return new IPhoneAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName + : inputDeviceName); + } return 0; } diff --git a/juce_amalgamated.h b/juce_amalgamated.h index b8bc11592b..17accbaac4 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -39225,7 +39225,7 @@ private: #define __JUCE_GLYPHARRANGEMENT_JUCEHEADER__ /** - An glyph from a particular font, with a particular size, style, + A glyph from a particular font, with a particular size, style, typeface and position. @see GlyphArrangement, Font @@ -47015,6 +47015,10 @@ public: */ virtual int getColumnAutoSizeWidth (int columnId); + /** Returns a tooltip for a particular cell in the table. + */ + virtual const String getCellTooltip (int rowNumber, int columnId); + /** Override this to be informed when rows are selected or deselected. @see ListBox::selectedRowsChanged() @@ -53668,7 +53672,7 @@ private: /********* End of inlined file: juce_ActiveXControlComponent.h *********/ typedef ActiveXControlComponent QTCompBaseClass; -#else +#elif JUCE_MAC typedef NSViewComponent QTCompBaseClass; #endif diff --git a/src/gui/components/controls/juce_TableListBox.cpp b/src/gui/components/controls/juce_TableListBox.cpp index 732199f304..d7f3266b34 100644 --- a/src/gui/components/controls/juce_TableListBox.cpp +++ b/src/gui/components/controls/juce_TableListBox.cpp @@ -38,7 +38,8 @@ BEGIN_JUCE_NAMESPACE //============================================================================== static const tchar* const tableColumnPropertyTag = T("_tableColumnID"); -class TableListRowComp : public Component +class TableListRowComp : public Component, + public TooltipClient { public: TableListRowComp (TableListBox& owner_) @@ -242,6 +243,19 @@ public: owner.getModel()->cellDoubleClicked (row, columnId, e); } + const String getTooltip() + { + int x, y; + getMouseXYRelative (x, y); + + const int columnId = owner.getHeader()->getColumnIdAtX (x); + + if (columnId != 0 && owner.getModel() != 0) + return owner.getModel()->getCellTooltip (row, columnId); + + return String::empty; + } + juce_UseDebuggingNewOperator private: diff --git a/src/gui/components/controls/juce_TableListBox.h b/src/gui/components/controls/juce_TableListBox.h index 2074446e87..9dd4e14f61 100644 --- a/src/gui/components/controls/juce_TableListBox.h +++ b/src/gui/components/controls/juce_TableListBox.h @@ -144,6 +144,10 @@ public: */ virtual int getColumnAutoSizeWidth (int columnId); + /** Returns a tooltip for a particular cell in the table. + */ + virtual const String getCellTooltip (int rowNumber, int columnId); + //============================================================================== /** Override this to be informed when rows are selected or deselected. diff --git a/src/gui/components/controls/juce_TreeView.cpp b/src/gui/components/controls/juce_TreeView.cpp index ebecc3da57..f2f215dc24 100644 --- a/src/gui/components/controls/juce_TreeView.cpp +++ b/src/gui/components/controls/juce_TreeView.cpp @@ -233,8 +233,8 @@ public: comp->setBounds (pos); } } - - if ((! keep) + + if ((! keep) && Component::isMouseButtonDownAnywhere() && (comp == Component::getComponentUnderMouse() || comp->isParentOf (Component::getComponentUnderMouse()))) diff --git a/src/gui/components/menus/juce_MenuBarComponent.cpp b/src/gui/components/menus/juce_MenuBarComponent.cpp index 47437ad2c8..9eb2966453 100644 --- a/src/gui/components/menus/juce_MenuBarComponent.cpp +++ b/src/gui/components/menus/juce_MenuBarComponent.cpp @@ -225,7 +225,7 @@ void MenuBarComponent::showMenu (int index) if (m.lookAndFeel == 0) m.setLookAndFeel (&getLookAndFeel()); - + currentPopup = m.createMenuComponent (x, getScreenY(), w, getHeight(), 0, w, 0, 0, diff --git a/src/gui/components/special/juce_QuickTimeMovieComponent.h b/src/gui/components/special/juce_QuickTimeMovieComponent.h index 11cc1c427e..cf1d45ae8b 100644 --- a/src/gui/components/special/juce_QuickTimeMovieComponent.h +++ b/src/gui/components/special/juce_QuickTimeMovieComponent.h @@ -33,7 +33,7 @@ #if JUCE_WINDOWS #include "juce_ActiveXControlComponent.h" typedef ActiveXControlComponent QTCompBaseClass; -#else +#elif JUCE_MAC #include "juce_NSViewComponent.h" typedef NSViewComponent QTCompBaseClass; #endif diff --git a/src/gui/graphics/fonts/juce_GlyphArrangement.h b/src/gui/graphics/fonts/juce_GlyphArrangement.h index 20e53d49f3..410ab2fd5e 100644 --- a/src/gui/graphics/fonts/juce_GlyphArrangement.h +++ b/src/gui/graphics/fonts/juce_GlyphArrangement.h @@ -32,7 +32,7 @@ //============================================================================== /** - An glyph from a particular font, with a particular size, style, + A glyph from a particular font, with a particular size, style, typeface and position. @see GlyphArrangement, Font diff --git a/src/native/mac/juce_iphone_Audio.cpp b/src/native/mac/juce_iphone_Audio.cpp index 06153b5a7e..46764a4169 100644 --- a/src/native/mac/juce_iphone_Audio.cpp +++ b/src/native/mac/juce_iphone_Audio.cpp @@ -28,39 +28,44 @@ #if JUCE_INCLUDED_FILE - - -class IPhoneAudioIODevice : public AudioIODeviceType +//================================================================================================== +class IPhoneAudioIODevice : public AudioIODevice { public: //============================================================================== - IPhoneAudioIODevice (const String& deviceName, const bool isInput_) + IPhoneAudioIODevice (const String& deviceName) : AudioIODevice (deviceName, T("Audio")), - isInput (isInput_), - isOpen_ (false), - isStarted (false) + audioUnit (0), + isRunning (false), + callback (0), + actualBufferSize (0), + floatData (1, 2) { + numInputChannels = 2; + numOutputChannels = 2; + preferredBufferSize = 0; + + AudioSessionInitialize (0, 0, interruptionListenerStatic, this); + updateDeviceInfo(); } - ~IPhoneAudioIODeviceType() + ~IPhoneAudioIODevice() { + close(); } const StringArray getOutputChannelNames() { StringArray s; - if (! isInput) - { - s.add ("Left"); - s.add ("Right"); - } + s.add ("Left"); + s.add ("Right"); return s; } const StringArray getInputChannelNames() { StringArray s; - if (isInput) + if (audioInputIsAvailable) { s.add ("Left"); s.add ("Right"); @@ -70,137 +75,150 @@ public: int getNumSampleRates() { - return sampleRates.size(); + return 1; } double getSampleRate (int index) { - return sampleRates [index]; + return sampleRate; } int getNumBufferSizesAvailable() { - return bufferSizes.size(); + return 1; } int getBufferSizeSamples (int index) { - return bufferSizes [index]; + return getDefaultBufferSize(); } int getDefaultBufferSize() { - for (int i = 0; i < getNumBufferSizesAvailable(); ++i) - if (getBufferSizeSamples(i) >= 512) - return getBufferSizeSamples(i); - - return 512; + return 1024; } const String open (const BitArray& inputChannels, const BitArray& outputChannels, double sampleRate, - int bufferSizeSamples) + int bufferSize) { - isOpen_ = true; - - if (bufferSizeSamples <= 0) - bufferSizeSamples = getDefaultBufferSize(); + close(); lastError = String::empty; + preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; + + // xxx set up channel mapping + + activeOutputChans = outputChannels; + activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false); + numOutputChannels = activeOutputChans.countNumberOfSetBits(); + monoOutputChannelNumber = activeOutputChans.findNextSetBit (0); + + activeInputChans = inputChannels; + activeInputChans.setRange (2, activeInputChans.getHighestBit(), false); + numInputChannels = activeInputChans.countNumberOfSetBits(); + monoInputChannelNumber = activeInputChans.findNextSetBit (0); + + AudioSessionSetActive (true); + UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord; + AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (audioCategory), &audioCategory); + AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, propertyChangedStatic, this); + fixAudioRouteIfSetToReceiver(); + updateDeviceInfo(); - isOpen_ = lastError.isEmpty(); + Float32 bufferDuration = preferredBufferSize / sampleRate; + AudioSessionSetProperty (kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof (bufferDuration), &bufferDuration); + actualBufferSize = preferredBufferSize; + + prepareFloatBuffers(); + + isRunning = true; + propertyChanged (0, 0, 0); // creates and starts the AU + + lastError = audioUnit != 0 ? String::empty + : T("Couldn't open the device"); return lastError; } void close() { - isOpen_ = false; - - + if (isRunning) + { + isRunning = false; + AudioSessionSetActive (false); + + if (audioUnit != 0) + { + AudioComponentInstanceDispose (audioUnit); + audioUnit = 0; + } + } } bool isOpen() { - return isOpen_; + return isRunning; } int getCurrentBufferSizeSamples() { - return internal != 0 ? internal->getBufferSize() : 512; + return actualBufferSize; } double getCurrentSampleRate() { - return internal != 0 ? internal->getSampleRate() : 0; + return sampleRate; } int getCurrentBitDepth() { - return 32; // no way to find out, so just assume it's high.. + return 16; } const BitArray getActiveOutputChannels() const { - return internal != 0 ? internal->activeOutputChans : BitArray(); + return activeOutputChans; } const BitArray getActiveInputChannels() const { - BitArray chans; - - if (internal != 0) - { - chans = internal->activeInputChans; - - if (internal->inputDevice != 0) - chans.orWith (internal->inputDevice->activeInputChans); - } - - return chans; + return activeInputChans; } 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; + return 0; //xxx } int getInputLatencyInSamples() { - if (internal == 0) - return 0; - - return internal->inputLatency + internal->getBufferSize() * 2; + return 0; //xxx } - void start (AudioIODeviceCallback* callback) + void start (AudioIODeviceCallback* callback_) { - if (internal != 0 && ! isStarted) + if (isRunning && callback != callback_) { - if (callback != 0) - callback->audioDeviceAboutToStart (this); + if (callback_ != 0) + callback_->audioDeviceAboutToStart (this); - isStarted = true; - internal->start (callback); + callbackLock.enter(); + callback = callback_; + callbackLock.exit(); } } void stop() { - if (isStarted && internal != 0) + if (isRunning) { - AudioIODeviceCallback* const lastCallback = internal->callback; - - isStarted = false; - internal->stop (true); + callbackLock.enter(); + AudioIODeviceCallback* const lastCallback = callback; + callback = 0; + callbackLock.exit(); if (lastCallback != 0) lastCallback->audioDeviceStopped(); @@ -209,10 +227,7 @@ public: bool isPlaying() { - if (internal->callback == 0) - isStarted = false; - - return isStarted; + return isRunning && callback != 0; } const String getLastError() @@ -220,36 +235,290 @@ public: return lastError; } - int inputIndex, outputIndex; - - juce_UseDebuggingNewOperator - private: - CoreAudioInternal* internal; - bool isOpen_, isStarted; + //================================================================================================== + CriticalSection callbackLock; + Float64 sampleRate; + int numInputChannels, numOutputChannels; + int preferredBufferSize; + int actualBufferSize; + bool isRunning; String lastError; - static OSStatus hardwareListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, const AudioObjectPropertyAddress* pa, void* inClientData) + AudioStreamBasicDescription format; + AudioUnit audioUnit; + UInt32 audioInputIsAvailable; + AudioIODeviceCallback* callback; + BitArray activeOutputChans, activeInputChans; + + AudioSampleBuffer floatData; + float* inputChannels[3]; + float* outputChannels[3]; + bool monoInputChannelNumber, monoOutputChannelNumber; + + void prepareFloatBuffers() { - CoreAudioInternal* const intern = (CoreAudioInternal*) inClientData; + floatData.setSize (numInputChannels + numOutputChannels, actualBufferSize); + zerostruct (inputChannels); + zerostruct (outputChannels); + + for (int i = 0; i < numInputChannels; ++i) + inputChannels[i] = floatData.getSampleData (i); + + for (int i = 0; i < numOutputChannels; ++i) + outputChannels[i] = floatData.getSampleData (i + numInputChannels); + } + + //================================================================================================== + OSStatus process (AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) throw() + { + OSStatus err = noErr; + + if (audioInputIsAvailable) + err = AudioUnitRender (audioUnit, ioActionFlags, inTimeStamp, 1, inNumberFrames, ioData); + + const ScopedLock sl (callbackLock); - switch (pa->mSelector) + if (callback != 0) { - case kAudioHardwarePropertyDevices: - intern->deviceDetailsChanged(); - break; - - case kAudioHardwarePropertyDefaultOutputDevice: - case kAudioHardwarePropertyDefaultInputDevice: - case kAudioHardwarePropertyDefaultSystemOutputDevice: - break; + if (audioInputIsAvailable && numInputChannels > 0) + { + short* shortData = (short*) ioData->mBuffers[0].mData; + + if (numInputChannels >= 2) + { + for (UInt32 i = 0; i < inNumberFrames; ++i) + { + inputChannels[0][i] = *shortData++ * (1.0f / 32768.0f); + inputChannels[1][i] = *shortData++ * (1.0f / 32768.0f); + } + } + else + { + if (monoInputChannelNumber > 0) + ++shortData; + + for (UInt32 i = 0; i < inNumberFrames; ++i) + { + inputChannels[0][i] = *shortData++ * (1.0f / 32768.0f); + ++shortData; + } + } + } + else + { + zeromem (inputChannels[0], sizeof (float) * inNumberFrames); + zeromem (inputChannels[1], sizeof (float) * inNumberFrames); + } + + callback->audioDeviceIOCallback ((const float**) inputChannels, numInputChannels, + outputChannels, numOutputChannels, + (int) inNumberFrames); + + short* shortData = (short*) ioData->mBuffers[0].mData; + int n = 0; + + if (numOutputChannels >= 2) + { + for (UInt32 i = 0; i < inNumberFrames; ++i) + { + shortData [n++] = (short) (outputChannels[0][i] * 32767.0f); + shortData [n++] = (short) (outputChannels[1][i] * 32767.0f); + } + } + else if (numOutputChannels == 1) + { + for (UInt32 i = 0; i < inNumberFrames; ++i) + { + const short s = (short) (outputChannels[monoOutputChannelNumber][i] * 32767.0f); + shortData [n++] = s; + shortData [n++] = s; + } + } + else + { + zeromem (ioData->mBuffers[0].mData, 2 * sizeof (short) * inNumberFrames); + } + } + else + { + zeromem (ioData->mBuffers[0].mData, 2 * sizeof (short) * inNumberFrames); } - return noErr; + return err; } - CoreAudioIODevice (const CoreAudioIODevice&); - const CoreAudioIODevice& operator= (const CoreAudioIODevice&); + void updateDeviceInfo() throw() + { + UInt32 size = sizeof (sampleRate); + AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareSampleRate, &size, &sampleRate); + + size = sizeof (audioInputIsAvailable); + AudioSessionGetProperty (kAudioSessionProperty_AudioInputAvailable, &size, &audioInputIsAvailable); + } + + void propertyChanged (AudioSessionPropertyID inID, UInt32 inDataSize, const void* inPropertyValue) + { + if (! isRunning) + return; + + if (inPropertyValue != 0) + { + CFDictionaryRef routeChangeDictionary = (CFDictionaryRef) inPropertyValue; + CFNumberRef routeChangeReasonRef = (CFNumberRef) CFDictionaryGetValue (routeChangeDictionary, + CFSTR (kAudioSession_AudioRouteChangeKey_Reason)); + + SInt32 routeChangeReason; + CFNumberGetValue (routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason); + + if (routeChangeReason == kAudioSessionRouteChangeReason_OldDeviceUnavailable) + fixAudioRouteIfSetToReceiver(); + } + + updateDeviceInfo(); + createAudioUnit(); + + AudioSessionSetActive (true); + + if (audioUnit != 0) + { + UInt32 formatSize = sizeof (format); + AudioUnitGetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, &formatSize); + + Float32 bufferDuration = preferredBufferSize / sampleRate; + UInt32 bufferDurationSize = sizeof (bufferDuration); + AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareIOBufferDuration, &bufferDurationSize, &bufferDurationSize); + actualBufferSize = (int) (sampleRate * bufferDuration + 0.5); + + AudioOutputUnitStart (audioUnit); + } + } + + void interruptionListener (UInt32 inInterruption) + { + if (inInterruption == kAudioSessionBeginInterruption) + { + isRunning = false; + AudioOutputUnitStop (audioUnit); + + if (juce_iPhoneShowModalAlert ("Audio Interrupted", + "This could have been interrupted by another application or by unplugging a headset", + @"Resume", + @"Cancel")) + { + isRunning = true; + propertyChanged (0, 0, 0); + } + } + + if (inInterruption == kAudioSessionEndInterruption) + { + isRunning = true; + AudioSessionSetActive (true); + AudioOutputUnitStart (audioUnit); + } + } + + //================================================================================================== + static OSStatus processStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) + { + return ((IPhoneAudioIODevice*) inRefCon)->process (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); + } + + static void propertyChangedStatic (void* inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void* inPropertyValue) + { + ((IPhoneAudioIODevice*) inClientData)->propertyChanged (inID, inDataSize, inPropertyValue); + } + + static void interruptionListenerStatic (void* inClientData, UInt32 inInterruption) + { + ((IPhoneAudioIODevice*) inClientData)->interruptionListener (inInterruption); + } + + //================================================================================================== + void resetFormat (const int numChannels) + { + memset (&format, 0, sizeof (format)); + format.mFormatID = kAudioFormatLinearPCM; + format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; + format.mBitsPerChannel = 8 * sizeof (short); + format.mChannelsPerFrame = 2; + format.mFramesPerPacket = 1; + format.mBytesPerFrame = format.mBytesPerPacket = 2 * sizeof (short); + } + + bool createAudioUnit() + { + if (audioUnit != 0) + { + AudioComponentInstanceDispose (audioUnit); + audioUnit = 0; + } + + resetFormat (2); + + AudioComponentDescription desc; + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_RemoteIO; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + AudioComponent comp = AudioComponentFindNext (0, &desc); + AudioComponentInstanceNew (comp, &audioUnit); + + if (audioUnit == 0) + return false; + + const UInt32 one = 1; + AudioUnitSetProperty (audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &one, sizeof (one)); + + AudioChannelLayout layout; + layout.mChannelBitmap = 0; + layout.mNumberChannelDescriptions = 0; + layout.mChannelLayoutTag = kAudioChannelLayoutTag_StereoHeadphones; + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, 0, &layout, sizeof (layout)); + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Output, 0, &layout, sizeof (layout)); + + + AURenderCallbackStruct inputProc; + inputProc.inputProc = processStatic; + inputProc.inputProcRefCon = this; + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inputProc, sizeof (inputProc)); + + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof (format)); + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, sizeof (format)); + + AudioUnitInitialize (audioUnit); + return true; + } + + // If the routing is set to go through the receiver (i.e. the speaker, but quiet), this re-routes it + // to make it loud. Needed because by default when using an input + output, the output is kept quiet. + static void fixAudioRouteIfSetToReceiver() throw() + { + CFStringRef audioRoute = 0; + UInt32 propertySize = sizeof (audioRoute); + if (AudioSessionGetProperty (kAudioSessionProperty_AudioRoute, &propertySize, &audioRoute) == noErr) + { + NSString* route = (NSString*) audioRoute; + + //printf ("audio route: %s\n", [route cString]); + + if ([route hasPrefix: @"Receiver"]) + { + UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker; + AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute, sizeof (audioRouteOverride), &audioRouteOverride); + } + + CFRelease (audioRoute); + } + } + + IPhoneAudioIODevice (const IPhoneAudioIODevice&); + const IPhoneAudioIODevice& operator= (const IPhoneAudioIODevice&); }; @@ -259,8 +528,7 @@ class IPhoneAudioIODeviceType : public AudioIODeviceType public: //============================================================================== IPhoneAudioIODeviceType() - : AudioIODeviceType (T("iPhone Audio")), - hasScanned (false) + : AudioIODeviceType (T("iPhone Audio")) { } @@ -276,6 +544,7 @@ public: const StringArray getDeviceNames (const bool wantInputNames) const { StringArray s; + s.add (wantInputNames ? "Microphone" : "Speaker"); return s; } @@ -286,20 +555,19 @@ public: int getIndexOfDevice (AudioIODevice* device, const bool asInput) const { - return 0; + return device != 0 ? 0 : -1; } - bool hasSeparateInputsAndOutputs() const { return true; } + bool hasSeparateInputsAndOutputs() const { return false; } AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName) { - if (outputDeviceName.isNotEmpty() && inputDeviceName.isNotEmpty()) - return new CoreAudioIODevice (deviceName, - inputIds [inputIndex], - inputIndex, - outputIds [outputIndex], - outputIndex); + if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty()) + { + return new IPhoneAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName + : inputDeviceName); + } return 0; } diff --git a/src/native/mac/juce_iphone_MiscUtilities.mm b/src/native/mac/juce_iphone_MiscUtilities.mm index aa9d0a91ab..47404ddba6 100644 --- a/src/native/mac/juce_iphone_MiscUtilities.mm +++ b/src/native/mac/juce_iphone_MiscUtilities.mm @@ -95,34 +95,77 @@ void PlatformUtilities::addItemToDock (const File& file) //============================================================================== #if ! JUCE_ONLY_BUILD_CORE_LIBRARY -bool AlertWindow::showNativeDialogBox (const String& title, - const String& bodyText, - bool isOkCancel) +END_JUCE_NAMESPACE + +@interface JuceAlertBoxDelegate : NSObject +{ +@public + bool clickedOk; +} + +- (void) alertView: (UIAlertView*) alertView clickedButtonAtIndex: (NSInteger) buttonIndex; + +@end + +@implementation JuceAlertBoxDelegate + +- (void) alertView: (UIAlertView*) alertView clickedButtonAtIndex: (NSInteger) buttonIndex +{ + clickedOk = (buttonIndex == 0); + alertView.hidden = true; +} + +@end + +BEGIN_JUCE_NAMESPACE + +// (This function is used directly by other bits of code) +bool juce_iPhoneShowModalAlert (const String& title, + const String& bodyText, + NSString* okButtonText, + NSString* cancelButtonText) { const ScopedAutoReleasePool pool; - UIAlertView *alert = [[[UIAlertView alloc] initWithTitle: juceStringToNS (title) - message: juceStringToNS (title) - delegate: nil - cancelButtonTitle: @"OK" - otherButtonTitles: (isOkCancel ? @"Cancel" : nil), nil] autorelease]; - alert.cancelButtonIndex = alert.firstOtherButtonIndex; + JuceAlertBoxDelegate* callback = [[JuceAlertBoxDelegate alloc] init]; + + UIAlertView* alert = [[UIAlertView alloc] initWithTitle: juceStringToNS (title) + message: juceStringToNS (bodyText) + delegate: callback + cancelButtonTitle: okButtonText + otherButtonTitles: cancelButtonText, nil]; + [alert retain]; [alert show]; - // xxx need to use a delegate to find which button was clicked - return false; + while (! alert.hidden && alert.superview != nil) + [[NSRunLoop mainRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; + + const bool result = callback->clickedOk; + [alert release]; + [callback release]; + + return result; +} + +bool AlertWindow::showNativeDialogBox (const String& title, + const String& bodyText, + bool isOkCancel) +{ + return juce_iPhoneShowModalAlert (title, bodyText, + @"OK", + isOkCancel ? @"Cancel" : nil); } //============================================================================== bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles) { - jassertfalse // not implemented! + jassertfalse // no such thing on the iphone! return false; } bool DragAndDropContainer::performExternalDragDropOfText (const String& text) { - jassertfalse // not implemented! + jassertfalse // no such thing on the iphone! return false; } diff --git a/src/native/mac/juce_mac_NativeIncludes.h b/src/native/mac/juce_mac_NativeIncludes.h index a2a2e5441a..64b195b488 100644 --- a/src/native/mac/juce_mac_NativeIncludes.h +++ b/src/native/mac/juce_mac_NativeIncludes.h @@ -74,8 +74,6 @@ #if MACOS_10_4_OR_EARLIER #include - typedef int NSInteger; - typedef unsigned int NSUInteger; #endif #endif // __JUCE_MAC_NATIVEINCLUDES_JUCEHEADER__ diff --git a/src/native/windows/juce_win32_ASIO.cpp b/src/native/windows/juce_win32_ASIO.cpp index 5b5f07cfb1..d15a2b27d0 100644 --- a/src/native/windows/juce_win32_ASIO.cpp +++ b/src/native/windows/juce_win32_ASIO.cpp @@ -1816,11 +1816,12 @@ public: AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName) { + // ASIO can't open two different devices for input and output - they must be the same one. jassert (inputDeviceName == outputDeviceName || outputDeviceName.isEmpty() || inputDeviceName.isEmpty()); - (void) inputDeviceName; jassert (hasScanned); // need to call scanForDevices() before doing this - const int index = deviceNames.indexOf (outputDeviceName); + const int index = deviceNames.indexOf (outputDeviceName.isNotEmpty() ? outputDeviceName + : inputDeviceName); if (index >= 0) {