Browse Source

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.

tags/2021-05-28
Julian Storer 14 years ago
parent
commit
d97ce5f9ee
20 changed files with 3038 additions and 1708 deletions
  1. +1
    -1
      extras/audio plugin host/Source/FilterGraph.cpp
  2. +3
    -3
      extras/audio plugin host/Source/GraphEditorPanel.cpp
  3. +1447
    -793
      juce_amalgamated.cpp
  4. +43
    -39
      juce_amalgamated.h
  5. +29
    -18
      src/application/juce_Application.cpp
  6. +15
    -22
      src/application/juce_Application.h
  7. +322
    -9
      src/audio/audio_file_formats/juce_AiffAudioFormat.cpp
  8. +3
    -2
      src/audio/audio_file_formats/juce_AiffAudioFormat.h
  9. +22
    -4
      src/audio/audio_file_formats/juce_AudioThumbnail.cpp
  10. +9
    -2
      src/audio/audio_file_formats/juce_AudioThumbnail.h
  11. +247
    -25
      src/audio/audio_file_formats/juce_WavAudioFormat.cpp
  12. +3
    -0
      src/audio/plugin_client/juce_PluginHeaders.h
  13. +875
    -774
      src/audio/processors/juce_AudioProcessorGraph.cpp
  14. +6
    -3
      src/audio/processors/juce_AudioProcessorGraph.h
  15. +1
    -2
      src/events/juce_MessageManager.cpp
  16. +4
    -7
      src/events/juce_MessageManager.h
  17. +2
    -0
      src/gui/components/windows/juce_DocumentWindow.h
  18. +2
    -2
      src/gui/components/windows/juce_ResizableWindow.h
  19. +2
    -1
      src/gui/graphics/imaging/juce_CameraDevice.h
  20. +2
    -1
      src/threads/juce_TimeSliceThread.h

+ 1
- 1
extras/audio plugin host/Source/FilterGraph.cpp View File

@@ -295,7 +295,7 @@ static XmlElement* createNodeXml (AudioProcessorGraph::Node* const node) noexcep
} }
XmlElement* e = new XmlElement ("FILTER"); 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 ("x", node->properties ["x"].toString());
e->setAttribute ("y", node->properties ["y"].toString()); e->setAttribute ("y", node->properties ["y"].toString());
e->setAttribute ("uiLastX", node->properties ["uiLastX"].toString()); e->setAttribute ("uiLastX", node->properties ["uiLastX"].toString());


+ 3
- 3
extras/audio plugin host/Source/GraphEditorPanel.cpp View File

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


+ 1447
- 793
juce_amalgamated.cpp
File diff suppressed because it is too large
View File


+ 43
- 39
juce_amalgamated.h View File

@@ -73,7 +73,7 @@ namespace JuceDummyNamespace {}
*/ */
#define JUCE_MAJOR_VERSION 1 #define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 53 #define JUCE_MINOR_VERSION 53
#define JUCE_BUILDNUMBER 73
#define JUCE_BUILDNUMBER 74


/** Current Juce version number. /** Current Juce version number.


@@ -23112,8 +23112,9 @@ public:
/** Returns one of the registered clients. */ /** Returns one of the registered clients. */
TimeSliceClient* getClient (int index) const; TimeSliceClient* getClient (int index) const;


/** @internal */
#ifndef DOXYGEN
void run(); void run();
#endif


private: private:
CriticalSection callbackLock, listLock; CriticalSection callbackLock, listLock;
@@ -33344,8 +33345,7 @@ public:


