|
- /*
- ==============================================================================
-
- 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.
-
- ==============================================================================
- */
-
- namespace juce
- {
-
- //==============================================================================
- /**
- Manages the state of some audio and midi i/o devices.
-
- This class keeps tracks of a currently-selected audio device, through
- with which it continuously streams data from an audio callback, as well as
- one or more midi inputs.
-
- The idea is that your application will create one global instance of this object,
- and let it take care of creating and deleting specific types of audio devices
- internally. So when the device is changed, your callbacks will just keep running
- without having to worry about this.
-
- The manager can save and reload all of its device settings as XML, which
- makes it very easy for you to save and reload the audio setup of your
- application.
-
- And to make it easy to let the user change its settings, there's a component
- to do just that - the AudioDeviceSelectorComponent class, which contains a set of
- device selection/sample-rate/latency controls.
-
- To use an AudioDeviceManager, create one, and use initialise() to set it up. Then
- call addAudioCallback() to register your audio callback with it, and use that to process
- your audio data.
-
- The manager also acts as a handy hub for incoming midi messages, allowing a
- listener to register for messages from either a specific midi device, or from whatever
- the current default midi input device is. The listener then doesn't have to worry about
- re-registering with different midi devices if they are changed or deleted.
-
- And yet another neat trick is that amount of CPU time being used is measured and
- available with the getCpuUsage() method.
-
- The AudioDeviceManager is a ChangeBroadcaster, and will send a change message to
- listeners whenever one of its settings is changed.
-
- @see AudioDeviceSelectorComponent, AudioIODevice, AudioIODeviceType
-
- @tags{Audio}
- */
- class JUCE_API AudioDeviceManager : public ChangeBroadcaster
- {
- public:
- //==============================================================================
- /** Creates a default AudioDeviceManager.
-
- Initially no audio device will be selected. You should call the initialise() method
- and register an audio callback with setAudioCallback() before it'll be able to
- actually make any noise.
- */
- AudioDeviceManager();
-
- /** Destructor. */
- ~AudioDeviceManager() override;
-
- //==============================================================================
- /**
- This structure holds a set of properties describing the current audio setup.
-
- An AudioDeviceManager uses this class to save/load its current settings, and to
- specify your preferred options when opening a device.
-
- @see AudioDeviceManager::setAudioDeviceSetup(), AudioDeviceManager::initialise()
- */
- struct JUCE_API AudioDeviceSetup
- {
- /** The name of the audio device used for output.
- The name has to be one of the ones listed by the AudioDeviceManager's currently
- selected device type.
- This may be the same as the input device.
- */
- String outputDeviceName;
-
- /** The name of the audio device used for input.
- This may be the same as the output device.
- */
- String inputDeviceName;
-
- /** The current sample rate.
- This rate is used for both the input and output devices.
- A value of 0 indicates that you don't care what rate is used, and the
- device will choose a sensible rate for you.
- */
- double sampleRate = 0;
-
- /** The buffer size, in samples.
- This buffer size is used for both the input and output devices.
- A value of 0 indicates the default buffer size.
- */
- int bufferSize = 0;
-
- /** The set of active input channels.
- The bits that are set in this array indicate the channels of the
- input device that are active.
- If useDefaultInputChannels is true, this value is ignored.
- */
- BigInteger inputChannels;
-
- /** If this is true, it indicates that the inputChannels array
- should be ignored, and instead, the device's default channels
- should be used.
- */
- bool useDefaultInputChannels = true;
-
- /** The set of active output channels.
- The bits that are set in this array indicate the channels of the
- input device that are active.
- If useDefaultOutputChannels is true, this value is ignored.
- */
- BigInteger outputChannels;
-
- /** If this is true, it indicates that the outputChannels array
- should be ignored, and instead, the device's default channels
- should be used.
- */
- bool useDefaultOutputChannels = true;
-
- bool operator== (const AudioDeviceSetup&) const;
- bool operator!= (const AudioDeviceSetup&) const;
- };
-
-
- //==============================================================================
- /** Opens a set of audio devices ready for use.
-
- This will attempt to open either a default audio device, or one that was
- previously saved as XML.
-
- @param numInputChannelsNeeded the maximum number of input channels your app would like to
- use (the actual number of channels opened may be less than
- the number requested)
- @param numOutputChannelsNeeded the maximum number of output channels your app would like to
- use (the actual number of channels opened may be less than
- the number requested)
- @param savedState either a previously-saved state that was produced
- by createStateXml(), or nullptr if you want the manager
- to choose the best device to open.
- @param selectDefaultDeviceOnFailure if true, then if the device specified in the XML
- fails to open, then a default device will be used
- instead. If false, then on failure, no device is
- opened.
- @param preferredDefaultDeviceName if this is not empty, and there's a device with this
- name, then that will be used as the default device
- (assuming that there wasn't one specified in the XML).
- The string can actually be a simple wildcard, containing "*"
- and "?" characters
- @param preferredSetupOptions if this is non-null, the structure will be used as the
- set of preferred settings when opening the device. If you
- use this parameter, the preferredDefaultDeviceName
- field will be ignored. If you set the outputDeviceName
- or inputDeviceName data members of the AudioDeviceSetup
- to empty strings, then a default device will be used.
-
-
- @returns an error message if anything went wrong, or an empty string if it worked ok.
- */
- String initialise (int numInputChannelsNeeded,
- int numOutputChannelsNeeded,
- const XmlElement* savedState,
- bool selectDefaultDeviceOnFailure,
- const String& preferredDefaultDeviceName = String(),
- const AudioDeviceSetup* preferredSetupOptions = nullptr);
-
- /** Resets everything to a default device setup, clearing any stored settings. */
- String initialiseWithDefaultDevices (int numInputChannelsNeeded,
- int numOutputChannelsNeeded);
-
- /** Returns some XML representing the current state of the manager.
-
- This stores the current device, its samplerate, block size, etc, and
- can be restored later with initialise().
-
- Note that this can return a null pointer if no settings have been explicitly changed
- (i.e. if the device manager has just been left in its default state).
- */
- std::unique_ptr<XmlElement> createStateXml() const;
-
- //==============================================================================
- /** Returns the current device properties that are in use.
- @see setAudioDeviceSetup
- */
- AudioDeviceSetup getAudioDeviceSetup() const;
-
- /** Returns the current device properties that are in use.
- This is an old method, kept around for compatibility, but you should prefer the new
- version which returns the result rather than taking an out-parameter.
- @see getAudioDeviceSetup()
- */
- void getAudioDeviceSetup (AudioDeviceSetup& result) const;
-
- /** Changes the current device or its settings.
-
- If you want to change a device property, like the current sample rate or
- block size, you can call getAudioDeviceSetup() to retrieve the current
- settings, then tweak the appropriate fields in the AudioDeviceSetup structure,
- and pass it back into this method to apply the new settings.
-
- @param newSetup the settings that you'd like to use.
- If you don't need an input or output device, set the
- inputDeviceName or outputDeviceName data members respectively
- to empty strings. Note that this behaviour differs from
- the behaviour of initialise().
- @param treatAsChosenDevice if this is true and if the device opens correctly, these new
- settings will be taken as having been explicitly chosen by the
- user, and the next time createStateXml() is called, these settings
- will be returned. If it's false, then the device is treated as a
- temporary or default device, and a call to createStateXml() will
- return either the last settings that were made with treatAsChosenDevice
- as true, or the last XML settings that were passed into initialise().
- @returns an error message if anything went wrong, or an empty string if it worked ok.
-
- @see getAudioDeviceSetup
- */
- String setAudioDeviceSetup (const AudioDeviceSetup& newSetup, bool treatAsChosenDevice);
-
-
- /** Returns the currently-active audio device. */
- AudioIODevice* getCurrentAudioDevice() const noexcept { return currentAudioDevice.get(); }
-
- /** Returns the type of audio device currently in use.
- @see setCurrentAudioDeviceType
- */
- String getCurrentAudioDeviceType() const { return currentDeviceType; }
-
- /** Returns the currently active audio device type object.
- Don't keep a copy of this pointer - it's owned by the device manager and could
- change at any time.
- */
- AudioIODeviceType* getCurrentDeviceTypeObject() const;
-
- /** Changes the class of audio device being used.
-
- This switches between, e.g. ASIO and DirectSound. On the Mac you probably won't ever call
- this because there's only one type: CoreAudio.
-
- For a list of types, see getAvailableDeviceTypes().
- */
- void setCurrentAudioDeviceType (const String& type, bool treatAsChosenDevice);
-
- /** Closes the currently-open device.
- You can call restartLastAudioDevice() later to reopen it in the same state
- that it was just in.
- */
- void closeAudioDevice();
-
- /** Tries to reload the last audio device that was running.
-
- Note that this only reloads the last device that was running before
- closeAudioDevice() was called - it doesn't reload any kind of saved-state,
- and can only be called after a device has been opened with setAudioDeviceSetup().
-
- If a device is already open, this call will do nothing.
- */
- void restartLastAudioDevice();
-
- //==============================================================================
- /** Registers an audio callback to be used.
-
- The manager will redirect callbacks from whatever audio device is currently
- in use to all registered callback objects. If more than one callback is
- active, they will all be given the same input data, and their outputs will
- be summed.
-
- If necessary, this method will invoke audioDeviceAboutToStart() on the callback
- object before returning.
-
- To remove a callback, use removeAudioCallback().
- */
- void addAudioCallback (AudioIODeviceCallback* newCallback);
-
- /** Deregisters a previously added callback.
-
- If necessary, this method will invoke audioDeviceStopped() on the callback
- object before returning.
-
- @see addAudioCallback
- */
- void removeAudioCallback (AudioIODeviceCallback* callback);
-
- //==============================================================================
- /** Returns the average proportion of available CPU being spent inside the audio callbacks.
- @returns A value between 0 and 1.0 to indicate the approximate proportion of CPU
- time spent in the callbacks.
- */
- double getCpuUsage() const;
-
- //==============================================================================
- /** Enables or disables a midi input device.
-
- The list of devices can be obtained with the MidiInput::getAvailableDevices() method.
-
- Any incoming messages from enabled input devices will be forwarded on to all the
- listeners that have been registered with the addMidiInputDeviceCallback() method. They
- can either register for messages from a particular device, or from just the "default"
- midi input.
-
- Routing the midi input via an AudioDeviceManager means that when a listener
- registers for the default midi input, this default device can be changed by the
- manager without the listeners having to know about it or re-register.
-
- It also means that a listener can stay registered for a midi input that is disabled
- or not present, so that when the input is re-enabled, the listener will start
- receiving messages again.
-
- @see addMidiInputDeviceCallback, isMidiInputDeviceEnabled
- */
- void setMidiInputDeviceEnabled (const String& deviceIdentifier, bool enabled);
-
- /** Returns true if a given midi input device is being used.
-
- @see setMidiInputDeviceEnabled
- */
- bool isMidiInputDeviceEnabled (const String& deviceIdentifier) const;
-
- /** Registers a listener for callbacks when midi events arrive from a midi input.
-
- The device identifier can be empty to indicate that it wants to receive all incoming
- events from all the enabled MIDI inputs. Or it can be the identifier of one of the
- MIDI input devices if it just wants the events from that device. (see
- MidiInput::getAvailableDevices() for the list of devices).
-
- Only devices which are enabled (see the setMidiInputDeviceEnabled() method) will have their
- events forwarded on to listeners.
- */
- void addMidiInputDeviceCallback (const String& deviceIdentifier,
- MidiInputCallback* callback);
-
- /** Removes a listener that was previously registered with addMidiInputDeviceCallback(). */
- void removeMidiInputDeviceCallback (const String& deviceIdentifier,
- MidiInputCallback* callback);
-
- //==============================================================================
- /** Sets a midi output device to use as the default.
-
- The list of devices can be obtained with the MidiOutput::getAvailableDevices() method.
-
- The specified device will be opened automatically and can be retrieved with the
- getDefaultMidiOutput() method.
-
- Pass in an empty string to deselect all devices. For the default device, you
- can use MidiOutput::getDefaultDevice().
-
- @see getDefaultMidiOutput, getDefaultMidiOutputIdentifier
- */
- void setDefaultMidiOutputDevice (const String& deviceIdentifier);
-
- /** Returns the name of the default midi output.
-
- @see setDefaultMidiOutputDevice, getDefaultMidiOutput
- */
- const String& getDefaultMidiOutputIdentifier() const noexcept { return defaultMidiOutputDeviceInfo.identifier; }
-
- /** Returns the current default midi output device. If no device has been selected, or the
- device can't be opened, this will return nullptr.
-
- @see getDefaultMidiOutputIdentifier
- */
- MidiOutput* getDefaultMidiOutput() const noexcept { return defaultMidiOutput.get(); }
-
- //==============================================================================
- /** Returns a list of the types of device supported. */
- const OwnedArray<AudioIODeviceType>& getAvailableDeviceTypes();
-
- /** Creates a list of available types.
-
- This will add a set of new AudioIODeviceType objects to the specified list, to
- represent each available types of device.
-
- You can override this if your app needs to do something specific, like avoid
- using DirectSound devices, etc.
- */
- virtual void createAudioDeviceTypes (OwnedArray<AudioIODeviceType>& types);
-
- /** Adds a new device type to the list of types. */
- void addAudioDeviceType (std::unique_ptr<AudioIODeviceType> newDeviceType);
-
- /** Removes a previously added device type from the manager. */
- void removeAudioDeviceType (AudioIODeviceType* deviceTypeToRemove);
-
- //==============================================================================
- /** Plays a beep through the current audio device.
-
- This is here to allow the audio setup UI panels to easily include a "test"
- button so that the user can check where the audio is coming from.
- */
- void playTestSound();
-
- //==============================================================================
- /**
- A simple reference-counted struct that holds a level-meter value that can be read
- using getCurrentLevel().
-
- This is used to ensure that the level processing code is only executed when something
- holds a reference to one of these objects and will be bypassed otherwise.
-
- @see getInputLevelGetter, getOutputLevelGetter
- */
- struct LevelMeter : public ReferenceCountedObject
- {
- LevelMeter() noexcept;
- double getCurrentLevel() const noexcept;
-
- using Ptr = ReferenceCountedObjectPtr<LevelMeter>;
-
- private:
- friend class AudioDeviceManager;
-
- Atomic<float> level { 0 };
- void updateLevel (const float* const*, int numChannels, int numSamples) noexcept;
- };
-
- /** Returns a reference-counted object that can be used to get the current input level.
-
- You need to store this object locally to ensure that the reference count is incremented
- and decremented properly. The current input level value can be read using getCurrentLevel().
- */
- LevelMeter::Ptr getInputLevelGetter() noexcept { return inputLevelGetter; }
-
- /** Returns a reference-counted object that can be used to get the current output level.
-
- You need to store this object locally to ensure that the reference count is incremented
- and decremented properly. The current output level value can be read using getCurrentLevel().
- */
- LevelMeter::Ptr getOutputLevelGetter() noexcept { return outputLevelGetter; }
-
- //==============================================================================
- /** Returns the a lock that can be used to synchronise access to the audio callback.
- Obviously while this is locked, you're blocking the audio thread from running, so
- it must only be used for very brief periods when absolutely necessary.
- */
- CriticalSection& getAudioCallbackLock() noexcept { return audioCallbackLock; }
-
- /** Returns the a lock that can be used to synchronise access to the midi callback.
- Obviously while this is locked, you're blocking the midi system from running, so
- it must only be used for very brief periods when absolutely necessary.
- */
- CriticalSection& getMidiCallbackLock() noexcept { return midiCallbackLock; }
-
- //==============================================================================
- /** Returns the number of under- or over runs reported.
-
- This method will use the underlying device's native getXRunCount if it supports
- it. Otherwise it will estimate the number of under-/overruns by measuring the
- time it spent in the audio callback.
- */
- int getXRunCount() const noexcept;
-
- //==============================================================================
- #ifndef DOXYGEN
- [[deprecated ("Use setMidiInputDeviceEnabled instead.")]]
- void setMidiInputEnabled (const String&, bool);
- [[deprecated ("Use isMidiInputDeviceEnabled instead.")]]
- bool isMidiInputEnabled (const String&) const;
- [[deprecated ("Use addMidiInputDeviceCallback instead.")]]
- void addMidiInputCallback (const String&, MidiInputCallback*);
- [[deprecated ("Use removeMidiInputDeviceCallback instead.")]]
- void removeMidiInputCallback (const String&, MidiInputCallback*);
- [[deprecated ("Use setDefaultMidiOutputDevice instead.")]]
- void setDefaultMidiOutput (const String&);
- [[deprecated ("Use getDefaultMidiOutputIdentifier instead.")]]
- const String& getDefaultMidiOutputName() const noexcept { return defaultMidiOutputDeviceInfo.name; }
- #endif
-
- private:
- //==============================================================================
- OwnedArray<AudioIODeviceType> availableDeviceTypes;
- OwnedArray<AudioDeviceSetup> lastDeviceTypeConfigs;
-
- AudioDeviceSetup currentSetup;
- std::unique_ptr<AudioIODevice> currentAudioDevice;
- Array<AudioIODeviceCallback*> callbacks;
- int numInputChansNeeded = 0, numOutputChansNeeded = 2;
- String preferredDeviceName, currentDeviceType;
- std::unique_ptr<XmlElement> lastExplicitSettings;
- mutable bool listNeedsScanning = true;
- AudioBuffer<float> tempBuffer;
-
- struct MidiCallbackInfo
- {
- String deviceIdentifier;
- MidiInputCallback* callback;
- };
-
- Array<MidiDeviceInfo> midiDeviceInfosFromXml;
- std::vector<std::unique_ptr<MidiInput>> enabledMidiInputs;
- Array<MidiCallbackInfo> midiCallbacks;
-
- MidiDeviceInfo defaultMidiOutputDeviceInfo;
- std::unique_ptr<MidiOutput> defaultMidiOutput;
- CriticalSection audioCallbackLock, midiCallbackLock;
-
- std::unique_ptr<AudioBuffer<float>> testSound;
- int testSoundPosition = 0;
-
- AudioProcessLoadMeasurer loadMeasurer;
-
- LevelMeter::Ptr inputLevelGetter { new LevelMeter() },
- outputLevelGetter { new LevelMeter() };
-
- //==============================================================================
- class CallbackHandler;
- std::unique_ptr<CallbackHandler> callbackHandler;
-
- void audioDeviceIOCallbackInt (const float** inputChannelData,
- int totalNumInputChannels,
- float** outputChannelData,
- int totalNumOutputChannels,
- int numSamples,
- const AudioIODeviceCallbackContext& context);
- void audioDeviceAboutToStartInt (AudioIODevice*);
- void audioDeviceStoppedInt();
- void audioDeviceErrorInt (const String&);
- void handleIncomingMidiMessageInt (MidiInput*, const MidiMessage&);
- void audioDeviceListChanged();
-
- String restartDevice (int blockSizeToUse, double sampleRateToUse,
- const BigInteger& ins, const BigInteger& outs);
- void stopDevice();
-
- void updateXml();
-
- void updateCurrentSetup();
- void createDeviceTypesIfNeeded();
- void scanDevicesIfNeeded();
- void deleteCurrentDevice();
- double chooseBestSampleRate (double preferred) const;
- int chooseBestBufferSize (int preferred) const;
- void insertDefaultDeviceNames (AudioDeviceSetup&) const;
- String initialiseDefault (const String& preferredDefaultDeviceName, const AudioDeviceSetup*);
- String initialiseFromXML (const XmlElement&, bool selectDefaultDeviceOnFailure,
- const String& preferredDefaultDeviceName, const AudioDeviceSetup*);
-
- AudioIODeviceType* findType (const String& inputName, const String& outputName);
- AudioIODeviceType* findType (const String& typeName);
- void pickCurrentDeviceTypeWithDevices();
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioDeviceManager)
- };
-
- } // namespace juce
|