@@ -5,7 +5,8 @@ package = make_library_project("drowaudio") | |||
package.includepaths = { | |||
".", | |||
"../../juce/source" | |||
"../../juce/source", | |||
"../../juce/source/modules" | |||
} | |||
package.files = { | |||
@@ -16,13 +16,13 @@ | |||
#include "modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp" | |||
#elif JucePlugin_Build_VST | |||
// we need to include 'juce_VSTMidiEventList' before 'juce_VST_Wrapper' | |||
#ifndef _MSC_VER | |||
#define __cdecl | |||
#endif | |||
#include "pluginterfaces/vst2.x/aeffectx.h" | |||
namespace juce { | |||
#include "modules/juce_audio_processors/format_types/juce_VSTMidiEventList.h" | |||
} | |||
// #ifndef _MSC_VER | |||
// #define __cdecl | |||
// #endif | |||
// #include "pluginterfaces/vst2.x/aeffectx.h" | |||
// namespace juce { | |||
// #include "modules/juce_audio_processors/format_types/juce_VSTMidiEventList.h" | |||
// } | |||
#ifdef JUCE_MAC | |||
#include "modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.mm" | |||
#else | |||
@@ -37,7 +37,3 @@ | |||
#if ! JucePlugin_Build_Standalone | |||
#include "modules/juce_audio_plugin_client/utility/juce_PluginUtilities.cpp" | |||
#endif | |||
namespace juce { | |||
#include "modules/juce_audio_processors/processors/juce_AudioProcessor_export.cpp" | |||
} |
@@ -28,6 +28,7 @@ | |||
//============================================================================= | |||
#define JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED 1 | |||
#define JUCE_STANDALONE_APPLICATION 0 | |||
//============================================================================= | |||
@@ -96,14 +97,14 @@ | |||
If your app doesn't need to read FLAC files, you might want to disable this to | |||
reduce the size of your codebase and build time. | |||
*/ | |||
#define JUCE_USE_FLAC 0 | |||
#define JUCE_USE_FLAC 1 | |||
/** Config: JUCE_USE_OGGVORBIS | |||
Enables the Ogg-Vorbis audio codec classes (available on all platforms). | |||
If your app doesn't need to read Ogg-Vorbis files, you might want to disable this to | |||
reduce the size of your codebase and build time. | |||
*/ | |||
#define JUCE_USE_OGGVORBIS 0 | |||
#define JUCE_USE_OGGVORBIS 1 | |||
/** Config: JUCE_USE_MP3AUDIOFORMAT | |||
Enables the software-based MP3AudioFormat class. | |||
@@ -6,6 +6,7 @@ package = make_library_project("juce") | |||
package.includepaths = { | |||
".", | |||
"../source", | |||
"../source/modules", | |||
"../../../sdks/vstsdk2.4/" | |||
} | |||
@@ -1,17 +1,17 @@ | |||
# The JUCE Library | |||
JUCE (Jules' Utility Class Extensions) is an all-encompassing | |||
C++ framework for developing cross-platform software. | |||
It contains pretty much everything you're likely to need to create | |||
most applications, and is particularly well-suited for building | |||
highly-customised GUIs, and for handling graphics and sound. | |||
Most JUCE modules are shared under the GNU Public Licence | |||
(GPLv2, v3, and the AGPLv3). This means that the code can | |||
be freely copied and distributed, and costs nothing to use | |||
in other GPL applications. One module (the juce_core module) | |||
is permissively licensed under the ISC. | |||
For more information, visit the website: | |||
# The JUCE Library | |||
JUCE (Jules' Utility Class Extensions) is an all-encompassing | |||
C++ framework for developing cross-platform software. | |||
It contains pretty much everything you're likely to need to create | |||
most applications, and is particularly well-suited for building | |||
highly-customised GUIs, and for handling graphics and sound. | |||
Most JUCE modules are shared under the GNU Public Licence | |||
(GPLv2, v3, and the AGPLv3). This means that the code can | |||
be freely copied and distributed, and costs nothing to use | |||
in other GPL applications. One module (the juce_core module) | |||
is permissively licensed under the ISC. | |||
For more information, visit the website: | |||
http://www.juce.com |
@@ -2060,6 +2060,7 @@ PREDEFINED = WIN32=1 \ | |||
JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS=1 \ | |||
JUCE_COMPILER_SUPPORTS_VARIADIC_TEMPLATES=1 \ | |||
JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL=1 \ | |||
JUCE_COMPILER_SUPPORTS_LAMBDAS=1 \ | |||
JUCE_MODAL_LOOPS_PERMITTED=1 | |||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this | |||
@@ -0,0 +1,336 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
AudioChannelSet::AudioChannelSet (uint32 c) : channels (c) {} | |||
bool AudioChannelSet::operator== (const AudioChannelSet& other) const noexcept { return channels == other.channels; } | |||
bool AudioChannelSet::operator!= (const AudioChannelSet& other) const noexcept { return channels != other.channels; } | |||
bool AudioChannelSet::operator< (const AudioChannelSet& other) const noexcept { return channels < other.channels; } | |||
String AudioChannelSet::getChannelTypeName (AudioChannelSet::ChannelType type) | |||
{ | |||
if (type >= discreteChannel0) | |||
return String ("Discrete ") + String (type - discreteChannel0 + 1); | |||
switch (type) | |||
{ | |||
case left: return NEEDS_TRANS("Left"); | |||
case right: return NEEDS_TRANS("Right"); | |||
case centre: return NEEDS_TRANS("Centre"); | |||
case LFE: return NEEDS_TRANS("LFE"); | |||
case leftSurround: return NEEDS_TRANS("Left Surround"); | |||
case rightSurround: return NEEDS_TRANS("Right Surround"); | |||
case leftCentre: return NEEDS_TRANS("Left Centre"); | |||
case rightCentre: return NEEDS_TRANS("Right Centre"); | |||
case centreSurround: return NEEDS_TRANS("Centre Surround"); | |||
case leftSurroundRear: return NEEDS_TRANS("Left Surround Rear"); | |||
case rightSurroundRear: return NEEDS_TRANS("Right Surround Rear"); | |||
case topMiddle: return NEEDS_TRANS("Top Middle"); | |||
case topFrontLeft: return NEEDS_TRANS("Top Front Left"); | |||
case topFrontCentre: return NEEDS_TRANS("Top Front Centre"); | |||
case topFrontRight: return NEEDS_TRANS("Top Front Right"); | |||
case topRearLeft: return NEEDS_TRANS("Top Rear Left"); | |||
case topRearCentre: return NEEDS_TRANS("Top Rear Centre"); | |||
case topRearRight: return NEEDS_TRANS("Top Rear Right"); | |||
case wideLeft: return NEEDS_TRANS("Wide Left"); | |||
case wideRight: return NEEDS_TRANS("Wide Right"); | |||
case LFE2: return NEEDS_TRANS("LFE 2"); | |||
case leftSurroundSide: return NEEDS_TRANS ("Left Surround Side"); | |||
case rightSurroundSide: return NEEDS_TRANS ("Right Surround Side"); | |||
case ambisonicW: return NEEDS_TRANS("Ambisonic W"); | |||
case ambisonicX: return NEEDS_TRANS("Ambisonic X"); | |||
case ambisonicY: return NEEDS_TRANS("Ambisonic Y"); | |||
case ambisonicZ: return NEEDS_TRANS("Ambisonic Z"); | |||
default: break; | |||
} | |||
return "Unknown"; | |||
} | |||
String AudioChannelSet::getAbbreviatedChannelTypeName (AudioChannelSet::ChannelType type) | |||
{ | |||
if (type >= discreteChannel0) | |||
return String (type - discreteChannel0 + 1); | |||
switch (type) | |||
{ | |||
case left: return "L"; | |||
case right: return "R"; | |||
case centre: return "C"; | |||
case LFE: return "Lfe"; | |||
case leftSurround: return "Ls"; | |||
case rightSurround: return "Rs"; | |||
case leftCentre: return "Lc"; | |||
case rightCentre: return "Rc"; | |||
case centreSurround: return "Cs"; | |||
case leftSurroundRear: return "Lrs"; | |||
case rightSurroundRear: return "Rrs"; | |||
case topMiddle: return "Tm"; | |||
case topFrontLeft: return "Tfl"; | |||
case topFrontCentre: return "Tfc"; | |||
case topFrontRight: return "Tfr"; | |||
case topRearLeft: return "Trl"; | |||
case topRearCentre: return "Trc"; | |||
case topRearRight: return "Trr"; | |||
case wideLeft: return "Wl"; | |||
case wideRight: return "Wr"; | |||
case LFE2: return "Lfe2"; | |||
case leftSurroundSide: return "Lss"; | |||
case rightSurroundSide: return "Rss"; | |||
case ambisonicW: return "W"; | |||
case ambisonicX: return "X"; | |||
case ambisonicY: return "Y"; | |||
case ambisonicZ: return "Z"; | |||
default: break; | |||
} | |||
return ""; | |||
} | |||
String AudioChannelSet::getSpeakerArrangementAsString() const | |||
{ | |||
StringArray speakerTypes; | |||
Array<AudioChannelSet::ChannelType> speakers = getChannelTypes(); | |||
for (int i = 0; i < speakers.size(); ++i) | |||
{ | |||
String name = getAbbreviatedChannelTypeName (speakers.getReference (i)); | |||
if (name.isNotEmpty()) | |||
speakerTypes.add (name); | |||
} | |||
return speakerTypes.joinIntoString (" "); | |||
} | |||
String AudioChannelSet::getDescription() const | |||
{ | |||
if (isDiscreteLayout()) return String ("Discrete #") + String (size()); | |||
if (*this == disabled()) return "Disabled"; | |||
if (*this == mono()) return "Mono"; | |||
if (*this == stereo()) return "Stereo"; | |||
if (*this == createLCR()) return "LCR"; | |||
if (*this == createLRS()) return "LRS"; | |||
if (*this == createLCRS()) return "LCRS"; | |||
if (*this == create5point0()) return "5.1 Surround"; | |||
if (*this == create5point1()) return "5.1 Surround (+Lfe)"; | |||
if (*this == create6point0()) return "6.1 Surround"; | |||
if (*this == create6point1()) return "6.1 Surround (+Lfe)"; | |||
if (*this == create6point0Music()) return "6.1 (Music) Surround"; | |||
if (*this == create6point1Music()) return "6.1 (Music) Surround (+Lfe)"; | |||
if (*this == create7point0()) return "7.1 Surround"; | |||
if (*this == create7point1()) return "7.1 Surround (Lfe)"; | |||
if (*this == create7point0SDDS()) return "7.1 Surround SDDS"; | |||
if (*this == create7point1SDDS()) return "7.1 Surround SDDS (+Lfe)"; | |||
if (*this == quadraphonic()) return "Quadraphonic"; | |||
if (*this == pentagonal()) return "Pentagonal"; | |||
if (*this == hexagonal()) return "Hexagonal"; | |||
if (*this == octagonal()) return "Octagonal"; | |||
if (*this == ambisonic()) return "Ambisonic"; | |||
return "Unknown"; | |||
} | |||
bool AudioChannelSet::isDiscreteLayout() const noexcept | |||
{ | |||
Array<AudioChannelSet::ChannelType> speakers = getChannelTypes(); | |||
for (int i = 0; i < speakers.size(); ++i) | |||
if (speakers.getReference (i) > ambisonicZ) | |||
return true; | |||
return false; | |||
} | |||
int AudioChannelSet::size() const noexcept | |||
{ | |||
return channels.countNumberOfSetBits(); | |||
} | |||
AudioChannelSet::ChannelType AudioChannelSet::getTypeOfChannel (int index) const noexcept | |||
{ | |||
int bit = channels.findNextSetBit(0); | |||
for (int i = 0; i < index && bit >= 0; ++i) | |||
bit = channels.findNextSetBit (bit + 1); | |||
return static_cast<ChannelType> (bit); | |||
} | |||
int AudioChannelSet::getChannelIndexForType (AudioChannelSet::ChannelType type) const noexcept | |||
{ | |||
int idx = 0; | |||
for (int bit = channels.findNextSetBit (0); bit >= 0; bit = channels.findNextSetBit (bit + 1)) | |||
{ | |||
if (static_cast<ChannelType> (bit) == type) | |||
return idx; | |||
idx++; | |||
} | |||
return -1; | |||
} | |||
Array<AudioChannelSet::ChannelType> AudioChannelSet::getChannelTypes() const | |||
{ | |||
Array<ChannelType> result; | |||
for (int bit = channels.findNextSetBit(0); bit >= 0; bit = channels.findNextSetBit (bit + 1)) | |||
result.add (static_cast<ChannelType> (bit)); | |||
return result; | |||
} | |||
void AudioChannelSet::addChannel (ChannelType newChannel) | |||
{ | |||
const int bit = static_cast<int> (newChannel); | |||
jassert (bit >= 0 && bit < 1024); | |||
channels.setBit (bit); | |||
} | |||
void AudioChannelSet::removeChannel (ChannelType newChannel) | |||
{ | |||
const int bit = static_cast<int> (newChannel); | |||
jassert (bit >= 0 && bit < 1024); | |||
channels.clearBit (bit); | |||
} | |||
AudioChannelSet AudioChannelSet::disabled() { return AudioChannelSet(); } | |||
AudioChannelSet AudioChannelSet::mono() { return AudioChannelSet (1u << centre); } | |||
AudioChannelSet AudioChannelSet::stereo() { return AudioChannelSet ((1u << left) | (1u << right)); } | |||
AudioChannelSet AudioChannelSet::createLCR() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre)); } | |||
AudioChannelSet AudioChannelSet::createLRS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << surround)); } | |||
AudioChannelSet AudioChannelSet::createLCRS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surround)); } | |||
AudioChannelSet AudioChannelSet::create5point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround)); } | |||
AudioChannelSet AudioChannelSet::create5point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << LFE)); } | |||
AudioChannelSet AudioChannelSet::create6point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << centreSurround)); } | |||
AudioChannelSet AudioChannelSet::create6point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << centreSurround) | (1u << LFE)); } | |||
AudioChannelSet AudioChannelSet::create6point0Music() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftSurroundSide) | (1u << rightSurroundSide)); } | |||
AudioChannelSet AudioChannelSet::create6point1Music() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << LFE)); } | |||
AudioChannelSet AudioChannelSet::create7point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); } | |||
AudioChannelSet AudioChannelSet::create7point0SDDS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftCentre) | (1u << rightCentre)); } | |||
AudioChannelSet AudioChannelSet::create7point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear) | (1u << LFE)); } | |||
AudioChannelSet AudioChannelSet::create7point1SDDS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftCentre) | (1u << rightCentre) | (1u << LFE)); } | |||
AudioChannelSet AudioChannelSet::ambisonic() { return AudioChannelSet ((1u << ambisonicW) | (1u << ambisonicX) | (1u << ambisonicY) | (1u << ambisonicZ)); } | |||
AudioChannelSet AudioChannelSet::quadraphonic() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << leftSurround) | (1u << rightSurround)); } | |||
AudioChannelSet AudioChannelSet::pentagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); } | |||
AudioChannelSet AudioChannelSet::hexagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << leftSurroundRear) | (1u << rightSurroundRear) | (1u << centre) | (1u << centreSurround)); } | |||
AudioChannelSet AudioChannelSet::octagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << leftSurround) | (1u << rightSurround) | (1u << centre) | (1u << centreSurround) | (1u << wideLeft) | (1u << wideRight)); } | |||
AudioChannelSet AudioChannelSet::discreteChannels (int numChannels) | |||
{ | |||
AudioChannelSet s; | |||
s.channels.setRange (discreteChannel0, numChannels, true); | |||
return s; | |||
} | |||
AudioChannelSet AudioChannelSet::canonicalChannelSet (int numChannels) | |||
{ | |||
if (numChannels == 1) return AudioChannelSet::mono(); | |||
if (numChannels == 2) return AudioChannelSet::stereo(); | |||
if (numChannels == 3) return AudioChannelSet::createLCR(); | |||
if (numChannels == 4) return AudioChannelSet::quadraphonic(); | |||
if (numChannels == 5) return AudioChannelSet::create5point0(); | |||
if (numChannels == 6) return AudioChannelSet::create5point1(); | |||
if (numChannels == 7) return AudioChannelSet::create7point0(); | |||
if (numChannels == 8) return AudioChannelSet::create7point1(); | |||
return discreteChannels (numChannels); | |||
} | |||
AudioChannelSet AudioChannelSet::namedChannelSet (int numChannels) | |||
{ | |||
if (numChannels == 1) return AudioChannelSet::mono(); | |||
if (numChannels == 2) return AudioChannelSet::stereo(); | |||
if (numChannels == 3) return AudioChannelSet::createLCR(); | |||
if (numChannels == 4) return AudioChannelSet::quadraphonic(); | |||
if (numChannels == 5) return AudioChannelSet::create5point0(); | |||
if (numChannels == 6) return AudioChannelSet::create5point1(); | |||
if (numChannels == 7) return AudioChannelSet::create7point0(); | |||
if (numChannels == 8) return AudioChannelSet::create7point1(); | |||
return AudioChannelSet(); | |||
} | |||
Array<AudioChannelSet> AudioChannelSet::channelSetsWithNumberOfChannels (int numChannels) | |||
{ | |||
Array<AudioChannelSet> retval; | |||
if (numChannels != 0) | |||
{ | |||
retval.add (AudioChannelSet::discreteChannels (numChannels)); | |||
if (numChannels == 1) | |||
{ | |||
retval.add (AudioChannelSet::mono()); | |||
} | |||
else if (numChannels == 2) | |||
{ | |||
retval.add (AudioChannelSet::stereo()); | |||
} | |||
else if (numChannels == 3) | |||
{ | |||
retval.add (AudioChannelSet::createLCR()); | |||
retval.add (AudioChannelSet::createLRS()); | |||
} | |||
else if (numChannels == 4) | |||
{ | |||
retval.add (AudioChannelSet::quadraphonic()); | |||
retval.add (AudioChannelSet::createLCRS()); | |||
retval.add (AudioChannelSet::ambisonic()); | |||
} | |||
else if (numChannels == 5) | |||
{ | |||
retval.add (AudioChannelSet::create5point0()); | |||
retval.add (AudioChannelSet::pentagonal()); | |||
} | |||
else if (numChannels == 6) | |||
{ | |||
retval.add (AudioChannelSet::create5point1()); | |||
retval.add (AudioChannelSet::create6point0()); | |||
retval.add (AudioChannelSet::create6point0Music()); | |||
retval.add (AudioChannelSet::hexagonal()); | |||
} | |||
else if (numChannels == 7) | |||
{ | |||
retval.add (AudioChannelSet::create7point0()); | |||
retval.add (AudioChannelSet::create7point0SDDS()); | |||
retval.add (AudioChannelSet::create6point1()); | |||
retval.add (AudioChannelSet::create6point1Music()); | |||
} | |||
else if (numChannels == 8) | |||
{ | |||
retval.add (AudioChannelSet::create7point1()); | |||
retval.add (AudioChannelSet::create7point1SDDS()); | |||
retval.add (AudioChannelSet::octagonal()); | |||
} | |||
} | |||
return retval; | |||
} |
@@ -0,0 +1,361 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#ifndef JUCE_AUDIOCHANNELSET_H_INCLUDED | |||
#define JUCE_AUDIOCHANNELSET_H_INCLUDED | |||
//============================================================================== | |||
/** | |||
Represents a set of audio channel types. | |||
For example, you might have a set of left + right channels, which is a stereo | |||
channel set. It is a collection of values from the AudioChannelSet::ChannelType | |||
enum, where each type may only occur once within the set. | |||
The documentation below lists which AudioChannelSet corresponds to which native | |||
layouts used by AAX, VST2/VST3 and CoreAudio/AU. The layout tags in CoreAudio | |||
are particularly confusing. For example, the layout which is labeled as "7.1 SDDS" | |||
in Logic Pro, corresponds to CoreAudio/AU's kAudioChannelLayoutTag_DTS_7_0 tag, whereas | |||
AAX's DTS 7.1 Layout corresponds to CoreAudio/AU's | |||
kAudioChannelLayoutTag_MPEG_7_1_A format, etc. Please do not use the CoreAudio tag | |||
as an indication to the actual layout of the speakers. | |||
@see Bus | |||
*/ | |||
class JUCE_API AudioChannelSet | |||
{ | |||
public: | |||
/** Creates an empty channel set. | |||
You can call addChannel to add channels to the set. | |||
*/ | |||
AudioChannelSet() noexcept {} | |||
/** Creates a zero-channel set which can be used to indicate that a | |||
bus is disabled. */ | |||
static AudioChannelSet disabled(); | |||
//============================================================================== | |||
/** Creates a one-channel mono set (centre). | |||
Is equivalent to: kMonoAAX (VST), AAX_eStemFormat_Mono (AAX), kAudioChannelLayoutTag_Mono (CoreAudio) | |||
*/ | |||
static AudioChannelSet mono(); | |||
/** Creates a set containing a stereo set (left, right). | |||
Is equivalent to: kStereo (VST), AAX_eStemFormat_Stereo (AAX), kAudioChannelLayoutTag_Stereo (CoreAudio) | |||
*/ | |||
static AudioChannelSet stereo(); | |||
//============================================================================== | |||
/** Creates a set containing an LCR set (left, right, centre). | |||
Is equivalent to: k30Cine (VST), AAX_eStemFormat_LCR (AAX), kAudioChannelLayoutTag_MPEG_3_0_A (CoreAudio) | |||
This format is referred to as "LRC" in Cubase. | |||
This format is referred to as "LCR" in Pro Tools. | |||
*/ | |||
static AudioChannelSet createLCR(); | |||
/** Creates a set containing an LRS set (left, right, surround). | |||
Is equivalent to: k30Music (VST), n/a (AAX), kAudioChannelLayoutTag_ITU_2_1 (CoreAudio) | |||
This format is referred to as "LRS" in Cubase. | |||
*/ | |||
static AudioChannelSet createLRS(); | |||
/** Creates a set containing an LCRS set (left, right, centre, surround). | |||
Is equivalent to: k40Cine (VST), AAX_eStemFormat_LCRS (AAX), kAudioChannelLayoutTag_MPEG_4_0_A (CoreAudio) | |||
This format is referred to as "LCRS (Pro Logic)" in Logic Pro. | |||
This format is referred to as "LRCS" in Cubase. | |||
This format is referred to as "LCRS" in Pro Tools. | |||
*/ | |||
static AudioChannelSet createLCRS(); | |||
//============================================================================== | |||
/** Creates a set for a 5.0 surround setup (left, right, centre, leftSurround, rightSurround). | |||
Is equivalent to: k50 (VST), AAX_eStemFormat_5_0 (AAX), kAudioChannelLayoutTag_MPEG_5_0_A (CoreAudio) | |||
This format is referred to as "5.0" in Cubase. | |||
This format is referred to as "5.0" in Pro Tools. | |||
*/ | |||
static AudioChannelSet create5point0(); | |||
/** Creates a set for a 5.1 surround setup (left, right, centre, leftSurround, rightSurround, LFE). | |||
Is equivalent to: k51 (VST), AAX_eStemFormat_5_1 (AAX), kAudioChannelLayoutTag_MPEG_5_1_A (CoreAudio) | |||
This format is referred to as "5.1 (ITU 775)" in Logic Pro. | |||
This format is referred to as "5.1" in Cubase. | |||
This format is referred to as "5.1" in Pro Tools. | |||
*/ | |||
static AudioChannelSet create5point1(); | |||
/** Creates a set for a 6.0 Cine surround setup (left, right, centre, leftSurround, rightSurround, centreSurround). | |||
Is equivalent to: k60Cine (VST), AAX_eStemFormat_6_0 (AAX), kAudioChannelLayoutTag_AudioUnit_6_0 (CoreAudio) | |||
Logic Pro incorrectly uses this for the surround format labeled "6.1 (ES/EX)". | |||
This format is referred to as "6.0 Cine" in Cubase. | |||
This format is referred to as "6.0" in Pro Tools. | |||
*/ | |||
static AudioChannelSet create6point0(); | |||
/** Creates a set for a 6.1 Cine surround setup (left, right, centre, leftSurround, rightSurround, centreSurround, LFE). | |||
Is equivalent to: k61Cine (VST), AAX_eStemFormat_6_1 (AAX), kAudioChannelLayoutTag_MPEG_6_1_A (CoreAudio) | |||
This format is referred to as "6.1" in Pro Tools. | |||
*/ | |||
static AudioChannelSet create6point1(); | |||
/** Creates a set for a 6.0 Music surround setup (left, right, leftSurround, rightSurround, leftSurroundSide, rightSurroundSide). | |||
Is equivalent to: k60Music (VST), n/a (AAX), kAudioChannelLayoutTag_DTS_6_0_A (CoreAudio) | |||
This format is referred to as "6.0 Music" in Cubase. | |||
*/ | |||
static AudioChannelSet create6point0Music(); | |||
/** Creates a set for a 6.0 Music surround setup (left, right, leftSurround, rightSurround, leftSurroundSide, rightSurroundSide, LFE). | |||
Is equivalent to: k61Music (VST), n/a (AAX), kAudioChannelLayoutTag_DTS_6_1_A (CoreAudio) | |||
*/ | |||
static AudioChannelSet create6point1Music(); | |||
/** Creates a set for a DTS 7.0 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear). | |||
Is equivalent to: k70Music (VST), AAX_eStemFormat_7_0_DTS (AAX), kAudioChannelLayoutTag_AudioUnit_7_0 (CoreAudio) | |||
This format is referred to as "7.0" in Pro Tools. | |||
*/ | |||
static AudioChannelSet create7point0(); | |||
/** Creates a set for a SDDS 7.0 surround setup (left, right, centre, leftSurround, rightSurround, leftCentre, rightCentre). | |||
Is equivalent to: k70Cine (VST), AAX_eStemFormat_7_0_SDDS (AAX), kAudioChannelLayoutTag_AudioUnit_7_0_Front (CoreAudio) | |||
This format is referred to as "7.0 SDDS" in Pro Tools. | |||
*/ | |||
static AudioChannelSet create7point0SDDS(); | |||
/** Creates a set for a DTS 7.1 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, LFE). | |||
Is equivalent to: k71CineSideFill (VST), AAX_eStemFormat_7_1_DTS (AAX), kAudioChannelLayoutTag_MPEG_7_1_C/kAudioChannelLayoutTag_ITU_3_4_1 (CoreAudio) | |||
This format is referred to as "7.1 (3/4.1)" in Logic Pro. | |||
This format is referred to as "7.1" in Pro Tools. | |||
*/ | |||
static AudioChannelSet create7point1(); | |||
/** Creates a set for a 7.1 surround setup (left, right, centre, leftSurround, rightSurround, leftCentre, rightCentre, LFE). | |||
Is equivalent to: k71Cine (VST), AAX_eStemFormat_7_1_SDDS (AAX), kAudioChannelLayoutTag_MPEG_7_1_A (CoreAudio) | |||
This format is referred to as "7.1 (SDDS)" in Logic Pro. | |||
This format is referred to as "7.1 SDDS" in Pro Tools. | |||
*/ | |||
static AudioChannelSet create7point1SDDS(); | |||
//============================================================================== | |||
/** Creates a set for ambisonic surround setups (ambisonicW, ambisonicX, ambisonicY, ambisonicZ). | |||
Is equivalent to: kBFormat (VST), n/a (AAX), kAudioChannelLayoutTag_Ambisonic_B_Format (CoreAudio) | |||
*/ | |||
static AudioChannelSet ambisonic(); | |||
/** Creates a set for quadraphonic surround setup (left, right, leftSurround, rightSurround) | |||
Is equivalent to: k40Music (VST), AAX_eStemFormat_Quad (AAX), kAudioChannelLayoutTag_Quadraphonic (CoreAudio) | |||
This format is referred to as "Quadraphonic" in Logic Pro. | |||
This format is referred to as "Quadro" in Cubase. | |||
This format is referred to as "Quad" in Pro Tools. | |||
*/ | |||
static AudioChannelSet quadraphonic(); | |||
/** Creates a set for pentagonal surround setup (left, right, centre, leftSurroundRear, rightSurroundRear). | |||
Is equivalent to: n/a (VST), n/a (AAX), kAudioChannelLayoutTag_Pentagonal (CoreAudio) | |||
*/ | |||
static AudioChannelSet pentagonal(); | |||
/** Creates a set for hexagonal surround setup (left, right, leftSurroundRear, rightSurroundRear, centre, surroundCentre). | |||
Is equivalent to: n/a (VST), n/a (AAX), kAudioChannelLayoutTag_Hexagonal (CoreAudio) | |||
*/ | |||
static AudioChannelSet hexagonal(); | |||
/** Creates a set for octagonal surround setup (left, right, leftSurround, rightSurround, centre, centreSurround, wideLeft, wideRight). | |||
Is equivalent to: n/a (VST), n/a (AAX), kAudioChannelLayoutTag_Octagonal (CoreAudio) | |||
*/ | |||
static AudioChannelSet octagonal(); | |||
//============================================================================== | |||
/** Creates a set of untyped discrete channels. */ | |||
static AudioChannelSet discreteChannels (int numChannels); | |||
/** Create a canonical channel set for a given number of channels. | |||
For example, numChannels = 1 will return mono, numChannels = 2 will return stereo, etc. */ | |||
static AudioChannelSet canonicalChannelSet (int numChannels); | |||
/** Create a channel set for a given number of channels which is non-discrete. | |||
If numChannels is larger than the number of channels of the surround format | |||
with the maximum amount of channels (currently 7.1 Surround), then this | |||
function returns an empty set.*/ | |||
static AudioChannelSet namedChannelSet (int numChannels); | |||
/** Return an array of channel sets which have a given number of channels */ | |||
static Array<AudioChannelSet> channelSetsWithNumberOfChannels (int numChannels); | |||
//============================================================================== | |||
/** Represents different audio channel types. */ | |||
enum ChannelType | |||
{ | |||
unknown = 0, | |||
left = 1, // L | |||
right = 2, // R | |||
centre = 3, // C (sometimes M for mono) | |||
LFE = 4, | |||
leftSurround = 5, // Ls | |||
rightSurround = 6, // Rs | |||
leftCentre = 7, // Lc (AAX/VST), Lc used as Lss in AU for most layouts | |||
rightCentre = 8, // Rc (AAX/VST), Rc used as Rss in AU for most layouts | |||
centreSurround = 9, // Cs/S | |||
surround = centreSurround, // Cs/S | |||
leftSurroundSide = 10, // Lss (AXX), Side Left "Sl" (VST), Left Centre "LC" (AU) | |||
rightSurroundSide = 11, // Rss (AXX), Side right "Sr" (VST), Right Centre "Rc" (AU) | |||
topMiddle = 12, | |||
topFrontLeft = 13, | |||
topFrontCentre = 14, | |||
topFrontRight = 15, | |||
topRearLeft = 16, | |||
topRearCentre = 17, | |||
topRearRight = 18, | |||
LFE2 = 19, | |||
leftSurroundRear = 20, // Lsr (AAX), Lcs (VST), Rls (AU) | |||
rightSurroundRear = 21, // Rsr (AAX), Rcs (VST), Rrs (AU) | |||
wideLeft = 22, | |||
wideRight = 23, | |||
ambisonicW = 24, | |||
ambisonicX = 25, | |||
ambisonicY = 26, | |||
ambisonicZ = 27, | |||
discreteChannel0 = 64 /**< Non-typed individual channels are indexed upwards from this value. */ | |||
}; | |||
/** Returns the name of a given channel type. For example, this method may return "Surround Left". */ | |||
static String getChannelTypeName (ChannelType); | |||
/** Returns the abbreviated name of a channel type. For example, this method may return "Ls". */ | |||
static String getAbbreviatedChannelTypeName (ChannelType); | |||
//============================================================================== | |||
enum | |||
{ | |||
maxChannelsOfNamedLayout = 8 | |||
}; | |||
/** Adds a channel to the set. */ | |||
void addChannel (ChannelType newChannelType); | |||
/** Removes a channel from the set. */ | |||
void removeChannel (ChannelType newChannelType); | |||
/** Returns the number of channels in the set. */ | |||
int size() const noexcept; | |||
/** Returns true if there are no channels in the set. */ | |||
bool isDisabled() const noexcept { return size() == 0; } | |||
/** Returns an array of all the types in this channel set. */ | |||
Array<ChannelType> getChannelTypes() const; | |||
/** Returns the type of one of the channels in the set, by index. */ | |||
ChannelType getTypeOfChannel (int channelIndex) const noexcept; | |||
/** Returns the index for a particular channel-type. | |||
Will return -1 if the this set does not contain a channel of this type. */ | |||
int getChannelIndexForType (ChannelType type) const noexcept; | |||
/** Returns a string containing a whitespace-separated list of speaker types | |||
corresponding to each channel. For example in a 5.1 arrangement, | |||
the string may be "L R C Lfe Ls Rs". If the speaker arrangement is unknown, | |||
the returned string will be empty.*/ | |||
String getSpeakerArrangementAsString() const; | |||
/** Returns the description of the current layout. For example, this method may return | |||
"Quadraphonic". Note that the returned string may not be unique. */ | |||
String getDescription() const; | |||
/** Returns if this is a channel layout made-up of discrete channels. */ | |||
bool isDiscreteLayout() const noexcept; | |||
/** Intersect two channel layouts. */ | |||
void intersect (const AudioChannelSet& other) { channels &= other.channels; } | |||
//============================================================================== | |||
bool operator== (const AudioChannelSet&) const noexcept; | |||
bool operator!= (const AudioChannelSet&) const noexcept; | |||
bool operator< (const AudioChannelSet&) const noexcept; | |||
private: | |||
BigInteger channels; | |||
explicit AudioChannelSet (uint32); | |||
}; | |||
#endif // JUCE_AUDIOCHANNELSET_H_INCLUDED |
@@ -397,9 +397,9 @@ public: | |||
convert between 32 and 64 bit float buffer types. | |||
*/ | |||
template <typename OtherType> | |||
void makeCopyOf (const AudioBuffer<OtherType>& other) | |||
void makeCopyOf (const AudioBuffer<OtherType>& other, bool avoidReallocating = false) | |||
{ | |||
setSize (other.getNumChannels(), other.getNumSamples()); | |||
setSize (other.getNumChannels(), other.getNumSamples(), false, false, avoidReallocating); | |||
if (other.hasBeenCleared()) | |||
{ | |||
@@ -204,10 +204,11 @@ namespace FloatVectorHelpers | |||
typedef float Type; | |||
typedef float32x4_t ParallelType; | |||
typedef uint32x4_t IntegerType; | |||
union signMaskUnion { ParallelType f; IntegerType i; }; | |||
enum { numParallel = 4 }; | |||
static forcedinline IntegerType toint (ParallelType v) noexcept { union { ParallelType f; IntegerType i; } u; u.f = v; return u.i; } | |||
static forcedinline ParallelType toflt (IntegerType v) noexcept { union { ParallelType f; IntegerType i; } u; u.i = v; return u.f; } | |||
static forcedinline IntegerType toint (ParallelType v) noexcept { signMaskUnion u; u.f = v; return u.i; } | |||
static forcedinline ParallelType toflt (IntegerType v) noexcept { signMaskUnion u; u.i = v; return u.f; } | |||
static forcedinline ParallelType load1 (Type v) noexcept { return vld1q_dup_f32 (&v); } | |||
static forcedinline ParallelType loadA (const Type* v) noexcept { return vld1q_f32 (v); } | |||
@@ -235,10 +236,11 @@ namespace FloatVectorHelpers | |||
typedef double Type; | |||
typedef double ParallelType; | |||
typedef uint64 IntegerType; | |||
union signMaskUnion { ParallelType f; IntegerType i; }; | |||
enum { numParallel = 1 }; | |||
static forcedinline IntegerType toint (ParallelType v) noexcept { union { ParallelType f; IntegerType i; } u; u.f = v; return u.i; } | |||
static forcedinline ParallelType toflt (IntegerType v) noexcept { union { ParallelType f; IntegerType i; } u; u.i = v; return u.f; } | |||
static forcedinline IntegerType toint (ParallelType v) noexcept { signMaskUnion u; u.f = v; return u.i; } | |||
static forcedinline ParallelType toflt (IntegerType v) noexcept { signMaskUnion u; u.i = v; return u.f; } | |||
static forcedinline ParallelType load1 (Type v) noexcept { return v; } | |||
static forcedinline ParallelType loadA (const Type* v) noexcept { return *v; } | |||
@@ -346,6 +348,9 @@ namespace FloatVectorHelpers | |||
#define JUCE_LOAD_SRC1_SRC2_DEST(src1Load, src2Load, dstLoad) const Mode::ParallelType d = dstLoad (dest), s1 = src1Load (src1), s2 = src2Load (src2); | |||
#define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest), s = srcLoad (src); | |||
union signMask32 { float f; uint32 i; }; | |||
union signMask64 { double d; uint64 i; }; | |||
#if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON | |||
template<int typeSize> struct ModeType { typedef BasicOps32 Mode; }; | |||
template<> struct ModeType<8> { typedef BasicOps64 Mode; }; | |||
@@ -806,7 +811,7 @@ void FloatVectorOperations::abs (float* dest, const float* src, int num) noexcep | |||
#if JUCE_USE_VDSP_FRAMEWORK | |||
vDSP_vabs ((float*) src, 1, dest, 1, (vDSP_Length) num); | |||
#else | |||
union { float f; uint32 i; } signMask; | |||
FloatVectorHelpers::signMask32 signMask; | |||
signMask.i = 0x7fffffffUL; | |||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = fabsf (src[i]), Mode::bit_and (s, mask), | |||
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, | |||
@@ -821,7 +826,7 @@ void FloatVectorOperations::abs (double* dest, const double* src, int num) noexc | |||
#if JUCE_USE_VDSP_FRAMEWORK | |||
vDSP_vabsD ((double*) src, 1, dest, 1, (vDSP_Length) num); | |||
#else | |||
union {double d; uint64 i;} signMask; | |||
FloatVectorHelpers::signMask64 signMask; | |||
signMask.i = 0x7fffffffffffffffULL; | |||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = fabs (src[i]), Mode::bit_and (s, mask), | |||
@@ -22,7 +22,6 @@ | |||
============================================================================== | |||
*/ | |||
struct CatmullRomAlgorithm | |||
{ | |||
static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept | |||
@@ -22,7 +22,6 @@ | |||
============================================================================== | |||
*/ | |||
/** | |||
Interpolator for resampling a stream of floats using Catmull-Rom interpolation. | |||
@@ -62,33 +62,131 @@ IIRCoefficients::IIRCoefficients (double c1, double c2, double c3, | |||
IIRCoefficients IIRCoefficients::makeLowPass (const double sampleRate, | |||
const double frequency) noexcept | |||
{ | |||
jassert (sampleRate > 0); | |||
return makeLowPass (sampleRate, frequency, 1.0 / std::sqrt (2.0)); | |||
} | |||
IIRCoefficients IIRCoefficients::makeLowPass (const double sampleRate, | |||
const double frequency, | |||
const double Q) noexcept | |||
{ | |||
jassert (sampleRate > 0.0); | |||
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); | |||
jassert (Q > 0.0); | |||
const double n = 1.0 / std::tan (double_Pi * frequency / sampleRate); | |||
const double nSquared = n * n; | |||
const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); | |||
const double c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); | |||
return IIRCoefficients (c1, | |||
c1 * 2.0, | |||
c1, | |||
1.0, | |||
c1 * 2.0 * (1.0 - nSquared), | |||
c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); | |||
c1 * (1.0 - 1.0 / Q * n + nSquared)); | |||
} | |||
IIRCoefficients IIRCoefficients::makeHighPass (const double sampleRate, | |||
const double frequency) noexcept | |||
{ | |||
return makeHighPass (sampleRate, frequency, 1.0 / std::sqrt(2.0)); | |||
} | |||
IIRCoefficients IIRCoefficients::makeHighPass (const double sampleRate, | |||
const double frequency, | |||
const double Q) noexcept | |||
{ | |||
jassert (sampleRate > 0.0); | |||
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); | |||
jassert (Q > 0.0); | |||
const double n = std::tan (double_Pi * frequency / sampleRate); | |||
const double nSquared = n * n; | |||
const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); | |||
const double c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); | |||
return IIRCoefficients (c1, | |||
c1 * -2.0, | |||
c1, | |||
1.0, | |||
c1 * 2.0 * (nSquared - 1.0), | |||
c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); | |||
c1 * (1.0 - 1.0 / Q * n + nSquared)); | |||
} | |||
IIRCoefficients IIRCoefficients::makeBandPass (const double sampleRate, | |||
const double frequency) noexcept | |||
{ | |||
return makeBandPass (sampleRate, frequency, 1.0 / std::sqrt (2.0)); | |||
} | |||
IIRCoefficients IIRCoefficients::makeBandPass (const double sampleRate, | |||
const double frequency, | |||
const double Q) noexcept | |||
{ | |||
jassert (sampleRate > 0.0); | |||
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); | |||
jassert (Q > 0.0); | |||
const double n = 1.0 / std::tan (double_Pi * frequency / sampleRate); | |||
const double nSquared = n * n; | |||
const double c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); | |||
return IIRCoefficients (c1 * n / Q, | |||
0.0, | |||
-c1 * n / Q, | |||
1.0, | |||
c1 * 2.0 * (1.0 - nSquared), | |||
c1 * (1.0 - 1.0 / Q * n + nSquared)); | |||
} | |||
IIRCoefficients IIRCoefficients::makeNotchFilter (const double sampleRate, | |||
const double frequency) noexcept | |||
{ | |||
return makeNotchFilter (sampleRate, frequency, 1.0 / std::sqrt (2.0)); | |||
} | |||
IIRCoefficients IIRCoefficients::makeNotchFilter (const double sampleRate, | |||
const double frequency, | |||
const double Q) noexcept | |||
{ | |||
jassert (sampleRate > 0.0); | |||
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); | |||
jassert (Q > 0.0); | |||
const double n = 1.0 / std::tan (double_Pi * frequency / sampleRate); | |||
const double nSquared = n * n; | |||
const double c1 = 1.0 / (1.0 + n / Q + nSquared); | |||
return IIRCoefficients (c1 * (1.0 + nSquared), | |||
2.0 * c1 * (1.0 - nSquared), | |||
c1 * (1.0 + nSquared), | |||
1.0, | |||
c1 * 2.0 * (1.0 - nSquared), | |||
c1 * (1.0 - n / Q + nSquared)); | |||
} | |||
IIRCoefficients IIRCoefficients::makeAllPass (const double sampleRate, | |||
const double frequency) noexcept | |||
{ | |||
return makeAllPass (sampleRate, frequency, 1.0 / std::sqrt (2.0)); | |||
} | |||
IIRCoefficients IIRCoefficients::makeAllPass (const double sampleRate, | |||
const double frequency, | |||
const double Q) noexcept | |||
{ | |||
jassert (sampleRate > 0.0); | |||
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); | |||
jassert (Q > 0.0); | |||
const double n = 1.0 / std::tan (double_Pi * frequency / sampleRate); | |||
const double nSquared = n * n; | |||
const double c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); | |||
return IIRCoefficients (c1 * (1.0 - n / Q + nSquared), | |||
c1 * 2.0 * (1.0 - nSquared), | |||
1.0, | |||
1.0, | |||
c1 * 2.0 * (1.0 - nSquared), | |||
c1 * (1.0 - n / Q + nSquared)); | |||
} | |||
IIRCoefficients IIRCoefficients::makeLowShelf (const double sampleRate, | |||
@@ -96,8 +194,9 @@ IIRCoefficients IIRCoefficients::makeLowShelf (const double sampleRate, | |||
const double Q, | |||
const float gainFactor) noexcept | |||
{ | |||
jassert (sampleRate > 0); | |||
jassert (Q > 0); | |||
jassert (sampleRate > 0.0); | |||
jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5); | |||
jassert (Q > 0.0); | |||
const double A = jmax (0.0f, std::sqrt (gainFactor)); | |||
const double aminus1 = A - 1.0; | |||
@@ -120,8 +219,9 @@ IIRCoefficients IIRCoefficients::makeHighShelf (const double sampleRate, | |||
const double Q, | |||
const float gainFactor) noexcept | |||
{ | |||
jassert (sampleRate > 0); | |||
jassert (Q > 0); | |||
jassert (sampleRate > 0.0); | |||
jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5); | |||
jassert (Q > 0.0); | |||
const double A = jmax (0.0f, std::sqrt (gainFactor)); | |||
const double aminus1 = A - 1.0; | |||
@@ -140,15 +240,16 @@ IIRCoefficients IIRCoefficients::makeHighShelf (const double sampleRate, | |||
} | |||
IIRCoefficients IIRCoefficients::makePeakFilter (const double sampleRate, | |||
const double centreFrequency, | |||
const double frequency, | |||
const double Q, | |||
const float gainFactor) noexcept | |||
{ | |||
jassert (sampleRate > 0); | |||
jassert (Q > 0); | |||
jassert (sampleRate > 0.0); | |||
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); | |||
jassert (Q > 0.0); | |||
const double A = jmax (0.0f, std::sqrt (gainFactor)); | |||
const double omega = (double_Pi * 2.0 * jmax (centreFrequency, 2.0)) / sampleRate; | |||
const double omega = (double_Pi * 2.0 * jmax (frequency, 2.0)) / sampleRate; | |||
const double alpha = 0.5 * std::sin (omega) / Q; | |||
const double c2 = -2.0 * std::cos (omega); | |||
const double alphaTimesA = alpha * A; | |||
@@ -164,12 +265,12 @@ IIRCoefficients IIRCoefficients::makePeakFilter (const double sampleRate, | |||
//============================================================================== | |||
IIRFilter::IIRFilter() noexcept | |||
: v1 (0), v2 (0), active (false) | |||
: v1 (0.0), v2 (0.0), active (false) | |||
{ | |||
} | |||
IIRFilter::IIRFilter (const IIRFilter& other) noexcept | |||
: v1 (0), v2 (0), active (other.active) | |||
: v1 (0.0), v2 (0.0), active (other.active) | |||
{ | |||
const SpinLock::ScopedLockType sl (other.processLock); | |||
coefficients = other.coefficients; | |||
@@ -198,7 +299,7 @@ void IIRFilter::setCoefficients (const IIRCoefficients& newCoefficients) noexcep | |||
void IIRFilter::reset() noexcept | |||
{ | |||
const SpinLock::ScopedLockType sl (processLock); | |||
v1 = v2 = 0; | |||
v1 = v2 = 0.0; | |||
} | |||
float IIRFilter::processSingleSampleRaw (const float in) noexcept | |||
@@ -55,14 +55,53 @@ public: | |||
/** Destructor. */ | |||
~IIRCoefficients() noexcept; | |||
//============================================================================== | |||
/** Returns the coefficients for a low-pass filter. */ | |||
static IIRCoefficients makeLowPass (double sampleRate, | |||
double frequency) noexcept; | |||
/** Returns the coefficients for a low-pass filter with variable Q. */ | |||
static IIRCoefficients makeLowPass (double sampleRate, | |||
double frequency, | |||
double Q) noexcept; | |||
//============================================================================== | |||
/** Returns the coefficients for a high-pass filter. */ | |||
static IIRCoefficients makeHighPass (double sampleRate, | |||
double frequency) noexcept; | |||
/** Returns the coefficients for a high-pass filter with variable Q. */ | |||
static IIRCoefficients makeHighPass (double sampleRate, | |||
double frequency, | |||
double Q) noexcept; | |||
//============================================================================== | |||
/** Returns the coefficients for a band-pass filter. */ | |||
static IIRCoefficients makeBandPass (double sampleRate, double frequency) noexcept; | |||
/** Returns the coefficients for a band-pass filter with variable Q. */ | |||
static IIRCoefficients makeBandPass (double sampleRate, | |||
double frequency, | |||
double Q) noexcept; | |||
//============================================================================== | |||
/** Returns the coefficients for a notch filter. */ | |||
static IIRCoefficients makeNotchFilter (double sampleRate, double frequency) noexcept; | |||
/** Returns the coefficients for a notch filter with variable Q. */ | |||
static IIRCoefficients makeNotchFilter (double sampleRate, | |||
double frequency, | |||
double Q) noexcept; | |||
//============================================================================== | |||
/** Returns the coefficients for an all-pass filter. */ | |||
static IIRCoefficients makeAllPass (double sampleRate, double frequency) noexcept; | |||
/** Returns the coefficients for an all-pass filter with variable Q. */ | |||
static IIRCoefficients makeAllPass (double sampleRate, | |||
double frequency, | |||
double Q) noexcept; | |||
//============================================================================== | |||
/** Returns the coefficients for a low-pass shelf filter with variable Q and gain. | |||
@@ -22,7 +22,6 @@ | |||
============================================================================== | |||
*/ | |||
/** | |||
Interpolator for resampling a stream of floats using 4-point lagrange interpolation. | |||
@@ -33,8 +33,8 @@ | |||
*/ | |||
//============================================================================== | |||
template<typename FloatType> | |||
class JUCE_API LinearSmoothedValue | |||
template <typename FloatType> | |||
class LinearSmoothedValue | |||
{ | |||
public: | |||
/** Constructor. */ | |||
@@ -31,6 +31,8 @@ | |||
#error "Incorrect use of JUCE cpp file" | |||
#endif | |||
#include "AppConfig.h" | |||
#include "juce_audio_basics.h" | |||
#if JUCE_MINGW && ! defined (__SSE2__) | |||
@@ -67,6 +69,13 @@ | |||
#define JUCE_USE_ARM_NEON 1 | |||
#endif | |||
#if TARGET_IPHONE_SIMULATOR | |||
#ifdef JUCE_USE_ARM_NEON | |||
#undef JUCE_USE_ARM_NEON | |||
#endif | |||
#define JUCE_USE_ARM_NEON 0 | |||
#endif | |||
#if JUCE_USE_ARM_NEON | |||
#include <arm_neon.h> | |||
#endif | |||
@@ -76,6 +85,7 @@ namespace juce | |||
#include "buffers/juce_AudioDataConverters.cpp" | |||
#include "buffers/juce_FloatVectorOperations.cpp" | |||
#include "buffers/juce_AudioChannelSet.cpp" | |||
#include "effects/juce_IIRFilter.cpp" | |||
#include "effects/juce_IIRFilterOld.cpp" | |||
#include "effects/juce_LagrangeInterpolator.cpp" | |||
@@ -22,12 +22,37 @@ | |||
============================================================================== | |||
*/ | |||
/******************************************************************************* | |||
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.txt file. | |||
BEGIN_JUCE_MODULE_DECLARATION | |||
ID: juce_audio_basics | |||
vendor: juce | |||
version: 4.3.0 | |||
name: JUCE audio and MIDI data classes | |||
description: Classes for audio buffer manipulation, midi message handling, synthesis, etc. | |||
website: http://www.juce.com/juce | |||
license: GPL/Commercial | |||
dependencies: juce_core | |||
OSXFrameworks: Accelerate | |||
iOSFrameworks: Accelerate | |||
END_JUCE_MODULE_DECLARATION | |||
*******************************************************************************/ | |||
#ifndef JUCE_AUDIO_BASICS_H_INCLUDED | |||
#define JUCE_AUDIO_BASICS_H_INCLUDED | |||
#include "../juce_core/juce_core.h" | |||
#include "juce_core/juce_core.h" | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
@@ -37,6 +62,7 @@ namespace juce | |||
#include "buffers/juce_AudioDataConverters.h" | |||
#include "buffers/juce_FloatVectorOperations.h" | |||
#include "buffers/juce_AudioSampleBuffer.h" | |||
#include "buffers/juce_AudioChannelSet.h" | |||
#include "effects/juce_Decibels.h" | |||
#include "effects/juce_IIRFilter.h" | |||
#include "effects/juce_IIRFilterOld.h" | |||
@@ -248,7 +248,7 @@ bool MidiFile::readFrom (InputStream& sourceStream) | |||
clear(); | |||
MemoryBlock data; | |||
const int maxSensibleMidiFileSize = 2 * 1024 * 1024; | |||
const int maxSensibleMidiFileSize = 200 * 1024 * 1024; | |||
// (put a sanity-check on the file size, as midi files are generally small) | |||
if (sourceStream.readIntoMemoryBlock (data, maxSensibleMidiFileSize)) | |||
@@ -33,11 +33,23 @@ namespace MidiHelpers | |||
{ | |||
return (uint8) jlimit (0, 127, v); | |||
} | |||
} | |||
inline uint8 floatVelocityToByte (const float v) noexcept | |||
{ | |||
return validVelocity (roundToInt (v * 127.0f)); | |||
} | |||
//============================================================================== | |||
uint8 MidiMessage::floatValueToMidiByte (const float v) noexcept | |||
{ | |||
return MidiHelpers::validVelocity (roundToInt (v * 127.0f)); | |||
} | |||
uint16 MidiMessage::pitchbendToPitchwheelPos (const float pitchbend, | |||
const float pitchbendRange) noexcept | |||
{ | |||
// can't translate a pitchbend value that is outside of the given range! | |||
jassert (std::abs (pitchbend) <= pitchbendRange); | |||
return static_cast<uint16> (pitchbend > 0.0f | |||
? jmap (pitchbend, 0.0f, pitchbendRange, 8192.0f, 16383.0f) | |||
: jmap (pitchbend, -pitchbendRange, 0.0f, 0.0f, 8192.0f)); | |||
} | |||
//============================================================================== | |||
@@ -84,25 +96,24 @@ int MidiMessage::getMessageLengthFromFirstByte (const uint8 firstByte) noexcept | |||
MidiMessage::MidiMessage() noexcept | |||
: timeStamp (0), size (2) | |||
{ | |||
preallocatedData.asBytes[0] = 0xf0; | |||
preallocatedData.asBytes[1] = 0xf7; | |||
packedData.asBytes[0] = 0xf0; | |||
packedData.asBytes[1] = 0xf7; | |||
} | |||
MidiMessage::MidiMessage (const void* const d, const int dataSize, const double t) | |||
: timeStamp (t), | |||
size (dataSize) | |||
: timeStamp (t), size (dataSize) | |||
{ | |||
jassert (dataSize > 0); | |||
memcpy (allocateSpace (dataSize), d, (size_t) dataSize); | |||
// this checks that the length matches the data.. | |||
jassert (dataSize > 3 || *(uint8*)d >= 0xf0 || getMessageLengthFromFirstByte (*(uint8*)d) == size); | |||
// check that the length matches the data.. | |||
jassert (size > 3 || *(uint8*)d >= 0xf0 || getMessageLengthFromFirstByte (*(uint8*)d) == size); | |||
memcpy (allocateSpace (dataSize), d, (size_t) dataSize); | |||
} | |||
MidiMessage::MidiMessage (const int byte1, const double t) noexcept | |||
: timeStamp (t), size (1) | |||
{ | |||
preallocatedData.asBytes[0] = (uint8) byte1; | |||
packedData.asBytes[0] = (uint8) byte1; | |||
// check that the length matches the data.. | |||
jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 1); | |||
@@ -111,8 +122,8 @@ MidiMessage::MidiMessage (const int byte1, const double t) noexcept | |||
MidiMessage::MidiMessage (const int byte1, const int byte2, const double t) noexcept | |||
: timeStamp (t), size (2) | |||
{ | |||
preallocatedData.asBytes[0] = (uint8) byte1; | |||
preallocatedData.asBytes[1] = (uint8) byte2; | |||
packedData.asBytes[0] = (uint8) byte1; | |||
packedData.asBytes[1] = (uint8) byte2; | |||
// check that the length matches the data.. | |||
jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 2); | |||
@@ -121,9 +132,9 @@ MidiMessage::MidiMessage (const int byte1, const int byte2, const double t) noex | |||
MidiMessage::MidiMessage (const int byte1, const int byte2, const int byte3, const double t) noexcept | |||
: timeStamp (t), size (3) | |||
{ | |||
preallocatedData.asBytes[0] = (uint8) byte1; | |||
preallocatedData.asBytes[1] = (uint8) byte2; | |||
preallocatedData.asBytes[2] = (uint8) byte3; | |||
packedData.asBytes[0] = (uint8) byte1; | |||
packedData.asBytes[1] = (uint8) byte2; | |||
packedData.asBytes[2] = (uint8) byte3; | |||
// check that the length matches the data.. | |||
jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 3); | |||
@@ -132,29 +143,19 @@ MidiMessage::MidiMessage (const int byte1, const int byte2, const int byte3, con | |||
MidiMessage::MidiMessage (const MidiMessage& other) | |||
: timeStamp (other.timeStamp), size (other.size) | |||
{ | |||
if (other.allocatedData != nullptr) | |||
{ | |||
allocatedData.malloc ((size_t) size); | |||
memcpy (allocatedData, other.allocatedData, (size_t) size); | |||
} | |||
if (isHeapAllocated()) | |||
memcpy (allocateSpace (size), other.getData(), (size_t) size); | |||
else | |||
{ | |||
preallocatedData.asInt32 = other.preallocatedData.asInt32; | |||
} | |||
packedData.allocatedData = other.packedData.allocatedData; | |||
} | |||
MidiMessage::MidiMessage (const MidiMessage& other, const double newTimeStamp) | |||
: timeStamp (newTimeStamp), size (other.size) | |||
{ | |||
if (other.allocatedData != nullptr) | |||
{ | |||
allocatedData.malloc ((size_t) size); | |||
memcpy (allocatedData, other.allocatedData, (size_t) size); | |||
} | |||
if (isHeapAllocated()) | |||
memcpy (allocateSpace (size), other.getData(), (size_t) size); | |||
else | |||
{ | |||
preallocatedData.asInt32 = other.preallocatedData.asInt32; | |||
} | |||
packedData.allocatedData = other.packedData.allocatedData; | |||
} | |||
MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const uint8 lastStatusByte, | |||
@@ -229,16 +230,15 @@ MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const | |||
} | |||
else | |||
{ | |||
preallocatedData.asInt32 = 0; | |||
size = getMessageLengthFromFirstByte ((uint8) byte); | |||
preallocatedData.asBytes[0] = (uint8) byte; | |||
packedData.asBytes[0] = (uint8) byte; | |||
if (size > 1) | |||
{ | |||
preallocatedData.asBytes[1] = src[0]; | |||
packedData.asBytes[1] = src[0]; | |||
if (size > 2) | |||
preallocatedData.asBytes[2] = src[1]; | |||
packedData.asBytes[2] = src[1]; | |||
} | |||
} | |||
@@ -246,7 +246,7 @@ MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const | |||
} | |||
else | |||
{ | |||
preallocatedData.asInt32 = 0; | |||
packedData.allocatedData = nullptr; | |||
size = 0; | |||
} | |||
} | |||
@@ -255,19 +255,25 @@ MidiMessage& MidiMessage::operator= (const MidiMessage& other) | |||
{ | |||
if (this != &other) | |||
{ | |||
timeStamp = other.timeStamp; | |||
size = other.size; | |||
if (other.allocatedData != nullptr) | |||
if (other.isHeapAllocated()) | |||
{ | |||
allocatedData.malloc ((size_t) size); | |||
memcpy (allocatedData, other.allocatedData, (size_t) size); | |||
if (isHeapAllocated()) | |||
packedData.allocatedData = static_cast<uint8*> (std::realloc (packedData.allocatedData, (size_t) other.size)); | |||
else | |||
packedData.allocatedData = static_cast<uint8*> (std::malloc ((size_t) other.size)); | |||
memcpy (packedData.allocatedData, other.packedData.allocatedData, (size_t) other.size); | |||
} | |||
else | |||
{ | |||
allocatedData.free(); | |||
preallocatedData.asInt32 = other.preallocatedData.asInt32; | |||
if (isHeapAllocated()) | |||
std::free (packedData.allocatedData); | |||
packedData.allocatedData = other.packedData.allocatedData; | |||
} | |||
timeStamp = other.timeStamp; | |||
size = other.size; | |||
} | |||
return *this; | |||
@@ -277,36 +283,36 @@ MidiMessage& MidiMessage::operator= (const MidiMessage& other) | |||
MidiMessage::MidiMessage (MidiMessage&& other) noexcept | |||
: timeStamp (other.timeStamp), size (other.size) | |||
{ | |||
if (other.allocatedData != nullptr) | |||
allocatedData.swapWith (other.allocatedData); | |||
else | |||
preallocatedData.asInt32 = other.preallocatedData.asInt32; | |||
packedData.allocatedData = other.packedData.allocatedData; | |||
other.size = 0; | |||
} | |||
MidiMessage& MidiMessage::operator= (MidiMessage&& other) noexcept | |||
{ | |||
jassert (this != &other); // shouldn't be possible | |||
packedData.allocatedData = other.packedData.allocatedData; | |||
timeStamp = other.timeStamp; | |||
size = other.size; | |||
allocatedData.swapWith (other.allocatedData); | |||
preallocatedData.asInt32 = other.preallocatedData.asInt32; | |||
other.size = 0; | |||
return *this; | |||
} | |||
#endif | |||
MidiMessage::~MidiMessage() {} | |||
MidiMessage::~MidiMessage() noexcept | |||
{ | |||
if (isHeapAllocated()) | |||
std::free (packedData.allocatedData); | |||
} | |||
uint8* MidiMessage::allocateSpace (int bytes) | |||
{ | |||
if (bytes > 4) | |||
if (bytes > (int) sizeof (packedData)) | |||
{ | |||
allocatedData.malloc ((size_t) bytes); | |||
return allocatedData; | |||
uint8* d = static_cast<uint8*> (std::malloc ((size_t) bytes)); | |||
packedData.allocatedData = d; | |||
return d; | |||
} | |||
return preallocatedData.asBytes; | |||
return packedData.asBytes; | |||
} | |||
String MidiMessage::getDescription() const | |||
@@ -416,7 +422,7 @@ float MidiMessage::getFloatVelocity() const noexcept | |||
void MidiMessage::setVelocity (const float newVelocity) noexcept | |||
{ | |||
if (isNoteOnOrOff()) | |||
getData()[2] = MidiHelpers::floatVelocityToByte (newVelocity); | |||
getData()[2] = floatValueToMidiByte (newVelocity); | |||
} | |||
void MidiMessage::multiplyVelocity (const float scaleFactor) noexcept | |||
@@ -563,7 +569,7 @@ MidiMessage MidiMessage::noteOn (const int channel, const int noteNumber, const | |||
MidiMessage MidiMessage::noteOn (const int channel, const int noteNumber, const float velocity) noexcept | |||
{ | |||
return noteOn (channel, noteNumber, MidiHelpers::floatVelocityToByte (velocity)); | |||
return noteOn (channel, noteNumber, floatValueToMidiByte (velocity)); | |||
} | |||
MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber, uint8 velocity) noexcept | |||
@@ -577,7 +583,7 @@ MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber, uint8 | |||
MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber, float velocity) noexcept | |||
{ | |||
return noteOff (channel, noteNumber, MidiHelpers::floatVelocityToByte (velocity)); | |||
return noteOff (channel, noteNumber, floatValueToMidiByte (velocity)); | |||
} | |||
MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber) noexcept | |||
@@ -722,9 +728,10 @@ MidiMessage MidiMessage::textMetaEvent (int type, StringRef text) | |||
header[--n] = 0xff; | |||
const size_t headerLen = sizeof (header) - n; | |||
const int totalSize = (int) (headerLen + textSize); | |||
uint8* const dest = result.allocateSpace ((int) (headerLen + textSize)); | |||
result.size = (int) (headerLen + textSize); | |||
uint8* const dest = result.allocateSpace (totalSize); | |||
result.size = totalSize; | |||
memcpy (dest, header + n, headerLen); | |||
memcpy (dest + headerLen, text.text.getAddress(), textSize); | |||
@@ -841,7 +848,7 @@ bool MidiMessage::isKeySignatureMetaEvent() const noexcept | |||
int MidiMessage::getKeySignatureNumberOfSharpsOrFlats() const noexcept | |||
{ | |||
return (int) getMetaEventData()[0]; | |||
return (int) (int8) getMetaEventData()[0]; | |||
} | |||
bool MidiMessage::isKeySignatureMajorKey() const noexcept | |||
@@ -1010,7 +1017,7 @@ String MidiMessage::getMidiNoteName (int note, bool useSharps, bool includeOctav | |||
return String(); | |||
} | |||
double MidiMessage::getMidiNoteInHertz (int noteNumber, const double frequencyOfA) noexcept | |||
double MidiMessage::getMidiNoteInHertz (const int noteNumber, const double frequencyOfA) noexcept | |||
{ | |||
return frequencyOfA * pow (2.0, (noteNumber - 69) / 12.0); | |||
} | |||
@@ -104,7 +104,7 @@ public: | |||
MidiMessage (const MidiMessage&, double newTimeStamp); | |||
/** Destructor. */ | |||
~MidiMessage(); | |||
~MidiMessage() noexcept; | |||
/** Copies this message from another one. */ | |||
MidiMessage& operator= (const MidiMessage& other); | |||
@@ -118,7 +118,7 @@ public: | |||
/** Returns a pointer to the raw midi data. | |||
@see getRawDataSize | |||
*/ | |||
const uint8* getRawData() const noexcept { return allocatedData != nullptr ? allocatedData.getData() : preallocatedData.asBytes; } | |||
const uint8* getRawData() const noexcept { return getData(); } | |||
/** Returns the number of bytes of data in the message. | |||
@see getRawData | |||
@@ -851,7 +851,7 @@ public: | |||
The value passed in must be 0x80 or higher. | |||
*/ | |||
static int getMessageLengthFromFirstByte (const uint8 firstByte) noexcept; | |||
static int getMessageLengthFromFirstByte (uint8 firstByte) noexcept; | |||
//============================================================================== | |||
/** Returns the name of a midi note number. | |||
@@ -878,7 +878,7 @@ public: | |||
The frequencyOfA parameter is an optional frequency for 'A', normally 440-444Hz for concert pitch. | |||
@see getMidiNoteName | |||
*/ | |||
static double getMidiNoteInHertz (int noteNumber, const double frequencyOfA = 440.0) noexcept; | |||
static double getMidiNoteInHertz (int noteNumber, double frequencyOfA = 440.0) noexcept; | |||
/** Returns true if the given midi note number is a black key. */ | |||
static bool isMidiNoteBlack (int noteNumber) noexcept; | |||
@@ -905,21 +905,29 @@ public: | |||
*/ | |||
static const char* getControllerName (int controllerNumber); | |||
/** Converts a floating-point value between 0 and 1 to a MIDI 7-bit value between 0 and 127. */ | |||
static uint8 floatValueToMidiByte (float valueBetween0and1) noexcept; | |||
/** Converts a pitchbend value in semitones to a MIDI 14-bit pitchwheel position value. */ | |||
static uint16 pitchbendToPitchwheelPos (float pitchbendInSemitones, | |||
float pitchbendRangeInSemitones) noexcept; | |||
private: | |||
//============================================================================== | |||
double timeStamp; | |||
HeapBlock<uint8> allocatedData; | |||
int size; | |||
#ifndef DOXYGEN | |||
union | |||
union PackedData | |||
{ | |||
uint8 asBytes[4]; | |||
uint32 asInt32; | |||
} preallocatedData; | |||
uint8* allocatedData; | |||
uint8 asBytes[sizeof (uint8*)]; | |||
}; | |||
PackedData packedData; | |||
double timeStamp; | |||
int size; | |||
#endif | |||
inline uint8* getData() noexcept { return allocatedData != nullptr ? allocatedData.getData() : preallocatedData.asBytes; } | |||
inline bool isHeapAllocated() const noexcept { return size > (int) sizeof (packedData); } | |||
inline uint8* getData() const noexcept { return isHeapAllocated() ? packedData.allocatedData : (uint8*) packedData.asBytes; } | |||
uint8* allocateSpace (int); | |||
}; | |||
@@ -80,7 +80,7 @@ int MidiMessageSequence::getIndexOfMatchingKeyUp (const int index) const noexcep | |||
return -1; | |||
} | |||
int MidiMessageSequence::getIndexOf (MidiEventHolder* const event) const noexcept | |||
int MidiMessageSequence::getIndexOf (const MidiEventHolder* const event) const noexcept | |||
{ | |||
return list.indexOf (event); | |||
} | |||
@@ -48,6 +48,18 @@ public: | |||
/** Replaces this sequence with another one. */ | |||
MidiMessageSequence& operator= (const MidiMessageSequence&); | |||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
MidiMessageSequence (MidiMessageSequence&& other) noexcept | |||
: list (static_cast<OwnedArray<MidiEventHolder>&&> (other.list)) | |||
{} | |||
MidiMessageSequence& operator= (MidiMessageSequence&& other) noexcept | |||
{ | |||
list = static_cast<OwnedArray<MidiEventHolder>&&> (other.list); | |||
return *this; | |||
} | |||
#endif | |||
/** Destructor. */ | |||
~MidiMessageSequence(); | |||
@@ -109,7 +121,7 @@ public: | |||
int getIndexOfMatchingKeyUp (int index) const noexcept; | |||
/** Returns the index of an event. */ | |||
int getIndexOf (MidiEventHolder* event) const noexcept; | |||
int getIndexOf (const MidiEventHolder* event) const noexcept; | |||
/** Returns the index of the first event on or after the given timestamp. | |||
If the time is beyond the end of the sequence, this will return the | |||
@@ -22,7 +22,6 @@ | |||
============================================================================== | |||
*/ | |||
MidiRPNDetector::MidiRPNDetector() noexcept | |||
{ | |||
} | |||
@@ -55,7 +54,7 @@ void MidiRPNDetector::reset() noexcept | |||
} | |||
//============================================================================== | |||
MidiRPNDetector::ChannelState::ChannelState () noexcept | |||
MidiRPNDetector::ChannelState::ChannelState() noexcept | |||
: parameterMSB (0xff), parameterLSB (0xff), valueMSB (0xff), valueLSB (0xff), isNRPN (false) | |||
{ | |||
} | |||
@@ -39,6 +39,9 @@ MPEInstrument::MPEInstrument() noexcept | |||
pressureDimension.value = &MPENote::pressure; | |||
timbreDimension.value = &MPENote::timbre; | |||
// the default value for pressure is 0, for all other dimension it is centre (= default MPEValue) | |||
std::fill_n (pressureDimension.lastValueReceivedOnChannel, 16, MPEValue::minValue()); | |||
legacyMode.isEnabled = false; | |||
legacyMode.pitchbendRange = 2; | |||
legacyMode.channelRange = Range<int> (1, 17); | |||
@@ -271,22 +274,6 @@ void MPEInstrument::handleTimbreLSB (int midiChannel, int value) noexcept | |||
lastTimbreLowerBitReceivedOnChannel[midiChannel - 1] = uint8 (value); | |||
} | |||
//============================================================================== | |||
MPEValue MPEInstrument::getInitialPitchbendForNoteOn (int midiChannel, int /*midiNoteNumber*/, MPEValue /*midiNoteOnVelocity*/) const | |||
{ | |||
return pitchbendDimension.lastValueReceivedOnChannel[midiChannel - 1]; | |||
} | |||
MPEValue MPEInstrument::getInitialPressureForNoteOn (int /*midiChannel*/, int /*midiNoteNumber*/, MPEValue midiNoteOnVelocity) const | |||
{ | |||
return midiNoteOnVelocity; | |||
} | |||
MPEValue MPEInstrument::getInitialTimbreForNoteOn (int midiChannel, int /*midiNoteNumber*/, MPEValue /*midiNoteOnVelocity*/) const | |||
{ | |||
return timbreDimension.lastValueReceivedOnChannel[midiChannel - 1]; | |||
} | |||
//============================================================================== | |||
void MPEInstrument::noteOn (int midiChannel, | |||
int midiNoteNumber, | |||
@@ -298,9 +285,9 @@ void MPEInstrument::noteOn (int midiChannel, | |||
MPENote newNote (midiChannel, | |||
midiNoteNumber, | |||
midiNoteOnVelocity, | |||
getInitialPitchbendForNoteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity), | |||
getInitialPressureForNoteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity), | |||
getInitialTimbreForNoteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity), | |||
getInitialValueForNewNote (midiChannel, pitchbendDimension), | |||
getInitialValueForNewNote (midiChannel, pressureDimension), | |||
getInitialValueForNewNote (midiChannel, timbreDimension), | |||
isNoteChannelSustained[midiChannel - 1] ? MPENote::keyDownAndSustained : MPENote::keyDown); | |||
const ScopedLock sl (lock); | |||
@@ -334,10 +321,11 @@ void MPEInstrument::noteOff (int midiChannel, | |||
note->keyState = (note->keyState == MPENote::keyDownAndSustained) ? MPENote::sustained : MPENote::off; | |||
note->noteOffVelocity = midiNoteOffVelocity; | |||
// last pitchbend and timbre values received for this note should not be re-used for | |||
// last dimension values received for this note should not be re-used for | |||
// any new notes, so reset them: | |||
pitchbendDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue(); | |||
timbreDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue(); | |||
pressureDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::minValue(); | |||
pitchbendDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::centreValue(); | |||
timbreDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::centreValue(); | |||
if (note->keyState == MPENote::off) | |||
{ | |||
@@ -370,6 +358,14 @@ void MPEInstrument::timbre (int midiChannel, MPEValue value) | |||
updateDimension (midiChannel, timbreDimension, value); | |||
} | |||
MPEValue MPEInstrument::getInitialValueForNewNote (int midiChannel, MPEDimension& dimension) const | |||
{ | |||
if (getLastNotePlayedPtr (midiChannel) != nullptr) | |||
return &dimension == &pressureDimension ? MPEValue::minValue() : MPEValue::centreValue(); | |||
return dimension.lastValueReceivedOnChannel[midiChannel - 1]; | |||
} | |||
//============================================================================== | |||
void MPEInstrument::updateDimension (int midiChannel, MPEDimension& dimension, MPEValue value) | |||
{ | |||
@@ -758,7 +754,7 @@ public: | |||
test.noteOn (3, 60, MPEValue::from7BitInt (100)); | |||
expectEquals (test.getNumPlayingNotes(), 1); | |||
expectEquals (test.noteAddedCallCounter, 1); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
// note-off | |||
test.noteOff (3, 60, MPEValue::from7BitInt (33)); | |||
@@ -774,13 +770,13 @@ public: | |||
// note off with non-matching note number shouldn't do anything | |||
test.noteOff (3, 61, MPEValue::from7BitInt (33)); | |||
expectEquals (test.getNumPlayingNotes(), 1); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.noteReleasedCallCounter, 0); | |||
// note off with non-matching midi channel shouldn't do anything | |||
test.noteOff (2, 60, MPEValue::from7BitInt (33)); | |||
expectEquals (test.getNumPlayingNotes(), 1); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.noteReleasedCallCounter, 0); | |||
} | |||
{ | |||
@@ -791,9 +787,9 @@ public: | |||
test.noteOn (3, 1, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 2, MPEValue::from7BitInt (100)); | |||
expectEquals (test.getNumPlayingNotes(), 3); | |||
expectNote (test.getNote (3, 0), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 1), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 2), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 0), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 1), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 2), 100, 0, 8192, 64, MPENote::keyDown); | |||
} | |||
{ | |||
// pathological case: second note-on for same note should retrigger it. | |||
@@ -802,7 +798,7 @@ public: | |||
test.noteOn (3, 0, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 0, MPEValue::from7BitInt (60)); | |||
expectEquals (test.getNumPlayingNotes(), 1); | |||
expectNote (test.getNote (3, 0), 60, 60, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 0), 60, 0, 8192, 64, MPENote::keyDown); | |||
} | |||
} | |||
@@ -844,42 +840,42 @@ public: | |||
// sustain pedal on per-note channel shouldn't do anything. | |||
test.sustainPedal (3, true); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.noteKeyStateChangedCallCounter, 0); | |||
// sustain pedal on non-zone channel shouldn't do anything either. | |||
test.sustainPedal (1, true); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.noteKeyStateChangedCallCounter, 0); | |||
// sustain pedal on master channel should sustain notes on *that* zone. | |||
test.sustainPedal (2, true); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDownAndSustained); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDownAndSustained); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.noteKeyStateChangedCallCounter, 1); | |||
// release | |||
test.sustainPedal (2, false); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.noteKeyStateChangedCallCounter, 2); | |||
// should also sustain new notes added after the press | |||
test.sustainPedal (2, true); | |||
expectEquals (test.noteKeyStateChangedCallCounter, 3); | |||
test.noteOn (4, 51, MPEValue::from7BitInt (100)); | |||
expectNote (test.getNote (4, 51), 100, 100, 8192, 64, MPENote::keyDownAndSustained); | |||
expectNote (test.getNote (4, 51), 100, 0, 8192, 64, MPENote::keyDownAndSustained); | |||
expectEquals (test.noteKeyStateChangedCallCounter, 3); | |||
// ...but only if that sustain came on the master channel of that zone! | |||
test.sustainPedal (11, true); | |||
test.noteOn (11, 52, MPEValue::from7BitInt (100)); | |||
expectNote (test.getNote (11, 52), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (11, 52), 100, 0, 8192, 64, MPENote::keyDown); | |||
test.noteOff (11, 52, MPEValue::from7BitInt (100)); | |||
expectEquals (test.noteReleasedCallCounter, 1); | |||
@@ -890,8 +886,8 @@ public: | |||
expectEquals (test.getNumPlayingNotes(), 2); | |||
expectEquals (test.noteReleasedCallCounter, 2); | |||
expectEquals (test.noteKeyStateChangedCallCounter, 5); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::sustained); | |||
expectNote (test.getNote (4, 51), 100, 100, 8192, 64, MPENote::sustained); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::sustained); | |||
expectNote (test.getNote (4, 51), 100, 0, 8192, 64, MPENote::sustained); | |||
// notes should be turned off when pedal is released | |||
test.sustainPedal (2, false); | |||
@@ -908,26 +904,26 @@ public: | |||
// sostenuto pedal on per-note channel shouldn't do anything. | |||
test.sostenutoPedal (3, true); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.noteKeyStateChangedCallCounter, 0); | |||
// sostenuto pedal on non-zone channel shouldn't do anything either. | |||
test.sostenutoPedal (1, true); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.noteKeyStateChangedCallCounter, 0); | |||
// sostenuto pedal on master channel should sustain notes on *that* zone. | |||
test.sostenutoPedal (2, true); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDownAndSustained); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDownAndSustained); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.noteKeyStateChangedCallCounter, 1); | |||
// release | |||
test.sostenutoPedal (2, false); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.noteKeyStateChangedCallCounter, 2); | |||
// should only sustain notes turned on *before* the press (difference to sustain pedal) | |||
@@ -935,9 +931,9 @@ public: | |||
expectEquals (test.noteKeyStateChangedCallCounter, 3); | |||
test.noteOn (4, 51, MPEValue::from7BitInt (100)); | |||
expectEquals (test.getNumPlayingNotes(), 3); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDownAndSustained); | |||
expectNote (test.getNote (4, 51), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDownAndSustained); | |||
expectNote (test.getNote (4, 51), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.noteKeyStateChangedCallCounter, 3); | |||
// note-off should not turn off sustained notes inside the same zone, | |||
@@ -946,7 +942,7 @@ public: | |||
test.noteOff (4, 51, MPEValue::from7BitInt (100)); | |||
test.noteOff (10, 60, MPEValue::from7BitInt (100)); // not affected! | |||
expectEquals (test.getNumPlayingNotes(), 1); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::sustained); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::sustained); | |||
expectEquals (test.noteReleasedCallCounter, 2); | |||
expectEquals (test.noteKeyStateChangedCallCounter, 4); | |||
@@ -1047,22 +1043,22 @@ public: | |||
// applying pressure on a per-note channel should modulate one note | |||
test.pressure (3, MPEValue::from7BitInt (33)); | |||
expectNote (test.getNote (3, 60), 100, 33, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.notePressureChangedCallCounter, 1); | |||
// applying pressure on a master channel should modulate all notes in this zone | |||
test.pressure (2, MPEValue::from7BitInt (44)); | |||
expectNote (test.getNote (3, 60), 100, 44, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 44, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.notePressureChangedCallCounter, 3); | |||
// applying pressure on an unrelated channel should be ignored | |||
test.pressure (1, MPEValue::from7BitInt (55)); | |||
expectNote (test.getNote (3, 60), 100, 44, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 44, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.notePressureChangedCallCounter, 3); | |||
} | |||
{ | |||
@@ -1073,7 +1069,7 @@ public: | |||
test.noteOn (3, 60, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
test.pressure (3, MPEValue::from7BitInt (66)); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 66, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.notePressureChangedCallCounter, 1); | |||
} | |||
@@ -1091,6 +1087,49 @@ public: | |||
expectNote (test.getNote (3, 60), 100, 77, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.notePressureChangedCallCounter, 1); | |||
} | |||
{ | |||
UnitTestInstrument test; | |||
test.setZoneLayout (testLayout); | |||
// if no pressure is sent before note-on, default = 0 should be used | |||
test.noteOn (3, 60, MPEValue::from7BitInt (100)); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
} | |||
{ | |||
UnitTestInstrument test; | |||
test.setZoneLayout (testLayout); | |||
// if pressure is sent before note-on, use that | |||
test.pressure (3, MPEValue::from7BitInt (77)); | |||
test.noteOn (3, 60, MPEValue::from7BitInt (100)); | |||
expectNote (test.getNote (3, 60), 100, 77, 8192, 64, MPENote::keyDown); | |||
} | |||
{ | |||
UnitTestInstrument test; | |||
test.setZoneLayout (testLayout); | |||
// if pressure is sent before note-on, but it belonged to another note | |||
// on the same channel that has since been turned off, use default = 0 | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
test.pressure (3, MPEValue::from7BitInt (77)); | |||
test.noteOff (3, 61, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 60, MPEValue::from7BitInt (100)); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
} | |||
{ | |||
UnitTestInstrument test; | |||
test.setZoneLayout (testLayout); | |||
// edge case: two notes on the same channel simultaneously. the second one should use | |||
// pressure = 0 initially but then react to additional pressure messages | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
test.pressure (3, MPEValue::from7BitInt (77)); | |||
test.noteOn (3, 60, MPEValue::from7BitInt (100)); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
test.pressure (3, MPEValue::from7BitInt (78)); | |||
expectNote (test.getNote (3, 60), 100, 78, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 77, 8192, 64, MPENote::keyDown); | |||
} | |||
} | |||
beginTest ("pitchbend"); | |||
@@ -1105,9 +1144,9 @@ public: | |||
// applying pitchbend on a per-note channel should modulate one note | |||
test.pitchbend (3, MPEValue::from14BitInt (1111)); | |||
expectNote (test.getNote (3, 60), 100, 100, 1111, 64, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 1111, 64, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.notePitchbendChangedCallCounter, 1); | |||
// applying pitchbend on a master channel should be ignored for the | |||
@@ -1115,16 +1154,16 @@ public: | |||
// Note: noteChanged will be called anyway for notes in that zone | |||
// because the total pitchbend for those notes has changed | |||
test.pitchbend (2, MPEValue::from14BitInt (2222)); | |||
expectNote (test.getNote (3, 60), 100, 100, 1111, 64, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 1111, 64, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.notePitchbendChangedCallCounter, 3); | |||
// applying pitchbend on an unrelated channel should do nothing. | |||
test.pitchbend (1, MPEValue::from14BitInt (3333)); | |||
expectNote (test.getNote (3, 60), 100, 100, 1111, 64, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 1111, 64, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.notePitchbendChangedCallCounter, 3); | |||
} | |||
{ | |||
@@ -1135,8 +1174,8 @@ public: | |||
test.noteOn (3, 60, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
test.pitchbend (3, MPEValue::from14BitInt (4444)); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 100, 4444, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 0, 4444, 64, MPENote::keyDown); | |||
expectEquals (test.notePitchbendChangedCallCounter, 1); | |||
} | |||
{ | |||
@@ -1150,7 +1189,7 @@ public: | |||
test.noteOff (3, 61, MPEValue::from7BitInt (100)); | |||
test.pitchbend (3, MPEValue::from14BitInt (5555)); | |||
expectEquals (test.getNumPlayingNotes(), 1); | |||
expectNote (test.getNote (3, 60), 100, 100, 5555, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 5555, 64, MPENote::keyDown); | |||
expectEquals (test.notePitchbendChangedCallCounter, 1); | |||
} | |||
{ | |||
@@ -1169,14 +1208,14 @@ public: | |||
test.sustainPedal (2, true); | |||
test.noteOff (3, 60, MPEValue::from7BitInt (64)); | |||
expectEquals (test.getNumPlayingNotes(), 1); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::sustained); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::sustained); | |||
expectEquals (test.noteKeyStateChangedCallCounter, 2); | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
test.pitchbend (3, MPEValue::from14BitInt (6666)); | |||
expectEquals (test.getNumPlayingNotes(), 2); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::sustained); | |||
expectNote (test.getNote (3, 61), 100, 100, 6666, 64, MPENote::keyDownAndSustained); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::sustained); | |||
expectNote (test.getNote (3, 61), 100, 0, 6666, 64, MPENote::keyDownAndSustained); | |||
expectEquals (test.notePitchbendChangedCallCounter, 1); | |||
} | |||
{ | |||
@@ -1193,11 +1232,11 @@ public: | |||
test.noteOn (3, 60, MPEValue::from7BitInt (100)); | |||
test.pitchbend (3, MPEValue::from14BitInt (5555)); | |||
expectNote (test.getNote (3, 60), 100, 100, 5555, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 5555, 64, MPENote::keyDown); | |||
test.noteOff (3, 60, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 60, MPEValue::from7BitInt (100)); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
} | |||
{ | |||
// applying per-note pitchbend should set the note's totalPitchbendInSemitones | |||
@@ -1289,23 +1328,23 @@ public: | |||
// modulating timbre on a per-note channel should modulate one note | |||
test.timbre (3, MPEValue::from7BitInt (33)); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 33, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 33, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.noteTimbreChangedCallCounter, 1); | |||
// modulating timbre on a master channel should modulate all notes in this zone | |||
test.timbre (2, MPEValue::from7BitInt (44)); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 44, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 100, 8192, 44, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 44, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 0, 8192, 44, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.noteTimbreChangedCallCounter, 3); | |||
// modulating timbre on an unrelated channel should be ignored | |||
test.timbre (1, MPEValue::from7BitInt (55)); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 44, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 100, 8192, 44, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 44, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 0, 8192, 44, MPENote::keyDown); | |||
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.noteTimbreChangedCallCounter, 3); | |||
} | |||
{ | |||
@@ -1316,8 +1355,8 @@ public: | |||
test.noteOn (3, 60, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
test.timbre (3, MPEValue::from7BitInt (66)); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 100, 8192, 66, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 0, 8192, 66, MPENote::keyDown); | |||
expectEquals (test.noteTimbreChangedCallCounter, 1); | |||
} | |||
{ | |||
@@ -1331,7 +1370,7 @@ public: | |||
test.noteOff (3, 61, MPEValue::from7BitInt (100)); | |||
test.timbre (3, MPEValue::from7BitInt (77)); | |||
expectEquals (test.getNumPlayingNotes(), 1); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 77, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 77, MPENote::keyDown); | |||
expectEquals (test.noteTimbreChangedCallCounter, 1); | |||
} | |||
{ | |||
@@ -1341,11 +1380,11 @@ public: | |||
// Zsolt's edge case for timbre | |||
test.noteOn (3, 60, MPEValue::from7BitInt (100)); | |||
test.timbre (3, MPEValue::from7BitInt (42)); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 42, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 42, MPENote::keyDown); | |||
test.noteOff (3, 60, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 60, MPEValue::from7BitInt (100)); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
} | |||
} | |||
@@ -1361,8 +1400,8 @@ public: | |||
test.noteOn (3, 62, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
test.pressure (3, MPEValue::from7BitInt (99)); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 99, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.notePressureChangedCallCounter, 1); | |||
} | |||
@@ -1377,8 +1416,8 @@ public: | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
test.pressure (3, MPEValue::from7BitInt (99)); | |||
expectNote (test.getNote (3, 60), 100, 99, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.notePressureChangedCallCounter, 1); | |||
} | |||
{ | |||
@@ -1391,9 +1430,9 @@ public: | |||
test.noteOn (3, 62, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
test.pressure (3, MPEValue::from7BitInt (99)); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 99, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.notePressureChangedCallCounter, 1); | |||
} | |||
{ | |||
@@ -1425,9 +1464,9 @@ public: | |||
test.noteOn (3, 62, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
test.pitchbend (3, MPEValue::from14BitInt (9999)); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 100, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 0, 9999, 64, MPENote::keyDown); | |||
expectEquals (test.notePitchbendChangedCallCounter, 1); | |||
} | |||
{ | |||
@@ -1440,9 +1479,9 @@ public: | |||
test.noteOn (3, 62, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
test.pitchbend (3, MPEValue::from14BitInt (9999)); | |||
expectNote (test.getNote (3, 60), 100, 100, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.notePitchbendChangedCallCounter, 1); | |||
} | |||
{ | |||
@@ -1455,9 +1494,9 @@ public: | |||
test.noteOn (3, 62, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
test.pitchbend (3, MPEValue::from14BitInt (9999)); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 100, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 0, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.notePitchbendChangedCallCounter, 1); | |||
} | |||
{ | |||
@@ -1470,9 +1509,9 @@ public: | |||
test.noteOn (3, 62, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
test.pitchbend (3, MPEValue::from14BitInt (9999)); | |||
expectNote (test.getNote (3, 60), 100, 100, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 100, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 100, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 0, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 0, 9999, 64, MPENote::keyDown); | |||
expectEquals (test.notePitchbendChangedCallCounter, 3); | |||
} | |||
} | |||
@@ -1489,9 +1528,9 @@ public: | |||
test.noteOn (3, 62, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
test.timbre (3, MPEValue::from7BitInt (99)); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 100, 8192, 99, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 0, 8192, 99, MPENote::keyDown); | |||
expectEquals (test.noteTimbreChangedCallCounter, 1); | |||
} | |||
{ | |||
@@ -1504,9 +1543,9 @@ public: | |||
test.noteOn (3, 62, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
test.timbre (3, MPEValue::from7BitInt (99)); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 99, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 99, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.noteTimbreChangedCallCounter, 1); | |||
} | |||
{ | |||
@@ -1519,9 +1558,9 @@ public: | |||
test.noteOn (3, 62, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
test.timbre (3, MPEValue::from7BitInt (99)); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 100, 8192, 99, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 0, 8192, 99, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectEquals (test.noteTimbreChangedCallCounter, 1); | |||
} | |||
{ | |||
@@ -1534,9 +1573,9 @@ public: | |||
test.noteOn (3, 62, MPEValue::from7BitInt (100)); | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
test.timbre (3, MPEValue::from7BitInt (99)); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 99, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 100, 8192, 99, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 100, 8192, 99, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 99, MPENote::keyDown); | |||
expectNote (test.getNote (3, 62), 100, 0, 8192, 99, MPENote::keyDown); | |||
expectNote (test.getNote (3, 61), 100, 0, 8192, 99, MPENote::keyDown); | |||
expectEquals (test.noteTimbreChangedCallCounter, 3); | |||
} | |||
} | |||
@@ -1724,7 +1763,7 @@ public: | |||
expectEquals (test.getNumPlayingNotes(), 0); | |||
} | |||
beginTest ("default getInitial...ForNoteOn"); | |||
beginTest ("default initial values for pitchbend and timbre"); | |||
{ | |||
MPEInstrument test; | |||
test.setZoneLayout (testLayout); | |||
@@ -1739,16 +1778,7 @@ public: | |||
test.noteOn (3, 60, MPEValue::from7BitInt (100)); | |||
expectNote (test.getMostRecentNote (3), 100, 100, 3333, 66, MPENote::keyDown); | |||
} | |||
beginTest ("overriding getInitial...ForNoteOn"); | |||
{ | |||
CustomInitialValuesTest<33, 4444, 55> test; | |||
test.setZoneLayout (testLayout); | |||
test.noteOn (3, 61, MPEValue::from7BitInt (100)); | |||
expectNote (test.getMostRecentNote (3), 100, 33, 4444, 55, MPENote::keyDown); | |||
expectNote (test.getMostRecentNote (3), 100, 0, 3333, 66, MPENote::keyDown); | |||
} | |||
beginTest ("Legacy mode"); | |||
@@ -1806,10 +1836,10 @@ public: | |||
test.pressure (2, MPEValue::from7BitInt (88)); | |||
test.timbre (15, MPEValue::from7BitInt (77)); | |||
expectNote (test.getNote (1, 60), 100, 100, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 60), 100, 0, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (2, 60), 100, 88, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (15, 60), 100, 100, 8192, 77, MPENote::keyDown); | |||
expectNote (test.getNote (16, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (15, 60), 100, 0, 8192, 77, MPENote::keyDown); | |||
expectNote (test.getNote (16, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
// note off should work in legacy mode | |||
@@ -1835,10 +1865,10 @@ public: | |||
test.noteOn (16, 60, MPEValue::from7BitInt (100)); | |||
expectEquals (test.getNumPlayingNotes(), 4); | |||
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (6, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (7, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (6, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (7, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
} | |||
{ | |||
// tracking mode in legacy mode | |||
@@ -1851,9 +1881,9 @@ public: | |||
test.noteOn (1, 62, MPEValue::from7BitInt (100)); | |||
test.noteOn (1, 61, MPEValue::from7BitInt (100)); | |||
test.pitchbend (1, MPEValue::from14BitInt (9999)); | |||
expectNote (test.getNote (1, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 61), 100, 100, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 62), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 61), 100, 0, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 62), 100, 0, 8192, 64, MPENote::keyDown); | |||
} | |||
{ | |||
UnitTestInstrument test; | |||
@@ -1864,9 +1894,9 @@ public: | |||
test.noteOn (1, 62, MPEValue::from7BitInt (100)); | |||
test.noteOn (1, 61, MPEValue::from7BitInt (100)); | |||
test.pitchbend (1, MPEValue::from14BitInt (9999)); | |||
expectNote (test.getNote (1, 60), 100, 100, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 61), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 62), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 60), 100, 0, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 61), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 62), 100, 0, 8192, 64, MPENote::keyDown); | |||
} | |||
{ | |||
UnitTestInstrument test; | |||
@@ -1877,9 +1907,9 @@ public: | |||
test.noteOn (1, 62, MPEValue::from7BitInt (100)); | |||
test.noteOn (1, 61, MPEValue::from7BitInt (100)); | |||
test.pitchbend (1, MPEValue::from14BitInt (9999)); | |||
expectNote (test.getNote (1, 60), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 61), 100, 100, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 62), 100, 100, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 60), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 61), 100, 0, 8192, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 62), 100, 0, 9999, 64, MPENote::keyDown); | |||
} | |||
{ | |||
UnitTestInstrument test; | |||
@@ -1890,9 +1920,9 @@ public: | |||
test.noteOn (1, 62, MPEValue::from7BitInt (100)); | |||
test.noteOn (1, 61, MPEValue::from7BitInt (100)); | |||
test.pitchbend (1, MPEValue::from14BitInt (9999)); | |||
expectNote (test.getNote (1, 60), 100, 100, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 61), 100, 100, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 62), 100, 100, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 60), 100, 0, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 61), 100, 0, 9999, 64, MPENote::keyDown); | |||
expectNote (test.getNote (1, 62), 100, 0, 9999, 64, MPENote::keyDown); | |||
} | |||
} | |||
{ | |||
@@ -1916,7 +1946,7 @@ public: | |||
test.noteOff (1, 60, MPEValue::from7BitInt (100)); | |||
expectEquals (test.getNumPlayingNotes(), 1); | |||
expectNote (test.getNote (1, 60), 100, 100, 8192, 64, MPENote::sustained); | |||
expectNote (test.getNote (1, 60), 100, 0, 8192, 64, MPENote::sustained); | |||
test.sustainPedal (1, false); | |||
expectEquals (test.getNumPlayingNotes(), 0); | |||
@@ -1939,7 +1969,7 @@ public: | |||
test.noteOff (2, 61, MPEValue::from7BitInt (100)); | |||
expectEquals (test.getNumPlayingNotes(), 1); | |||
expectNote (test.getNote (1, 60), 100, 100, 8192, 64, MPENote::sustained); | |||
expectNote (test.getNote (1, 60), 100, 0, 8192, 64, MPENote::sustained); | |||
test.sostenutoPedal (1, false); | |||
expectEquals (test.getNumPlayingNotes(), 0); | |||
@@ -2081,26 +2111,6 @@ private: | |||
} | |||
}; | |||
//============================================================================== | |||
template <int initial7BitPressure, int initial14BitPitchbend, int initial7BitTimbre> | |||
class CustomInitialValuesTest : public MPEInstrument | |||
{ | |||
MPEValue getInitialPitchbendForNoteOn (int, int, MPEValue) const override | |||
{ | |||
return MPEValue::from14BitInt (initial14BitPitchbend); | |||
} | |||
MPEValue getInitialPressureForNoteOn (int, int, MPEValue) const override | |||
{ | |||
return MPEValue::from7BitInt (initial7BitPressure); | |||
} | |||
MPEValue getInitialTimbreForNoteOn (int, int, MPEValue) const override | |||
{ | |||
return MPEValue::from7BitInt (initial7BitTimbre); | |||
} | |||
}; | |||
//============================================================================== | |||
void expectNote (MPENote noteToTest, | |||
int noteOnVelocity7Bit, | |||
@@ -320,36 +320,6 @@ public: | |||
/** Re-sets the pitchbend range in semitones (0-96) to be used for notes when in legacy mode. */ | |||
void setLegacyModePitchbendRange (int pitchbendRange); | |||
protected: | |||
//============================================================================== | |||
/** This method defines what initial pitchbend value should be used for newly | |||
triggered notes. The default is to use the last pitchbend value | |||
that has been received on the same MIDI channel (or no pitchbend | |||
if no pitchbend messages have been received so far). | |||
Override this method if you need different behaviour. | |||
*/ | |||
virtual MPEValue getInitialPitchbendForNoteOn (int midiChannel, | |||
int midiNoteNumber, | |||
MPEValue midiNoteOnVelocity) const; | |||
/** This method defines what initial pressure value should be used for newly | |||
triggered notes. The default is to re-use the note-on velocity value. | |||
Override this method if you need different behaviour. | |||
*/ | |||
virtual MPEValue getInitialPressureForNoteOn (int midiChannel, | |||
int midiNoteNumber, | |||
MPEValue midiNoteOnVelocity) const; | |||
/** This method defines what initial timbre value should be used for newly | |||
triggered notes. The default is to use the last timbre value that has | |||
that has been received on the same MIDI channel (or a neutral centred value | |||
if no pitchbend messages have been received so far). | |||
Override this method if you need different behaviour. | |||
*/ | |||
virtual MPEValue getInitialTimbreForNoteOn (int midiChannel, | |||
int midiNoteNumber, | |||
MPEValue midiNoteOnVelocity) const; | |||
private: | |||
//============================================================================== | |||
CriticalSection lock; | |||
@@ -384,6 +354,7 @@ private: | |||
void updateDimensionMaster (MPEZone&, MPEDimension&, MPEValue); | |||
void updateDimensionForNote (MPENote&, MPEDimension&, MPEValue); | |||
void callListenersDimensionChanged (MPENote&, MPEDimension&); | |||
MPEValue getInitialValueForNewNote (int midiChannel, MPEDimension&) const; | |||
void processMidiNoteOnMessage (const MidiMessage&); | |||
void processMidiNoteOffMessage (const MidiMessage&); | |||
@@ -22,7 +22,6 @@ | |||
============================================================================== | |||
*/ | |||
MidiBuffer MPEMessages::addZone (MPEZone zone) | |||
{ | |||
MidiBuffer buffer (MidiRPNGenerator::generate (zone.getFirstNoteChannel(), | |||
@@ -25,7 +25,8 @@ | |||
MPESynthesiserBase::MPESynthesiserBase() | |||
: instrument (new MPEInstrument), | |||
sampleRate (0), | |||
minimumSubBlockSize (32) | |||
minimumSubBlockSize (32), | |||
subBlockSubdivisionIsStrict (false) | |||
{ | |||
instrument->addListener (this); | |||
} | |||
@@ -100,6 +101,7 @@ void MPESynthesiserBase::renderNextBlock (AudioBuffer<floatType>& outputAudio, | |||
MidiBuffer::Iterator midiIterator (inputMidi); | |||
midiIterator.setNextSamplePosition (startSample); | |||
bool firstEvent = true; | |||
int midiEventPos; | |||
MidiMessage m; | |||
@@ -122,12 +124,14 @@ void MPESynthesiserBase::renderNextBlock (AudioBuffer<floatType>& outputAudio, | |||
break; | |||
} | |||
if (samplesToNextMidiMessage < minimumSubBlockSize) | |||
if (samplesToNextMidiMessage < ((firstEvent && ! subBlockSubdivisionIsStrict) ? 1 : minimumSubBlockSize)) | |||
{ | |||
handleMidiEvent (m); | |||
continue; | |||
} | |||
firstEvent = false; | |||
renderNextSubBlock (outputAudio, startSample, samplesToNextMidiMessage); | |||
handleMidiEvent (m); | |||
startSample += samplesToNextMidiMessage; | |||
@@ -154,8 +158,9 @@ void MPESynthesiserBase::setCurrentPlaybackSampleRate (const double newRate) | |||
} | |||
//============================================================================== | |||
void MPESynthesiserBase::setMinimumRenderingSubdivisionSize (int numSamples) noexcept | |||
void MPESynthesiserBase::setMinimumRenderingSubdivisionSize (int numSamples, bool shouldBeStrict) noexcept | |||
{ | |||
jassert (numSamples > 0); // it wouldn't make much sense for this to be less than 1 | |||
minimumSubBlockSize = numSamples; | |||
subBlockSubdivisionIsStrict = shouldBeStrict; | |||
} |
@@ -127,8 +127,14 @@ public: | |||
The default setting is 32, which means that midi messages are accurate to about < 1ms | |||
accuracy, which is probably fine for most purposes, but you may want to increase or | |||
decrease this value for your synth. | |||
If shouldBeStrict is true, the audio sub-blocks will strictly never be smaller than numSamples. | |||
If shouldBeStrict is false (default), the first audio sub-block in the buffer is allowed | |||
to be smaller, to make sure that the first MIDI event in a buffer will always be sample-accurate | |||
(this can sometimes help to avoid quantisation or phasing issues). | |||
*/ | |||
void setMinimumRenderingSubdivisionSize (int numSamples) noexcept; | |||
void setMinimumRenderingSubdivisionSize (int numSamples, bool shouldBeStrict = false) noexcept; | |||
//============================================================================== | |||
/** Puts the synthesiser into legacy mode. | |||
@@ -185,6 +191,7 @@ private: | |||
CriticalSection noteStateLock; | |||
double sampleRate; | |||
int minimumSubBlockSize; | |||
bool subBlockSubdivisionIsStrict; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiserBase) | |||
}; | |||
@@ -69,27 +69,35 @@ struct JUCE_API MPEZone | |||
int perNotePitchbendRange = 48, | |||
int masterPitchbendRange = 2) noexcept; | |||
/* Returns the MIDI master channel of this zone. */ | |||
/* Returns the MIDI master channel number (in the range 1-16) of this zone. */ | |||
int getMasterChannel() const noexcept; | |||
/** Returns the number of note channels occupied by this zone. */ | |||
int getNumNoteChannels() const noexcept; | |||
/* Returns the MIDI channel number of the lowest-numbered note channel of this zone. */ | |||
/* Returns the MIDI channel number (in the range 1-16) of the | |||
lowest-numbered note channel of this zone. | |||
*/ | |||
int getFirstNoteChannel() const noexcept; | |||
/* Returns the MIDI channel number of the highest-numbered note channel of this zone. */ | |||
/* Returns the MIDI channel number (in the range 1-16) of the | |||
highest-numbered note channel of this zone. | |||
*/ | |||
int getLastNoteChannel() const noexcept; | |||
/** Returns the MIDI channel numbers of the note channels of this zone as a Range. */ | |||
/** Returns the MIDI channel numbers (in the range 1-16) of the | |||
note channels of this zone as a Range. | |||
*/ | |||
Range<int> getNoteChannelRange() const noexcept; | |||
/** Returns true if the MIDI channel (in the range 1-16) is used by this zone | |||
either as a note channel or as the master channel; false otherwise. */ | |||
either as a note channel or as the master channel; false otherwise. | |||
*/ | |||
bool isUsingChannel (int channel) const noexcept; | |||
/** Returns true if the MIDI channel (in the range 1-16) is used by this zone | |||
as a note channel; false otherwise. */ | |||
as a note channel; false otherwise. | |||
*/ | |||
bool isUsingChannelAsNoteChannel (int channel) const noexcept; | |||
/** Returns the per-note pitchbend range in semitones set for this zone. */ | |||
@@ -26,7 +26,8 @@ BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* s, | |||
TimeSliceThread& thread, | |||
const bool deleteSourceWhenDeleted, | |||
const int bufferSizeSamples, | |||
const int numChannels) | |||
const int numChannels, | |||
bool prefillBufferOnPrepareToPlay) | |||
: source (s, deleteSourceWhenDeleted), | |||
backgroundThread (thread), | |||
numberOfSamplesToBuffer (jmax (1024, bufferSizeSamples)), | |||
@@ -36,7 +37,8 @@ BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* s, | |||
nextPlayPos (0), | |||
sampleRate (0), | |||
wasSourceLooping (false), | |||
isPrepared (false) | |||
isPrepared (false), | |||
prefillBuffer (prefillBufferOnPrepareToPlay) | |||
{ | |||
jassert (source != nullptr); | |||
@@ -73,12 +75,13 @@ void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double ne | |||
backgroundThread.addTimeSliceClient (this); | |||
while (bufferValidEnd - bufferValidStart < jmin (((int) newSampleRate) / 4, | |||
buffer.getNumSamples() / 2)) | |||
do | |||
{ | |||
backgroundThread.moveToFrontOfQueue (this); | |||
Thread::sleep (5); | |||
} | |||
while (prefillBuffer | |||
&& (bufferValidEnd - bufferValidStart < jmin (((int) newSampleRate) / 4, buffer.getNumSamples() / 2))); | |||
} | |||
} | |||
@@ -88,7 +91,12 @@ void BufferingAudioSource::releaseResources() | |||
backgroundThread.removeTimeSliceClient (this); | |||
buffer.setSize (numberOfChannels, 0); | |||
source->releaseResources(); | |||
// MSVC2015 seems to need this if statement to not generate a warning during linking. | |||
// As source is set in the constructor, there is no way that source could | |||
// ever equal this, but it seems to make MSVC2015 happy. | |||
if (source != this) | |||
source->releaseResources(); | |||
} | |||
void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) | |||
@@ -148,6 +156,41 @@ void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info | |||
} | |||
} | |||
bool BufferingAudioSource::waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, const uint32 timeout) | |||
{ | |||
if (!source || source->getTotalLength() <= 0) | |||
return false; | |||
if (nextPlayPos + info.numSamples < 0) | |||
return true; | |||
if (! isLooping() && nextPlayPos > getTotalLength()) | |||
return true; | |||
const uint32 endTime = Time::getMillisecondCounter() + timeout; | |||
uint32 now = Time::getMillisecondCounter(); | |||
while (now < endTime) | |||
{ | |||
{ | |||
const ScopedLock sl (bufferStartPosLock); | |||
const int validStart = static_cast<int> (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos); | |||
const int validEnd = static_cast<int> (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos); | |||
if (validStart <= 0 && validStart < validEnd && validEnd >= info.numSamples) | |||
return true; | |||
} | |||
if (! bufferReadyEvent.wait (static_cast<int> (endTime - now))) | |||
return false; | |||
now = Time::getMillisecondCounter(); | |||
} | |||
return false; | |||
} | |||
int64 BufferingAudioSource::getNextReadPosition() const | |||
{ | |||
jassert (source->getTotalLength() > 0); | |||
@@ -241,6 +284,8 @@ bool BufferingAudioSource::readNextBufferChunk() | |||
bufferValidEnd = newBVE; | |||
} | |||
bufferReadyEvent.signal(); | |||
return true; | |||
} | |||
@@ -43,21 +43,24 @@ public: | |||
//============================================================================== | |||
/** Creates a BufferingAudioSource. | |||
@param source the input source to read from | |||
@param backgroundThread a background thread that will be used for the | |||
background read-ahead. This object must not be deleted | |||
until after any BufferingAudioSources that are using it | |||
have been deleted! | |||
@param deleteSourceWhenDeleted if true, then the input source object will | |||
be deleted when this object is deleted | |||
@param numberOfSamplesToBuffer the size of buffer to use for reading ahead | |||
@param numberOfChannels the number of channels that will be played | |||
@param source the input source to read from | |||
@param backgroundThread a background thread that will be used for the | |||
background read-ahead. This object must not be deleted | |||
until after any BufferingAudioSources that are using it | |||
have been deleted! | |||
@param deleteSourceWhenDeleted if true, then the input source object will | |||
be deleted when this object is deleted | |||
@param numberOfSamplesToBuffer the size of buffer to use for reading ahead | |||
@param numberOfChannels the number of channels that will be played | |||
@param prefillBufferOnPrepareToPlay if true, then calling prepareToPlay on this object will | |||
block until the buffer has been filled | |||
*/ | |||
BufferingAudioSource (PositionableAudioSource* source, | |||
TimeSliceThread& backgroundThread, | |||
bool deleteSourceWhenDeleted, | |||
int numberOfSamplesToBuffer, | |||
int numberOfChannels = 2); | |||
int numberOfChannels = 2, | |||
bool prefillBufferOnPrepareToPlay = true); | |||
/** Destructor. | |||
@@ -89,6 +92,12 @@ public: | |||
/** Implements the PositionableAudioSource method. */ | |||
bool isLooping() const override { return source->isLooping(); } | |||
/** A useful function to block until the next the buffer info can be filled. | |||
This is useful for offline rendering. | |||
*/ | |||
bool waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, const uint32 timeout); | |||
private: | |||
//============================================================================== | |||
OptionalScopedPointer<PositionableAudioSource> source; | |||
@@ -96,9 +105,10 @@ private: | |||
int numberOfSamplesToBuffer, numberOfChannels; | |||
AudioSampleBuffer buffer; | |||
CriticalSection bufferStartPosLock; | |||
WaitableEvent bufferReadyEvent; | |||
int64 volatile bufferValidStart, bufferValidEnd, nextPlayPos; | |||
double volatile sampleRate; | |||
bool wasSourceLooping, isPrepared; | |||
bool wasSourceLooping, isPrepared, prefillBuffer; | |||
bool readNextBufferChunk(); | |||
void readBufferSection (int64 start, int length, int bufferOffset); | |||
@@ -88,6 +88,7 @@ Synthesiser::Synthesiser() | |||
: sampleRate (0), | |||
lastNoteOnCounter (0), | |||
minimumSubBlockSize (32), | |||
subBlockSubdivisionIsStrict (false), | |||
shouldStealNotes (true) | |||
{ | |||
for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i) | |||
@@ -147,10 +148,11 @@ void Synthesiser::setNoteStealingEnabled (const bool shouldSteal) | |||
shouldStealNotes = shouldSteal; | |||
} | |||
void Synthesiser::setMinimumRenderingSubdivisionSize (int numSamples) noexcept | |||
void Synthesiser::setMinimumRenderingSubdivisionSize (int numSamples, bool shouldBeStrict) noexcept | |||
{ | |||
jassert (numSamples > 0); // it wouldn't make much sense for this to be less than 1 | |||
minimumSubBlockSize = numSamples; | |||
subBlockSubdivisionIsStrict = shouldBeStrict; | |||
} | |||
//============================================================================== | |||
@@ -177,10 +179,12 @@ void Synthesiser::processNextBlock (AudioBuffer<floatType>& outputAudio, | |||
{ | |||
// must set the sample rate before using this! | |||
jassert (sampleRate != 0); | |||
const int targetChannels = outputAudio.getNumChannels(); | |||
MidiBuffer::Iterator midiIterator (midiData); | |||
midiIterator.setNextSamplePosition (startSample); | |||
bool firstEvent = true; | |||
int midiEventPos; | |||
MidiMessage m; | |||
@@ -190,7 +194,9 @@ void Synthesiser::processNextBlock (AudioBuffer<floatType>& outputAudio, | |||
{ | |||
if (! midiIterator.getNextEvent (m, midiEventPos)) | |||
{ | |||
renderVoices (outputAudio, startSample, numSamples); | |||
if (targetChannels > 0) | |||
renderVoices (outputAudio, startSample, numSamples); | |||
return; | |||
} | |||
@@ -198,18 +204,24 @@ void Synthesiser::processNextBlock (AudioBuffer<floatType>& outputAudio, | |||
if (samplesToNextMidiMessage >= numSamples) | |||
{ | |||
renderVoices (outputAudio, startSample, numSamples); | |||
if (targetChannels > 0) | |||
renderVoices (outputAudio, startSample, numSamples); | |||
handleMidiEvent (m); | |||
break; | |||
} | |||
if (samplesToNextMidiMessage < minimumSubBlockSize) | |||
if (samplesToNextMidiMessage < ((firstEvent && ! subBlockSubdivisionIsStrict) ? 1 : minimumSubBlockSize)) | |||
{ | |||
handleMidiEvent (m); | |||
continue; | |||
} | |||
renderVoices (outputAudio, startSample, samplesToNextMidiMessage); | |||
firstEvent = false; | |||
if (targetChannels > 0) | |||
renderVoices (outputAudio, startSample, samplesToNextMidiMessage); | |||
handleMidiEvent (m); | |||
startSample += samplesToNextMidiMessage; | |||
numSamples -= samplesToNextMidiMessage; | |||
@@ -539,8 +539,14 @@ public: | |||
The default setting is 32, which means that midi messages are accurate to about < 1ms | |||
accuracy, which is probably fine for most purposes, but you may want to increase or | |||
decrease this value for your synth. | |||
If shouldBeStrict is true, the audio sub-blocks will strictly never be smaller than numSamples. | |||
If shouldBeStrict is false (default), the first audio sub-block in the buffer is allowed | |||
to be smaller, to make sure that the first MIDI event in a buffer will always be sample-accurate | |||
(this can sometimes help to avoid quantisation or phasing issues). | |||
*/ | |||
void setMinimumRenderingSubdivisionSize (int numSamples) noexcept; | |||
void setMinimumRenderingSubdivisionSize (int numSamples, bool shouldBeStrict = false) noexcept; | |||
protected: | |||
//============================================================================== | |||
@@ -615,6 +621,7 @@ private: | |||
double sampleRate; | |||
uint32 lastNoteOnCounter; | |||
int minimumSubBlockSize; | |||
bool subBlockSubdivisionIsStrict; | |||
bool shouldStealNotes; | |||
BigInteger sustainPedalsDown; | |||
@@ -86,72 +86,12 @@ private: | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHandler) | |||
}; | |||
//============================================================================== | |||
// This is an AudioTransportSource which will own it's assigned source | |||
struct AudioSourceOwningTransportSource : public AudioTransportSource | |||
{ | |||
AudioSourceOwningTransportSource (PositionableAudioSource* s) : source (s) | |||
{ | |||
AudioTransportSource::setSource (s); | |||
} | |||
~AudioSourceOwningTransportSource() | |||
{ | |||
setSource (nullptr); | |||
} | |||
private: | |||
ScopedPointer<PositionableAudioSource> source; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSourceOwningTransportSource) | |||
}; | |||
//============================================================================== | |||
// An AudioSourcePlayer which will remove itself from the AudioDeviceManager's | |||
// callback list once it finishes playing its source | |||
struct AutoRemovingSourcePlayer : public AudioSourcePlayer, | |||
private Timer | |||
{ | |||
AutoRemovingSourcePlayer (AudioDeviceManager& dm, AudioTransportSource* ts, bool ownSource) | |||
: manager (dm), transportSource (ts, ownSource) | |||
{ | |||
jassert (ts != nullptr); | |||
manager.addAudioCallback (this); | |||
AudioSourcePlayer::setSource (transportSource); | |||
startTimerHz (10); | |||
} | |||
~AutoRemovingSourcePlayer() | |||
{ | |||
setSource (nullptr); | |||
manager.removeAudioCallback (this); | |||
} | |||
void timerCallback() override | |||
{ | |||
if (getCurrentSource() == nullptr || ! transportSource->isPlaying()) | |||
delete this; | |||
} | |||
void audioDeviceStopped() override | |||
{ | |||
AudioSourcePlayer::audioDeviceStopped(); | |||
setSource (nullptr); | |||
} | |||
private: | |||
AudioDeviceManager& manager; | |||
OptionalScopedPointer<AudioTransportSource> transportSource; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AutoRemovingSourcePlayer) | |||
}; | |||
//============================================================================== | |||
AudioDeviceManager::AudioDeviceManager() | |||
: numInputChansNeeded (0), | |||
numOutputChansNeeded (2), | |||
listNeedsScanning (true), | |||
inputLevel (0), | |||
testSoundPosition (0), | |||
cpuUsageMs (0), | |||
timeToCpuScale (0) | |||
{ | |||
@@ -162,10 +102,6 @@ AudioDeviceManager::~AudioDeviceManager() | |||
{ | |||
currentAudioDevice = nullptr; | |||
defaultMidiOutput = nullptr; | |||
for (int i = 0; i < callbacks.size(); ++i) | |||
if (AutoRemovingSourcePlayer* p = dynamic_cast<AutoRemovingSourcePlayer*> (callbacks.getUnchecked(i))) | |||
delete p; | |||
} | |||
//============================================================================== | |||
@@ -346,8 +282,8 @@ String AudioDeviceManager::initialiseFromXML (const XmlElement& xml, | |||
currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName(); | |||
} | |||
setup.bufferSize = xml.getIntAttribute ("audioDeviceBufferSize"); | |||
setup.sampleRate = xml.getDoubleAttribute ("audioDeviceRate"); | |||
setup.bufferSize = xml.getIntAttribute ("audioDeviceBufferSize", setup.bufferSize); | |||
setup.sampleRate = xml.getDoubleAttribute ("audioDeviceRate", setup.sampleRate); | |||
setup.inputChannels .parseString (xml.getStringAttribute ("audioDeviceInChans", "11"), 2); | |||
setup.outputChannels.parseString (xml.getStringAttribute ("audioDeviceOutChans", "11"), 2); | |||
@@ -650,6 +586,8 @@ void AudioDeviceManager::stopDevice() | |||
{ | |||
if (currentAudioDevice != nullptr) | |||
currentAudioDevice->stop(); | |||
testSound = nullptr; | |||
} | |||
void AudioDeviceManager::closeAudioDevice() | |||
@@ -761,31 +699,8 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat | |||
{ | |||
const ScopedLock sl (audioCallbackLock); | |||
if (inputLevelMeasurementEnabledCount.get() > 0 && numInputChannels > 0) | |||
{ | |||
for (int j = 0; j < numSamples; ++j) | |||
{ | |||
float s = 0; | |||
for (int i = 0; i < numInputChannels; ++i) | |||
s += std::abs (inputChannelData[i][j]); | |||
s /= numInputChannels; | |||
const double decayFactor = 0.99992; | |||
if (s > inputLevel) | |||
inputLevel = s; | |||
else if (inputLevel > 0.001f) | |||
inputLevel *= decayFactor; | |||
else | |||
inputLevel = 0; | |||
} | |||
} | |||
else | |||
{ | |||
inputLevel = 0; | |||
} | |||
inputLevelMeter.updateLevel (inputChannelData, numInputChannels, numSamples); | |||
outputLevelMeter.updateLevel (const_cast<const float**> (outputChannelData), numOutputChannels, numSamples); | |||
if (callbacks.size() > 0) | |||
{ | |||
@@ -821,6 +736,20 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat | |||
for (int i = 0; i < numOutputChannels; ++i) | |||
zeromem (outputChannelData[i], sizeof (float) * (size_t) numSamples); | |||
} | |||
if (testSound != nullptr) | |||
{ | |||
const int numSamps = jmin (numSamples, testSound->getNumSamples() - testSoundPosition); | |||
const float* const src = testSound->getReadPointer (0, testSoundPosition); | |||
for (int i = 0; i < numOutputChannels; ++i) | |||
for (int j = 0; j < numSamps; ++j) | |||
outputChannelData [i][j] += src[j]; | |||
testSoundPosition += numSamps; | |||
if (testSoundPosition >= testSound->getNumSamples()) | |||
testSound = nullptr; | |||
} | |||
} | |||
void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device) | |||
@@ -989,170 +918,87 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) | |||
} | |||
//============================================================================== | |||
// An AudioSource which simply outputs a buffer | |||
class AudioSampleBufferSource : public PositionableAudioSource | |||
{ | |||
public: | |||
AudioSampleBufferSource (AudioSampleBuffer* audioBuffer, bool ownBuffer, bool playOnAllChannels) | |||
: buffer (audioBuffer, ownBuffer), | |||
position (0), looping (false), playAcrossAllChannels (playOnAllChannels) | |||
{} | |||
AudioDeviceManager::LevelMeter::LevelMeter() noexcept : level() {} | |||
//============================================================================== | |||
void setNextReadPosition (int64 newPosition) override | |||
{ | |||
jassert (newPosition >= 0); | |||
if (looping) | |||
newPosition = newPosition % static_cast<int64> (buffer->getNumSamples()); | |||
position = jmin (buffer->getNumSamples(), static_cast<int> (newPosition)); | |||
} | |||
int64 getNextReadPosition() const override { return static_cast<int64> (position); } | |||
int64 getTotalLength() const override { return static_cast<int64> (buffer->getNumSamples()); } | |||
bool isLooping() const override { return looping; } | |||
void setLooping (bool shouldLoop) override { looping = shouldLoop; } | |||
//============================================================================== | |||
void prepareToPlay (int, double) override {} | |||
void releaseResources() override {} | |||
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override | |||
void AudioDeviceManager::LevelMeter::updateLevel (const float* const* channelData, int numChannels, int numSamples) noexcept | |||
{ | |||
if (enabled.get() != 0 && numChannels > 0) | |||
{ | |||
bufferToFill.clearActiveBufferRegion(); | |||
const int bufferSize = buffer->getNumSamples(); | |||
const int samplesNeeded = bufferToFill.numSamples; | |||
const int samplesToCopy = jmin (bufferSize - position, samplesNeeded); | |||
if (samplesToCopy > 0) | |||
for (int j = 0; j < numSamples; ++j) | |||
{ | |||
int maxInChannels = buffer->getNumChannels(); | |||
int maxOutChannels = bufferToFill.buffer->getNumChannels(); | |||
if (! playAcrossAllChannels) | |||
maxOutChannels = jmin (maxOutChannels, maxInChannels); | |||
for (int i = 0; i < maxOutChannels; ++i) | |||
bufferToFill.buffer->copyFrom (i, bufferToFill.startSample, *buffer, | |||
i % maxInChannels, position, samplesToCopy); | |||
} | |||
position += samplesNeeded; | |||
float s = 0; | |||
if (looping) | |||
position %= bufferSize; | |||
} | |||
for (int i = 0; i < numChannels; ++i) | |||
s += std::abs (channelData[i][j]); | |||
private: | |||
//============================================================================== | |||
OptionalScopedPointer<AudioSampleBuffer> buffer; | |||
int position; | |||
bool looping, playAcrossAllChannels; | |||
s /= numChannels; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSampleBufferSource) | |||
}; | |||
void AudioDeviceManager::playSound (const File& file) | |||
{ | |||
if (file.existsAsFile()) | |||
{ | |||
AudioFormatManager formatManager; | |||
const double decayFactor = 0.99992; | |||
formatManager.registerBasicFormats(); | |||
playSound (formatManager.createReaderFor (file), true); | |||
if (s > level) | |||
level = s; | |||
else if (level > 0.001f) | |||
level *= decayFactor; | |||
else | |||
level = 0; | |||
} | |||
} | |||
} | |||
void AudioDeviceManager::playSound (const void* resourceData, size_t resourceSize) | |||
{ | |||
if (resourceData != nullptr && resourceSize > 0) | |||
else | |||
{ | |||
AudioFormatManager formatManager; | |||
formatManager.registerBasicFormats(); | |||
MemoryInputStream* mem = new MemoryInputStream (resourceData, resourceSize, false); | |||
playSound (formatManager.createReaderFor (mem), true); | |||
level = 0; | |||
} | |||
} | |||
void AudioDeviceManager::playSound (AudioFormatReader* reader, bool deleteWhenFinished) | |||
void AudioDeviceManager::LevelMeter::setEnabled (bool shouldBeEnabled) noexcept | |||
{ | |||
if (reader != nullptr) | |||
playSound (new AudioFormatReaderSource (reader, deleteWhenFinished), true); | |||
enabled.set (shouldBeEnabled ? 1 : 0); | |||
level = 0; | |||
} | |||
void AudioDeviceManager::playSound (AudioSampleBuffer* buffer, bool deleteWhenFinished, bool playOnAllOutputChannels) | |||
double AudioDeviceManager::LevelMeter::getCurrentLevel() const noexcept | |||
{ | |||
if (buffer != nullptr) | |||
playSound (new AudioSampleBufferSource (buffer, deleteWhenFinished, playOnAllOutputChannels), true); | |||
jassert (enabled.get() != 0); // you need to call setEnabled (true) before using this! | |||
return level; | |||
} | |||
void AudioDeviceManager::playSound (PositionableAudioSource* audioSource, bool deleteWhenFinished) | |||
void AudioDeviceManager::playTestSound() | |||
{ | |||
if (audioSource != nullptr && currentAudioDevice != nullptr) | |||
{ | |||
AudioTransportSource* transport = dynamic_cast<AudioTransportSource*> (audioSource); | |||
{ // cunningly nested to swap, unlock and delete in that order. | |||
ScopedPointer<AudioSampleBuffer> oldSound; | |||
if (transport == nullptr) | |||
{ | |||
if (deleteWhenFinished) | |||
{ | |||
transport = new AudioSourceOwningTransportSource (audioSource); | |||
} | |||
else | |||
{ | |||
transport = new AudioTransportSource(); | |||
transport->setSource (audioSource); | |||
deleteWhenFinished = true; | |||
} | |||
const ScopedLock sl (audioCallbackLock); | |||
oldSound = testSound; | |||
} | |||
transport->start(); | |||
new AutoRemovingSourcePlayer (*this, transport, deleteWhenFinished); | |||
} | |||
else | |||
{ | |||
if (deleteWhenFinished) | |||
delete audioSource; | |||
} | |||
} | |||
void AudioDeviceManager::playTestSound() | |||
{ | |||
const double sampleRate = currentAudioDevice->getCurrentSampleRate(); | |||
const int soundLength = (int) sampleRate; | |||
testSoundPosition = 0; | |||
const double frequency = 440.0; | |||
const float amplitude = 0.5f; | |||
const double phasePerSample = double_Pi * 2.0 / (sampleRate / frequency); | |||
if (currentAudioDevice != nullptr) | |||
{ | |||
const double sampleRate = currentAudioDevice->getCurrentSampleRate(); | |||
const int soundLength = (int) sampleRate; | |||
AudioSampleBuffer* newSound = new AudioSampleBuffer (1, soundLength); | |||
const double frequency = 440.0; | |||
const float amplitude = 0.5f; | |||
for (int i = 0; i < soundLength; ++i) | |||
newSound->setSample (0, i, amplitude * (float) std::sin (i * phasePerSample)); | |||
const double phasePerSample = double_Pi * 2.0 / (sampleRate / frequency); | |||
newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f); | |||
newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f); | |||
AudioSampleBuffer* const newSound = new AudioSampleBuffer (1, soundLength); | |||
playSound (newSound, true, true); | |||
} | |||
for (int i = 0; i < soundLength; ++i) | |||
newSound->setSample (0, i, amplitude * (float) std::sin (i * phasePerSample)); | |||
//============================================================================== | |||
void AudioDeviceManager::enableInputLevelMeasurement (const bool enableMeasurement) | |||
{ | |||
if (enableMeasurement) | |||
++inputLevelMeasurementEnabledCount; | |||
else | |||
--inputLevelMeasurementEnabledCount; | |||
newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f); | |||
newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f); | |||
inputLevel = 0; | |||
const ScopedLock sl (audioCallbackLock); | |||
testSound = newSound; | |||
} | |||
} | |||
double AudioDeviceManager::getCurrentInputLevel() const | |||
{ | |||
jassert (inputLevelMeasurementEnabledCount.get() > 0); // you need to call enableInputLevelMeasurement() before using this! | |||
return inputLevel; | |||
} | |||
double AudioDeviceManager::getCurrentInputLevel() const noexcept { return inputLevelMeter.getCurrentLevel(); } | |||
double AudioDeviceManager::getCurrentOutputLevel() const noexcept { return outputLevelMeter.getCurrentLevel(); } | |||
void AudioDeviceManager::enableInputLevelMeasurement (bool enable) noexcept { inputLevelMeter.setEnabled (enable); } | |||
void AudioDeviceManager::enableOutputLevelMeasurement (bool enable) noexcept { outputLevelMeter.setEnabled (enable); } |
@@ -404,78 +404,28 @@ public: | |||
*/ | |||
void playTestSound(); | |||
/** Plays a sound from a file. */ | |||
void playSound (const File& file); | |||
/** Convenient method to play sound from a JUCE resource. */ | |||
void playSound (const void* resourceData, size_t resourceSize); | |||
/** Plays the sound from an audio format reader. | |||
If deleteWhenFinished is true then the format reader will be | |||
automatically deleted once the sound has finished playing. | |||
*/ | |||
void playSound (AudioFormatReader* buffer, bool deleteWhenFinished = false); | |||
/** Plays the sound from a positionable audio source. | |||
This will output the sound coming from a positionable audio source. | |||
This gives you slightly more control over the sound playback compared | |||
to the other playSound methods. For example, if you would like to | |||
stop the sound prematurely you can call this method with a | |||
TransportAudioSource and then call audioSource->stop. Note that, | |||
you must call audioSource->start to start the playback, if your | |||
audioSource is a TransportAudioSource. | |||
The audio device manager will not hold any references to this audio | |||
source once the audio source has stopped playing for any reason, | |||
for example when the sound has finished playing or when you have | |||
called audioSource->stop. Therefore, calling audioSource->start() on | |||
a finished audioSource will not restart the sound again. If this is | |||
desired simply call playSound with the same audioSource again. | |||
@param audioSource the audio source to play | |||
@param deleteWhenFinished If this is true then the audio source will | |||
be deleted once the device manager has finished playing. | |||
*/ | |||
void playSound (PositionableAudioSource* audioSource, bool deleteWhenFinished = false); | |||
/** Plays the sound from an audio sample buffer. | |||
This will output the sound contained in an audio sample buffer. If | |||
deleteWhenFinished is true then the audio sample buffer will be | |||
automatically deleted once the sound has finished playing. | |||
If playOnAllOutputChannels is true, then if there are more output channels | |||
than buffer channels, then the ones that are available will be re-used on | |||
multiple outputs so that something is sent to all output channels. If it | |||
is false, then the buffer will just be played on the first output channels. | |||
*/ | |||
void playSound (AudioSampleBuffer* buffer, | |||
bool deleteWhenFinished = false, | |||
bool playOnAllOutputChannels = false); | |||
//============================================================================== | |||
/** Turns on level-measuring. | |||
When enabled, the device manager will measure the peak input level | |||
across all channels, and you can get this level by calling getCurrentInputLevel(). | |||
This is mainly intended for audio setup UI panels to use to create a mic | |||
level display, so that the user can check that they've selected the right | |||
device. | |||
/** Turns on level-measuring for input channels. | |||
@see getCurrentInputLevel() | |||
*/ | |||
void enableInputLevelMeasurement (bool enableMeasurement) noexcept; | |||
A simple filter is used to make the level decay smoothly, but this is | |||
only intended for giving rough feedback, and not for any kind of accurate | |||
measurement. | |||
/** Turns on level-measuring for output channels. | |||
@see getCurrentOutputLevel() | |||
*/ | |||
void enableInputLevelMeasurement (bool enableMeasurement); | |||
void enableOutputLevelMeasurement (bool enableMeasurement) noexcept; | |||
/** Returns the current input level. | |||
To use this, you must first enable it by calling enableInputLevelMeasurement(). | |||
See enableInputLevelMeasurement() for more info. | |||
@see enableInputLevelMeasurement() | |||
*/ | |||
double getCurrentInputLevel() const; | |||
double getCurrentInputLevel() const noexcept; | |||
/** Returns the current output level. | |||
To use this, you must first enable it by calling enableOutputLevelMeasurement(). | |||
@see enableOutputLevelMeasurement() | |||
*/ | |||
double getCurrentOutputLevel() const noexcept; | |||
/** Returns the a lock that can be used to synchronise access to the audio callback. | |||
Obviously while this is locked, you're blocking the audio thread from running, so | |||
@@ -502,8 +452,6 @@ private: | |||
BigInteger inputChannels, outputChannels; | |||
ScopedPointer<XmlElement> lastExplicitSettings; | |||
mutable bool listNeedsScanning; | |||
Atomic<int> inputLevelMeasurementEnabledCount; | |||
double inputLevel; | |||
AudioSampleBuffer tempBuffer; | |||
struct MidiCallbackInfo | |||
@@ -520,8 +468,24 @@ private: | |||
ScopedPointer<MidiOutput> defaultMidiOutput; | |||
CriticalSection audioCallbackLock, midiCallbackLock; | |||
ScopedPointer<AudioSampleBuffer> testSound; | |||
int testSoundPosition; | |||
double cpuUsageMs, timeToCpuScale; | |||
struct LevelMeter | |||
{ | |||
LevelMeter() noexcept; | |||
void updateLevel (const float* const*, int numChannels, int numSamples) noexcept; | |||
void setEnabled (bool) noexcept; | |||
double getCurrentLevel() const noexcept; | |||
Atomic<int> enabled; | |||
double level; | |||
}; | |||
LevelMeter inputLevelMeter, outputLevelMeter; | |||
//============================================================================== | |||
class CallbackHandler; | |||
friend class CallbackHandler; | |||
@@ -31,6 +31,8 @@ | |||
#error "Incorrect use of JUCE cpp file" | |||
#endif | |||
#include "AppConfig.h" | |||
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1 | |||
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1 | |||
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1 | |||
@@ -45,7 +47,6 @@ | |||
#define Component CarbonDummyCompName | |||
#import <CoreAudio/AudioHardware.h> | |||
#import <CoreMIDI/MIDIServices.h> | |||
#import <DiscRecording/DiscRecording.h> | |||
#import <AudioToolbox/AudioServices.h> | |||
#undef Point | |||
#undef Component | |||
@@ -55,10 +56,14 @@ | |||
#import <AVFoundation/AVFoundation.h> | |||
#import <CoreMIDI/MIDIServices.h> | |||
#if TARGET_OS_SIMULATOR | |||
#import <CoreMIDI/MIDINetworkSession.h> | |||
#endif | |||
//============================================================================== | |||
#elif JUCE_WINDOWS | |||
#if JUCE_WASAPI | |||
#include <MMReg.h> | |||
#include <mmreg.h> | |||
#endif | |||
#if JUCE_ASIO | |||
@@ -84,15 +89,6 @@ | |||
#include <iasiodrv.h> | |||
#endif | |||
#if JUCE_USE_CDBURNER | |||
/* You'll need the Platform SDK for these headers - if you don't have it and don't | |||
need to use CD-burning, then you might just want to set the JUCE_USE_CDBURNER flag | |||
to 0, to avoid these includes. | |||
*/ | |||
#include <imapi.h> | |||
#include <imapierror.h> | |||
#endif | |||
//============================================================================== | |||
#elif JUCE_LINUX | |||
#if JUCE_ALSA | |||
@@ -139,7 +135,6 @@ namespace juce | |||
#include "audio_io/juce_AudioIODeviceType.cpp" | |||
#include "midi_io/juce_MidiMessageCollector.cpp" | |||
#include "midi_io/juce_MidiOutput.cpp" | |||
#include "audio_cd/juce_AudioCDReader.cpp" | |||
#include "sources/juce_AudioSourcePlayer.cpp" | |||
#include "sources/juce_AudioTransportSource.cpp" | |||
#include "native/juce_MidiDataConcatenator.h" | |||
@@ -149,14 +144,6 @@ namespace juce | |||
#include "native/juce_mac_CoreAudio.cpp" | |||
#include "native/juce_mac_CoreMidi.cpp" | |||
#if JUCE_USE_CDREADER | |||
#include "native/juce_mac_AudioCDReader.mm" | |||
#endif | |||
#if JUCE_USE_CDBURNER | |||
#include "native/juce_mac_AudioCDBurner.mm" | |||
#endif | |||
//============================================================================== | |||
#elif JUCE_IOS | |||
#include "native/juce_ios_Audio.cpp" | |||
@@ -179,14 +166,6 @@ namespace juce | |||
#include "native/juce_win32_ASIO.cpp" | |||
#endif | |||
#if JUCE_USE_CDREADER | |||
#include "native/juce_win32_AudioCDReader.cpp" | |||
#endif | |||
#if JUCE_USE_CDBURNER | |||
#include "native/juce_win32_AudioCDBurner.cpp" | |||
#endif | |||
//============================================================================== | |||
#elif JUCE_LINUX | |||
#if JUCE_ALSA | |||
@@ -199,10 +178,6 @@ namespace juce | |||
#include "native/juce_linux_JackAudio.cpp" | |||
#endif | |||
#if JUCE_USE_CDREADER | |||
#include "native/juce_linux_AudioCDReader.cpp" | |||
#endif | |||
//============================================================================== | |||
#elif JUCE_ANDROID | |||
#include "native/juce_android_Audio.cpp" | |||
@@ -22,12 +22,39 @@ | |||
============================================================================== | |||
*/ | |||
/******************************************************************************* | |||
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.txt file. | |||
BEGIN_JUCE_MODULE_DECLARATION | |||
ID: juce_audio_devices | |||
vendor: juce | |||
version: 4.3.0 | |||
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: GPL/Commercial | |||
dependencies: juce_audio_basics, juce_events | |||
OSXFrameworks: CoreAudio CoreMIDI AudioToolbox | |||
iOSFrameworks: CoreAudio CoreMIDI AudioToolbox AVFoundation | |||
linuxPackages: alsa | |||
mingwLibs: winmm | |||
END_JUCE_MODULE_DECLARATION | |||
*******************************************************************************/ | |||
#ifndef JUCE_AUDIO_DEVICES_H_INCLUDED | |||
#define JUCE_AUDIO_DEVICES_H_INCLUDED | |||
#include "../juce_events/juce_events.h" | |||
#include "../juce_audio_basics/juce_audio_basics.h" | |||
#include "../juce_audio_formats/juce_audio_formats.h" | |||
#include <juce_events/juce_events.h> | |||
#include <juce_audio_basics/juce_audio_basics.h> | |||
//============================================================================== | |||
/** Config: JUCE_ASIO | |||
@@ -90,21 +117,6 @@ | |||
#endif | |||
#endif | |||
//============================================================================== | |||
/** Config: JUCE_USE_CDREADER | |||
Enables the AudioCDReader class (on supported platforms). | |||
*/ | |||
#ifndef JUCE_USE_CDREADER | |||
#define JUCE_USE_CDREADER 0 | |||
#endif | |||
/** Config: JUCE_USE_CDBURNER | |||
Enables the AudioCDBurner class (on supported platforms). | |||
*/ | |||
#ifndef JUCE_USE_CDBURNER | |||
#define JUCE_USE_CDBURNER 0 | |||
#endif | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
@@ -117,8 +129,6 @@ namespace juce | |||
#include "midi_io/juce_MidiOutput.h" | |||
#include "sources/juce_AudioSourcePlayer.h" | |||
#include "sources/juce_AudioTransportSource.h" | |||
#include "audio_cd/juce_AudioCDBurner.h" | |||
#include "audio_cd/juce_AudioCDReader.h" | |||
#include "audio_io/juce_AudioDeviceManager.h" | |||
} | |||
@@ -71,8 +71,7 @@ public: | |||
// the normal message, handle it now.. | |||
if (*d >= 0xf8 && *d <= 0xfe) | |||
{ | |||
const MidiMessage m (*d++, time); | |||
callback.handleIncomingMidiMessage (input, m); | |||
callback.handleIncomingMidiMessage (input, MidiMessage (*d++, time)); | |||
--numBytes; | |||
} | |||
else | |||
@@ -83,7 +82,15 @@ public: | |||
data[len++] = *d++; | |||
--numBytes; | |||
if (len >= MidiMessage::getMessageLengthFromFirstByte (data[0])) | |||
const uint8 firstByte = data[0]; | |||
if (firstByte < 0x80 || firstByte == 0xf7) | |||
{ | |||
len = 0; | |||
break; // ignore this malformed MIDI message.. | |||
} | |||
if (len >= MidiMessage::getMessageLengthFromFirstByte (firstByte)) | |||
break; | |||
} | |||
} | |||
@@ -153,7 +153,7 @@ JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024JuceM | |||
class AndroidMidiDeviceManager | |||
{ | |||
public: | |||
AndroidMidiDeviceManager () | |||
AndroidMidiDeviceManager() | |||
: deviceManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidMidiDeviceManager)) | |||
{ | |||
} | |||
@@ -332,7 +332,7 @@ private: | |||
void run() override | |||
{ | |||
setThreadToAudioPriority (); | |||
setThreadToAudioPriority(); | |||
if (recorder != nullptr) recorder->start(); | |||
if (player != nullptr) player->start(); | |||
@@ -341,7 +341,7 @@ private: | |||
processBuffers(); | |||
} | |||
void setThreadToAudioPriority () | |||
void setThreadToAudioPriority() | |||
{ | |||
// see android.os.Process.THREAD_PRIORITY_AUDIO | |||
const int THREAD_PRIORITY_AUDIO = -16; | |||
@@ -287,7 +287,14 @@ public: | |||
return r; | |||
} | |||
int getDefaultBufferSize() override { return 256; } | |||
int getDefaultBufferSize() override | |||
{ | |||
#if TARGET_IPHONE_SIMULATOR | |||
return 512; | |||
#else | |||
return 256; | |||
#endif | |||
} | |||
String open (const BigInteger& inputChannelsWanted, | |||
const BigInteger& outputChannelsWanted, | |||
@@ -431,6 +438,8 @@ public: | |||
void handleStatusChange (bool enabled, const char* reason) | |||
{ | |||
const ScopedLock myScopedLock (callbackLock); | |||
JUCE_IOS_AUDIO_LOG ("handleStatusChange: enabled: " << (int) enabled << ", reason: " << reason); | |||
isRunning = enabled; | |||
@@ -447,6 +456,8 @@ public: | |||
void handleRouteChange (const char* reason) | |||
{ | |||
const ScopedLock myScopedLock (callbackLock); | |||
JUCE_IOS_AUDIO_LOG ("handleRouteChange: reason: " << reason); | |||
fixAudioRouteIfSetToReceiver(); | |||
@@ -518,9 +529,9 @@ private: | |||
if (audioInputIsAvailable && numInputChannels > 0) | |||
err = AudioUnitRender (audioUnit, flags, time, 1, numFrames, data); | |||
const ScopedLock sl (callbackLock); | |||
const ScopedTryLock stl (callbackLock); | |||
if (callback != nullptr) | |||
if (stl.isLocked() && callback != nullptr) | |||
{ | |||
if ((int) numFrames > floatData.getNumSamples()) | |||
prepareFloatBuffers ((int) numFrames); | |||
@@ -679,7 +690,23 @@ private: | |||
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof (format)); | |||
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, sizeof (format)); | |||
UInt32 framesPerSlice; | |||
UInt32 dataSize = sizeof (framesPerSlice); | |||
AudioUnitInitialize (audioUnit); | |||
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, | |||
kAudioUnitScope_Global, 0, &actualBufferSize, sizeof (actualBufferSize)); | |||
if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, | |||
kAudioUnitScope_Global, 0, &framesPerSlice, &dataSize) == noErr | |||
&& dataSize == sizeof (framesPerSlice) && static_cast<int> (framesPerSlice) != actualBufferSize) | |||
{ | |||
actualBufferSize = static_cast<int> (framesPerSlice); | |||
prepareFloatBuffers (actualBufferSize); | |||
} | |||
return true; | |||
} | |||
@@ -755,8 +782,24 @@ void AudioSessionHolder::handleStatusChange (bool enabled, const char* reason) c | |||
void AudioSessionHolder::handleRouteChange (const char* reason) const | |||
{ | |||
for (auto device: activeDevices) | |||
device->handleRouteChange (reason); | |||
struct RouteChangeMessage : public CallbackMessage | |||
{ | |||
RouteChangeMessage (Array<iOSAudioIODevice*> devs, const char* r) | |||
: devices (devs), changeReason (r) | |||
{ | |||
} | |||
void messageCallback() override | |||
{ | |||
for (auto device: devices) | |||
device->handleRouteChange (changeReason); | |||
} | |||
Array<iOSAudioIODevice*> devices; | |||
const char* changeReason; | |||
}; | |||
(new RouteChangeMessage (activeDevices, reason))->post(); | |||
} | |||
#undef JUCE_NSERROR_CHECK |
@@ -183,7 +183,7 @@ public: | |||
// open output ports | |||
const StringArray outputChannels (getOutputChannelNames()); | |||
for (int i = 0; i < outputChannels.size (); ++i) | |||
for (int i = 0; i < outputChannels.size(); ++i) | |||
{ | |||
String outputName; | |||
outputName << "out_" << ++totalNumberOfOutputChannels; | |||
@@ -290,6 +290,8 @@ public: | |||
} | |||
} | |||
updateActivePorts(); | |||
return lastError; | |||
} | |||
@@ -45,37 +45,17 @@ class AlsaClient : public ReferenceCountedObject | |||
public: | |||
typedef ReferenceCountedObjectPtr<AlsaClient> Ptr; | |||
AlsaClient (bool forInput) | |||
: input (forInput), handle (nullptr) | |||
static Ptr getInstance (bool forInput) | |||
{ | |||
snd_seq_open (&handle, "default", forInput ? SND_SEQ_OPEN_INPUT | |||
: SND_SEQ_OPEN_OUTPUT, 0); | |||
} | |||
~AlsaClient() | |||
{ | |||
if (handle != nullptr) | |||
{ | |||
snd_seq_close (handle); | |||
handle = nullptr; | |||
} | |||
jassert (activeCallbacks.size() == 0); | |||
AlsaClient*& instance = (forInput ? inInstance : outInstance); | |||
if (instance == nullptr) | |||
instance = new AlsaClient (forInput); | |||
if (inputThread) | |||
{ | |||
inputThread->stopThread (3000); | |||
inputThread = nullptr; | |||
} | |||
return instance; | |||
} | |||
bool isInput() const noexcept { return input; } | |||
void setName (const String& name) | |||
{ | |||
snd_seq_set_client_name (handle, name.toUTF8()); | |||
} | |||
void registerCallback (AlsaPortAndCallback* cb) | |||
{ | |||
if (cb != nullptr) | |||
@@ -103,7 +83,8 @@ public: | |||
inputThread->signalThreadShouldExit(); | |||
} | |||
void handleIncomingMidiMessage (const MidiMessage& message, int port); | |||
void handleIncomingMidiMessage (snd_seq_event*, const MidiMessage&); | |||
void handlePartialSysexMessage (snd_seq_event*, const uint8*, int, double); | |||
snd_seq_t* get() const noexcept { return handle; } | |||
@@ -114,12 +95,56 @@ private: | |||
Array<AlsaPortAndCallback*> activeCallbacks; | |||
CriticalSection callbackLock; | |||
static AlsaClient* inInstance; | |||
static AlsaClient* outInstance; | |||
//============================================================================== | |||
friend class ReferenceCountedObjectPtr<AlsaClient>; | |||
friend struct ContainerDeletePolicy<AlsaClient>; | |||
AlsaClient (bool forInput) | |||
: input (forInput), handle (nullptr) | |||
{ | |||
AlsaClient*& instance = (input ? inInstance : outInstance); | |||
jassert (instance == nullptr); | |||
instance = this; | |||
snd_seq_open (&handle, "default", forInput ? SND_SEQ_OPEN_INPUT | |||
: SND_SEQ_OPEN_OUTPUT, 0); | |||
snd_seq_set_client_name (handle, forInput ? JUCE_ALSA_MIDI_INPUT_NAME | |||
: JUCE_ALSA_MIDI_OUTPUT_NAME); | |||
} | |||
~AlsaClient() | |||
{ | |||
AlsaClient*& instance = (input ? inInstance : outInstance); | |||
jassert (instance != nullptr); | |||
instance = nullptr; | |||
if (handle != nullptr) | |||
{ | |||
snd_seq_close (handle); | |||
handle = nullptr; | |||
} | |||
jassert (activeCallbacks.size() == 0); | |||
if (inputThread) | |||
{ | |||
inputThread->stopThread (3000); | |||
inputThread = nullptr; | |||
} | |||
} | |||
//============================================================================== | |||
class MidiInputThread : public Thread | |||
{ | |||
public: | |||
MidiInputThread (AlsaClient& c) | |||
: Thread ("Juce MIDI Input"), client (c) | |||
: Thread ("Juce MIDI Input"), client (c), concatenator (2048) | |||
{ | |||
jassert (client.input && client.get() != nullptr); | |||
} | |||
@@ -159,13 +184,9 @@ private: | |||
snd_midi_event_reset_decode (midiParser); | |||
if (numBytes > 0) | |||
{ | |||
const MidiMessage message ((const uint8*) buffer, (int) numBytes, | |||
Time::getMillisecondCounter() * 0.001); | |||
client.handleIncomingMidiMessage (message, inputEvent->dest.port); | |||
} | |||
concatenator.pushMidiData (buffer, (int) numBytes, | |||
Time::getMillisecondCounter() * 0.001, | |||
inputEvent, client); | |||
snd_seq_free_event (inputEvent); | |||
} | |||
@@ -180,29 +201,14 @@ private: | |||
private: | |||
AlsaClient& client; | |||
MidiDataConcatenator concatenator; | |||
}; | |||
ScopedPointer<MidiInputThread> inputThread; | |||
}; | |||
static AlsaClient::Ptr globalAlsaSequencerIn() | |||
{ | |||
static AlsaClient::Ptr global (new AlsaClient (true)); | |||
return global; | |||
} | |||
static AlsaClient::Ptr globalAlsaSequencerOut() | |||
{ | |||
static AlsaClient::Ptr global (new AlsaClient (false)); | |||
return global; | |||
} | |||
static AlsaClient::Ptr globalAlsaSequencer (bool input) | |||
{ | |||
return input ? globalAlsaSequencerIn() | |||
: globalAlsaSequencerOut(); | |||
} | |||
AlsaClient* AlsaClient::inInstance = nullptr; | |||
AlsaClient* AlsaClient::outInstance = nullptr; | |||
//============================================================================== | |||
// represents an input or output port of the supplied AlsaClient | |||
@@ -282,6 +288,11 @@ public: | |||
callback->handleIncomingMidiMessage (midiInput, message); | |||
} | |||
void handlePartialSysexMessage (const uint8* messageData, int numBytesSoFar, double timeStamp) | |||
{ | |||
callback->handlePartialSysexMessage (midiInput, messageData, numBytesSoFar, timeStamp); | |||
} | |||
private: | |||
AlsaPort port; | |||
MidiInput* midiInput; | |||
@@ -291,14 +302,22 @@ private: | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AlsaPortAndCallback) | |||
}; | |||
void AlsaClient::handleIncomingMidiMessage (const MidiMessage& message, int port) | |||
void AlsaClient::handleIncomingMidiMessage (snd_seq_event_t* event, const MidiMessage& message) | |||
{ | |||
const ScopedLock sl (callbackLock); | |||
if (AlsaPortAndCallback* const cb = activeCallbacks[port]) | |||
if (AlsaPortAndCallback* const cb = activeCallbacks[event->dest.port]) | |||
cb->handleIncomingMidiMessage (message); | |||
} | |||
void AlsaClient::handlePartialSysexMessage (snd_seq_event* event, const uint8* messageData, int numBytesSoFar, double timeStamp) | |||
{ | |||
const ScopedLock sl (callbackLock); | |||
if (AlsaPortAndCallback* const cb = activeCallbacks[event->dest.port]) | |||
cb->handlePartialSysexMessage (messageData, numBytesSoFar, timeStamp); | |||
} | |||
//============================================================================== | |||
static AlsaPort iterateMidiClient (const AlsaClient::Ptr& seq, | |||
snd_seq_client_info_t* clientInfo, | |||
@@ -325,19 +344,23 @@ static AlsaPort iterateMidiClient (const AlsaClient::Ptr& seq, | |||
&& (snd_seq_port_info_get_capability (portInfo) & (forInput ? SND_SEQ_PORT_CAP_READ | |||
: SND_SEQ_PORT_CAP_WRITE)) != 0) | |||
{ | |||
deviceNamesFound.add (snd_seq_client_info_get_name (clientInfo)); | |||
const String clientName = snd_seq_client_info_get_name (clientInfo); | |||
const String portName = snd_seq_port_info_get_name(portInfo); | |||
if (clientName == portName) | |||
deviceNamesFound.add (clientName); | |||
else | |||
deviceNamesFound.add (clientName + ": " + portName); | |||
if (deviceNamesFound.size() == deviceIndexToOpen + 1) | |||
{ | |||
const int sourcePort = snd_seq_port_info_get_port (portInfo); | |||
const int sourceClient = snd_seq_client_info_get_client (clientInfo); | |||
if (sourcePort != -1) | |||
{ | |||
const String name (forInput ? JUCE_ALSA_MIDI_INPUT_NAME | |||
: JUCE_ALSA_MIDI_OUTPUT_NAME); | |||
seq->setName (name); | |||
port.createPort (seq, name, forInput); | |||
const int sourceClient = snd_seq_client_info_get_client (clientInfo); | |||
port.createPort (seq, portName, forInput); | |||
port.connectWith (sourceClient, sourcePort); | |||
} | |||
} | |||
@@ -355,7 +378,7 @@ static AlsaPort iterateMidiDevices (const bool forInput, | |||
const int deviceIndexToOpen) | |||
{ | |||
AlsaPort port; | |||
const AlsaClient::Ptr client (globalAlsaSequencer (forInput)); | |||
const AlsaClient::Ptr client (AlsaClient::getInstance (forInput)); | |||
if (snd_seq_t* const seqHandle = client->get()) | |||
{ | |||
@@ -387,19 +410,6 @@ static AlsaPort iterateMidiDevices (const bool forInput, | |||
return port; | |||
} | |||
AlsaPort createMidiDevice (const bool forInput, const String& deviceNameToOpen) | |||
{ | |||
AlsaPort port; | |||
AlsaClient::Ptr client (new AlsaClient (forInput)); | |||
if (client->get()) | |||
{ | |||
client->setName (deviceNameToOpen + (forInput ? " Input" : " Output")); | |||
port.createPort (client, forInput ? "in" : "out", forInput); | |||
} | |||
return port; | |||
} | |||
//============================================================================== | |||
class MidiOutputDevice | |||
@@ -450,7 +460,7 @@ public: | |||
numBytes -= numSent; | |||
data += numSent; | |||
snd_seq_ev_set_source (&event, 0); | |||
snd_seq_ev_set_source (&event, port.portId); | |||
snd_seq_ev_set_subs (&event); | |||
snd_seq_ev_set_direct (&event); | |||
@@ -507,8 +517,11 @@ MidiOutput* MidiOutput::openDevice (int deviceIndex) | |||
MidiOutput* MidiOutput::createNewDevice (const String& deviceName) | |||
{ | |||
MidiOutput* newDevice = nullptr; | |||
AlsaPort port; | |||
const AlsaClient::Ptr client (AlsaClient::getInstance (false)); | |||
AlsaPort port (createMidiDevice (false, deviceName)); | |||
port.createPort (client, deviceName, false); | |||
if (port.isValid()) | |||
{ | |||
@@ -584,8 +597,11 @@ MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback) | |||
MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) | |||
{ | |||
MidiInput* newDevice = nullptr; | |||
AlsaPort port; | |||
const AlsaClient::Ptr client (AlsaClient::getInstance (true)); | |||
AlsaPort port (createMidiDevice (true, deviceName)); | |||
port.createPort (client, deviceName, true); | |||
if (port.isValid()) | |||
{ | |||
@@ -149,6 +149,7 @@ bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool mute) { return SystemVol | |||
struct CoreAudioClasses | |||
{ | |||
class CoreAudioIODeviceType; | |||
class CoreAudioIODevice; | |||
//============================================================================== | |||
@@ -691,7 +692,7 @@ public: | |||
{ const ScopedLock sl (callbackLock); } | |||
// wait until it's definately stopped calling back.. | |||
// wait until it's definitely stopped calling back.. | |||
for (int i = 40; --i >= 0;) | |||
{ | |||
Thread::sleep (50); | |||
@@ -847,6 +848,11 @@ private: | |||
intern->deviceDetailsChanged(); | |||
break; | |||
case kAudioObjectPropertyOwnedObjects: | |||
intern->stop (false); | |||
intern->owner.deviceType.triggerAsyncAudioDeviceListChange(); | |||
break; | |||
case kAudioDevicePropertyBufferSizeRange: | |||
case kAudioDevicePropertyVolumeScalar: | |||
case kAudioDevicePropertyMute: | |||
@@ -902,10 +908,12 @@ private: | |||
class CoreAudioIODevice : public AudioIODevice | |||
{ | |||
public: | |||
CoreAudioIODevice (const String& deviceName, | |||
CoreAudioIODevice (CoreAudioIODeviceType& dt, | |||
const String& deviceName, | |||
AudioDeviceID inputDeviceId, const int inputIndex_, | |||
AudioDeviceID outputDeviceId, const int outputIndex_) | |||
: AudioIODevice (deviceName, "CoreAudio"), | |||
deviceType (dt), | |||
inputIndex (inputIndex_), | |||
outputIndex (outputIndex_), | |||
isOpen_ (false), | |||
@@ -1060,6 +1068,12 @@ public: | |||
return lastError; | |||
} | |||
void audioDeviceListChanged() | |||
{ | |||
deviceType.audioDeviceListChanged(); | |||
} | |||
CoreAudioIODeviceType& deviceType; | |||
int inputIndex, outputIndex; | |||
private: | |||
@@ -1745,7 +1759,8 @@ private: | |||
//============================================================================== | |||
class CoreAudioIODeviceType : public AudioIODeviceType | |||
class CoreAudioIODeviceType : public AudioIODeviceType, | |||
private AsyncUpdater | |||
{ | |||
public: | |||
CoreAudioIODeviceType() | |||
@@ -1771,7 +1786,7 @@ public: | |||
} | |||
//============================================================================== | |||
void scanForDevices() | |||
void scanForDevices() override | |||
{ | |||
hasScanned = true; | |||
@@ -1827,7 +1842,7 @@ public: | |||
outputDeviceNames.appendNumbersToDuplicates (false, true); | |||
} | |||
StringArray getDeviceNames (bool wantInputNames) const | |||
StringArray getDeviceNames (bool wantInputNames) const override | |||
{ | |||
jassert (hasScanned); // need to call scanForDevices() before doing this | |||
@@ -1835,7 +1850,7 @@ public: | |||
: outputDeviceNames; | |||
} | |||
int getDefaultDeviceIndex (bool forInput) const | |||
int getDefaultDeviceIndex (bool forInput) const override | |||
{ | |||
jassert (hasScanned); // need to call scanForDevices() before doing this | |||
@@ -1870,7 +1885,7 @@ public: | |||
return 0; | |||
} | |||
int getIndexOfDevice (AudioIODevice* device, bool asInput) const | |||
int getIndexOfDevice (AudioIODevice* device, bool asInput) const override | |||
{ | |||
jassert (hasScanned); // need to call scanForDevices() before doing this | |||
@@ -1894,10 +1909,10 @@ public: | |||
return -1; | |||
} | |||
bool hasSeparateInputsAndOutputs() const { return true; } | |||
bool hasSeparateInputsAndOutputs() const override { return true; } | |||
AudioIODevice* createDevice (const String& outputDeviceName, | |||
const String& inputDeviceName) | |||
const String& inputDeviceName) override | |||
{ | |||
jassert (hasScanned); // need to call scanForDevices() before doing this | |||
@@ -1913,15 +1928,15 @@ public: | |||
String combinedName (outputDeviceName.isEmpty() ? inputDeviceName : outputDeviceName); | |||
if (inputDeviceID == outputDeviceID) | |||
return new CoreAudioIODevice (combinedName, inputDeviceID, inputIndex, outputDeviceID, outputIndex); | |||
return new CoreAudioIODevice (*this, combinedName, inputDeviceID, inputIndex, outputDeviceID, outputIndex); | |||
ScopedPointer<CoreAudioIODevice> in, out; | |||
if (inputDeviceID != 0) | |||
in = new CoreAudioIODevice (inputDeviceName, inputDeviceID, inputIndex, 0, -1); | |||
in = new CoreAudioIODevice (*this, inputDeviceName, inputDeviceID, inputIndex, 0, -1); | |||
if (outputDeviceID != 0) | |||
out = new CoreAudioIODevice (outputDeviceName, 0, -1, outputDeviceID, outputIndex); | |||
out = new CoreAudioIODevice (*this, outputDeviceName, 0, -1, outputDeviceID, outputIndex); | |||
if (in == nullptr) return out.release(); | |||
if (out == nullptr) return in.release(); | |||
@@ -1932,6 +1947,17 @@ public: | |||
return combo.release(); | |||
} | |||
void audioDeviceListChanged() | |||
{ | |||
scanForDevices(); | |||
callDeviceChangeListeners(); | |||
} | |||
void triggerAsyncAudioDeviceListChange() | |||
{ | |||
triggerAsyncUpdate(); | |||
} | |||
//============================================================================== | |||
private: | |||
StringArray inputDeviceNames, outputDeviceNames; | |||
@@ -1969,18 +1995,17 @@ private: | |||
return total; | |||
} | |||
void audioDeviceListChanged() | |||
{ | |||
scanForDevices(); | |||
callDeviceChangeListeners(); | |||
} | |||
static OSStatus hardwareListenerProc (AudioDeviceID, UInt32, const AudioObjectPropertyAddress*, void* clientData) | |||
{ | |||
static_cast<CoreAudioIODeviceType*> (clientData)->audioDeviceListChanged(); | |||
return noErr; | |||
} | |||
void handleAsyncUpdate() override | |||
{ | |||
audioDeviceListChanged(); | |||
} | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODeviceType) | |||
}; | |||
@@ -220,6 +220,14 @@ namespace CoreMidiHelpers | |||
// correctly when called from the message thread! | |||
jassert (MessageManager::getInstance()->isThisTheMessageThread()); | |||
#if TARGET_OS_SIMULATOR | |||
// Enable MIDI for iOS simulator | |||
MIDINetworkSession* session = [MIDINetworkSession defaultSession]; | |||
session.enabled = YES; | |||
session.connectionPolicy = MIDINetworkConnectionPolicy_Anyone; | |||
#endif | |||
CoreMidiHelpers::ScopedCFString name; | |||
name.cfString = getGlobalMidiClientName().toCFString(); | |||
CHECK_ERROR (MIDIClientCreate (name.cfString, &globalSystemChangeCallback, nullptr, &globalMidiClient)); | |||
@@ -365,17 +365,18 @@ public: | |||
void updateSampleRates() | |||
{ | |||
// find a list of sample rates.. | |||
const int possibleSampleRates[] = { 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 }; | |||
Array<double> newRates; | |||
if (asioObject != nullptr) | |||
{ | |||
const int possibleSampleRates[] = { 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 }; | |||
for (int index = 0; index < numElementsInArray (possibleSampleRates); ++index) | |||
if (asioObject->canSampleRate ((double) possibleSampleRates[index]) == 0) | |||
newRates.add ((double) possibleSampleRates[index]); | |||
} | |||
if (newRates.size() == 0) | |||
if (newRates.isEmpty()) | |||
{ | |||
double cr = getSampleRate(); | |||
JUCE_ASIO_LOG ("No sample rates supported - current rate: " + String ((int) cr)); | |||
@@ -1571,7 +1572,7 @@ private: | |||
DWORD dsize = sizeof (pathName); | |||
if (RegQueryValueEx (pathKey, 0, 0, &dtype, (LPBYTE) pathName, &dsize) == ERROR_SUCCESS) | |||
// In older code, this used to check for the existance of the file, but there are situations | |||
// In older code, this used to check for the existence of the file, but there are situations | |||
// where our process doesn't have access to it, but where the driver still loads ok.. | |||
ok = (pathName[0] != 0); | |||
@@ -96,11 +96,18 @@ bool check (HRESULT hr) | |||
} | |||
#if JUCE_MINGW | |||
#define JUCE_COMCLASS(name, guid) \ | |||
struct name; \ | |||
template<> struct UUIDGetter<name> { static CLSID get() { return uuidFromString (guid); } }; \ | |||
struct name | |||
#ifdef __uuidof | |||
#undef __uuidof | |||
#endif | |||
#define __uuidof(cls) UUIDGetter<cls>::get() | |||
struct PROPERTYKEY | |||
{ | |||
GUID fmtid; | |||
@@ -60,7 +60,7 @@ public: | |||
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 zero | |||
@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 | |||
@@ -41,11 +41,14 @@ | |||
* before #including this file, otherwise SIZE_MAX might not be defined | |||
*/ | |||
#include <limits.h> /* for SIZE_MAX */ | |||
#if HAVE_STDINT_H | |||
#include <stdint.h> /* for SIZE_MAX in case limits.h didn't get it */ | |||
#endif | |||
#include <stdlib.h> /* for size_t, malloc(), etc */ | |||
// JUCE: removed as JUCE already includes standard headers and including | |||
// these in FlacNamespace will cause problems | |||
//#include <limits.h> /* for SIZE_MAX */ | |||
//#if HAVE_STDINT_H | |||
//#include <stdint.h> /* for SIZE_MAX in case limits.h didn't get it */ | |||
//#endif | |||
//#include <stdlib.h> /* for size_t, malloc(), etc */ | |||
#include "compat.h" | |||
#ifndef SIZE_MAX | |||
@@ -35,7 +35,10 @@ | |||
/* we need this since some compilers (like MSVC) leave assert()s on release code (and we don't want to use their ASSERT) */ | |||
#ifdef DEBUG | |||
#include <assert.h> | |||
// JUCE: removed as JUCE already includes standard headers and including | |||
// these in FlacNamespace will cause problems | |||
//#include <assert.h> | |||
#define FLAC__ASSERT(x) assert(x) | |||
#define FLAC__ASSERT_DECLARATION(x) x | |||
#else | |||
@@ -34,7 +34,10 @@ | |||
#define FLAC__CALLBACK_H | |||
#include "ordinals.h" | |||
#include <stdlib.h> /* for size_t */ | |||
// JUCE: removed as JUCE already includes this and including stdlib | |||
// in FlacNamespace will cause problems | |||
//#include <stdlib.h> | |||
/** \file include/FLAC/callback.h | |||
* | |||
@@ -39,15 +39,7 @@ | |||
#ifndef FLAC__SHARE__COMPAT_H | |||
#define FLAC__SHARE__COMPAT_H | |||
#if defined _WIN32 && !defined __CYGWIN__ | |||
/* where MSVC puts unlink() */ | |||
# include <io.h> | |||
#else | |||
# include <unistd.h> | |||
#endif | |||
#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ | |||
#include <sys/types.h> /* for off_t */ | |||
#define FLAC__off_t __int64 /* use this instead of off_t to fix the 2 GB limit */ | |||
#if !defined __MINGW32__ | |||
#define fseeko _fseeki64 | |||
@@ -62,11 +54,6 @@ | |||
#define FLAC__off_t off_t | |||
#endif | |||
#if HAVE_INTTYPES_H | |||
#define __STDC_FORMAT_MACROS | |||
#include <inttypes.h> | |||
#endif | |||
#if defined(_MSC_VER) | |||
#define strtoll _strtoi64 | |||
#define strtoull _strtoui64 | |||
@@ -95,33 +82,13 @@ | |||
#define FLAC__STRNCASECMP strncasecmp | |||
#endif | |||
#if defined _MSC_VER || defined __MINGW32__ || defined __CYGWIN__ || defined __EMX__ | |||
#include <io.h> /* for _setmode(), chmod() */ | |||
#include <fcntl.h> /* for _O_BINARY */ | |||
#else | |||
#include <unistd.h> /* for chown(), unlink() */ | |||
#endif | |||
#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ | |||
#if defined __BORLANDC__ | |||
#include <utime.h> /* for utime() */ | |||
#else | |||
#include <sys/utime.h> /* for utime() */ | |||
#endif | |||
#else | |||
#include <sys/types.h> /* some flavors of BSD (like OS X) require this to get time_t */ | |||
#include <utime.h> /* for utime() */ | |||
#endif | |||
#if defined _MSC_VER | |||
# if _MSC_VER >= 1600 | |||
/* Visual Studio 2010 has decent C99 support */ | |||
# include <stdint.h> | |||
# define PRIu64 "llu" | |||
# define PRId64 "lld" | |||
# define PRIx64 "llx" | |||
# else | |||
# include <limits.h> | |||
# ifndef UINT32_MAX | |||
# define UINT32_MAX _UI32_MAX | |||
# endif | |||
@@ -51,7 +51,9 @@ static inline unsigned short __builtin_bswap16(unsigned short a) | |||
#elif defined HAVE_BYTESWAP_H /* Linux */ | |||
#include <byteswap.h> | |||
// JUCE: removed as JUCE already includes standard headers and including | |||
// these in FlacNamespace will cause problems | |||
//#include <byteswap.h> | |||
#define ENDSWAP_16(x) (bswap_16 (x)) | |||
#define ENDSWAP_32(x) (bswap_32 (x)) | |||
@@ -33,7 +33,6 @@ | |||
#ifndef FLAC__METADATA_H | |||
#define FLAC__METADATA_H | |||
#include <sys/types.h> /* for off_t */ | |||
#include "export.h" | |||
#include "callback.h" | |||
#include "format.h" | |||
@@ -33,7 +33,6 @@ | |||
#ifndef FLAC__STREAM_DECODER_H | |||
#define FLAC__STREAM_DECODER_H | |||
#include <stdio.h> /* for FILE */ | |||
#include "export.h" | |||
#include "format.h" | |||
@@ -33,7 +33,6 @@ | |||
#ifndef FLAC__STREAM_ENCODER_H | |||
#define FLAC__STREAM_ENCODER_H | |||
#include <stdio.h> /* for FILE */ | |||
#include "export.h" | |||
#include "format.h" | |||
#include "stream_decoder.h" | |||
@@ -38,11 +38,6 @@ | |||
extern "C" { | |||
#endif | |||
#include <stdio.h> | |||
#include <sys/stat.h> | |||
#include <stdarg.h> | |||
#include <windows.h> | |||
int get_utf8_argv(int *argc, char ***argv); | |||
int printf_utf8(const char *format, ...); | |||
@@ -668,13 +668,14 @@ public: | |||
//============================================================================== | |||
bool write (const int** data, int numSamples) override | |||
{ | |||
jassert (numSamples >= 0); | |||
jassert (data != nullptr && *data != nullptr); // the input must contain at least one channel! | |||
if (writeFailed) | |||
return false; | |||
const size_t bytes = (size_t) numSamples * numChannels * bitsPerSample / 8; | |||
tempBlock.ensureSize ((size_t) bytes, false); | |||
const size_t bytes = numChannels * (size_t) numSamples * bitsPerSample / 8; | |||
tempBlock.ensureSize (bytes, false); | |||
switch (bitsPerSample) | |||
{ | |||
@@ -695,13 +696,10 @@ public: | |||
writeFailed = true; | |||
return false; | |||
} | |||
else | |||
{ | |||
bytesWritten += bytes; | |||
lengthInSamples += (uint64) numSamples; | |||
return true; | |||
} | |||
bytesWritten += bytes; | |||
lengthInSamples += (uint64) numSamples; | |||
return true; | |||
} | |||
private: | |||
@@ -24,6 +24,66 @@ | |||
#if JUCE_USE_FLAC | |||
} | |||
#if defined _WIN32 && !defined __CYGWIN__ | |||
#include <io.h> | |||
#else | |||
#include <unistd.h> | |||
#endif | |||
#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ | |||
#include <sys/types.h> /* for off_t */ | |||
#endif | |||
#if HAVE_INTTYPES_H | |||
#define __STDC_FORMAT_MACROS | |||
#include <inttypes.h> | |||
#endif | |||
#if defined _MSC_VER || defined __MINGW32__ || defined __CYGWIN__ || defined __EMX__ | |||
#include <io.h> /* for _setmode(), chmod() */ | |||
#include <fcntl.h> /* for _O_BINARY */ | |||
#else | |||
#include <unistd.h> /* for chown(), unlink() */ | |||
#endif | |||
#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ | |||
#if defined __BORLANDC__ | |||
#include <utime.h> /* for utime() */ | |||
#else | |||
#include <sys/utime.h> /* for utime() */ | |||
#endif | |||
#else | |||
#include <sys/types.h> /* some flavors of BSD (like OS X) require this to get time_t */ | |||
#include <utime.h> /* for utime() */ | |||
#endif | |||
#if defined _MSC_VER | |||
#if _MSC_VER >= 1600 | |||
#include <stdint.h> | |||
#else | |||
#include <limits.h> | |||
#endif | |||
#endif | |||
#ifdef _WIN32 | |||
#include <stdio.h> | |||
#include <sys/stat.h> | |||
#include <stdarg.h> | |||
#include <windows.h> | |||
#endif | |||
#ifdef DEBUG | |||
#include <assert.h> | |||
#endif | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
namespace juce | |||
{ | |||
namespace FlacNamespace | |||
{ | |||
#if JUCE_INCLUDE_FLAC_CODE || ! defined (JUCE_INCLUDE_FLAC_CODE) | |||
@@ -35,6 +95,8 @@ namespace FlacNamespace | |||
#if JUCE_MSVC | |||
#pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312 4505 4365 4005 4334 181 111) | |||
#else | |||
#define HAVE_LROUND 1 | |||
#endif | |||
#if JUCE_MAC | |||
@@ -66,6 +128,7 @@ namespace FlacNamespace | |||
#define __STDC_LIMIT_MACROS 1 | |||
#define flac_max jmax | |||
#define flac_min jmin | |||
#undef DEBUG // (some flac code dumps debug trace if the app defines this macro) | |||
#include "flac/all.h" | |||
#include "flac/libFLAC/bitmath.c" | |||
#include "flac/libFLAC/bitreader.c" | |||
@@ -324,7 +387,8 @@ class FlacWriter : public AudioFormatWriter | |||
{ | |||
public: | |||
FlacWriter (OutputStream* const out, double rate, uint32 numChans, uint32 bits, int qualityOptionIndex) | |||
: AudioFormatWriter (out, flacFormatName, rate, numChans, bits) | |||
: AudioFormatWriter (out, flacFormatName, rate, numChans, bits), | |||
streamStartPos (output != nullptr ? jmax (output->getPosition(), 0ll) : 0ll) | |||
{ | |||
using namespace FlacNamespace; | |||
encoder = FLAC__stream_encoder_new(); | |||
@@ -432,7 +496,7 @@ public: | |||
packUint32 ((FLAC__uint32) info.total_samples, buffer + 14, 4); | |||
memcpy (buffer + 18, info.md5sum, 16); | |||
const bool seekOk = output->setPosition (4); | |||
const bool seekOk = output->setPosition (streamStartPos + 4); | |||
ignoreUnused (seekOk); | |||
// if this fails, you've given it an output stream that can't seek! It needs | |||
@@ -482,6 +546,7 @@ public: | |||
private: | |||
FlacNamespace::FLAC__StreamEncoder* encoder; | |||
int64 streamStartPos; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacWriter) | |||
}; | |||
@@ -222,7 +222,7 @@ public: | |||
DisposeMovie (movie); | |||
#if JUCE_MAC | |||
ExitMoviesOnThread (); | |||
ExitMoviesOnThread(); | |||
#endif | |||
} | |||
} | |||
@@ -646,24 +646,6 @@ namespace WavFileHelpers | |||
return true; | |||
} | |||
static String getStringFromWindows1252Codepage (const uint8* data, size_t num) | |||
{ | |||
HeapBlock<juce_wchar> unicode (num + 1); | |||
for (size_t i = 0; i < num; ++i) | |||
unicode[i] = CharacterFunctions::getUnicodeCharFromWindows1252Codepage (data[i]); | |||
unicode[num] = 0; | |||
return CharPointer_UTF32 (unicode); | |||
} | |||
static String getStringFromData (const MemoryBlock& mb) | |||
{ | |||
return CharPointer_UTF8::isValidString ((const char*) mb.getData(), (int) mb.getSize()) | |||
? mb.toString() | |||
: getStringFromWindows1252Codepage ((const uint8*) mb.getData(), mb.getSize()); | |||
} | |||
static void addToMetadata (StringPairArray& values, InputStream& input, int64 chunkEnd) | |||
{ | |||
while (input.getPosition() < chunkEnd) | |||
@@ -674,7 +656,10 @@ namespace WavFileHelpers | |||
if (infoLength > 0) | |||
{ | |||
infoLength = jlimit ((int64) 0, infoLength, (int64) input.readInt()); | |||
infoLength = jmin (infoLength, (int64) input.readInt()); | |||
if (infoLength <= 0) | |||
return; | |||
for (int i = 0; i < numElementsInArray (types); ++i) | |||
{ | |||
@@ -682,7 +667,8 @@ namespace WavFileHelpers | |||
{ | |||
MemoryBlock mb; | |||
input.readIntoMemoryBlock (mb, (ssize_t) infoLength); | |||
values.set (types[i], getStringFromData (mb)); | |||
values.set (types[i], String::createStringFromData ((const char*) mb.getData(), | |||
(int) mb.getSize())); | |||
break; | |||
} | |||
} | |||
@@ -1276,7 +1262,7 @@ public: | |||
if (writeFailed) | |||
return false; | |||
const size_t bytes = numChannels * (unsigned int) numSamples * bitsPerSample / 8; | |||
const size_t bytes = numChannels * (size_t) numSamples * bitsPerSample / 8; | |||
tempBlock.ensureSize (bytes, false); | |||
switch (bitsPerSample) | |||
@@ -31,6 +31,8 @@ | |||
#error "Incorrect use of JUCE cpp file" | |||
#endif | |||
#include "AppConfig.h" | |||
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1 | |||
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1 | |||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 | |||
@@ -22,10 +22,36 @@ | |||
============================================================================== | |||
*/ | |||
/******************************************************************************* | |||
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.txt file. | |||
BEGIN_JUCE_MODULE_DECLARATION | |||
ID: juce_audio_formats | |||
vendor: juce | |||
version: 4.3.0 | |||
name: JUCE audio file format codecs | |||
description: Classes for reading and writing various audio file formats. | |||
website: http://www.juce.com/juce | |||
license: GPL/Commercial | |||
dependencies: juce_audio_basics | |||
OSXFrameworks: CoreAudio CoreMIDI QuartzCore AudioToolbox | |||
iOSFrameworks: AudioToolbox QuartzCore | |||
END_JUCE_MODULE_DECLARATION | |||
*******************************************************************************/ | |||
#ifndef JUCE_AUDIO_FORMATS_H_INCLUDED | |||
#define JUCE_AUDIO_FORMATS_H_INCLUDED | |||
#include "../juce_audio_basics/juce_audio_basics.h" | |||
#include <juce_audio_basics/juce_audio_basics.h> | |||
//============================================================================== | |||
/** Config: JUCE_USE_FLAC | |||
@@ -0,0 +1,41 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2016 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#ifndef JUCE_AAX_MODIFIER_INJECTOR_H_INCLUDED | |||
#define JUCE_AAX_MODIFIER_INJECTOR_H_INCLUDED | |||
struct ModifierKeyProvider | |||
{ | |||
virtual ~ModifierKeyProvider() {} | |||
virtual int getWin32Modifiers() const = 0; | |||
}; | |||
struct ModifierKeyReceiver | |||
{ | |||
virtual ~ModifierKeyReceiver() {} | |||
virtual void setModifierKeyProvider (ModifierKeyProvider*) = 0; | |||
virtual void removeModifierKeyProvider() = 0; | |||
}; | |||
#endif |
@@ -82,19 +82,19 @@ void AUCarbonViewControl::Bind() | |||
WantEventTypes(GetControlEventTarget(mControl), GetEventTypeCount(events), events); | |||
if (mType == kTypeContinuous || mType == kTypeText || mType == kTypeDiscrete) { | |||
EventTypeSpec events[] = { | |||
EventTypeSpec controlEvents[] = { | |||
{ kEventClassControl, kEventControlHit }, | |||
{ kEventClassControl, kEventControlClick }, | |||
{ kEventClassControl, kEventControlTrack } | |||
}; | |||
WantEventTypes(GetControlEventTarget(mControl), GetEventTypeCount(events), events); | |||
WantEventTypes(GetControlEventTarget(mControl), GetEventTypeCount(controlEvents), controlEvents); | |||
} | |||
if (mType == kTypeText) { | |||
EventTypeSpec events[] = { | |||
EventTypeSpec controlFocusEvents[] = { | |||
{ kEventClassControl, kEventControlSetFocusPart } | |||
}; | |||
WantEventTypes(GetControlEventTarget(mControl), GetEventTypeCount(events), events); | |||
WantEventTypes(GetControlEventTarget(mControl), GetEventTypeCount(controlFocusEvents), controlFocusEvents); | |||
ControlKeyFilterUPP proc = mParam.ValuesHaveStrings() ? StdKeyFilterCallback : NumericKeyFilterCallback; | |||
// this will fail for a static text field | |||
SetControlData(mControl, 0, kControlEditTextKeyFilterTag, sizeof(proc), &proc); | |||
@@ -591,7 +591,7 @@ AUVPresets::AUVPresets (AUCarbonViewBase* inParentView, | |||
#endif | |||
#ifndef __LP64__ | |||
if (result != noErr) { // if the PresentPreset property is not implemented, fall back to the CurrentPreset property | |||
OSStatus result = AudioUnitGetProperty (mView->GetEditAudioUnit(), | |||
result = AudioUnitGetProperty (mView->GetEditAudioUnit(), | |||
kAudioUnitProperty_CurrentPreset, | |||
kAudioUnitScope_Global, | |||
0, | |||
@@ -0,0 +1,140 @@ | |||
/* | |||
File: AUResources.r | |||
Abstract: AUResources.r | |||
Version: 1.1 | |||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple | |||
Inc. ("Apple") in consideration of your agreement to the following | |||
terms, and your use, installation, modification or redistribution of | |||
this Apple software constitutes acceptance of these terms. If you do | |||
not agree with these terms, please do not use, install, modify or | |||
redistribute this Apple software. | |||
In consideration of your agreement to abide by the following terms, and | |||
subject to these terms, Apple grants you a personal, non-exclusive | |||
license, under Apple's copyrights in this original Apple software (the | |||
"Apple Software"), to use, reproduce, modify and redistribute the Apple | |||
Software, with or without modifications, in source and/or binary forms; | |||
provided that if you redistribute the Apple Software in its entirety and | |||
without modifications, you must retain this notice and the following | |||
text and disclaimers in all such redistributions of the Apple Software. | |||
Neither the name, trademarks, service marks or logos of Apple Inc. may | |||
be used to endorse or promote products derived from the Apple Software | |||
without specific prior written permission from Apple. Except as | |||
expressly stated in this notice, no other rights or licenses, express or | |||
implied, are granted by Apple herein, including but not limited to any | |||
patent rights that may be infringed by your derivative works or by other | |||
works in which the Apple Software may be incorporated. | |||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE | |||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION | |||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS | |||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND | |||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. | |||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL | |||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, | |||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED | |||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), | |||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE | |||
POSSIBILITY OF SUCH DAMAGE. | |||
Copyright (C) 2014 Apple Inc. All Rights Reserved. | |||
*/ | |||
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
// AUResources.r | |||
// | |||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | |||
/* sample macro definitions -- all of these symbols must be defined | |||
#define RES_ID kHALOutputResID | |||
#define COMP_TYPE kAudioUnitComponentType | |||
#define COMP_SUBTYPE kAudioUnitOutputSubType | |||
#define COMP_MANUF kAudioUnitAudioHardwareOutputSubSubType | |||
#define VERSION 0x00010000 | |||
#define NAME "AudioHALOutput" | |||
#define DESCRIPTION "Audio hardware output AudioUnit" | |||
#define ENTRY_POINT "AUHALEntry" | |||
*/ | |||
#define UseExtendedThingResource 1 | |||
#include <CoreServices/CoreServices.r> | |||
// this is a define used to indicate that a component has no static data that would mean | |||
// that no more than one instance could be open at a time - never been true for AUs | |||
#ifndef cmpThreadSafeOnMac | |||
#define cmpThreadSafeOnMac 0x10000000 | |||
#endif | |||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
resource 'STR ' (RES_ID, purgeable) { | |||
NAME | |||
}; | |||
resource 'STR ' (RES_ID + 1, purgeable) { | |||
DESCRIPTION | |||
}; | |||
resource 'dlle' (RES_ID) { | |||
ENTRY_POINT | |||
}; | |||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
resource 'thng' (RES_ID, NAME) { | |||
COMP_TYPE, | |||
COMP_SUBTYPE, | |||
COMP_MANUF, | |||
0, 0, 0, 0, // no 68K | |||
'STR ', RES_ID, | |||
'STR ', RES_ID + 1, | |||
0, 0, /* icon */ | |||
VERSION, | |||
componentHasMultiplePlatforms | componentDoAutoVersion, | |||
0, | |||
{ | |||
#if defined(ppc_YES) | |||
cmpThreadSafeOnMac, | |||
'dlle', RES_ID, platformPowerPCNativeEntryPoint | |||
#define NeedLeadingComma 1 | |||
#endif | |||
#if defined(ppc64_YES) | |||
#if defined(NeedLeadingComma) | |||
, | |||
#endif | |||
cmpThreadSafeOnMac, | |||
'dlle', RES_ID, platformPowerPC64NativeEntryPoint | |||
#define NeedLeadingComma 1 | |||
#endif | |||
#if defined(i386_YES) | |||
#if defined(NeedLeadingComma) | |||
, | |||
#endif | |||
cmpThreadSafeOnMac, | |||
'dlle', RES_ID, platformIA32NativeEntryPoint | |||
#define NeedLeadingComma 1 | |||
#endif | |||
#if defined(x86_64_YES) | |||
#if defined(NeedLeadingComma) | |||
, | |||
#endif | |||
cmpThreadSafeOnMac, | |||
'dlle', RES_ID, 8 | |||
#define NeedLeadingComma 1 | |||
#endif | |||
} | |||
}; | |||
#undef RES_ID | |||
#undef COMP_TYPE | |||
#undef COMP_SUBTYPE | |||
#undef COMP_MANUF | |||
#undef VERSION | |||
#undef NAME | |||
#undef DESCRIPTION | |||
#undef ENTRY_POINT | |||
#undef NeedLeadingComma |
@@ -49,6 +49,16 @@ | |||
#undef UNICODE | |||
#endif | |||
#ifdef __clang__ | |||
#pragma clang diagnostic push | |||
#pragma clang diagnostic ignored "-Wnon-virtual-dtor" | |||
#pragma clang diagnostic ignored "-Wcomment" | |||
#pragma clang diagnostic ignored "-Wreorder" | |||
#pragma clang diagnostic ignored "-Wextra-tokens" | |||
#pragma clang diagnostic ignored "-Wunused-variable" | |||
#pragma clang diagnostic ignored "-Wdeprecated" | |||
#endif | |||
#include <CEffectGroup.cpp> | |||
#include <CEffectGroupMIDI.cpp> | |||
#include <CEffectMIDIUtils.cpp> | |||
@@ -58,4 +68,8 @@ | |||
#include <CEffectTypeRTAS.cpp> | |||
#include <ChunkDataParser.cpp> | |||
#ifdef __clang__ | |||
#pragma clang diagnostic pop | |||
#endif | |||
#endif |
@@ -29,6 +29,15 @@ | |||
#include "juce_RTAS_DigiCode_Header.h" | |||
#ifdef __clang__ | |||
#pragma clang diagnostic push | |||
#pragma clang diagnostic ignored "-Wcomment" | |||
#pragma clang diagnostic ignored "-Wextra-tokens" | |||
#pragma clang diagnostic ignored "-Wnon-virtual-dtor" | |||
#pragma clang diagnostic ignored "-Wreorder" | |||
#pragma clang diagnostic ignored "-Wdeprecated" | |||
#endif | |||
/* | |||
This file is used to include and build the required digidesign CPP files without your project | |||
needing to reference the files directly. Because these files will be found via your include path, | |||
@@ -47,4 +56,8 @@ | |||
#include <CEffectProcessMIDI.cpp> | |||
#include <PlugInUtils.cpp> | |||
#ifdef __clang__ | |||
#pragma clang diagnostic pop | |||
#endif | |||
#endif |
@@ -27,8 +27,16 @@ | |||
#if JucePlugin_Build_RTAS | |||
#include "../utility/juce_IncludeSystemHeaders.h" | |||
#include "juce_RTAS_DigiCode_Header.h" | |||
#ifdef __clang__ | |||
#pragma clang diagnostic push | |||
#pragma clang diagnostic ignored "-Wnon-virtual-dtor" | |||
#pragma clang diagnostic ignored "-Wextra-tokens" | |||
#pragma clang diagnostic ignored "-Wreorder" | |||
#endif | |||
/* | |||
This file is used to include and build the required digidesign CPP files without your project | |||
needing to reference the files directly. Because these files will be found via your include path, | |||
@@ -47,18 +55,20 @@ | |||
#if WINDOWS_VERSION | |||
#undef _UNICODE | |||
#undef UNICODE | |||
#define DllMain DllMainRTAS | |||
#include <DLLMain.cpp> | |||
#undef DllMain | |||
#include <DefaultSwap.cpp> | |||
#else | |||
#include <PlugInInitialize.cpp> | |||
#include <Dispatcher.cpp> | |||
#endif | |||
#else | |||
#ifdef __clang__ | |||
#pragma clang diagnostic pop | |||
#endif | |||
#else | |||
#if _MSC_VER | |||
short __stdcall NewPlugIn (void*) { return 0; } | |||
@@ -43,10 +43,11 @@ | |||
#define WINDOWS_VERSION 1 | |||
#define PLUGIN_SDK_BUILD 1 | |||
#define PLUGIN_SDK_DIRECTMIDI 1 | |||
#define _STDINT_H 1 | |||
// the Digidesign projects all use a struct alignment of 2.. | |||
#pragma pack (2) | |||
#pragma warning (disable: 4267 4996 4311 4312 4103 4121 4100 4127 4189 4245 4389 4512 4701) | |||
#pragma warning (disable: 4267 4996 4311 4312 4103 4121 4100 4127 4189 4245 4389 4512 4701 4703) | |||
#include <ForcedInclude.h> | |||
@@ -39,6 +39,13 @@ | |||
#include <Mac2Win.H> | |||
#endif | |||
#ifdef __clang__ | |||
#pragma clang diagnostic push | |||
#pragma clang diagnostic ignored "-Widiomatic-parentheses" | |||
#pragma clang diagnostic ignored "-Wnon-virtual-dtor" | |||
#pragma clang diagnostic ignored "-Wcomment" | |||
#endif | |||
/* Note about include paths | |||
------------------------ | |||
@@ -86,10 +93,14 @@ | |||
#include <FicProcessTokens.h> | |||
#include <ExternalVersionDefines.h> | |||
#ifdef __clang__ | |||
#pragma clang diagnostic pop | |||
#endif | |||
//============================================================================== | |||
#ifdef _MSC_VER | |||
#pragma pack (push, 8) | |||
#pragma warning (disable: 4263 4264) | |||
#pragma warning (disable: 4263 4264 4250) | |||
#endif | |||
#include "../utility/juce_IncludeModuleHeaders.h" | |||
@@ -269,7 +280,7 @@ public: | |||
} | |||
} | |||
void DrawContents (Rect*) | |||
void DrawContents (Rect*) override | |||
{ | |||
#if JUCE_WINDOWS | |||
if (wrapper != nullptr) | |||
@@ -280,7 +291,7 @@ public: | |||
#endif | |||
} | |||
void DrawBackground (Rect*) {} | |||
void DrawBackground (Rect*) override {} | |||
//============================================================================== | |||
private: | |||
@@ -466,8 +477,19 @@ public: | |||
AddControl (new CPluginControl_OnOff ('bypa', "Master Bypass\nMastrByp\nMByp\nByp", false, true)); | |||
DefineMasterBypassControlIndex (bypassControlIndex); | |||
for (int i = 0; i < juceFilter->getNumParameters(); ++i) | |||
AddControl (new JucePluginControl (*juceFilter, i)); | |||
const int numParameters = juceFilter->getNumParameters(); | |||
#if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
const bool usingManagedParameters = false; | |||
#else | |||
const bool usingManagedParameters = (juceFilter->getParameters().size() == numParameters); | |||
#endif | |||
for (int i = 0; i < numParameters; ++i) | |||
{ | |||
OSType rtasParamID = static_cast<OSType> (usingManagedParameters ? juceFilter->getParameterID (i).hashCode() : i); | |||
AddControl (new JucePluginControl (*juceFilter, i, rtasParamID)); | |||
} | |||
// we need to do this midi log-in to get timecode, regardless of whether | |||
// the plugin actually uses midi... | |||
@@ -476,14 +498,14 @@ public: | |||
#if JucePlugin_WantsMidiInput | |||
if (CEffectType* const type = dynamic_cast<CEffectType*> (this->GetProcessType())) | |||
{ | |||
char nodeName [64]; | |||
char nodeName[80] = { 0 }; | |||
type->GetProcessTypeName (63, nodeName); | |||
p2cstrcpy (nodeName, reinterpret_cast<unsigned char*> (nodeName)); | |||
nodeName[nodeName[0] + 1] = 0; | |||
midiBufferNode = new CEffectMIDIOtherBufferedNode (&mMIDIWorld, | |||
8192, | |||
eLocalNode, | |||
nodeName, | |||
nodeName + 1, | |||
midiBuffer); | |||
midiBufferNode->Initialize (0xffff, true); | |||
@@ -803,14 +825,14 @@ private: | |||
{ | |||
public: | |||
//============================================================================== | |||
JucePluginControl (AudioProcessor& p, const int i) | |||
: processor (p), index (i) | |||
JucePluginControl (AudioProcessor& p, const int i, OSType rtasParamID) | |||
: processor (p), index (i), paramID (rtasParamID) | |||
{ | |||
CPluginControl::SetValue (GetDefaultValue()); | |||
} | |||
//============================================================================== | |||
OSType GetID() const { return index + 1; } | |||
OSType GetID() const { return paramID; } | |||
long GetDefaultValue() const { return floatToLong (processor.getParameterDefaultValue (index)); } | |||
void SetDefaultValue (long) {} | |||
long GetNumSteps() const { return processor.getParameterNumSteps (index); } | |||
@@ -855,6 +877,7 @@ private: | |||
//============================================================================== | |||
AudioProcessor& processor; | |||
const int index; | |||
const OSType paramID; | |||
JUCE_DECLARE_NON_COPYABLE (JucePluginControl) | |||
}; | |||
@@ -933,7 +956,7 @@ private: | |||
#if JUCE_WINDOWS | |||
Process::setCurrentModuleInstanceHandle (gThisModule); | |||
#endif | |||
PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_RTAS; | |||
initialiseJuce_GUI(); | |||
return new JucePlugInProcess(); | |||
@@ -0,0 +1,110 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#include "../../juce_core/system/juce_TargetPlatform.h" | |||
#include "../utility/juce_CheckSettingMacros.h" | |||
#include "../utility/juce_IncludeSystemHeaders.h" | |||
#include "../utility/juce_IncludeModuleHeaders.h" | |||
#include "../utility/juce_FakeMouseMoveGenerator.h" | |||
#include "../utility/juce_WindowsHooks.h" | |||
#include <juce_audio_devices/juce_audio_devices.h> | |||
#include <juce_gui_extra/juce_gui_extra.h> | |||
#include <juce_audio_utils/juce_audio_utils.h> | |||
// You can set this flag in your build if you need to specify a different | |||
// standalone JUCEApplication class for your app to use. If you don't | |||
// set it then by default we'll just create a simple one as below. | |||
#if ! JUCE_USE_CUSTOM_AU3_STANDALONE_APP | |||
extern AudioProcessor* JUCE_CALLTYPE createPluginFilter(); | |||
namespace juce | |||
{ | |||
#include "juce_StandaloneFilterWindow.h" | |||
} | |||
//============================================================================== | |||
class StandaloneFilterApp : public JUCEApplication | |||
{ | |||
public: | |||
StandaloneFilterApp() | |||
{ | |||
PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_Standalone; | |||
PropertiesFile::Options options; | |||
options.applicationName = getApplicationName(); | |||
options.filenameSuffix = ".settings"; | |||
options.osxLibrarySubFolder = "Application Support"; | |||
#if JUCE_LINUX | |||
options.folderName = "~/.config"; | |||
#else | |||
options.folderName = ""; | |||
#endif | |||
appProperties.setStorageParameters (options); | |||
} | |||
const String getApplicationName() override { return JucePlugin_Name; } | |||
const String getApplicationVersion() override { return JucePlugin_VersionString; } | |||
bool moreThanOneInstanceAllowed() override { return true; } | |||
void anotherInstanceStarted (const String&) override {} | |||
virtual StandaloneFilterWindow* createWindow() | |||
{ | |||
return new StandaloneFilterWindow (getApplicationName(), Colours::white, appProperties.getUserSettings(), false); | |||
} | |||
//============================================================================== | |||
void initialise (const String&) override | |||
{ | |||
mainWindow = createWindow(); | |||
#if JUCE_IOS || JUCE_ANDROID | |||
Desktop::getInstance().setKioskModeComponent (mainWindow, false); | |||
#endif | |||
mainWindow->setVisible (true); | |||
} | |||
void shutdown() override | |||
{ | |||
mainWindow = nullptr; | |||
appProperties.saveIfNeeded(); | |||
} | |||
//============================================================================== | |||
void systemRequestedQuit() override | |||
{ | |||
quit(); | |||
} | |||
protected: | |||
ApplicationProperties appProperties; | |||
ScopedPointer<StandaloneFilterWindow> mainWindow; | |||
}; | |||
#endif |
@@ -25,8 +25,6 @@ | |||
#ifndef JUCE_STANDALONEFILTERWINDOW_H_INCLUDED | |||
#define JUCE_STANDALONEFILTERWINDOW_H_INCLUDED | |||
extern AudioProcessor* JUCE_CALLTYPE createPluginFilter(); | |||
//============================================================================== | |||
/** | |||
An object that creates and plays a standalone instance of an AudioProcessor. | |||
@@ -36,6 +34,9 @@ extern AudioProcessor* JUCE_CALLTYPE createPluginFilter(); | |||
computer's audio/MIDI devices using AudioDeviceManager and AudioProcessorPlayer. | |||
*/ | |||
class StandalonePluginHolder | |||
#if JUCE_IOS || JUCE_ANDROID | |||
: private Timer | |||
#endif | |||
{ | |||
public: | |||
/** Creates an instance of the default plugin. | |||
@@ -63,10 +64,18 @@ public: | |||
setupAudioDevices (preferredDefaultDeviceName, preferredSetupOptions); | |||
reloadPluginState(); | |||
startPlaying(); | |||
#if JUCE_IOS || JUCE_ANDROID | |||
startTimer (500); | |||
#endif | |||
} | |||
virtual ~StandalonePluginHolder() | |||
{ | |||
#if JUCE_IOS || JUCE_ANDROID | |||
stopTimer(); | |||
#endif | |||
deletePlugin(); | |||
shutDownAudioDevices(); | |||
} | |||
@@ -74,21 +83,17 @@ public: | |||
//============================================================================== | |||
virtual void createPlugin() | |||
{ | |||
#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client | |||
processor = ::createPluginFilterOfType (AudioProcessor::wrapperType_Standalone); | |||
#else | |||
AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::wrapperType_Standalone); | |||
processor = createPluginFilter(); | |||
jassert (processor != nullptr); // Your createPluginFilter() function must return a valid object! | |||
AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::wrapperType_Undefined); | |||
#endif | |||
jassert (processor != nullptr); // Your createPluginFilter() function must return a valid object! | |||
// try to disable sidechain and aux buses | |||
const int numInBuses = processor->busArrangement.inputBuses.size(); | |||
const int numOutBuses = processor->busArrangement.inputBuses.size(); | |||
for (int busIdx = 1; busIdx < numInBuses; ++busIdx) | |||
processor->setPreferredBusArrangement (true, busIdx, AudioChannelSet::disabled()); | |||
for (int busIdx = 1; busIdx < numOutBuses; ++busIdx) | |||
processor->setPreferredBusArrangement (false, busIdx, AudioChannelSet::disabled()); | |||
processor->disableNonMainBuses(); | |||
processor->setRateAndBufferSizeDetails(44100, 512); | |||
} | |||
@@ -115,7 +120,7 @@ public: | |||
if (settings != nullptr) | |||
f = File (settings->getValue ("lastStateFile")); | |||
if (f == File::nonexistent) | |||
if (f == File()) | |||
f = File::getSpecialLocation (File::userDocumentsDirectory); | |||
return f; | |||
@@ -130,6 +135,7 @@ public: | |||
/** Pops up a dialog letting the user save the processor's state to a file. */ | |||
void askUserToSaveState (const String& fileSuffix = String()) | |||
{ | |||
#if JUCE_MODAL_LOOPS_PERMITTED | |||
FileChooser fc (TRANS("Save current state"), getLastFile(), getFilePatterns (fileSuffix)); | |||
if (fc.browseForFileToSave (true)) | |||
@@ -144,11 +150,15 @@ public: | |||
TRANS("Error whilst saving"), | |||
TRANS("Couldn't write to the specified file!")); | |||
} | |||
#else | |||
ignoreUnused (fileSuffix); | |||
#endif | |||
} | |||
/** Pops up a dialog letting the user re-load the processor's state from a file. */ | |||
void askUserToLoadState (const String& fileSuffix = String()) | |||
{ | |||
#if JUCE_MODAL_LOOPS_PERMITTED | |||
FileChooser fc (TRANS("Load a saved state"), getLastFile(), getFilePatterns (fileSuffix)); | |||
if (fc.browseForFileToOpen()) | |||
@@ -164,6 +174,9 @@ public: | |||
TRANS("Error whilst loading"), | |||
TRANS("Couldn't read from the specified file!")); | |||
} | |||
#else | |||
ignoreUnused (fileSuffix); | |||
#endif | |||
} | |||
//============================================================================== | |||
@@ -254,6 +267,10 @@ public: | |||
AudioDeviceManager deviceManager; | |||
AudioProcessorPlayer player; | |||
#if JUCE_IOS || JUCE_ANDROID | |||
StringArray lastMidiDevices; | |||
#endif | |||
private: | |||
void setupAudioDevices (const String& preferredDefaultDeviceName, | |||
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions) | |||
@@ -272,6 +289,43 @@ private: | |||
deviceManager.removeAudioCallback (&player); | |||
} | |||
#if JUCE_IOS || JUCE_ANDROID | |||
void timerCallback() override | |||
{ | |||
StringArray midiInputDevices = MidiInput::getDevices(); | |||
if (midiInputDevices != lastMidiDevices) | |||
{ | |||
{ | |||
const int n = lastMidiDevices.size(); | |||
for (int i = 0; i < n; ++i) | |||
{ | |||
const String& oldDevice = lastMidiDevices[i]; | |||
if (! midiInputDevices.contains (oldDevice)) | |||
{ | |||
deviceManager.setMidiInputEnabled (oldDevice, false); | |||
deviceManager.removeMidiInputCallback (oldDevice, &player); | |||
} | |||
} | |||
} | |||
{ | |||
const int n = midiInputDevices.size(); | |||
for (int i = 0; i < n; ++i) | |||
{ | |||
const String& newDevice = midiInputDevices[i]; | |||
if (! lastMidiDevices.contains (newDevice)) | |||
{ | |||
deviceManager.addMidiInputCallback (newDevice, &player); | |||
deviceManager.setMidiInputEnabled (newDevice, true); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
#endif | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandalonePluginHolder) | |||
}; | |||
@@ -23,6 +23,9 @@ | |||
*/ | |||
#include "../../juce_core/system/juce_TargetPlatform.h" | |||
#if JUCE_MAC | |||
#include "../utility/juce_CheckSettingMacros.h" | |||
#if JucePlugin_Build_VST || JucePlugin_Build_VST3 | |||
@@ -39,7 +42,7 @@ namespace juce | |||
{ | |||
#if ! JUCE_64BIT | |||
void updateEditorCompBoundsVST (Component*); | |||
JUCE_API void updateEditorCompBoundsVST (Component*); | |||
void updateEditorCompBoundsVST (Component* comp) | |||
{ | |||
HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int) | |||
@@ -66,12 +69,12 @@ static pascal OSStatus viewBoundsChangedEvent (EventHandlerCallRef, EventRef, vo | |||
static bool shouldManuallyCloseHostWindow() | |||
{ | |||
return getHostType().isCubase7orLater() || getHostType().isRenoise(); | |||
return getHostType().isCubase7orLater() || getHostType().isRenoise() || ((SystemStats::getOperatingSystemType() & 0xff) >= 12); | |||
} | |||
#endif | |||
//============================================================================== | |||
void initialiseMacVST(); | |||
JUCE_API void initialiseMacVST(); | |||
void initialiseMacVST() | |||
{ | |||
#if ! JUCE_64BIT | |||
@@ -79,7 +82,7 @@ void initialiseMacVST() | |||
#endif | |||
} | |||
void* attachComponentToWindowRefVST (Component* comp, void* parentWindowOrView, bool isNSView); | |||
JUCE_API void* attachComponentToWindowRefVST (Component* comp, void* parentWindowOrView, bool isNSView); | |||
void* attachComponentToWindowRefVST (Component* comp, void* parentWindowOrView, bool isNSView) | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
@@ -184,7 +187,7 @@ void* attachComponentToWindowRefVST (Component* comp, void* parentWindowOrView, | |||
} | |||
} | |||
void detachComponentFromWindowRefVST (Component* comp, void* window, bool isNSView); | |||
JUCE_API void detachComponentFromWindowRefVST (Component* comp, void* window, bool isNSView); | |||
void detachComponentFromWindowRefVST (Component* comp, void* window, bool isNSView) | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
@@ -242,7 +245,7 @@ void detachComponentFromWindowRefVST (Component* comp, void* window, bool isNSVi | |||
} | |||
} | |||
void setNativeHostWindowSizeVST (void* window, Component* component, int newWidth, int newHeight, bool isNSView); | |||
JUCE_API void setNativeHostWindowSizeVST (void* window, Component* component, int newWidth, int newHeight, bool isNSView); | |||
void setNativeHostWindowSizeVST (void* window, Component* component, int newWidth, int newHeight, bool isNSView) | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
@@ -280,7 +283,7 @@ void setNativeHostWindowSizeVST (void* window, Component* component, int newWidt | |||
} | |||
} | |||
void checkWindowVisibilityVST (void* window, Component* comp, bool isNSView); | |||
JUCE_API void checkWindowVisibilityVST (void* window, Component* comp, bool isNSView); | |||
void checkWindowVisibilityVST (void* window, Component* comp, bool isNSView) | |||
{ | |||
ignoreUnused (window, comp, isNSView); | |||
@@ -291,7 +294,7 @@ void checkWindowVisibilityVST (void* window, Component* comp, bool isNSView) | |||
#endif | |||
} | |||
bool forwardCurrentKeyEventToHostVST (Component* comp, bool isNSView); | |||
JUCE_API bool forwardCurrentKeyEventToHostVST (Component* comp, bool isNSView); | |||
bool forwardCurrentKeyEventToHostVST (Component* comp, bool isNSView) | |||
{ | |||
#if ! JUCE_64BIT | |||
@@ -311,3 +314,4 @@ bool forwardCurrentKeyEventToHostVST (Component* comp, bool isNSView) | |||
} // (juce namespace) | |||
#endif | |||
#endif |
@@ -27,11 +27,19 @@ | |||
//============================================================================== | |||
#if JucePlugin_Build_VST3 && (__APPLE_CPP__ || __APPLE_CC__ || _WIN32 || _WIN64) | |||
#if JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS) | |||
#undef JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY | |||
#define JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY 1 | |||
#endif | |||
#include "../../juce_audio_processors/format_types/juce_VST3Headers.h" | |||
#undef JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY | |||
#include "../utility/juce_CheckSettingMacros.h" | |||
#include "../utility/juce_IncludeModuleHeaders.h" | |||
#include "../utility/juce_WindowsHooks.h" | |||
#include "../utility/juce_PluginBusUtilities.h" | |||
#include "../utility/juce_FakeMouseMoveGenerator.h" | |||
#include "../../juce_audio_processors/format_types/juce_VST3Common.h" | |||
#ifndef JUCE_VST3_CAN_REPLACE_VST2 | |||
@@ -68,9 +76,9 @@ using namespace Steinberg; | |||
extern void updateEditorCompBoundsVST (Component*); | |||
#endif | |||
extern void* attachComponentToWindowRefVST (Component*, void* parentWindowOrView, bool isNSView); | |||
extern void detachComponentFromWindowRefVST (Component*, void* nsWindow, bool isNSView); | |||
extern void setNativeHostWindowSizeVST (void* window, Component*, int newWidth, int newHeight, bool isNSView); | |||
extern JUCE_API void* attachComponentToWindowRefVST (Component*, void* parentWindowOrView, bool isNSView); | |||
extern JUCE_API void detachComponentFromWindowRefVST (Component*, void* nsWindow, bool isNSView); | |||
extern JUCE_API void setNativeHostWindowSizeVST (void* window, Component*, int newWidth, int newHeight, bool isNSView); | |||
#endif | |||
//============================================================================== | |||
@@ -100,6 +108,8 @@ private: | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceAudioProcessor) | |||
}; | |||
class JuceVST3Component; | |||
//============================================================================== | |||
class JuceVST3EditController : public Vst::EditController, | |||
public Vst::IMidiMapping, | |||
@@ -107,6 +117,9 @@ class JuceVST3EditController : public Vst::EditController, | |||
{ | |||
public: | |||
JuceVST3EditController (Vst::IHostApplication* host) | |||
#if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
: usingManagedParameter (false) | |||
#endif | |||
{ | |||
if (host != nullptr) | |||
host->queryInterface (FUnknown::iid, (void**) &hostContext); | |||
@@ -178,11 +191,18 @@ public: | |||
} | |||
//============================================================================== | |||
enum InternalParameters | |||
{ | |||
paramPreset = 0x70727374, // 'prst' | |||
paramBypass = 0x62797073, // 'byps' | |||
paramMidiControllerOffset = 0x6d636d00 // 'mdm*' | |||
}; | |||
struct Param : public Vst::Parameter | |||
{ | |||
Param (AudioProcessor& p, int index) : owner (p), paramIndex (index) | |||
Param (AudioProcessor& p, int index, Vst::ParamID paramID) : owner (p), paramIndex (index) | |||
{ | |||
info.id = (Vst::ParamID) index; | |||
info.id = paramID; | |||
toString128 (info.title, p.getParameterName (index)); | |||
toString128 (info.shortTitle, p.getParameterName (index, 8)); | |||
toString128 (info.units, p.getParameterLabel (index)); | |||
@@ -204,6 +224,8 @@ public: | |||
if (v != valueNormalized) | |||
{ | |||
valueNormalized = v; | |||
owner.setParameter (paramIndex, static_cast<float> (v)); | |||
changed(); | |||
return true; | |||
} | |||
@@ -249,9 +271,9 @@ public: | |||
//============================================================================== | |||
struct BypassParam : public Vst::Parameter | |||
{ | |||
BypassParam (AudioProcessor& p, int index) : owner (p), paramIndex (index) | |||
BypassParam (AudioProcessor& p, Vst::ParamID vstParamID) : owner (p) | |||
{ | |||
info.id = (Vst::ParamID) index; | |||
info.id = vstParamID; | |||
toString128 (info.title, "Bypass"); | |||
toString128 (info.shortTitle, "Bypass"); | |||
toString128 (info.units, ""); | |||
@@ -327,11 +349,83 @@ public: | |||
private: | |||
AudioProcessor& owner; | |||
int paramIndex; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BypassParam) | |||
}; | |||
//============================================================================== | |||
struct ProgramChangeParameter : public Vst::Parameter | |||
{ | |||
ProgramChangeParameter (AudioProcessor& p) : owner (p) | |||
{ | |||
jassert (owner.getNumPrograms() > 1); | |||
info.id = paramPreset; | |||
toString128 (info.title, "Program"); | |||
toString128 (info.shortTitle, "Program"); | |||
toString128 (info.units, ""); | |||
info.stepCount = owner.getNumPrograms() - 1; | |||
info.defaultNormalizedValue = static_cast<Vst::ParamValue> (owner.getCurrentProgram()) / static_cast<Vst::ParamValue> (info.stepCount); | |||
info.unitId = Vst::kRootUnitId; | |||
info.flags = Vst::ParameterInfo::kIsProgramChange | Vst::ParameterInfo::kCanAutomate; | |||
} | |||
virtual ~ProgramChangeParameter() {} | |||
bool setNormalized (Vst::ParamValue v) override | |||
{ | |||
Vst::ParamValue program = v * info.stepCount; | |||
if (! isPositiveAndBelow ((int) program, owner.getNumPrograms())) | |||
return false; | |||
if (valueNormalized != v) | |||
{ | |||
valueNormalized = v; | |||
changed(); | |||
return true; | |||
} | |||
return false; | |||
} | |||
void toString (Vst::ParamValue value, Vst::String128 result) const override | |||
{ | |||
Vst::ParamValue program = value * info.stepCount; | |||
toString128 (result, owner.getProgramName ((int) program)); | |||
} | |||
bool fromString (const Vst::TChar* text, Vst::ParamValue& outValueNormalized) const override | |||
{ | |||
const String paramValueString (getStringFromVstTChars (text)); | |||
const int n = owner.getNumPrograms(); | |||
for (int i = 0; i < n; ++i) | |||
{ | |||
if (paramValueString == owner.getProgramName (i)) | |||
{ | |||
outValueNormalized = static_cast<Vst::ParamValue> (i) / info.stepCount; | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
static String getStringFromVstTChars (const Vst::TChar* text) | |||
{ | |||
return juce::String (juce::CharPointer_UTF16 (reinterpret_cast<const juce::CharPointer_UTF16::CharType*> (text))); | |||
} | |||
Vst::ParamValue toPlain (Vst::ParamValue v) const override { return v * info.stepCount; } | |||
Vst::ParamValue toNormalized (Vst::ParamValue v) const override { return v / info.stepCount; } | |||
private: | |||
AudioProcessor& owner; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProgramChangeParameter) | |||
}; | |||
//============================================================================== | |||
tresult PLUGIN_API setComponentState (IBStream* stream) override | |||
{ | |||
@@ -341,11 +435,18 @@ public: | |||
const int numParameters = pluginInstance->getNumParameters(); | |||
for (int i = 0; i < numParameters; ++i) | |||
setParamNormalized ((Vst::ParamID) i, (double) pluginInstance->getParameter (i)); | |||
setParamNormalized (getVSTParamIDForIndex (i), (double) pluginInstance->getParameter (i)); | |||
setParamNormalized ((Vst::ParamID) numParameters, audioProcessor->isBypassed ? 1.0f : 0.0f); | |||
setParamNormalized (bypassParamID, audioProcessor->isBypassed ? 1.0f : 0.0f); | |||
const int numPrograms = pluginInstance->getNumPrograms(); | |||
if (numPrograms > 1) | |||
setParamNormalized (paramPreset, static_cast<Vst::ParamValue> (pluginInstance->getCurrentProgram()) / static_cast<Vst::ParamValue> (numPrograms - 1)); | |||
} | |||
if (Vst::IComponentHandler* handler = getComponentHandler()) | |||
handler->restartComponent (Vst::kParamValuesChanged); | |||
return Vst::EditController::setComponentState (stream); | |||
} | |||
@@ -380,15 +481,15 @@ public: | |||
tresult PLUGIN_API getMidiControllerAssignment (Steinberg::int32 /*busIndex*/, Steinberg::int16 channel, | |||
Vst::CtrlNumber midiControllerNumber, Vst::ParamID& resultID) override | |||
{ | |||
resultID = (Vst::ParamID) midiControllerToParameter[channel][midiControllerNumber]; | |||
resultID = midiControllerToParameter[channel][midiControllerNumber]; | |||
return kResultTrue; // Returning false makes some hosts stop asking for further MIDI Controller Assignments | |||
} | |||
// Converts an incoming parameter index to a MIDI controller: | |||
bool getMidiControllerForParameter (int index, int& channel, int& ctrlNumber) | |||
bool getMidiControllerForParameter (Vst::ParamID index, int& channel, int& ctrlNumber) | |||
{ | |||
const int mappedIndex = index - parameterToMidiControllerOffset; | |||
const int mappedIndex = static_cast<int> (index - parameterToMidiControllerOffset); | |||
if (isPositiveAndBelow (mappedIndex, numElementsInArray (parameterToMidiController))) | |||
{ | |||
@@ -405,6 +506,13 @@ public: | |||
return false; | |||
} | |||
inline bool isMidiControllerParamID (Vst::ParamID paramID) const noexcept | |||
{ | |||
return (paramID >= parameterToMidiControllerOffset | |||
&& isPositiveAndBelow (paramID - parameterToMidiControllerOffset, | |||
static_cast<Vst::ParamID> (numElementsInArray (parameterToMidiController)))); | |||
} | |||
//============================================================================== | |||
IPlugView* PLUGIN_API createView (const char* name) override | |||
{ | |||
@@ -421,19 +529,26 @@ public: | |||
} | |||
//============================================================================== | |||
void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index) override { beginEdit ((Vst::ParamID) index); } | |||
void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index) override { beginEdit (getVSTParamIDForIndex (index)); } | |||
void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue) override | |||
{ | |||
// NB: Cubase has problems if performEdit is called without setParamNormalized | |||
EditController::setParamNormalized ((Vst::ParamID) index, (double) newValue); | |||
performEdit ((Vst::ParamID) index, (double) newValue); | |||
EditController::setParamNormalized (getVSTParamIDForIndex (index), (double) newValue); | |||
performEdit (getVSTParamIDForIndex (index), (double) newValue); | |||
} | |||
void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index) override { endEdit ((Vst::ParamID) index); } | |||
void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index) override { endEdit (getVSTParamIDForIndex (index)); } | |||
void audioProcessorChanged (AudioProcessor*) override | |||
{ | |||
if (AudioProcessor* pluginInstance = getPluginInstance()) | |||
{ | |||
if (pluginInstance->getNumPrograms() > 1) | |||
EditController::setParamNormalized (paramPreset, static_cast<Vst::ParamValue> (pluginInstance->getCurrentProgram()) | |||
/ static_cast<Vst::ParamValue> (pluginInstance->getNumPrograms() - 1)); | |||
} | |||
if (componentHandler != nullptr) | |||
componentHandler->restartComponent (Vst::kLatencyChanged | Vst::kParamValuesChanged); | |||
} | |||
@@ -448,6 +563,9 @@ public: | |||
} | |||
private: | |||
friend class JuceVST3Component; | |||
//============================================================================== | |||
ComSmartPtr<JuceAudioProcessor> audioProcessor; | |||
ScopedJuceInitialiser_GUI libraryInitialiser; | |||
@@ -460,9 +578,17 @@ private: | |||
}; | |||
enum { numMIDIChannels = 16 }; | |||
int parameterToMidiControllerOffset; | |||
Vst::ParamID parameterToMidiControllerOffset; | |||
MidiController parameterToMidiController[numMIDIChannels * Vst::kCountCtrlNumber]; | |||
int midiControllerToParameter[numMIDIChannels][Vst::kCountCtrlNumber]; | |||
Vst::ParamID midiControllerToParameter[numMIDIChannels][Vst::kCountCtrlNumber]; | |||
//============================================================================== | |||
#if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
bool usingManagedParameter; | |||
Array<Vst::ParamID> vstParamIDs; | |||
#endif | |||
Vst::ParamID bypassParamID; | |||
//============================================================================== | |||
void setupParameters() | |||
@@ -471,40 +597,61 @@ private: | |||
{ | |||
pluginInstance->addListener (this); | |||
#if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
const bool usingManagedParameter = false; | |||
#endif | |||
if (parameters.getParameterCount() <= 0) | |||
{ | |||
const int numParameters = pluginInstance->getNumParameters(); | |||
#if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
usingManagedParameter = (pluginInstance->getParameters().size() == numParameters); | |||
#endif | |||
for (int i = 0; i < numParameters; ++i) | |||
parameters.addParameter (new Param (*pluginInstance, i)); | |||
{ | |||
#if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
const Vst::ParamID vstParamID = static_cast<Vst::ParamID> (i); | |||
#else | |||
const Vst::ParamID vstParamID = generateVSTParamIDForIndex (pluginInstance, i); | |||
vstParamIDs.add (vstParamID); | |||
#endif | |||
parameters.addParameter (new Param (*pluginInstance, i, vstParamID)); | |||
} | |||
bypassParamID = static_cast<Vst::ParamID> (usingManagedParameter ? paramBypass : numParameters); | |||
parameters.addParameter (new BypassParam (*pluginInstance, bypassParamID)); | |||
parameters.addParameter (new BypassParam (*pluginInstance, numParameters)); | |||
if (pluginInstance->getNumPrograms() > 1) | |||
parameters.addParameter (new ProgramChangeParameter (*pluginInstance)); | |||
} | |||
#if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS | |||
// (NB: the +1 is to account for the bypass parameter) | |||
initialiseMidiControllerMappings (pluginInstance->getNumParameters() + 1); | |||
parameterToMidiControllerOffset = static_cast<Vst::ParamID> (usingManagedParameter ? paramMidiControllerOffset | |||
: parameters.getParameterCount()); | |||
initialiseMidiControllerMappings(); | |||
#endif | |||
audioProcessorChanged (pluginInstance); | |||
} | |||
} | |||
void initialiseMidiControllerMappings (const int numVstParameters) | |||
void initialiseMidiControllerMappings() | |||
{ | |||
parameterToMidiControllerOffset = numVstParameters; | |||
for (int c = 0, p = 0; c < numMIDIChannels; ++c) | |||
{ | |||
for (int i = 0; i < Vst::kCountCtrlNumber; ++i, ++p) | |||
{ | |||
midiControllerToParameter[c][i] = p + parameterToMidiControllerOffset; | |||
midiControllerToParameter[c][i] = static_cast<Vst::ParamID> (p) + parameterToMidiControllerOffset; | |||
parameterToMidiController[p].channel = c; | |||
parameterToMidiController[p].ctrlNumber = i; | |||
parameters.addParameter (new Vst::Parameter (toString ("MIDI CC " + String (c) + "|" + String (i)), | |||
(Vst::ParamID) (p + parameterToMidiControllerOffset), 0, 0, 0, | |||
Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId)); | |||
static_cast<Vst::ParamID> (p) + parameterToMidiControllerOffset, 0, 0, 0, | |||
Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId)); | |||
} | |||
} | |||
} | |||
@@ -522,6 +669,34 @@ private: | |||
} | |||
} | |||
//============================================================================== | |||
#if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
inline Vst::ParamID getVSTParamIDForIndex (int paramIndex) const noexcept { return static_cast<Vst::ParamID> (paramIndex); } | |||
#else | |||
static Vst::ParamID generateVSTParamIDForIndex (AudioProcessor* const pluginFilter, int paramIndex) | |||
{ | |||
jassert (pluginFilter != nullptr); | |||
const int n = pluginFilter->getNumParameters(); | |||
const bool managedParameter = (pluginFilter->getParameters().size() == n); | |||
if (isPositiveAndBelow (paramIndex, n)) | |||
{ | |||
const String& juceParamID = pluginFilter->getParameterID (paramIndex); | |||
return managedParameter ? static_cast<Vst::ParamID> (juceParamID.hashCode()) | |||
: static_cast<Vst::ParamID> (juceParamID.getIntValue()); | |||
} | |||
return static_cast<Vst::ParamID> (-1); | |||
} | |||
inline Vst::ParamID getVSTParamIDForIndex (int paramIndex) const noexcept | |||
{ | |||
return usingManagedParameter ? vstParamIDs.getReference (paramIndex) | |||
: static_cast<Vst::ParamID> (paramIndex); | |||
} | |||
#endif | |||
//============================================================================== | |||
class JuceVST3Editor : public Vst::EditorView | |||
{ | |||
@@ -625,14 +800,30 @@ private: | |||
return kResultFalse; | |||
} | |||
tresult PLUGIN_API canResize() override { return kResultTrue; } | |||
tresult PLUGIN_API canResize() override | |||
{ | |||
if (component != nullptr) | |||
if (AudioProcessorEditor* editor = component->pluginEditor) | |||
return editor->isResizable() ? kResultTrue : kResultFalse; | |||
return kResultFalse; | |||
} | |||
tresult PLUGIN_API checkSizeConstraint (ViewRect* rectToCheck) override | |||
{ | |||
if (rectToCheck != nullptr && component != nullptr) | |||
{ | |||
rectToCheck->right = rectToCheck->left + component->getWidth(); | |||
rectToCheck->bottom = rectToCheck->top + component->getHeight(); | |||
// checkSizeConstraint | |||
Rectangle<int> juceRect = Rectangle<int>::leftTopRightBottom (rectToCheck->left, rectToCheck->top, | |||
rectToCheck->right, rectToCheck->bottom); | |||
if (AudioProcessorEditor* editor = component->pluginEditor) | |||
if (ComponentBoundsConstrainer* constrainer = editor->getConstrainer()) | |||
juceRect.setSize (jlimit (constrainer->getMinimumWidth(), constrainer->getMaximumWidth(), juceRect.getWidth()), | |||
jlimit (constrainer->getMinimumHeight(), constrainer->getMaximumHeight(), juceRect.getHeight())); | |||
rectToCheck->right = rectToCheck->left + juceRect.getWidth(); | |||
rectToCheck->bottom = rectToCheck->top + juceRect.getHeight(); | |||
return kResultTrue; | |||
} | |||
@@ -646,8 +837,8 @@ private: | |||
{ | |||
public: | |||
ContentWrapperComponent (JuceVST3Editor& editor, AudioProcessor& plugin) | |||
: owner (editor), | |||
pluginEditor (plugin.createEditorIfNeeded()) | |||
: pluginEditor (plugin.createEditorIfNeeded()), | |||
owner (editor) | |||
{ | |||
setOpaque (true); | |||
setBroughtToFrontOnMouseClick (true); | |||
@@ -661,6 +852,8 @@ private: | |||
setBounds (pluginEditor->getLocalBounds()); | |||
resizeHostWindow(); | |||
} | |||
ignoreUnused (fakeMouseGenerator); | |||
} | |||
~ContentWrapperComponent() | |||
@@ -718,9 +911,11 @@ private: | |||
} | |||
} | |||
ScopedPointer<AudioProcessorEditor> pluginEditor; | |||
private: | |||
JuceVST3Editor& owner; | |||
ScopedPointer<AudioProcessorEditor> pluginEditor; | |||
FakeMouseMoveGenerator fakeMouseGenerator; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentWrapperComponent) | |||
}; | |||
@@ -771,8 +966,7 @@ public: | |||
pluginInstance (createPluginFilterOfType (AudioProcessor::wrapperType_VST3)), | |||
host (h), | |||
isMidiInputBusEnabled (false), | |||
isMidiOutputBusEnabled (false), | |||
busUtils (*pluginInstance, false) | |||
isMidiOutputBusEnabled (false) | |||
{ | |||
#if JucePlugin_WantsMidiInput | |||
isMidiInputBusEnabled = true; | |||
@@ -782,10 +976,19 @@ public: | |||
isMidiOutputBusEnabled = true; | |||
#endif | |||
busUtils.findAllCompatibleLayouts(); | |||
#ifdef JucePlugin_PreferredChannelConfigurations | |||
short configs[][2] = {JucePlugin_PreferredChannelConfigurations}; | |||
const int numConfigs = sizeof (configs) / sizeof (short[2]); | |||
jassert (numConfigs > 0 && (configs[0][0] > 0 || configs[0][1] > 0)); | |||
pluginInstance->setPlayConfigDetails (configs[0][0], configs[0][1], 44100.0, 1024); | |||
#endif | |||
copyEnabledBuses (lastEnabledBusStates.inputBuses, pluginInstance->busArrangement.inputBuses, Vst::kInput); | |||
copyEnabledBuses (lastEnabledBusStates.outputBuses, pluginInstance->busArrangement.outputBuses, Vst::kOutput); | |||
// VST-3 requires your default layout to be non-discrete! | |||
// For example, your default layout must be mono, stereo, quadrophonic | |||
// and not AudioChannelSet::discreteChannels (2) etc. | |||
jassert (checkBusFormatsAreNotDiscrete()); | |||
comPluginInstance = new JuceAudioProcessor (pluginInstance); | |||
@@ -796,7 +999,11 @@ public: | |||
processSetup.sampleRate = 44100.0; | |||
processSetup.symbolicSampleSize = Vst::kSample32; | |||
vstBypassParameterId = pluginInstance->getNumParameters(); | |||
#if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
vstBypassParameterId = static_cast<Vst::ParamID> (pluginInstance->getNumParameters()); | |||
#else | |||
cacheParameterIDs(); | |||
#endif | |||
pluginInstance->setPlayHead (this); | |||
} | |||
@@ -1249,7 +1456,7 @@ public: | |||
{ | |||
if (listIndex == 0) | |||
{ | |||
info.id = paramPreset; | |||
info.id = JuceVST3EditController::paramPreset; | |||
info.programCount = (Steinberg::int32) getPluginInstance().getNumPrograms(); | |||
toString128 (info.name, TRANS("Factory Presets")); | |||
@@ -1264,7 +1471,7 @@ public: | |||
tresult PLUGIN_API getProgramName (Vst::ProgramListID listId, Steinberg::int32 programIndex, Vst::String128 name) override | |||
{ | |||
if (listId == paramPreset | |||
if (listId == JuceVST3EditController::paramPreset | |||
&& isPositiveAndBelow ((int) programIndex, getPluginInstance().getNumPrograms())) | |||
{ | |||
toString128 (name, getPluginInstance().getProgramName ((int) programIndex)); | |||
@@ -1335,8 +1542,7 @@ public: | |||
Steinberg::int32 PLUGIN_API getBusCount (Vst::MediaType type, Vst::BusDirection dir) override | |||
{ | |||
if (type == Vst::kAudio) | |||
return (dir == Vst::kInput ? pluginInstance->busArrangement.inputBuses | |||
: pluginInstance->busArrangement.outputBuses).size(); | |||
return pluginInstance->getBusCount (dir == Vst::kInput); | |||
if (type == Vst::kEvent) | |||
{ | |||
@@ -1350,33 +1556,25 @@ public: | |||
return 0; | |||
} | |||
static const AudioProcessor::AudioProcessorBus* getAudioBus (AudioProcessor::AudioBusArrangement& busArrangement, | |||
Vst::BusDirection dir, Steinberg::int32 index) noexcept | |||
{ | |||
const Array<AudioProcessor::AudioProcessorBus>& buses = dir == Vst::kInput ? busArrangement.inputBuses | |||
: busArrangement.outputBuses; | |||
return isPositiveAndBelow (index, static_cast<Steinberg::int32> (buses.size())) ? &buses.getReference (index) : nullptr; | |||
} | |||
const AudioProcessor::AudioProcessorBus* getAudioBus (Vst::BusDirection dir, Steinberg::int32 index) const noexcept | |||
{ | |||
return getAudioBus (pluginInstance->busArrangement, dir, index); | |||
} | |||
tresult PLUGIN_API getBusInfo (Vst::MediaType type, Vst::BusDirection dir, | |||
Steinberg::int32 index, Vst::BusInfo& info) override | |||
{ | |||
if (type == Vst::kAudio) | |||
{ | |||
if (const AudioProcessor::AudioProcessorBus* bus = getAudioBus (lastEnabledBusStates, dir, index)) | |||
if (const AudioProcessor::Bus* bus = pluginInstance->getBus (dir == Vst::kInput, index)) | |||
{ | |||
info.mediaType = Vst::kAudio; | |||
info.direction = dir; | |||
info.channelCount = bus->channels.size(); | |||
toString128 (info.name, bus->name); | |||
info.busType = index == 0 ? Vst::kMain : Vst::kAux; | |||
info.flags = busUtils.getSupportedBusLayouts (dir == Vst::kInput, index).isEnabledByDefault ? Vst::BusInfo::kDefaultActive : 0; | |||
info.channelCount = bus->getLastEnabledLayout().size(); | |||
toString128 (info.name, bus->getName()); | |||
#if JucePlugin_IsSynth | |||
info.busType = (dir == Vst::kInput && index > 0 ? Vst::kAux : Vst::kMain); | |||
#else | |||
info.busType = (index == 0 ? Vst::kMain : Vst::kAux); | |||
#endif | |||
info.flags = (bus->isEnabledByDefault()) ? Vst::BusInfo::kDefaultActive : 0; | |||
return kResultTrue; | |||
} | |||
} | |||
@@ -1386,11 +1584,11 @@ public: | |||
info.flags = Vst::BusInfo::kDefaultActive; | |||
#if JucePlugin_WantsMidiInput | |||
if (dir == Vst::kInput) | |||
if (dir == Vst::kInput && index == 0) | |||
{ | |||
info.mediaType = Vst::kEvent; | |||
info.direction = dir; | |||
info.channelCount = 0; | |||
info.channelCount = 16; | |||
toString128 (info.name, TRANS("MIDI Input")); | |||
info.busType = Vst::kMain; | |||
return kResultTrue; | |||
@@ -1398,11 +1596,11 @@ public: | |||
#endif | |||
#if JucePlugin_ProducesMidiOutput | |||
if (dir == Vst::kOutput) | |||
if (dir == Vst::kOutput && index == 0) | |||
{ | |||
info.mediaType = Vst::kEvent; | |||
info.direction = dir; | |||
info.channelCount = 0; | |||
info.channelCount = 16; | |||
toString128 (info.name, TRANS("MIDI Output")); | |||
info.busType = Vst::kMain; | |||
return kResultTrue; | |||
@@ -1430,73 +1628,59 @@ public: | |||
} | |||
if (type == Vst::kAudio) | |||
{ | |||
if (const AudioProcessor::AudioProcessorBus* bus = getAudioBus (dir, index)) | |||
{ | |||
if (state == (bus->channels.size() > 0)) | |||
return kResultTrue; | |||
AudioChannelSet newChannels; | |||
if (state) | |||
if (const AudioProcessor::AudioProcessorBus* lastBusState = getAudioBus (lastEnabledBusStates, dir, index)) | |||
newChannels = lastBusState->channels; | |||
if (pluginInstance->setPreferredBusArrangement (dir == Vst::kInput, index, newChannels)) | |||
return kResultTrue; | |||
} | |||
} | |||
if (AudioProcessor::Bus* bus = pluginInstance->getBus (dir == Vst::kInput, index)) | |||
return (bus->enable (state != 0) ? kResultTrue : kResultFalse); | |||
return kResultFalse; | |||
} | |||
void copyEnabledBuses (Array<AudioProcessor::AudioProcessorBus>& copies, | |||
const Array<AudioProcessor::AudioProcessorBus>& source, | |||
Vst::BusDirection dir) | |||
bool checkBusFormatsAreNotDiscrete() | |||
{ | |||
for (int i = 0; i < source.size(); ++i) | |||
{ | |||
AudioProcessor::AudioProcessorBus bus = source.getReference (i); | |||
const int numInputBuses = pluginInstance->getBusCount (true); | |||
const int numOutputBuses = pluginInstance->getBusCount (false); | |||
if (bus.channels.size() == 0 && i < copies.size()) | |||
bus = AudioProcessor::AudioProcessorBus (bus.name, copies.getReference (i).channels); | |||
for (int i = 0; i < numInputBuses; ++i) | |||
if (pluginInstance->getChannelLayoutOfBus (true, i).isDiscreteLayout()) | |||
return false; | |||
if (bus.channels.size() == 0) | |||
bus = AudioProcessor::AudioProcessorBus (bus.name, busUtils.getDefaultLayoutForBus (dir == Vst::kInput, i)); | |||
for (int i = 0; i < numOutputBuses; ++i) | |||
if (pluginInstance->getChannelLayoutOfBus (false, i).isDiscreteLayout()) | |||
return false; | |||
copies.set (i, bus); | |||
} | |||
return true; | |||
} | |||
tresult PLUGIN_API setBusArrangements (Vst::SpeakerArrangement* inputs, Steinberg::int32 numIns, | |||
Vst::SpeakerArrangement* outputs, Steinberg::int32 numOuts) override | |||
{ | |||
PluginBusUtilities::ScopedBusRestorer restorer (busUtils); | |||
const int numInputBuses = pluginInstance->getBusCount (true); | |||
const int numOutputBuses = pluginInstance->getBusCount (false); | |||
for (int i = 0; i < numIns; ++i) | |||
if (! pluginInstance->setPreferredBusArrangement (true, i, getChannelSetForSpeakerArrangement (inputs[i]))) | |||
return kInvalidArgument; | |||
if (numIns > numInputBuses || numOuts > numOutputBuses) | |||
return false; | |||
for (int i = 0; i < numOuts; ++i) | |||
if (! pluginInstance->setPreferredBusArrangement (false, i, getChannelSetForSpeakerArrangement (outputs[i]))) | |||
return kInvalidArgument; | |||
AudioProcessor::BusesLayout requested = pluginInstance->getBusesLayout(); | |||
restorer.release(); | |||
for (int i = 0; i < numIns; ++i) | |||
requested.getChannelSet (true, i) = getChannelSetForSpeakerArrangement (inputs[i]); | |||
preparePlugin (getPluginInstance().getSampleRate(), | |||
getPluginInstance().getBlockSize()); | |||
for (int i = 0; i < numOuts; ++i) | |||
requested.getChannelSet (false, i) = getChannelSetForSpeakerArrangement (outputs[i]); | |||
copyEnabledBuses (lastEnabledBusStates.inputBuses, pluginInstance->busArrangement.inputBuses, Vst::kInput); | |||
copyEnabledBuses (lastEnabledBusStates.outputBuses, pluginInstance->busArrangement.outputBuses, Vst::kOutput); | |||
#ifdef JucePlugin_PreferredChannelConfigurations | |||
short configs[][2] = {JucePlugin_PreferredChannelConfigurations}; | |||
if (! AudioProcessor::containsLayout (requested, configs)) | |||
return kResultFalse; | |||
#endif | |||
return kResultTrue; | |||
return (pluginInstance->setBusesLayoutWithoutEnabling (requested) ? kResultTrue : kResultFalse); | |||
} | |||
tresult PLUGIN_API getBusArrangement (Vst::BusDirection dir, Steinberg::int32 index, Vst::SpeakerArrangement& arr) override | |||
{ | |||
if (const AudioProcessor::AudioProcessorBus* bus = getAudioBus (lastEnabledBusStates, dir, index)) | |||
if (AudioProcessor::Bus* bus = pluginInstance->getBus (dir == Vst::kInput, index)) | |||
{ | |||
arr = getSpeakerArrangement (bus->channels); | |||
arr = getVst3SpeakerArrangement (bus->getLastEnabledLayout()); | |||
return kResultTrue; | |||
} | |||
@@ -1569,22 +1753,35 @@ public: | |||
if (paramQueue->getPoint (numPoints - 1, offsetSamples, value) == kResultTrue) | |||
{ | |||
const int id = (int) paramQueue->getParameterId(); | |||
const Vst::ParamID vstParamID = paramQueue->getParameterId(); | |||
if (isPositiveAndBelow (id, pluginInstance->getNumParameters())) | |||
pluginInstance->setParameter (id, static_cast<float> (value)); | |||
else if (id == vstBypassParameterId) | |||
if (vstParamID == vstBypassParameterId) | |||
setBypassed (static_cast<float> (value) != 0.0f); | |||
#if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS | |||
else | |||
addParameterChangeToMidiBuffer (offsetSamples, id, value); | |||
else if (juceVST3EditController->isMidiControllerParamID (vstParamID)) | |||
addParameterChangeToMidiBuffer (offsetSamples, vstParamID, value); | |||
#endif | |||
else if (vstParamID == JuceVST3EditController::paramPreset) | |||
{ | |||
const int numPrograms = pluginInstance->getNumPrograms(); | |||
const int programValue = roundToInt (value * numPrograms); | |||
if (numPrograms > 1 && isPositiveAndBelow (programValue, numPrograms) | |||
&& programValue != pluginInstance->getCurrentProgram()) | |||
pluginInstance->setCurrentProgram (programValue); | |||
} | |||
else | |||
{ | |||
const int index = getJuceIndexForVSTParamID (vstParamID); | |||
if (isPositiveAndBelow (index, pluginInstance->getNumParameters())) | |||
pluginInstance->setParameter (index, static_cast<float> (value)); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
void addParameterChangeToMidiBuffer (const Steinberg::int32 offsetSamples, const int id, const double value) | |||
void addParameterChangeToMidiBuffer (const Steinberg::int32 offsetSamples, const Vst::ParamID id, const double value) | |||
{ | |||
// If the parameter is mapped to a MIDI CC message then insert it into the midiBuffer. | |||
int channel, ctrlNumber; | |||
@@ -1625,10 +1822,6 @@ public: | |||
MidiEventList::toMidiBuffer (midiBuffer, *data.inputEvents); | |||
#endif | |||
#if JUCE_DEBUG && ! JucePlugin_ProducesMidiOutput | |||
const int numMidiEventsComingIn = midiBuffer.getNumEvents(); | |||
#endif | |||
if (getHostType().isWavelab()) | |||
{ | |||
const int numInputChans = (data.inputs != nullptr && data.inputs[0].channelBuffers32 != nullptr) ? (int) data.inputs[0].numChannels : 0; | |||
@@ -1646,22 +1839,6 @@ public: | |||
#if JucePlugin_ProducesMidiOutput | |||
if (data.outputEvents != nullptr) | |||
MidiEventList::toEventList (*data.outputEvents, midiBuffer); | |||
#elif JUCE_DEBUG | |||
/* This assertion is caused when you've added some events to the | |||
midiMessages array in your processBlock() method, which usually means | |||
that you're trying to send them somewhere. But in this case they're | |||
getting thrown away. | |||
If your plugin does want to send MIDI messages, you'll need to set | |||
the JucePlugin_ProducesMidiOutput macro to 1 in your | |||
JucePluginCharacteristics.h file. | |||
If you don't want to produce any MIDI output, then you should clear the | |||
midiMessages array at the end of your processBlock() method, to | |||
indicate that you don't want any of the events to be passed through | |||
to the output. | |||
*/ | |||
jassert (midiBuffer.getNumEvents() <= numMidiEventsComingIn); | |||
#endif | |||
return kResultTrue; | |||
@@ -1687,13 +1864,16 @@ private: | |||
Array<float*> channelListFloat; | |||
Array<double*> channelListDouble; | |||
AudioProcessor::AudioBusArrangement lastEnabledBusStates; | |||
bool isMidiInputBusEnabled, isMidiOutputBusEnabled; | |||
PluginBusUtilities busUtils; | |||
ScopedJuceInitialiser_GUI libraryInitialiser; | |||
int vstBypassParameterId; | |||
#if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
bool usingManagedParameter; | |||
Array<Vst::ParamID> vstParamIDs; | |||
HashMap<int32, int> paramMap; | |||
#endif | |||
Vst::ParamID vstBypassParameterId; | |||
static const char* kJucePrivateDataIdentifier; | |||
@@ -1715,7 +1895,8 @@ private: | |||
const int numChans = jmin ((int) data.inputs[bus].numChannels, plugInInputChannels - totalInputChans); | |||
for (int i = 0; i < numChans; ++i) | |||
channelList.set (totalInputChans++, busChannels[i]); | |||
if (busChannels[i] != nullptr) | |||
channelList.set (totalInputChans++, busChannels[i]); | |||
} | |||
} | |||
} | |||
@@ -1732,10 +1913,16 @@ private: | |||
for (int i = 0; i < numChans; ++i) | |||
{ | |||
if (totalOutputChans >= totalInputChans) | |||
channelList.set (totalOutputChans, busChannels[i]); | |||
++totalOutputChans; | |||
if (busChannels[i] != nullptr) | |||
{ | |||
if (totalOutputChans >= totalInputChans) | |||
{ | |||
FloatVectorOperations::clear (busChannels[i], data.numSamples); | |||
channelList.set (totalOutputChans, busChannels[i]); | |||
} | |||
++totalOutputChans; | |||
} | |||
} | |||
} | |||
} | |||
@@ -1754,6 +1941,10 @@ private: | |||
if (data.inputParameterChanges != nullptr) | |||
processParameterChanges (*data.inputParameterChanges); | |||
#if JUCE_DEBUG && ! JucePlugin_ProducesMidiOutput | |||
const int numMidiEventsComingIn = midiBuffer.getNumEvents(); | |||
#endif | |||
if (pluginInstance->isSuspended()) | |||
{ | |||
buffer.clear(); | |||
@@ -1769,6 +1960,24 @@ private: | |||
pluginInstance->processBlock (buffer, midiBuffer); | |||
} | |||
} | |||
#if JUCE_DEBUG && (! JucePlugin_ProducesMidiOutput) | |||
/* This assertion is caused when you've added some events to the | |||
midiMessages array in your processBlock() method, which usually means | |||
that you're trying to send them somewhere. But in this case they're | |||
getting thrown away. | |||
If your plugin does want to send MIDI messages, you'll need to set | |||
the JucePlugin_ProducesMidiOutput macro to 1 in your | |||
JucePluginCharacteristics.h file. | |||
If you don't want to produce any MIDI output, then you should clear the | |||
midiMessages array at the end of your processBlock() method, to | |||
indicate that you don't want any of the events to be passed through | |||
to the output. | |||
*/ | |||
jassert (midiBuffer.getNumEvents() <= numMidiEventsComingIn); | |||
#endif | |||
} | |||
if (data.outputs != nullptr) | |||
@@ -1783,9 +1992,9 @@ private: | |||
for (int i = 0; i < numChans; ++i) | |||
{ | |||
if (outChanIndex < totalInputChans) | |||
if (outChanIndex < totalInputChans && busChannels[i] != nullptr) | |||
FloatVectorOperations::copy (busChannels[i], buffer.getReadPointer (outChanIndex), (int) data.numSamples); | |||
else if (outChanIndex >= totalOutputChans) | |||
else if (outChanIndex >= totalOutputChans && busChannels[i] != nullptr) | |||
FloatVectorOperations::clear (busChannels[i], (int) data.numSamples); | |||
++outChanIndex; | |||
@@ -1809,12 +2018,6 @@ private: | |||
return AudioBusPointerHelper<FloatType>::impl (data); | |||
} | |||
//============================================================================== | |||
enum InternalParameters | |||
{ | |||
paramPreset = 'prst' | |||
}; | |||
void preparePlugin (double sampleRate, int bufferSize) | |||
{ | |||
AudioProcessor& p = getPluginInstance(); | |||
@@ -1823,6 +2026,45 @@ private: | |||
p.prepareToPlay (sampleRate, bufferSize); | |||
} | |||
//============================================================================== | |||
#if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
inline Vst::ParamID getVSTParamIDForIndex (int paramIndex) const noexcept { return static_cast<Vst::ParamID> (paramIndex); } | |||
inline int getJuceIndexForVSTParamID (Vst::ParamID paramID) const noexcept { return static_cast<int> (paramID); } | |||
#else | |||
void cacheParameterIDs() | |||
{ | |||
const int numParameters = pluginInstance->getNumParameters(); | |||
usingManagedParameter = (pluginInstance->getParameters().size() == numParameters); | |||
vstBypassParameterId = static_cast<Vst::ParamID> (usingManagedParameter ? JuceVST3EditController::paramBypass : numParameters); | |||
for (int i = 0; i < numParameters; ++i) | |||
{ | |||
const Vst::ParamID paramID = JuceVST3EditController::generateVSTParamIDForIndex (pluginInstance, i); | |||
// Consider yourself very unlucky if you hit this assertion. The hash code of your | |||
// parameter ids are not unique. | |||
jassert (! vstParamIDs.contains (static_cast<Vst::ParamID> (paramID))); | |||
vstParamIDs.add (paramID); | |||
paramMap.set (static_cast<int32> (paramID), i); | |||
} | |||
} | |||
inline Vst::ParamID getVSTParamIDForIndex (int paramIndex) const noexcept | |||
{ | |||
return usingManagedParameter ? vstParamIDs.getReference (paramIndex) | |||
: static_cast<Vst::ParamID> (paramIndex); | |||
} | |||
inline int getJuceIndexForVSTParamID (Vst::ParamID paramID) const noexcept | |||
{ | |||
return usingManagedParameter ? paramMap[static_cast<int32> (paramID)] | |||
: static_cast<int> (paramID); | |||
} | |||
#endif | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVST3Component) | |||
}; | |||
@@ -2211,6 +2453,8 @@ private: | |||
// The VST3 plugin entry point. | |||
JUCE_EXPORTED_FUNCTION IPluginFactory* PLUGIN_API GetPluginFactory() | |||
{ | |||
PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_VST3; | |||
#if JUCE_WINDOWS | |||
// Cunning trick to force this function to be exported. Life's too short to | |||
// faff around creating .def files for this kind of thing. | |||
@@ -22,12 +22,47 @@ | |||
============================================================================== | |||
*/ | |||
/******************************************************************************* | |||
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.txt file. | |||
BEGIN_JUCE_MODULE_DECLARATION | |||
ID: juce_audio_plugin_client | |||
vendor: juce | |||
version: 4.3.0 | |||
name: JUCE audio plugin wrapper classes | |||
description: Classes for building VST, VST3, AudioUnit, AAX and RTAS plugins. | |||
website: http://www.juce.com/juce | |||
license: GPL/Commercial | |||
dependencies: juce_gui_basics, juce_audio_basics, juce_audio_processors | |||
END_JUCE_MODULE_DECLARATION | |||
*******************************************************************************/ | |||
#ifndef JUCE_AUDIO_PLUGIN_CLIENT_H_INCLUDED | |||
#define JUCE_AUDIO_PLUGIN_CLIENT_H_INCLUDED | |||
#include "../juce_gui_basics/juce_gui_basics.h" | |||
#include "../juce_audio_basics/juce_audio_basics.h" | |||
#include "../juce_audio_processors/juce_audio_processors.h" | |||
#include <juce_gui_basics/juce_gui_basics.h> | |||
#include <juce_audio_basics/juce_audio_basics.h> | |||
#include <juce_audio_processors/juce_audio_processors.h> | |||
/** Config: JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
Enable this if you want to force JUCE to use a continuous parameter | |||
index to identify a parameter in a DAW (this was the default in old | |||
versions of JUCE). This is index is usually used by the DAW to save | |||
automation data and enabling this may mess up user's DAW projects. | |||
*/ | |||
#ifndef JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
#define JUCE_FORCE_USE_LEGACY_PARAM_IDS 0 | |||
#endif | |||
namespace juce | |||
{ | |||
@@ -0,0 +1,25 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#include "AAX/juce_AAX_Wrapper.cpp" |
@@ -0,0 +1,26 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#define JUCE_INCLUDED_AAX_IN_MM 1 | |||
#include "AAX/juce_AAX_Wrapper.cpp" |
@@ -0,0 +1,69 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#define UseExtendedThingResource 1 | |||
#include <AudioUnit/AudioUnit.r> | |||
//============================================================================== | |||
/* The AppConfig.h file should be a file in your project, containing info to describe the | |||
plugin's name, type, etc. The introjucer will generate this file automatically for you. | |||
You may need to adjust the include path of your project to make sure it can be | |||
found by this include statement. (Don't hack this file to change the include path) | |||
*/ | |||
#include "AppConfig.h" | |||
//============================================================================== | |||
// component resources for Audio Unit | |||
#define RES_ID 1000 | |||
#define COMP_TYPE JucePlugin_AUMainType | |||
#define COMP_SUBTYPE JucePlugin_AUSubType | |||
#define COMP_MANUF JucePlugin_AUManufacturerCode | |||
#define VERSION JucePlugin_VersionCode | |||
#define NAME JucePlugin_Manufacturer ": " JucePlugin_Name | |||
#define DESCRIPTION JucePlugin_Desc | |||
#define ENTRY_POINT JucePlugin_AUExportPrefixQuoted "Entry" | |||
#include "AUResources.r" | |||
//============================================================================== | |||
// component resources for Audio Unit Carbon View | |||
#ifndef BUILD_AU_CARBON_UI | |||
#define BUILD_AU_CARBON_UI 1 | |||
#endif | |||
#if BUILD_AU_CARBON_UI | |||
#define RES_ID 2000 | |||
#define COMP_TYPE kAudioUnitCarbonViewComponentType | |||
#define COMP_SUBTYPE JucePlugin_AUSubType | |||
#define COMP_MANUF JucePlugin_AUManufacturerCode | |||
#define VERSION JucePlugin_VersionCode | |||
#define NAME JucePlugin_Manufacturer ": " JucePlugin_Name " View" | |||
#define DESCRIPTION NAME | |||
#define ENTRY_POINT JucePlugin_AUExportPrefixQuoted "ViewEntry" | |||
#include "AUResources.r" | |||
#endif |
@@ -0,0 +1,25 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#include "AU/juce_AU_Wrapper.mm" |
@@ -0,0 +1,66 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#ifdef __clang__ | |||
#pragma clang diagnostic push | |||
#pragma clang diagnostic ignored "-Wparentheses" | |||
#pragma clang diagnostic ignored "-Wextra-tokens" | |||
#pragma clang diagnostic ignored "-Wcomment" | |||
#pragma clang diagnostic ignored "-Wconversion" | |||
#pragma clang diagnostic ignored "-Wunused-parameter" | |||
#pragma clang diagnostic ignored "-Wunused" | |||
#endif | |||
#ifdef _MSC_VER | |||
#pragma warning (push) | |||
// #pragma warning (disable : 4127) | |||
#endif | |||
#include "AU/CoreAudioUtilityClasses/AUBase.cpp" | |||
#include "AU/CoreAudioUtilityClasses/AUBuffer.cpp" | |||
#include "AU/CoreAudioUtilityClasses/AUCarbonViewBase.cpp" | |||
#include "AU/CoreAudioUtilityClasses/AUCarbonViewControl.cpp" | |||
#include "AU/CoreAudioUtilityClasses/AUCarbonViewDispatch.cpp" | |||
#include "AU/CoreAudioUtilityClasses/AUDispatch.cpp" | |||
#include "AU/CoreAudioUtilityClasses/AUInputElement.cpp" | |||
#include "AU/CoreAudioUtilityClasses/AUMIDIBase.cpp" | |||
#include "AU/CoreAudioUtilityClasses/AUOutputBase.cpp" | |||
#include "AU/CoreAudioUtilityClasses/AUOutputElement.cpp" | |||
#include "AU/CoreAudioUtilityClasses/AUScopeElement.cpp" | |||
#include "AU/CoreAudioUtilityClasses/CAAUParameter.cpp" | |||
#include "AU/CoreAudioUtilityClasses/CAAudioChannelLayout.cpp" | |||
#include "AU/CoreAudioUtilityClasses/CAMutex.cpp" | |||
#include "AU/CoreAudioUtilityClasses/CAStreamBasicDescription.cpp" | |||
#include "AU/CoreAudioUtilityClasses/CAVectorUnit.cpp" | |||
#include "AU/CoreAudioUtilityClasses/CarbonEventHandler.cpp" | |||
#include "AU/CoreAudioUtilityClasses/ComponentBase.cpp" | |||
#include "AU/CoreAudioUtilityClasses/MusicDeviceBase.cpp" | |||
#ifdef __clang__ | |||
#pragma clang diagnostic pop | |||
#endif | |||
#ifdef _MSC_VER | |||
#pragma warning (pop) | |||
#endif |
@@ -0,0 +1,25 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#include "AU/juce_AUv3_Wrapper.mm" |
@@ -0,0 +1,6 @@ | |||
/* | |||
This dummy file is added to the resources section of the project to | |||
force XCode to create some resources for the dpm. If there aren't any | |||
resources, PT will refuse to load the plugin.. | |||
*/ |
@@ -0,0 +1,25 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#include "RTAS/juce_RTAS_Wrapper.cpp" |
@@ -0,0 +1,25 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#include "RTAS/juce_RTAS_DigiCode1.cpp" |
@@ -0,0 +1,25 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#include "RTAS/juce_RTAS_DigiCode2.cpp" |
@@ -0,0 +1,25 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#include "RTAS/juce_RTAS_DigiCode3.cpp" |
@@ -0,0 +1,25 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#include "RTAS/juce_RTAS_WinUtilities.cpp" |
@@ -0,0 +1,25 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#include "RTAS/juce_RTAS_MacUtilities.mm" |
@@ -0,0 +1,33 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#if ! JUCE_MODULE_AVAILABLE_juce_audio_utils | |||
#error To compile AudioUnitv3 and/or Standalone plug-ins, you need to add the juce_audio_utils and juce_audio_devices modules! | |||
#endif | |||
#include "Standalone/juce_StandaloneFilterApp.cpp" | |||
#if ! JUCE_USE_CUSTOM_AU3_STANDALONE_APP | |||
START_JUCE_APPLICATION (StandaloneFilterApp); | |||
#endif |
@@ -0,0 +1,25 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#include "VST/juce_VST_Wrapper.cpp" |
@@ -0,0 +1,25 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#include "VST3/juce_VST3_Wrapper.cpp" |
@@ -0,0 +1,25 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#include "VST/juce_VST_Wrapper.mm" |
@@ -0,0 +1,25 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#include "utility/juce_PluginUtilities.cpp" |
@@ -26,7 +26,8 @@ | |||
// define all your plugin settings properly.. | |||
#if ! (JucePlugin_Build_VST || JucePlugin_Build_VST3 \ | |||
|| JucePlugin_Build_AU || JucePlugin_Build_RTAS || JucePlugin_Build_AAX \ | |||
|| JucePlugin_Build_AU || JucePlugin_Build_AUv3 \ | |||
||JucePlugin_Build_RTAS || JucePlugin_Build_AAX \ | |||
|| JucePlugin_Build_Standalone || JucePlugin_Build_LV2) | |||
#error "You need to enable at least one plugin format!" | |||
#endif | |||