migrating to the latest JUCE version
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,45 +1,45 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
AudioIODevice::AudioIODevice (const String& deviceName, const String& deviceTypeName)
|
||||
: name (deviceName), typeName (deviceTypeName)
|
||||
{
|
||||
}
|
||||
|
||||
AudioIODevice::~AudioIODevice() {}
|
||||
|
||||
void AudioIODeviceCallback::audioDeviceError (const String&) {}
|
||||
bool AudioIODevice::setAudioPreprocessingEnabled (bool) { return false; }
|
||||
bool AudioIODevice::hasControlPanel() const { return false; }
|
||||
int AudioIODevice::getXRunCount() const noexcept { return -1; }
|
||||
|
||||
bool AudioIODevice::showControlPanel()
|
||||
{
|
||||
jassertfalse; // this should only be called for devices which return true from
|
||||
// their hasControlPanel() method.
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
AudioIODevice::AudioIODevice (const String& deviceName, const String& deviceTypeName)
|
||||
: name (deviceName), typeName (deviceTypeName)
|
||||
{
|
||||
}
|
||||
|
||||
AudioIODevice::~AudioIODevice() {}
|
||||
|
||||
void AudioIODeviceCallback::audioDeviceError (const String&) {}
|
||||
bool AudioIODevice::setAudioPreprocessingEnabled (bool) { return false; }
|
||||
bool AudioIODevice::hasControlPanel() const { return false; }
|
||||
int AudioIODevice::getXRunCount() const noexcept { return -1; }
|
||||
|
||||
bool AudioIODevice::showControlPanel()
|
||||
{
|
||||
jassertfalse; // this should only be called for devices which return true from
|
||||
// their hasControlPanel() method.
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,325 +1,352 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
class AudioIODevice;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
One of these is passed to an AudioIODevice object to stream the audio data
|
||||
in and out.
|
||||
|
||||
The AudioIODevice will repeatedly call this class's audioDeviceIOCallback()
|
||||
method on its own high-priority audio thread, when it needs to send or receive
|
||||
the next block of data.
|
||||
|
||||
@see AudioIODevice, AudioDeviceManager
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API AudioIODeviceCallback
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~AudioIODeviceCallback() = default;
|
||||
|
||||
/** Processes a block of incoming and outgoing audio data.
|
||||
|
||||
The subclass's implementation should use the incoming audio for whatever
|
||||
purposes it needs to, and must fill all the output channels with the next
|
||||
block of output data before returning.
|
||||
|
||||
The channel data is arranged with the same array indices as the channel name
|
||||
array returned by AudioIODevice::getOutputChannelNames(), but those channels
|
||||
that aren't specified in AudioIODevice::open() will have a null pointer for their
|
||||
associated channel, so remember to check for this.
|
||||
|
||||
@param inputChannelData a set of arrays containing the audio data for each
|
||||
incoming channel - this data is valid until the function
|
||||
returns. There will be one channel of data for each input
|
||||
channel that was enabled when the audio device was opened
|
||||
(see AudioIODevice::open())
|
||||
@param numInputChannels the number of pointers to channel data in the
|
||||
inputChannelData array.
|
||||
@param outputChannelData a set of arrays which need to be filled with the data
|
||||
that should be sent to each outgoing channel of the device.
|
||||
There will be one channel of data for each output channel
|
||||
that was enabled when the audio device was opened (see
|
||||
AudioIODevice::open())
|
||||
The initial contents of the array is undefined, so the
|
||||
callback function must fill all the channels with zeros if
|
||||
its output is silence. Failing to do this could cause quite
|
||||
an unpleasant noise!
|
||||
@param numOutputChannels the number of pointers to channel data in the
|
||||
outputChannelData array.
|
||||
@param numSamples the number of samples in each channel of the input and
|
||||
output arrays. The number of samples will depend on the
|
||||
audio device's buffer size and will usually remain constant,
|
||||
although this isn't guaranteed. For example, on Android,
|
||||
on devices which support it, Android will chop up your audio
|
||||
processing into several smaller callbacks to ensure higher audio
|
||||
performance. So make sure your code can cope with reasonable
|
||||
changes in the buffer size from one callback to the next.
|
||||
*/
|
||||
virtual void audioDeviceIOCallback (const float** inputChannelData,
|
||||
int numInputChannels,
|
||||
float** outputChannelData,
|
||||
int numOutputChannels,
|
||||
int numSamples) = 0;
|
||||
|
||||
/** Called to indicate that the device is about to start calling back.
|
||||
|
||||
This will be called just before the audio callbacks begin, either when this
|
||||
callback has just been added to an audio device, or after the device has been
|
||||
restarted because of a sample-rate or block-size change.
|
||||
|
||||
You can use this opportunity to find out the sample rate and block size
|
||||
that the device is going to use by calling the AudioIODevice::getCurrentSampleRate()
|
||||
and AudioIODevice::getCurrentBufferSizeSamples() on the supplied pointer.
|
||||
|
||||
@param device the audio IO device that will be used to drive the callback.
|
||||
Note that if you're going to store this this pointer, it is
|
||||
only valid until the next time that audioDeviceStopped is called.
|
||||
*/
|
||||
virtual void audioDeviceAboutToStart (AudioIODevice* device) = 0;
|
||||
|
||||
/** Called to indicate that the device has stopped. */
|
||||
virtual void audioDeviceStopped() = 0;
|
||||
|
||||
/** This can be overridden to be told if the device generates an error while operating.
|
||||
Be aware that this could be called by any thread! And not all devices perform
|
||||
this callback.
|
||||
*/
|
||||
virtual void audioDeviceError (const String& errorMessage);
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Base class for an audio device with synchronised input and output channels.
|
||||
|
||||
Subclasses of this are used to implement different protocols such as DirectSound,
|
||||
ASIO, CoreAudio, etc.
|
||||
|
||||
To create one of these, you'll need to use the AudioIODeviceType class - see the
|
||||
documentation for that class for more info.
|
||||
|
||||
For an easier way of managing audio devices and their settings, have a look at the
|
||||
AudioDeviceManager class.
|
||||
|
||||
@see AudioIODeviceType, AudioDeviceManager
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API AudioIODevice
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~AudioIODevice();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the device's name, (as set in the constructor). */
|
||||
const String& getName() const noexcept { return name; }
|
||||
|
||||
/** Returns the type of the device.
|
||||
|
||||
E.g. "CoreAudio", "ASIO", etc. - this comes from the AudioIODeviceType that created it.
|
||||
*/
|
||||
const String& getTypeName() const noexcept { return typeName; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the names of all the available output channels on this device.
|
||||
To find out which of these are currently in use, call getActiveOutputChannels().
|
||||
*/
|
||||
virtual StringArray getOutputChannelNames() = 0;
|
||||
|
||||
/** Returns the names of all the available input channels on this device.
|
||||
To find out which of these are currently in use, call getActiveInputChannels().
|
||||
*/
|
||||
virtual StringArray getInputChannelNames() = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the set of sample-rates this device supports.
|
||||
@see getCurrentSampleRate
|
||||
*/
|
||||
virtual Array<double> getAvailableSampleRates() = 0;
|
||||
|
||||
/** Returns the set of buffer sizes that are available.
|
||||
@see getCurrentBufferSizeSamples, getDefaultBufferSize
|
||||
*/
|
||||
virtual Array<int> getAvailableBufferSizes() = 0;
|
||||
|
||||
/** Returns the default buffer-size to use.
|
||||
@returns a number of samples
|
||||
@see getAvailableBufferSizes
|
||||
*/
|
||||
virtual int getDefaultBufferSize() = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to open the device ready to play.
|
||||
|
||||
@param inputChannels a BigInteger in which a set bit indicates that the corresponding
|
||||
input channel should be enabled
|
||||
@param outputChannels a BigInteger in which a set bit indicates that the corresponding
|
||||
output channel should be enabled
|
||||
@param sampleRate the sample rate to try to use - to find out which rates are
|
||||
available, see getAvailableSampleRates()
|
||||
@param bufferSizeSamples the size of i/o buffer to use - to find out the available buffer
|
||||
sizes, see getAvailableBufferSizes()
|
||||
@returns an error description if there's a problem, or an empty string if it succeeds in
|
||||
opening the device
|
||||
@see close
|
||||
*/
|
||||
virtual String open (const BigInteger& inputChannels,
|
||||
const BigInteger& outputChannels,
|
||||
double sampleRate,
|
||||
int bufferSizeSamples) = 0;
|
||||
|
||||
/** Closes and releases the device if it's open. */
|
||||
virtual void close() = 0;
|
||||
|
||||
/** Returns true if the device is still open.
|
||||
|
||||
A device might spontaneously close itself if something goes wrong, so this checks if
|
||||
it's still open.
|
||||
*/
|
||||
virtual bool isOpen() = 0;
|
||||
|
||||
/** Starts the device actually playing.
|
||||
|
||||
This must be called after the device has been opened.
|
||||
|
||||
@param callback the callback to use for streaming the data.
|
||||
@see AudioIODeviceCallback, open
|
||||
*/
|
||||
virtual void start (AudioIODeviceCallback* callback) = 0;
|
||||
|
||||
/** Stops the device playing.
|
||||
|
||||
Once a device has been started, this will stop it. Any pending calls to the
|
||||
callback class will be flushed before this method returns.
|
||||
*/
|
||||
virtual void stop() = 0;
|
||||
|
||||
/** Returns true if the device is still calling back.
|
||||
|
||||
The device might mysteriously stop, so this checks whether it's
|
||||
still playing.
|
||||
*/
|
||||
virtual bool isPlaying() = 0;
|
||||
|
||||
/** Returns the last error that happened if anything went wrong. */
|
||||
virtual String getLastError() = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the buffer size that the device is currently using.
|
||||
|
||||
If the device isn't actually open, this value doesn't really mean much.
|
||||
*/
|
||||
virtual int getCurrentBufferSizeSamples() = 0;
|
||||
|
||||
/** Returns the sample rate that the device is currently using.
|
||||
|
||||
If the device isn't actually open, this value doesn't really mean much.
|
||||
*/
|
||||
virtual double getCurrentSampleRate() = 0;
|
||||
|
||||
/** Returns the device's current physical bit-depth.
|
||||
|
||||
If the device isn't actually open, this value doesn't really mean much.
|
||||
*/
|
||||
virtual int getCurrentBitDepth() = 0;
|
||||
|
||||
/** Returns a mask showing which of the available output channels are currently
|
||||
enabled.
|
||||
@see getOutputChannelNames
|
||||
*/
|
||||
virtual BigInteger getActiveOutputChannels() const = 0;
|
||||
|
||||
/** Returns a mask showing which of the available input channels are currently
|
||||
enabled.
|
||||
@see getInputChannelNames
|
||||
*/
|
||||
virtual BigInteger getActiveInputChannels() const = 0;
|
||||
|
||||
/** Returns the device's output latency.
|
||||
|
||||
This is the delay in samples between a callback getting a block of data, and
|
||||
that data actually getting played.
|
||||
*/
|
||||
virtual int getOutputLatencyInSamples() = 0;
|
||||
|
||||
/** Returns the device's input latency.
|
||||
|
||||
This is the delay in samples between some audio actually arriving at the soundcard,
|
||||
and the callback getting passed this block of data.
|
||||
*/
|
||||
virtual int getInputLatencyInSamples() = 0;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** True if this device can show a pop-up control panel for editing its settings.
|
||||
|
||||
This is generally just true of ASIO devices. If true, you can call showControlPanel()
|
||||
to display it.
|
||||
*/
|
||||
virtual bool hasControlPanel() const;
|
||||
|
||||
/** Shows a device-specific control panel if there is one.
|
||||
|
||||
This should only be called for devices which return true from hasControlPanel().
|
||||
*/
|
||||
virtual bool showControlPanel();
|
||||
|
||||
/** On devices which support it, this allows automatic gain control or other
|
||||
mic processing to be disabled.
|
||||
If the device doesn't support this operation, it'll return false.
|
||||
*/
|
||||
virtual bool setAudioPreprocessingEnabled (bool shouldBeEnabled);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of under- or over runs reported by the OS since
|
||||
playback/recording has started.
|
||||
|
||||
This number may be different than determining the Xrun count manually (by
|
||||
measuring the time spent in the audio callback) as the OS may be doing
|
||||
some buffering internally - especially on mobile devices.
|
||||
|
||||
Returns -1 if playback/recording has not started yet or if getting the underrun
|
||||
count is not supported for this device (Android SDK 23 and lower).
|
||||
*/
|
||||
virtual int getXRunCount() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
protected:
|
||||
/** Creates a device, setting its name and type member variables. */
|
||||
AudioIODevice (const String& deviceName,
|
||||
const String& typeName);
|
||||
|
||||
/** @internal */
|
||||
String name, typeName;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
class AudioIODevice;
|
||||
|
||||
/** Additional information that may be passed to the AudioIODeviceCallback. */
|
||||
struct AudioIODeviceCallbackContext
|
||||
{
|
||||
/** If the host provides this information, this field will be set to point to
|
||||
an integer holding the current value; otherwise, this will be nullptr.
|
||||
*/
|
||||
const uint64_t* hostTimeNs = nullptr;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
One of these is passed to an AudioIODevice object to stream the audio data
|
||||
in and out.
|
||||
|
||||
The AudioIODevice will repeatedly call this class's audioDeviceIOCallback()
|
||||
method on its own high-priority audio thread, when it needs to send or receive
|
||||
the next block of data.
|
||||
|
||||
@see AudioIODevice, AudioDeviceManager
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API AudioIODeviceCallback
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~AudioIODeviceCallback() = default;
|
||||
|
||||
/** Processes a block of incoming and outgoing audio data.
|
||||
|
||||
The subclass's implementation should use the incoming audio for whatever
|
||||
purposes it needs to, and must fill all the output channels with the next
|
||||
block of output data before returning.
|
||||
|
||||
The channel data is arranged with the same array indices as the channel name
|
||||
array returned by AudioIODevice::getOutputChannelNames(), but those channels
|
||||
that aren't specified in AudioIODevice::open() will have a null pointer for their
|
||||
associated channel, so remember to check for this.
|
||||
|
||||
@param inputChannelData a set of arrays containing the audio data for each
|
||||
incoming channel - this data is valid until the function
|
||||
returns. There will be one channel of data for each input
|
||||
channel that was enabled when the audio device was opened
|
||||
(see AudioIODevice::open())
|
||||
@param numInputChannels the number of pointers to channel data in the
|
||||
inputChannelData array.
|
||||
@param outputChannelData a set of arrays which need to be filled with the data
|
||||
that should be sent to each outgoing channel of the device.
|
||||
There will be one channel of data for each output channel
|
||||
that was enabled when the audio device was opened (see
|
||||
AudioIODevice::open())
|
||||
The initial contents of the array is undefined, so the
|
||||
callback function must fill all the channels with zeros if
|
||||
its output is silence. Failing to do this could cause quite
|
||||
an unpleasant noise!
|
||||
@param numOutputChannels the number of pointers to channel data in the
|
||||
outputChannelData array.
|
||||
@param numSamples the number of samples in each channel of the input and
|
||||
output arrays. The number of samples will depend on the
|
||||
audio device's buffer size and will usually remain constant,
|
||||
although this isn't guaranteed. For example, on Android,
|
||||
on devices which support it, Android will chop up your audio
|
||||
processing into several smaller callbacks to ensure higher audio
|
||||
performance. So make sure your code can cope with reasonable
|
||||
changes in the buffer size from one callback to the next.
|
||||
*/
|
||||
virtual void audioDeviceIOCallback (const float** inputChannelData,
|
||||
int numInputChannels,
|
||||
float** outputChannelData,
|
||||
int numOutputChannels,
|
||||
int numSamples)
|
||||
{
|
||||
ignoreUnused (inputChannelData, numInputChannels, outputChannelData, numOutputChannels, numSamples);
|
||||
}
|
||||
|
||||
/** The same as audioDeviceIOCallback(), but with an additional context argument.
|
||||
|
||||
The default implementation of this function will call audioDeviceIOCallback(),
|
||||
but you can override this function if you need to make use of the context information.
|
||||
*/
|
||||
virtual void audioDeviceIOCallbackWithContext (const float** inputChannelData,
|
||||
int numInputChannels,
|
||||
float** outputChannelData,
|
||||
int numOutputChannels,
|
||||
int numSamples,
|
||||
const AudioIODeviceCallbackContext& context)
|
||||
{
|
||||
audioDeviceIOCallback (inputChannelData, numInputChannels, outputChannelData, numOutputChannels, numSamples);
|
||||
ignoreUnused (context);
|
||||
}
|
||||
|
||||
/** Called to indicate that the device is about to start calling back.
|
||||
|
||||
This will be called just before the audio callbacks begin, either when this
|
||||
callback has just been added to an audio device, or after the device has been
|
||||
restarted because of a sample-rate or block-size change.
|
||||
|
||||
You can use this opportunity to find out the sample rate and block size
|
||||
that the device is going to use by calling the AudioIODevice::getCurrentSampleRate()
|
||||
and AudioIODevice::getCurrentBufferSizeSamples() on the supplied pointer.
|
||||
|
||||
@param device the audio IO device that will be used to drive the callback.
|
||||
Note that if you're going to store this this pointer, it is
|
||||
only valid until the next time that audioDeviceStopped is called.
|
||||
*/
|
||||
virtual void audioDeviceAboutToStart (AudioIODevice* device) = 0;
|
||||
|
||||
/** Called to indicate that the device has stopped. */
|
||||
virtual void audioDeviceStopped() = 0;
|
||||
|
||||
/** This can be overridden to be told if the device generates an error while operating.
|
||||
Be aware that this could be called by any thread! And not all devices perform
|
||||
this callback.
|
||||
*/
|
||||
virtual void audioDeviceError (const String& errorMessage);
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Base class for an audio device with synchronised input and output channels.
|
||||
|
||||
Subclasses of this are used to implement different protocols such as DirectSound,
|
||||
ASIO, CoreAudio, etc.
|
||||
|
||||
To create one of these, you'll need to use the AudioIODeviceType class - see the
|
||||
documentation for that class for more info.
|
||||
|
||||
For an easier way of managing audio devices and their settings, have a look at the
|
||||
AudioDeviceManager class.
|
||||
|
||||
@see AudioIODeviceType, AudioDeviceManager
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API AudioIODevice
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~AudioIODevice();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the device's name, (as set in the constructor). */
|
||||
const String& getName() const noexcept { return name; }
|
||||
|
||||
/** Returns the type of the device.
|
||||
|
||||
E.g. "CoreAudio", "ASIO", etc. - this comes from the AudioIODeviceType that created it.
|
||||
*/
|
||||
const String& getTypeName() const noexcept { return typeName; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the names of all the available output channels on this device.
|
||||
To find out which of these are currently in use, call getActiveOutputChannels().
|
||||
*/
|
||||
virtual StringArray getOutputChannelNames() = 0;
|
||||
|
||||
/** Returns the names of all the available input channels on this device.
|
||||
To find out which of these are currently in use, call getActiveInputChannels().
|
||||
*/
|
||||
virtual StringArray getInputChannelNames() = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the set of sample-rates this device supports.
|
||||
@see getCurrentSampleRate
|
||||
*/
|
||||
virtual Array<double> getAvailableSampleRates() = 0;
|
||||
|
||||
/** Returns the set of buffer sizes that are available.
|
||||
@see getCurrentBufferSizeSamples, getDefaultBufferSize
|
||||
*/
|
||||
virtual Array<int> getAvailableBufferSizes() = 0;
|
||||
|
||||
/** Returns the default buffer-size to use.
|
||||
@returns a number of samples
|
||||
@see getAvailableBufferSizes
|
||||
*/
|
||||
virtual int getDefaultBufferSize() = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to open the device ready to play.
|
||||
|
||||
@param inputChannels a BigInteger in which a set bit indicates that the corresponding
|
||||
input channel should be enabled
|
||||
@param outputChannels a BigInteger in which a set bit indicates that the corresponding
|
||||
output channel should be enabled
|
||||
@param sampleRate the sample rate to try to use - to find out which rates are
|
||||
available, see getAvailableSampleRates()
|
||||
@param bufferSizeSamples the size of i/o buffer to use - to find out the available buffer
|
||||
sizes, see getAvailableBufferSizes()
|
||||
@returns an error description if there's a problem, or an empty string if it succeeds in
|
||||
opening the device
|
||||
@see close
|
||||
*/
|
||||
virtual String open (const BigInteger& inputChannels,
|
||||
const BigInteger& outputChannels,
|
||||
double sampleRate,
|
||||
int bufferSizeSamples) = 0;
|
||||
|
||||
/** Closes and releases the device if it's open. */
|
||||
virtual void close() = 0;
|
||||
|
||||
/** Returns true if the device is still open.
|
||||
|
||||
A device might spontaneously close itself if something goes wrong, so this checks if
|
||||
it's still open.
|
||||
*/
|
||||
virtual bool isOpen() = 0;
|
||||
|
||||
/** Starts the device actually playing.
|
||||
|
||||
This must be called after the device has been opened.
|
||||
|
||||
@param callback the callback to use for streaming the data.
|
||||
@see AudioIODeviceCallback, open
|
||||
*/
|
||||
virtual void start (AudioIODeviceCallback* callback) = 0;
|
||||
|
||||
/** Stops the device playing.
|
||||
|
||||
Once a device has been started, this will stop it. Any pending calls to the
|
||||
callback class will be flushed before this method returns.
|
||||
*/
|
||||
virtual void stop() = 0;
|
||||
|
||||
/** Returns true if the device is still calling back.
|
||||
|
||||
The device might mysteriously stop, so this checks whether it's
|
||||
still playing.
|
||||
*/
|
||||
virtual bool isPlaying() = 0;
|
||||
|
||||
/** Returns the last error that happened if anything went wrong. */
|
||||
virtual String getLastError() = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the buffer size that the device is currently using.
|
||||
|
||||
If the device isn't actually open, this value doesn't really mean much.
|
||||
*/
|
||||
virtual int getCurrentBufferSizeSamples() = 0;
|
||||
|
||||
/** Returns the sample rate that the device is currently using.
|
||||
|
||||
If the device isn't actually open, this value doesn't really mean much.
|
||||
*/
|
||||
virtual double getCurrentSampleRate() = 0;
|
||||
|
||||
/** Returns the device's current physical bit-depth.
|
||||
|
||||
If the device isn't actually open, this value doesn't really mean much.
|
||||
*/
|
||||
virtual int getCurrentBitDepth() = 0;
|
||||
|
||||
/** Returns a mask showing which of the available output channels are currently
|
||||
enabled.
|
||||
@see getOutputChannelNames
|
||||
*/
|
||||
virtual BigInteger getActiveOutputChannels() const = 0;
|
||||
|
||||
/** Returns a mask showing which of the available input channels are currently
|
||||
enabled.
|
||||
@see getInputChannelNames
|
||||
*/
|
||||
virtual BigInteger getActiveInputChannels() const = 0;
|
||||
|
||||
/** Returns the device's output latency.
|
||||
|
||||
This is the delay in samples between a callback getting a block of data, and
|
||||
that data actually getting played.
|
||||
*/
|
||||
virtual int getOutputLatencyInSamples() = 0;
|
||||
|
||||
/** Returns the device's input latency.
|
||||
|
||||
This is the delay in samples between some audio actually arriving at the soundcard,
|
||||
and the callback getting passed this block of data.
|
||||
*/
|
||||
virtual int getInputLatencyInSamples() = 0;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** True if this device can show a pop-up control panel for editing its settings.
|
||||
|
||||
This is generally just true of ASIO devices. If true, you can call showControlPanel()
|
||||
to display it.
|
||||
*/
|
||||
virtual bool hasControlPanel() const;
|
||||
|
||||
/** Shows a device-specific control panel if there is one.
|
||||
|
||||
This should only be called for devices which return true from hasControlPanel().
|
||||
*/
|
||||
virtual bool showControlPanel();
|
||||
|
||||
/** On devices which support it, this allows automatic gain control or other
|
||||
mic processing to be disabled.
|
||||
If the device doesn't support this operation, it'll return false.
|
||||
*/
|
||||
virtual bool setAudioPreprocessingEnabled (bool shouldBeEnabled);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of under- or over runs reported by the OS since
|
||||
playback/recording has started.
|
||||
|
||||
This number may be different than determining the Xrun count manually (by
|
||||
measuring the time spent in the audio callback) as the OS may be doing
|
||||
some buffering internally - especially on mobile devices.
|
||||
|
||||
Returns -1 if playback/recording has not started yet or if getting the underrun
|
||||
count is not supported for this device (Android SDK 23 and lower).
|
||||
*/
|
||||
virtual int getXRunCount() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
protected:
|
||||
/** Creates a device, setting its name and type member variables. */
|
||||
AudioIODevice (const String& deviceName,
|
||||
const String& typeName);
|
||||
|
||||
/** @internal */
|
||||
String name, typeName;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,146 +1,146 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
AudioIODeviceType::AudioIODeviceType (const String& name)
|
||||
: typeName (name)
|
||||
{
|
||||
}
|
||||
|
||||
AudioIODeviceType::~AudioIODeviceType()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AudioIODeviceType::addListener (Listener* l) { listeners.add (l); }
|
||||
void AudioIODeviceType::removeListener (Listener* l) { listeners.remove (l); }
|
||||
|
||||
void AudioIODeviceType::callDeviceChangeListeners()
|
||||
{
|
||||
listeners.call ([] (Listener& l) { l.audioDeviceListChanged(); });
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return new CoreAudioClasses::CoreAudioIODeviceType(); }
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if JUCE_IOS
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return new iOSAudioIODeviceType(); }
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if JUCE_WINDOWS && JUCE_WASAPI
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode deviceMode)
|
||||
{
|
||||
auto windowsVersion = SystemStats::getOperatingSystemType();
|
||||
|
||||
if (windowsVersion < SystemStats::WinVista
|
||||
|| (WasapiClasses::isLowLatencyMode (deviceMode) && windowsVersion < SystemStats::Windows10))
|
||||
return nullptr;
|
||||
|
||||
return new WasapiClasses::WASAPIAudioIODeviceType (deviceMode);
|
||||
}
|
||||
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool exclusiveMode)
|
||||
{
|
||||
return createAudioIODeviceType_WASAPI (exclusiveMode ? WASAPIDeviceMode::exclusive
|
||||
: WASAPIDeviceMode::shared);
|
||||
}
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode) { return nullptr; }
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool) { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if JUCE_WINDOWS && JUCE_DIRECTSOUND
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return new DSoundAudioIODeviceType(); }
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if JUCE_WINDOWS && JUCE_ASIO
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return new ASIOAudioIODeviceType(); }
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if (JUCE_LINUX || JUCE_BSD) && JUCE_ALSA
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return createAudioIODeviceType_ALSA_PCMDevices(); }
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if (JUCE_LINUX || JUCE_BSD) && JUCE_JACK
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return new JackAudioIODeviceType(); }
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if JUCE_LINUX && JUCE_BELA
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() { return new BelaAudioIODeviceType(); }
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if JUCE_ANDROID
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android()
|
||||
{
|
||||
#if JUCE_USE_ANDROID_OBOE
|
||||
if (isOboeAvailable())
|
||||
return nullptr;
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_ANDROID_OPENSLES
|
||||
if (isOpenSLAvailable())
|
||||
return nullptr;
|
||||
#endif
|
||||
|
||||
return new AndroidAudioIODeviceType();
|
||||
}
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if JUCE_ANDROID && JUCE_USE_ANDROID_OPENSLES
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES()
|
||||
{
|
||||
return isOpenSLAvailable() ? new OpenSLAudioDeviceType() : nullptr;
|
||||
}
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if JUCE_ANDROID && JUCE_USE_ANDROID_OBOE
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe()
|
||||
{
|
||||
return isOboeAvailable() ? new OboeAudioIODeviceType() : nullptr;
|
||||
}
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe() { return nullptr; }
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
AudioIODeviceType::AudioIODeviceType (const String& name)
|
||||
: typeName (name)
|
||||
{
|
||||
}
|
||||
|
||||
AudioIODeviceType::~AudioIODeviceType()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AudioIODeviceType::addListener (Listener* l) { listeners.add (l); }
|
||||
void AudioIODeviceType::removeListener (Listener* l) { listeners.remove (l); }
|
||||
|
||||
void AudioIODeviceType::callDeviceChangeListeners()
|
||||
{
|
||||
listeners.call ([] (Listener& l) { l.audioDeviceListChanged(); });
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return new CoreAudioClasses::CoreAudioIODeviceType(); }
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if JUCE_IOS
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return new iOSAudioIODeviceType(); }
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if JUCE_WINDOWS && JUCE_WASAPI
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode deviceMode)
|
||||
{
|
||||
auto windowsVersion = SystemStats::getOperatingSystemType();
|
||||
|
||||
if (windowsVersion < SystemStats::WinVista
|
||||
|| (WasapiClasses::isLowLatencyMode (deviceMode) && windowsVersion < SystemStats::Windows10))
|
||||
return nullptr;
|
||||
|
||||
return new WasapiClasses::WASAPIAudioIODeviceType (deviceMode);
|
||||
}
|
||||
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool exclusiveMode)
|
||||
{
|
||||
return createAudioIODeviceType_WASAPI (exclusiveMode ? WASAPIDeviceMode::exclusive
|
||||
: WASAPIDeviceMode::shared);
|
||||
}
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode) { return nullptr; }
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool) { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if JUCE_WINDOWS && JUCE_DIRECTSOUND
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return new DSoundAudioIODeviceType(); }
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if JUCE_WINDOWS && JUCE_ASIO
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return new ASIOAudioIODeviceType(); }
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if (JUCE_LINUX || JUCE_BSD) && JUCE_ALSA
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return createAudioIODeviceType_ALSA_PCMDevices(); }
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if (JUCE_LINUX || JUCE_BSD) && JUCE_JACK
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return new JackAudioIODeviceType(); }
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if JUCE_LINUX && JUCE_BELA
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() { return new BelaAudioIODeviceType(); }
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if JUCE_ANDROID
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android()
|
||||
{
|
||||
#if JUCE_USE_ANDROID_OBOE
|
||||
if (isOboeAvailable())
|
||||
return nullptr;
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_ANDROID_OPENSLES
|
||||
if (isOpenSLAvailable())
|
||||
return nullptr;
|
||||
#endif
|
||||
|
||||
return new AndroidAudioIODeviceType();
|
||||
}
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if JUCE_ANDROID && JUCE_USE_ANDROID_OPENSLES
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES()
|
||||
{
|
||||
return isOpenSLAvailable() ? new OpenSLAudioDeviceType() : nullptr;
|
||||
}
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if JUCE_ANDROID && JUCE_USE_ANDROID_OBOE
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe()
|
||||
{
|
||||
return isOboeAvailable() ? new OboeAudioIODeviceType() : nullptr;
|
||||
}
|
||||
#else
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe() { return nullptr; }
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,188 +1,188 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a type of audio driver, such as DirectSound, ASIO, CoreAudio, etc.
|
||||
|
||||
To get a list of available audio driver types, use the AudioDeviceManager::createAudioDeviceTypes()
|
||||
method. Each of the objects returned can then be used to list the available
|
||||
devices of that type. E.g.
|
||||
@code
|
||||
OwnedArray<AudioIODeviceType> types;
|
||||
myAudioDeviceManager.createAudioDeviceTypes (types);
|
||||
|
||||
for (int i = 0; i < types.size(); ++i)
|
||||
{
|
||||
String typeName (types[i]->getTypeName()); // This will be things like "DirectSound", "CoreAudio", etc.
|
||||
|
||||
types[i]->scanForDevices(); // This must be called before getting the list of devices
|
||||
|
||||
StringArray deviceNames (types[i]->getDeviceNames()); // This will now return a list of available devices of this type
|
||||
|
||||
for (int j = 0; j < deviceNames.size(); ++j)
|
||||
{
|
||||
AudioIODevice* device = types[i]->createDevice (deviceNames [j]);
|
||||
|
||||
...
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
|
||||
For an easier way of managing audio devices and their settings, have a look at the
|
||||
AudioDeviceManager class.
|
||||
|
||||
@see AudioIODevice, AudioDeviceManager
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API AudioIODeviceType
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Returns the name of this type of driver that this object manages.
|
||||
|
||||
This will be something like "DirectSound", "ASIO", "CoreAudio", "ALSA", etc.
|
||||
*/
|
||||
const String& getTypeName() const noexcept { return typeName; }
|
||||
|
||||
//==============================================================================
|
||||
/** Refreshes the object's cached list of known devices.
|
||||
|
||||
This must be called at least once before calling getDeviceNames() or any of
|
||||
the other device creation methods.
|
||||
*/
|
||||
virtual void scanForDevices() = 0;
|
||||
|
||||
/** Returns the list of available devices of this type.
|
||||
|
||||
The scanForDevices() method must have been called to create this list.
|
||||
|
||||
@param wantInputNames for devices which have separate inputs and outputs
|
||||
this determines which list of names is returned
|
||||
*/
|
||||
virtual StringArray getDeviceNames (bool wantInputNames = false) const = 0;
|
||||
|
||||
/** Returns the name of the default device.
|
||||
|
||||
This will be one of the names from the getDeviceNames() list.
|
||||
|
||||
@param forInput if true, this means that a default input device should be
|
||||
returned; if false, it should return the default output
|
||||
*/
|
||||
virtual int getDefaultDeviceIndex (bool forInput) const = 0;
|
||||
|
||||
/** Returns the index of a given device in the list of device names.
|
||||
If asInput is true, it shows the index in the inputs list, otherwise it
|
||||
looks for it in the outputs list.
|
||||
*/
|
||||
virtual int getIndexOfDevice (AudioIODevice* device, bool asInput) const = 0;
|
||||
|
||||
/** Returns true if two different devices can be used for the input and output.
|
||||
*/
|
||||
virtual bool hasSeparateInputsAndOutputs() const = 0;
|
||||
|
||||
/** Creates one of the devices of this type.
|
||||
|
||||
The deviceName must be one of the strings returned by getDeviceNames(), and
|
||||
scanForDevices() must have been called before this method is used.
|
||||
*/
|
||||
virtual AudioIODevice* createDevice (const String& outputDeviceName,
|
||||
const String& inputDeviceName) = 0;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class for receiving events when audio devices are inserted or removed.
|
||||
|
||||
You can register an AudioIODeviceType::Listener with an~AudioIODeviceType object
|
||||
using the AudioIODeviceType::addListener() method, and it will be called when
|
||||
devices of that type are added or removed.
|
||||
|
||||
@see AudioIODeviceType::addListener, AudioIODeviceType::removeListener
|
||||
*/
|
||||
class Listener
|
||||
{
|
||||
public:
|
||||
virtual ~Listener() = default;
|
||||
|
||||
/** Called when the list of available audio devices changes. */
|
||||
virtual void audioDeviceListChanged() = 0;
|
||||
};
|
||||
|
||||
/** Adds a listener that will be called when this type of device is added or
|
||||
removed from the system.
|
||||
*/
|
||||
void addListener (Listener* listener);
|
||||
|
||||
/** Removes a listener that was previously added with addListener(). */
|
||||
void removeListener (Listener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** Destructor. */
|
||||
virtual ~AudioIODeviceType();
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a CoreAudio device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_CoreAudio();
|
||||
/** Creates an iOS device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_iOSAudio();
|
||||
/** Creates a WASAPI device type in the specified mode if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_WASAPI (WASAPIDeviceMode deviceMode);
|
||||
/** Creates a DirectSound device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_DirectSound();
|
||||
/** Creates an ASIO device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_ASIO();
|
||||
/** Creates an ALSA device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_ALSA();
|
||||
/** Creates a JACK device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_JACK();
|
||||
/** Creates an Android device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_Android();
|
||||
/** Creates an Android OpenSLES device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_OpenSLES();
|
||||
/** Creates an Oboe device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_Oboe();
|
||||
/** Creates a Bela device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_Bela();
|
||||
|
||||
#ifndef DOXYGEN
|
||||
[[deprecated ("You should call the method which takes a WASAPIDeviceMode instead.")]]
|
||||
static AudioIODeviceType* createAudioIODeviceType_WASAPI (bool exclusiveMode);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
explicit AudioIODeviceType (const String& typeName);
|
||||
|
||||
/** Synchronously calls all the registered device list change listeners. */
|
||||
void callDeviceChangeListeners();
|
||||
|
||||
private:
|
||||
String typeName;
|
||||
ListenerList<Listener> listeners;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (AudioIODeviceType)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a type of audio driver, such as DirectSound, ASIO, CoreAudio, etc.
|
||||
|
||||
To get a list of available audio driver types, use the AudioDeviceManager::createAudioDeviceTypes()
|
||||
method. Each of the objects returned can then be used to list the available
|
||||
devices of that type. E.g.
|
||||
@code
|
||||
OwnedArray<AudioIODeviceType> types;
|
||||
myAudioDeviceManager.createAudioDeviceTypes (types);
|
||||
|
||||
for (int i = 0; i < types.size(); ++i)
|
||||
{
|
||||
String typeName (types[i]->getTypeName()); // This will be things like "DirectSound", "CoreAudio", etc.
|
||||
|
||||
types[i]->scanForDevices(); // This must be called before getting the list of devices
|
||||
|
||||
StringArray deviceNames (types[i]->getDeviceNames()); // This will now return a list of available devices of this type
|
||||
|
||||
for (int j = 0; j < deviceNames.size(); ++j)
|
||||
{
|
||||
AudioIODevice* device = types[i]->createDevice (deviceNames [j]);
|
||||
|
||||
...
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
|
||||
For an easier way of managing audio devices and their settings, have a look at the
|
||||
AudioDeviceManager class.
|
||||
|
||||
@see AudioIODevice, AudioDeviceManager
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API AudioIODeviceType
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Returns the name of this type of driver that this object manages.
|
||||
|
||||
This will be something like "DirectSound", "ASIO", "CoreAudio", "ALSA", etc.
|
||||
*/
|
||||
const String& getTypeName() const noexcept { return typeName; }
|
||||
|
||||
//==============================================================================
|
||||
/** Refreshes the object's cached list of known devices.
|
||||
|
||||
This must be called at least once before calling getDeviceNames() or any of
|
||||
the other device creation methods.
|
||||
*/
|
||||
virtual void scanForDevices() = 0;
|
||||
|
||||
/** Returns the list of available devices of this type.
|
||||
|
||||
The scanForDevices() method must have been called to create this list.
|
||||
|
||||
@param wantInputNames for devices which have separate inputs and outputs
|
||||
this determines which list of names is returned
|
||||
*/
|
||||
virtual StringArray getDeviceNames (bool wantInputNames = false) const = 0;
|
||||
|
||||
/** Returns the name of the default device.
|
||||
|
||||
This will be one of the names from the getDeviceNames() list.
|
||||
|
||||
@param forInput if true, this means that a default input device should be
|
||||
returned; if false, it should return the default output
|
||||
*/
|
||||
virtual int getDefaultDeviceIndex (bool forInput) const = 0;
|
||||
|
||||
/** Returns the index of a given device in the list of device names.
|
||||
If asInput is true, it shows the index in the inputs list, otherwise it
|
||||
looks for it in the outputs list.
|
||||
*/
|
||||
virtual int getIndexOfDevice (AudioIODevice* device, bool asInput) const = 0;
|
||||
|
||||
/** Returns true if two different devices can be used for the input and output.
|
||||
*/
|
||||
virtual bool hasSeparateInputsAndOutputs() const = 0;
|
||||
|
||||
/** Creates one of the devices of this type.
|
||||
|
||||
The deviceName must be one of the strings returned by getDeviceNames(), and
|
||||
scanForDevices() must have been called before this method is used.
|
||||
*/
|
||||
virtual AudioIODevice* createDevice (const String& outputDeviceName,
|
||||
const String& inputDeviceName) = 0;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class for receiving events when audio devices are inserted or removed.
|
||||
|
||||
You can register an AudioIODeviceType::Listener with an~AudioIODeviceType object
|
||||
using the AudioIODeviceType::addListener() method, and it will be called when
|
||||
devices of that type are added or removed.
|
||||
|
||||
@see AudioIODeviceType::addListener, AudioIODeviceType::removeListener
|
||||
*/
|
||||
class Listener
|
||||
{
|
||||
public:
|
||||
virtual ~Listener() = default;
|
||||
|
||||
/** Called when the list of available audio devices changes. */
|
||||
virtual void audioDeviceListChanged() = 0;
|
||||
};
|
||||
|
||||
/** Adds a listener that will be called when this type of device is added or
|
||||
removed from the system.
|
||||
*/
|
||||
void addListener (Listener* listener);
|
||||
|
||||
/** Removes a listener that was previously added with addListener(). */
|
||||
void removeListener (Listener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** Destructor. */
|
||||
virtual ~AudioIODeviceType();
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a CoreAudio device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_CoreAudio();
|
||||
/** Creates an iOS device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_iOSAudio();
|
||||
/** Creates a WASAPI device type in the specified mode if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_WASAPI (WASAPIDeviceMode deviceMode);
|
||||
/** Creates a DirectSound device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_DirectSound();
|
||||
/** Creates an ASIO device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_ASIO();
|
||||
/** Creates an ALSA device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_ALSA();
|
||||
/** Creates a JACK device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_JACK();
|
||||
/** Creates an Android device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_Android();
|
||||
/** Creates an Android OpenSLES device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_OpenSLES();
|
||||
/** Creates an Oboe device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_Oboe();
|
||||
/** Creates a Bela device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_Bela();
|
||||
|
||||
#ifndef DOXYGEN
|
||||
[[deprecated ("You should call the method which takes a WASAPIDeviceMode instead.")]]
|
||||
static AudioIODeviceType* createAudioIODeviceType_WASAPI (bool exclusiveMode);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
explicit AudioIODeviceType (const String& typeName);
|
||||
|
||||
/** Synchronously calls all the registered device list change listeners. */
|
||||
void callDeviceChangeListeners();
|
||||
|
||||
private:
|
||||
String typeName;
|
||||
ListenerList<Listener> listeners;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (AudioIODeviceType)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
48
deps/juce/modules/juce_audio_devices/audio_io/juce_SampleRateHelpers.cpp
vendored
Normal file
48
deps/juce/modules/juce_audio_devices/audio_io/juce_SampleRateHelpers.cpp
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
namespace SampleRateHelpers
|
||||
{
|
||||
|
||||
static inline const std::vector<double>& getAllSampleRates()
|
||||
{
|
||||
static auto sampleRates = []
|
||||
{
|
||||
std::vector<double> result;
|
||||
constexpr double baseRates[] = { 8000.0, 11025.0, 12000.0 };
|
||||
constexpr double maxRate = 768000.0;
|
||||
|
||||
for (auto rate : baseRates)
|
||||
for (; rate <= maxRate; rate *= 2)
|
||||
result.insert (std::upper_bound (result.begin(), result.end(), rate),
|
||||
rate);
|
||||
|
||||
return result;
|
||||
}();
|
||||
|
||||
return sampleRates;
|
||||
}
|
||||
|
||||
} // namespace SampleRateHelpers
|
||||
} // namespace juce
|
@ -1,59 +1,59 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Contains functions to control the system's master volume.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API SystemAudioVolume
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Returns the operating system's current volume level in the range 0 to 1.0 */
|
||||
static float JUCE_CALLTYPE getGain();
|
||||
|
||||
/** Attempts to set the operating system's current volume level.
|
||||
@param newGain the level, between 0 and 1.0
|
||||
@returns true if the operation succeeds
|
||||
*/
|
||||
static bool JUCE_CALLTYPE setGain (float newGain);
|
||||
|
||||
/** Returns true if the system's audio output is currently muted. */
|
||||
static bool JUCE_CALLTYPE isMuted();
|
||||
|
||||
/** Attempts to mute the operating system's audio output.
|
||||
@param shouldBeMuted true if you want it to be muted
|
||||
@returns true if the operation succeeds
|
||||
*/
|
||||
static bool JUCE_CALLTYPE setMuted (bool shouldBeMuted);
|
||||
|
||||
private:
|
||||
SystemAudioVolume(); // Don't instantiate this class, just call its static fns.
|
||||
JUCE_DECLARE_NON_COPYABLE (SystemAudioVolume)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Contains functions to control the system's master volume.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API SystemAudioVolume
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Returns the operating system's current volume level in the range 0 to 1.0 */
|
||||
static float JUCE_CALLTYPE getGain();
|
||||
|
||||
/** Attempts to set the operating system's current volume level.
|
||||
@param newGain the level, between 0 and 1.0
|
||||
@returns true if the operation succeeds
|
||||
*/
|
||||
static bool JUCE_CALLTYPE setGain (float newGain);
|
||||
|
||||
/** Returns true if the system's audio output is currently muted. */
|
||||
static bool JUCE_CALLTYPE isMuted();
|
||||
|
||||
/** Attempts to mute the operating system's audio output.
|
||||
@param shouldBeMuted true if you want it to be muted
|
||||
@returns true if the operation succeeds
|
||||
*/
|
||||
static bool JUCE_CALLTYPE setMuted (bool shouldBeMuted);
|
||||
|
||||
private:
|
||||
SystemAudioVolume(); // Don't instantiate this class, just call its static fns.
|
||||
JUCE_DECLARE_NON_COPYABLE (SystemAudioVolume)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,240 +1,242 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifdef JUCE_AUDIO_DEVICES_H_INCLUDED
|
||||
/* When you add this cpp file to your project, you mustn't include it in a file where you've
|
||||
already included any other headers - just put it inside a file on its own, possibly with your config
|
||||
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
|
||||
header files that the compiler may be using.
|
||||
*/
|
||||
#error "Incorrect use of JUCE cpp file"
|
||||
#endif
|
||||
|
||||
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1
|
||||
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1
|
||||
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1
|
||||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1
|
||||
#define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1
|
||||
|
||||
#ifndef JUCE_USE_WINRT_MIDI
|
||||
#define JUCE_USE_WINRT_MIDI 0
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_WINRT_MIDI
|
||||
#define JUCE_EVENTS_INCLUDE_WINRT_WRAPPER 1
|
||||
#endif
|
||||
|
||||
#include "juce_audio_devices.h"
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
#include <juce_audio_basics/midi/ump/juce_UMP.h>
|
||||
#include "midi_io/ump/juce_UMPBytestreamInputHandler.h"
|
||||
#include "midi_io/ump/juce_UMPU32InputHandler.h"
|
||||
#endif
|
||||
|
||||
#if JUCE_MAC
|
||||
#define Point CarbonDummyPointName
|
||||
#define Component CarbonDummyCompName
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
#import <CoreMIDI/MIDIServices.h>
|
||||
#import <AudioToolbox/AudioServices.h>
|
||||
#undef Point
|
||||
#undef Component
|
||||
|
||||
#include "native/juce_mac_CoreAudio.cpp"
|
||||
#include "native/juce_mac_CoreMidi.mm"
|
||||
|
||||
#elif JUCE_IOS
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <CoreMIDI/MIDIServices.h>
|
||||
|
||||
#if TARGET_OS_SIMULATOR
|
||||
#import <CoreMIDI/MIDINetworkSession.h>
|
||||
#endif
|
||||
|
||||
#include "native/juce_ios_Audio.cpp"
|
||||
#include "native/juce_mac_CoreMidi.mm"
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_WINDOWS
|
||||
#if JUCE_WASAPI
|
||||
#include <mmreg.h>
|
||||
#include "native/juce_win32_WASAPI.cpp"
|
||||
#endif
|
||||
|
||||
#if JUCE_DIRECTSOUND
|
||||
#include "native/juce_win32_DirectSound.cpp"
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_WINRT_MIDI && (JUCE_MSVC || JUCE_CLANG)
|
||||
/* If you cannot find any of the header files below then you are probably
|
||||
attempting to use the Windows 10 Bluetooth Low Energy API. For this to work you
|
||||
need to install version 10.0.14393.0 of the Windows Standalone SDK and you may
|
||||
need to add the path to the WinRT headers to your build system. This path should
|
||||
have the form "C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\winrt".
|
||||
|
||||
Also please note that Microsoft's Bluetooth MIDI stack has multiple issues, so
|
||||
this API is EXPERIMENTAL - use at your own risk!
|
||||
*/
|
||||
#include <windows.devices.h>
|
||||
#include <windows.devices.midi.h>
|
||||
#include <windows.devices.enumeration.h>
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4265)
|
||||
#include <wrl/event.h>
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4467)
|
||||
#include <robuffer.h>
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
#endif
|
||||
|
||||
#include <juce_audio_basics/midi/juce_MidiDataConcatenator.h>
|
||||
#include "native/juce_win32_Midi.cpp"
|
||||
|
||||
#if JUCE_ASIO
|
||||
/* This is very frustrating - we only need to use a handful of definitions from
|
||||
a couple of the header files in Steinberg's ASIO SDK, and it'd be easy to copy
|
||||
about 30 lines of code into this cpp file to create a fully stand-alone ASIO
|
||||
implementation...
|
||||
|
||||
..unfortunately that would break Steinberg's license agreement for use of
|
||||
their SDK, so I'm not allowed to do this.
|
||||
|
||||
This means that anyone who wants to use JUCE's ASIO abilities will have to:
|
||||
|
||||
1) Agree to Steinberg's licensing terms and download the ASIO SDK
|
||||
(see http://www.steinberg.net/en/company/developers.html).
|
||||
|
||||
2) Enable this code with a global definition #define JUCE_ASIO 1.
|
||||
|
||||
3) Make sure that your header search path contains the iasiodrv.h file that
|
||||
comes with the SDK. (Only about a handful of the SDK header files are actually
|
||||
needed - so to simplify things, you could just copy these into your JUCE directory).
|
||||
*/
|
||||
#include <iasiodrv.h>
|
||||
#include "native/juce_win32_ASIO.cpp"
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_LINUX || JUCE_BSD
|
||||
#if JUCE_ALSA
|
||||
/* Got an include error here? If so, you've either not got ALSA installed, or you've
|
||||
not got your paths set up correctly to find its header files.
|
||||
|
||||
The package you need to install to get ASLA support is "libasound2-dev".
|
||||
|
||||
If you don't have the ALSA library and don't want to build JUCE with audio support,
|
||||
just set the JUCE_ALSA flag to 0.
|
||||
*/
|
||||
#include <alsa/asoundlib.h>
|
||||
#include "native/juce_linux_ALSA.cpp"
|
||||
#endif
|
||||
|
||||
#if JUCE_JACK
|
||||
/* Got an include error here? If so, you've either not got jack-audio-connection-kit
|
||||
installed, or you've not got your paths set up correctly to find its header files.
|
||||
|
||||
The package you need to install to get JACK support is "libjack-dev".
|
||||
|
||||
If you don't have the jack-audio-connection-kit library and don't want to build
|
||||
JUCE with low latency audio support, just set the JUCE_JACK flag to 0.
|
||||
*/
|
||||
#include <jack/jack.h>
|
||||
#include "native/juce_linux_JackAudio.cpp"
|
||||
#endif
|
||||
|
||||
#if (JUCE_LINUX && JUCE_BELA)
|
||||
/* Got an include error here? If so, you've either not got the bela headers
|
||||
installed, or you've not got your paths set up correctly to find its header
|
||||
files.
|
||||
*/
|
||||
#include <Bela.h>
|
||||
#include <Midi.h>
|
||||
#include <juce_audio_basics/midi/juce_MidiDataConcatenator.h>
|
||||
#include "native/juce_linux_Bela.cpp"
|
||||
#endif
|
||||
|
||||
#undef SIZEOF
|
||||
|
||||
#if ! JUCE_BELA
|
||||
#include <juce_audio_basics/midi/juce_MidiDataConcatenator.h>
|
||||
#include "native/juce_linux_Midi.cpp"
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_ANDROID
|
||||
|
||||
#include "native/juce_android_Audio.cpp"
|
||||
|
||||
#include <juce_audio_basics/midi/juce_MidiDataConcatenator.h>
|
||||
#include "native/juce_android_Midi.cpp"
|
||||
|
||||
#if JUCE_USE_ANDROID_OPENSLES || JUCE_USE_ANDROID_OBOE
|
||||
#include "native/juce_android_HighPerformanceAudioHelpers.h"
|
||||
|
||||
#if JUCE_USE_ANDROID_OPENSLES
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
#include <SLES/OpenSLES_AndroidConfiguration.h>
|
||||
#include "native/juce_android_OpenSL.cpp"
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_ANDROID_OBOE
|
||||
#if JUCE_USE_ANDROID_OPENSLES
|
||||
#error "Oboe cannot be enabled at the same time as openSL! Please disable JUCE_USE_ANDROID_OPENSLES"
|
||||
#endif
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunused-parameter",
|
||||
"-Wzero-as-null-pointer-constant",
|
||||
"-Winconsistent-missing-destructor-override",
|
||||
"-Wshadow-field-in-constructor",
|
||||
"-Wshadow-field")
|
||||
#include <oboe/Oboe.h>
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
#include "native/juce_android_Oboe.cpp"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if ! JUCE_SYSTEMAUDIOVOL_IMPLEMENTED
|
||||
namespace juce
|
||||
{
|
||||
// None of these methods are available. (On Windows you might need to enable WASAPI for this)
|
||||
float JUCE_CALLTYPE SystemAudioVolume::getGain() { jassertfalse; return 0.0f; }
|
||||
bool JUCE_CALLTYPE SystemAudioVolume::setGain (float) { jassertfalse; return false; }
|
||||
bool JUCE_CALLTYPE SystemAudioVolume::isMuted() { jassertfalse; return false; }
|
||||
bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool) { jassertfalse; return false; }
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "audio_io/juce_AudioDeviceManager.cpp"
|
||||
#include "audio_io/juce_AudioIODevice.cpp"
|
||||
#include "audio_io/juce_AudioIODeviceType.cpp"
|
||||
#include "midi_io/juce_MidiMessageCollector.cpp"
|
||||
#include "midi_io/juce_MidiDevices.cpp"
|
||||
#include "sources/juce_AudioSourcePlayer.cpp"
|
||||
#include "sources/juce_AudioTransportSource.cpp"
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifdef JUCE_AUDIO_DEVICES_H_INCLUDED
|
||||
/* When you add this cpp file to your project, you mustn't include it in a file where you've
|
||||
already included any other headers - just put it inside a file on its own, possibly with your config
|
||||
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
|
||||
header files that the compiler may be using.
|
||||
*/
|
||||
#error "Incorrect use of JUCE cpp file"
|
||||
#endif
|
||||
|
||||
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1
|
||||
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1
|
||||
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1
|
||||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1
|
||||
#define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1
|
||||
|
||||
#ifndef JUCE_USE_WINRT_MIDI
|
||||
#define JUCE_USE_WINRT_MIDI 0
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_WINRT_MIDI
|
||||
#define JUCE_EVENTS_INCLUDE_WINRT_WRAPPER 1
|
||||
#endif
|
||||
|
||||
#include "juce_audio_devices.h"
|
||||
|
||||
#include "audio_io/juce_SampleRateHelpers.cpp"
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
#include <juce_audio_basics/midi/ump/juce_UMP.h>
|
||||
#include "midi_io/ump/juce_UMPBytestreamInputHandler.h"
|
||||
#include "midi_io/ump/juce_UMPU32InputHandler.h"
|
||||
#endif
|
||||
|
||||
#if JUCE_MAC
|
||||
#define Point CarbonDummyPointName
|
||||
#define Component CarbonDummyCompName
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
#import <CoreMIDI/MIDIServices.h>
|
||||
#import <AudioToolbox/AudioServices.h>
|
||||
#undef Point
|
||||
#undef Component
|
||||
|
||||
#include "native/juce_mac_CoreAudio.cpp"
|
||||
#include "native/juce_mac_CoreMidi.mm"
|
||||
|
||||
#elif JUCE_IOS
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <CoreMIDI/MIDIServices.h>
|
||||
|
||||
#if TARGET_OS_SIMULATOR
|
||||
#import <CoreMIDI/MIDINetworkSession.h>
|
||||
#endif
|
||||
|
||||
#include "native/juce_ios_Audio.cpp"
|
||||
#include "native/juce_mac_CoreMidi.mm"
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_WINDOWS
|
||||
#if JUCE_WASAPI
|
||||
#include <mmreg.h>
|
||||
#include "native/juce_win32_WASAPI.cpp"
|
||||
#endif
|
||||
|
||||
#if JUCE_DIRECTSOUND
|
||||
#include "native/juce_win32_DirectSound.cpp"
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_WINRT_MIDI && (JUCE_MSVC || JUCE_CLANG)
|
||||
/* If you cannot find any of the header files below then you are probably
|
||||
attempting to use the Windows 10 Bluetooth Low Energy API. For this to work you
|
||||
need to install version 10.0.14393.0 of the Windows Standalone SDK and you may
|
||||
need to add the path to the WinRT headers to your build system. This path should
|
||||
have the form "C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\winrt".
|
||||
|
||||
Also please note that Microsoft's Bluetooth MIDI stack has multiple issues, so
|
||||
this API is EXPERIMENTAL - use at your own risk!
|
||||
*/
|
||||
#include <windows.devices.h>
|
||||
#include <windows.devices.midi.h>
|
||||
#include <windows.devices.enumeration.h>
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4265)
|
||||
#include <wrl/event.h>
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4467)
|
||||
#include <robuffer.h>
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
#endif
|
||||
|
||||
#include <juce_audio_basics/midi/juce_MidiDataConcatenator.h>
|
||||
#include "native/juce_win32_Midi.cpp"
|
||||
|
||||
#if JUCE_ASIO
|
||||
/* This is very frustrating - we only need to use a handful of definitions from
|
||||
a couple of the header files in Steinberg's ASIO SDK, and it'd be easy to copy
|
||||
about 30 lines of code into this cpp file to create a fully stand-alone ASIO
|
||||
implementation...
|
||||
|
||||
..unfortunately that would break Steinberg's license agreement for use of
|
||||
their SDK, so I'm not allowed to do this.
|
||||
|
||||
This means that anyone who wants to use JUCE's ASIO abilities will have to:
|
||||
|
||||
1) Agree to Steinberg's licensing terms and download the ASIO SDK
|
||||
(see http://www.steinberg.net/en/company/developers.html).
|
||||
|
||||
2) Enable this code with a global definition #define JUCE_ASIO 1.
|
||||
|
||||
3) Make sure that your header search path contains the iasiodrv.h file that
|
||||
comes with the SDK. (Only about a handful of the SDK header files are actually
|
||||
needed - so to simplify things, you could just copy these into your JUCE directory).
|
||||
*/
|
||||
#include <iasiodrv.h>
|
||||
#include "native/juce_win32_ASIO.cpp"
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_LINUX || JUCE_BSD
|
||||
#if JUCE_ALSA
|
||||
/* Got an include error here? If so, you've either not got ALSA installed, or you've
|
||||
not got your paths set up correctly to find its header files.
|
||||
|
||||
The package you need to install to get ASLA support is "libasound2-dev".
|
||||
|
||||
If you don't have the ALSA library and don't want to build JUCE with audio support,
|
||||
just set the JUCE_ALSA flag to 0.
|
||||
*/
|
||||
#include <alsa/asoundlib.h>
|
||||
#include "native/juce_linux_ALSA.cpp"
|
||||
#endif
|
||||
|
||||
#if JUCE_JACK
|
||||
/* Got an include error here? If so, you've either not got jack-audio-connection-kit
|
||||
installed, or you've not got your paths set up correctly to find its header files.
|
||||
|
||||
The package you need to install to get JACK support is "libjack-dev".
|
||||
|
||||
If you don't have the jack-audio-connection-kit library and don't want to build
|
||||
JUCE with low latency audio support, just set the JUCE_JACK flag to 0.
|
||||
*/
|
||||
#include <jack/jack.h>
|
||||
#include "native/juce_linux_JackAudio.cpp"
|
||||
#endif
|
||||
|
||||
#if (JUCE_LINUX && JUCE_BELA)
|
||||
/* Got an include error here? If so, you've either not got the bela headers
|
||||
installed, or you've not got your paths set up correctly to find its header
|
||||
files.
|
||||
*/
|
||||
#include <Bela.h>
|
||||
#include <Midi.h>
|
||||
#include <juce_audio_basics/midi/juce_MidiDataConcatenator.h>
|
||||
#include "native/juce_linux_Bela.cpp"
|
||||
#endif
|
||||
|
||||
#undef SIZEOF
|
||||
|
||||
#if ! JUCE_BELA
|
||||
#include <juce_audio_basics/midi/juce_MidiDataConcatenator.h>
|
||||
#include "native/juce_linux_Midi.cpp"
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_ANDROID
|
||||
|
||||
#include "native/juce_android_Audio.cpp"
|
||||
|
||||
#include <juce_audio_basics/midi/juce_MidiDataConcatenator.h>
|
||||
#include "native/juce_android_Midi.cpp"
|
||||
|
||||
#if JUCE_USE_ANDROID_OPENSLES || JUCE_USE_ANDROID_OBOE
|
||||
#include "native/juce_android_HighPerformanceAudioHelpers.h"
|
||||
|
||||
#if JUCE_USE_ANDROID_OPENSLES
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
#include <SLES/OpenSLES_AndroidConfiguration.h>
|
||||
#include "native/juce_android_OpenSL.cpp"
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_ANDROID_OBOE
|
||||
#if JUCE_USE_ANDROID_OPENSLES
|
||||
#error "Oboe cannot be enabled at the same time as openSL! Please disable JUCE_USE_ANDROID_OPENSLES"
|
||||
#endif
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunused-parameter",
|
||||
"-Wzero-as-null-pointer-constant",
|
||||
"-Winconsistent-missing-destructor-override",
|
||||
"-Wshadow-field-in-constructor",
|
||||
"-Wshadow-field")
|
||||
#include <oboe/Oboe.h>
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
#include "native/juce_android_Oboe.cpp"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if ! JUCE_SYSTEMAUDIOVOL_IMPLEMENTED
|
||||
namespace juce
|
||||
{
|
||||
// None of these methods are available. (On Windows you might need to enable WASAPI for this)
|
||||
float JUCE_CALLTYPE SystemAudioVolume::getGain() { jassertfalse; return 0.0f; }
|
||||
bool JUCE_CALLTYPE SystemAudioVolume::setGain (float) { jassertfalse; return false; }
|
||||
bool JUCE_CALLTYPE SystemAudioVolume::isMuted() { jassertfalse; return false; }
|
||||
bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool) { jassertfalse; return false; }
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "audio_io/juce_AudioDeviceManager.cpp"
|
||||
#include "audio_io/juce_AudioIODevice.cpp"
|
||||
#include "audio_io/juce_AudioIODeviceType.cpp"
|
||||
#include "midi_io/juce_MidiMessageCollector.cpp"
|
||||
#include "midi_io/juce_MidiDevices.cpp"
|
||||
#include "sources/juce_AudioSourcePlayer.cpp"
|
||||
#include "sources/juce_AudioTransportSource.cpp"
|
||||
|
@ -1,190 +1,190 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
The block below describes the properties of this module, and is read by
|
||||
the Projucer to automatically generate project code that uses it.
|
||||
For details about the syntax and how to create or use a module, see the
|
||||
JUCE Module Format.md file.
|
||||
|
||||
|
||||
BEGIN_JUCE_MODULE_DECLARATION
|
||||
|
||||
ID: juce_audio_devices
|
||||
vendor: juce
|
||||
version: 6.1.2
|
||||
name: JUCE audio and MIDI I/O device classes
|
||||
description: Classes to play and record from audio and MIDI I/O devices
|
||||
website: http://www.juce.com/juce
|
||||
license: ISC
|
||||
minimumCppStandard: 14
|
||||
|
||||
dependencies: juce_audio_basics, juce_events
|
||||
OSXFrameworks: CoreAudio CoreMIDI AudioToolbox
|
||||
iOSFrameworks: CoreAudio CoreMIDI AudioToolbox AVFoundation
|
||||
linuxPackages: alsa
|
||||
mingwLibs: winmm
|
||||
|
||||
END_JUCE_MODULE_DECLARATION
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#pragma once
|
||||
#define JUCE_AUDIO_DEVICES_H_INCLUDED
|
||||
|
||||
#include <juce_events/juce_events.h>
|
||||
#include <juce_audio_basics/juce_audio_basics.h>
|
||||
|
||||
#if JUCE_MODULE_AVAILABLE_juce_graphics
|
||||
#include <juce_graphics/juce_graphics.h>
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/** Config: JUCE_USE_WINRT_MIDI
|
||||
Enables the use of the Windows Runtime API for MIDI, allowing connections
|
||||
to Bluetooth Low Energy devices on Windows 10 version 1809 (October 2018
|
||||
Update) and later. If you enable this flag then older versions of Windows
|
||||
will automatically fall back to using the regular Win32 MIDI API.
|
||||
|
||||
You will need version 10.0.14393.0 of the Windows Standalone SDK to compile
|
||||
and you may need to add the path to the WinRT headers. The path to the
|
||||
headers will be something similar to
|
||||
"C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\winrt".
|
||||
*/
|
||||
#ifndef JUCE_USE_WINRT_MIDI
|
||||
#define JUCE_USE_WINRT_MIDI 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_ASIO
|
||||
Enables ASIO audio devices (MS Windows only).
|
||||
Turning this on means that you'll need to have the Steinberg ASIO SDK installed
|
||||
on your Windows build machine.
|
||||
|
||||
See the comments in the ASIOAudioIODevice class's header file for more
|
||||
info about this.
|
||||
*/
|
||||
#ifndef JUCE_ASIO
|
||||
#define JUCE_ASIO 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_WASAPI
|
||||
Enables WASAPI audio devices (Windows Vista and above).
|
||||
*/
|
||||
#ifndef JUCE_WASAPI
|
||||
#define JUCE_WASAPI 1
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_DIRECTSOUND
|
||||
Enables DirectSound audio (MS Windows only).
|
||||
*/
|
||||
#ifndef JUCE_DIRECTSOUND
|
||||
#define JUCE_DIRECTSOUND 1
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_ALSA
|
||||
Enables ALSA audio devices (Linux only).
|
||||
*/
|
||||
#ifndef JUCE_ALSA
|
||||
#define JUCE_ALSA 1
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_JACK
|
||||
Enables JACK audio devices (Linux only).
|
||||
*/
|
||||
#ifndef JUCE_JACK
|
||||
#define JUCE_JACK 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_BELA
|
||||
Enables Bela audio devices on Bela boards.
|
||||
*/
|
||||
#ifndef JUCE_BELA
|
||||
#define JUCE_BELA 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_ANDROID_OBOE
|
||||
Enables Oboe devices (Android only).
|
||||
*/
|
||||
#ifndef JUCE_USE_ANDROID_OBOE
|
||||
#define JUCE_USE_ANDROID_OBOE 1
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_OBOE_STABILIZED_CALLBACK
|
||||
If JUCE_USE_ANDROID_OBOE is enabled, enabling this will wrap output audio
|
||||
streams in the oboe::StabilizedCallback class. This class attempts to keep
|
||||
the CPU spinning to avoid it being scaled down on certain devices.
|
||||
(Android only).
|
||||
*/
|
||||
#ifndef JUCE_USE_ANDROID_OBOE_STABILIZED_CALLBACK
|
||||
#define JUCE_USE_ANDROID_OBOE_STABILIZED_CALLBACK 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_ANDROID_OPENSLES
|
||||
Enables OpenSLES devices (Android only).
|
||||
*/
|
||||
#ifndef JUCE_USE_ANDROID_OPENSLES
|
||||
#if ! JUCE_USE_ANDROID_OBOE
|
||||
#define JUCE_USE_ANDROID_OPENSLES 1
|
||||
#else
|
||||
#define JUCE_USE_ANDROID_OPENSLES 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS
|
||||
Turning this on gives your app exclusive access to the system's audio
|
||||
on platforms which support it (currently iOS only).
|
||||
*/
|
||||
#ifndef JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS
|
||||
#define JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS 0
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#include "midi_io/juce_MidiDevices.h"
|
||||
#include "midi_io/juce_MidiMessageCollector.h"
|
||||
|
||||
namespace juce
|
||||
{
|
||||
/** Available modes for the WASAPI audio device.
|
||||
|
||||
Pass one of these to the AudioIODeviceType::createAudioIODeviceType_WASAPI()
|
||||
method to create a WASAPI AudioIODeviceType object in this mode.
|
||||
*/
|
||||
enum class WASAPIDeviceMode
|
||||
{
|
||||
shared,
|
||||
exclusive,
|
||||
sharedLowLatency
|
||||
};
|
||||
}
|
||||
|
||||
#include "audio_io/juce_AudioIODevice.h"
|
||||
#include "audio_io/juce_AudioIODeviceType.h"
|
||||
#include "audio_io/juce_SystemAudioVolume.h"
|
||||
#include "sources/juce_AudioSourcePlayer.h"
|
||||
#include "sources/juce_AudioTransportSource.h"
|
||||
#include "audio_io/juce_AudioDeviceManager.h"
|
||||
|
||||
#if JUCE_IOS
|
||||
#include "native/juce_ios_Audio.h"
|
||||
#endif
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
The block below describes the properties of this module, and is read by
|
||||
the Projucer to automatically generate project code that uses it.
|
||||
For details about the syntax and how to create or use a module, see the
|
||||
JUCE Module Format.md file.
|
||||
|
||||
|
||||
BEGIN_JUCE_MODULE_DECLARATION
|
||||
|
||||
ID: juce_audio_devices
|
||||
vendor: juce
|
||||
version: 7.0.2
|
||||
name: JUCE audio and MIDI I/O device classes
|
||||
description: Classes to play and record from audio and MIDI I/O devices
|
||||
website: http://www.juce.com/juce
|
||||
license: ISC
|
||||
minimumCppStandard: 14
|
||||
|
||||
dependencies: juce_audio_basics, juce_events
|
||||
OSXFrameworks: CoreAudio CoreMIDI AudioToolbox
|
||||
iOSFrameworks: CoreAudio CoreMIDI AudioToolbox AVFoundation
|
||||
linuxPackages: alsa
|
||||
mingwLibs: winmm
|
||||
|
||||
END_JUCE_MODULE_DECLARATION
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#pragma once
|
||||
#define JUCE_AUDIO_DEVICES_H_INCLUDED
|
||||
|
||||
#include <juce_events/juce_events.h>
|
||||
#include <juce_audio_basics/juce_audio_basics.h>
|
||||
|
||||
#if JUCE_MODULE_AVAILABLE_juce_graphics
|
||||
#include <juce_graphics/juce_graphics.h>
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/** Config: JUCE_USE_WINRT_MIDI
|
||||
Enables the use of the Windows Runtime API for MIDI, allowing connections
|
||||
to Bluetooth Low Energy devices on Windows 10 version 1809 (October 2018
|
||||
Update) and later. If you enable this flag then older versions of Windows
|
||||
will automatically fall back to using the regular Win32 MIDI API.
|
||||
|
||||
You will need version 10.0.14393.0 of the Windows Standalone SDK to compile
|
||||
and you may need to add the path to the WinRT headers. The path to the
|
||||
headers will be something similar to
|
||||
"C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\winrt".
|
||||
*/
|
||||
#ifndef JUCE_USE_WINRT_MIDI
|
||||
#define JUCE_USE_WINRT_MIDI 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_ASIO
|
||||
Enables ASIO audio devices (MS Windows only).
|
||||
Turning this on means that you'll need to have the Steinberg ASIO SDK installed
|
||||
on your Windows build machine.
|
||||
|
||||
See the comments in the ASIOAudioIODevice class's header file for more
|
||||
info about this.
|
||||
*/
|
||||
#ifndef JUCE_ASIO
|
||||
#define JUCE_ASIO 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_WASAPI
|
||||
Enables WASAPI audio devices (Windows Vista and above).
|
||||
*/
|
||||
#ifndef JUCE_WASAPI
|
||||
#define JUCE_WASAPI 1
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_DIRECTSOUND
|
||||
Enables DirectSound audio (MS Windows only).
|
||||
*/
|
||||
#ifndef JUCE_DIRECTSOUND
|
||||
#define JUCE_DIRECTSOUND 1
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_ALSA
|
||||
Enables ALSA audio devices (Linux only).
|
||||
*/
|
||||
#ifndef JUCE_ALSA
|
||||
#define JUCE_ALSA 1
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_JACK
|
||||
Enables JACK audio devices (Linux only).
|
||||
*/
|
||||
#ifndef JUCE_JACK
|
||||
#define JUCE_JACK 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_BELA
|
||||
Enables Bela audio devices on Bela boards.
|
||||
*/
|
||||
#ifndef JUCE_BELA
|
||||
#define JUCE_BELA 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_ANDROID_OBOE
|
||||
Enables Oboe devices (Android only).
|
||||
*/
|
||||
#ifndef JUCE_USE_ANDROID_OBOE
|
||||
#define JUCE_USE_ANDROID_OBOE 1
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_OBOE_STABILIZED_CALLBACK
|
||||
If JUCE_USE_ANDROID_OBOE is enabled, enabling this will wrap output audio
|
||||
streams in the oboe::StabilizedCallback class. This class attempts to keep
|
||||
the CPU spinning to avoid it being scaled down on certain devices.
|
||||
(Android only).
|
||||
*/
|
||||
#ifndef JUCE_USE_ANDROID_OBOE_STABILIZED_CALLBACK
|
||||
#define JUCE_USE_ANDROID_OBOE_STABILIZED_CALLBACK 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_ANDROID_OPENSLES
|
||||
Enables OpenSLES devices (Android only).
|
||||
*/
|
||||
#ifndef JUCE_USE_ANDROID_OPENSLES
|
||||
#if ! JUCE_USE_ANDROID_OBOE
|
||||
#define JUCE_USE_ANDROID_OPENSLES 1
|
||||
#else
|
||||
#define JUCE_USE_ANDROID_OPENSLES 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS
|
||||
Turning this on gives your app exclusive access to the system's audio
|
||||
on platforms which support it (currently iOS only).
|
||||
*/
|
||||
#ifndef JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS
|
||||
#define JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS 0
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#include "midi_io/juce_MidiDevices.h"
|
||||
#include "midi_io/juce_MidiMessageCollector.h"
|
||||
|
||||
namespace juce
|
||||
{
|
||||
/** Available modes for the WASAPI audio device.
|
||||
|
||||
Pass one of these to the AudioIODeviceType::createAudioIODeviceType_WASAPI()
|
||||
method to create a WASAPI AudioIODeviceType object in this mode.
|
||||
*/
|
||||
enum class WASAPIDeviceMode
|
||||
{
|
||||
shared,
|
||||
exclusive,
|
||||
sharedLowLatency
|
||||
};
|
||||
}
|
||||
|
||||
#include "audio_io/juce_AudioIODevice.h"
|
||||
#include "audio_io/juce_AudioIODeviceType.h"
|
||||
#include "audio_io/juce_SystemAudioVolume.h"
|
||||
#include "sources/juce_AudioSourcePlayer.h"
|
||||
#include "sources/juce_AudioTransportSource.h"
|
||||
#include "audio_io/juce_AudioDeviceManager.h"
|
||||
|
||||
#if JUCE_IOS
|
||||
#include "native/juce_ios_Audio.h"
|
||||
#endif
|
||||
|
@ -2,7 +2,7 @@
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
@ -1,153 +1,153 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
MidiOutput::MidiOutput (const String& deviceName, const String& deviceIdentifier)
|
||||
: Thread ("midi out"), deviceInfo (deviceName, deviceIdentifier)
|
||||
{
|
||||
}
|
||||
|
||||
void MidiOutput::sendBlockOfMessagesNow (const MidiBuffer& buffer)
|
||||
{
|
||||
for (const auto metadata : buffer)
|
||||
sendMessageNow (metadata.getMessage());
|
||||
}
|
||||
|
||||
void MidiOutput::sendBlockOfMessages (const MidiBuffer& buffer,
|
||||
double millisecondCounterToStartAt,
|
||||
double samplesPerSecondForBuffer)
|
||||
{
|
||||
// You've got to call startBackgroundThread() for this to actually work..
|
||||
jassert (isThreadRunning());
|
||||
|
||||
// this needs to be a value in the future - RTFM for this method!
|
||||
jassert (millisecondCounterToStartAt > 0);
|
||||
|
||||
auto timeScaleFactor = 1000.0 / samplesPerSecondForBuffer;
|
||||
|
||||
for (const auto metadata : buffer)
|
||||
{
|
||||
auto eventTime = millisecondCounterToStartAt + timeScaleFactor * metadata.samplePosition;
|
||||
auto* m = new PendingMessage (metadata.data, metadata.numBytes, eventTime);
|
||||
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (firstMessage == nullptr || firstMessage->message.getTimeStamp() > eventTime)
|
||||
{
|
||||
m->next = firstMessage;
|
||||
firstMessage = m;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* mm = firstMessage;
|
||||
|
||||
while (mm->next != nullptr && mm->next->message.getTimeStamp() <= eventTime)
|
||||
mm = mm->next;
|
||||
|
||||
m->next = mm->next;
|
||||
mm->next = m;
|
||||
}
|
||||
}
|
||||
|
||||
notify();
|
||||
}
|
||||
|
||||
void MidiOutput::clearAllPendingMessages()
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
while (firstMessage != nullptr)
|
||||
{
|
||||
auto* m = firstMessage;
|
||||
firstMessage = firstMessage->next;
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
|
||||
void MidiOutput::startBackgroundThread()
|
||||
{
|
||||
startThread (9);
|
||||
}
|
||||
|
||||
void MidiOutput::stopBackgroundThread()
|
||||
{
|
||||
stopThread (5000);
|
||||
}
|
||||
|
||||
void MidiOutput::run()
|
||||
{
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
auto now = Time::getMillisecondCounter();
|
||||
uint32 eventTime = 0;
|
||||
uint32 timeToWait = 500;
|
||||
|
||||
PendingMessage* message;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
message = firstMessage;
|
||||
|
||||
if (message != nullptr)
|
||||
{
|
||||
eventTime = (uint32) roundToInt (message->message.getTimeStamp());
|
||||
|
||||
if (eventTime > now + 20)
|
||||
{
|
||||
timeToWait = eventTime - (now + 20);
|
||||
message = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
firstMessage = message->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (message != nullptr)
|
||||
{
|
||||
std::unique_ptr<PendingMessage> messageDeleter (message);
|
||||
|
||||
if (eventTime > now)
|
||||
{
|
||||
Time::waitForMillisecondCounter (eventTime);
|
||||
|
||||
if (threadShouldExit())
|
||||
break;
|
||||
}
|
||||
|
||||
if (eventTime > now - 200)
|
||||
sendMessageNow (message->message);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassert (timeToWait < 1000 * 30);
|
||||
wait ((int) timeToWait);
|
||||
}
|
||||
}
|
||||
|
||||
clearAllPendingMessages();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
MidiOutput::MidiOutput (const String& deviceName, const String& deviceIdentifier)
|
||||
: Thread ("midi out"), deviceInfo (deviceName, deviceIdentifier)
|
||||
{
|
||||
}
|
||||
|
||||
void MidiOutput::sendBlockOfMessagesNow (const MidiBuffer& buffer)
|
||||
{
|
||||
for (const auto metadata : buffer)
|
||||
sendMessageNow (metadata.getMessage());
|
||||
}
|
||||
|
||||
void MidiOutput::sendBlockOfMessages (const MidiBuffer& buffer,
|
||||
double millisecondCounterToStartAt,
|
||||
double samplesPerSecondForBuffer)
|
||||
{
|
||||
// You've got to call startBackgroundThread() for this to actually work..
|
||||
jassert (isThreadRunning());
|
||||
|
||||
// this needs to be a value in the future - RTFM for this method!
|
||||
jassert (millisecondCounterToStartAt > 0);
|
||||
|
||||
auto timeScaleFactor = 1000.0 / samplesPerSecondForBuffer;
|
||||
|
||||
for (const auto metadata : buffer)
|
||||
{
|
||||
auto eventTime = millisecondCounterToStartAt + timeScaleFactor * metadata.samplePosition;
|
||||
auto* m = new PendingMessage (metadata.data, metadata.numBytes, eventTime);
|
||||
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (firstMessage == nullptr || firstMessage->message.getTimeStamp() > eventTime)
|
||||
{
|
||||
m->next = firstMessage;
|
||||
firstMessage = m;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* mm = firstMessage;
|
||||
|
||||
while (mm->next != nullptr && mm->next->message.getTimeStamp() <= eventTime)
|
||||
mm = mm->next;
|
||||
|
||||
m->next = mm->next;
|
||||
mm->next = m;
|
||||
}
|
||||
}
|
||||
|
||||
notify();
|
||||
}
|
||||
|
||||
void MidiOutput::clearAllPendingMessages()
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
while (firstMessage != nullptr)
|
||||
{
|
||||
auto* m = firstMessage;
|
||||
firstMessage = firstMessage->next;
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
|
||||
void MidiOutput::startBackgroundThread()
|
||||
{
|
||||
startThread (9);
|
||||
}
|
||||
|
||||
void MidiOutput::stopBackgroundThread()
|
||||
{
|
||||
stopThread (5000);
|
||||
}
|
||||
|
||||
void MidiOutput::run()
|
||||
{
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
auto now = Time::getMillisecondCounter();
|
||||
uint32 eventTime = 0;
|
||||
uint32 timeToWait = 500;
|
||||
|
||||
PendingMessage* message;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
message = firstMessage;
|
||||
|
||||
if (message != nullptr)
|
||||
{
|
||||
eventTime = (uint32) roundToInt (message->message.getTimeStamp());
|
||||
|
||||
if (eventTime > now + 20)
|
||||
{
|
||||
timeToWait = eventTime - (now + 20);
|
||||
message = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
firstMessage = message->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (message != nullptr)
|
||||
{
|
||||
std::unique_ptr<PendingMessage> messageDeleter (message);
|
||||
|
||||
if (eventTime > now)
|
||||
{
|
||||
Time::waitForMillisecondCounter (eventTime);
|
||||
|
||||
if (threadShouldExit())
|
||||
break;
|
||||
}
|
||||
|
||||
if (eventTime > now - 200)
|
||||
sendMessageNow (message->message);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassert (timeToWait < 1000 * 30);
|
||||
wait ((int) timeToWait);
|
||||
}
|
||||
}
|
||||
|
||||
clearAllPendingMessages();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,391 +1,391 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
//==============================================================================
|
||||
/**
|
||||
This struct contains information about a MIDI input or output device.
|
||||
|
||||
You can get one of these structs by calling the static getAvailableDevices() or
|
||||
getDefaultDevice() methods of MidiInput and MidiOutput or by calling getDeviceInfo()
|
||||
on an instance of these classes. Devices can be opened by passing the identifier to
|
||||
the openDevice() method.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct MidiDeviceInfo
|
||||
{
|
||||
MidiDeviceInfo() = default;
|
||||
|
||||
MidiDeviceInfo (const String& deviceName, const String& deviceIdentifier)
|
||||
: name (deviceName), identifier (deviceIdentifier)
|
||||
{
|
||||
}
|
||||
|
||||
/** The name of this device.
|
||||
|
||||
This will be provided by the OS unless the device has been created with the
|
||||
createNewDevice() method.
|
||||
|
||||
Note that the name is not guaranteed to be unique and two devices with the
|
||||
same name will be indistinguishable. If you want to address a specific device
|
||||
it is better to use the identifier.
|
||||
*/
|
||||
String name;
|
||||
|
||||
/** The identifier for this device.
|
||||
|
||||
This will be provided by the OS and it's format will differ on different systems
|
||||
e.g. on macOS it will be a number whereas on Windows it will be a long alphanumeric string.
|
||||
*/
|
||||
String identifier;
|
||||
|
||||
//==============================================================================
|
||||
bool operator== (const MidiDeviceInfo& other) const noexcept { return name == other.name && identifier == other.identifier; }
|
||||
bool operator!= (const MidiDeviceInfo& other) const noexcept { return ! operator== (other); }
|
||||
};
|
||||
|
||||
class MidiInputCallback;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a midi input device.
|
||||
|
||||
To create one of these, use the static getAvailableDevices() method to find out what
|
||||
inputs are available, and then use the openDevice() method to try to open one.
|
||||
|
||||
@see MidiOutput
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API MidiInput final
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Returns a list of the available midi input devices.
|
||||
|
||||
You can open one of the devices by passing its identifier into the openDevice() method.
|
||||
|
||||
@see MidiDeviceInfo, getDevices, getDefaultDeviceIndex, openDevice
|
||||
*/
|
||||
static Array<MidiDeviceInfo> getAvailableDevices();
|
||||
|
||||
/** Returns the MidiDeviceInfo of the default midi input device to use. */
|
||||
static MidiDeviceInfo getDefaultDevice();
|
||||
|
||||
/** Tries to open one of the midi input devices.
|
||||
|
||||
This will return a MidiInput object if it manages to open it, you can then
|
||||
call start() and stop() on this device.
|
||||
|
||||
If the device can't be opened, this will return an empty object.
|
||||
|
||||
@param deviceIdentifier the ID of the device to open - use the getAvailableDevices() method to
|
||||
find the available devices that can be opened
|
||||
@param callback the object that will receive the midi messages from this device
|
||||
|
||||
@see MidiInputCallback, getDevices
|
||||
*/
|
||||
static std::unique_ptr<MidiInput> openDevice (const String& deviceIdentifier, MidiInputCallback* callback);
|
||||
|
||||
#if JUCE_LINUX || JUCE_BSD || JUCE_MAC || JUCE_IOS || DOXYGEN
|
||||
/** This will try to create a new midi input device (only available on Linux, macOS and iOS).
|
||||
|
||||
This will attempt to create a new midi input device with the specified name for other
|
||||
apps to connect to.
|
||||
|
||||
NB - if you are calling this method on iOS you must have enabled the "Audio Background Capability"
|
||||
setting in the iOS exporter otherwise this method will fail.
|
||||
|
||||
Returns an empty object if a device can't be created.
|
||||
|
||||
@param deviceName the name of the device to create
|
||||
@param callback the object that will receive the midi messages from this device
|
||||
*/
|
||||
static std::unique_ptr<MidiInput> createNewDevice (const String& deviceName, MidiInputCallback* callback);
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/** Destructor. */
|
||||
~MidiInput();
|
||||
|
||||
/** Starts the device running.
|
||||
|
||||
After calling this, the device will start sending midi messages to the MidiInputCallback
|
||||
object that was specified when the openDevice() method was called.
|
||||
|
||||
@see stop
|
||||
*/
|
||||
void start();
|
||||
|
||||
/** Stops the device running.
|
||||
|
||||
@see start
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/** Returns the MidiDeviceInfo struct containing some information about this device. */
|
||||
MidiDeviceInfo getDeviceInfo() const noexcept { return deviceInfo; }
|
||||
|
||||
/** Returns the identifier of this device. */
|
||||
String getIdentifier() const noexcept { return deviceInfo.identifier; }
|
||||
|
||||
/** Returns the name of this device. */
|
||||
String getName() const noexcept { return deviceInfo.name; }
|
||||
|
||||
/** Sets a custom name for the device. */
|
||||
void setName (const String& newName) noexcept { deviceInfo.name = newName; }
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
[[deprecated ("Use getAvailableDevices instead.")]]
|
||||
static StringArray getDevices();
|
||||
[[deprecated ("Use getDefaultDevice instead.")]]
|
||||
static int getDefaultDeviceIndex();
|
||||
[[deprecated ("Use openDevice that takes a device identifier instead.")]]
|
||||
static std::unique_ptr<MidiInput> openDevice (int, MidiInputCallback*);
|
||||
#endif
|
||||
|
||||
/** @internal */
|
||||
class Pimpl;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
explicit MidiInput (const String&, const String&);
|
||||
|
||||
MidiDeviceInfo deviceInfo;
|
||||
|
||||
std::unique_ptr<Pimpl> internal;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInput)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Receives incoming messages from a physical MIDI input device.
|
||||
|
||||
This class is overridden to handle incoming midi messages. See the MidiInput
|
||||
class for more details.
|
||||
|
||||
@see MidiInput
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API MidiInputCallback
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~MidiInputCallback() = default;
|
||||
|
||||
/** Receives an incoming message.
|
||||
|
||||
A MidiInput object will call this method when a midi event arrives. It'll be
|
||||
called on a high-priority system thread, so avoid doing anything time-consuming
|
||||
in here, and avoid making any UI calls. You might find the MidiBuffer class helpful
|
||||
for queueing incoming messages for use later.
|
||||
|
||||
@param source the MidiInput object that generated the message
|
||||
@param message the incoming message. The message's timestamp is set to a value
|
||||
equivalent to (Time::getMillisecondCounter() / 1000.0) to specify the
|
||||
time when the message arrived
|
||||
*/
|
||||
virtual void handleIncomingMidiMessage (MidiInput* source,
|
||||
const MidiMessage& message) = 0;
|
||||
|
||||
/** Notification sent each time a packet of a multi-packet sysex message arrives.
|
||||
|
||||
If a long sysex message is broken up into multiple packets, this callback is made
|
||||
for each packet that arrives until the message is finished, at which point
|
||||
the normal handleIncomingMidiMessage() callback will be made with the entire
|
||||
message.
|
||||
|
||||
The message passed in will contain the start of a sysex, but won't be finished
|
||||
with the terminating 0xf7 byte.
|
||||
*/
|
||||
virtual void handlePartialSysexMessage (MidiInput* source,
|
||||
const uint8* messageData,
|
||||
int numBytesSoFar,
|
||||
double timestamp)
|
||||
{
|
||||
ignoreUnused (source, messageData, numBytesSoFar, timestamp);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a midi output device.
|
||||
|
||||
To create one of these, use the static getAvailableDevices() method to find out what
|
||||
outputs are available, and then use the openDevice() method to try to open one.
|
||||
|
||||
@see MidiInput
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API MidiOutput final : private Thread
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Returns a list of the available midi output devices.
|
||||
|
||||
You can open one of the devices by passing its identifier into the openDevice() method.
|
||||
|
||||
@see MidiDeviceInfo, getDevices, getDefaultDeviceIndex, openDevice
|
||||
*/
|
||||
static Array<MidiDeviceInfo> getAvailableDevices();
|
||||
|
||||
/** Returns the MidiDeviceInfo of the default midi output device to use. */
|
||||
static MidiDeviceInfo getDefaultDevice();
|
||||
|
||||
/** Tries to open one of the midi output devices.
|
||||
|
||||
This will return a MidiOutput object if it manages to open it, you can then
|
||||
send messages to this device.
|
||||
|
||||
If the device can't be opened, this will return an empty object.
|
||||
|
||||
@param deviceIdentifier the ID of the device to open - use the getAvailableDevices() method to
|
||||
find the available devices that can be opened
|
||||
@see getDevices
|
||||
*/
|
||||
static std::unique_ptr<MidiOutput> openDevice (const String& deviceIdentifier);
|
||||
|
||||
#if JUCE_LINUX || JUCE_BSD || JUCE_MAC || JUCE_IOS || DOXYGEN
|
||||
/** This will try to create a new midi output device (only available on Linux, macOS and iOS).
|
||||
|
||||
This will attempt to create a new midi output device with the specified name that other
|
||||
apps can connect to and use as their midi input.
|
||||
|
||||
NB - if you are calling this method on iOS you must have enabled the "Audio Background Capability"
|
||||
setting in the iOS exporter otherwise this method will fail.
|
||||
|
||||
Returns an empty object if a device can't be created.
|
||||
|
||||
@param deviceName the name of the device to create
|
||||
*/
|
||||
static std::unique_ptr<MidiOutput> createNewDevice (const String& deviceName);
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/** Destructor. */
|
||||
~MidiOutput() override;
|
||||
|
||||
/** Returns the MidiDeviceInfo struct containing some information about this device. */
|
||||
MidiDeviceInfo getDeviceInfo() const noexcept { return deviceInfo; }
|
||||
|
||||
/** Returns the identifier of this device. */
|
||||
String getIdentifier() const noexcept { return deviceInfo.identifier; }
|
||||
|
||||
/** Returns the name of this device. */
|
||||
String getName() const noexcept { return deviceInfo.name; }
|
||||
|
||||
/** Sets a custom name for the device. */
|
||||
void setName (const String& newName) noexcept { deviceInfo.name = newName; }
|
||||
|
||||
//==============================================================================
|
||||
/** Sends out a MIDI message immediately. */
|
||||
void sendMessageNow (const MidiMessage& message);
|
||||
|
||||
/** Sends out a sequence of MIDI messages immediately. */
|
||||
void sendBlockOfMessagesNow (const MidiBuffer& buffer);
|
||||
|
||||
/** This lets you supply a block of messages that will be sent out at some point
|
||||
in the future.
|
||||
|
||||
The MidiOutput class has an internal thread that can send out timestamped
|
||||
messages - this appends a set of messages to its internal buffer, ready for
|
||||
sending.
|
||||
|
||||
This will only work if you've already started the thread with startBackgroundThread().
|
||||
|
||||
A time is specified, at which the block of messages should be sent. This time uses
|
||||
the same time base as Time::getMillisecondCounter(), and must be in the future.
|
||||
|
||||
The samplesPerSecondForBuffer parameter indicates the number of samples per second
|
||||
used by the MidiBuffer. Each event in a MidiBuffer has a sample position, and the
|
||||
samplesPerSecondForBuffer value is needed to convert this sample position to a
|
||||
real time.
|
||||
*/
|
||||
void sendBlockOfMessages (const MidiBuffer& buffer,
|
||||
double millisecondCounterToStartAt,
|
||||
double samplesPerSecondForBuffer);
|
||||
|
||||
/** Gets rid of any midi messages that had been added by sendBlockOfMessages(). */
|
||||
void clearAllPendingMessages();
|
||||
|
||||
/** Starts up a background thread so that the device can send blocks of data.
|
||||
Call this to get the device ready, before using sendBlockOfMessages().
|
||||
*/
|
||||
void startBackgroundThread();
|
||||
|
||||
/** Stops the background thread, and clears any pending midi events.
|
||||
@see startBackgroundThread
|
||||
*/
|
||||
void stopBackgroundThread();
|
||||
|
||||
/** Returns true if the background thread used to send blocks of data is running.
|
||||
@see startBackgroundThread, stopBackgroundThread
|
||||
*/
|
||||
bool isBackgroundThreadRunning() const noexcept { return isThreadRunning(); }
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
[[deprecated ("Use getAvailableDevices instead.")]]
|
||||
static StringArray getDevices();
|
||||
[[deprecated ("Use getDefaultDevice instead.")]]
|
||||
static int getDefaultDeviceIndex();
|
||||
[[deprecated ("Use openDevice that takes a device identifier instead.")]]
|
||||
static std::unique_ptr<MidiOutput> openDevice (int);
|
||||
#endif
|
||||
|
||||
/** @internal */
|
||||
class Pimpl;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct PendingMessage
|
||||
{
|
||||
PendingMessage (const void* data, int len, double timeStamp)
|
||||
: message (data, len, timeStamp)
|
||||
{
|
||||
}
|
||||
|
||||
MidiMessage message;
|
||||
PendingMessage* next;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
explicit MidiOutput (const String&, const String&);
|
||||
void run() override;
|
||||
|
||||
MidiDeviceInfo deviceInfo;
|
||||
|
||||
std::unique_ptr<Pimpl> internal;
|
||||
|
||||
CriticalSection lock;
|
||||
PendingMessage* firstMessage = nullptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutput)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
//==============================================================================
|
||||
/**
|
||||
This struct contains information about a MIDI input or output device.
|
||||
|
||||
You can get one of these structs by calling the static getAvailableDevices() or
|
||||
getDefaultDevice() methods of MidiInput and MidiOutput or by calling getDeviceInfo()
|
||||
on an instance of these classes. Devices can be opened by passing the identifier to
|
||||
the openDevice() method.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct MidiDeviceInfo
|
||||
{
|
||||
MidiDeviceInfo() = default;
|
||||
|
||||
MidiDeviceInfo (const String& deviceName, const String& deviceIdentifier)
|
||||
: name (deviceName), identifier (deviceIdentifier)
|
||||
{
|
||||
}
|
||||
|
||||
/** The name of this device.
|
||||
|
||||
This will be provided by the OS unless the device has been created with the
|
||||
createNewDevice() method.
|
||||
|
||||
Note that the name is not guaranteed to be unique and two devices with the
|
||||
same name will be indistinguishable. If you want to address a specific device
|
||||
it is better to use the identifier.
|
||||
*/
|
||||
String name;
|
||||
|
||||
/** The identifier for this device.
|
||||
|
||||
This will be provided by the OS and it's format will differ on different systems
|
||||
e.g. on macOS it will be a number whereas on Windows it will be a long alphanumeric string.
|
||||
*/
|
||||
String identifier;
|
||||
|
||||
//==============================================================================
|
||||
bool operator== (const MidiDeviceInfo& other) const noexcept { return name == other.name && identifier == other.identifier; }
|
||||
bool operator!= (const MidiDeviceInfo& other) const noexcept { return ! operator== (other); }
|
||||
};
|
||||
|
||||
class MidiInputCallback;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a midi input device.
|
||||
|
||||
To create one of these, use the static getAvailableDevices() method to find out what
|
||||
inputs are available, and then use the openDevice() method to try to open one.
|
||||
|
||||
@see MidiOutput
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API MidiInput final
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Returns a list of the available midi input devices.
|
||||
|
||||
You can open one of the devices by passing its identifier into the openDevice() method.
|
||||
|
||||
@see MidiDeviceInfo, getDevices, getDefaultDeviceIndex, openDevice
|
||||
*/
|
||||
static Array<MidiDeviceInfo> getAvailableDevices();
|
||||
|
||||
/** Returns the MidiDeviceInfo of the default midi input device to use. */
|
||||
static MidiDeviceInfo getDefaultDevice();
|
||||
|
||||
/** Tries to open one of the midi input devices.
|
||||
|
||||
This will return a MidiInput object if it manages to open it, you can then
|
||||
call start() and stop() on this device.
|
||||
|
||||
If the device can't be opened, this will return an empty object.
|
||||
|
||||
@param deviceIdentifier the ID of the device to open - use the getAvailableDevices() method to
|
||||
find the available devices that can be opened
|
||||
@param callback the object that will receive the midi messages from this device
|
||||
|
||||
@see MidiInputCallback, getDevices
|
||||
*/
|
||||
static std::unique_ptr<MidiInput> openDevice (const String& deviceIdentifier, MidiInputCallback* callback);
|
||||
|
||||
#if JUCE_LINUX || JUCE_BSD || JUCE_MAC || JUCE_IOS || DOXYGEN
|
||||
/** This will try to create a new midi input device (only available on Linux, macOS and iOS).
|
||||
|
||||
This will attempt to create a new midi input device with the specified name for other
|
||||
apps to connect to.
|
||||
|
||||
NB - if you are calling this method on iOS you must have enabled the "Audio Background Capability"
|
||||
setting in the iOS exporter otherwise this method will fail.
|
||||
|
||||
Returns an empty object if a device can't be created.
|
||||
|
||||
@param deviceName the name of the device to create
|
||||
@param callback the object that will receive the midi messages from this device
|
||||
*/
|
||||
static std::unique_ptr<MidiInput> createNewDevice (const String& deviceName, MidiInputCallback* callback);
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/** Destructor. */
|
||||
~MidiInput();
|
||||
|
||||
/** Starts the device running.
|
||||
|
||||
After calling this, the device will start sending midi messages to the MidiInputCallback
|
||||
object that was specified when the openDevice() method was called.
|
||||
|
||||
@see stop
|
||||
*/
|
||||
void start();
|
||||
|
||||
/** Stops the device running.
|
||||
|
||||
@see start
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/** Returns the MidiDeviceInfo struct containing some information about this device. */
|
||||
MidiDeviceInfo getDeviceInfo() const noexcept { return deviceInfo; }
|
||||
|
||||
/** Returns the identifier of this device. */
|
||||
String getIdentifier() const noexcept { return deviceInfo.identifier; }
|
||||
|
||||
/** Returns the name of this device. */
|
||||
String getName() const noexcept { return deviceInfo.name; }
|
||||
|
||||
/** Sets a custom name for the device. */
|
||||
void setName (const String& newName) noexcept { deviceInfo.name = newName; }
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
[[deprecated ("Use getAvailableDevices instead.")]]
|
||||
static StringArray getDevices();
|
||||
[[deprecated ("Use getDefaultDevice instead.")]]
|
||||
static int getDefaultDeviceIndex();
|
||||
[[deprecated ("Use openDevice that takes a device identifier instead.")]]
|
||||
static std::unique_ptr<MidiInput> openDevice (int, MidiInputCallback*);
|
||||
#endif
|
||||
|
||||
/** @internal */
|
||||
class Pimpl;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
explicit MidiInput (const String&, const String&);
|
||||
|
||||
MidiDeviceInfo deviceInfo;
|
||||
|
||||
std::unique_ptr<Pimpl> internal;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInput)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Receives incoming messages from a physical MIDI input device.
|
||||
|
||||
This class is overridden to handle incoming midi messages. See the MidiInput
|
||||
class for more details.
|
||||
|
||||
@see MidiInput
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API MidiInputCallback
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~MidiInputCallback() = default;
|
||||
|
||||
/** Receives an incoming message.
|
||||
|
||||
A MidiInput object will call this method when a midi event arrives. It'll be
|
||||
called on a high-priority system thread, so avoid doing anything time-consuming
|
||||
in here, and avoid making any UI calls. You might find the MidiBuffer class helpful
|
||||
for queueing incoming messages for use later.
|
||||
|
||||
@param source the MidiInput object that generated the message
|
||||
@param message the incoming message. The message's timestamp is set to a value
|
||||
equivalent to (Time::getMillisecondCounter() / 1000.0) to specify the
|
||||
time when the message arrived
|
||||
*/
|
||||
virtual void handleIncomingMidiMessage (MidiInput* source,
|
||||
const MidiMessage& message) = 0;
|
||||
|
||||
/** Notification sent each time a packet of a multi-packet sysex message arrives.
|
||||
|
||||
If a long sysex message is broken up into multiple packets, this callback is made
|
||||
for each packet that arrives until the message is finished, at which point
|
||||
the normal handleIncomingMidiMessage() callback will be made with the entire
|
||||
message.
|
||||
|
||||
The message passed in will contain the start of a sysex, but won't be finished
|
||||
with the terminating 0xf7 byte.
|
||||
*/
|
||||
virtual void handlePartialSysexMessage (MidiInput* source,
|
||||
const uint8* messageData,
|
||||
int numBytesSoFar,
|
||||
double timestamp)
|
||||
{
|
||||
ignoreUnused (source, messageData, numBytesSoFar, timestamp);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a midi output device.
|
||||
|
||||
To create one of these, use the static getAvailableDevices() method to find out what
|
||||
outputs are available, and then use the openDevice() method to try to open one.
|
||||
|
||||
@see MidiInput
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API MidiOutput final : private Thread
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Returns a list of the available midi output devices.
|
||||
|
||||
You can open one of the devices by passing its identifier into the openDevice() method.
|
||||
|
||||
@see MidiDeviceInfo, getDevices, getDefaultDeviceIndex, openDevice
|
||||
*/
|
||||
static Array<MidiDeviceInfo> getAvailableDevices();
|
||||
|
||||
/** Returns the MidiDeviceInfo of the default midi output device to use. */
|
||||
static MidiDeviceInfo getDefaultDevice();
|
||||
|
||||
/** Tries to open one of the midi output devices.
|
||||
|
||||
This will return a MidiOutput object if it manages to open it, you can then
|
||||
send messages to this device.
|
||||
|
||||
If the device can't be opened, this will return an empty object.
|
||||
|
||||
@param deviceIdentifier the ID of the device to open - use the getAvailableDevices() method to
|
||||
find the available devices that can be opened
|
||||
@see getDevices
|
||||
*/
|
||||
static std::unique_ptr<MidiOutput> openDevice (const String& deviceIdentifier);
|
||||
|
||||
#if JUCE_LINUX || JUCE_BSD || JUCE_MAC || JUCE_IOS || DOXYGEN
|
||||
/** This will try to create a new midi output device (only available on Linux, macOS and iOS).
|
||||
|
||||
This will attempt to create a new midi output device with the specified name that other
|
||||
apps can connect to and use as their midi input.
|
||||
|
||||
NB - if you are calling this method on iOS you must have enabled the "Audio Background Capability"
|
||||
setting in the iOS exporter otherwise this method will fail.
|
||||
|
||||
Returns an empty object if a device can't be created.
|
||||
|
||||
@param deviceName the name of the device to create
|
||||
*/
|
||||
static std::unique_ptr<MidiOutput> createNewDevice (const String& deviceName);
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/** Destructor. */
|
||||
~MidiOutput() override;
|
||||
|
||||
/** Returns the MidiDeviceInfo struct containing some information about this device. */
|
||||
MidiDeviceInfo getDeviceInfo() const noexcept { return deviceInfo; }
|
||||
|
||||
/** Returns the identifier of this device. */
|
||||
String getIdentifier() const noexcept { return deviceInfo.identifier; }
|
||||
|
||||
/** Returns the name of this device. */
|
||||
String getName() const noexcept { return deviceInfo.name; }
|
||||
|
||||
/** Sets a custom name for the device. */
|
||||
void setName (const String& newName) noexcept { deviceInfo.name = newName; }
|
||||
|
||||
//==============================================================================
|
||||
/** Sends out a MIDI message immediately. */
|
||||
void sendMessageNow (const MidiMessage& message);
|
||||
|
||||
/** Sends out a sequence of MIDI messages immediately. */
|
||||
void sendBlockOfMessagesNow (const MidiBuffer& buffer);
|
||||
|
||||
/** This lets you supply a block of messages that will be sent out at some point
|
||||
in the future.
|
||||
|
||||
The MidiOutput class has an internal thread that can send out timestamped
|
||||
messages - this appends a set of messages to its internal buffer, ready for
|
||||
sending.
|
||||
|
||||
This will only work if you've already started the thread with startBackgroundThread().
|
||||
|
||||
A time is specified, at which the block of messages should be sent. This time uses
|
||||
the same time base as Time::getMillisecondCounter(), and must be in the future.
|
||||
|
||||
The samplesPerSecondForBuffer parameter indicates the number of samples per second
|
||||
used by the MidiBuffer. Each event in a MidiBuffer has a sample position, and the
|
||||
samplesPerSecondForBuffer value is needed to convert this sample position to a
|
||||
real time.
|
||||
*/
|
||||
void sendBlockOfMessages (const MidiBuffer& buffer,
|
||||
double millisecondCounterToStartAt,
|
||||
double samplesPerSecondForBuffer);
|
||||
|
||||
/** Gets rid of any midi messages that had been added by sendBlockOfMessages(). */
|
||||
void clearAllPendingMessages();
|
||||
|
||||
/** Starts up a background thread so that the device can send blocks of data.
|
||||
Call this to get the device ready, before using sendBlockOfMessages().
|
||||
*/
|
||||
void startBackgroundThread();
|
||||
|
||||
/** Stops the background thread, and clears any pending midi events.
|
||||
@see startBackgroundThread
|
||||
*/
|
||||
void stopBackgroundThread();
|
||||
|
||||
/** Returns true if the background thread used to send blocks of data is running.
|
||||
@see startBackgroundThread, stopBackgroundThread
|
||||
*/
|
||||
bool isBackgroundThreadRunning() const noexcept { return isThreadRunning(); }
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
[[deprecated ("Use getAvailableDevices instead.")]]
|
||||
static StringArray getDevices();
|
||||
[[deprecated ("Use getDefaultDevice instead.")]]
|
||||
static int getDefaultDeviceIndex();
|
||||
[[deprecated ("Use openDevice that takes a device identifier instead.")]]
|
||||
static std::unique_ptr<MidiOutput> openDevice (int);
|
||||
#endif
|
||||
|
||||
/** @internal */
|
||||
class Pimpl;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct PendingMessage
|
||||
{
|
||||
PendingMessage (const void* data, int len, double timeStamp)
|
||||
: message (data, len, timeStamp)
|
||||
{
|
||||
}
|
||||
|
||||
MidiMessage message;
|
||||
PendingMessage* next;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
explicit MidiOutput (const String&, const String&);
|
||||
void run() override;
|
||||
|
||||
MidiDeviceInfo deviceInfo;
|
||||
|
||||
std::unique_ptr<Pimpl> internal;
|
||||
|
||||
CriticalSection lock;
|
||||
PendingMessage* firstMessage = nullptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutput)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,158 +1,158 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
MidiMessageCollector::MidiMessageCollector()
|
||||
{
|
||||
}
|
||||
|
||||
MidiMessageCollector::~MidiMessageCollector()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MidiMessageCollector::reset (const double newSampleRate)
|
||||
{
|
||||
const ScopedLock sl (midiCallbackLock);
|
||||
|
||||
jassert (newSampleRate > 0);
|
||||
|
||||
#if JUCE_DEBUG
|
||||
hasCalledReset = true;
|
||||
#endif
|
||||
sampleRate = newSampleRate;
|
||||
incomingMessages.clear();
|
||||
lastCallbackTime = Time::getMillisecondCounterHiRes();
|
||||
}
|
||||
|
||||
void MidiMessageCollector::addMessageToQueue (const MidiMessage& message)
|
||||
{
|
||||
const ScopedLock sl (midiCallbackLock);
|
||||
|
||||
#if JUCE_DEBUG
|
||||
jassert (hasCalledReset); // you need to call reset() to set the correct sample rate before using this object
|
||||
#endif
|
||||
|
||||
// the messages that come in here need to be time-stamped correctly - see MidiInput
|
||||
// for details of what the number should be.
|
||||
jassert (message.getTimeStamp() != 0);
|
||||
|
||||
auto sampleNumber = (int) ((message.getTimeStamp() - 0.001 * lastCallbackTime) * sampleRate);
|
||||
|
||||
incomingMessages.addEvent (message, sampleNumber);
|
||||
|
||||
// if the messages don't get used for over a second, we'd better
|
||||
// get rid of any old ones to avoid the queue getting too big
|
||||
if (sampleNumber > sampleRate)
|
||||
incomingMessages.clear (0, sampleNumber - (int) sampleRate);
|
||||
}
|
||||
|
||||
void MidiMessageCollector::removeNextBlockOfMessages (MidiBuffer& destBuffer,
|
||||
const int numSamples)
|
||||
{
|
||||
const ScopedLock sl (midiCallbackLock);
|
||||
|
||||
#if JUCE_DEBUG
|
||||
jassert (hasCalledReset); // you need to call reset() to set the correct sample rate before using this object
|
||||
#endif
|
||||
|
||||
jassert (numSamples > 0);
|
||||
|
||||
auto timeNow = Time::getMillisecondCounterHiRes();
|
||||
auto msElapsed = timeNow - lastCallbackTime;
|
||||
|
||||
lastCallbackTime = timeNow;
|
||||
|
||||
if (! incomingMessages.isEmpty())
|
||||
{
|
||||
int numSourceSamples = jmax (1, roundToInt (msElapsed * 0.001 * sampleRate));
|
||||
int startSample = 0;
|
||||
int scale = 1 << 16;
|
||||
|
||||
if (numSourceSamples > numSamples)
|
||||
{
|
||||
// if our list of events is longer than the buffer we're being
|
||||
// asked for, scale them down to squeeze them all in..
|
||||
const int maxBlockLengthToUse = numSamples << 5;
|
||||
|
||||
auto iter = incomingMessages.cbegin();
|
||||
|
||||
if (numSourceSamples > maxBlockLengthToUse)
|
||||
{
|
||||
startSample = numSourceSamples - maxBlockLengthToUse;
|
||||
numSourceSamples = maxBlockLengthToUse;
|
||||
iter = incomingMessages.findNextSamplePosition (startSample);
|
||||
}
|
||||
|
||||
scale = (numSamples << 10) / numSourceSamples;
|
||||
|
||||
std::for_each (iter, incomingMessages.cend(), [&] (const MidiMessageMetadata& meta)
|
||||
{
|
||||
const auto pos = ((meta.samplePosition - startSample) * scale) >> 10;
|
||||
destBuffer.addEvent (meta.data, meta.numBytes, jlimit (0, numSamples - 1, pos));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// if our event list is shorter than the number we need, put them
|
||||
// towards the end of the buffer
|
||||
startSample = numSamples - numSourceSamples;
|
||||
|
||||
for (const auto metadata : incomingMessages)
|
||||
destBuffer.addEvent (metadata.data, metadata.numBytes,
|
||||
jlimit (0, numSamples - 1, metadata.samplePosition + startSample));
|
||||
}
|
||||
|
||||
incomingMessages.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void MidiMessageCollector::ensureStorageAllocated (size_t bytes)
|
||||
{
|
||||
incomingMessages.ensureSize (bytes);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MidiMessageCollector::handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity)
|
||||
{
|
||||
MidiMessage m (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity));
|
||||
m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
|
||||
|
||||
addMessageToQueue (m);
|
||||
}
|
||||
|
||||
void MidiMessageCollector::handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity)
|
||||
{
|
||||
MidiMessage m (MidiMessage::noteOff (midiChannel, midiNoteNumber, velocity));
|
||||
m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
|
||||
|
||||
addMessageToQueue (m);
|
||||
}
|
||||
|
||||
void MidiMessageCollector::handleIncomingMidiMessage (MidiInput*, const MidiMessage& message)
|
||||
{
|
||||
addMessageToQueue (message);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
MidiMessageCollector::MidiMessageCollector()
|
||||
{
|
||||
}
|
||||
|
||||
MidiMessageCollector::~MidiMessageCollector()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MidiMessageCollector::reset (const double newSampleRate)
|
||||
{
|
||||
const ScopedLock sl (midiCallbackLock);
|
||||
|
||||
jassert (newSampleRate > 0);
|
||||
|
||||
#if JUCE_DEBUG
|
||||
hasCalledReset = true;
|
||||
#endif
|
||||
sampleRate = newSampleRate;
|
||||
incomingMessages.clear();
|
||||
lastCallbackTime = Time::getMillisecondCounterHiRes();
|
||||
}
|
||||
|
||||
void MidiMessageCollector::addMessageToQueue (const MidiMessage& message)
|
||||
{
|
||||
const ScopedLock sl (midiCallbackLock);
|
||||
|
||||
#if JUCE_DEBUG
|
||||
jassert (hasCalledReset); // you need to call reset() to set the correct sample rate before using this object
|
||||
#endif
|
||||
|
||||
// the messages that come in here need to be time-stamped correctly - see MidiInput
|
||||
// for details of what the number should be.
|
||||
jassert (message.getTimeStamp() != 0);
|
||||
|
||||
auto sampleNumber = (int) ((message.getTimeStamp() - 0.001 * lastCallbackTime) * sampleRate);
|
||||
|
||||
incomingMessages.addEvent (message, sampleNumber);
|
||||
|
||||
// if the messages don't get used for over a second, we'd better
|
||||
// get rid of any old ones to avoid the queue getting too big
|
||||
if (sampleNumber > sampleRate)
|
||||
incomingMessages.clear (0, sampleNumber - (int) sampleRate);
|
||||
}
|
||||
|
||||
void MidiMessageCollector::removeNextBlockOfMessages (MidiBuffer& destBuffer,
|
||||
const int numSamples)
|
||||
{
|
||||
const ScopedLock sl (midiCallbackLock);
|
||||
|
||||
#if JUCE_DEBUG
|
||||
jassert (hasCalledReset); // you need to call reset() to set the correct sample rate before using this object
|
||||
#endif
|
||||
|
||||
jassert (numSamples > 0);
|
||||
|
||||
auto timeNow = Time::getMillisecondCounterHiRes();
|
||||
auto msElapsed = timeNow - lastCallbackTime;
|
||||
|
||||
lastCallbackTime = timeNow;
|
||||
|
||||
if (! incomingMessages.isEmpty())
|
||||
{
|
||||
int numSourceSamples = jmax (1, roundToInt (msElapsed * 0.001 * sampleRate));
|
||||
int startSample = 0;
|
||||
int scale = 1 << 16;
|
||||
|
||||
if (numSourceSamples > numSamples)
|
||||
{
|
||||
// if our list of events is longer than the buffer we're being
|
||||
// asked for, scale them down to squeeze them all in..
|
||||
const int maxBlockLengthToUse = numSamples << 5;
|
||||
|
||||
auto iter = incomingMessages.cbegin();
|
||||
|
||||
if (numSourceSamples > maxBlockLengthToUse)
|
||||
{
|
||||
startSample = numSourceSamples - maxBlockLengthToUse;
|
||||
numSourceSamples = maxBlockLengthToUse;
|
||||
iter = incomingMessages.findNextSamplePosition (startSample);
|
||||
}
|
||||
|
||||
scale = (numSamples << 10) / numSourceSamples;
|
||||
|
||||
std::for_each (iter, incomingMessages.cend(), [&] (const MidiMessageMetadata& meta)
|
||||
{
|
||||
const auto pos = ((meta.samplePosition - startSample) * scale) >> 10;
|
||||
destBuffer.addEvent (meta.data, meta.numBytes, jlimit (0, numSamples - 1, pos));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// if our event list is shorter than the number we need, put them
|
||||
// towards the end of the buffer
|
||||
startSample = numSamples - numSourceSamples;
|
||||
|
||||
for (const auto metadata : incomingMessages)
|
||||
destBuffer.addEvent (metadata.data, metadata.numBytes,
|
||||
jlimit (0, numSamples - 1, metadata.samplePosition + startSample));
|
||||
}
|
||||
|
||||
incomingMessages.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void MidiMessageCollector::ensureStorageAllocated (size_t bytes)
|
||||
{
|
||||
incomingMessages.ensureSize (bytes);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MidiMessageCollector::handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity)
|
||||
{
|
||||
MidiMessage m (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity));
|
||||
m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
|
||||
|
||||
addMessageToQueue (m);
|
||||
}
|
||||
|
||||
void MidiMessageCollector::handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity)
|
||||
{
|
||||
MidiMessage m (MidiMessage::noteOff (midiChannel, midiNoteNumber, velocity));
|
||||
m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
|
||||
|
||||
addMessageToQueue (m);
|
||||
}
|
||||
|
||||
void MidiMessageCollector::handleIncomingMidiMessage (MidiInput*, const MidiMessage& message)
|
||||
{
|
||||
addMessageToQueue (message);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,113 +1,113 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Collects incoming realtime MIDI messages and turns them into blocks suitable for
|
||||
processing by a block-based audio callback.
|
||||
|
||||
The class can also be used as either a MidiKeyboardState::Listener or a MidiInputCallback
|
||||
so it can easily use a midi input or keyboard component as its source.
|
||||
|
||||
@see MidiMessage, MidiInput
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API MidiMessageCollector : public MidiKeyboardState::Listener,
|
||||
public MidiInputCallback
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a MidiMessageCollector. */
|
||||
MidiMessageCollector();
|
||||
|
||||
/** Destructor. */
|
||||
~MidiMessageCollector() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Clears any messages from the queue.
|
||||
|
||||
You need to call this method before starting to use the collector, so that
|
||||
it knows the correct sample rate to use.
|
||||
*/
|
||||
void reset (double sampleRate);
|
||||
|
||||
/** Takes an incoming real-time message and adds it to the queue.
|
||||
|
||||
The message's timestamp is taken, and it will be ready for retrieval as part
|
||||
of the block returned by the next call to removeNextBlockOfMessages().
|
||||
|
||||
This method is fully thread-safe when overlapping calls are made with
|
||||
removeNextBlockOfMessages().
|
||||
*/
|
||||
void addMessageToQueue (const MidiMessage& message);
|
||||
|
||||
/** Removes all the pending messages from the queue as a buffer.
|
||||
|
||||
This will also correct the messages' timestamps to make sure they're in
|
||||
the range 0 to numSamples - 1.
|
||||
|
||||
This call should be made regularly by something like an audio processing
|
||||
callback, because the time that it happens is used in calculating the
|
||||
midi event positions.
|
||||
|
||||
This method is fully thread-safe when overlapping calls are made with
|
||||
addMessageToQueue().
|
||||
|
||||
Precondition: numSamples must be greater than 0.
|
||||
*/
|
||||
void removeNextBlockOfMessages (MidiBuffer& destBuffer, int numSamples);
|
||||
|
||||
/** Preallocates storage for collected messages.
|
||||
|
||||
This can be called before audio processing begins to ensure that there
|
||||
is sufficient space for the expected MIDI messages, in order to avoid
|
||||
allocations within the audio callback.
|
||||
*/
|
||||
void ensureStorageAllocated (size_t bytes);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override;
|
||||
/** @internal */
|
||||
void handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override;
|
||||
/** @internal */
|
||||
void handleIncomingMidiMessage (MidiInput*, const MidiMessage&) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
double lastCallbackTime = 0;
|
||||
CriticalSection midiCallbackLock;
|
||||
MidiBuffer incomingMessages;
|
||||
double sampleRate = 44100.0;
|
||||
#if JUCE_DEBUG
|
||||
bool hasCalledReset = false;
|
||||
#endif
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiMessageCollector)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Collects incoming realtime MIDI messages and turns them into blocks suitable for
|
||||
processing by a block-based audio callback.
|
||||
|
||||
The class can also be used as either a MidiKeyboardState::Listener or a MidiInputCallback
|
||||
so it can easily use a midi input or keyboard component as its source.
|
||||
|
||||
@see MidiMessage, MidiInput
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API MidiMessageCollector : public MidiKeyboardState::Listener,
|
||||
public MidiInputCallback
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a MidiMessageCollector. */
|
||||
MidiMessageCollector();
|
||||
|
||||
/** Destructor. */
|
||||
~MidiMessageCollector() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Clears any messages from the queue.
|
||||
|
||||
You need to call this method before starting to use the collector, so that
|
||||
it knows the correct sample rate to use.
|
||||
*/
|
||||
void reset (double sampleRate);
|
||||
|
||||
/** Takes an incoming real-time message and adds it to the queue.
|
||||
|
||||
The message's timestamp is taken, and it will be ready for retrieval as part
|
||||
of the block returned by the next call to removeNextBlockOfMessages().
|
||||
|
||||
This method is fully thread-safe when overlapping calls are made with
|
||||
removeNextBlockOfMessages().
|
||||
*/
|
||||
void addMessageToQueue (const MidiMessage& message);
|
||||
|
||||
/** Removes all the pending messages from the queue as a buffer.
|
||||
|
||||
This will also correct the messages' timestamps to make sure they're in
|
||||
the range 0 to numSamples - 1.
|
||||
|
||||
This call should be made regularly by something like an audio processing
|
||||
callback, because the time that it happens is used in calculating the
|
||||
midi event positions.
|
||||
|
||||
This method is fully thread-safe when overlapping calls are made with
|
||||
addMessageToQueue().
|
||||
|
||||
Precondition: numSamples must be greater than 0.
|
||||
*/
|
||||
void removeNextBlockOfMessages (MidiBuffer& destBuffer, int numSamples);
|
||||
|
||||
/** Preallocates storage for collected messages.
|
||||
|
||||
This can be called before audio processing begins to ensure that there
|
||||
is sufficient space for the expected MIDI messages, in order to avoid
|
||||
allocations within the audio callback.
|
||||
*/
|
||||
void ensureStorageAllocated (size_t bytes);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override;
|
||||
/** @internal */
|
||||
void handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override;
|
||||
/** @internal */
|
||||
void handleIncomingMidiMessage (MidiInput*, const MidiMessage&) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
double lastCallbackTime = 0;
|
||||
CriticalSection midiCallbackLock;
|
||||
MidiBuffer incomingMessages;
|
||||
double sampleRate = 44100.0;
|
||||
#if JUCE_DEBUG
|
||||
bool hasCalledReset = false;
|
||||
#endif
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiMessageCollector)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,140 +1,144 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
namespace universal_midi_packets
|
||||
{
|
||||
|
||||
/**
|
||||
A base class for classes which convert bytestream midi to other formats.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct BytestreamInputHandler
|
||||
{
|
||||
virtual ~BytestreamInputHandler() noexcept = default;
|
||||
|
||||
virtual void reset() = 0;
|
||||
virtual void pushMidiData (const void* data, int bytes, double time) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
Parses a continuous bytestream and emits complete MidiMessages whenever a full
|
||||
message is received.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct BytestreamToBytestreamHandler : public BytestreamInputHandler
|
||||
{
|
||||
BytestreamToBytestreamHandler (MidiInput& i, MidiInputCallback& c)
|
||||
: input (i), callback (c), concatenator (2048) {}
|
||||
|
||||
/**
|
||||
Provides an `operator()` which can create an input handler for a given
|
||||
MidiInput.
|
||||
|
||||
All handler classes should have a similar Factory to facilitate
|
||||
creation of handlers in generic contexts.
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
public:
|
||||
explicit Factory (MidiInputCallback* c)
|
||||
: callback (c) {}
|
||||
|
||||
std::unique_ptr<BytestreamToBytestreamHandler> operator() (MidiInput& i) const
|
||||
{
|
||||
if (callback != nullptr)
|
||||
return std::make_unique<BytestreamToBytestreamHandler> (i, *callback);
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
MidiInputCallback* callback = nullptr;
|
||||
};
|
||||
|
||||
void reset() override { concatenator.reset(); }
|
||||
|
||||
void pushMidiData (const void* data, int bytes, double time) override
|
||||
{
|
||||
concatenator.pushMidiData (data, bytes, time, &input, callback);
|
||||
}
|
||||
|
||||
MidiInput& input;
|
||||
MidiInputCallback& callback;
|
||||
MidiDataConcatenator concatenator;
|
||||
};
|
||||
|
||||
/**
|
||||
Parses a continuous MIDI 1.0 bytestream, and emits full messages in the requested
|
||||
UMP format.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct BytestreamToUMPHandler : public BytestreamInputHandler
|
||||
{
|
||||
BytestreamToUMPHandler (PacketProtocol protocol, Receiver& c)
|
||||
: recipient (c), dispatcher (protocol, 2048) {}
|
||||
|
||||
/**
|
||||
Provides an `operator()` which can create an input handler for a given
|
||||
MidiInput.
|
||||
|
||||
All handler classes should have a similar Factory to facilitate
|
||||
creation of handlers in generic contexts.
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
public:
|
||||
Factory (PacketProtocol p, Receiver& c)
|
||||
: protocol (p), callback (c) {}
|
||||
|
||||
std::unique_ptr<BytestreamToUMPHandler> operator() (MidiInput&) const
|
||||
{
|
||||
return std::make_unique<BytestreamToUMPHandler> (protocol, callback);
|
||||
}
|
||||
|
||||
private:
|
||||
PacketProtocol protocol;
|
||||
Receiver& callback;
|
||||
};
|
||||
|
||||
void reset() override { dispatcher.reset(); }
|
||||
|
||||
void pushMidiData (const void* data, int bytes, double time) override
|
||||
{
|
||||
const auto* ptr = static_cast<const uint8_t*> (data);
|
||||
dispatcher.dispatch (ptr, ptr + bytes, time, [&] (const View& v)
|
||||
{
|
||||
recipient.packetReceived (v, time);
|
||||
});
|
||||
}
|
||||
|
||||
Receiver& recipient;
|
||||
BytestreamToUMPDispatcher dispatcher;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef DOXYGEN
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace universal_midi_packets
|
||||
{
|
||||
|
||||
/**
|
||||
A base class for classes which convert bytestream midi to other formats.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct BytestreamInputHandler
|
||||
{
|
||||
virtual ~BytestreamInputHandler() noexcept = default;
|
||||
|
||||
virtual void reset() = 0;
|
||||
virtual void pushMidiData (const void* data, int bytes, double time) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
Parses a continuous bytestream and emits complete MidiMessages whenever a full
|
||||
message is received.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct BytestreamToBytestreamHandler : public BytestreamInputHandler
|
||||
{
|
||||
BytestreamToBytestreamHandler (MidiInput& i, MidiInputCallback& c)
|
||||
: input (i), callback (c), concatenator (2048) {}
|
||||
|
||||
/**
|
||||
Provides an `operator()` which can create an input handler for a given
|
||||
MidiInput.
|
||||
|
||||
All handler classes should have a similar Factory to facilitate
|
||||
creation of handlers in generic contexts.
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
public:
|
||||
explicit Factory (MidiInputCallback* c)
|
||||
: callback (c) {}
|
||||
|
||||
std::unique_ptr<BytestreamToBytestreamHandler> operator() (MidiInput& i) const
|
||||
{
|
||||
if (callback != nullptr)
|
||||
return std::make_unique<BytestreamToBytestreamHandler> (i, *callback);
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
MidiInputCallback* callback = nullptr;
|
||||
};
|
||||
|
||||
void reset() override { concatenator.reset(); }
|
||||
|
||||
void pushMidiData (const void* data, int bytes, double time) override
|
||||
{
|
||||
concatenator.pushMidiData (data, bytes, time, &input, callback);
|
||||
}
|
||||
|
||||
MidiInput& input;
|
||||
MidiInputCallback& callback;
|
||||
MidiDataConcatenator concatenator;
|
||||
};
|
||||
|
||||
/**
|
||||
Parses a continuous MIDI 1.0 bytestream, and emits full messages in the requested
|
||||
UMP format.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct BytestreamToUMPHandler : public BytestreamInputHandler
|
||||
{
|
||||
BytestreamToUMPHandler (PacketProtocol protocol, Receiver& c)
|
||||
: recipient (c), dispatcher (protocol, 2048) {}
|
||||
|
||||
/**
|
||||
Provides an `operator()` which can create an input handler for a given
|
||||
MidiInput.
|
||||
|
||||
All handler classes should have a similar Factory to facilitate
|
||||
creation of handlers in generic contexts.
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
public:
|
||||
Factory (PacketProtocol p, Receiver& c)
|
||||
: protocol (p), callback (c) {}
|
||||
|
||||
std::unique_ptr<BytestreamToUMPHandler> operator() (MidiInput&) const
|
||||
{
|
||||
return std::make_unique<BytestreamToUMPHandler> (protocol, callback);
|
||||
}
|
||||
|
||||
private:
|
||||
PacketProtocol protocol;
|
||||
Receiver& callback;
|
||||
};
|
||||
|
||||
void reset() override { dispatcher.reset(); }
|
||||
|
||||
void pushMidiData (const void* data, int bytes, double time) override
|
||||
{
|
||||
const auto* ptr = static_cast<const uint8_t*> (data);
|
||||
dispatcher.dispatch (ptr, ptr + bytes, time, [&] (const View& v)
|
||||
{
|
||||
recipient.packetReceived (v, time);
|
||||
});
|
||||
}
|
||||
|
||||
Receiver& recipient;
|
||||
BytestreamToUMPDispatcher dispatcher;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,151 +1,155 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
namespace universal_midi_packets
|
||||
{
|
||||
|
||||
/**
|
||||
A base class for classes which convert Universal MIDI Packets to other
|
||||
formats.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct U32InputHandler
|
||||
{
|
||||
virtual ~U32InputHandler() noexcept = default;
|
||||
|
||||
virtual void reset() = 0;
|
||||
virtual void pushMidiData (const uint32_t* begin, const uint32_t* end, double time) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
Parses a continuous stream of U32 words and emits complete MidiMessages whenever a full
|
||||
message is received.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct U32ToBytestreamHandler : public U32InputHandler
|
||||
{
|
||||
U32ToBytestreamHandler (MidiInput& i, MidiInputCallback& c)
|
||||
: input (i), callback (c), dispatcher (2048) {}
|
||||
|
||||
/**
|
||||
Provides an `operator()` which can create an input handler for a given
|
||||
MidiInput.
|
||||
|
||||
All handler classes should have a similar Factory to facilitate
|
||||
creation of handlers in generic contexts.
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
public:
|
||||
explicit Factory (MidiInputCallback* c)
|
||||
: callback (c) {}
|
||||
|
||||
std::unique_ptr<U32ToBytestreamHandler> operator() (MidiInput& i) const
|
||||
{
|
||||
if (callback != nullptr)
|
||||
return std::make_unique<U32ToBytestreamHandler> (i, *callback);
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
MidiInputCallback* callback = nullptr;
|
||||
};
|
||||
|
||||
void reset() override { dispatcher.reset(); }
|
||||
|
||||
void pushMidiData (const uint32_t* begin, const uint32_t* end, double time) override
|
||||
{
|
||||
dispatcher.dispatch (begin, end, time, [this] (const MidiMessage& m)
|
||||
{
|
||||
callback.handleIncomingMidiMessage (&input, m);
|
||||
});
|
||||
}
|
||||
|
||||
MidiInput& input;
|
||||
MidiInputCallback& callback;
|
||||
ToBytestreamDispatcher dispatcher;
|
||||
};
|
||||
|
||||
/**
|
||||
Parses a continuous stream of U32 words and emits full messages in the requested
|
||||
UMP format.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct U32ToUMPHandler : public U32InputHandler
|
||||
{
|
||||
U32ToUMPHandler (PacketProtocol protocol, Receiver& c)
|
||||
: recipient (c), converter (protocol) {}
|
||||
|
||||
/**
|
||||
Provides an `operator()` which can create an input handler for a given
|
||||
MidiInput.
|
||||
|
||||
All handler classes should have a similar Factory to facilitate
|
||||
creation of handlers in generic contexts.
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
public:
|
||||
Factory (PacketProtocol p, Receiver& c)
|
||||
: protocol (p), callback (c) {}
|
||||
|
||||
std::unique_ptr<U32ToUMPHandler> operator() (MidiInput&) const
|
||||
{
|
||||
return std::make_unique<U32ToUMPHandler> (protocol, callback);
|
||||
}
|
||||
|
||||
private:
|
||||
PacketProtocol protocol;
|
||||
Receiver& callback;
|
||||
};
|
||||
|
||||
void reset() override
|
||||
{
|
||||
dispatcher.reset();
|
||||
converter.reset();
|
||||
}
|
||||
|
||||
void pushMidiData (const uint32_t* begin, const uint32_t* end, double time) override
|
||||
{
|
||||
dispatcher.dispatch (begin, end, time, [this] (const View& view, double thisTime)
|
||||
{
|
||||
converter.convert (view, [&] (const View& converted)
|
||||
{
|
||||
recipient.packetReceived (converted, thisTime);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Receiver& recipient;
|
||||
Dispatcher dispatcher;
|
||||
GenericUMPConverter converter;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef DOXYGEN
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace universal_midi_packets
|
||||
{
|
||||
|
||||
/**
|
||||
A base class for classes which convert Universal MIDI Packets to other
|
||||
formats.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct U32InputHandler
|
||||
{
|
||||
virtual ~U32InputHandler() noexcept = default;
|
||||
|
||||
virtual void reset() = 0;
|
||||
virtual void pushMidiData (const uint32_t* begin, const uint32_t* end, double time) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
Parses a continuous stream of U32 words and emits complete MidiMessages whenever a full
|
||||
message is received.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct U32ToBytestreamHandler : public U32InputHandler
|
||||
{
|
||||
U32ToBytestreamHandler (MidiInput& i, MidiInputCallback& c)
|
||||
: input (i), callback (c), dispatcher (2048) {}
|
||||
|
||||
/**
|
||||
Provides an `operator()` which can create an input handler for a given
|
||||
MidiInput.
|
||||
|
||||
All handler classes should have a similar Factory to facilitate
|
||||
creation of handlers in generic contexts.
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
public:
|
||||
explicit Factory (MidiInputCallback* c)
|
||||
: callback (c) {}
|
||||
|
||||
std::unique_ptr<U32ToBytestreamHandler> operator() (MidiInput& i) const
|
||||
{
|
||||
if (callback != nullptr)
|
||||
return std::make_unique<U32ToBytestreamHandler> (i, *callback);
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
MidiInputCallback* callback = nullptr;
|
||||
};
|
||||
|
||||
void reset() override { dispatcher.reset(); }
|
||||
|
||||
void pushMidiData (const uint32_t* begin, const uint32_t* end, double time) override
|
||||
{
|
||||
dispatcher.dispatch (begin, end, time, [this] (const MidiMessage& m)
|
||||
{
|
||||
callback.handleIncomingMidiMessage (&input, m);
|
||||
});
|
||||
}
|
||||
|
||||
MidiInput& input;
|
||||
MidiInputCallback& callback;
|
||||
ToBytestreamDispatcher dispatcher;
|
||||
};
|
||||
|
||||
/**
|
||||
Parses a continuous stream of U32 words and emits full messages in the requested
|
||||
UMP format.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct U32ToUMPHandler : public U32InputHandler
|
||||
{
|
||||
U32ToUMPHandler (PacketProtocol protocol, Receiver& c)
|
||||
: recipient (c), converter (protocol) {}
|
||||
|
||||
/**
|
||||
Provides an `operator()` which can create an input handler for a given
|
||||
MidiInput.
|
||||
|
||||
All handler classes should have a similar Factory to facilitate
|
||||
creation of handlers in generic contexts.
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
public:
|
||||
Factory (PacketProtocol p, Receiver& c)
|
||||
: protocol (p), callback (c) {}
|
||||
|
||||
std::unique_ptr<U32ToUMPHandler> operator() (MidiInput&) const
|
||||
{
|
||||
return std::make_unique<U32ToUMPHandler> (protocol, callback);
|
||||
}
|
||||
|
||||
private:
|
||||
PacketProtocol protocol;
|
||||
Receiver& callback;
|
||||
};
|
||||
|
||||
void reset() override
|
||||
{
|
||||
dispatcher.reset();
|
||||
converter.reset();
|
||||
}
|
||||
|
||||
void pushMidiData (const uint32_t* begin, const uint32_t* end, double time) override
|
||||
{
|
||||
dispatcher.dispatch (begin, end, time, [this] (const View& view, double thisTime)
|
||||
{
|
||||
converter.convert (view, [&] (const View& converted)
|
||||
{
|
||||
recipient.packetReceived (converted, thisTime);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Receiver& recipient;
|
||||
Dispatcher dispatcher;
|
||||
GenericUMPConverter converter;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -2,7 +2,7 @@
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
@ -1,481 +1,470 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (getMinBufferSize, "getMinBufferSize", "(III)I") \
|
||||
STATICMETHOD (getNativeOutputSampleRate, "getNativeOutputSampleRate", "(I)I") \
|
||||
METHOD (constructor, "<init>", "(IIIIII)V") \
|
||||
METHOD (getState, "getState", "()I") \
|
||||
METHOD (play, "play", "()V") \
|
||||
METHOD (stop, "stop", "()V") \
|
||||
METHOD (release, "release", "()V") \
|
||||
METHOD (flush, "flush", "()V") \
|
||||
METHOD (write, "write", "([SII)I") \
|
||||
|
||||
DECLARE_JNI_CLASS (AudioTrack, "android/media/AudioTrack")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (getMinBufferSize, "getMinBufferSize", "(III)I") \
|
||||
METHOD (constructor, "<init>", "(IIIII)V") \
|
||||
METHOD (getState, "getState", "()I") \
|
||||
METHOD (startRecording, "startRecording", "()V") \
|
||||
METHOD (stop, "stop", "()V") \
|
||||
METHOD (read, "read", "([SII)I") \
|
||||
METHOD (release, "release", "()V") \
|
||||
|
||||
DECLARE_JNI_CLASS (AudioRecord, "android/media/AudioRecord")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
enum
|
||||
{
|
||||
CHANNEL_OUT_STEREO = 12,
|
||||
CHANNEL_IN_STEREO = 12,
|
||||
CHANNEL_IN_MONO = 16,
|
||||
ENCODING_PCM_16BIT = 2,
|
||||
STREAM_MUSIC = 3,
|
||||
MODE_STREAM = 1,
|
||||
STATE_UNINITIALIZED = 0
|
||||
};
|
||||
|
||||
const char* const javaAudioTypeName = "Android Audio";
|
||||
|
||||
//==============================================================================
|
||||
class AndroidAudioIODevice : public AudioIODevice,
|
||||
public Thread
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
AndroidAudioIODevice (const String& deviceName)
|
||||
: AudioIODevice (deviceName, javaAudioTypeName),
|
||||
Thread ("audio"),
|
||||
minBufferSizeOut (0), minBufferSizeIn (0), callback (nullptr), sampleRate (0),
|
||||
numClientInputChannels (0), numDeviceInputChannels (0), numDeviceInputChannelsAvailable (2),
|
||||
numClientOutputChannels (0), numDeviceOutputChannels (0),
|
||||
actualBufferSize (0), isRunning (false),
|
||||
inputChannelBuffer (1, 1),
|
||||
outputChannelBuffer (1, 1)
|
||||
{
|
||||
JNIEnv* env = getEnv();
|
||||
sampleRate = env->CallStaticIntMethod (AudioTrack, AudioTrack.getNativeOutputSampleRate, MODE_STREAM);
|
||||
|
||||
minBufferSizeOut = (int) env->CallStaticIntMethod (AudioTrack, AudioTrack.getMinBufferSize, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT);
|
||||
minBufferSizeIn = (int) env->CallStaticIntMethod (AudioRecord, AudioRecord.getMinBufferSize, sampleRate, CHANNEL_IN_STEREO, ENCODING_PCM_16BIT);
|
||||
|
||||
if (minBufferSizeIn <= 0)
|
||||
{
|
||||
minBufferSizeIn = env->CallStaticIntMethod (AudioRecord, AudioRecord.getMinBufferSize, sampleRate, CHANNEL_IN_MONO, ENCODING_PCM_16BIT);
|
||||
|
||||
if (minBufferSizeIn > 0)
|
||||
numDeviceInputChannelsAvailable = 1;
|
||||
else
|
||||
numDeviceInputChannelsAvailable = 0;
|
||||
}
|
||||
|
||||
DBG ("Audio device - min buffers: " << minBufferSizeOut << ", " << minBufferSizeIn << "; "
|
||||
<< sampleRate << " Hz; input chans: " << numDeviceInputChannelsAvailable);
|
||||
}
|
||||
|
||||
~AndroidAudioIODevice() override
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
StringArray getOutputChannelNames() override
|
||||
{
|
||||
StringArray s;
|
||||
s.add ("Left");
|
||||
s.add ("Right");
|
||||
return s;
|
||||
}
|
||||
|
||||
StringArray getInputChannelNames() override
|
||||
{
|
||||
StringArray s;
|
||||
|
||||
if (numDeviceInputChannelsAvailable == 2)
|
||||
{
|
||||
s.add ("Left");
|
||||
s.add ("Right");
|
||||
}
|
||||
else if (numDeviceInputChannelsAvailable == 1)
|
||||
{
|
||||
s.add ("Audio Input");
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Array<double> getAvailableSampleRates() override
|
||||
{
|
||||
Array<double> r;
|
||||
r.add ((double) sampleRate);
|
||||
return r;
|
||||
}
|
||||
|
||||
Array<int> getAvailableBufferSizes() override
|
||||
{
|
||||
Array<int> b;
|
||||
int n = 16;
|
||||
|
||||
for (int i = 0; i < 50; ++i)
|
||||
{
|
||||
b.add (n);
|
||||
n += n < 64 ? 16
|
||||
: (n < 512 ? 32
|
||||
: (n < 1024 ? 64
|
||||
: (n < 2048 ? 128 : 256)));
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
int getDefaultBufferSize() override { return 2048; }
|
||||
|
||||
String open (const BigInteger& inputChannels,
|
||||
const BigInteger& outputChannels,
|
||||
double requestedSampleRate,
|
||||
int bufferSize) override
|
||||
{
|
||||
close();
|
||||
|
||||
if (sampleRate != (int) requestedSampleRate)
|
||||
return "Sample rate not allowed";
|
||||
|
||||
lastError.clear();
|
||||
int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize;
|
||||
|
||||
numDeviceInputChannels = 0;
|
||||
numDeviceOutputChannels = 0;
|
||||
|
||||
activeOutputChans = outputChannels;
|
||||
activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false);
|
||||
numClientOutputChannels = activeOutputChans.countNumberOfSetBits();
|
||||
|
||||
activeInputChans = inputChannels;
|
||||
activeInputChans.setRange (2, activeInputChans.getHighestBit(), false);
|
||||
numClientInputChannels = activeInputChans.countNumberOfSetBits();
|
||||
|
||||
actualBufferSize = preferredBufferSize;
|
||||
inputChannelBuffer.setSize (2, actualBufferSize);
|
||||
inputChannelBuffer.clear();
|
||||
outputChannelBuffer.setSize (2, actualBufferSize);
|
||||
outputChannelBuffer.clear();
|
||||
|
||||
JNIEnv* env = getEnv();
|
||||
|
||||
if (numClientOutputChannels > 0)
|
||||
{
|
||||
numDeviceOutputChannels = 2;
|
||||
outputDevice = GlobalRef (LocalRef<jobject>(env->NewObject (AudioTrack, AudioTrack.constructor,
|
||||
STREAM_MUSIC, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT,
|
||||
(jint) (minBufferSizeOut * numDeviceOutputChannels * static_cast<int> (sizeof (int16))), MODE_STREAM)));
|
||||
|
||||
const bool supportsUnderrunCount = (getAndroidSDKVersion() >= 24);
|
||||
getUnderrunCount = supportsUnderrunCount ? env->GetMethodID (AudioTrack, "getUnderrunCount", "()I") : nullptr;
|
||||
|
||||
int outputDeviceState = env->CallIntMethod (outputDevice, AudioTrack.getState);
|
||||
if (outputDeviceState > 0)
|
||||
{
|
||||
isRunning = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// failed to open the device
|
||||
outputDevice.clear();
|
||||
lastError = "Error opening audio output device: android.media.AudioTrack failed with state = " + String (outputDeviceState);
|
||||
}
|
||||
}
|
||||
|
||||
if (numClientInputChannels > 0 && numDeviceInputChannelsAvailable > 0)
|
||||
{
|
||||
if (! RuntimePermissions::isGranted (RuntimePermissions::recordAudio))
|
||||
{
|
||||
// If you hit this assert, you probably forgot to get RuntimePermissions::recordAudio
|
||||
// before trying to open an audio input device. This is not going to work!
|
||||
jassertfalse;
|
||||
|
||||
inputDevice.clear();
|
||||
lastError = "Error opening audio input device: the app was not granted android.permission.RECORD_AUDIO";
|
||||
}
|
||||
else
|
||||
{
|
||||
numDeviceInputChannels = jmin (numClientInputChannels, numDeviceInputChannelsAvailable);
|
||||
inputDevice = GlobalRef (LocalRef<jobject>(env->NewObject (AudioRecord, AudioRecord.constructor,
|
||||
0 /* (default audio source) */, sampleRate,
|
||||
numDeviceInputChannelsAvailable > 1 ? CHANNEL_IN_STEREO : CHANNEL_IN_MONO,
|
||||
ENCODING_PCM_16BIT,
|
||||
(jint) (minBufferSizeIn * numDeviceInputChannels * static_cast<int> (sizeof (int16))))));
|
||||
|
||||
int inputDeviceState = env->CallIntMethod (inputDevice, AudioRecord.getState);
|
||||
if (inputDeviceState > 0)
|
||||
{
|
||||
isRunning = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// failed to open the device
|
||||
inputDevice.clear();
|
||||
lastError = "Error opening audio input device: android.media.AudioRecord failed with state = " + String (inputDeviceState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isRunning)
|
||||
{
|
||||
if (outputDevice != nullptr)
|
||||
env->CallVoidMethod (outputDevice, AudioTrack.play);
|
||||
|
||||
if (inputDevice != nullptr)
|
||||
env->CallVoidMethod (inputDevice, AudioRecord.startRecording);
|
||||
|
||||
startThread (8);
|
||||
}
|
||||
else
|
||||
{
|
||||
closeDevices();
|
||||
}
|
||||
|
||||
return lastError;
|
||||
}
|
||||
|
||||
void close() override
|
||||
{
|
||||
if (isRunning)
|
||||
{
|
||||
stopThread (2000);
|
||||
isRunning = false;
|
||||
closeDevices();
|
||||
}
|
||||
}
|
||||
|
||||
int getOutputLatencyInSamples() override { return (minBufferSizeOut * 3) / 4; }
|
||||
int getInputLatencyInSamples() override { return (minBufferSizeIn * 3) / 4; }
|
||||
bool isOpen() override { return isRunning; }
|
||||
int getCurrentBufferSizeSamples() override { return actualBufferSize; }
|
||||
int getCurrentBitDepth() override { return 16; }
|
||||
double getCurrentSampleRate() override { return sampleRate; }
|
||||
BigInteger getActiveOutputChannels() const override { return activeOutputChans; }
|
||||
BigInteger getActiveInputChannels() const override { return activeInputChans; }
|
||||
String getLastError() override { return lastError; }
|
||||
bool isPlaying() override { return isRunning && callback != nullptr; }
|
||||
|
||||
int getXRunCount() const noexcept override
|
||||
{
|
||||
if (outputDevice != nullptr && getUnderrunCount != nullptr)
|
||||
return getEnv()->CallIntMethod (outputDevice, getUnderrunCount);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void start (AudioIODeviceCallback* newCallback) override
|
||||
{
|
||||
if (isRunning && callback != newCallback)
|
||||
{
|
||||
if (newCallback != nullptr)
|
||||
newCallback->audioDeviceAboutToStart (this);
|
||||
|
||||
const ScopedLock sl (callbackLock);
|
||||
callback = newCallback;
|
||||
}
|
||||
}
|
||||
|
||||
void stop() override
|
||||
{
|
||||
if (isRunning)
|
||||
{
|
||||
AudioIODeviceCallback* lastCallback;
|
||||
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
lastCallback = callback;
|
||||
callback = nullptr;
|
||||
}
|
||||
|
||||
if (lastCallback != nullptr)
|
||||
lastCallback->audioDeviceStopped();
|
||||
}
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
JNIEnv* env = getEnv();
|
||||
jshortArray audioBuffer = env->NewShortArray (actualBufferSize * jmax (numDeviceOutputChannels, numDeviceInputChannels));
|
||||
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
if (inputDevice != nullptr)
|
||||
{
|
||||
jint numRead = env->CallIntMethod (inputDevice, AudioRecord.read, audioBuffer, 0, actualBufferSize * numDeviceInputChannels);
|
||||
|
||||
if (numRead < actualBufferSize * numDeviceInputChannels)
|
||||
{
|
||||
DBG ("Audio read under-run! " << numRead);
|
||||
}
|
||||
|
||||
jshort* const src = env->GetShortArrayElements (audioBuffer, nullptr);
|
||||
|
||||
for (int chan = 0; chan < inputChannelBuffer.getNumChannels(); ++chan)
|
||||
{
|
||||
AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> d (inputChannelBuffer.getWritePointer (chan));
|
||||
|
||||
if (chan < numDeviceInputChannels)
|
||||
{
|
||||
AudioData::Pointer <AudioData::Int16, AudioData::NativeEndian, AudioData::Interleaved, AudioData::Const> s (src + chan, numDeviceInputChannels);
|
||||
d.convertSamples (s, actualBufferSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
d.clearSamples (actualBufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
env->ReleaseShortArrayElements (audioBuffer, src, 0);
|
||||
}
|
||||
|
||||
if (threadShouldExit())
|
||||
break;
|
||||
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
if (callback != nullptr)
|
||||
{
|
||||
callback->audioDeviceIOCallback (inputChannelBuffer.getArrayOfReadPointers(), numClientInputChannels,
|
||||
outputChannelBuffer.getArrayOfWritePointers(), numClientOutputChannels,
|
||||
actualBufferSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
outputChannelBuffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (outputDevice != nullptr)
|
||||
{
|
||||
if (threadShouldExit())
|
||||
break;
|
||||
|
||||
jshort* const dest = env->GetShortArrayElements (audioBuffer, nullptr);
|
||||
|
||||
for (int chan = 0; chan < numDeviceOutputChannels; ++chan)
|
||||
{
|
||||
AudioData::Pointer <AudioData::Int16, AudioData::NativeEndian, AudioData::Interleaved, AudioData::NonConst> d (dest + chan, numDeviceOutputChannels);
|
||||
|
||||
const float* const sourceChanData = outputChannelBuffer.getReadPointer (jmin (chan, outputChannelBuffer.getNumChannels() - 1));
|
||||
AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> s (sourceChanData);
|
||||
d.convertSamples (s, actualBufferSize);
|
||||
}
|
||||
|
||||
env->ReleaseShortArrayElements (audioBuffer, dest, 0);
|
||||
jint numWritten = env->CallIntMethod (outputDevice, AudioTrack.write, audioBuffer, 0, actualBufferSize * numDeviceOutputChannels);
|
||||
|
||||
if (numWritten < actualBufferSize * numDeviceOutputChannels)
|
||||
{
|
||||
DBG ("Audio write underrun! " << numWritten);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int minBufferSizeOut, minBufferSizeIn;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
CriticalSection callbackLock;
|
||||
AudioIODeviceCallback* callback;
|
||||
jint sampleRate;
|
||||
int numClientInputChannels, numDeviceInputChannels, numDeviceInputChannelsAvailable;
|
||||
int numClientOutputChannels, numDeviceOutputChannels;
|
||||
int actualBufferSize;
|
||||
bool isRunning;
|
||||
String lastError;
|
||||
BigInteger activeOutputChans, activeInputChans;
|
||||
GlobalRef outputDevice, inputDevice;
|
||||
AudioBuffer<float> inputChannelBuffer, outputChannelBuffer;
|
||||
jmethodID getUnderrunCount = nullptr;
|
||||
|
||||
void closeDevices()
|
||||
{
|
||||
if (outputDevice != nullptr)
|
||||
{
|
||||
outputDevice.callVoidMethod (AudioTrack.stop);
|
||||
outputDevice.callVoidMethod (AudioTrack.release);
|
||||
outputDevice.clear();
|
||||
}
|
||||
|
||||
if (inputDevice != nullptr)
|
||||
{
|
||||
inputDevice.callVoidMethod (AudioRecord.stop);
|
||||
inputDevice.callVoidMethod (AudioRecord.release);
|
||||
inputDevice.clear();
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (AndroidAudioIODevice)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class AndroidAudioIODeviceType : public AudioIODeviceType
|
||||
{
|
||||
public:
|
||||
AndroidAudioIODeviceType() : AudioIODeviceType (javaAudioTypeName) {}
|
||||
|
||||
//==============================================================================
|
||||
void scanForDevices() {}
|
||||
StringArray getDeviceNames (bool) const { return StringArray (javaAudioTypeName); }
|
||||
int getDefaultDeviceIndex (bool) const { return 0; }
|
||||
int getIndexOfDevice (AudioIODevice* device, bool) const { return device != nullptr ? 0 : -1; }
|
||||
bool hasSeparateInputsAndOutputs() const { return false; }
|
||||
|
||||
AudioIODevice* createDevice (const String& outputDeviceName,
|
||||
const String& inputDeviceName)
|
||||
{
|
||||
std::unique_ptr<AndroidAudioIODevice> dev;
|
||||
|
||||
if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty())
|
||||
{
|
||||
dev.reset (new AndroidAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
|
||||
: inputDeviceName));
|
||||
|
||||
if (dev->getCurrentSampleRate() <= 0 || dev->getDefaultBufferSize() <= 0)
|
||||
dev = nullptr;
|
||||
}
|
||||
|
||||
return dev.release();
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidAudioIODeviceType)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
extern bool isOboeAvailable();
|
||||
extern bool isOpenSLAvailable();
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (getMinBufferSize, "getMinBufferSize", "(III)I") \
|
||||
STATICMETHOD (getNativeOutputSampleRate, "getNativeOutputSampleRate", "(I)I") \
|
||||
METHOD (constructor, "<init>", "(IIIIII)V") \
|
||||
METHOD (getState, "getState", "()I") \
|
||||
METHOD (play, "play", "()V") \
|
||||
METHOD (stop, "stop", "()V") \
|
||||
METHOD (release, "release", "()V") \
|
||||
METHOD (flush, "flush", "()V") \
|
||||
METHOD (write, "write", "([SII)I") \
|
||||
|
||||
DECLARE_JNI_CLASS (AudioTrack, "android/media/AudioTrack")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (getMinBufferSize, "getMinBufferSize", "(III)I") \
|
||||
METHOD (constructor, "<init>", "(IIIII)V") \
|
||||
METHOD (getState, "getState", "()I") \
|
||||
METHOD (startRecording, "startRecording", "()V") \
|
||||
METHOD (stop, "stop", "()V") \
|
||||
METHOD (read, "read", "([SII)I") \
|
||||
METHOD (release, "release", "()V") \
|
||||
|
||||
DECLARE_JNI_CLASS (AudioRecord, "android/media/AudioRecord")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
enum
|
||||
{
|
||||
CHANNEL_OUT_STEREO = 12,
|
||||
CHANNEL_IN_STEREO = 12,
|
||||
CHANNEL_IN_MONO = 16,
|
||||
ENCODING_PCM_16BIT = 2,
|
||||
STREAM_MUSIC = 3,
|
||||
MODE_STREAM = 1,
|
||||
STATE_UNINITIALIZED = 0
|
||||
};
|
||||
|
||||
const char* const javaAudioTypeName = "Android Audio";
|
||||
|
||||
//==============================================================================
|
||||
class AndroidAudioIODevice : public AudioIODevice,
|
||||
public Thread
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
AndroidAudioIODevice (const String& deviceName)
|
||||
: AudioIODevice (deviceName, javaAudioTypeName),
|
||||
Thread ("audio"),
|
||||
minBufferSizeOut (0), minBufferSizeIn (0), callback (nullptr), sampleRate (0),
|
||||
numClientInputChannels (0), numDeviceInputChannels (0), numDeviceInputChannelsAvailable (2),
|
||||
numClientOutputChannels (0), numDeviceOutputChannels (0),
|
||||
actualBufferSize (0), isRunning (false),
|
||||
inputChannelBuffer (1, 1),
|
||||
outputChannelBuffer (1, 1)
|
||||
{
|
||||
JNIEnv* env = getEnv();
|
||||
sampleRate = env->CallStaticIntMethod (AudioTrack, AudioTrack.getNativeOutputSampleRate, MODE_STREAM);
|
||||
|
||||
minBufferSizeOut = (int) env->CallStaticIntMethod (AudioTrack, AudioTrack.getMinBufferSize, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT);
|
||||
minBufferSizeIn = (int) env->CallStaticIntMethod (AudioRecord, AudioRecord.getMinBufferSize, sampleRate, CHANNEL_IN_STEREO, ENCODING_PCM_16BIT);
|
||||
|
||||
if (minBufferSizeIn <= 0)
|
||||
{
|
||||
minBufferSizeIn = env->CallStaticIntMethod (AudioRecord, AudioRecord.getMinBufferSize, sampleRate, CHANNEL_IN_MONO, ENCODING_PCM_16BIT);
|
||||
|
||||
if (minBufferSizeIn > 0)
|
||||
numDeviceInputChannelsAvailable = 1;
|
||||
else
|
||||
numDeviceInputChannelsAvailable = 0;
|
||||
}
|
||||
|
||||
DBG ("Audio device - min buffers: " << minBufferSizeOut << ", " << minBufferSizeIn << "; "
|
||||
<< sampleRate << " Hz; input chans: " << numDeviceInputChannelsAvailable);
|
||||
}
|
||||
|
||||
~AndroidAudioIODevice() override
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
StringArray getOutputChannelNames() override
|
||||
{
|
||||
StringArray s;
|
||||
s.add ("Left");
|
||||
s.add ("Right");
|
||||
return s;
|
||||
}
|
||||
|
||||
StringArray getInputChannelNames() override
|
||||
{
|
||||
StringArray s;
|
||||
|
||||
if (numDeviceInputChannelsAvailable == 2)
|
||||
{
|
||||
s.add ("Left");
|
||||
s.add ("Right");
|
||||
}
|
||||
else if (numDeviceInputChannelsAvailable == 1)
|
||||
{
|
||||
s.add ("Audio Input");
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Array<double> getAvailableSampleRates() override
|
||||
{
|
||||
Array<double> r;
|
||||
r.add ((double) sampleRate);
|
||||
return r;
|
||||
}
|
||||
|
||||
Array<int> getAvailableBufferSizes() override
|
||||
{
|
||||
Array<int> b;
|
||||
int n = 16;
|
||||
|
||||
for (int i = 0; i < 50; ++i)
|
||||
{
|
||||
b.add (n);
|
||||
n += n < 64 ? 16
|
||||
: (n < 512 ? 32
|
||||
: (n < 1024 ? 64
|
||||
: (n < 2048 ? 128 : 256)));
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
int getDefaultBufferSize() override { return 2048; }
|
||||
|
||||
String open (const BigInteger& inputChannels,
|
||||
const BigInteger& outputChannels,
|
||||
double requestedSampleRate,
|
||||
int bufferSize) override
|
||||
{
|
||||
close();
|
||||
|
||||
if (sampleRate != (int) requestedSampleRate)
|
||||
return "Sample rate not allowed";
|
||||
|
||||
lastError.clear();
|
||||
int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize;
|
||||
|
||||
numDeviceInputChannels = 0;
|
||||
numDeviceOutputChannels = 0;
|
||||
|
||||
activeOutputChans = outputChannels;
|
||||
activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false);
|
||||
numClientOutputChannels = activeOutputChans.countNumberOfSetBits();
|
||||
|
||||
activeInputChans = inputChannels;
|
||||
activeInputChans.setRange (2, activeInputChans.getHighestBit(), false);
|
||||
numClientInputChannels = activeInputChans.countNumberOfSetBits();
|
||||
|
||||
actualBufferSize = preferredBufferSize;
|
||||
inputChannelBuffer.setSize (2, actualBufferSize);
|
||||
inputChannelBuffer.clear();
|
||||
outputChannelBuffer.setSize (2, actualBufferSize);
|
||||
outputChannelBuffer.clear();
|
||||
|
||||
JNIEnv* env = getEnv();
|
||||
|
||||
if (numClientOutputChannels > 0)
|
||||
{
|
||||
numDeviceOutputChannels = 2;
|
||||
outputDevice = GlobalRef (LocalRef<jobject>(env->NewObject (AudioTrack, AudioTrack.constructor,
|
||||
STREAM_MUSIC, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT,
|
||||
(jint) (minBufferSizeOut * numDeviceOutputChannels * static_cast<int> (sizeof (int16))), MODE_STREAM)));
|
||||
|
||||
const bool supportsUnderrunCount = (getAndroidSDKVersion() >= 24);
|
||||
getUnderrunCount = supportsUnderrunCount ? env->GetMethodID (AudioTrack, "getUnderrunCount", "()I") : nullptr;
|
||||
|
||||
int outputDeviceState = env->CallIntMethod (outputDevice, AudioTrack.getState);
|
||||
if (outputDeviceState > 0)
|
||||
{
|
||||
isRunning = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// failed to open the device
|
||||
outputDevice.clear();
|
||||
lastError = "Error opening audio output device: android.media.AudioTrack failed with state = " + String (outputDeviceState);
|
||||
}
|
||||
}
|
||||
|
||||
if (numClientInputChannels > 0 && numDeviceInputChannelsAvailable > 0)
|
||||
{
|
||||
if (! RuntimePermissions::isGranted (RuntimePermissions::recordAudio))
|
||||
{
|
||||
// If you hit this assert, you probably forgot to get RuntimePermissions::recordAudio
|
||||
// before trying to open an audio input device. This is not going to work!
|
||||
jassertfalse;
|
||||
|
||||
inputDevice.clear();
|
||||
lastError = "Error opening audio input device: the app was not granted android.permission.RECORD_AUDIO";
|
||||
}
|
||||
else
|
||||
{
|
||||
numDeviceInputChannels = jmin (numClientInputChannels, numDeviceInputChannelsAvailable);
|
||||
inputDevice = GlobalRef (LocalRef<jobject>(env->NewObject (AudioRecord, AudioRecord.constructor,
|
||||
0 /* (default audio source) */, sampleRate,
|
||||
numDeviceInputChannelsAvailable > 1 ? CHANNEL_IN_STEREO : CHANNEL_IN_MONO,
|
||||
ENCODING_PCM_16BIT,
|
||||
(jint) (minBufferSizeIn * numDeviceInputChannels * static_cast<int> (sizeof (int16))))));
|
||||
|
||||
int inputDeviceState = env->CallIntMethod (inputDevice, AudioRecord.getState);
|
||||
if (inputDeviceState > 0)
|
||||
{
|
||||
isRunning = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// failed to open the device
|
||||
inputDevice.clear();
|
||||
lastError = "Error opening audio input device: android.media.AudioRecord failed with state = " + String (inputDeviceState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isRunning)
|
||||
{
|
||||
if (outputDevice != nullptr)
|
||||
env->CallVoidMethod (outputDevice, AudioTrack.play);
|
||||
|
||||
if (inputDevice != nullptr)
|
||||
env->CallVoidMethod (inputDevice, AudioRecord.startRecording);
|
||||
|
||||
startThread (8);
|
||||
}
|
||||
else
|
||||
{
|
||||
closeDevices();
|
||||
}
|
||||
|
||||
return lastError;
|
||||
}
|
||||
|
||||
void close() override
|
||||
{
|
||||
if (isRunning)
|
||||
{
|
||||
stopThread (2000);
|
||||
isRunning = false;
|
||||
closeDevices();
|
||||
}
|
||||
}
|
||||
|
||||
int getOutputLatencyInSamples() override { return (minBufferSizeOut * 3) / 4; }
|
||||
int getInputLatencyInSamples() override { return (minBufferSizeIn * 3) / 4; }
|
||||
bool isOpen() override { return isRunning; }
|
||||
int getCurrentBufferSizeSamples() override { return actualBufferSize; }
|
||||
int getCurrentBitDepth() override { return 16; }
|
||||
double getCurrentSampleRate() override { return sampleRate; }
|
||||
BigInteger getActiveOutputChannels() const override { return activeOutputChans; }
|
||||
BigInteger getActiveInputChannels() const override { return activeInputChans; }
|
||||
String getLastError() override { return lastError; }
|
||||
bool isPlaying() override { return isRunning && callback != nullptr; }
|
||||
|
||||
int getXRunCount() const noexcept override
|
||||
{
|
||||
if (outputDevice != nullptr && getUnderrunCount != nullptr)
|
||||
return getEnv()->CallIntMethod (outputDevice, getUnderrunCount);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void start (AudioIODeviceCallback* newCallback) override
|
||||
{
|
||||
if (isRunning && callback != newCallback)
|
||||
{
|
||||
if (newCallback != nullptr)
|
||||
newCallback->audioDeviceAboutToStart (this);
|
||||
|
||||
const ScopedLock sl (callbackLock);
|
||||
callback = newCallback;
|
||||
}
|
||||
}
|
||||
|
||||
void stop() override
|
||||
{
|
||||
if (isRunning)
|
||||
{
|
||||
AudioIODeviceCallback* lastCallback;
|
||||
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
lastCallback = callback;
|
||||
callback = nullptr;
|
||||
}
|
||||
|
||||
if (lastCallback != nullptr)
|
||||
lastCallback->audioDeviceStopped();
|
||||
}
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
JNIEnv* env = getEnv();
|
||||
jshortArray audioBuffer = env->NewShortArray (actualBufferSize * jmax (numDeviceOutputChannels, numDeviceInputChannels));
|
||||
|
||||
using NativeInt16 = AudioData::Format<AudioData::Int16, AudioData::NativeEndian>;
|
||||
using NativeFloat32 = AudioData::Format<AudioData::Float32, AudioData::NativeEndian>;
|
||||
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
if (inputDevice != nullptr)
|
||||
{
|
||||
jint numRead = env->CallIntMethod (inputDevice, AudioRecord.read, audioBuffer, 0, actualBufferSize * numDeviceInputChannels);
|
||||
|
||||
if (numRead < actualBufferSize * numDeviceInputChannels)
|
||||
{
|
||||
DBG ("Audio read under-run! " << numRead);
|
||||
}
|
||||
|
||||
jshort* const src = env->GetShortArrayElements (audioBuffer, nullptr);
|
||||
|
||||
AudioData::deinterleaveSamples (AudioData::InterleavedSource<NativeInt16> { reinterpret_cast<const uint16*> (src), numDeviceInputChannels },
|
||||
AudioData::NonInterleavedDest<NativeFloat32> { inputChannelBuffer.getArrayOfWritePointers(), inputChannelBuffer.getNumChannels() },
|
||||
actualBufferSize);
|
||||
|
||||
env->ReleaseShortArrayElements (audioBuffer, src, 0);
|
||||
}
|
||||
|
||||
if (threadShouldExit())
|
||||
break;
|
||||
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
if (callback != nullptr)
|
||||
{
|
||||
callback->audioDeviceIOCallbackWithContext (inputChannelBuffer.getArrayOfReadPointers(),
|
||||
numClientInputChannels,
|
||||
outputChannelBuffer.getArrayOfWritePointers(),
|
||||
numClientOutputChannels,
|
||||
actualBufferSize, {});
|
||||
}
|
||||
else
|
||||
{
|
||||
outputChannelBuffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (outputDevice != nullptr)
|
||||
{
|
||||
if (threadShouldExit())
|
||||
break;
|
||||
|
||||
jshort* const dest = env->GetShortArrayElements (audioBuffer, nullptr);
|
||||
|
||||
AudioData::interleaveSamples (AudioData::NonInterleavedSource<NativeFloat32> { outputChannelBuffer.getArrayOfReadPointers(), outputChannelBuffer.getNumChannels() },
|
||||
AudioData::InterleavedDest<NativeInt16> { reinterpret_cast<uint16*> (dest), numDeviceOutputChannels },
|
||||
actualBufferSize);
|
||||
|
||||
env->ReleaseShortArrayElements (audioBuffer, dest, 0);
|
||||
jint numWritten = env->CallIntMethod (outputDevice, AudioTrack.write, audioBuffer, 0, actualBufferSize * numDeviceOutputChannels);
|
||||
|
||||
if (numWritten < actualBufferSize * numDeviceOutputChannels)
|
||||
{
|
||||
DBG ("Audio write underrun! " << numWritten);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int minBufferSizeOut, minBufferSizeIn;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
CriticalSection callbackLock;
|
||||
AudioIODeviceCallback* callback;
|
||||
jint sampleRate;
|
||||
int numClientInputChannels, numDeviceInputChannels, numDeviceInputChannelsAvailable;
|
||||
int numClientOutputChannels, numDeviceOutputChannels;
|
||||
int actualBufferSize;
|
||||
bool isRunning;
|
||||
String lastError;
|
||||
BigInteger activeOutputChans, activeInputChans;
|
||||
GlobalRef outputDevice, inputDevice;
|
||||
AudioBuffer<float> inputChannelBuffer, outputChannelBuffer;
|
||||
jmethodID getUnderrunCount = nullptr;
|
||||
|
||||
void closeDevices()
|
||||
{
|
||||
if (outputDevice != nullptr)
|
||||
{
|
||||
outputDevice.callVoidMethod (AudioTrack.stop);
|
||||
outputDevice.callVoidMethod (AudioTrack.release);
|
||||
outputDevice.clear();
|
||||
}
|
||||
|
||||
if (inputDevice != nullptr)
|
||||
{
|
||||
inputDevice.callVoidMethod (AudioRecord.stop);
|
||||
inputDevice.callVoidMethod (AudioRecord.release);
|
||||
inputDevice.clear();
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (AndroidAudioIODevice)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class AndroidAudioIODeviceType : public AudioIODeviceType
|
||||
{
|
||||
public:
|
||||
AndroidAudioIODeviceType() : AudioIODeviceType (javaAudioTypeName) {}
|
||||
|
||||
//==============================================================================
|
||||
void scanForDevices() {}
|
||||
StringArray getDeviceNames (bool) const { return StringArray (javaAudioTypeName); }
|
||||
int getDefaultDeviceIndex (bool) const { return 0; }
|
||||
int getIndexOfDevice (AudioIODevice* device, bool) const { return device != nullptr ? 0 : -1; }
|
||||
bool hasSeparateInputsAndOutputs() const { return false; }
|
||||
|
||||
AudioIODevice* createDevice (const String& outputDeviceName,
|
||||
const String& inputDeviceName)
|
||||
{
|
||||
std::unique_ptr<AndroidAudioIODevice> dev;
|
||||
|
||||
if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty())
|
||||
{
|
||||
dev.reset (new AndroidAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
|
||||
: inputDeviceName));
|
||||
|
||||
if (dev->getCurrentSampleRate() <= 0 || dev->getDefaultBufferSize() <= 0)
|
||||
dev = nullptr;
|
||||
}
|
||||
|
||||
return dev.release();
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidAudioIODeviceType)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
extern bool isOboeAvailable();
|
||||
extern bool isOpenSLAvailable();
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,131 +1,131 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Some shared helpers methods for using the high-performance audio paths on
|
||||
Android devices (OpenSL and Oboe).
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
namespace AndroidHighPerformanceAudioHelpers
|
||||
{
|
||||
//==============================================================================
|
||||
static double getNativeSampleRate()
|
||||
{
|
||||
return audioManagerGetProperty ("android.media.property.OUTPUT_SAMPLE_RATE").getDoubleValue();
|
||||
}
|
||||
|
||||
static int getNativeBufferSizeHint()
|
||||
{
|
||||
// This property is a hint of a native buffer size but it does not guarantee the size used.
|
||||
auto deviceBufferSize = audioManagerGetProperty ("android.media.property.OUTPUT_FRAMES_PER_BUFFER").getIntValue();
|
||||
|
||||
if (deviceBufferSize == 0)
|
||||
return 192;
|
||||
|
||||
return deviceBufferSize;
|
||||
}
|
||||
|
||||
static bool isProAudioDevice()
|
||||
{
|
||||
static bool isSapaSupported = SystemStats::getDeviceManufacturer().containsIgnoreCase ("SAMSUNG")
|
||||
&& DynamicLibrary().open ("libapa_jni.so");
|
||||
|
||||
return androidHasSystemFeature ("android.hardware.audio.pro") || isSapaSupported;
|
||||
}
|
||||
|
||||
static bool hasLowLatencyAudioPath()
|
||||
{
|
||||
return androidHasSystemFeature ("android.hardware.audio.low_latency");
|
||||
}
|
||||
|
||||
static bool canUseHighPerformanceAudioPath (int nativeBufferSize, int requestedBufferSize, int requestedSampleRate)
|
||||
{
|
||||
return ((requestedBufferSize % nativeBufferSize) == 0)
|
||||
&& (requestedSampleRate == getNativeSampleRate())
|
||||
&& isProAudioDevice();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static int getMinimumBuffersToEnqueue (int nativeBufferSize, double requestedSampleRate)
|
||||
{
|
||||
if (canUseHighPerformanceAudioPath (nativeBufferSize, nativeBufferSize, (int) requestedSampleRate))
|
||||
{
|
||||
// see https://developer.android.com/ndk/guides/audio/opensl/opensl-prog-notes.html#sandp
|
||||
// "For Android 4.2 (API level 17) and earlier, a buffer count of two or more is required
|
||||
// for lower latency. Beginning with Android 4.3 (API level 18), a buffer count of one
|
||||
// is sufficient for lower latency."
|
||||
return (getAndroidSDKVersion() >= 18 ? 1 : 2);
|
||||
}
|
||||
|
||||
// not using low-latency path so we can use the absolute minimum number of buffers to queue
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int buffersToQueueForBufferDuration (int nativeBufferSize, int bufferDurationInMs, double sampleRate) noexcept
|
||||
{
|
||||
auto maxBufferFrames = static_cast<int> (std::ceil (bufferDurationInMs * sampleRate / 1000.0));
|
||||
auto maxNumBuffers = static_cast<int> (std::ceil (static_cast<double> (maxBufferFrames)
|
||||
/ static_cast<double> (nativeBufferSize)));
|
||||
|
||||
return jmax (getMinimumBuffersToEnqueue (nativeBufferSize, sampleRate), maxNumBuffers);
|
||||
}
|
||||
|
||||
static int getMaximumBuffersToEnqueue (int nativeBufferSize, double maximumSampleRate) noexcept
|
||||
{
|
||||
static constexpr int maxBufferSizeMs = 200;
|
||||
|
||||
return jmax (8, buffersToQueueForBufferDuration (nativeBufferSize, maxBufferSizeMs, maximumSampleRate));
|
||||
}
|
||||
|
||||
static Array<int> getAvailableBufferSizes (int nativeBufferSize, Array<double> availableSampleRates)
|
||||
{
|
||||
auto minBuffersToQueue = getMinimumBuffersToEnqueue (nativeBufferSize, getNativeSampleRate());
|
||||
auto maxBuffersToQueue = getMaximumBuffersToEnqueue (nativeBufferSize, findMaximum (availableSampleRates.getRawDataPointer(),
|
||||
availableSampleRates.size()));
|
||||
|
||||
Array<int> bufferSizes;
|
||||
|
||||
for (int i = minBuffersToQueue; i <= maxBuffersToQueue; ++i)
|
||||
bufferSizes.add (i * nativeBufferSize);
|
||||
|
||||
return bufferSizes;
|
||||
}
|
||||
|
||||
static int getDefaultBufferSize (int nativeBufferSize, double currentSampleRate)
|
||||
{
|
||||
static constexpr int defaultBufferSizeForLowLatencyDeviceMs = 40;
|
||||
static constexpr int defaultBufferSizeForStandardLatencyDeviceMs = 100;
|
||||
|
||||
auto defaultBufferLength = (hasLowLatencyAudioPath() ? defaultBufferSizeForLowLatencyDeviceMs
|
||||
: defaultBufferSizeForStandardLatencyDeviceMs);
|
||||
|
||||
auto defaultBuffersToEnqueue = buffersToQueueForBufferDuration (nativeBufferSize, defaultBufferLength, currentSampleRate);
|
||||
return defaultBuffersToEnqueue * nativeBufferSize;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Some shared helpers methods for using the high-performance audio paths on
|
||||
Android devices (OpenSL and Oboe).
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
namespace AndroidHighPerformanceAudioHelpers
|
||||
{
|
||||
//==============================================================================
|
||||
static double getNativeSampleRate()
|
||||
{
|
||||
return audioManagerGetProperty ("android.media.property.OUTPUT_SAMPLE_RATE").getDoubleValue();
|
||||
}
|
||||
|
||||
static int getNativeBufferSizeHint()
|
||||
{
|
||||
// This property is a hint of a native buffer size but it does not guarantee the size used.
|
||||
auto deviceBufferSize = audioManagerGetProperty ("android.media.property.OUTPUT_FRAMES_PER_BUFFER").getIntValue();
|
||||
|
||||
if (deviceBufferSize == 0)
|
||||
return 192;
|
||||
|
||||
return deviceBufferSize;
|
||||
}
|
||||
|
||||
static bool isProAudioDevice()
|
||||
{
|
||||
static bool isSapaSupported = SystemStats::getDeviceManufacturer().containsIgnoreCase ("SAMSUNG")
|
||||
&& DynamicLibrary().open ("libapa_jni.so");
|
||||
|
||||
return androidHasSystemFeature ("android.hardware.audio.pro") || isSapaSupported;
|
||||
}
|
||||
|
||||
static bool hasLowLatencyAudioPath()
|
||||
{
|
||||
return androidHasSystemFeature ("android.hardware.audio.low_latency");
|
||||
}
|
||||
|
||||
static bool canUseHighPerformanceAudioPath (int nativeBufferSize, int requestedBufferSize, int requestedSampleRate)
|
||||
{
|
||||
return ((requestedBufferSize % nativeBufferSize) == 0)
|
||||
&& (requestedSampleRate == getNativeSampleRate())
|
||||
&& isProAudioDevice();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static int getMinimumBuffersToEnqueue (int nativeBufferSize, double requestedSampleRate)
|
||||
{
|
||||
if (canUseHighPerformanceAudioPath (nativeBufferSize, nativeBufferSize, (int) requestedSampleRate))
|
||||
{
|
||||
// see https://developer.android.com/ndk/guides/audio/opensl/opensl-prog-notes.html#sandp
|
||||
// "For Android 4.2 (API level 17) and earlier, a buffer count of two or more is required
|
||||
// for lower latency. Beginning with Android 4.3 (API level 18), a buffer count of one
|
||||
// is sufficient for lower latency."
|
||||
return (getAndroidSDKVersion() >= 18 ? 1 : 2);
|
||||
}
|
||||
|
||||
// not using low-latency path so we can use the absolute minimum number of buffers to queue
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int buffersToQueueForBufferDuration (int nativeBufferSize, int bufferDurationInMs, double sampleRate) noexcept
|
||||
{
|
||||
auto maxBufferFrames = static_cast<int> (std::ceil (bufferDurationInMs * sampleRate / 1000.0));
|
||||
auto maxNumBuffers = static_cast<int> (std::ceil (static_cast<double> (maxBufferFrames)
|
||||
/ static_cast<double> (nativeBufferSize)));
|
||||
|
||||
return jmax (getMinimumBuffersToEnqueue (nativeBufferSize, sampleRate), maxNumBuffers);
|
||||
}
|
||||
|
||||
static int getMaximumBuffersToEnqueue (int nativeBufferSize, double maximumSampleRate) noexcept
|
||||
{
|
||||
static constexpr int maxBufferSizeMs = 200;
|
||||
|
||||
return jmax (8, buffersToQueueForBufferDuration (nativeBufferSize, maxBufferSizeMs, maximumSampleRate));
|
||||
}
|
||||
|
||||
static Array<int> getAvailableBufferSizes (int nativeBufferSize, Array<double> availableSampleRates)
|
||||
{
|
||||
auto minBuffersToQueue = getMinimumBuffersToEnqueue (nativeBufferSize, getNativeSampleRate());
|
||||
auto maxBuffersToQueue = getMaximumBuffersToEnqueue (nativeBufferSize, findMaximum (availableSampleRates.getRawDataPointer(),
|
||||
availableSampleRates.size()));
|
||||
|
||||
Array<int> bufferSizes;
|
||||
|
||||
for (int i = minBuffersToQueue; i <= maxBuffersToQueue; ++i)
|
||||
bufferSizes.add (i * nativeBufferSize);
|
||||
|
||||
return bufferSizes;
|
||||
}
|
||||
|
||||
static int getDefaultBufferSize (int nativeBufferSize, double currentSampleRate)
|
||||
{
|
||||
static constexpr int defaultBufferSizeForLowLatencyDeviceMs = 40;
|
||||
static constexpr int defaultBufferSizeForStandardLatencyDeviceMs = 100;
|
||||
|
||||
auto defaultBufferLength = (hasLowLatencyAudioPath() ? defaultBufferSizeForLowLatencyDeviceMs
|
||||
: defaultBufferSizeForStandardLatencyDeviceMs);
|
||||
|
||||
auto defaultBuffersToEnqueue = buffersToQueueForBufferDuration (nativeBufferSize, defaultBufferLength, currentSampleRate);
|
||||
return defaultBuffersToEnqueue * nativeBufferSize;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,100 +1,92 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
class iOSAudioIODeviceType;
|
||||
|
||||
class iOSAudioIODevice : public AudioIODevice
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
String open (const BigInteger&, const BigInteger&, double, int) override;
|
||||
void close() override;
|
||||
|
||||
void start (AudioIODeviceCallback*) override;
|
||||
void stop() override;
|
||||
|
||||
Array<double> getAvailableSampleRates() override;
|
||||
Array<int> getAvailableBufferSizes() override;
|
||||
|
||||
bool setAudioPreprocessingEnabled (bool) override;
|
||||
|
||||
//==============================================================================
|
||||
bool isPlaying() override;
|
||||
bool isOpen() override;
|
||||
String getLastError() override;
|
||||
|
||||
//==============================================================================
|
||||
StringArray getOutputChannelNames() override;
|
||||
StringArray getInputChannelNames() override;
|
||||
|
||||
int getDefaultBufferSize() override;
|
||||
int getCurrentBufferSizeSamples() override;
|
||||
|
||||
double getCurrentSampleRate() override;
|
||||
|
||||
int getCurrentBitDepth() override;
|
||||
|
||||
BigInteger getActiveOutputChannels() const override;
|
||||
BigInteger getActiveInputChannels() const override;
|
||||
|
||||
int getOutputLatencyInSamples() override;
|
||||
int getInputLatencyInSamples() override;
|
||||
|
||||
int getXRunCount() const noexcept override;
|
||||
|
||||
//==============================================================================
|
||||
void setMidiMessageCollector (MidiMessageCollector*);
|
||||
AudioPlayHead* getAudioPlayHead() const;
|
||||
|
||||
//==============================================================================
|
||||
bool isInterAppAudioConnected() const;
|
||||
#if JUCE_MODULE_AVAILABLE_juce_graphics
|
||||
Image getIcon (int size);
|
||||
#endif
|
||||
void switchApplication();
|
||||
|
||||
bool isHeadphonesConnected() const;
|
||||
|
||||
bool setInputGain(float val);
|
||||
float getInputGain() const;
|
||||
|
||||
void setAllowBluetoothInput(bool flag);
|
||||
bool getAllowBluetoothInput() const;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
iOSAudioIODevice (iOSAudioIODeviceType*, const String&, const String&);
|
||||
|
||||
//==============================================================================
|
||||
friend class iOSAudioIODeviceType;
|
||||
friend struct AudioSessionHolder;
|
||||
|
||||
struct Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (iOSAudioIODevice)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
class iOSAudioIODeviceType;
|
||||
|
||||
class iOSAudioIODevice : public AudioIODevice
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
String open (const BigInteger&, const BigInteger&, double, int) override;
|
||||
void close() override;
|
||||
|
||||
void start (AudioIODeviceCallback*) override;
|
||||
void stop() override;
|
||||
|
||||
Array<double> getAvailableSampleRates() override;
|
||||
Array<int> getAvailableBufferSizes() override;
|
||||
|
||||
bool setAudioPreprocessingEnabled (bool) override;
|
||||
|
||||
//==============================================================================
|
||||
bool isPlaying() override;
|
||||
bool isOpen() override;
|
||||
String getLastError() override;
|
||||
|
||||
//==============================================================================
|
||||
StringArray getOutputChannelNames() override;
|
||||
StringArray getInputChannelNames() override;
|
||||
|
||||
int getDefaultBufferSize() override;
|
||||
int getCurrentBufferSizeSamples() override;
|
||||
|
||||
double getCurrentSampleRate() override;
|
||||
|
||||
int getCurrentBitDepth() override;
|
||||
|
||||
BigInteger getActiveOutputChannels() const override;
|
||||
BigInteger getActiveInputChannels() const override;
|
||||
|
||||
int getOutputLatencyInSamples() override;
|
||||
int getInputLatencyInSamples() override;
|
||||
|
||||
int getXRunCount() const noexcept override;
|
||||
|
||||
//==============================================================================
|
||||
void setMidiMessageCollector (MidiMessageCollector*);
|
||||
AudioPlayHead* getAudioPlayHead() const;
|
||||
|
||||
//==============================================================================
|
||||
bool isInterAppAudioConnected() const;
|
||||
#if JUCE_MODULE_AVAILABLE_juce_graphics
|
||||
Image getIcon (int size);
|
||||
#endif
|
||||
void switchApplication();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
iOSAudioIODevice (iOSAudioIODeviceType*, const String&, const String&);
|
||||
|
||||
//==============================================================================
|
||||
friend class iOSAudioIODeviceType;
|
||||
friend struct AudioSessionHolder;
|
||||
|
||||
struct Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (iOSAudioIODevice)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
@ -82,10 +82,8 @@ namespace CoreMidiHelpers
|
||||
struct Sender;
|
||||
|
||||
#if JUCE_HAS_NEW_COREMIDI_API
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability-new")
|
||||
|
||||
template <>
|
||||
struct Sender<ImplementationStrategy::onlyNew> : public SenderBase
|
||||
struct API_AVAILABLE (macos (11.0), ios (14.0)) Sender<ImplementationStrategy::onlyNew> : public SenderBase
|
||||
{
|
||||
explicit Sender (MIDIEndpointRef ep)
|
||||
: umpConverter (getProtocolForEndpoint (ep))
|
||||
@ -177,8 +175,6 @@ namespace CoreMidiHelpers
|
||||
send();
|
||||
}
|
||||
};
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
#endif
|
||||
|
||||
#if JUCE_HAS_OLD_COREMIDI_API
|
||||
@ -829,10 +825,8 @@ namespace CoreMidiHelpers
|
||||
struct CreatorFunctions;
|
||||
|
||||
#if JUCE_HAS_NEW_COREMIDI_API
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability-new")
|
||||
|
||||
template <>
|
||||
struct CreatorFunctions<ImplementationStrategy::onlyNew>
|
||||
struct API_AVAILABLE (macos (11.0), ios (14.0)) CreatorFunctions<ImplementationStrategy::onlyNew>
|
||||
{
|
||||
static OSStatus createInputPort (ump::PacketProtocol protocol,
|
||||
MIDIClientRef client,
|
||||
@ -894,8 +888,6 @@ namespace CoreMidiHelpers
|
||||
static_cast<MidiPortAndCallback*> (readProcRefCon)->handlePackets (*list);
|
||||
}
|
||||
};
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
#endif
|
||||
|
||||
#if JUCE_HAS_OLD_COREMIDI_API
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,6 @@
|
||||
#include <vector>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <cassert>
|
||||
|
||||
#ifndef MCR_USE_KAISER
|
||||
// It appears from the spectrogram that the HyperbolicCosine window leads to fewer artifacts.
|
||||
|
@ -1,180 +1,180 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
AudioSourcePlayer::AudioSourcePlayer()
|
||||
{
|
||||
}
|
||||
|
||||
AudioSourcePlayer::~AudioSourcePlayer()
|
||||
{
|
||||
setSource (nullptr);
|
||||
}
|
||||
|
||||
void AudioSourcePlayer::setSource (AudioSource* newSource)
|
||||
{
|
||||
if (source != newSource)
|
||||
{
|
||||
auto* oldSource = source;
|
||||
|
||||
if (newSource != nullptr && bufferSize > 0 && sampleRate > 0)
|
||||
newSource->prepareToPlay (bufferSize, sampleRate);
|
||||
|
||||
{
|
||||
const ScopedLock sl (readLock);
|
||||
source = newSource;
|
||||
}
|
||||
|
||||
if (oldSource != nullptr)
|
||||
oldSource->releaseResources();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSourcePlayer::setGain (const float newGain) noexcept
|
||||
{
|
||||
gain = newGain;
|
||||
}
|
||||
|
||||
void AudioSourcePlayer::audioDeviceIOCallback (const float** inputChannelData,
|
||||
int totalNumInputChannels,
|
||||
float** outputChannelData,
|
||||
int totalNumOutputChannels,
|
||||
int numSamples)
|
||||
{
|
||||
// these should have been prepared by audioDeviceAboutToStart()...
|
||||
jassert (sampleRate > 0 && bufferSize > 0);
|
||||
|
||||
const ScopedLock sl (readLock);
|
||||
|
||||
if (source != nullptr)
|
||||
{
|
||||
int numActiveChans = 0, numInputs = 0, numOutputs = 0;
|
||||
|
||||
// messy stuff needed to compact the channels down into an array
|
||||
// of non-zero pointers..
|
||||
for (int i = 0; i < totalNumInputChannels; ++i)
|
||||
{
|
||||
if (inputChannelData[i] != nullptr)
|
||||
{
|
||||
inputChans [numInputs++] = inputChannelData[i];
|
||||
if (numInputs >= numElementsInArray (inputChans))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < totalNumOutputChannels; ++i)
|
||||
{
|
||||
if (outputChannelData[i] != nullptr)
|
||||
{
|
||||
outputChans [numOutputs++] = outputChannelData[i];
|
||||
if (numOutputs >= numElementsInArray (outputChans))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (numInputs > numOutputs)
|
||||
{
|
||||
// if there aren't enough output channels for the number of
|
||||
// inputs, we need to create some temporary extra ones (can't
|
||||
// use the input data in case it gets written to)
|
||||
tempBuffer.setSize (numInputs - numOutputs, numSamples,
|
||||
false, false, true);
|
||||
|
||||
for (int i = 0; i < numOutputs; ++i)
|
||||
{
|
||||
channels[numActiveChans] = outputChans[i];
|
||||
memcpy (channels[numActiveChans], inputChans[i], (size_t) numSamples * sizeof (float));
|
||||
++numActiveChans;
|
||||
}
|
||||
|
||||
for (int i = numOutputs; i < numInputs; ++i)
|
||||
{
|
||||
channels[numActiveChans] = tempBuffer.getWritePointer (i - numOutputs);
|
||||
memcpy (channels[numActiveChans], inputChans[i], (size_t) numSamples * sizeof (float));
|
||||
++numActiveChans;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < numInputs; ++i)
|
||||
{
|
||||
channels[numActiveChans] = outputChans[i];
|
||||
memcpy (channels[numActiveChans], inputChans[i], (size_t) numSamples * sizeof (float));
|
||||
++numActiveChans;
|
||||
}
|
||||
|
||||
for (int i = numInputs; i < numOutputs; ++i)
|
||||
{
|
||||
channels[numActiveChans] = outputChans[i];
|
||||
zeromem (channels[numActiveChans], (size_t) numSamples * sizeof (float));
|
||||
++numActiveChans;
|
||||
}
|
||||
}
|
||||
|
||||
AudioBuffer<float> buffer (channels, numActiveChans, numSamples);
|
||||
|
||||
AudioSourceChannelInfo info (&buffer, 0, numSamples);
|
||||
source->getNextAudioBlock (info);
|
||||
|
||||
for (int i = info.buffer->getNumChannels(); --i >= 0;)
|
||||
buffer.applyGainRamp (i, info.startSample, info.numSamples, lastGain, gain);
|
||||
|
||||
lastGain = gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < totalNumOutputChannels; ++i)
|
||||
if (outputChannelData[i] != nullptr)
|
||||
zeromem (outputChannelData[i], (size_t) numSamples * sizeof (float));
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSourcePlayer::audioDeviceAboutToStart (AudioIODevice* device)
|
||||
{
|
||||
prepareToPlay (device->getCurrentSampleRate(),
|
||||
device->getCurrentBufferSizeSamples());
|
||||
}
|
||||
|
||||
void AudioSourcePlayer::prepareToPlay (double newSampleRate, int newBufferSize)
|
||||
{
|
||||
sampleRate = newSampleRate;
|
||||
bufferSize = newBufferSize;
|
||||
zeromem (channels, sizeof (channels));
|
||||
|
||||
if (source != nullptr)
|
||||
source->prepareToPlay (bufferSize, sampleRate);
|
||||
}
|
||||
|
||||
void AudioSourcePlayer::audioDeviceStopped()
|
||||
{
|
||||
if (source != nullptr)
|
||||
source->releaseResources();
|
||||
|
||||
sampleRate = 0.0;
|
||||
bufferSize = 0;
|
||||
|
||||
tempBuffer.setSize (2, 8);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
AudioSourcePlayer::AudioSourcePlayer()
|
||||
{
|
||||
}
|
||||
|
||||
AudioSourcePlayer::~AudioSourcePlayer()
|
||||
{
|
||||
setSource (nullptr);
|
||||
}
|
||||
|
||||
void AudioSourcePlayer::setSource (AudioSource* newSource)
|
||||
{
|
||||
if (source != newSource)
|
||||
{
|
||||
auto* oldSource = source;
|
||||
|
||||
if (newSource != nullptr && bufferSize > 0 && sampleRate > 0)
|
||||
newSource->prepareToPlay (bufferSize, sampleRate);
|
||||
|
||||
{
|
||||
const ScopedLock sl (readLock);
|
||||
source = newSource;
|
||||
}
|
||||
|
||||
if (oldSource != nullptr)
|
||||
oldSource->releaseResources();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSourcePlayer::setGain (const float newGain) noexcept
|
||||
{
|
||||
gain = newGain;
|
||||
}
|
||||
|
||||
void AudioSourcePlayer::audioDeviceIOCallback (const float** inputChannelData,
|
||||
int totalNumInputChannels,
|
||||
float** outputChannelData,
|
||||
int totalNumOutputChannels,
|
||||
int numSamples)
|
||||
{
|
||||
// these should have been prepared by audioDeviceAboutToStart()...
|
||||
jassert (sampleRate > 0 && bufferSize > 0);
|
||||
|
||||
const ScopedLock sl (readLock);
|
||||
|
||||
if (source != nullptr)
|
||||
{
|
||||
int numActiveChans = 0, numInputs = 0, numOutputs = 0;
|
||||
|
||||
// messy stuff needed to compact the channels down into an array
|
||||
// of non-zero pointers..
|
||||
for (int i = 0; i < totalNumInputChannels; ++i)
|
||||
{
|
||||
if (inputChannelData[i] != nullptr)
|
||||
{
|
||||
inputChans [numInputs++] = inputChannelData[i];
|
||||
if (numInputs >= numElementsInArray (inputChans))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < totalNumOutputChannels; ++i)
|
||||
{
|
||||
if (outputChannelData[i] != nullptr)
|
||||
{
|
||||
outputChans [numOutputs++] = outputChannelData[i];
|
||||
if (numOutputs >= numElementsInArray (outputChans))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (numInputs > numOutputs)
|
||||
{
|
||||
// if there aren't enough output channels for the number of
|
||||
// inputs, we need to create some temporary extra ones (can't
|
||||
// use the input data in case it gets written to)
|
||||
tempBuffer.setSize (numInputs - numOutputs, numSamples,
|
||||
false, false, true);
|
||||
|
||||
for (int i = 0; i < numOutputs; ++i)
|
||||
{
|
||||
channels[numActiveChans] = outputChans[i];
|
||||
memcpy (channels[numActiveChans], inputChans[i], (size_t) numSamples * sizeof (float));
|
||||
++numActiveChans;
|
||||
}
|
||||
|
||||
for (int i = numOutputs; i < numInputs; ++i)
|
||||
{
|
||||
channels[numActiveChans] = tempBuffer.getWritePointer (i - numOutputs);
|
||||
memcpy (channels[numActiveChans], inputChans[i], (size_t) numSamples * sizeof (float));
|
||||
++numActiveChans;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < numInputs; ++i)
|
||||
{
|
||||
channels[numActiveChans] = outputChans[i];
|
||||
memcpy (channels[numActiveChans], inputChans[i], (size_t) numSamples * sizeof (float));
|
||||
++numActiveChans;
|
||||
}
|
||||
|
||||
for (int i = numInputs; i < numOutputs; ++i)
|
||||
{
|
||||
channels[numActiveChans] = outputChans[i];
|
||||
zeromem (channels[numActiveChans], (size_t) numSamples * sizeof (float));
|
||||
++numActiveChans;
|
||||
}
|
||||
}
|
||||
|
||||
AudioBuffer<float> buffer (channels, numActiveChans, numSamples);
|
||||
|
||||
AudioSourceChannelInfo info (&buffer, 0, numSamples);
|
||||
source->getNextAudioBlock (info);
|
||||
|
||||
for (int i = info.buffer->getNumChannels(); --i >= 0;)
|
||||
buffer.applyGainRamp (i, info.startSample, info.numSamples, lastGain, gain);
|
||||
|
||||
lastGain = gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < totalNumOutputChannels; ++i)
|
||||
if (outputChannelData[i] != nullptr)
|
||||
zeromem (outputChannelData[i], (size_t) numSamples * sizeof (float));
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSourcePlayer::audioDeviceAboutToStart (AudioIODevice* device)
|
||||
{
|
||||
prepareToPlay (device->getCurrentSampleRate(),
|
||||
device->getCurrentBufferSizeSamples());
|
||||
}
|
||||
|
||||
void AudioSourcePlayer::prepareToPlay (double newSampleRate, int newBufferSize)
|
||||
{
|
||||
sampleRate = newSampleRate;
|
||||
bufferSize = newBufferSize;
|
||||
zeromem (channels, sizeof (channels));
|
||||
|
||||
if (source != nullptr)
|
||||
source->prepareToPlay (bufferSize, sampleRate);
|
||||
}
|
||||
|
||||
void AudioSourcePlayer::audioDeviceStopped()
|
||||
{
|
||||
if (source != nullptr)
|
||||
source->releaseResources();
|
||||
|
||||
sampleRate = 0.0;
|
||||
bufferSize = 0;
|
||||
|
||||
tempBuffer.setSize (2, 8);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,114 +1,114 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Wrapper class to continuously stream audio from an audio source to an
|
||||
AudioIODevice.
|
||||
|
||||
This object acts as an AudioIODeviceCallback, so can be attached to an
|
||||
output device, and will stream audio from an AudioSource.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API AudioSourcePlayer : public AudioIODeviceCallback
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty AudioSourcePlayer. */
|
||||
AudioSourcePlayer();
|
||||
|
||||
/** Destructor.
|
||||
|
||||
Make sure this object isn't still being used by an AudioIODevice before
|
||||
deleting it!
|
||||
*/
|
||||
~AudioSourcePlayer() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the current audio source to play from.
|
||||
|
||||
If the source passed in is already being used, this method will do nothing.
|
||||
If the source is not null, its prepareToPlay() method will be called
|
||||
before it starts being used for playback.
|
||||
|
||||
If there's another source currently playing, its releaseResources() method
|
||||
will be called after it has been swapped for the new one.
|
||||
|
||||
@param newSource the new source to use - this will NOT be deleted
|
||||
by this object when no longer needed, so it's the
|
||||
caller's responsibility to manage it.
|
||||
*/
|
||||
void setSource (AudioSource* newSource);
|
||||
|
||||
/** Returns the source that's playing.
|
||||
May return nullptr if there's no source.
|
||||
*/
|
||||
AudioSource* getCurrentSource() const noexcept { return source; }
|
||||
|
||||
/** Sets a gain to apply to the audio data.
|
||||
@see getGain
|
||||
*/
|
||||
void setGain (float newGain) noexcept;
|
||||
|
||||
/** Returns the current gain.
|
||||
@see setGain
|
||||
*/
|
||||
float getGain() const noexcept { return gain; }
|
||||
|
||||
//==============================================================================
|
||||
/** Implementation of the AudioIODeviceCallback method. */
|
||||
void audioDeviceIOCallback (const float** inputChannelData,
|
||||
int totalNumInputChannels,
|
||||
float** outputChannelData,
|
||||
int totalNumOutputChannels,
|
||||
int numSamples) override;
|
||||
|
||||
/** Implementation of the AudioIODeviceCallback method. */
|
||||
void audioDeviceAboutToStart (AudioIODevice* device) override;
|
||||
|
||||
/** Implementation of the AudioIODeviceCallback method. */
|
||||
void audioDeviceStopped() override;
|
||||
|
||||
/** An alternative method for initialising the source without an AudioIODevice. */
|
||||
void prepareToPlay (double sampleRate, int blockSize);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
CriticalSection readLock;
|
||||
AudioSource* source = nullptr;
|
||||
double sampleRate = 0;
|
||||
int bufferSize = 0;
|
||||
float* channels[128];
|
||||
float* outputChans[128];
|
||||
const float* inputChans[128];
|
||||
AudioBuffer<float> tempBuffer;
|
||||
float lastGain = 1.0f;
|
||||
std::atomic<float> gain { 1.0f };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSourcePlayer)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Wrapper class to continuously stream audio from an audio source to an
|
||||
AudioIODevice.
|
||||
|
||||
This object acts as an AudioIODeviceCallback, so can be attached to an
|
||||
output device, and will stream audio from an AudioSource.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API AudioSourcePlayer : public AudioIODeviceCallback
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty AudioSourcePlayer. */
|
||||
AudioSourcePlayer();
|
||||
|
||||
/** Destructor.
|
||||
|
||||
Make sure this object isn't still being used by an AudioIODevice before
|
||||
deleting it!
|
||||
*/
|
||||
~AudioSourcePlayer() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the current audio source to play from.
|
||||
|
||||
If the source passed in is already being used, this method will do nothing.
|
||||
If the source is not null, its prepareToPlay() method will be called
|
||||
before it starts being used for playback.
|
||||
|
||||
If there's another source currently playing, its releaseResources() method
|
||||
will be called after it has been swapped for the new one.
|
||||
|
||||
@param newSource the new source to use - this will NOT be deleted
|
||||
by this object when no longer needed, so it's the
|
||||
caller's responsibility to manage it.
|
||||
*/
|
||||
void setSource (AudioSource* newSource);
|
||||
|
||||
/** Returns the source that's playing.
|
||||
May return nullptr if there's no source.
|
||||
*/
|
||||
AudioSource* getCurrentSource() const noexcept { return source; }
|
||||
|
||||
/** Sets a gain to apply to the audio data.
|
||||
@see getGain
|
||||
*/
|
||||
void setGain (float newGain) noexcept;
|
||||
|
||||
/** Returns the current gain.
|
||||
@see setGain
|
||||
*/
|
||||
float getGain() const noexcept { return gain; }
|
||||
|
||||
//==============================================================================
|
||||
/** Implementation of the AudioIODeviceCallback method. */
|
||||
void audioDeviceIOCallback (const float** inputChannelData,
|
||||
int totalNumInputChannels,
|
||||
float** outputChannelData,
|
||||
int totalNumOutputChannels,
|
||||
int numSamples) override;
|
||||
|
||||
/** Implementation of the AudioIODeviceCallback method. */
|
||||
void audioDeviceAboutToStart (AudioIODevice* device) override;
|
||||
|
||||
/** Implementation of the AudioIODeviceCallback method. */
|
||||
void audioDeviceStopped() override;
|
||||
|
||||
/** An alternative method for initialising the source without an AudioIODevice. */
|
||||
void prepareToPlay (double sampleRate, int blockSize);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
CriticalSection readLock;
|
||||
AudioSource* source = nullptr;
|
||||
double sampleRate = 0;
|
||||
int bufferSize = 0;
|
||||
float* channels[128];
|
||||
float* outputChans[128];
|
||||
const float* inputChans[128];
|
||||
AudioBuffer<float> tempBuffer;
|
||||
float lastGain = 1.0f;
|
||||
std::atomic<float> gain { 1.0f };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSourcePlayer)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,326 +1,283 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
AudioTransportSource::AudioTransportSource()
|
||||
{
|
||||
}
|
||||
|
||||
AudioTransportSource::~AudioTransportSource()
|
||||
{
|
||||
setSource (nullptr);
|
||||
releaseMasterResources();
|
||||
}
|
||||
|
||||
void AudioTransportSource::setSource (PositionableAudioSource* const newSource,
|
||||
int readAheadSize, TimeSliceThread* readAheadThread,
|
||||
double sourceSampleRateToCorrectFor, int maxNumChannels)
|
||||
{
|
||||
if (source == newSource)
|
||||
{
|
||||
if (source == nullptr)
|
||||
return;
|
||||
|
||||
setSource (nullptr, 0, nullptr); // deselect and reselect to avoid releasing resources wrongly
|
||||
}
|
||||
|
||||
readAheadBufferSize = readAheadSize;
|
||||
sourceSampleRate = sourceSampleRateToCorrectFor;
|
||||
|
||||
ResamplingAudioSource* newResamplerSource = nullptr;
|
||||
BufferingAudioSource* newBufferingSource = nullptr;
|
||||
PositionableAudioSource* newPositionableSource = nullptr;
|
||||
AudioSource* newMasterSource = nullptr;
|
||||
|
||||
std::unique_ptr<ResamplingAudioSource> oldResamplerSource (resamplerSource);
|
||||
std::unique_ptr<BufferingAudioSource> oldBufferingSource (bufferingSource);
|
||||
AudioSource* oldMasterSource = masterSource;
|
||||
|
||||
if (newSource != nullptr)
|
||||
{
|
||||
newPositionableSource = newSource;
|
||||
|
||||
if (readAheadSize > 0)
|
||||
{
|
||||
// If you want to use a read-ahead buffer, you must also provide a TimeSliceThread
|
||||
// for it to use!
|
||||
jassert (readAheadThread != nullptr);
|
||||
|
||||
newPositionableSource = newBufferingSource
|
||||
= new BufferingAudioSource (newPositionableSource, *readAheadThread,
|
||||
false, readAheadSize, maxNumChannels);
|
||||
}
|
||||
|
||||
newPositionableSource->setNextReadPosition (0);
|
||||
|
||||
if (sourceSampleRateToCorrectFor > 0)
|
||||
newMasterSource = newResamplerSource
|
||||
= new ResamplingAudioSource (newPositionableSource, false, maxNumChannels);
|
||||
else
|
||||
newMasterSource = newPositionableSource;
|
||||
|
||||
if (isPrepared)
|
||||
{
|
||||
if (newResamplerSource != nullptr && sourceSampleRate > 0 && sampleRate > 0)
|
||||
newResamplerSource->setResamplingRatio (sourceSampleRate / sampleRate);
|
||||
|
||||
newMasterSource->prepareToPlay (blockSize, sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
source = newSource;
|
||||
resamplerSource = newResamplerSource;
|
||||
bufferingSource = newBufferingSource;
|
||||
masterSource = newMasterSource;
|
||||
positionableSource = newPositionableSource;
|
||||
|
||||
inputStreamEOF = false;
|
||||
playing = false;
|
||||
}
|
||||
|
||||
if (oldMasterSource != nullptr)
|
||||
oldMasterSource->releaseResources();
|
||||
}
|
||||
|
||||
void AudioTransportSource::start()
|
||||
{
|
||||
if ((! playing) && masterSource != nullptr)
|
||||
{
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
playing = true;
|
||||
stopped = false;
|
||||
inputStreamEOF = false;
|
||||
}
|
||||
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioTransportSource::stop()
|
||||
{
|
||||
if (playing)
|
||||
{
|
||||
playing = false;
|
||||
|
||||
int n = 500;
|
||||
while (--n >= 0 && ! stopped)
|
||||
Thread::sleep (2);
|
||||
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioTransportSource::setPosition (double newPosition)
|
||||
{
|
||||
if (sampleRate > 0.0)
|
||||
setNextReadPosition ((int64) (newPosition * sampleRate));
|
||||
}
|
||||
|
||||
double AudioTransportSource::getCurrentPosition() const
|
||||
{
|
||||
if (sampleRate > 0.0)
|
||||
return (double) getNextReadPosition() / sampleRate;
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double AudioTransportSource::getLengthInSeconds() const
|
||||
{
|
||||
if (sampleRate > 0.0)
|
||||
return (double) getTotalLength() / sampleRate;
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void AudioTransportSource::setNextReadPosition (int64 newPosition)
|
||||
{
|
||||
if (positionableSource != nullptr)
|
||||
{
|
||||
if (sampleRate > 0 && sourceSampleRate > 0)
|
||||
newPosition = (int64) ((double) newPosition * sourceSampleRate / sampleRate);
|
||||
|
||||
positionableSource->setNextReadPosition (newPosition);
|
||||
|
||||
if (resamplerSource != nullptr)
|
||||
resamplerSource->flushBuffers();
|
||||
|
||||
inputStreamEOF = false;
|
||||
}
|
||||
}
|
||||
|
||||
int64 AudioTransportSource::getNextReadPosition() const
|
||||
{
|
||||
if (positionableSource != nullptr)
|
||||
{
|
||||
const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0;
|
||||
return (int64) ((double) positionableSource->getNextReadPosition() * ratio);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64 AudioTransportSource::getTotalLength() const
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
if (positionableSource != nullptr)
|
||||
{
|
||||
const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0;
|
||||
return (int64) ((double) positionableSource->getTotalLength() * ratio);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AudioTransportSource::isLooping() const
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
return positionableSource != nullptr && positionableSource->isLooping();
|
||||
}
|
||||
|
||||
void AudioTransportSource::setLooping(bool shouldLoop)
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
if (positionableSource != nullptr) {
|
||||
positionableSource->setLooping(shouldLoop);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AudioTransportSource::getLoopRange (int64 & loopStart, int64 & loopLength) const
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
if (positionableSource != nullptr) {
|
||||
const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0;
|
||||
|
||||
positionableSource->getLoopRange(loopStart, loopLength);
|
||||
loopStart = (int64) (loopStart * ratio);
|
||||
loopLength = (int64) (loopLength * ratio);
|
||||
}
|
||||
else {
|
||||
loopStart = 0;
|
||||
loopLength = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioTransportSource::setLoopRange (int64 loopStart, int64 loopLength)
|
||||
{
|
||||
if (positionableSource != nullptr)
|
||||
{
|
||||
if (sampleRate > 0 && sourceSampleRate > 0) {
|
||||
loopStart = (int64) (loopStart * sourceSampleRate / sampleRate);
|
||||
loopLength = (int64) (loopLength * sourceSampleRate / sampleRate);
|
||||
}
|
||||
|
||||
positionableSource->setLoopRange(loopStart, loopLength);
|
||||
|
||||
if (resamplerSource != nullptr)
|
||||
resamplerSource->flushBuffers();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AudioTransportSource::setGain (const float newGain) noexcept
|
||||
{
|
||||
gain = newGain;
|
||||
}
|
||||
|
||||
void AudioTransportSource::prepareToPlay (int samplesPerBlockExpected, double newSampleRate)
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
sampleRate = newSampleRate;
|
||||
blockSize = samplesPerBlockExpected;
|
||||
|
||||
if (masterSource != nullptr)
|
||||
masterSource->prepareToPlay (samplesPerBlockExpected, sampleRate);
|
||||
|
||||
if (resamplerSource != nullptr && sourceSampleRate > 0)
|
||||
resamplerSource->setResamplingRatio (sourceSampleRate / sampleRate);
|
||||
|
||||
inputStreamEOF = false;
|
||||
isPrepared = true;
|
||||
}
|
||||
|
||||
void AudioTransportSource::releaseMasterResources()
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
if (masterSource != nullptr)
|
||||
masterSource->releaseResources();
|
||||
|
||||
isPrepared = false;
|
||||
}
|
||||
|
||||
void AudioTransportSource::releaseResources()
|
||||
{
|
||||
releaseMasterResources();
|
||||
}
|
||||
|
||||
void AudioTransportSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
if (masterSource != nullptr && ! stopped)
|
||||
{
|
||||
masterSource->getNextAudioBlock (info);
|
||||
|
||||
if (! playing)
|
||||
{
|
||||
// just stopped playing, so fade out the last block..
|
||||
for (int i = info.buffer->getNumChannels(); --i >= 0;)
|
||||
info.buffer->applyGainRamp (i, info.startSample, jmin (256, info.numSamples), 1.0f, 0.0f);
|
||||
|
||||
if (info.numSamples > 256)
|
||||
info.buffer->clear (info.startSample + 256, info.numSamples - 256);
|
||||
}
|
||||
|
||||
if (positionableSource->getNextReadPosition() > positionableSource->getTotalLength() + 1
|
||||
&& ! positionableSource->isLooping())
|
||||
{
|
||||
playing = false;
|
||||
inputStreamEOF = true;
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
stopped = ! playing;
|
||||
|
||||
for (int i = info.buffer->getNumChannels(); --i >= 0;)
|
||||
info.buffer->applyGainRamp (i, info.startSample, info.numSamples, lastGain, gain);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.clearActiveBufferRegion();
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
lastGain = gain;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
AudioTransportSource::AudioTransportSource()
|
||||
{
|
||||
}
|
||||
|
||||
AudioTransportSource::~AudioTransportSource()
|
||||
{
|
||||
setSource (nullptr);
|
||||
releaseMasterResources();
|
||||
}
|
||||
|
||||
void AudioTransportSource::setSource (PositionableAudioSource* const newSource,
|
||||
int readAheadSize, TimeSliceThread* readAheadThread,
|
||||
double sourceSampleRateToCorrectFor, int maxNumChannels)
|
||||
{
|
||||
if (source == newSource)
|
||||
{
|
||||
if (source == nullptr)
|
||||
return;
|
||||
|
||||
setSource (nullptr, 0, nullptr); // deselect and reselect to avoid releasing resources wrongly
|
||||
}
|
||||
|
||||
ResamplingAudioSource* newResamplerSource = nullptr;
|
||||
BufferingAudioSource* newBufferingSource = nullptr;
|
||||
PositionableAudioSource* newPositionableSource = nullptr;
|
||||
AudioSource* newMasterSource = nullptr;
|
||||
|
||||
std::unique_ptr<ResamplingAudioSource> oldResamplerSource (resamplerSource);
|
||||
std::unique_ptr<BufferingAudioSource> oldBufferingSource (bufferingSource);
|
||||
AudioSource* oldMasterSource = masterSource;
|
||||
|
||||
if (newSource != nullptr)
|
||||
{
|
||||
newPositionableSource = newSource;
|
||||
|
||||
if (readAheadSize > 0)
|
||||
{
|
||||
// If you want to use a read-ahead buffer, you must also provide a TimeSliceThread
|
||||
// for it to use!
|
||||
jassert (readAheadThread != nullptr);
|
||||
|
||||
newPositionableSource = newBufferingSource
|
||||
= new BufferingAudioSource (newPositionableSource, *readAheadThread,
|
||||
false, readAheadSize, maxNumChannels);
|
||||
}
|
||||
|
||||
newPositionableSource->setNextReadPosition (0);
|
||||
|
||||
if (sourceSampleRateToCorrectFor > 0)
|
||||
newMasterSource = newResamplerSource
|
||||
= new ResamplingAudioSource (newPositionableSource, false, maxNumChannels);
|
||||
else
|
||||
newMasterSource = newPositionableSource;
|
||||
|
||||
if (isPrepared)
|
||||
{
|
||||
if (newResamplerSource != nullptr && sourceSampleRateToCorrectFor > 0 && sampleRate > 0)
|
||||
newResamplerSource->setResamplingRatio (sourceSampleRateToCorrectFor / sampleRate);
|
||||
|
||||
newMasterSource->prepareToPlay (blockSize, sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
source = newSource;
|
||||
resamplerSource = newResamplerSource;
|
||||
bufferingSource = newBufferingSource;
|
||||
masterSource = newMasterSource;
|
||||
positionableSource = newPositionableSource;
|
||||
readAheadBufferSize = readAheadSize;
|
||||
sourceSampleRate = sourceSampleRateToCorrectFor;
|
||||
|
||||
playing = false;
|
||||
}
|
||||
|
||||
if (oldMasterSource != nullptr)
|
||||
oldMasterSource->releaseResources();
|
||||
}
|
||||
|
||||
void AudioTransportSource::start()
|
||||
{
|
||||
if ((! playing) && masterSource != nullptr)
|
||||
{
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
playing = true;
|
||||
stopped = false;
|
||||
}
|
||||
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioTransportSource::stop()
|
||||
{
|
||||
if (playing)
|
||||
{
|
||||
playing = false;
|
||||
|
||||
int n = 500;
|
||||
while (--n >= 0 && ! stopped)
|
||||
Thread::sleep (2);
|
||||
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioTransportSource::setPosition (double newPosition)
|
||||
{
|
||||
if (sampleRate > 0.0)
|
||||
setNextReadPosition ((int64) (newPosition * sampleRate));
|
||||
}
|
||||
|
||||
double AudioTransportSource::getCurrentPosition() const
|
||||
{
|
||||
if (sampleRate > 0.0)
|
||||
return (double) getNextReadPosition() / sampleRate;
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double AudioTransportSource::getLengthInSeconds() const
|
||||
{
|
||||
if (sampleRate > 0.0)
|
||||
return (double) getTotalLength() / sampleRate;
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
bool AudioTransportSource::hasStreamFinished() const noexcept
|
||||
{
|
||||
return positionableSource->getNextReadPosition() > positionableSource->getTotalLength() + 1
|
||||
&& ! positionableSource->isLooping();
|
||||
}
|
||||
|
||||
void AudioTransportSource::setNextReadPosition (int64 newPosition)
|
||||
{
|
||||
if (positionableSource != nullptr)
|
||||
{
|
||||
if (sampleRate > 0 && sourceSampleRate > 0)
|
||||
newPosition = (int64) ((double) newPosition * sourceSampleRate / sampleRate);
|
||||
|
||||
positionableSource->setNextReadPosition (newPosition);
|
||||
|
||||
if (resamplerSource != nullptr)
|
||||
resamplerSource->flushBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
int64 AudioTransportSource::getNextReadPosition() const
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
if (positionableSource != nullptr)
|
||||
{
|
||||
const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0;
|
||||
return (int64) ((double) positionableSource->getNextReadPosition() * ratio);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64 AudioTransportSource::getTotalLength() const
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
if (positionableSource != nullptr)
|
||||
{
|
||||
const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0;
|
||||
return (int64) ((double) positionableSource->getTotalLength() * ratio);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AudioTransportSource::isLooping() const
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
return positionableSource != nullptr && positionableSource->isLooping();
|
||||
}
|
||||
|
||||
void AudioTransportSource::setGain (const float newGain) noexcept
|
||||
{
|
||||
gain = newGain;
|
||||
}
|
||||
|
||||
void AudioTransportSource::prepareToPlay (int samplesPerBlockExpected, double newSampleRate)
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
sampleRate = newSampleRate;
|
||||
blockSize = samplesPerBlockExpected;
|
||||
|
||||
if (masterSource != nullptr)
|
||||
masterSource->prepareToPlay (samplesPerBlockExpected, sampleRate);
|
||||
|
||||
if (resamplerSource != nullptr && sourceSampleRate > 0)
|
||||
resamplerSource->setResamplingRatio (sourceSampleRate / sampleRate);
|
||||
|
||||
isPrepared = true;
|
||||
}
|
||||
|
||||
void AudioTransportSource::releaseMasterResources()
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
if (masterSource != nullptr)
|
||||
masterSource->releaseResources();
|
||||
|
||||
isPrepared = false;
|
||||
}
|
||||
|
||||
void AudioTransportSource::releaseResources()
|
||||
{
|
||||
releaseMasterResources();
|
||||
}
|
||||
|
||||
void AudioTransportSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
|
||||
{
|
||||
const ScopedLock sl (callbackLock);
|
||||
|
||||
if (masterSource != nullptr && ! stopped)
|
||||
{
|
||||
masterSource->getNextAudioBlock (info);
|
||||
|
||||
if (! playing)
|
||||
{
|
||||
// just stopped playing, so fade out the last block..
|
||||
for (int i = info.buffer->getNumChannels(); --i >= 0;)
|
||||
info.buffer->applyGainRamp (i, info.startSample, jmin (256, info.numSamples), 1.0f, 0.0f);
|
||||
|
||||
if (info.numSamples > 256)
|
||||
info.buffer->clear (info.startSample + 256, info.numSamples - 256);
|
||||
}
|
||||
|
||||
if (hasStreamFinished())
|
||||
{
|
||||
playing = false;
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
stopped = ! playing;
|
||||
|
||||
for (int i = info.buffer->getNumChannels(); --i >= 0;)
|
||||
info.buffer->applyGainRamp (i, info.startSample, info.numSamples, lastGain, gain);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.clearActiveBufferRegion();
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
lastGain = gain;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,190 +1,180 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - 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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An AudioSource that takes a PositionableAudioSource and allows it to be
|
||||
played, stopped, started, etc.
|
||||
|
||||
This can also be told use a buffer and background thread to read ahead, and
|
||||
if can correct for different sample-rates.
|
||||
|
||||
You may want to use one of these along with an AudioSourcePlayer and AudioIODevice
|
||||
to control playback of an audio file.
|
||||
|
||||
@see AudioSource, AudioSourcePlayer
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API AudioTransportSource : public PositionableAudioSource,
|
||||
public ChangeBroadcaster
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an AudioTransportSource.
|
||||
After creating one of these, use the setSource() method to select an input source.
|
||||
*/
|
||||
AudioTransportSource();
|
||||
|
||||
/** Destructor. */
|
||||
~AudioTransportSource() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the reader that is being used as the input source.
|
||||
|
||||
This will stop playback, reset the position to 0 and change to the new reader.
|
||||
|
||||
The source passed in will not be deleted by this object, so must be managed by
|
||||
the caller.
|
||||
|
||||
@param newSource the new input source to use. This may be a nullptr
|
||||
@param readAheadBufferSize a size of buffer to use for reading ahead. If this
|
||||
is zero, no reading ahead will be done; if it's
|
||||
greater than zero, a BufferingAudioSource will be used
|
||||
to do the reading-ahead. If you set a non-zero value here,
|
||||
you'll also need to set the readAheadThread parameter.
|
||||
@param readAheadThread if you set readAheadBufferSize to a non-zero value, then
|
||||
you'll also need to supply this TimeSliceThread object for
|
||||
the background reader to use. The thread object must not be
|
||||
deleted while the AudioTransport source is still using it.
|
||||
@param sourceSampleRateToCorrectFor if this is non-zero, it specifies the sample
|
||||
rate of the source, and playback will be sample-rate
|
||||
adjusted to maintain playback at the correct pitch. If
|
||||
this is 0, no sample-rate adjustment will be performed
|
||||
@param maxNumChannels the maximum number of channels that may need to be played
|
||||
*/
|
||||
void setSource (PositionableAudioSource* newSource,
|
||||
int readAheadBufferSize = 0,
|
||||
TimeSliceThread* readAheadThread = nullptr,
|
||||
double sourceSampleRateToCorrectFor = 0.0,
|
||||
int maxNumChannels = 2);
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the current playback position in the source stream.
|
||||
|
||||
The next time the getNextAudioBlock() method is called, this
|
||||
is the time from which it'll read data.
|
||||
|
||||
@param newPosition the new playback position in seconds
|
||||
|
||||
@see getCurrentPosition
|
||||
*/
|
||||
void setPosition (double newPosition);
|
||||
|
||||
/** Returns the position that the next data block will be read from
|
||||
This is a time in seconds.
|
||||
*/
|
||||
double getCurrentPosition() const;
|
||||
|
||||
/** Returns the stream's length in seconds. */
|
||||
double getLengthInSeconds() const;
|
||||
|
||||
/** Returns true if the player has stopped because its input stream ran out of data. */
|
||||
bool hasStreamFinished() const noexcept { return inputStreamEOF; }
|
||||
|
||||
//==============================================================================
|
||||
/** Starts playing (if a source has been selected).
|
||||
|
||||
If it starts playing, this will send a message to any ChangeListeners
|
||||
that are registered with this object.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/** Stops playing.
|
||||
|
||||
If it's actually playing, this will send a message to any ChangeListeners
|
||||
that are registered with this object.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/** Returns true if it's currently playing. */
|
||||
bool isPlaying() const noexcept { return playing; }
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the gain to apply to the output.
|
||||
@param newGain a factor by which to multiply the outgoing samples,
|
||||
so 1.0 = 0dB, 0.5 = -6dB, 2.0 = 6dB, etc.
|
||||
*/
|
||||
void setGain (float newGain) noexcept;
|
||||
|
||||
/** Returns the current gain setting.
|
||||
@see setGain
|
||||
*/
|
||||
float getGain() const noexcept { return gain; }
|
||||
|
||||
//==============================================================================
|
||||
/** Implementation of the AudioSource method. */
|
||||
void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
|
||||
|
||||
/** Implementation of the AudioSource method. */
|
||||
void releaseResources() override;
|
||||
|
||||
/** Implementation of the AudioSource method. */
|
||||
void getNextAudioBlock (const AudioSourceChannelInfo&) override;
|
||||
|
||||
//==============================================================================
|
||||
/** Implements the PositionableAudioSource method. */
|
||||
void setNextReadPosition (int64 newPosition) override;
|
||||
|
||||
/** Implements the PositionableAudioSource method. */
|
||||
int64 getNextReadPosition() const override;
|
||||
|
||||
/** Implements the PositionableAudioSource method. */
|
||||
int64 getTotalLength() const override;
|
||||
|
||||
/** Implements the PositionableAudioSource method. */
|
||||
bool isLooping() const override;
|
||||
|
||||
/** Implements the PositionableAudioSource method. */
|
||||
void setLooping(bool shouldLoop) override;
|
||||
|
||||
/** Implements the PositionableAudioSource method. */
|
||||
void setLoopRange (int64 loopStart, int64 loopLength) override;
|
||||
|
||||
/** Implements the PositionableAudioSource method. */
|
||||
void getLoopRange (int64 & loopStart, int64 & loopLength) const override;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
PositionableAudioSource* source = nullptr;
|
||||
ResamplingAudioSource* resamplerSource = nullptr;
|
||||
BufferingAudioSource* bufferingSource = nullptr;
|
||||
PositionableAudioSource* positionableSource = nullptr;
|
||||
AudioSource* masterSource = nullptr;
|
||||
|
||||
CriticalSection callbackLock;
|
||||
float gain = 1.0f, lastGain = 1.0f;
|
||||
std::atomic<bool> playing { false }, stopped { true };
|
||||
double sampleRate = 44100.0, sourceSampleRate = 0;
|
||||
int blockSize = 128, readAheadBufferSize = 0;
|
||||
bool isPrepared = false, inputStreamEOF = false;
|
||||
|
||||
void releaseMasterResources();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioTransportSource)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An AudioSource that takes a PositionableAudioSource and allows it to be
|
||||
played, stopped, started, etc.
|
||||
|
||||
This can also be told use a buffer and background thread to read ahead, and
|
||||
if can correct for different sample-rates.
|
||||
|
||||
You may want to use one of these along with an AudioSourcePlayer and AudioIODevice
|
||||
to control playback of an audio file.
|
||||
|
||||
@see AudioSource, AudioSourcePlayer
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API AudioTransportSource : public PositionableAudioSource,
|
||||
public ChangeBroadcaster
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an AudioTransportSource.
|
||||
After creating one of these, use the setSource() method to select an input source.
|
||||
*/
|
||||
AudioTransportSource();
|
||||
|
||||
/** Destructor. */
|
||||
~AudioTransportSource() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the reader that is being used as the input source.
|
||||
|
||||
This will stop playback, reset the position to 0 and change to the new reader.
|
||||
|
||||
The source passed in will not be deleted by this object, so must be managed by
|
||||
the caller.
|
||||
|
||||
@param newSource the new input source to use. This may be a nullptr
|
||||
@param readAheadBufferSize a size of buffer to use for reading ahead. If this
|
||||
is zero, no reading ahead will be done; if it's
|
||||
greater than zero, a BufferingAudioSource will be used
|
||||
to do the reading-ahead. If you set a non-zero value here,
|
||||
you'll also need to set the readAheadThread parameter.
|
||||
@param readAheadThread if you set readAheadBufferSize to a non-zero value, then
|
||||
you'll also need to supply this TimeSliceThread object for
|
||||
the background reader to use. The thread object must not be
|
||||
deleted while the AudioTransport source is still using it.
|
||||
@param sourceSampleRateToCorrectFor if this is non-zero, it specifies the sample
|
||||
rate of the source, and playback will be sample-rate
|
||||
adjusted to maintain playback at the correct pitch. If
|
||||
this is 0, no sample-rate adjustment will be performed
|
||||
@param maxNumChannels the maximum number of channels that may need to be played
|
||||
*/
|
||||
void setSource (PositionableAudioSource* newSource,
|
||||
int readAheadBufferSize = 0,
|
||||
TimeSliceThread* readAheadThread = nullptr,
|
||||
double sourceSampleRateToCorrectFor = 0.0,
|
||||
int maxNumChannels = 2);
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the current playback position in the source stream.
|
||||
|
||||
The next time the getNextAudioBlock() method is called, this
|
||||
is the time from which it'll read data.
|
||||
|
||||
@param newPosition the new playback position in seconds
|
||||
|
||||
@see getCurrentPosition
|
||||
*/
|
||||
void setPosition (double newPosition);
|
||||
|
||||
/** Returns the position that the next data block will be read from
|
||||
This is a time in seconds.
|
||||
*/
|
||||
double getCurrentPosition() const;
|
||||
|
||||
/** Returns the stream's length in seconds. */
|
||||
double getLengthInSeconds() const;
|
||||
|
||||
/** Returns true if the player has stopped because its input stream ran out of data. */
|
||||
bool hasStreamFinished() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Starts playing (if a source has been selected).
|
||||
|
||||
If it starts playing, this will send a message to any ChangeListeners
|
||||
that are registered with this object.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/** Stops playing.
|
||||
|
||||
If it's actually playing, this will send a message to any ChangeListeners
|
||||
that are registered with this object.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/** Returns true if it's currently playing. */
|
||||
bool isPlaying() const noexcept { return playing; }
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the gain to apply to the output.
|
||||
@param newGain a factor by which to multiply the outgoing samples,
|
||||
so 1.0 = 0dB, 0.5 = -6dB, 2.0 = 6dB, etc.
|
||||
*/
|
||||
void setGain (float newGain) noexcept;
|
||||
|
||||
/** Returns the current gain setting.
|
||||
@see setGain
|
||||
*/
|
||||
float getGain() const noexcept { return gain; }
|
||||
|
||||
//==============================================================================
|
||||
/** Implementation of the AudioSource method. */
|
||||
void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
|
||||
|
||||
/** Implementation of the AudioSource method. */
|
||||
void releaseResources() override;
|
||||
|
||||
/** Implementation of the AudioSource method. */
|
||||
void getNextAudioBlock (const AudioSourceChannelInfo&) override;
|
||||
|
||||
//==============================================================================
|
||||
/** Implements the PositionableAudioSource method. */
|
||||
void setNextReadPosition (int64 newPosition) override;
|
||||
|
||||
/** Implements the PositionableAudioSource method. */
|
||||
int64 getNextReadPosition() const override;
|
||||
|
||||
/** Implements the PositionableAudioSource method. */
|
||||
int64 getTotalLength() const override;
|
||||
|
||||
/** Implements the PositionableAudioSource method. */
|
||||
bool isLooping() const override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
PositionableAudioSource* source = nullptr;
|
||||
ResamplingAudioSource* resamplerSource = nullptr;
|
||||
BufferingAudioSource* bufferingSource = nullptr;
|
||||
PositionableAudioSource* positionableSource = nullptr;
|
||||
AudioSource* masterSource = nullptr;
|
||||
|
||||
CriticalSection callbackLock;
|
||||
float gain = 1.0f, lastGain = 1.0f;
|
||||
std::atomic<bool> playing { false }, stopped { true };
|
||||
double sampleRate = 44100.0, sourceSampleRate = 0;
|
||||
int blockSize = 128, readAheadBufferSize = 0;
|
||||
bool isPrepared = false;
|
||||
|
||||
void releaseMasterResources();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioTransportSource)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
Reference in New Issue
Block a user