1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-23 01:44:22 +00:00

Fix for audio plugin builds on win32. AudioProcessorGraph optimisations. Access to min/max values in audio thumbnails. More metadata support for wav and aiff formats.

This commit is contained in:
Julian Storer 2011-04-13 11:28:35 +01:00
parent 345c7aa23c
commit d97ce5f9ee
20 changed files with 2256 additions and 926 deletions

View file

@ -295,7 +295,7 @@ static XmlElement* createNodeXml (AudioProcessorGraph::Node* const node) noexcep
}
XmlElement* e = new XmlElement ("FILTER");
e->setAttribute ("uid", (int) node->id);
e->setAttribute ("uid", (int) node->nodeId);
e->setAttribute ("x", node->properties ["x"].toString());
e->setAttribute ("y", node->properties ["y"].toString());
e->setAttribute ("uiLastX", node->properties ["uiLastX"].toString());

View file

@ -55,7 +55,7 @@ PluginWindow::PluginWindow (Component* const uiComp,
void PluginWindow::closeCurrentlyOpenWindowsFor (const uint32 nodeId)
{
for (int i = activePluginWindows.size(); --i >= 0;)
if (activePluginWindows.getUnchecked(i)->owner->id == nodeId)
if (activePluginWindows.getUnchecked(i)->owner->nodeId == nodeId)
delete activePluginWindows.getUnchecked(i);
}
@ -839,9 +839,9 @@ void GraphEditorPanel::updateComponents()
{
const AudioProcessorGraph::Node::Ptr f (graph.getNode (i));
if (getComponentForFilter (f->id) == 0)
if (getComponentForFilter (f->nodeId) == 0)
{
FilterComponent* const comp = new FilterComponent (graph, f->id);
FilterComponent* const comp = new FilterComponent (graph, f->nodeId);
addAndMakeVisible (comp);
comp->update();
}

File diff suppressed because it is too large Load diff

View file

@ -73,7 +73,7 @@ namespace JuceDummyNamespace {}
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 53
#define JUCE_BUILDNUMBER 73
#define JUCE_BUILDNUMBER 74
/** Current Juce version number.
@ -23112,8 +23112,9 @@ public:
/** Returns one of the registered clients. */
TimeSliceClient* getClient (int index) const;
/** @internal */
#ifndef DOXYGEN
void run();
#endif
private:
CriticalSection callbackLock, listLock;
@ -33344,8 +33345,7 @@ public:
@see MessageManager, DeletedAtShutdown
*/
class JUCE_API JUCEApplication : public ApplicationCommandTarget,
private ActionListener
class JUCE_API JUCEApplication : public ApplicationCommandTarget
{
protected:
@ -33497,18 +33497,9 @@ public:
*/
const String getCommandLineParameters() const noexcept { return commandLineParameters; }
// These are used by the START_JUCE_APPLICATION() macro and aren't for public use.
/** @internal */
static int main (const String& commandLine);
/** @internal */
static int main (int argc, const char* argv[]);
/** @internal */
static void sendUnhandledException (const std::exception* e, const char* sourceFile, int lineNumber);
/** Returns true if this executable is running as an app (as opposed to being a plugin
or other kind of shared library. */
static inline bool isStandaloneApp() noexcept { return createInstance != 0; }
static inline bool isStandaloneApp() noexcept { return createInstance != 0; }
/** @internal */
ApplicationCommandTarget* getNextCommandTarget();
@ -33518,26 +33509,28 @@ public:
void getAllCommands (Array <CommandID>& commands);
/** @internal */
bool perform (const InvocationInfo& info);
/** @internal */
void actionListenerCallback (const String& message);
/** @internal */
#ifndef DOXYGEN
// The following methods are internal calls - not for public use.
static int main (const String& commandLine);
static int main (int argc, const char* argv[]);
static void sendUnhandledException (const std::exception* e, const char* sourceFile, int lineNumber);
bool initialiseApp (const String& commandLine);
/** @internal */
int shutdownApp();
/** @internal */
static void appWillTerminateByForce();
/** @internal */
typedef JUCEApplication* (*CreateInstanceFunction)();
/** @internal */
static CreateInstanceFunction createInstance;
#endif
private:
static JUCEApplication* appInstance;
String commandLineParameters;
ScopedPointer<InterProcessLock> appLock;
ScopedPointer<ActionListener> broadcastCallback;
int appReturnValue;
bool stillInitialising;
ScopedPointer<InterProcessLock> appLock;
static JUCEApplication* appInstance;
JUCE_DECLARE_NON_COPYABLE (JUCEApplication);
};
@ -36550,9 +36543,10 @@ public:
const Array <int> getPossibleBitDepths();
bool canDoStereo();
bool canDoMono();
#if JUCE_MAC
#if JUCE_MAC
bool canHandleFile (const File& fileToTest);
#endif
#endif
AudioFormatReader* createReaderFor (InputStream* sourceStream,
bool deleteStreamIfOpeningFails);
@ -37224,12 +37218,19 @@ public:
*/
float getApproximatePeak() const;
/** Reads the approximate min and max levels from a section of the thumbnail.
The lowest and highest samples are returned in minValue and maxValue, but obviously
because the thumb only stores low-resolution data, these numbers will only be a rough
approximation of the true values.
*/
void getApproximateMinMax (double startTime, double endTime, int channelIndex,
float& minValue, float& maxValue) const noexcept;
/** Returns the hash code that was set by setSource() or setReader(). */
int64 getHashCode() const;
#ifndef DOXYGEN
// (this is only public to avoid a VC6 bug)
class LevelDataSource;
class LevelDataSource; // (this is only public to avoid a VC6 bug)
#endif
private:
@ -47098,10 +47099,9 @@ public:
public:
/** The ID number assigned to this node.
This is assigned by the graph that owns it, and can't be changed.
*/
const uint32 id;
const uint32 nodeId;
/** The actual processor object that this node represents. */
AudioProcessor* getProcessor() const noexcept { return processor; }
@ -47125,7 +47125,7 @@ public:
const ScopedPointer<AudioProcessor> processor;
bool isPrepared;
Node (uint32 id, AudioProcessor* processor);
Node (uint32 nodeId, AudioProcessor* processor) noexcept;
void prepare (double sampleRate, int blockSize, AudioProcessorGraph* graph);
void unprepare();
@ -47141,6 +47141,9 @@ public:
{
public:
Connection (uint32 sourceNodeId, int sourceChannelIndex,
uint32 destNodeId, int destChannelIndex) noexcept;
/** The ID number of the node which is the input source for this connection.
@see AudioProcessorGraph::getNodeForId
*/
@ -48766,8 +48769,7 @@ public:
@returns the value that the callback function returns.
@see MessageManagerLock
*/
void* callFunctionOnMessageThread (MessageCallbackFunction* callback,
void* userData);
void* callFunctionOnMessageThread (MessageCallbackFunction* callback, void* userData);
/** Returns true if the caller-thread is the message thread. */
bool isThisTheMessageThread() const noexcept;
@ -48815,12 +48817,12 @@ public:
/** Deregisters a broadcast listener. */
void deregisterBroadcastListener (ActionListener* listener);
/** @internal */
#ifndef DOXYGEN
// Internal methods - do not use!
void deliverMessage (Message*);
/** @internal */
void deliverBroadcastMessage (const String&);
/** @internal */
~MessageManager() noexcept;
#endif
private:
@ -48946,7 +48948,6 @@ public:
~MessageManagerLock() noexcept;
/** Returns true if the lock was successfully acquired.
(See the constructor that takes a Thread for more info).
*/
bool lockWasGained() const noexcept { return locked; }
@ -56566,7 +56567,7 @@ protected:
/** @internal */
int getDesktopWindowStyleFlags() const;
#if JUCE_DEBUG
#if JUCE_DEBUG
/** Overridden to warn people about adding components directly to this component
instead of using setContentOwned().
@ -56581,7 +56582,7 @@ protected:
a base-class method call to Component::addAndMakeVisible(), to side-step this warning.
*/
void addAndMakeVisible (Component* child, int zOrder = -1);
#endif
#endif
ScopedPointer <ResizableCornerComponent> resizableCorner;
ScopedPointer <ResizableBorderComponent> resizableBorder;
@ -59438,6 +59439,7 @@ public:
and feel class how this is used. */
};
#ifndef DOXYGEN
/** @internal */
void paint (Graphics& g);
/** @internal */
@ -59460,6 +59462,7 @@ public:
void parentHierarchyChanged();
/** @internal */
const Rectangle<int> getTitleBarArea();
#endif
private:
@ -66942,8 +66945,9 @@ public:
void removeListener (Listener* listenerToRemove);
protected:
/** @internal */
#ifndef DOXYGEN
CameraDevice (const String& name, int index);
#endif
private:
void* internal;

View file

@ -38,6 +38,22 @@ BEGIN_JUCE_NAMESPACE
extern void juce_initialiseMacMainMenu();
#endif
//==============================================================================
class AppBroadcastCallback : public ActionListener
{
public:
AppBroadcastCallback() { MessageManager::getInstance()->registerBroadcastListener (this); }
~AppBroadcastCallback() { MessageManager::getInstance()->deregisterBroadcastListener (this); }
void actionListenerCallback (const String& message)
{
JUCEApplication* const app = JUCEApplication::getInstance();
if (app != 0 && message.startsWith (app->getApplicationName() + "/"))
app->anotherInstanceStarted (message.substring (app->getApplicationName().length() + 1));
}
};
//==============================================================================
JUCEApplication::JUCEApplication()
: appReturnValue (0),
@ -87,12 +103,6 @@ void JUCEApplication::setApplicationReturnValue (const int newReturnValue) noexc
appReturnValue = newReturnValue;
}
void JUCEApplication::actionListenerCallback (const String& message)
{
if (message.startsWith (getApplicationName() + "/"))
anotherInstanceStarted (message.substring (getApplicationName().length() + 1));
}
//==============================================================================
void JUCEApplication::unhandledException (const std::exception*,
const String&,
@ -149,7 +159,7 @@ bool JUCEApplication::initialiseApp (const String& commandLine)
{
commandLineParameters = commandLine.trim();
#if ! JUCE_IOS
#if ! JUCE_IOS
jassert (appLock == nullptr); // initialiseApp must only be called once!
if (! moreThanOneInstanceAllowed())
@ -165,17 +175,18 @@ bool JUCEApplication::initialiseApp (const String& commandLine)
return false;
}
}
#endif
#endif
// let the app do its setting-up..
initialise (commandLineParameters);
#if JUCE_MAC
#if JUCE_MAC
juce_initialiseMacMainMenu(); // needs to be called after the app object has created, to get its name
#endif
#endif
// register for broadcast new app messages
MessageManager::getInstance()->registerBroadcastListener (this);
#if ! JUCE_IOS
broadcastCallback = new AppBroadcastCallback();
#endif
stillInitialising = false;
return true;
@ -185,7 +196,7 @@ int JUCEApplication::shutdownApp()
{
jassert (appInstance == this);
MessageManager::getInstance()->deregisterBroadcastListener (this);
broadcastCallback = nullptr;
JUCE_TRY
{
@ -249,20 +260,20 @@ int JUCEApplication::main (int argc, const char* argv[])
{
JUCE_AUTORELEASEPOOL
#if ! JUCE_WINDOWS
#if ! JUCE_WINDOWS
jassert (createInstance != nullptr);
juce_Argv0 = argv[0];
#endif
#endif
#if JUCE_IOS
#if JUCE_IOS
return juce_iOSMain (argc, argv);
#else
#else
String cmd;
for (int i = 1; i < argc; ++i)
cmd << argv[i] << ' ';
return JUCEApplication::main (cmd);
#endif
#endif
}
#endif

View file

@ -87,8 +87,7 @@
@see MessageManager, DeletedAtShutdown
*/
class JUCE_API JUCEApplication : public ApplicationCommandTarget,
private ActionListener
class JUCE_API JUCEApplication : public ApplicationCommandTarget
{
protected:
//==============================================================================
@ -244,20 +243,11 @@ public:
*/
const String getCommandLineParameters() const noexcept { return commandLineParameters; }
//==============================================================================
// These are used by the START_JUCE_APPLICATION() macro and aren't for public use.
/** @internal */
static int main (const String& commandLine);
/** @internal */
static int main (int argc, const char* argv[]);
/** @internal */
static void sendUnhandledException (const std::exception* e, const char* sourceFile, int lineNumber);
/** Returns true if this executable is running as an app (as opposed to being a plugin
or other kind of shared library. */
static inline bool isStandaloneApp() noexcept { return createInstance != 0; }
static inline bool isStandaloneApp() noexcept { return createInstance != 0; }
//==============================================================================
/** @internal */
ApplicationCommandTarget* getNextCommandTarget();
/** @internal */
@ -266,26 +256,29 @@ public:
void getAllCommands (Array <CommandID>& commands);
/** @internal */
bool perform (const InvocationInfo& info);
/** @internal */
void actionListenerCallback (const String& message);
/** @internal */
//==============================================================================
#ifndef DOXYGEN
// The following methods are internal calls - not for public use.
static int main (const String& commandLine);
static int main (int argc, const char* argv[]);
static void sendUnhandledException (const std::exception* e, const char* sourceFile, int lineNumber);
bool initialiseApp (const String& commandLine);
/** @internal */
int shutdownApp();
/** @internal */
static void appWillTerminateByForce();
/** @internal */
typedef JUCEApplication* (*CreateInstanceFunction)();
/** @internal */
static CreateInstanceFunction createInstance;
#endif
private:
//==============================================================================
static JUCEApplication* appInstance;
String commandLineParameters;
ScopedPointer<InterProcessLock> appLock;
ScopedPointer<ActionListener> broadcastCallback;
int appReturnValue;
bool stillInitialising;
ScopedPointer<InterProcessLock> appLock;
static JUCEApplication* appInstance;
JUCE_DECLARE_NON_COPYABLE (JUCEApplication);
};

View file

@ -29,6 +29,7 @@ BEGIN_JUCE_NAMESPACE
#include "juce_AiffAudioFormat.h"
#include "../../io/streams/juce_BufferedInputStream.h"
#include "../../io/streams/juce_MemoryOutputStream.h"
#include "../../core/juce_PlatformUtilities.h"
#include "../../text/juce_LocalisedStrings.h"
@ -37,6 +38,215 @@ BEGIN_JUCE_NAMESPACE
static const char* const aiffFormatName = "AIFF file";
static const char* const aiffExtensions[] = { ".aiff", ".aif", 0 };
//==============================================================================
namespace AiffFileHelpers
{
inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); }
#if JUCE_MSVC
#pragma pack (push, 1)
#define PACKED
#elif JUCE_GCC
#define PACKED __attribute__((packed))
#else
#define PACKED
#endif
//==============================================================================
struct InstChunk
{
struct Loop
{
uint16 type; // these are different in AIFF and WAV
uint16 startIdentifier;
uint16 endIdentifier;
} PACKED;
int8 baseNote;
int8 detune;
int8 lowNote;
int8 highNote;
int8 lowVelocity;
int8 highVelocity;
int16 gain;
Loop sustainLoop;
Loop releaseLoop;
void copyTo (StringPairArray& values) const
{
values.set ("MidiUnityNote", String (baseNote));
values.set ("Detune", String (detune));
values.set ("LowNote", String (lowNote));
values.set ("HighNote", String (highNote));
values.set ("LowVelocity", String (lowVelocity));
values.set ("HighVelocity", String (highVelocity));
values.set ("Gain", String ((int16) ByteOrder::swapIfLittleEndian ((uint16) gain)));
values.set ("NumSampleLoops", String (2)); // always 2 with AIFF, WAV can have more
values.set ("Loop0Type", String (ByteOrder::swapIfLittleEndian (sustainLoop.type)));
values.set ("Loop0StartIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.startIdentifier)));
values.set ("Loop0EndIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.endIdentifier)));
values.set ("Loop1Type", String (ByteOrder::swapIfLittleEndian (releaseLoop.type)));
values.set ("Loop1StartIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.startIdentifier)));
values.set ("Loop1EndIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.endIdentifier)));
}
static void create (MemoryBlock& block, const StringPairArray& values)
{
if (values.getAllKeys().contains ("MidiUnityNote", true))
{
block.setSize ((sizeof (InstChunk) + 3) & ~3, true);
InstChunk* const inst = static_cast <InstChunk*> (block.getData());
inst->baseNote = (int8) values.getValue ("MidiUnityNote", "60").getIntValue();
inst->detune = (int8) values.getValue ("Detune", "0").getIntValue();
inst->lowNote = (int8) values.getValue ("LowNote", "0").getIntValue();
inst->highNote = (int8) values.getValue ("HighNote", "127").getIntValue();
inst->lowVelocity = (int8) values.getValue ("LowVelocity", "1").getIntValue();
inst->highVelocity = (int8) values.getValue ("HighVelocity", "127").getIntValue();
inst->gain = (int16) ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Gain", "0").getIntValue());
inst->sustainLoop.type = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop0Type", "0").getIntValue());
inst->sustainLoop.startIdentifier = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop0StartIdentifier", "0").getIntValue());
inst->sustainLoop.endIdentifier = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop0EndIdentifier", "0").getIntValue());
inst->releaseLoop.type = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop1Type", "0").getIntValue());
inst->releaseLoop.startIdentifier = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop1StartIdentifier", "0").getIntValue());
inst->releaseLoop.endIdentifier = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop1EndIdentifier", "0").getIntValue());
}
}
} PACKED;
#if JUCE_MSVC
#pragma pack (pop)
#endif
#undef PACKED
//==============================================================================
namespace MarkChunk
{
bool metaDataContainsZeroIdentifiers (const StringPairArray& values)
{
// (zero cue identifiers are valid for WAV but not for AIFF)
const String cueString ("Cue");
const String noteString ("CueNote");
const String identifierString ("Identifier");
const StringArray& keys = values.getAllKeys();
for (int i = 0; i < keys.size(); ++i)
{
const String key (keys[i]);
if (key.startsWith (noteString))
continue; // zero identifier IS valid in a COMT chunk
if (key.startsWith (cueString) && key.contains (identifierString))
{
const int value = values.getValue (key, "-1").getIntValue();
if (value == 0)
return true;
}
}
return false;
}
void create (MemoryBlock& block, const StringPairArray& values)
{
const int numCues = values.getValue ("NumCuePoints", "0").getIntValue();
if (numCues > 0)
{
MemoryOutputStream out (block, false);
out.writeShortBigEndian ((short) numCues);
const int numCueLabels = values.getValue ("NumCueLabels", "0").getIntValue();
const int idOffset = metaDataContainsZeroIdentifiers (values) ? 1 : 0; // can't have zero IDs in AIFF
#if JUCE_DEBUG
Array<int> identifiers;
#endif
for (int i = 0; i < numCues; ++i)
{
const String prefixCue ("Cue" + String (i));
const String prefixLabel ("CueLabel" + String (i));
const int identifier = idOffset + values.getValue (prefixCue + "Identifier", "1").getIntValue();
#if JUCE_DEBUG
jassert (! identifiers.contains (identifier));
identifiers.add (identifier);
#endif
const int offset = values.getValue (prefixCue + "Offset", "0").getIntValue();
String label (prefixLabel);
for (int labelIndex = 0; labelIndex < numCueLabels; ++labelIndex)
{
const String prefixLabel ("CueLabel" + String (labelIndex));
const int labelIdentifier = idOffset + values.getValue (prefixLabel + "Identifier", "1").getIntValue();
if (labelIdentifier == identifier)
{
label = values.getValue (prefixLabel + "Text", label);
break;
}
}
out.writeShortBigEndian ((short) identifier);
out.writeIntBigEndian (offset);
const int labelLength = jmin (254, label.getNumBytesAsUTF8()); // seems to need null terminator even though it's a pstring
out.writeByte ((char) labelLength + 1);
out.write (label.toUTF8(), labelLength);
out.writeByte (0);
}
if ((out.getDataSize() & 1) != 0)
out.writeByte (0);
}
}
}
//==============================================================================
namespace COMTChunk
{
void create (MemoryBlock& block, const StringPairArray& values)
{
const int numNotes = values.getValue ("NumCueNotes", "0").getIntValue();
if (numNotes > 0)
{
MemoryOutputStream out (block, false);
out.writeShortBigEndian ((short) numNotes);
for (int i = 0; i < numNotes; ++i)
{
const String prefix ("CueNote" + String (i));
out.writeIntBigEndian (values.getValue (prefix + "TimeStamp", "0").getIntValue());
out.writeShortBigEndian ((short) values.getValue (prefix + "Identifier", "0").getIntValue());
const String comment (values.getValue (prefix + "Text", String::empty));
out.write (comment.toUTF8(), jmin (comment.getNumBytesAsUTF8(), 65534));
out.writeByte (0);
if ((out.getDataSize() & 1) != 0)
out.writeByte (0);
}
}
}
}
}
//==============================================================================
class AiffAudioFormatReader : public AudioFormatReader
@ -50,6 +260,8 @@ public:
AiffAudioFormatReader (InputStream* in)
: AudioFormatReader (in, TRANS (aiffFormatName))
{
using namespace AiffFileHelpers;
if (input->readInt() == chunkName ("FORM"))
{
const int len = input->readIntBigEndian();
@ -131,6 +343,67 @@ public:
dataChunkStart = input->getPosition() + 4 + offset;
lengthInSamples = (bytesPerFrame > 0) ? jmin (lengthInSamples, (int64) (length / bytesPerFrame)) : 0;
}
else if (type == chunkName ("MARK"))
{
const uint16 numCues = (uint16) input->readShortBigEndian();
// these two are always the same for AIFF-read files
metadataValues.set ("NumCuePoints", String (numCues));
metadataValues.set ("NumCueLabels", String (numCues));
for (uint16 i = 0; i < numCues; ++i)
{
uint16 identifier = (uint16) input->readShortBigEndian();
uint32 offset = (uint32) input->readIntBigEndian();
uint8 stringLength = (uint8) input->readByte();
MemoryBlock textBlock;
input->readIntoMemoryBlock (textBlock, stringLength);
// if the stringLength is even then read one more byte as the
// string needs to be an even number of bytes INCLUDING the
// leading length character in the pascal string
if ((stringLength & 1) == 0)
input->readByte();
const String text = String::fromUTF8 ((const char*)textBlock.getData(), stringLength);
const String prefixCue ("Cue" + String (i));
metadataValues.set (prefixCue + "Identifier", String (identifier));
metadataValues.set (prefixCue + "Offset", String (offset));
const String prefixLabel ("CueLabel" + String (i));
metadataValues.set (prefixLabel + "Identifier", String (identifier));
metadataValues.set (prefixLabel + "Text", text);
}
}
else if (type == chunkName ("COMT"))
{
const uint16 numNotes = (uint16) input->readShortBigEndian();
metadataValues.set ("NumCueNotes", String (numNotes));
for (uint16 i = 0; i < numNotes; ++i)
{
uint32 timestamp = (uint32) input->readIntBigEndian();
uint16 identifier = (uint16) input->readShortBigEndian(); // may be zero in this case
uint16 stringLength = (uint16) input->readShortBigEndian();
MemoryBlock textBlock;
input->readIntoMemoryBlock (textBlock, stringLength + (stringLength & 1));
const String text = String::fromUTF8 ((const char*)textBlock.getData(), stringLength);
const String prefix ("CueNote" + String (i));
metadataValues.set (prefix + "TimeStamp", String (timestamp));
metadataValues.set (prefix + "Identifier", String (identifier));
metadataValues.set (prefix + "Text", text);
}
}
else if (type == chunkName ("INST"))
{
HeapBlock <InstChunk> inst;
inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1);
input->read (inst, length);
inst->copyTo (metadataValues);
}
else if ((hasGotVer && hasGotData && hasGotType)
|| chunkEnd < input->getPosition()
|| input->isExhausted())
@ -142,6 +415,9 @@ public:
}
}
}
if (metadataValues.size() > 0)
metadataValues.set ("MetaDataSource", "AIFF");
}
//==============================================================================
@ -211,8 +487,6 @@ public:
}
private:
static inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); }
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatReader);
};
@ -221,12 +495,28 @@ class AiffAudioFormatWriter : public AudioFormatWriter
{
public:
//==============================================================================
AiffAudioFormatWriter (OutputStream* out, double sampleRate_, unsigned int numChans, int bits)
AiffAudioFormatWriter (OutputStream* out, double sampleRate_,
unsigned int numChans, int bits,
const StringPairArray& metadataValues)
: AudioFormatWriter (out, TRANS (aiffFormatName), sampleRate_, numChans, bits),
lengthInSamples (0),
bytesWritten (0),
writeFailed (false)
{
using namespace AiffFileHelpers;
if (metadataValues.size() > 0)
{
// The meta data should have been santised for the AIFF format.
// If it was originally sourced from a WAV file the MetaDataSource
// key should be removed (or set to "AIFF") once this has been done
jassert (metadataValues.getValue ("MetaDataSource", "None") != "WAV");
MarkChunk::create (markChunk, metadataValues);
COMTChunk::create (comtChunk, metadataValues);
InstChunk::create (instChunk, metadataValues);
}
headerPosition = out->getPosition();
writeHeader();
}
@ -279,15 +569,15 @@ public:
}
private:
MemoryBlock tempBlock;
MemoryBlock tempBlock, markChunk, comtChunk, instChunk;
uint32 lengthInSamples, bytesWritten;
int64 headerPosition;
bool writeFailed;
static inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); }
void writeHeader()
{
using namespace AiffFileHelpers;
const bool couldSeekOk = output->setPosition (headerPosition);
(void) couldSeekOk;
@ -295,7 +585,9 @@ private:
// to be able to seek back to write the header
jassert (couldSeekOk);
const int headerLen = 54;
const int headerLen = 54 + (markChunk.getSize() > 0 ? markChunk.getSize() + 8 : 0)
+ (comtChunk.getSize() > 0 ? comtChunk.getSize() + 8 : 0)
+ (instChunk.getSize() > 0 ? instChunk.getSize() + 8 : 0);
int audioBytes = lengthInSamples * ((bitsPerSample * numChannels) / 8);
audioBytes += (audioBytes & 1);
@ -351,6 +643,27 @@ private:
output->write (sampleRateBytes, 10);
if (markChunk.getSize() > 0)
{
output->writeInt (chunkName ("MARK"));
output->writeIntBigEndian ((int) markChunk.getSize());
output->write (markChunk.getData(), (int) markChunk.getSize());
}
if (comtChunk.getSize() > 0)
{
output->writeInt (chunkName ("COMT"));
output->writeIntBigEndian ((int) comtChunk.getSize());
output->write (comtChunk.getData(), (int) comtChunk.getSize());
}
if (instChunk.getSize() > 0)
{
output->writeInt (chunkName ("INST"));
output->writeIntBigEndian ((int) instChunk.getSize());
output->write (instChunk.getData(), (int) instChunk.getSize());
}
output->writeInt (chunkName ("SSND"));
output->writeIntBigEndian (audioBytes + 8);
output->writeInt (0);
@ -416,11 +729,11 @@ AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out,
double sampleRate,
unsigned int numberOfChannels,
int bitsPerSample,
const StringPairArray& /*metadataValues*/,
const StringPairArray& metadataValues,
int /*qualityOptionIndex*/)
{
if (getPossibleBitDepths().contains (bitsPerSample))
return new AiffAudioFormatWriter (out, sampleRate, numberOfChannels, bitsPerSample);
return new AiffAudioFormatWriter (out, sampleRate, numberOfChannels, bitsPerSample, metadataValues);
return nullptr;
}