@see MessageManager, DeletedAtShutdown @see MessageManager, DeletedAtShutdown
*/ */
class JUCE_API JUCEApplication : public ApplicationCommandTarget,
private ActionListener
class JUCE_API JUCEApplication : public ApplicationCommandTarget
{ {
protected: protected:


@@ -33497,18 +33497,9 @@ public:
*/ */
const String getCommandLineParameters() const noexcept { return commandLineParameters; } 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 /** Returns true if this executable is running as an app (as opposed to being a plugin
or other kind of shared library. */ or other kind of shared library. */
static inline bool isStandaloneApp() noexcept { return createInstance != 0; }
static inline bool isStandaloneApp() noexcept { return createInstance != 0; }


/** @internal */ /** @internal */
ApplicationCommandTarget* getNextCommandTarget(); ApplicationCommandTarget* getNextCommandTarget();
@@ -33518,26 +33509,28 @@ public:
void getAllCommands (Array <CommandID>& commands); void getAllCommands (Array <CommandID>& commands);
/** @internal */ /** @internal */
bool perform (const InvocationInfo& info); 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); bool initialiseApp (const String& commandLine);
/** @internal */
int shutdownApp(); int shutdownApp();
/** @internal */
static void appWillTerminateByForce(); static void appWillTerminateByForce();
/** @internal */
typedef JUCEApplication* (*CreateInstanceFunction)(); typedef JUCEApplication* (*CreateInstanceFunction)();
/** @internal */
static CreateInstanceFunction createInstance; static CreateInstanceFunction createInstance;
#endif


private: private:


static JUCEApplication* appInstance;

String commandLineParameters; String commandLineParameters;
ScopedPointer<InterProcessLock> appLock;
ScopedPointer<ActionListener> broadcastCallback;
int appReturnValue; int appReturnValue;
bool stillInitialising; bool stillInitialising;
ScopedPointer<InterProcessLock> appLock;
static JUCEApplication* appInstance;


JUCE_DECLARE_NON_COPYABLE (JUCEApplication); JUCE_DECLARE_NON_COPYABLE (JUCEApplication);
}; };
@@ -36550,9 +36543,10 @@ public:
const Array <int> getPossibleBitDepths(); const Array <int> getPossibleBitDepths();
bool canDoStereo(); bool canDoStereo();
bool canDoMono(); bool canDoMono();
#if JUCE_MAC

#if JUCE_MAC
bool canHandleFile (const File& fileToTest); bool canHandleFile (const File& fileToTest);
#endif
#endif


AudioFormatReader* createReaderFor (InputStream* sourceStream, AudioFormatReader* createReaderFor (InputStream* sourceStream,
bool deleteStreamIfOpeningFails); bool deleteStreamIfOpeningFails);
@@ -37224,12 +37218,19 @@ public:
*/ */
float getApproximatePeak() const; 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(). */ /** Returns the hash code that was set by setSource() or setReader(). */
int64 getHashCode() const; int64 getHashCode() const;


#ifndef DOXYGEN #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 #endif


private: private:
@@ -47098,10 +47099,9 @@ public:
public: public:


/** The ID number assigned to this node. /** The ID number assigned to this node.

This is assigned by the graph that owns it, and can't be changed. 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. */ /** The actual processor object that this node represents. */
AudioProcessor* getProcessor() const noexcept { return processor; } AudioProcessor* getProcessor() const noexcept { return processor; }
@@ -47125,7 +47125,7 @@ public:
const ScopedPointer<AudioProcessor> processor; const ScopedPointer<AudioProcessor> processor;
bool isPrepared; bool isPrepared;


Node (uint32 id, AudioProcessor* processor);
Node (uint32 nodeId, AudioProcessor* processor) noexcept;


void prepare (double sampleRate, int blockSize, AudioProcessorGraph* graph); void prepare (double sampleRate, int blockSize, AudioProcessorGraph* graph);
void unprepare(); void unprepare();
@@ -47141,6 +47141,9 @@ public:
{ {
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. /** The ID number of the node which is the input source for this connection.
@see AudioProcessorGraph::getNodeForId @see AudioProcessorGraph::getNodeForId
*/ */
@@ -48766,8 +48769,7 @@ public:
@returns the value that the callback function returns. @returns the value that the callback function returns.
@see MessageManagerLock @see MessageManagerLock
*/ */
void* callFunctionOnMessageThread (MessageCallbackFunction* callback,
void* userData);
void* callFunctionOnMessageThread (MessageCallbackFunction* callback, void* userData);


/** Returns true if the caller-thread is the message thread. */ /** Returns true if the caller-thread is the message thread. */
bool isThisTheMessageThread() const noexcept; bool isThisTheMessageThread() const noexcept;
@@ -48815,12 +48817,12 @@ public:
/** Deregisters a broadcast listener. */ /** Deregisters a broadcast listener. */
void deregisterBroadcastListener (ActionListener* listener); void deregisterBroadcastListener (ActionListener* listener);


/** @internal */
#ifndef DOXYGEN
// Internal methods - do not use!
void deliverMessage (Message*); void deliverMessage (Message*);
/** @internal */
void deliverBroadcastMessage (const String&); void deliverBroadcastMessage (const String&);
/** @internal */
~MessageManager() noexcept; ~MessageManager() noexcept;
#endif


private: private:


@@ -48946,7 +48948,6 @@ public:
~MessageManagerLock() noexcept; ~MessageManagerLock() noexcept;


/** Returns true if the lock was successfully acquired. /** Returns true if the lock was successfully acquired.

(See the constructor that takes a Thread for more info). (See the constructor that takes a Thread for more info).
*/ */
bool lockWasGained() const noexcept { return locked; } bool lockWasGained() const noexcept { return locked; }
@@ -56566,7 +56567,7 @@ protected:
/** @internal */ /** @internal */
int getDesktopWindowStyleFlags() const; int getDesktopWindowStyleFlags() const;


#if JUCE_DEBUG
#if JUCE_DEBUG
/** Overridden to warn people about adding components directly to this component /** Overridden to warn people about adding components directly to this component
instead of using setContentOwned(). instead of using setContentOwned().


@@ -56581,7 +56582,7 @@ protected:
a base-class method call to Component::addAndMakeVisible(), to side-step this warning. a base-class method call to Component::addAndMakeVisible(), to side-step this warning.
*/ */
void addAndMakeVisible (Component* child, int zOrder = -1); void addAndMakeVisible (Component* child, int zOrder = -1);
#endif
#endif


ScopedPointer <ResizableCornerComponent> resizableCorner; ScopedPointer <ResizableCornerComponent> resizableCorner;
ScopedPointer <ResizableBorderComponent> resizableBorder; ScopedPointer <ResizableBorderComponent> resizableBorder;
@@ -59438,6 +59439,7 @@ public:
and feel class how this is used. */ and feel class how this is used. */
}; };


#ifndef DOXYGEN
/** @internal */ /** @internal */
void paint (Graphics& g); void paint (Graphics& g);
/** @internal */ /** @internal */
@@ -59460,6 +59462,7 @@ public:
void parentHierarchyChanged(); void parentHierarchyChanged();
/** @internal */ /** @internal */
const Rectangle<int> getTitleBarArea(); const Rectangle<int> getTitleBarArea();
#endif


private: private:


@@ -66942,8 +66945,9 @@ public:
void removeListener (Listener* listenerToRemove); void removeListener (Listener* listenerToRemove);


protected: protected:
/** @internal */
#ifndef DOXYGEN
CameraDevice (const String& name, int index); CameraDevice (const String& name, int index);
#endif


private: private:
void* internal; void* internal;


+ 29
- 18
src/application/juce_Application.cpp View File

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


+ 15
- 22
src/application/juce_Application.h View File

@@ -87,8 +87,7 @@
@see MessageManager, DeletedAtShutdown @see MessageManager, DeletedAtShutdown
*/ */
class JUCE_API JUCEApplication : public ApplicationCommandTarget,
private ActionListener
class JUCE_API JUCEApplication : public ApplicationCommandTarget
{ {
protected: protected:
//============================================================================== //==============================================================================
@@ -244,20 +243,11 @@ public:
*/ */
const String getCommandLineParameters() const noexcept { return commandLineParameters; } 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 /** Returns true if this executable is running as an app (as opposed to being a plugin
or other kind of shared library. */ or other kind of shared library. */
static inline bool isStandaloneApp() noexcept { return createInstance != 0; }
static inline bool isStandaloneApp() noexcept { return createInstance != 0; }
//==============================================================================
/** @internal */ /** @internal */
ApplicationCommandTarget* getNextCommandTarget(); ApplicationCommandTarget* getNextCommandTarget();
/** @internal */ /** @internal */
@@ -266,26 +256,29 @@ public:
void getAllCommands (Array <CommandID>& commands); void getAllCommands (Array <CommandID>& commands);
/** @internal */ /** @internal */
bool perform (const InvocationInfo& info); 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); bool initialiseApp (const String& commandLine);
/** @internal */
int shutdownApp(); int shutdownApp();
/** @internal */
static void appWillTerminateByForce(); static void appWillTerminateByForce();
/** @internal */
typedef JUCEApplication* (*CreateInstanceFunction)(); typedef JUCEApplication* (*CreateInstanceFunction)();
/** @internal */
static CreateInstanceFunction createInstance; static CreateInstanceFunction createInstance;
#endif
private: private:
//============================================================================== //==============================================================================
static JUCEApplication* appInstance;
String commandLineParameters; String commandLineParameters;
ScopedPointer<InterProcessLock> appLock;
ScopedPointer<ActionListener> broadcastCallback;
int appReturnValue; int appReturnValue;
bool stillInitialising; bool stillInitialising;
ScopedPointer<InterProcessLock> appLock;
static JUCEApplication* appInstance;
JUCE_DECLARE_NON_COPYABLE (JUCEApplication); JUCE_DECLARE_NON_COPYABLE (JUCEApplication);
}; };


+ 322
- 9
src/audio/audio_file_formats/juce_AiffAudioFormat.cpp View File

@@ -29,6 +29,7 @@ BEGIN_JUCE_NAMESPACE
#include "juce_AiffAudioFormat.h" #include "juce_AiffAudioFormat.h"
#include "../../io/streams/juce_BufferedInputStream.h" #include "../../io/streams/juce_BufferedInputStream.h"
#include "../../io/streams/juce_MemoryOutputStream.h"
#include "../../core/juce_PlatformUtilities.h" #include "../../core/juce_PlatformUtilities.h"
#include "../../text/juce_LocalisedStrings.h" #include "../../text/juce_LocalisedStrings.h"
@@ -37,6 +38,215 @@ BEGIN_JUCE_NAMESPACE
static const char* const aiffFormatName = "AIFF file"; static const char* const aiffFormatName = "AIFF file";
static const char* const aiffExtensions[] = { ".aiff", ".aif", 0 }; 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 class AiffAudioFormatReader : public AudioFormatReader
@@ -50,6 +260,8 @@ public:
AiffAudioFormatReader (InputStream* in) AiffAudioFormatReader (InputStream* in)
: AudioFormatReader (in, TRANS (aiffFormatName)) : AudioFormatReader (in, TRANS (aiffFormatName))
{ {
using namespace AiffFileHelpers;
if (input->readInt() == chunkName ("FORM")) if (input->readInt() == chunkName ("FORM"))
{ {
const int len = input->readIntBigEndian(); const int len = input->readIntBigEndian();
@@ -131,6 +343,67 @@ public:
dataChunkStart = input->getPosition() + 4 + offset; dataChunkStart = input->getPosition() + 4 + offset;
lengthInSamples = (bytesPerFrame > 0) ? jmin (lengthInSamples, (int64) (length / bytesPerFrame)) : 0; 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) else if ((hasGotVer && hasGotData && hasGotType)
|| chunkEnd < input->getPosition() || chunkEnd < input->getPosition()
|| input->isExhausted()) || input->isExhausted())
@@ -142,6 +415,9 @@ public:
} }
} }
} }
if (metadataValues.size() > 0)
metadataValues.set ("MetaDataSource", "AIFF");
} }
//============================================================================== //==============================================================================
@@ -211,8 +487,6 @@ public:
} }
private: private:
static inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); }
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatReader); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatReader);
}; };
@@ -221,12 +495,28 @@ class AiffAudioFormatWriter : public AudioFormatWriter
{ {
public: 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), : AudioFormatWriter (out, TRANS (aiffFormatName), sampleRate_, numChans, bits),
lengthInSamples (0), lengthInSamples (0),
bytesWritten (0), bytesWritten (0),
writeFailed (false) 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(); headerPosition = out->getPosition();
writeHeader(); writeHeader();
} }
@@ -279,15 +569,15 @@ public:
} }
private: private:
MemoryBlock tempBlock;
MemoryBlock tempBlock, markChunk, comtChunk, instChunk;
uint32 lengthInSamples, bytesWritten; uint32 lengthInSamples, bytesWritten;
int64 headerPosition; int64 headerPosition;
bool writeFailed; bool writeFailed;
static inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); }
void writeHeader() void writeHeader()
{ {
using namespace AiffFileHelpers;
const bool couldSeekOk = output->setPosition (headerPosition); const bool couldSeekOk = output->setPosition (headerPosition);
(void) couldSeekOk; (void) couldSeekOk;
@@ -295,7 +585,9 @@ private:
// to be able to seek back to write the header // to be able to seek back to write the header
jassert (couldSeekOk); 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); int audioBytes = lengthInSamples * ((bitsPerSample * numChannels) / 8);
audioBytes += (audioBytes & 1); audioBytes += (audioBytes & 1);
@@ -351,6 +643,27 @@ private:
output->write (sampleRateBytes, 10); 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->writeInt (chunkName ("SSND"));
output->writeIntBigEndian (audioBytes + 8); output->writeIntBigEndian (audioBytes + 8);
output->writeInt (0); output->writeInt (0);
@@ -416,11 +729,11 @@ AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out,
double sampleRate, double sampleRate,
unsigned int numberOfChannels, unsigned int numberOfChannels,
int bitsPerSample, int bitsPerSample,
const StringPairArray& /*metadataValues*/,
const StringPairArray& metadataValues,
int /*qualityOptionIndex*/) int /*qualityOptionIndex*/)
{ {
if (getPossibleBitDepths().contains (bitsPerSample)) if (getPossibleBitDepths().contains (bitsPerSample))
return new AiffAudioFormatWriter (out, sampleRate, numberOfChannels, bitsPerSample);
return new AiffAudioFormatWriter (out, sampleRate, numberOfChannels, bitsPerSample, metadataValues);
return nullptr; return nullptr;
} }


+ 3
- 2
src/audio/audio_file_formats/juce_AiffAudioFormat.h View File

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


+ 22
- 4
src/audio/audio_file_formats/juce_AudioThumbnail.cpp View File

