|
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495 |
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2022 - Raw Material Software Limited
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- The code included in this file is provided under the terms of the ISC license
- http://www.isc.org/downloads/software-support-policy/isc-license. Permission
- To use, copy, modify, and/or distribute this software for any purpose with or
- without fee is hereby granted provided that the above copyright notice and
- this permission notice appear in all copies.
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- #include <juce_audio_basics/native/juce_mac_CoreAudioTimeConversions.h>
-
- namespace juce
- {
-
- class iOSAudioIODevice;
-
- constexpr const char* const iOSAudioDeviceName = "iOS Audio";
-
- #ifndef JUCE_IOS_AUDIO_EXPLICIT_SAMPLERATES
- #define JUCE_IOS_AUDIO_EXPLICIT_SAMPLERATES
- #endif
-
- constexpr std::initializer_list<double> iOSExplicitSampleRates { JUCE_IOS_AUDIO_EXPLICIT_SAMPLERATES };
-
- //==============================================================================
- struct AudioSessionHolder
- {
- AudioSessionHolder();
- ~AudioSessionHolder();
-
- void handleStatusChange (bool enabled, const char* reason) const;
- void handleRouteChange (AVAudioSessionRouteChangeReason reason);
-
- Array<iOSAudioIODevice::Pimpl*> activeDevices;
- Array<iOSAudioIODeviceType*> activeDeviceTypes;
-
- id nativeSession;
- };
-
- static const char* getRoutingChangeReason (AVAudioSessionRouteChangeReason reason) noexcept
- {
- switch (reason)
- {
- case AVAudioSessionRouteChangeReasonNewDeviceAvailable: return "New device available";
- case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: return "Old device unavailable";
- case AVAudioSessionRouteChangeReasonCategoryChange: return "Category change";
- case AVAudioSessionRouteChangeReasonOverride: return "Override";
- case AVAudioSessionRouteChangeReasonWakeFromSleep: return "Wake from sleep";
- case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory: return "No suitable route for category";
- case AVAudioSessionRouteChangeReasonRouteConfigurationChange: return "Route configuration change";
- case AVAudioSessionRouteChangeReasonUnknown:
- default: return "Unknown";
- }
- }
-
- JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes")
-
- bool getNotificationValueForKey (NSNotification* notification, NSString* key, NSUInteger& value) noexcept
- {
- if (notification != nil)
- {
- if (NSDictionary* userInfo = [notification userInfo])
- {
- if (NSNumber* number = [userInfo objectForKey: key])
- {
- value = [number unsignedIntegerValue];
- return true;
- }
- }
- }
-
- jassertfalse;
- return false;
- }
-
- JUCE_END_IGNORE_WARNINGS_GCC_LIKE
-
- } // namespace juce
-
- //==============================================================================
- @interface iOSAudioSessionNative : NSObject
- {
- @private
- juce::AudioSessionHolder* audioSessionHolder;
- };
-
- - (id) init: (juce::AudioSessionHolder*) holder;
- - (void) dealloc;
-
- - (void) audioSessionChangedInterruptionType: (NSNotification*) notification;
- - (void) handleMediaServicesReset;
- - (void) handleMediaServicesLost;
- - (void) handleRouteChange: (NSNotification*) notification;
- @end
-
- @implementation iOSAudioSessionNative
-
- - (id) init: (juce::AudioSessionHolder*) holder
- {
- self = [super init];
-
- if (self != nil)
- {
- audioSessionHolder = holder;
-
- auto session = [AVAudioSession sharedInstance];
- auto centre = [NSNotificationCenter defaultCenter];
-
- [centre addObserver: self
- selector: @selector (audioSessionChangedInterruptionType:)
- name: AVAudioSessionInterruptionNotification
- object: session];
-
- [centre addObserver: self
- selector: @selector (handleMediaServicesLost)
- name: AVAudioSessionMediaServicesWereLostNotification
- object: session];
-
- [centre addObserver: self
- selector: @selector (handleMediaServicesReset)
- name: AVAudioSessionMediaServicesWereResetNotification
- object: session];
-
- [centre addObserver: self
- selector: @selector (handleRouteChange:)
- name: AVAudioSessionRouteChangeNotification
- object: session];
- }
- else
- {
- jassertfalse;
- }
-
- return self;
- }
-
- - (void) dealloc
- {
- [[NSNotificationCenter defaultCenter] removeObserver: self];
- [super dealloc];
- }
-
- - (void) audioSessionChangedInterruptionType: (NSNotification*) notification
- {
- NSUInteger value;
-
- if (juce::getNotificationValueForKey (notification, AVAudioSessionInterruptionTypeKey, value))
- {
- switch ((AVAudioSessionInterruptionType) value)
- {
- case AVAudioSessionInterruptionTypeBegan:
- audioSessionHolder->handleStatusChange (false, "AVAudioSessionInterruptionTypeBegan");
- break;
-
- case AVAudioSessionInterruptionTypeEnded:
- audioSessionHolder->handleStatusChange (true, "AVAudioSessionInterruptionTypeEnded");
- break;
-
- // No default so the code doesn't compile if this enum is extended.
- }
- }
- }
-
- - (void) handleMediaServicesReset
- {
- audioSessionHolder->handleStatusChange (true, "AVAudioSessionMediaServicesWereResetNotification");
- }
-
- - (void) handleMediaServicesLost
- {
- audioSessionHolder->handleStatusChange (false, "AVAudioSessionMediaServicesWereLostNotification");
- }
-
- - (void) handleRouteChange: (NSNotification*) notification
- {
- NSUInteger value;
-
- if (juce::getNotificationValueForKey (notification, AVAudioSessionRouteChangeReasonKey, value))
- audioSessionHolder->handleRouteChange ((AVAudioSessionRouteChangeReason) value);
- }
-
- @end
-
- //==============================================================================
- #if JUCE_MODULE_AVAILABLE_juce_graphics
- #include <juce_graphics/native/juce_mac_CoreGraphicsHelpers.h>
- #endif
-
- namespace juce {
-
- #ifndef JUCE_IOS_AUDIO_LOGGING
- #define JUCE_IOS_AUDIO_LOGGING 0
- #endif
-
- #if JUCE_IOS_AUDIO_LOGGING
- #define JUCE_IOS_AUDIO_LOG(x) DBG(x)
- #else
- #define JUCE_IOS_AUDIO_LOG(x)
- #endif
-
- static void logNSError (NSError* e)
- {
- if (e != nil)
- {
- JUCE_IOS_AUDIO_LOG ("iOS Audio error: " << [e.localizedDescription UTF8String]);
- jassertfalse;
- }
- }
-
- #define JUCE_NSERROR_CHECK(X) { NSError* error = nil; X; logNSError (error); }
-
- //==============================================================================
- class iOSAudioIODeviceType : public AudioIODeviceType,
- public AsyncUpdater
- {
- public:
- iOSAudioIODeviceType();
- ~iOSAudioIODeviceType() override;
-
- void scanForDevices() override;
- StringArray getDeviceNames (bool) const override;
- int getDefaultDeviceIndex (bool) const override;
- int getIndexOfDevice (AudioIODevice*, bool) const override;
- bool hasSeparateInputsAndOutputs() const override;
- AudioIODevice* createDevice (const String&, const String&) override;
-
- private:
- void handleRouteChange (AVAudioSessionRouteChangeReason);
-
- void handleAsyncUpdate() override;
-
- friend struct AudioSessionHolder;
- friend struct iOSAudioIODevice::Pimpl;
-
- SharedResourcePointer<AudioSessionHolder> sessionHolder;
-
- JUCE_DECLARE_WEAK_REFERENCEABLE (iOSAudioIODeviceType)
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSAudioIODeviceType)
- };
-
- //==============================================================================
- struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
- public AsyncUpdater
- {
- Pimpl (iOSAudioIODeviceType* ioDeviceType, iOSAudioIODevice& ioDevice)
- : deviceType (ioDeviceType),
- owner (ioDevice)
- {
- JUCE_IOS_AUDIO_LOG ("Creating iOS audio device");
-
- // We need to activate the audio session here to obtain the available sample rates and buffer sizes,
- // but if we don't set a category first then background audio will always be stopped. This category
- // may be changed later.
- setAudioSessionCategory (AVAudioSessionCategoryPlayAndRecord);
-
- setAudioSessionActive (true);
- updateHardwareInfo();
- channelData.reconfigure ({}, {});
- setAudioSessionActive (false);
-
- sessionHolder->activeDevices.add (this);
- }
-
- ~Pimpl() override
- {
- sessionHolder->activeDevices.removeFirstMatchingValue (this);
-
- close();
- }
-
- static void setAudioSessionCategory (NSString* category)
- {
- NSUInteger options = 0;
-
- #if ! JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS
- options |= AVAudioSessionCategoryOptionMixWithOthers; // Alternatively AVAudioSessionCategoryOptionDuckOthers
- #endif
-
- if (category == AVAudioSessionCategoryPlayAndRecord)
- {
- options |= (AVAudioSessionCategoryOptionDefaultToSpeaker
- | AVAudioSessionCategoryOptionAllowBluetooth);
-
- #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
- if (@available (iOS 10.0, *))
- options |= AVAudioSessionCategoryOptionAllowBluetoothA2DP;
- #endif
- }
-
- JUCE_NSERROR_CHECK ([[AVAudioSession sharedInstance] setCategory: category
- withOptions: options
- error: &error]);
- }
-
- static void setAudioSessionActive (bool enabled)
- {
- JUCE_NSERROR_CHECK ([[AVAudioSession sharedInstance] setActive: enabled
- error: &error]);
- }
-
- int getBufferSize (const double currentSampleRate)
- {
- return roundToInt (currentSampleRate * [AVAudioSession sharedInstance].IOBufferDuration);
- }
-
- int tryBufferSize (const double currentSampleRate, const int newBufferSize)
- {
- NSTimeInterval bufferDuration = currentSampleRate > 0 ? (NSTimeInterval) ((newBufferSize + 1) / currentSampleRate) : 0.0;
-
- auto session = [AVAudioSession sharedInstance];
- JUCE_NSERROR_CHECK ([session setPreferredIOBufferDuration: bufferDuration
- error: &error]);
-
- return getBufferSize (currentSampleRate);
- }
-
- void updateAvailableBufferSizes()
- {
- availableBufferSizes.clear();
-
- auto newBufferSize = tryBufferSize (sampleRate, 64);
- jassert (newBufferSize > 0);
-
- const auto longestBufferSize = tryBufferSize (sampleRate, 4096);
-
- while (newBufferSize <= longestBufferSize)
- {
- availableBufferSizes.add (newBufferSize);
- newBufferSize *= 2;
- }
-
- // Sometimes the largest supported buffer size is not a power of 2
- availableBufferSizes.addIfNotAlreadyThere (longestBufferSize);
-
- bufferSize = tryBufferSize (sampleRate, bufferSize);
-
- #if JUCE_IOS_AUDIO_LOGGING
- {
- String info ("Available buffer sizes:");
-
- for (auto size : availableBufferSizes)
- info << " " << size;
-
- JUCE_IOS_AUDIO_LOG (info);
- }
- #endif
-
- JUCE_IOS_AUDIO_LOG ("Buffer size after detecting available buffer sizes: " << bufferSize);
- }
-
- double trySampleRate (double rate)
- {
- auto session = [AVAudioSession sharedInstance];
- JUCE_NSERROR_CHECK ([session setPreferredSampleRate: rate
- error: &error]);
-
- return session.sampleRate;
- }
-
- // Important: the supported audio sample rates change on the iPhone 6S
- // depending on whether the headphones are plugged in or not!
- void updateAvailableSampleRates()
- {
- if (iOSExplicitSampleRates.size() != 0)
- {
- availableSampleRates = Array<double> (iOSExplicitSampleRates);
- return;
- }
-
- availableSampleRates.clear();
-
- AudioUnitRemovePropertyListenerWithUserData (audioUnit,
- kAudioUnitProperty_StreamFormat,
- dispatchAudioUnitPropertyChange,
- this);
-
- const double lowestRate = trySampleRate (4000);
- availableSampleRates.add (lowestRate);
- const double highestRate = trySampleRate (192000);
-
- JUCE_IOS_AUDIO_LOG ("Lowest supported sample rate: " << lowestRate);
- JUCE_IOS_AUDIO_LOG ("Highest supported sample rate: " << highestRate);
-
- for (double rate = lowestRate + 1000; rate < highestRate; rate += 1000)
- {
- const double supportedRate = trySampleRate (rate);
- JUCE_IOS_AUDIO_LOG ("Trying a sample rate of " << rate << ", got " << supportedRate);
- availableSampleRates.addIfNotAlreadyThere (supportedRate);
- rate = jmax (rate, supportedRate);
- }
-
- availableSampleRates.addIfNotAlreadyThere (highestRate);
-
- // Restore the original values.
- sampleRate = trySampleRate (sampleRate);
- bufferSize = tryBufferSize (sampleRate, bufferSize);
-
- AudioUnitAddPropertyListener (audioUnit,
- kAudioUnitProperty_StreamFormat,
- dispatchAudioUnitPropertyChange,
- this);
-
- // Check the current stream format in case things have changed whilst we
- // were going through the sample rates
- handleStreamFormatChange();
-
- #if JUCE_IOS_AUDIO_LOGGING
- {
- String info ("Available sample rates:");
-
- for (auto rate : availableSampleRates)
- info << " " << rate;
-
- JUCE_IOS_AUDIO_LOG (info);
- }
- #endif
-
- JUCE_IOS_AUDIO_LOG ("Sample rate after detecting available sample rates: " << sampleRate);
- }
-
- void updateHardwareInfo (bool forceUpdate = false)
- {
- if (! forceUpdate && ! hardwareInfoNeedsUpdating.compareAndSetBool (false, true))
- return;
-
- JUCE_IOS_AUDIO_LOG ("Updating hardware info");
-
- updateAvailableSampleRates();
- updateAvailableBufferSizes();
-
- if (deviceType != nullptr)
- deviceType->callDeviceChangeListeners();
- }
-
- void setTargetSampleRateAndBufferSize()
- {
- JUCE_IOS_AUDIO_LOG ("Setting target sample rate: " << targetSampleRate);
- sampleRate = trySampleRate (targetSampleRate);
- JUCE_IOS_AUDIO_LOG ("Actual sample rate: " << sampleRate);
-
- JUCE_IOS_AUDIO_LOG ("Setting target buffer size: " << targetBufferSize);
- bufferSize = tryBufferSize (sampleRate, targetBufferSize);
- JUCE_IOS_AUDIO_LOG ("Actual buffer size: " << bufferSize);
- }
-
- String open (const BigInteger& inputChannelsWanted,
- const BigInteger& outputChannelsWanted,
- double sampleRateWanted, int bufferSizeWanted)
- {
- close();
-
- firstHostTime = true;
- lastNumFrames = 0;
- xrun = 0;
- lastError.clear();
-
- requestedInputChannels = inputChannelsWanted;
- requestedOutputChannels = outputChannelsWanted;
- targetSampleRate = sampleRateWanted;
- targetBufferSize = bufferSizeWanted > 0 ? bufferSizeWanted : defaultBufferSize;
-
- JUCE_IOS_AUDIO_LOG ("Opening audio device:"
- << " inputChannelsWanted: " << requestedInputChannels .toString (2)
- << ", outputChannelsWanted: " << requestedOutputChannels.toString (2)
- << ", targetSampleRate: " << targetSampleRate
- << ", targetBufferSize: " << targetBufferSize);
-
- setAudioSessionActive (true);
- setAudioSessionCategory (requestedInputChannels > 0 ? AVAudioSessionCategoryPlayAndRecord
- : AVAudioSessionCategoryPlayback);
- channelData.reconfigure (requestedInputChannels, requestedOutputChannels);
- updateHardwareInfo (true);
- setTargetSampleRateAndBufferSize();
- fixAudioRouteIfSetToReceiver();
-
- isRunning = true;
-
- if (! createAudioUnit())
- {
- lastError = "Couldn't open the device";
- return lastError;
- }
-
- const ScopedLock sl (callbackLock);
-
- AudioOutputUnitStart (audioUnit);
-
- if (callback != nullptr)
- callback->audioDeviceAboutToStart (&owner);
-
- return lastError;
- }
-
- void close()
- {
- stop();
-
- if (isRunning)
- {
- isRunning = false;
-
- if (audioUnit != nullptr)
- {
- AudioOutputUnitStart (audioUnit);
- AudioComponentInstanceDispose (audioUnit);
- audioUnit = nullptr;
- }
-
- setAudioSessionActive (false);
- }
- }
-
- void start (AudioIODeviceCallback* newCallback)
- {
- if (isRunning && callback != newCallback)
- {
- if (newCallback != nullptr)
- newCallback->audioDeviceAboutToStart (&owner);
-
- const ScopedLock sl (callbackLock);
- callback = newCallback;
- }
- }
-
- void stop()
- {
- if (isRunning)
- {
- AudioIODeviceCallback* lastCallback;
-
- {
- const ScopedLock sl (callbackLock);
- lastCallback = callback;
- callback = nullptr;
- }
-
- if (lastCallback != nullptr)
- lastCallback->audioDeviceStopped();
- }
- }
-
- bool setAudioPreprocessingEnabled (bool enable)
- {
- auto session = [AVAudioSession sharedInstance];
-
- NSString* mode = (enable ? AVAudioSessionModeDefault
- : AVAudioSessionModeMeasurement);
-
- JUCE_NSERROR_CHECK ([session setMode: mode
- error: &error]);
-
- return session.mode == mode;
- }
-
- //==============================================================================
- bool canControlTransport() override { return interAppAudioConnected; }
-
- void transportPlay (bool shouldSartPlaying) override
- {
- if (! canControlTransport())
- return;
-
- HostCallbackInfo callbackInfo;
- fillHostCallbackInfo (callbackInfo);
-
- Boolean hostIsPlaying = NO;
- OSStatus err = callbackInfo.transportStateProc2 (callbackInfo.hostUserData,
- &hostIsPlaying,
- nullptr,
- nullptr,
- nullptr,
- nullptr,
- nullptr,
- nullptr);
-
- ignoreUnused (err);
- jassert (err == noErr);
-
- if (hostIsPlaying != shouldSartPlaying)
- handleAudioTransportEvent (kAudioUnitRemoteControlEvent_TogglePlayPause);
- }
-
- void transportRecord (bool shouldStartRecording) override
- {
- if (! canControlTransport())
- return;
-
- HostCallbackInfo callbackInfo;
- fillHostCallbackInfo (callbackInfo);
-
- Boolean hostIsRecording = NO;
- OSStatus err = callbackInfo.transportStateProc2 (callbackInfo.hostUserData,
- nullptr,
- &hostIsRecording,
- nullptr,
- nullptr,
- nullptr,
- nullptr,
- nullptr);
- ignoreUnused (err);
- jassert (err == noErr);
-
- if (hostIsRecording != shouldStartRecording)
- handleAudioTransportEvent (kAudioUnitRemoteControlEvent_ToggleRecord);
- }
-
- void transportRewind() override
- {
- if (canControlTransport())
- handleAudioTransportEvent (kAudioUnitRemoteControlEvent_Rewind);
- }
-
- bool getCurrentPosition (CurrentPositionInfo& result) override
- {
- if (! canControlTransport())
- return false;
-
- zerostruct (result);
-
- HostCallbackInfo callbackInfo;
- fillHostCallbackInfo (callbackInfo);
-
- if (callbackInfo.hostUserData == nullptr)
- return false;
-
- Boolean hostIsPlaying = NO;
- Boolean hostIsRecording = NO;
- Float64 hostCurrentSampleInTimeLine = 0;
- Boolean hostIsCycling = NO;
- Float64 hostCycleStartBeat = 0;
- Float64 hostCycleEndBeat = 0;
- OSStatus err = callbackInfo.transportStateProc2 (callbackInfo.hostUserData,
- &hostIsPlaying,
- &hostIsRecording,
- nullptr,
- &hostCurrentSampleInTimeLine,
- &hostIsCycling,
- &hostCycleStartBeat,
- &hostCycleEndBeat);
- if (err == kAUGraphErr_CannotDoInCurrentContext)
- return false;
-
- jassert (err == noErr);
-
- result.timeInSamples = (int64) hostCurrentSampleInTimeLine;
- result.isPlaying = hostIsPlaying;
- result.isRecording = hostIsRecording;
- result.isLooping = hostIsCycling;
- result.ppqLoopStart = hostCycleStartBeat;
- result.ppqLoopEnd = hostCycleEndBeat;
-
- result.timeInSeconds = result.timeInSamples / sampleRate;
-
- Float64 hostBeat = 0;
- Float64 hostTempo = 0;
- err = callbackInfo.beatAndTempoProc (callbackInfo.hostUserData,
- &hostBeat,
- &hostTempo);
- jassert (err == noErr);
-
- result.ppqPosition = hostBeat;
- result.bpm = hostTempo;
-
- Float32 hostTimeSigNumerator = 0;
- UInt32 hostTimeSigDenominator = 0;
- Float64 hostCurrentMeasureDownBeat = 0;
- err = callbackInfo.musicalTimeLocationProc (callbackInfo.hostUserData,
- nullptr,
- &hostTimeSigNumerator,
- &hostTimeSigDenominator,
- &hostCurrentMeasureDownBeat);
- jassert (err == noErr);
-
- result.ppqPositionOfLastBarStart = hostCurrentMeasureDownBeat;
- result.timeSigNumerator = (int) hostTimeSigNumerator;
- result.timeSigDenominator = (int) hostTimeSigDenominator;
-
- result.frameRate = AudioPlayHead::fpsUnknown;
-
- return true;
- }
-
- //==============================================================================
- #if JUCE_MODULE_AVAILABLE_juce_graphics
- JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
- Image getIcon (int size)
- {
- if (interAppAudioConnected)
- {
- UIImage* hostUIImage = AudioOutputUnitGetHostIcon (audioUnit, size);
- if (hostUIImage != nullptr)
- return juce_createImageFromUIImage (hostUIImage);
- }
- return Image();
- }
- JUCE_END_IGNORE_WARNINGS_GCC_LIKE
- #endif
-
- void switchApplication()
- {
- if (! interAppAudioConnected)
- return;
-
- CFURLRef hostUrl;
- UInt32 dataSize = sizeof (hostUrl);
- OSStatus err = AudioUnitGetProperty(audioUnit,
- kAudioUnitProperty_PeerURL,
- kAudioUnitScope_Global,
- 0,
- &hostUrl,
- &dataSize);
- if (err == noErr)
- {
- #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
- if (@available (iOS 10.0, *))
- {
- [[UIApplication sharedApplication] openURL: (NSURL*) hostUrl
- options: @{}
- completionHandler: nil];
-
- return;
- }
- #endif
-
- JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
- [[UIApplication sharedApplication] openURL: (NSURL*) hostUrl];
- JUCE_END_IGNORE_WARNINGS_GCC_LIKE
- }
- }
-
- //==============================================================================
- void invokeAudioDeviceErrorCallback (const String& reason)
- {
- const ScopedLock sl (callbackLock);
-
- if (callback != nullptr)
- callback->audioDeviceError (reason);
- }
-
- void handleStatusChange (bool enabled, const char* reason)
- {
- const ScopedLock myScopedLock (callbackLock);
-
- JUCE_IOS_AUDIO_LOG ("handleStatusChange: enabled: " << (int) enabled << ", reason: " << reason);
-
- isRunning = enabled;
- setAudioSessionActive (enabled);
-
- if (enabled)
- AudioOutputUnitStart (audioUnit);
- else
- AudioOutputUnitStop (audioUnit);
-
- if (! enabled)
- invokeAudioDeviceErrorCallback (reason);
- }
-
- void handleRouteChange (AVAudioSessionRouteChangeReason reason)
- {
- const ScopedLock myScopedLock (callbackLock);
-
- const String reasonString (getRoutingChangeReason (reason));
- JUCE_IOS_AUDIO_LOG ("handleRouteChange: " << reasonString);
-
- if (isRunning)
- invokeAudioDeviceErrorCallback (reasonString);
-
- switch (reason)
- {
- case AVAudioSessionRouteChangeReasonCategoryChange:
- case AVAudioSessionRouteChangeReasonOverride:
- case AVAudioSessionRouteChangeReasonRouteConfigurationChange:
- break;
- case AVAudioSessionRouteChangeReasonUnknown:
- case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
- case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
- case AVAudioSessionRouteChangeReasonWakeFromSleep:
- case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
- {
- hardwareInfoNeedsUpdating = true;
- triggerAsyncUpdate();
- break;
- }
-
- // No default so the code doesn't compile if this enum is extended.
- }
- }
-
- void handleAudioUnitPropertyChange (AudioUnit,
- AudioUnitPropertyID propertyID,
- AudioUnitScope scope,
- AudioUnitElement element)
- {
- ignoreUnused (scope);
- ignoreUnused (element);
- JUCE_IOS_AUDIO_LOG ("handleAudioUnitPropertyChange: propertyID: " << String (propertyID)
- << " scope: " << String (scope)
- << " element: " << String (element));
-
- switch (propertyID)
- {
- case kAudioUnitProperty_IsInterAppConnected:
- handleInterAppAudioConnectionChange();
- return;
- case kAudioUnitProperty_StreamFormat:
- handleStreamFormatChange();
- return;
- default:
- jassertfalse;
- }
- }
-
- void handleInterAppAudioConnectionChange()
- {
- UInt32 connected;
- UInt32 dataSize = sizeof (connected);
- OSStatus err = AudioUnitGetProperty (audioUnit, kAudioUnitProperty_IsInterAppConnected,
- kAudioUnitScope_Global, 0, &connected, &dataSize);
- ignoreUnused (err);
- jassert (err == noErr);
-
- JUCE_IOS_AUDIO_LOG ("handleInterAppAudioConnectionChange: " << (connected ? "connected"
- : "disconnected"));
-
- if (connected != interAppAudioConnected)
- {
- const ScopedLock myScopedLock (callbackLock);
-
- interAppAudioConnected = connected;
-
- UIApplicationState appstate = [UIApplication sharedApplication].applicationState;
- bool inForeground = (appstate != UIApplicationStateBackground);
-
- if (interAppAudioConnected || inForeground)
- {
- setAudioSessionActive (true);
- AudioOutputUnitStart (audioUnit);
-
- if (callback != nullptr)
- callback->audioDeviceAboutToStart (&owner);
- }
- else if (! inForeground)
- {
- AudioOutputUnitStop (audioUnit);
- setAudioSessionActive (false);
-
- if (callback != nullptr)
- callback->audioDeviceStopped();
- }
- }
- }
-
- //==============================================================================
- OSStatus process (AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time,
- const UInt32 numFrames, AudioBufferList* data)
- {
- OSStatus err = noErr;
-
- recordXruns (time, numFrames);
-
- const bool useInput = channelData.areInputChannelsAvailable();
-
- if (useInput)
- err = AudioUnitRender (audioUnit, flags, time, 1, numFrames, data);
-
- const auto channelDataSize = sizeof (float) * numFrames;
-
- const ScopedTryLock stl (callbackLock);
-
- if (stl.isLocked() && callback != nullptr)
- {
- if ((int) numFrames > channelData.getFloatBufferSize())
- channelData.setFloatBufferSize ((int) numFrames);
-
- float** const inputData = channelData.audioData.getArrayOfWritePointers();
- float** const outputData = inputData + channelData.inputs->numActiveChannels;
-
- if (useInput)
- {
- for (int c = 0; c < channelData.inputs->numActiveChannels; ++c)
- {
- auto channelIndex = channelData.inputs->activeChannelIndices[c];
- memcpy (inputData[c], (float*) data->mBuffers[channelIndex].mData, channelDataSize);
- }
- }
- else
- {
- for (int c = 0; c < channelData.inputs->numActiveChannels; ++c)
- zeromem (inputData[c], channelDataSize);
- }
-
- const auto nanos = time != nullptr ? timeConversions.hostTimeToNanos (time->mHostTime) : 0;
-
- callback->audioDeviceIOCallbackWithContext ((const float**) inputData,
- channelData.inputs ->numActiveChannels,
- outputData,
- channelData.outputs->numActiveChannels,
- (int) numFrames,
- { (time != nullptr && (time->mFlags & kAudioTimeStampHostTimeValid) != 0) ? &nanos : nullptr });
-
- for (int c = 0; c < channelData.outputs->numActiveChannels; ++c)
- {
- auto channelIndex = channelData.outputs->activeChannelIndices[c];
- memcpy (data->mBuffers[channelIndex].mData, outputData[c], channelDataSize);
- }
-
- for (auto c : channelData.outputs->inactiveChannelIndices)
- zeromem (data->mBuffers[c].mData, channelDataSize);
- }
- else
- {
- for (uint32 c = 0; c < data->mNumberBuffers; ++c)
- zeromem (data->mBuffers[c].mData, channelDataSize);
- }
-
- return err;
- }
-
- void recordXruns (const AudioTimeStamp* time, UInt32 numFrames)
- {
- if (time != nullptr && (time->mFlags & kAudioTimeStampSampleTimeValid) != 0)
- {
- if (! firstHostTime)
- {
- if ((time->mSampleTime - lastSampleTime) != lastNumFrames)
- xrun++;
- }
- else
- firstHostTime = false;
-
- lastSampleTime = time->mSampleTime;
- }
- else
- firstHostTime = true;
-
- lastNumFrames = numFrames;
- }
-
- //==============================================================================
- static OSStatus processStatic (void* client, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time,
- UInt32 /*busNumber*/, UInt32 numFrames, AudioBufferList* data)
- {
- return static_cast<Pimpl*> (client)->process (flags, time, numFrames, data);
- }
-
- //==============================================================================
- bool createAudioUnit()
- {
- JUCE_IOS_AUDIO_LOG ("Creating the audio unit");
-
- if (audioUnit != nullptr)
- {
- AudioComponentInstanceDispose (audioUnit);
- audioUnit = nullptr;
- }
-
- AudioComponentDescription desc;
- desc.componentType = kAudioUnitType_Output;
- desc.componentSubType = kAudioUnitSubType_RemoteIO;
- desc.componentManufacturer = kAudioUnitManufacturer_Apple;
- desc.componentFlags = 0;
- desc.componentFlagsMask = 0;
-
- AudioComponent comp = AudioComponentFindNext (nullptr, &desc);
- AudioComponentInstanceNew (comp, &audioUnit);
-
- if (audioUnit == nullptr)
- return false;
-
- #if JucePlugin_Enable_IAA
- AudioComponentDescription appDesc;
- appDesc.componentType = JucePlugin_IAAType;
- appDesc.componentSubType = JucePlugin_IAASubType;
- appDesc.componentManufacturer = JucePlugin_ManufacturerCode;
- appDesc.componentFlags = 0;
- appDesc.componentFlagsMask = 0;
- OSStatus err = AudioOutputUnitPublish (&appDesc,
- CFSTR(JucePlugin_IAAName),
- JucePlugin_VersionCode,
- audioUnit);
-
- // This assert will be hit if the Inter-App Audio entitlement has not
- // been enabled, or the description being published with
- // AudioOutputUnitPublish is different from any in the AudioComponents
- // array in this application's .plist file.
- jassert (err == noErr);
-
- err = AudioUnitAddPropertyListener (audioUnit,
- kAudioUnitProperty_IsInterAppConnected,
- dispatchAudioUnitPropertyChange,
- this);
- jassert (err == noErr);
-
- AudioOutputUnitMIDICallbacks midiCallbacks;
- midiCallbacks.userData = this;
- midiCallbacks.MIDIEventProc = midiEventCallback;
- midiCallbacks.MIDISysExProc = midiSysExCallback;
- err = AudioUnitSetProperty (audioUnit,
- kAudioOutputUnitProperty_MIDICallbacks,
- kAudioUnitScope_Global,
- 0,
- &midiCallbacks,
- sizeof (midiCallbacks));
- jassert (err == noErr);
- #endif
-
- if (channelData.areInputChannelsAvailable())
- {
- const UInt32 one = 1;
- AudioUnitSetProperty (audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &one, sizeof (one));
- }
-
- {
- AURenderCallbackStruct inputProc;
- inputProc.inputProc = processStatic;
- inputProc.inputProcRefCon = this;
- AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inputProc, sizeof (inputProc));
- }
-
- {
- AudioStreamBasicDescription format;
- zerostruct (format);
- format.mSampleRate = sampleRate;
- format.mFormatID = kAudioFormatLinearPCM;
- format.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved | kAudioFormatFlagsNativeEndian | kLinearPCMFormatFlagIsPacked;
- format.mBitsPerChannel = 8 * sizeof (float);
- format.mFramesPerPacket = 1;
- format.mChannelsPerFrame = (UInt32) jmax (channelData.inputs->numHardwareChannels, channelData.outputs->numHardwareChannels);
- format.mBytesPerFrame = format.mBytesPerPacket = sizeof (float);
-
- AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof (format));
- AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, sizeof (format));
- }
-
- AudioUnitInitialize (audioUnit);
-
- {
- // Querying the kAudioUnitProperty_MaximumFramesPerSlice property after calling AudioUnitInitialize
- // seems to be more reliable than calling it before.
- UInt32 framesPerSlice, dataSize = sizeof (framesPerSlice);
-
- if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_MaximumFramesPerSlice,
- kAudioUnitScope_Global, 0, &framesPerSlice, &dataSize) == noErr
- && dataSize == sizeof (framesPerSlice)
- && static_cast<int> (framesPerSlice) != bufferSize)
- {
- JUCE_IOS_AUDIO_LOG ("Internal buffer size: " << String (framesPerSlice));
- channelData.setFloatBufferSize (static_cast<int> (framesPerSlice));
- }
- }
-
- AudioUnitAddPropertyListener (audioUnit, kAudioUnitProperty_StreamFormat, dispatchAudioUnitPropertyChange, this);
-
- return true;
- }
-
- void fillHostCallbackInfo (HostCallbackInfo& callbackInfo)
- {
- zerostruct (callbackInfo);
- UInt32 dataSize = sizeof (HostCallbackInfo);
- OSStatus err = AudioUnitGetProperty (audioUnit,
- kAudioUnitProperty_HostCallbacks,
- kAudioUnitScope_Global,
- 0,
- &callbackInfo,
- &dataSize);
- ignoreUnused (err);
- jassert (err == noErr);
- }
-
- void handleAudioTransportEvent (AudioUnitRemoteControlEvent event)
- {
- OSStatus err = AudioUnitSetProperty (audioUnit, kAudioOutputUnitProperty_RemoteControlToHost,
- kAudioUnitScope_Global, 0, &event, sizeof (event));
- ignoreUnused (err);
- jassert (err == noErr);
- }
-
- // 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()
- {
- auto session = [AVAudioSession sharedInstance];
- auto route = session.currentRoute;
-
- for (AVAudioSessionPortDescription* port in route.outputs)
- {
- if ([port.portName isEqualToString: @"Receiver"])
- {
- JUCE_NSERROR_CHECK ([session overrideOutputAudioPort: AVAudioSessionPortOverrideSpeaker
- error: &error]);
- setAudioSessionActive (true);
- }
- }
- }
-
- void restart()
- {
- const ScopedLock sl (callbackLock);
-
- updateHardwareInfo();
- setTargetSampleRateAndBufferSize();
-
- if (isRunning)
- {
- if (audioUnit != nullptr)
- {
- AudioComponentInstanceDispose (audioUnit);
- audioUnit = nullptr;
-
- if (callback != nullptr)
- callback->audioDeviceStopped();
- }
-
- channelData.reconfigure (requestedInputChannels, requestedOutputChannels);
-
- createAudioUnit();
-
- if (audioUnit != nullptr)
- {
- isRunning = true;
-
- if (callback != nullptr)
- callback->audioDeviceAboutToStart (&owner);
-
- AudioOutputUnitStart (audioUnit);
- }
- }
- }
-
- void handleAsyncUpdate() override
- {
- restart();
- }
-
- void handleStreamFormatChange()
- {
- AudioStreamBasicDescription desc;
- zerostruct (desc);
- UInt32 dataSize = sizeof (desc);
- AudioUnitGetProperty (audioUnit,
- kAudioUnitProperty_StreamFormat,
- kAudioUnitScope_Output,
- 0,
- &desc,
- &dataSize);
-
- if (desc.mSampleRate != 0 && desc.mSampleRate != sampleRate)
- {
- JUCE_IOS_AUDIO_LOG ("Stream format has changed: Sample rate " << desc.mSampleRate);
- triggerAsyncUpdate();
- }
- }
-
- static void dispatchAudioUnitPropertyChange (void* data, AudioUnit unit, AudioUnitPropertyID propertyID,
- AudioUnitScope scope, AudioUnitElement element)
- {
- static_cast<Pimpl*> (data)->handleAudioUnitPropertyChange (unit, propertyID, scope, element);
- }
-
- static double getTimestampForMIDI()
- {
- return Time::getMillisecondCounter() / 1000.0;
- }
-
- static void midiEventCallback (void *client, UInt32 status, UInt32 data1, UInt32 data2, UInt32)
- {
- return static_cast<Pimpl*> (client)->handleMidiMessage (MidiMessage ((int) status,
- (int) data1,
- (int) data2,
- getTimestampForMIDI()));
- }
-
- static void midiSysExCallback (void *client, const UInt8 *data, UInt32 length)
- {
- return static_cast<Pimpl*> (client)->handleMidiMessage (MidiMessage (data, (int) length, getTimestampForMIDI()));
- }
-
- void handleMidiMessage (MidiMessage msg)
- {
- if (messageCollector != nullptr)
- messageCollector->addMessageToQueue (msg);
- }
-
- struct IOChannelData
- {
- class IOChannelConfig
- {
- public:
- IOChannelConfig (const bool isInput, const BigInteger requiredChannels)
- : hardwareChannelNames (getHardwareChannelNames (isInput)),
- numHardwareChannels (hardwareChannelNames.size()),
- areChannelsAccessible ((! isInput) || [AVAudioSession sharedInstance].isInputAvailable),
- activeChannels (limitRequiredChannelsToHardware (numHardwareChannels, requiredChannels)),
- numActiveChannels (activeChannels.countNumberOfSetBits()),
- activeChannelIndices (getActiveChannelIndices (activeChannels)),
- inactiveChannelIndices (getInactiveChannelIndices (activeChannelIndices, numHardwareChannels))
- {
- #if JUCE_IOS_AUDIO_LOGGING
- {
- String info;
-
- info << "Number of hardware channels: " << numHardwareChannels
- << ", Hardware channel names:";
-
- for (auto& name : hardwareChannelNames)
- info << " \"" << name << "\"";
-
- info << ", Are channels available: " << (areChannelsAccessible ? "yes" : "no")
- << ", Active channel indices:";
-
- for (auto i : activeChannelIndices)
- info << " " << i;
-
- info << ", Inactive channel indices:";
-
- for (auto i : inactiveChannelIndices)
- info << " " << i;
-
- JUCE_IOS_AUDIO_LOG ((isInput ? "Input" : "Output") << " channel configuration: {" << info << "}");
- }
- #endif
- }
-
- const StringArray hardwareChannelNames;
- const int numHardwareChannels;
- const bool areChannelsAccessible;
-
- const BigInteger activeChannels;
- const int numActiveChannels;
-
- const Array<int> activeChannelIndices, inactiveChannelIndices;
-
- private:
- static StringArray getHardwareChannelNames (const bool isInput)
- {
- StringArray result;
-
- auto route = [AVAudioSession sharedInstance].currentRoute;
-
- for (AVAudioSessionPortDescription* port in (isInput ? route.inputs : route.outputs))
- {
- for (AVAudioSessionChannelDescription* desc in port.channels)
- result.add (nsStringToJuce (desc.channelName));
- }
-
- // A fallback for the iOS simulator and older iOS versions
- if (result.isEmpty())
- return { "Left", "Right" };
-
- return result;
- }
-
- static BigInteger limitRequiredChannelsToHardware (const int numHardwareChannelsAvailable,
- BigInteger requiredChannels)
- {
- requiredChannels.setRange (numHardwareChannelsAvailable,
- requiredChannels.getHighestBit() + 1,
- false);
-
- return requiredChannels;
- }
-
- static Array<int> getActiveChannelIndices (const BigInteger activeChannelsToIndex)
- {
- Array<int> result;
-
- auto index = activeChannelsToIndex.findNextSetBit (0);
-
- while (index != -1)
- {
- result.add (index);
- index = activeChannelsToIndex.findNextSetBit (++index);
- }
-
- return result;
- }
-
- static Array<int> getInactiveChannelIndices (const Array<int>& activeIndices, int numChannels)
- {
- Array<int> result;
-
- auto nextActiveChannel = activeIndices.begin();
-
- for (int i = 0; i < numChannels; ++i)
- if (nextActiveChannel != activeIndices.end() && i == *nextActiveChannel)
- ++nextActiveChannel;
- else
- result.add (i);
-
- return result;
- }
- };
-
- void reconfigure (const BigInteger requiredInputChannels,
- const BigInteger requiredOutputChannels)
- {
- inputs .reset (new IOChannelConfig (true, requiredInputChannels));
- outputs.reset (new IOChannelConfig (false, requiredOutputChannels));
-
- audioData.setSize (inputs->numActiveChannels + outputs->numActiveChannels,
- audioData.getNumSamples());
- }
-
- int getFloatBufferSize() const
- {
- return audioData.getNumSamples();
- }
-
- void setFloatBufferSize (const int newSize)
- {
- audioData.setSize (audioData.getNumChannels(), newSize);
- }
-
- bool areInputChannelsAvailable() const
- {
- return inputs->areChannelsAccessible && inputs->numActiveChannels > 0;
- }
-
- std::unique_ptr<IOChannelConfig> inputs;
- std::unique_ptr<IOChannelConfig> outputs;
-
- AudioBuffer<float> audioData { 0, 0 };
- };
-
- CoreAudioTimeConversions timeConversions;
-
- IOChannelData channelData;
-
- BigInteger requestedInputChannels, requestedOutputChannels;
-
- bool isRunning = false;
-
- AudioIODeviceCallback* callback = nullptr;
-
- String lastError;
-
- #if TARGET_IPHONE_SIMULATOR
- static constexpr int defaultBufferSize = 512;
- #else
- static constexpr int defaultBufferSize = 256;
- #endif
- int targetBufferSize = defaultBufferSize, bufferSize = targetBufferSize;
-
- double targetSampleRate = 44100.0, sampleRate = targetSampleRate;
-
- Array<double> availableSampleRates;
- Array<int> availableBufferSizes;
-
- bool interAppAudioConnected = false;
-
- MidiMessageCollector* messageCollector = nullptr;
-
- WeakReference<iOSAudioIODeviceType> deviceType;
- iOSAudioIODevice& owner;
-
- CriticalSection callbackLock;
-
- Atomic<bool> hardwareInfoNeedsUpdating { true };
-
- AudioUnit audioUnit {};
-
- SharedResourcePointer<AudioSessionHolder> sessionHolder;
-
- bool firstHostTime;
- Float64 lastSampleTime;
- unsigned int lastNumFrames;
- int xrun;
-
- JUCE_DECLARE_NON_COPYABLE (Pimpl)
- };
-
- //==============================================================================
- iOSAudioIODevice::iOSAudioIODevice (iOSAudioIODeviceType* ioDeviceType, const String&, const String&)
- : AudioIODevice (iOSAudioDeviceName, iOSAudioDeviceName),
- pimpl (new Pimpl (ioDeviceType, *this))
- {
- }
-
- //==============================================================================
- String iOSAudioIODevice::open (const BigInteger& inChans, const BigInteger& outChans,
- double requestedSampleRate, int requestedBufferSize)
- {
- return pimpl->open (inChans, outChans, requestedSampleRate, requestedBufferSize);
- }
-
- void iOSAudioIODevice::close() { pimpl->close(); }
-
- void iOSAudioIODevice::start (AudioIODeviceCallback* callbackToUse) { pimpl->start (callbackToUse); }
- void iOSAudioIODevice::stop() { pimpl->stop(); }
-
- Array<double> iOSAudioIODevice::getAvailableSampleRates() { return pimpl->availableSampleRates; }
- Array<int> iOSAudioIODevice::getAvailableBufferSizes() { return pimpl->availableBufferSizes; }
-
- bool iOSAudioIODevice::setAudioPreprocessingEnabled (bool enabled) { return pimpl->setAudioPreprocessingEnabled (enabled); }
-
- bool iOSAudioIODevice::isPlaying() { return pimpl->isRunning && pimpl->callback != nullptr; }
- bool iOSAudioIODevice::isOpen() { return pimpl->isRunning; }
- String iOSAudioIODevice::getLastError() { return pimpl->lastError; }
-
- StringArray iOSAudioIODevice::getOutputChannelNames() { return pimpl->channelData.outputs->hardwareChannelNames; }
- StringArray iOSAudioIODevice::getInputChannelNames() { return pimpl->channelData.inputs->areChannelsAccessible ? pimpl->channelData.inputs->hardwareChannelNames : StringArray(); }
-
- int iOSAudioIODevice::getDefaultBufferSize() { return pimpl->defaultBufferSize; }
- int iOSAudioIODevice::getCurrentBufferSizeSamples() { return pimpl->bufferSize; }
-
- double iOSAudioIODevice::getCurrentSampleRate() { return pimpl->sampleRate; }
-
- int iOSAudioIODevice::getCurrentBitDepth() { return 16; }
-
- BigInteger iOSAudioIODevice::getActiveInputChannels() const { return pimpl->channelData.inputs->activeChannels; }
- BigInteger iOSAudioIODevice::getActiveOutputChannels() const { return pimpl->channelData.outputs->activeChannels; }
-
- int iOSAudioIODevice::getInputLatencyInSamples() { return roundToInt (pimpl->sampleRate * [AVAudioSession sharedInstance].inputLatency); }
- int iOSAudioIODevice::getOutputLatencyInSamples() { return roundToInt (pimpl->sampleRate * [AVAudioSession sharedInstance].outputLatency); }
- int iOSAudioIODevice::getXRunCount() const noexcept { return pimpl->xrun; }
-
- void iOSAudioIODevice::setMidiMessageCollector (MidiMessageCollector* collector) { pimpl->messageCollector = collector; }
- AudioPlayHead* iOSAudioIODevice::getAudioPlayHead() const { return pimpl.get(); }
-
- bool iOSAudioIODevice::isInterAppAudioConnected() const { return pimpl->interAppAudioConnected; }
- #if JUCE_MODULE_AVAILABLE_juce_graphics
- Image iOSAudioIODevice::getIcon (int size) { return pimpl->getIcon (size); }
- #endif
- void iOSAudioIODevice::switchApplication() { return pimpl->switchApplication(); }
-
- //==============================================================================
- iOSAudioIODeviceType::iOSAudioIODeviceType()
- : AudioIODeviceType (iOSAudioDeviceName)
- {
- sessionHolder->activeDeviceTypes.add (this);
- }
-
- iOSAudioIODeviceType::~iOSAudioIODeviceType()
- {
- sessionHolder->activeDeviceTypes.removeFirstMatchingValue (this);
- }
-
- // The list of devices is updated automatically
- void iOSAudioIODeviceType::scanForDevices() {}
- StringArray iOSAudioIODeviceType::getDeviceNames (bool) const { return { iOSAudioDeviceName }; }
- int iOSAudioIODeviceType::getDefaultDeviceIndex (bool) const { return 0; }
- int iOSAudioIODeviceType::getIndexOfDevice (AudioIODevice*, bool) const { return 0; }
- bool iOSAudioIODeviceType::hasSeparateInputsAndOutputs() const { return false; }
-
- AudioIODevice* iOSAudioIODeviceType::createDevice (const String& outputDeviceName, const String& inputDeviceName)
- {
- return new iOSAudioIODevice (this, outputDeviceName, inputDeviceName);
- }
-
- void iOSAudioIODeviceType::handleRouteChange (AVAudioSessionRouteChangeReason)
- {
- triggerAsyncUpdate();
- }
-
- void iOSAudioIODeviceType::handleAsyncUpdate()
- {
- callDeviceChangeListeners();
- }
-
- //==============================================================================
- AudioSessionHolder::AudioSessionHolder() { nativeSession = [[iOSAudioSessionNative alloc] init: this]; }
- AudioSessionHolder::~AudioSessionHolder() { [nativeSession release]; }
-
- void AudioSessionHolder::handleStatusChange (bool enabled, const char* reason) const
- {
- for (auto device: activeDevices)
- device->handleStatusChange (enabled, reason);
- }
-
- void AudioSessionHolder::handleRouteChange (AVAudioSessionRouteChangeReason reason)
- {
- for (auto device: activeDevices)
- device->handleRouteChange (reason);
-
- for (auto deviceType: activeDeviceTypes)
- deviceType->handleRouteChange (reason);
- }
-
- #undef JUCE_NSERROR_CHECK
-
- } // namespace juce
|