View file

@ -50,9 +50,10 @@ public:
const Array <int> getPossibleBitDepths();
bool canDoStereo();
bool canDoMono();
#if JUCE_MAC
#if JUCE_MAC
bool canHandleFile (const File& fileToTest);
#endif
#endif
//==============================================================================
AudioFormatReader* createReaderFor (InputStream* sourceStream,

View file

@ -38,7 +38,7 @@ struct AudioThumbnail::MinMaxValue
char minValue;
char maxValue;
MinMaxValue() : minValue (0), maxValue (0)
MinMaxValue() noexcept : minValue (0), maxValue (0)
{
}
@ -281,7 +281,7 @@ public:
return data.size();
}
void getMinMax (int startSample, int endSample, MinMaxValue& result) noexcept
void getMinMax (int startSample, int endSample, MinMaxValue& result) const noexcept
{
if (startSample >= 0)
{
@ -323,12 +323,12 @@ public:
dest[i] = source[i];
}
void resetPeak()
void resetPeak() noexcept
{
peakLevel = -1;
}
int getPeak()
int getPeak() noexcept
{
if (peakLevel < 0)
{
@ -743,6 +743,24 @@ float AudioThumbnail::getApproximatePeak() const
return jlimit (0, 127, peak) / 127.0f;
}
void AudioThumbnail::getApproximateMinMax (const double startTime, const double endTime, const int channelIndex,
float& minValue, float& maxValue) const noexcept
{
MinMaxValue result;
const ThumbData* const data = channels [channelIndex];
if (data != nullptr && sampleRate > 0)
{
const int firstThumbIndex = (int) ((startTime * sampleRate) / samplesPerThumbSample);
const int lastThumbIndex = (int) (((endTime * sampleRate) + samplesPerThumbSample - 1) / samplesPerThumbSample);
data->getMinMax (jmax (0, firstThumbIndex), lastThumbIndex, result);
}
minValue = result.minValue / 128.0f;
maxValue = result.maxValue / 128.0f;
}
void AudioThumbnail::drawChannel (Graphics& g, const Rectangle<int>& area, double startTime,
double endTime, int channelNum, float verticalZoomFactor)
{

View file

@ -186,12 +186,19 @@ public:
*/
float getApproximatePeak() const;
/** Reads the approximate min and max levels from a section of the thumbnail.
The lowest and highest samples are returned in minValue and maxValue, but obviously
because the thumb only stores low-resolution data, these numbers will only be a rough
approximation of the true values.
*/
void getApproximateMinMax (double startTime, double endTime, int channelIndex,
float& minValue, float& maxValue) const noexcept;
/** Returns the hash code that was set by setSource() or setReader(). */
int64 getHashCode() const;
#ifndef DOXYGEN
// (this is only public to avoid a VC6 bug)
class LevelDataSource;
class LevelDataSource; // (this is only public to avoid a VC6 bug)
#endif
private:

View file

@ -29,6 +29,7 @@ BEGIN_JUCE_NAMESPACE
#include "juce_WavAudioFormat.h"
#include "../../io/streams/juce_BufferedInputStream.h"
#include "../../io/streams/juce_MemoryOutputStream.h"
#include "../../text/juce_LocalisedStrings.h"
#include "../../io/files/juce_FileInputStream.h"
#include "../../io/files/juce_TemporaryFile.h"
@ -156,7 +157,7 @@ struct SMPLChunk
struct SampleLoop
{
uint32 identifier;
uint32 type;
uint32 type; // these are different in AIFF and WAV
uint32 start;
uint32 end;
uint32 fraction;
@ -214,8 +215,6 @@ struct SMPLChunk
SMPLChunk* const s = static_cast <SMPLChunk*> (data.getData());
// Allow these calls to overwrite an extra byte at the end, which is fine as long
// as they get called in the right order..
s->manufacturer = ByteOrder::swapIfBigEndian ((uint32) values.getValue ("Manufacturer", "0").getIntValue());
s->product = ByteOrder::swapIfBigEndian ((uint32) values.getValue ("Product", "0").getIntValue());
s->samplePeriod = ByteOrder::swapIfBigEndian ((uint32) values.getValue ("SamplePeriod", "0").getIntValue());
@ -241,6 +240,52 @@ struct SMPLChunk
}
} PACKED;
//==============================================================================
struct InstChunk
{
int8 baseNote;
int8 detune;
int8 gain;
int8 lowNote;
int8 highNote;
int8 lowVelocity;
int8 highVelocity;
void copyTo (StringPairArray& values) const
{
values.set ("MidiUnityNote", String (baseNote));
values.set ("Detune", String (detune));
values.set ("Gain", String (gain));
values.set ("LowNote", String (lowNote));
values.set ("HighNote", String (highNote));
values.set ("LowVelocity", String (lowVelocity));
values.set ("HighVelocity", String (highVelocity));
}
static MemoryBlock createFrom (const StringPairArray& values)
{
const StringArray& keys = values.getAllKeys();
if (! (keys.contains ("LowNote", true) && keys.contains ("HighNote", true)))
return MemoryBlock();
MemoryBlock data (8);
data.fillWith (0);
InstChunk* const inst = static_cast <InstChunk*> (data.getData());
inst->baseNote = (int8) values.getValue ("MidiUnityNote", "60").getIntValue();
inst->detune = (int8) values.getValue ("Detune", "0").getIntValue();
inst->gain = (int8) values.getValue ("Gain", "0").getIntValue();
inst->lowNote = (int8) values.getValue ("LowNote", "0").getIntValue();
inst->highNote = (int8) values.getValue ("HighNote", "127").getIntValue();
inst->lowVelocity = (int8) values.getValue ("LowVelocity", "1").getIntValue();
inst->highVelocity = (int8) values.getValue ("HighVelocity", "127").getIntValue();
return data;
}
} PACKED;
//==============================================================================
struct CueChunk
{
@ -276,39 +321,118 @@ struct CueChunk
}
}
static MemoryBlock createFrom (const StringPairArray& values)
static void create (MemoryBlock& data, const StringPairArray& values)
{
const int numCues = values.getValue ("NumCuePoints", "0").getIntValue();
if (numCues <= 0)
return MemoryBlock();
const size_t sizeNeeded = sizeof (CueChunk) + (numCues - 1) * sizeof (Cue);
MemoryBlock data ((sizeNeeded + 3) & ~3);
data.fillWith (0);
CueChunk* const c = static_cast <CueChunk*> (data.getData());
c->numCues = ByteOrder::swapIfBigEndian ((uint32) numCues);
const String dataChunkID (chunkName ("data"));
for (int i = 0; i < numCues; ++i)
if (numCues > 0)
{
const String prefix ("Cue" + String(i));
c->cues[i].identifier = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "Identifier", "0").getIntValue());
c->cues[i].order = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "Order", "0").getIntValue());
c->cues[i].chunkID = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkID", dataChunkID).getIntValue());
c->cues[i].chunkStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkStart", "0").getIntValue());
c->cues[i].blockStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "BlockStart", "0").getIntValue());
c->cues[i].offset = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "Offset", "0").getIntValue());
}
const size_t sizeNeeded = sizeof (CueChunk) + (numCues - 1) * sizeof (Cue);
data.setSize ((sizeNeeded + 3) & ~3, true);
return data;
CueChunk* const c = static_cast <CueChunk*> (data.getData());
c->numCues = ByteOrder::swapIfBigEndian ((uint32) numCues);
const String dataChunkID (chunkName ("data"));
int nextOrder = 0;
#if JUCE_DEBUG
Array<int> identifiers;
#endif
for (int i = 0; i < numCues; ++i)
{
const String prefix ("Cue" + String (i));
uint32 identifier = values.getValue (prefix + "Identifier", "0").getIntValue();
#if JUCE_DEBUG
jassert (! identifiers.contains (identifier));
identifiers.add (identifier);
#endif
c->cues[i].identifier = ByteOrder::swapIfBigEndian ((uint32) identifier);
const int order = values.getValue (prefix + "Order", String (nextOrder)).getIntValue();
nextOrder = jmax (nextOrder, order) + 1;
c->cues[i].order = ByteOrder::swapIfBigEndian ((uint32) order);
c->cues[i].chunkID = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkID", dataChunkID).getIntValue());
c->cues[i].chunkStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkStart", "0").getIntValue());
c->cues[i].blockStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "BlockStart", "0").getIntValue());
c->cues[i].offset = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "Offset", "0").getIntValue());
}
}
}
} PACKED;
//==============================================================================
namespace ListChunk
{
void appendLabelOrNoteChunk (const StringPairArray& values, const String& prefix,
const int chunkType, MemoryOutputStream& out)
{
const String label (values.getValue (prefix + "Text", prefix));
const int labelLength = label.getNumBytesAsUTF8() + 1;
const int chunkLength = 4 + labelLength + (labelLength & 1);
out.writeInt (chunkType);
out.writeInt (chunkLength);
out.writeInt (values.getValue (prefix + "Identifier", "0").getIntValue());
out.write (label.toUTF8(), labelLength);
if ((out.getDataSize() & 1) != 0)
out.writeByte (0);
}
void appendExtraChunk (const StringPairArray& values, const String& prefix, MemoryOutputStream& out)
{
const String text (values.getValue (prefix + "Text", prefix));
const int textLength = text.getNumBytesAsUTF8() + 1; // include null terminator
uint32 chunkLength = textLength + 20 + (textLength & 1);
out.writeInt (chunkName ("ltxt"));
out.writeInt (chunkLength);
out.writeInt (values.getValue (prefix + "Identifier", "0").getIntValue());
out.writeInt (values.getValue (prefix + "SampleLength", "0").getIntValue());
out.writeInt (values.getValue (prefix + "Purpose", "0").getIntValue());
out.writeShort ((short) values.getValue (prefix + "Country", "0").getIntValue());
out.writeShort ((short) values.getValue (prefix + "Language", "0").getIntValue());
out.writeShort ((short) values.getValue (prefix + "Dialect", "0").getIntValue());
out.writeShort ((short) values.getValue (prefix + "CodePage", "0").getIntValue());
out.write (text.toUTF8(), textLength);
if ((out.getDataSize() & 1) != 0)
out.writeByte (0);
}
void create (MemoryBlock& block, const StringPairArray& values)
{
const int numCueLabels = values.getValue ("NumCueLabels", "0").getIntValue();
const int numCueNotes = values.getValue ("NumCueNotes", "0").getIntValue();
const int numCueRegions = values.getValue ("NumCueRegions", "0").getIntValue();
if (numCueLabels > 0 || numCueNotes > 0 || numCueRegions > 0)
{
MemoryOutputStream out (block, false);
int i;
for (i = 0; i < numCueLabels; ++i)
appendLabelOrNoteChunk (values, "CueLabel" + String (i), chunkName ("labl"), out);
for (i = 0; i < numCueNotes; ++i)
appendLabelOrNoteChunk (values, "CueNote" + String (i), chunkName ("note"), out);
for (i = 0; i < numCueRegions; ++i)
appendExtraChunk (values, "CueRegion" + String (i), out);
}
}
}
//==============================================================================
struct ExtensibleWavSubFormat
{
@ -355,6 +479,9 @@ public:
int64 end = 0;
bool hasGotType = false;
bool hasGotData = false;
int cueNoteIndex = 0;
int cueLabelIndex = 0;
int cueRegionIndex = 0;
const int firstChunkType = input->readInt();
@ -479,6 +606,13 @@ public:
input->read (smpl, length);
smpl->copyTo (metadataValues, length);
}
else if (chunkType == chunkName ("inst") || chunkType == chunkName ("INST")) // need to check which...
{
HeapBlock <InstChunk> inst;
inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1);
input->read (inst, length);
inst->copyTo (metadataValues);
}
else if (chunkType == chunkName ("cue "))
{
HeapBlock <CueChunk> cue;
@ -486,6 +620,65 @@ public:
input->read (cue, length);
cue->copyTo (metadataValues, length);
}
else if (chunkType == chunkName ("LIST"))
{
if (input->readInt() == chunkName ("adtl"))
{
while (input->getPosition() < chunkEnd)
{
const int adtlChunkType = input->readInt();
const uint32 adtlLength = (uint32) input->readInt();
const int64 adtlChunkEnd = input->getPosition() + (adtlLength + (adtlLength & 1));
if (adtlChunkType == chunkName ("labl") || adtlChunkType == chunkName ("note"))
{
String prefix;
if (adtlChunkType == chunkName ("labl"))
prefix << "CueLabel" << cueLabelIndex++;
else if (adtlChunkType == chunkName ("note"))
prefix << "CueNote" << cueNoteIndex++;
const uint32 identifier = (uint32) input->readInt();
const uint32 stringLength = adtlLength - 4;
MemoryBlock textBlock;
input->readIntoMemoryBlock (textBlock, stringLength);
const String text (String::fromUTF8 (static_cast <const char*> (textBlock.getData()), textBlock.getSize()));
metadataValues.set (prefix + "Identifier", String (identifier));
metadataValues.set (prefix + "Text", text);
}
else if (adtlChunkType == chunkName ("ltxt"))
{
const String prefix ("CueRegion" + String (cueRegionIndex++));
const uint32 identifier = (uint32) input->readInt();
const uint32 sampleLength = (uint32) input->readInt();
const uint32 purpose = (uint32) input->readInt();
const uint16 country = (uint16) input->readInt();
const uint16 language = (uint16) input->readInt();
const uint16 dialect = (uint16) input->readInt();
const uint16 codePage = (uint16) input->readInt();
const uint32 stringLength = adtlLength - 20;
MemoryBlock textBlock;
input->readIntoMemoryBlock (textBlock, stringLength);
const String text = String::fromUTF8 ((const char*)textBlock.getData(), textBlock.getSize());
metadataValues.set (prefix + "Identifier", String (identifier));
metadataValues.set (prefix + "SampleLength", String (sampleLength));
metadataValues.set (prefix + "Purpose", String (purpose));
metadataValues.set (prefix + "Country", String (country));
metadataValues.set (prefix + "Language", String (language));
metadataValues.set (prefix + "Dialect", String (dialect));
metadataValues.set (prefix + "CodePage", String (codePage));
metadataValues.set (prefix + "Text", text);
}
input->setPosition (adtlChunkEnd);
}
}
}
else if (chunkEnd <= input->getPosition())
{
break;
@ -494,6 +687,11 @@ public:
input->setPosition (chunkEnd);
}
}
if (cueLabelIndex > 0) metadataValues.set ("NumCueLabels", String (cueLabelIndex));
if (cueNoteIndex > 0) metadataValues.set ("NumCueNotes", String (cueNoteIndex));
if (cueRegionIndex > 0) metadataValues.set ("NumCueRegions", String (cueRegionIndex));
if (metadataValues.size() > 0) metadataValues.set ("MetaDataSource", "WAV");
}
//==============================================================================
@ -576,9 +774,16 @@ public:
if (metadataValues.size() > 0)
{
// The meta data should have been santised for the WAV format.
// If it was originally sourced from an AIFF file the MetaDataSource
// key should be removed (or set to "WAV") once this has been done
jassert (metadataValues.getValue ("MetaDataSource", "None") != "AIFF");
bwavChunk = BWAVChunk::createFrom (metadataValues);
smplChunk = SMPLChunk::createFrom (metadataValues);
cueChunk = CueChunk ::createFrom (metadataValues);
instChunk = InstChunk::createFrom (metadataValues);
CueChunk ::create (cueChunk, metadataValues);
ListChunk::create (listChunk, metadataValues);
}
headerPosition = out->getPosition();
@ -636,7 +841,7 @@ public:
private:
ScopedPointer<AudioData::Converter> converter;
MemoryBlock tempBlock, bwavChunk, smplChunk, cueChunk;
MemoryBlock tempBlock, bwavChunk, smplChunk, instChunk, cueChunk, listChunk;
uint64 lengthInSamples, bytesWritten;
int64 headerPosition;
bool writeFailed;
@ -675,7 +880,9 @@ private:
+ 8 + audioDataSize + (audioDataSize & 1)
+ (bwavChunk.getSize() > 0 ? (8 + bwavChunk.getSize()) : 0)
+ (smplChunk.getSize() > 0 ? (8 + smplChunk.getSize()) : 0)
+ (instChunk.getSize() > 0 ? (8 + instChunk.getSize()) : 0)
+ (cueChunk .getSize() > 0 ? (8 + cueChunk .getSize()) : 0)
+ (listChunk.getSize() > 0 ? (12 + listChunk.getSize()) : 0)
+ (8 + 28); // (ds64 chunk)
riffChunkSize += (riffChunkSize & 0x1);
@ -754,6 +961,13 @@ private:
output->write (smplChunk.getData(), (int) smplChunk.getSize());
}
if (instChunk.getSize() > 0)
{
output->writeInt (chunkName ("inst"));
output->writeInt (7);
output->write (instChunk.getData(), (int) instChunk.getSize());
}
if (cueChunk.getSize() > 0)
{
output->writeInt (chunkName ("cue "));
@ -761,6 +975,14 @@ private:
output->write (cueChunk.getData(), (int) cueChunk.getSize());
}
if (listChunk.getSize() > 0)
{
output->writeInt (chunkName ("LIST"));
output->writeInt ((int) listChunk.getSize() + 4);
output->writeInt (chunkName ("adtl"));
output->write (listChunk.getData(), (int) listChunk.getSize());
}
output->writeInt (chunkName ("data"));
output->writeInt (isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame));