@@ -38,7 +38,7 @@ struct AudioThumbnail::MinMaxValue
char minValue; char minValue;
char maxValue; char maxValue;
MinMaxValue() : minValue (0), maxValue (0)
MinMaxValue() noexcept : minValue (0), maxValue (0)
{ {
} }
@@ -281,7 +281,7 @@ public:
return data.size(); 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) if (startSample >= 0)
{ {
@@ -323,12 +323,12 @@ public:
dest[i] = source[i]; dest[i] = source[i];
} }
void resetPeak()
void resetPeak() noexcept
{ {
peakLevel = -1; peakLevel = -1;
} }
int getPeak()
int getPeak() noexcept
{ {
if (peakLevel < 0) if (peakLevel < 0)
{ {
@@ -743,6 +743,24 @@ float AudioThumbnail::getApproximatePeak() const
return jlimit (0, 127, peak) / 127.0f; 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, void AudioThumbnail::drawChannel (Graphics& g, const Rectangle<int>& area, double startTime,
double endTime, int channelNum, float verticalZoomFactor) double endTime, int channelNum, float verticalZoomFactor)
{ {


+ 9
- 2
src/audio/audio_file_formats/juce_AudioThumbnail.h View File

@@ -186,12 +186,19 @@ public:
*/ */
float getApproximatePeak() const; 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(). */ /** Returns the hash code that was set by setSource() or setReader(). */
int64 getHashCode() const; int64 getHashCode() const;
#ifndef DOXYGEN #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 #endif
private: private:


+ 247
- 25
src/audio/audio_file_formats/juce_WavAudioFormat.cpp View File

@@ -29,6 +29,7 @@ BEGIN_JUCE_NAMESPACE
#include "juce_WavAudioFormat.h" #include "juce_WavAudioFormat.h"
#include "../../io/streams/juce_BufferedInputStream.h" #include "../../io/streams/juce_BufferedInputStream.h"
#include "../../io/streams/juce_MemoryOutputStream.h"
#include "../../text/juce_LocalisedStrings.h" #include "../../text/juce_LocalisedStrings.h"
#include "../../io/files/juce_FileInputStream.h" #include "../../io/files/juce_FileInputStream.h"
#include "../../io/files/juce_TemporaryFile.h" #include "../../io/files/juce_TemporaryFile.h"
@@ -156,7 +157,7 @@ struct SMPLChunk
struct SampleLoop struct SampleLoop
{ {
uint32 identifier; uint32 identifier;
uint32 type;
uint32 type; // these are different in AIFF and WAV
uint32 start; uint32 start;
uint32 end; uint32 end;
uint32 fraction; uint32 fraction;
@@ -214,8 +215,6 @@ struct SMPLChunk
SMPLChunk* const s = static_cast <SMPLChunk*> (data.getData()); 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->manufacturer = ByteOrder::swapIfBigEndian ((uint32) values.getValue ("Manufacturer", "0").getIntValue());
s->product = ByteOrder::swapIfBigEndian ((uint32) values.getValue ("Product", "0").getIntValue()); s->product = ByteOrder::swapIfBigEndian ((uint32) values.getValue ("Product", "0").getIntValue());
s->samplePeriod = ByteOrder::swapIfBigEndian ((uint32) values.getValue ("SamplePeriod", "0").getIntValue()); s->samplePeriod = ByteOrder::swapIfBigEndian ((uint32) values.getValue ("SamplePeriod", "0").getIntValue());
@@ -241,6 +240,52 @@ struct SMPLChunk
} }
} PACKED; } 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 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(); const int numCues = values.getValue ("NumCuePoints", "0").getIntValue();
if (numCues <= 0)
return MemoryBlock();
if (numCues > 0)
{
const size_t sizeNeeded = sizeof (CueChunk) + (numCues - 1) * sizeof (Cue);
data.setSize ((sizeNeeded + 3) & ~3, true);
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());
CueChunk* const c = static_cast <CueChunk*> (data.getData());
c->numCues = ByteOrder::swapIfBigEndian ((uint32) numCues);
c->numCues = ByteOrder::swapIfBigEndian ((uint32) numCues);
const String dataChunkID (chunkName ("data"));
const String dataChunkID (chunkName ("data"));
int nextOrder = 0;
for (int i = 0; i < numCues; ++i)
{
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());
}
#if JUCE_DEBUG
Array<int> identifiers;
#endif
return data;
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; } 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 struct ExtensibleWavSubFormat
{ {
@@ -355,6 +479,9 @@ public:
int64 end = 0; int64 end = 0;
bool hasGotType = false; bool hasGotType = false;
bool hasGotData = false; bool hasGotData = false;
int cueNoteIndex = 0;
int cueLabelIndex = 0;
int cueRegionIndex = 0;
const int firstChunkType = input->readInt(); const int firstChunkType = input->readInt();
@@ -479,6 +606,13 @@ public:
input->read (smpl, length); input->read (smpl, length);
smpl->copyTo (metadataValues, 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 ")) else if (chunkType == chunkName ("cue "))
{ {
HeapBlock <CueChunk> cue; HeapBlock <CueChunk> cue;
@@ -486,6 +620,65 @@ public:
input->read (cue, length); input->read (cue, length);
cue->copyTo (metadataValues, 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()) else if (chunkEnd <= input->getPosition())
{ {
break; break;
@@ -494,6 +687,11 @@ public:
input->setPosition (chunkEnd); 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) 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); bwavChunk = BWAVChunk::createFrom (metadataValues);
smplChunk = SMPLChunk::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(); headerPosition = out->getPosition();
@@ -636,7 +841,7 @@ public:
private: private:
ScopedPointer<AudioData::Converter> converter; ScopedPointer<AudioData::Converter> converter;
MemoryBlock tempBlock, bwavChunk, smplChunk, cueChunk;
MemoryBlock tempBlock, bwavChunk, smplChunk, instChunk, cueChunk, listChunk;
uint64 lengthInSamples, bytesWritten; uint64 lengthInSamples, bytesWritten;
int64 headerPosition; int64 headerPosition;
bool writeFailed; bool writeFailed;
@@ -675,7 +880,9 @@ private:
+ 8 + audioDataSize + (audioDataSize & 1) + 8 + audioDataSize + (audioDataSize & 1)
+ (bwavChunk.getSize() > 0 ? (8 + bwavChunk.getSize()) : 0) + (bwavChunk.getSize() > 0 ? (8 + bwavChunk.getSize()) : 0)
+ (smplChunk.getSize() > 0 ? (8 + smplChunk.getSize()) : 0) + (smplChunk.getSize() > 0 ? (8 + smplChunk.getSize()) : 0)
+ (instChunk.getSize() > 0 ? (8 + instChunk.getSize()) : 0)
+ (cueChunk .getSize() > 0 ? (8 + cueChunk .getSize()) : 0) + (cueChunk .getSize() > 0 ? (8 + cueChunk .getSize()) : 0)
+ (listChunk.getSize() > 0 ? (12 + listChunk.getSize()) : 0)
+ (8 + 28); // (ds64 chunk) + (8 + 28); // (ds64 chunk)
riffChunkSize += (riffChunkSize & 0x1); riffChunkSize += (riffChunkSize & 0x1);
@@ -754,6 +961,13 @@ private:
output->write (smplChunk.getData(), (int) smplChunk.getSize()); 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) if (cueChunk.getSize() > 0)
{ {
output->writeInt (chunkName ("cue ")); output->writeInt (chunkName ("cue "));
@@ -761,6 +975,14 @@ private:
output->write (cueChunk.getData(), (int) cueChunk.getSize()); 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 (chunkName ("data"));
output->writeInt (isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame)); output->writeInt (isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame));


+ 3
- 0
src/audio/plugin_client/juce_PluginHeaders.h View File

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


+ 875
- 774
src/audio/processors/juce_AudioProcessorGraph.cpp
File diff suppressed because it is too large
View File


+ 6
- 3
src/audio/processors/juce_AudioProcessorGraph.h View File

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


+ 1
- 2
src/events/juce_MessageManager.cpp View File

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


+ 4
- 7
src/events/juce_MessageManager.h View File

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


+ 2
- 0
src/gui/components/windows/juce_DocumentWindow.h View File

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


+ 2
- 2
src/gui/components/windows/juce_ResizableWindow.h View File

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


+ 2
- 1
src/gui/graphics/imaging/juce_CameraDevice.h View File

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


+ 2
- 1
src/threads/juce_TimeSliceThread.h View File

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


Loading…
Cancel
Save