View file

@ -24,6 +24,9 @@
*/
#include "juce_IncludeCharacteristics.h"
#define DONT_AUTOLINK_TO_JUCE_LIBRARY 1
#include "../../../juce.h"
#ifndef __JUCE_PLUGINHEADERS_JUCEHEADER__

View file

@ -30,309 +30,8 @@ BEGIN_JUCE_NAMESPACE
#include "juce_AudioProcessorGraph.h"
#include "../../events/juce_MessageManager.h"
const int AudioProcessorGraph::midiChannelIndex = 0x1000;
//==============================================================================
AudioProcessorGraph::Node::Node (const uint32 id_, AudioProcessor* const processor_)
: id (id_),
processor (processor_),
isPrepared (false)
{
jassert (processor_ != nullptr);
}
void AudioProcessorGraph::Node::prepare (const double sampleRate, const int blockSize,
AudioProcessorGraph* const graph)
{
if (! isPrepared)
{
isPrepared = true;
AudioProcessorGraph::AudioGraphIOProcessor* const ioProc
= dynamic_cast <AudioProcessorGraph::AudioGraphIOProcessor*> (static_cast<AudioProcessor*> (processor));
if (ioProc != nullptr)
ioProc->setParentGraph (graph);
processor->setPlayConfigDetails (processor->getNumInputChannels(),
processor->getNumOutputChannels(),
sampleRate, blockSize);
processor->prepareToPlay (sampleRate, blockSize);
}
}
void AudioProcessorGraph::Node::unprepare()
{
if (isPrepared)
{
isPrepared = false;
processor->releaseResources();
}
}
//==============================================================================
AudioProcessorGraph::AudioProcessorGraph()
: lastNodeId (0),
renderingBuffers (1, 1),
currentAudioOutputBuffer (1, 1)
{
}
AudioProcessorGraph::~AudioProcessorGraph()
{
clearRenderingSequence();
clear();
}
const String AudioProcessorGraph::getName() const
{
return "Audio Graph";
}
//==============================================================================
void AudioProcessorGraph::clear()
{
nodes.clear();
connections.clear();
triggerAsyncUpdate();
}
AudioProcessorGraph::Node* AudioProcessorGraph::getNodeForId (const uint32 nodeId) const
{
for (int i = nodes.size(); --i >= 0;)
if (nodes.getUnchecked(i)->id == nodeId)
return nodes.getUnchecked(i);
return nullptr;
}
AudioProcessorGraph::Node* AudioProcessorGraph::addNode (AudioProcessor* const newProcessor,
uint32 nodeId)
{
if (newProcessor == nullptr)
{
jassertfalse;
return nullptr;
}
if (nodeId == 0)
{
nodeId = ++lastNodeId;
}
else
{
// you can't add a node with an id that already exists in the graph..
jassert (getNodeForId (nodeId) == nullptr);
removeNode (nodeId);
}
lastNodeId = nodeId;
Node* const n = new Node (nodeId, newProcessor);
nodes.add (n);
triggerAsyncUpdate();
AudioProcessorGraph::AudioGraphIOProcessor* const ioProc
= dynamic_cast <AudioProcessorGraph::AudioGraphIOProcessor*> (static_cast<AudioProcessor*> (n->processor));
if (ioProc != nullptr)
ioProc->setParentGraph (this);
return n;
}
bool AudioProcessorGraph::removeNode (const uint32 nodeId)
{
disconnectNode (nodeId);
for (int i = nodes.size(); --i >= 0;)
{
if (nodes.getUnchecked(i)->id == nodeId)
{
AudioProcessorGraph::AudioGraphIOProcessor* const ioProc
= dynamic_cast <AudioProcessorGraph::AudioGraphIOProcessor*> (static_cast<AudioProcessor*> (nodes.getUnchecked(i)->processor));
if (ioProc != nullptr)
ioProc->setParentGraph (nullptr);
nodes.remove (i);
triggerAsyncUpdate();
return true;
}
}
return false;
}
//==============================================================================
const AudioProcessorGraph::Connection* AudioProcessorGraph::getConnectionBetween (const uint32 sourceNodeId,
const int sourceChannelIndex,
const uint32 destNodeId,
const int destChannelIndex) const
{
for (int i = connections.size(); --i >= 0;)
{
const Connection* const c = connections.getUnchecked(i);
if (c->sourceNodeId == sourceNodeId
&& c->destNodeId == destNodeId
&& c->sourceChannelIndex == sourceChannelIndex
&& c->destChannelIndex == destChannelIndex)
{
return c;
}
}
return nullptr;
}
bool AudioProcessorGraph::isConnected (const uint32 possibleSourceNodeId,
const uint32 possibleDestNodeId) const
{
for (int i = connections.size(); --i >= 0;)
{
const Connection* const c = connections.getUnchecked(i);
if (c->sourceNodeId == possibleSourceNodeId
&& c->destNodeId == possibleDestNodeId)
{
return true;
}
}
return false;
}
bool AudioProcessorGraph::canConnect (const uint32 sourceNodeId,
const int sourceChannelIndex,
const uint32 destNodeId,
const int destChannelIndex) const
{
if (sourceChannelIndex < 0
|| destChannelIndex < 0
|| sourceNodeId == destNodeId
|| (destChannelIndex == midiChannelIndex) != (sourceChannelIndex == midiChannelIndex))
return false;
const Node* const source = getNodeForId (sourceNodeId);
if (source == nullptr
|| (sourceChannelIndex != midiChannelIndex && sourceChannelIndex >= source->processor->getNumOutputChannels())
|| (sourceChannelIndex == midiChannelIndex && ! source->processor->producesMidi()))
return false;
const Node* const dest = getNodeForId (destNodeId);
if (dest == nullptr
|| (destChannelIndex != midiChannelIndex && destChannelIndex >= dest->processor->getNumInputChannels())
|| (destChannelIndex == midiChannelIndex && ! dest->processor->acceptsMidi()))
return false;
return getConnectionBetween (sourceNodeId, sourceChannelIndex,
destNodeId, destChannelIndex) == nullptr;
}
bool AudioProcessorGraph::addConnection (const uint32 sourceNodeId,
const int sourceChannelIndex,
const uint32 destNodeId,
const int destChannelIndex)
{
if (! canConnect (sourceNodeId, sourceChannelIndex, destNodeId, destChannelIndex))
return false;
Connection* const c = new Connection();
c->sourceNodeId = sourceNodeId;
c->sourceChannelIndex = sourceChannelIndex;
c->destNodeId = destNodeId;
c->destChannelIndex = destChannelIndex;
connections.add (c);
triggerAsyncUpdate();
return true;
}
void AudioProcessorGraph::removeConnection (const int index)
{
connections.remove (index);
triggerAsyncUpdate();
}
bool AudioProcessorGraph::removeConnection (const uint32 sourceNodeId, const int sourceChannelIndex,
const uint32 destNodeId, const int destChannelIndex)
{
bool doneAnything = false;
for (int i = connections.size(); --i >= 0;)
{
const Connection* const c = connections.getUnchecked(i);
if (c->sourceNodeId == sourceNodeId
&& c->destNodeId == destNodeId
&& c->sourceChannelIndex == sourceChannelIndex
&& c->destChannelIndex == destChannelIndex)
{
removeConnection (i);
doneAnything = true;
triggerAsyncUpdate();
}
}
return doneAnything;
}
bool AudioProcessorGraph::disconnectNode (const uint32 nodeId)
{
bool doneAnything = false;
for (int i = connections.size(); --i >= 0;)
{
const Connection* const c = connections.getUnchecked(i);
if (c->sourceNodeId == nodeId || c->destNodeId == nodeId)
{
removeConnection (i);
doneAnything = true;
triggerAsyncUpdate();
}
}
return doneAnything;
}
bool AudioProcessorGraph::removeIllegalConnections()
{
bool doneAnything = false;
for (int i = connections.size(); --i >= 0;)
{
const Connection* const c = connections.getUnchecked(i);
const Node* const source = getNodeForId (c->sourceNodeId);
const Node* const dest = getNodeForId (c->destNodeId);
if (source == nullptr || dest == nullptr
|| (c->sourceChannelIndex != midiChannelIndex
&& ! isPositiveAndBelow (c->sourceChannelIndex, source->processor->getNumOutputChannels()))
|| (c->sourceChannelIndex == midiChannelIndex
&& ! source->processor->producesMidi())
|| (c->destChannelIndex != midiChannelIndex
&& ! isPositiveAndBelow (c->destChannelIndex, dest->processor->getNumInputChannels()))
|| (c->destChannelIndex == midiChannelIndex
&& ! dest->processor->acceptsMidi()))
{
removeConnection (i);
doneAnything = true;
triggerAsyncUpdate();
}
}
return doneAnything;
}
//==============================================================================
namespace GraphRenderingOps
{
@ -575,7 +274,7 @@ private:
{
const AudioProcessorGraph::Connection* const c = graph.getConnection (i);
if (c->destNodeId == node->id && c->destChannelIndex == inputChan)
if (c->destNodeId == node->nodeId && c->destChannelIndex == inputChan)
{
sourceNodes.add (c->sourceNodeId);
sourceOutputChans.add (c->sourceChannelIndex);
@ -690,7 +389,7 @@ private:
audioChannelsToUse.add (bufIndex);
if (inputChan < numOuts)
markBufferAsContaining (bufIndex, node->id, inputChan);
markBufferAsContaining (bufIndex, node->nodeId, inputChan);
}
for (int outputChan = numIns; outputChan < numOuts; ++outputChan)
@ -699,7 +398,7 @@ private:
jassert (bufIndex != 0);
audioChannelsToUse.add (bufIndex);
markBufferAsContaining (bufIndex, node->id, outputChan);
markBufferAsContaining (bufIndex, node->nodeId, outputChan);
}
// Now the same thing for midi..
@ -709,7 +408,7 @@ private:
{
const AudioProcessorGraph::Connection* const c = graph.getConnection (i);
if (c->destNodeId == node->id && c->destChannelIndex == AudioProcessorGraph::midiChannelIndex)
if (c->destNodeId == node->nodeId && c->destChannelIndex == AudioProcessorGraph::midiChannelIndex)
midiSourceNodes.add (c->sourceNodeId);
}
@ -799,7 +498,7 @@ private:
}
if (node->getProcessor()->producesMidi())
markBufferAsContaining (midiBufferToUse, node->id,
markBufferAsContaining (midiBufferToUse, node->nodeId,
AudioProcessorGraph::midiChannelIndex);
renderingOps.add (new ProcessBufferOp (node, audioChannelsToUse,
@ -830,12 +529,12 @@ private:
}
}
int getReadOnlyEmptyBuffer() const
int getReadOnlyEmptyBuffer() const noexcept
{
return 0;
}
int getBufferContaining (const uint32 nodeId, const int outputChannel) const
int getBufferContaining (const uint32 nodeId, const int outputChannel) const noexcept
{
if (outputChannel == AudioProcessorGraph::midiChannelIndex)
{
@ -893,7 +592,7 @@ private:
{
if (inputChannelOfIndexToIgnore != AudioProcessorGraph::midiChannelIndex
&& graph.getConnectionBetween (nodeId, AudioProcessorGraph::midiChannelIndex,
node->id, AudioProcessorGraph::midiChannelIndex) != nullptr)
node->nodeId, AudioProcessorGraph::midiChannelIndex) != nullptr)
return true;
}
else
@ -901,7 +600,7 @@ private:
for (int i = 0; i < node->getProcessor()->getNumInputChannels(); ++i)
if (i != inputChannelOfIndexToIgnore
&& graph.getConnectionBetween (nodeId, outputChanIndex,
node->id, i) != nullptr)
node->nodeId, i) != nullptr)
return true;
}
@ -932,6 +631,419 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RenderingOpSequenceCalculator);
};
//==============================================================================
// Holds a fast lookup table for checking which nodes are inputs to others.
class ConnectionLookupTable
{
public:
explicit ConnectionLookupTable (const OwnedArray<AudioProcessorGraph::Connection>& connections)
{
for (int i = 0; i < connections.size(); ++i)
{
const AudioProcessorGraph::Connection* const c = connections.getUnchecked(i);
int index;
Entry* entry = findEntry (c->destNodeId, index);
if (entry == nullptr)
{
entry = new Entry (c->destNodeId);
entries.insert (index, entry);
}
entry->srcNodes.add (c->sourceNodeId);
}
}
bool isAnInputTo (const uint32 possibleInputId,
const uint32 possibleDestinationId) const noexcept
{
return isAnInputToRecursive (possibleInputId, possibleDestinationId, entries.size());
}
private:
//==============================================================================
struct Entry
{
explicit Entry (const uint32 destNodeId_) noexcept : destNodeId (destNodeId_) {}
const uint32 destNodeId;
SortedSet<uint32> srcNodes;
JUCE_DECLARE_NON_COPYABLE (Entry);
};
OwnedArray<Entry> entries;
bool isAnInputToRecursive (const uint32 possibleInputId,
const uint32 possibleDestinationId,
int recursionCheck) const noexcept
{
int index;
const Entry* const entry = findEntry (possibleDestinationId, index);
if (entry != nullptr)
{
const SortedSet<uint32>& srcNodes = entry->srcNodes;
if (srcNodes.contains (possibleInputId))
return true;
if (--recursionCheck >= 0)
{
for (int i = 0; i < srcNodes.size(); ++i)
if (isAnInputToRecursive (possibleInputId, srcNodes.getUnchecked(i), recursionCheck))
return true;
}
}
return false;
}
Entry* findEntry (const uint32 destNodeId, int& insertIndex) const noexcept
{
Entry* result = nullptr;
int firstElement = 0, lastElement = entries.size();
while (firstElement < lastElement)
{
Entry* const firstEntry = entries.getUnchecked (firstElement);
if (destNodeId == firstEntry->destNodeId)
{
result = firstEntry;
break;
}
const int halfway = (firstElement + lastElement) / 2;
if (halfway <= firstElement)
break;
if (destNodeId >= entries.getUnchecked (halfway)->destNodeId)
firstElement = halfway;
else
lastElement = halfway;
}
insertIndex = firstElement;
return result;
}
JUCE_DECLARE_NON_COPYABLE (ConnectionLookupTable);
};
//==============================================================================
struct ConnectionSorter
{
static int compareElements (const AudioProcessorGraph::Connection* const first,
const AudioProcessorGraph::Connection* const second) noexcept
{
if (first->sourceNodeId < second->sourceNodeId) return -1;
else if (first->sourceNodeId > second->sourceNodeId) return 1;
else if (first->destNodeId < second->destNodeId) return -1;
else if (first->destNodeId > second->destNodeId) return 1;
else if (first->sourceChannelIndex < second->sourceChannelIndex) return -1;
else if (first->sourceChannelIndex > second->sourceChannelIndex) return 1;
else if (first->destChannelIndex < second->destChannelIndex) return -1;
else if (first->destChannelIndex > second->destChannelIndex) return 1;
return 0;
}
};
}
//==============================================================================
AudioProcessorGraph::Connection::Connection (const uint32 sourceNodeId_, const int sourceChannelIndex_,
const uint32 destNodeId_, const int destChannelIndex_) noexcept
: sourceNodeId (sourceNodeId_), sourceChannelIndex (sourceChannelIndex_),
destNodeId (destNodeId_), destChannelIndex (destChannelIndex_)
{
}
//==============================================================================
AudioProcessorGraph::Node::Node (const uint32 nodeId_, AudioProcessor* const processor_) noexcept
: nodeId (nodeId_),
processor (processor_),
isPrepared (false)
{
jassert (processor_ != nullptr);
}
void AudioProcessorGraph::Node::prepare (const double sampleRate, const int blockSize,
AudioProcessorGraph* const graph)
{
if (! isPrepared)
{
isPrepared = true;
AudioProcessorGraph::AudioGraphIOProcessor* const ioProc
= dynamic_cast <AudioProcessorGraph::AudioGraphIOProcessor*> (static_cast<AudioProcessor*> (processor));
if (ioProc != nullptr)
ioProc->setParentGraph (graph);
processor->setPlayConfigDetails (processor->getNumInputChannels(),
processor->getNumOutputChannels(),
sampleRate, blockSize);
processor->prepareToPlay (sampleRate, blockSize);
}
}
void AudioProcessorGraph::Node::unprepare()
{
if (isPrepared)
{
isPrepared = false;
processor->releaseResources();
}
}
//==============================================================================
AudioProcessorGraph::AudioProcessorGraph()
: lastNodeId (0),
renderingBuffers (1, 1),
currentAudioOutputBuffer (1, 1)
{
}
AudioProcessorGraph::~AudioProcessorGraph()
{
clearRenderingSequence();
clear();
}
const String AudioProcessorGraph::getName() const
{
return "Audio Graph";
}
//==============================================================================
void AudioProcessorGraph::clear()
{
nodes.clear();
connections.clear();
triggerAsyncUpdate();
}
AudioProcessorGraph::Node* AudioProcessorGraph::getNodeForId (const uint32 nodeId) const
{
for (int i = nodes.size(); --i >= 0;)
if (nodes.getUnchecked(i)->nodeId == nodeId)
return nodes.getUnchecked(i);
return nullptr;
}
AudioProcessorGraph::Node* AudioProcessorGraph::addNode (AudioProcessor* const newProcessor,
uint32 nodeId)
{
if (newProcessor == nullptr)
{
jassertfalse;
return nullptr;
}
if (nodeId == 0)
{
nodeId = ++lastNodeId;
}
else
{
// you can't add a node with an id that already exists in the graph..
jassert (getNodeForId (nodeId) == nullptr);
removeNode (nodeId);
}
lastNodeId = nodeId;
Node* const n = new Node (nodeId, newProcessor);
nodes.add (n);
triggerAsyncUpdate();
AudioProcessorGraph::AudioGraphIOProcessor* const ioProc
= dynamic_cast <AudioProcessorGraph::AudioGraphIOProcessor*> (static_cast<AudioProcessor*> (n->processor));
if (ioProc != nullptr)
ioProc->setParentGraph (this);
return n;
}
bool AudioProcessorGraph::removeNode (const uint32 nodeId)
{
disconnectNode (nodeId);
for (int i = nodes.size(); --i >= 0;)
{
if (nodes.getUnchecked(i)->nodeId == nodeId)
{
AudioProcessorGraph::AudioGraphIOProcessor* const ioProc
= dynamic_cast <AudioProcessorGraph::AudioGraphIOProcessor*> (static_cast<AudioProcessor*> (nodes.getUnchecked(i)->processor));
if (ioProc != nullptr)
ioProc->setParentGraph (nullptr);
nodes.remove (i);
triggerAsyncUpdate();
return true;
}
}
return false;
}
//==============================================================================
const AudioProcessorGraph::Connection* AudioProcessorGraph::getConnectionBetween (const uint32 sourceNodeId,
const int sourceChannelIndex,
const uint32 destNodeId,
const int destChannelIndex) const
{
const Connection c (sourceNodeId, sourceChannelIndex, destNodeId, destChannelIndex);
GraphRenderingOps::ConnectionSorter sorter;
return connections [connections.indexOfSorted (sorter, &c)];
}
bool AudioProcessorGraph::isConnected (const uint32 possibleSourceNodeId,
const uint32 possibleDestNodeId) const
{
for (int i = connections.size(); --i >= 0;)
{
const Connection* const c = connections.getUnchecked(i);
if (c->sourceNodeId == possibleSourceNodeId
&& c->destNodeId == possibleDestNodeId)
{
return true;
}
}
return false;
}
bool AudioProcessorGraph::canConnect (const uint32 sourceNodeId,
const int sourceChannelIndex,
const uint32 destNodeId,
const int destChannelIndex) const
{
if (sourceChannelIndex < 0
|| destChannelIndex < 0
|| sourceNodeId == destNodeId
|| (destChannelIndex == midiChannelIndex) != (sourceChannelIndex == midiChannelIndex))
return false;
const Node* const source = getNodeForId (sourceNodeId);
if (source == nullptr
|| (sourceChannelIndex != midiChannelIndex && sourceChannelIndex >= source->processor->getNumOutputChannels())
|| (sourceChannelIndex == midiChannelIndex && ! source->processor->producesMidi()))
return false;
const Node* const dest = getNodeForId (destNodeId);
if (dest == nullptr
|| (destChannelIndex != midiChannelIndex && destChannelIndex >= dest->processor->getNumInputChannels())
|| (destChannelIndex == midiChannelIndex && ! dest->processor->acceptsMidi()))
return false;
return getConnectionBetween (sourceNodeId, sourceChannelIndex,
destNodeId, destChannelIndex) == nullptr;
}
bool AudioProcessorGraph::addConnection (const uint32 sourceNodeId,
const int sourceChannelIndex,
const uint32 destNodeId,
const int destChannelIndex)
{
if (! canConnect (sourceNodeId, sourceChannelIndex, destNodeId, destChannelIndex))
return false;
GraphRenderingOps::ConnectionSorter sorter;
connections.addSorted (sorter, new Connection (sourceNodeId, sourceChannelIndex,
destNodeId, destChannelIndex));
triggerAsyncUpdate();
return true;
}
void AudioProcessorGraph::removeConnection (const int index)
{
connections.remove (index);
triggerAsyncUpdate();
}
bool AudioProcessorGraph::removeConnection (const uint32 sourceNodeId, const int sourceChannelIndex,
const uint32 destNodeId, const int destChannelIndex)
{
bool doneAnything = false;
for (int i = connections.size(); --i >= 0;)
{
const Connection* const c = connections.getUnchecked(i);
if (c->sourceNodeId == sourceNodeId
&& c->destNodeId == destNodeId
&& c->sourceChannelIndex == sourceChannelIndex
&& c->destChannelIndex == destChannelIndex)
{
removeConnection (i);
doneAnything = true;
triggerAsyncUpdate();
}
}
return doneAnything;
}
bool AudioProcessorGraph::disconnectNode (const uint32 nodeId)
{
bool doneAnything = false;
for (int i = connections.size(); --i >= 0;)
{
const Connection* const c = connections.getUnchecked(i);
if (c->sourceNodeId == nodeId || c->destNodeId == nodeId)
{
removeConnection (i);
doneAnything = true;
triggerAsyncUpdate();
}
}
return doneAnything;
}
bool AudioProcessorGraph::removeIllegalConnections()
{
bool doneAnything = false;
for (int i = connections.size(); --i >= 0;)
{
const Connection* const c = connections.getUnchecked(i);
const Node* const source = getNodeForId (c->sourceNodeId);
const Node* const dest = getNodeForId (c->destNodeId);
if (source == nullptr || dest == nullptr
|| (c->sourceChannelIndex != midiChannelIndex
&& ! isPositiveAndBelow (c->sourceChannelIndex, source->processor->getNumOutputChannels()))
|| (c->sourceChannelIndex == midiChannelIndex
&& ! source->processor->producesMidi())
|| (c->destChannelIndex != midiChannelIndex
&& ! isPositiveAndBelow (c->destChannelIndex, dest->processor->getNumInputChannels()))
|| (c->destChannelIndex == midiChannelIndex
&& ! dest->processor->acceptsMidi()))
{
removeConnection (i);
doneAnything = true;
triggerAsyncUpdate();
}
}
return doneAnything;
}
//==============================================================================
@ -980,21 +1092,22 @@ void AudioProcessorGraph::buildRenderingSequence()
Array<void*> orderedNodes;
int i;
for (i = 0; i < nodes.size(); ++i)
{
Node* const node = nodes.getUnchecked(i);
const GraphRenderingOps::ConnectionLookupTable table (connections);
node->prepare (getSampleRate(), getBlockSize(), this);
for (int i = 0; i < nodes.size(); ++i)
{
Node* const node = nodes.getUnchecked(i);
int j = 0;
for (; j < orderedNodes.size(); ++j)
if (isAnInputTo (node->id,
((Node*) orderedNodes.getUnchecked (j))->id,
nodes.size() + 1))
break;
node->prepare (getSampleRate(), getBlockSize(), this);
orderedNodes.insert (j, node);
int j = 0;
for (; j < orderedNodes.size(); ++j)
if (table.isAnInputTo (node->nodeId, ((Node*) orderedNodes.getUnchecked(j))->nodeId))
break;
orderedNodes.insert (j, node);
}
}
GraphRenderingOps::RenderingOpSequenceCalculator calculator (*this, orderedNodes, newRenderingOps);
@ -1162,38 +1275,38 @@ void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioSampleBuffer
switch (type)
{
case audioOutputNode:
{
for (int i = jmin (graph->currentAudioOutputBuffer.getNumChannels(),
buffer.getNumChannels()); --i >= 0;)
case audioOutputNode:
{
graph->currentAudioOutputBuffer.addFrom (i, 0, buffer, i, 0, buffer.getNumSamples());
for (int i = jmin (graph->currentAudioOutputBuffer.getNumChannels(),
buffer.getNumChannels()); --i >= 0;)
{
graph->currentAudioOutputBuffer.addFrom (i, 0, buffer, i, 0, buffer.getNumSamples());
}
break;
}
break;
}
case audioInputNode:
{
for (int i = jmin (graph->currentAudioInputBuffer->getNumChannels(),
buffer.getNumChannels()); --i >= 0;)
case audioInputNode:
{
buffer.copyFrom (i, 0, *graph->currentAudioInputBuffer, i, 0, buffer.getNumSamples());
for (int i = jmin (graph->currentAudioInputBuffer->getNumChannels(),
buffer.getNumChannels()); --i >= 0;)
{
buffer.copyFrom (i, 0, *graph->currentAudioInputBuffer, i, 0, buffer.getNumSamples());
}
break;
}
break;
}
case midiOutputNode:
graph->currentMidiOutputBuffer.addEvents (midiMessages, 0, buffer.getNumSamples(), 0);
break;
case midiOutputNode:
graph->currentMidiOutputBuffer.addEvents (midiMessages, 0, buffer.getNumSamples(), 0);
break;
case midiInputNode:
midiMessages.addEvents (*graph->currentMidiInputBuffer, 0, buffer.getNumSamples(), 0);
break;
case midiInputNode:
midiMessages.addEvents (*graph->currentMidiInputBuffer, 0, buffer.getNumSamples(), 0);
break;
default:
break;
default:
break;
}
}
@ -1241,15 +1354,8 @@ bool AudioProcessorGraph::AudioGraphIOProcessor::isOutputChannelStereoPair (int
return isInputChannelStereoPair (index);
}
bool AudioProcessorGraph::AudioGraphIOProcessor::isInput() const
{
return type == audioInputNode || type == midiInputNode;
}
bool AudioProcessorGraph::AudioGraphIOProcessor::isOutput() const
{
return type == audioOutputNode || type == midiOutputNode;
}
bool AudioProcessorGraph::AudioGraphIOProcessor::isInput() const { return type == audioInputNode || type == midiInputNode; }
bool AudioProcessorGraph::AudioGraphIOProcessor::isOutput() const { return type == audioOutputNode || type == midiOutputNode; }
bool AudioProcessorGraph::AudioGraphIOProcessor::hasEditor() const { return false; }
AudioProcessorEditor* AudioProcessorGraph::AudioGraphIOProcessor::createEditor() { return nullptr; }
@ -1266,15 +1372,10 @@ int AudioProcessorGraph::AudioGraphIOProcessor::getCurrentProgram()
void AudioProcessorGraph::AudioGraphIOProcessor::setCurrentProgram (int) { }
const String AudioProcessorGraph::AudioGraphIOProcessor::getProgramName (int) { return String::empty; }
void AudioProcessorGraph::AudioGraphIOProcessor::changeProgramName (int, const String&) { }
void AudioProcessorGraph::AudioGraphIOProcessor::changeProgramName (int, const String&) {}
void AudioProcessorGraph::AudioGraphIOProcessor::getStateInformation (JUCE_NAMESPACE::MemoryBlock&)
{
}
void AudioProcessorGraph::AudioGraphIOProcessor::setStateInformation (const void*, int)
{
}
void AudioProcessorGraph::AudioGraphIOProcessor::getStateInformation (JUCE_NAMESPACE::MemoryBlock&) {}
void AudioProcessorGraph::AudioGraphIOProcessor::setStateInformation (const void*, int) {}
void AudioProcessorGraph::AudioGraphIOProcessor::setParentGraph (AudioProcessorGraph* const newGraph)
{

View file

@ -71,10 +71,9 @@ public:
public:
//==============================================================================
/** The ID number assigned to this node.
This is assigned by the graph that owns it, and can't be changed.
*/
const uint32 id;
const uint32 nodeId;
/** The actual processor object that this node represents. */
AudioProcessor* getProcessor() const noexcept { return processor; }
@ -99,7 +98,7 @@ public:
const ScopedPointer<AudioProcessor> processor;
bool isPrepared;
Node (uint32 id, AudioProcessor* processor);
Node (uint32 nodeId, AudioProcessor* processor) noexcept;
void prepare (double sampleRate, int blockSize, AudioProcessorGraph* graph);
void unprepare();
@ -115,6 +114,10 @@ public:
struct JUCE_API Connection
{
public:
//==============================================================================
Connection (uint32 sourceNodeId, int sourceChannelIndex,
uint32 destNodeId, int destChannelIndex) noexcept;
//==============================================================================
/** The ID number of the node which is the input source for this connection.
@see AudioProcessorGraph::getNodeForId

View file

@ -42,10 +42,9 @@ static const int quitMessageId = 0xfffff321;
MessageManager::MessageManager() noexcept
: quitMessagePosted (false),
quitMessageReceived (false),
messageThreadId (Thread::getCurrentThreadId()),
threadWithLock (0)
{
messageThreadId = Thread::getCurrentThreadId();
if (JUCEApplication::isStandaloneApp())
Thread::setCurrentThreadName ("Juce Message Thread");
}

View file

@ -106,8 +106,7 @@ public:
@returns the value that the callback function returns.
@see MessageManagerLock
*/
void* callFunctionOnMessageThread (MessageCallbackFunction* callback,
void* userData);
void* callFunctionOnMessageThread (MessageCallbackFunction* callback, void* userData);
/** Returns true if the caller-thread is the message thread. */
bool isThisTheMessageThread() const noexcept;
@ -157,12 +156,12 @@ public:
void deregisterBroadcastListener (ActionListener* listener);
//==============================================================================
/** @internal */
#ifndef DOXYGEN
// Internal methods - do not use!
void deliverMessage (Message*);
/** @internal */
void deliverBroadcastMessage (const String&);
/** @internal */
~MessageManager() noexcept;
#endif
private:
//==============================================================================
@ -294,12 +293,10 @@ public:
//==============================================================================
/** Returns true if the lock was successfully acquired.
(See the constructor that takes a Thread for more info).
*/
bool lockWasGained() const noexcept { return locked; }
private:
class BlockingMessage;
friend class ReferenceCountedObjectPtr<BlockingMessage>;

View file

@ -229,6 +229,7 @@ public:
};
//==============================================================================
#ifndef DOXYGEN
/** @internal */
void paint (Graphics& g);
/** @internal */
@ -251,6 +252,7 @@ public:
void parentHierarchyChanged();
/** @internal */
const Rectangle<int> getTitleBarArea();
#endif
private:
//==============================================================================

View file

@ -340,7 +340,7 @@ protected:
/** @internal */
int getDesktopWindowStyleFlags() const;
#if JUCE_DEBUG
#if JUCE_DEBUG
/** Overridden to warn people about adding components directly to this component
instead of using setContentOwned().
@ -355,7 +355,7 @@ protected:
a base-class method call to Component::addAndMakeVisible(), to side-step this warning.
*/
void addAndMakeVisible (Component* child, int zOrder = -1);
#endif
#endif
ScopedPointer <ResizableCornerComponent> resizableCorner;
ScopedPointer <ResizableBorderComponent> resizableBorder;

View file

@ -140,8 +140,9 @@ public:
protected:
/** @internal */
#ifndef DOXYGEN
CameraDevice (const String& name, int index);
#endif
private:
void* internal;

View file

@ -123,8 +123,9 @@ public:
TimeSliceClient* getClient (int index) const;
//==============================================================================
/** @internal */
#ifndef DOXYGEN
void run();
#endif
//==============================================================================
private: