Browse Source

Try to get a minimal version of juce v4.x in, for patchbay mode

tags/1.9.8
falkTX 7 years ago
parent
commit
898121c8e4
42 changed files with 19039 additions and 0 deletions
  1. +122
    -0
      source/modules/juce_audio_graph/Makefile
  2. +1100
    -0
      source/modules/juce_audio_graph/buffers/juce_AudioSampleBuffer.h
  3. +1155
    -0
      source/modules/juce_audio_graph/containers/juce_Array.h
  4. +140
    -0
      source/modules/juce_audio_graph/containers/juce_ArrayAllocationBase.h
  5. +197
    -0
      source/modules/juce_audio_graph/containers/juce_ElementComparator.h
  6. +255
    -0
      source/modules/juce_audio_graph/containers/juce_NamedValueSet.cpp
  7. +188
    -0
      source/modules/juce_audio_graph/containers/juce_NamedValueSet.h
  8. +841
    -0
      source/modules/juce_audio_graph/containers/juce_OwnedArray.h
  9. +799
    -0
      source/modules/juce_audio_graph/containers/juce_Variant.cpp
  10. +353
    -0
      source/modules/juce_audio_graph/containers/juce_Variant.h
  11. +75
    -0
      source/modules/juce_audio_graph/juce_audio_graph.cpp
  12. +167
    -0
      source/modules/juce_audio_graph/juce_audio_graph.h
  13. +19
    -0
      source/modules/juce_audio_graph/juce_audio_graph.mm
  14. +554
    -0
      source/modules/juce_audio_graph/maths/juce_MathsFunctions.h
  15. +249
    -0
      source/modules/juce_audio_graph/memory/juce_Atomic.h
  16. +241
    -0
      source/modules/juce_audio_graph/memory/juce_ByteOrder.h
  17. +314
    -0
      source/modules/juce_audio_graph/memory/juce_HeapBlock.h
  18. +120
    -0
      source/modules/juce_audio_graph/memory/juce_Memory.h
  19. +418
    -0
      source/modules/juce_audio_graph/memory/juce_MemoryBlock.cpp
  20. +263
    -0
      source/modules/juce_audio_graph/memory/juce_MemoryBlock.h
  21. +235
    -0
      source/modules/juce_audio_graph/midi/juce_MidiBuffer.cpp
  22. +241
    -0
      source/modules/juce_audio_graph/midi/juce_MidiBuffer.h
  23. +1150
    -0
      source/modules/juce_audio_graph/midi/juce_MidiMessage.cpp
  24. +940
    -0
      source/modules/juce_audio_graph/midi/juce_MidiMessage.h
  25. +144
    -0
      source/modules/juce_audio_graph/processors/juce_AudioPlayHead.h
  26. +124
    -0
      source/modules/juce_audio_graph/processors/juce_AudioProcessor.cpp
  27. +381
    -0
      source/modules/juce_audio_graph/processors/juce_AudioProcessor.h
  28. +1630
    -0
      source/modules/juce_audio_graph/processors/juce_AudioProcessorGraph.cpp
  29. +387
    -0
      source/modules/juce_audio_graph/processors/juce_AudioProcessorGraph.h
  30. +238
    -0
      source/modules/juce_audio_graph/streams/juce_InputStream.cpp
  31. +268
    -0
      source/modules/juce_audio_graph/streams/juce_InputStream.h
  32. +216
    -0
      source/modules/juce_audio_graph/streams/juce_MemoryOutputStream.cpp
  33. +141
    -0
      source/modules/juce_audio_graph/streams/juce_MemoryOutputStream.h
  34. +353
    -0
      source/modules/juce_audio_graph/streams/juce_OutputStream.cpp
  35. +279
    -0
      source/modules/juce_audio_graph/streams/juce_OutputStream.h
  36. +572
    -0
      source/modules/juce_audio_graph/text/juce_CharPointer_UTF8.h
  37. +171
    -0
      source/modules/juce_audio_graph/text/juce_CharacterFunctions.cpp
  38. +620
    -0
      source/modules/juce_audio_graph/text/juce_CharacterFunctions.h
  39. +90
    -0
      source/modules/juce_audio_graph/text/juce_NewLine.h
  40. +1972
    -0
      source/modules/juce_audio_graph/text/juce_String.cpp
  41. +1181
    -0
      source/modules/juce_audio_graph/text/juce_String.h
  42. +136
    -0
      source/modules/juce_audio_graph/text/juce_StringRef.h

+ 122
- 0
source/modules/juce_audio_graph/Makefile View File

@@ -0,0 +1,122 @@
#!/usr/bin/make -f
# Makefile for juce_audio_graph #
# ----------------------------- #
# Created by falkTX
#

CWD=../..
MODULENAME=juce_audio_graph
include ../Makefile.mk

# ----------------------------------------------------------------------------------------------------------------------------

BUILD_CXX_FLAGS += -I..

# ----------------------------------------------------------------------------------------------------------------------------

ifeq ($(MACOS),true)
OBJS = $(OBJDIR)/$(MODULENAME).mm.o
OBJS_posix32 = $(OBJDIR)/$(MODULENAME).mm.posix32.o
OBJS_posix64 = $(OBJDIR)/$(MODULENAME).mm.posix64.o
else
OBJS = $(OBJDIR)/$(MODULENAME).cpp.o
OBJS_posix32 = $(OBJDIR)/$(MODULENAME).cpp.posix32.o
OBJS_posix64 = $(OBJDIR)/$(MODULENAME).cpp.posix64.o
endif
OBJS_win32 = $(OBJDIR)/$(MODULENAME).cpp.win32.o
OBJS_win64 = $(OBJDIR)/$(MODULENAME).cpp.win64.o

# ----------------------------------------------------------------------------------------------------------------------------

all: $(MODULEDIR)/$(MODULENAME).a
posix32: $(MODULEDIR)/$(MODULENAME).posix32.a
posix64: $(MODULEDIR)/$(MODULENAME).posix64.a
win32: $(MODULEDIR)/$(MODULENAME).win32.a
win64: $(MODULEDIR)/$(MODULENAME).win64.a

# ----------------------------------------------------------------------------------------------------------------------------

clean:
rm -f $(OBJDIR)/*.o $(MODULEDIR)/$(MODULENAME)*.a

debug:
$(MAKE) DEBUG=true

test: main.cpp $(MODULEDIR)/$(MODULENAME).a
@$(CXX) $< $(MODULEDIR)/$(MODULENAME).a $(BUILD_CXX_FLAGS) $(LINK_FLAGS) -lpthread -o $@

# ----------------------------------------------------------------------------------------------------------------------------

$(MODULEDIR)/$(MODULENAME).a: $(OBJS)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).a"
@rm -f $@
@$(AR) crs $@ $^

$(MODULEDIR)/$(MODULENAME).posix32.a: $(OBJS_posix32)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).posix32.a"
@rm -f $@
@$(AR) crs $@ $^

$(MODULEDIR)/$(MODULENAME).posix64.a: $(OBJS_posix64)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).posix64.a"
@rm -f $@
@$(AR) crs $@ $^

$(MODULEDIR)/$(MODULENAME).win32.a: $(OBJS_win32)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).win32.a"
@rm -f $@
@$(AR) crs $@ $^

$(MODULEDIR)/$(MODULENAME).win64.a: $(OBJS_win64)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).win64.a"
@rm -f $@
@$(AR) crs $@ $^

# ----------------------------------------------------------------------------------------------------------------------------

$(OBJDIR)/$(MODULENAME).cpp.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

$(OBJDIR)/$(MODULENAME).cpp.%32.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $< (32bit)"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -c -o $@

$(OBJDIR)/$(MODULENAME).cpp.%64.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $< (64bit)"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -c -o $@

# ----------------------------------------------------------------------------------------------------------------------------

$(OBJDIR)/$(MODULENAME).mm.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@

$(OBJDIR)/$(MODULENAME).mm.%32.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $< (32bit)"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -ObjC++ -c -o $@

$(OBJDIR)/$(MODULENAME).mm.%64.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $< (64bit)"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -ObjC++ -c -o $@

# ----------------------------------------------------------------------------------------------------------------------------

-include $(OBJS:%.o=%.d)
-include $(OBJS_posix32:%.o=%.d)
-include $(OBJS_posix64:%.o=%.d)
-include $(OBJS_win32:%.o=%.d)
-include $(OBJS_win64:%.o=%.d)

# ----------------------------------------------------------------------------------------------------------------------------

+ 1100
- 0
source/modules/juce_audio_graph/buffers/juce_AudioSampleBuffer.h
File diff suppressed because it is too large
View File


+ 1155
- 0
source/modules/juce_audio_graph/containers/juce_Array.h
File diff suppressed because it is too large
View File


+ 140
- 0
source/modules/juce_audio_graph/containers/juce_ArrayAllocationBase.h View File

@@ -0,0 +1,140 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_ARRAYALLOCATIONBASE_H_INCLUDED
#define JUCE_ARRAYALLOCATIONBASE_H_INCLUDED
//==============================================================================
/**
Implements some basic array storage allocation functions.
This class isn't really for public use - it's used by the other
array classes, but might come in handy for some purposes.
It inherits from a critical section class to allow the arrays to use
the "empty base class optimisation" pattern to reduce their footprint.
@see Array, OwnedArray, ReferenceCountedArray
*/
template <class ElementType>
class ArrayAllocationBase
{
public:
//==============================================================================
/** Creates an empty array. */
ArrayAllocationBase() noexcept
: numAllocated (0)
{
}
/** Destructor. */
~ArrayAllocationBase() noexcept
{
}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
ArrayAllocationBase (ArrayAllocationBase<ElementType>&& other) noexcept
: elements (static_cast<HeapBlock<ElementType>&&> (other.elements)),
numAllocated (other.numAllocated)
{
}
ArrayAllocationBase& operator= (ArrayAllocationBase<ElementType>&& other) noexcept
{
elements = static_cast<HeapBlock<ElementType>&&> (other.elements);
numAllocated = other.numAllocated;
return *this;
}
#endif
//==============================================================================
/** Changes the amount of storage allocated.
This will retain any data currently held in the array, and either add or
remove extra space at the end.
@param numElements the number of elements that are needed
*/
void setAllocatedSize (const int numElements)
{
if (numAllocated != numElements)
{
if (numElements > 0)
elements.realloc ((size_t) numElements);
else
elements.free();
numAllocated = numElements;
}
}
/** Increases the amount of storage allocated if it is less than a given amount.
This will retain any data currently held in the array, but will add
extra space at the end to make sure there it's at least as big as the size
passed in. If it's already bigger, no action is taken.
@param minNumElements the minimum number of elements that are needed
*/
void ensureAllocatedSize (const int minNumElements)
{
if (minNumElements > numAllocated)
setAllocatedSize ((minNumElements + minNumElements / 2 + 8) & ~7);
jassert (numAllocated <= 0 || elements != nullptr);
}
/** Minimises the amount of storage allocated so that it's no more than
the given number of elements.
*/
void shrinkToNoMoreThan (const int maxNumElements)
{
if (maxNumElements < numAllocated)
setAllocatedSize (maxNumElements);
}
/** Swap the contents of two objects. */
void swapWith (ArrayAllocationBase <ElementType>& other) noexcept
{
elements.swapWith (other.elements);
std::swap (numAllocated, other.numAllocated);
}
//==============================================================================
HeapBlock<ElementType> elements;
int numAllocated;
private:
JUCE_DECLARE_NON_COPYABLE (ArrayAllocationBase)
};
#endif // JUCE_ARRAYALLOCATIONBASE_H_INCLUDED

+ 197
- 0
source/modules/juce_audio_graph/containers/juce_ElementComparator.h View File

@@ -0,0 +1,197 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_ELEMENTCOMPARATOR_H_INCLUDED
#define JUCE_ELEMENTCOMPARATOR_H_INCLUDED
#ifndef DOXYGEN
/** This is an internal helper class which converts a juce ElementComparator style
class (using a "compareElements" method) into a class that's compatible with
std::sort (i.e. using an operator() to compare the elements)
*/
template <typename ElementComparator>
struct SortFunctionConverter
{
SortFunctionConverter (ElementComparator& e) : comparator (e) {}
template <typename Type>
bool operator() (Type a, Type b) { return comparator.compareElements (a, b) < 0; }
private:
ElementComparator& comparator;
SortFunctionConverter& operator= (const SortFunctionConverter&) JUCE_DELETED_FUNCTION;
};
#endif
//==============================================================================
/**
Sorts a range of elements in an array.
The comparator object that is passed-in must define a public method with the following
signature:
@code
int compareElements (ElementType first, ElementType second);
@endcode
..and this method must return:
- a value of < 0 if the first comes before the second
- a value of 0 if the two objects are equivalent
- a value of > 0 if the second comes before the first
To improve performance, the compareElements() method can be declared as static or const.
@param comparator an object which defines a compareElements() method
@param array the array to sort
@param firstElement the index of the first element of the range to be sorted
@param lastElement the index of the last element in the range that needs
sorting (this is inclusive)
@param retainOrderOfEquivalentItems if true, the order of items that the
comparator deems the same will be maintained - this will be
a slower algorithm than if they are allowed to be moved around.
@see sortArrayRetainingOrder
*/
template <class ElementType, class ElementComparator>
static void sortArray (ElementComparator& comparator,
ElementType* const array,
int firstElement,
int lastElement,
const bool retainOrderOfEquivalentItems)
{
SortFunctionConverter<ElementComparator> converter (comparator);
if (retainOrderOfEquivalentItems)
std::stable_sort (array + firstElement, array + lastElement + 1, converter);
else
std::sort (array + firstElement, array + lastElement + 1, converter);
}
//==============================================================================
/**
Searches a sorted array of elements, looking for the index at which a specified value
should be inserted for it to be in the correct order.
The comparator object that is passed-in must define a public method with the following
signature:
@code
int compareElements (ElementType first, ElementType second);
@endcode
..and this method must return:
- a value of < 0 if the first comes before the second
- a value of 0 if the two objects are equivalent
- a value of > 0 if the second comes before the first
To improve performance, the compareElements() method can be declared as static or const.
@param comparator an object which defines a compareElements() method
@param array the array to search
@param newElement the value that is going to be inserted
@param firstElement the index of the first element to search
@param lastElement the index of the last element in the range (this is non-inclusive)
*/
template <class ElementType, class ElementComparator>
static int findInsertIndexInSortedArray (ElementComparator& comparator,
ElementType* const array,
const ElementType newElement,
int firstElement,
int lastElement)
{
jassert (firstElement <= lastElement);
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this
// avoids getting warning messages about the parameter being unused
while (firstElement < lastElement)
{
if (comparator.compareElements (newElement, array [firstElement]) == 0)
{
++firstElement;
break;
}
else
{
const int halfway = (firstElement + lastElement) >> 1;
if (halfway == firstElement)
{
if (comparator.compareElements (newElement, array [halfway]) >= 0)
++firstElement;
break;
}
else if (comparator.compareElements (newElement, array [halfway]) >= 0)
{
firstElement = halfway;
}
else
{
lastElement = halfway;
}
}
}
return firstElement;
}
//==============================================================================
/**
A simple ElementComparator class that can be used to sort an array of
objects that support the '<' operator.
This will work for primitive types and objects that implement operator<().
Example: @code
Array <int> myArray;
DefaultElementComparator<int> sorter;
myArray.sort (sorter);
@endcode
@see ElementComparator
*/
template <class ElementType>
class DefaultElementComparator
{
private:
typedef PARAMETER_TYPE (ElementType) ParameterType;
public:
static int compareElements (ParameterType first, ParameterType second)
{
return (first < second) ? -1 : ((second < first) ? 1 : 0);
}
};
#endif // JUCE_ELEMENTCOMPARATOR_H_INCLUDED

+ 255
- 0
source/modules/juce_audio_graph/containers/juce_NamedValueSet.cpp View File

@@ -0,0 +1,255 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
//==============================================================================
NamedValueSet::NamedValueSet() noexcept
{
}
NamedValueSet::NamedValueSet (const NamedValueSet& other)
: values (other.values)
{
}
NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other)
{
clear();
values = other.values;
return *this;
}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept
: values (static_cast<Array<NamedValue>&&> (other.values))
{
}
NamedValueSet& NamedValueSet::operator= (NamedValueSet&& other) noexcept
{
other.values.swapWith (values);
return *this;
}
#endif
NamedValueSet::~NamedValueSet() noexcept
{
}
void NamedValueSet::clear()
{
values.clear();
}
bool NamedValueSet::operator== (const NamedValueSet& other) const
{
return values == other.values;
}
bool NamedValueSet::operator!= (const NamedValueSet& other) const
{
return ! operator== (other);
}
int NamedValueSet::size() const noexcept
{
return values.size();
}
bool NamedValueSet::isEmpty() const noexcept
{
return values.isEmpty();
}
static const var& getNullVarRef() noexcept
{
#if JUCE_ALLOW_STATIC_NULL_VARIABLES
return var::null;
#else
static var nullVar;
return nullVar;
#endif
}
const var& NamedValueSet::operator[] (const Identifier& name) const noexcept
{
if (const var* v = getVarPointer (name))
return *v;
return getNullVarRef();
}
var NamedValueSet::getWithDefault (const Identifier& name, const var& defaultReturnValue) const
{
if (const var* const v = getVarPointer (name))
return *v;
return defaultReturnValue;
}
var* NamedValueSet::getVarPointer (const Identifier& name) const noexcept
{
for (NamedValue* e = values.end(), *i = values.begin(); i != e; ++i)
if (i->name == name)
return &(i->value);
return nullptr;
}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
bool NamedValueSet::set (const Identifier& name, var&& newValue)
{
if (var* const v = getVarPointer (name))
{
if (v->equalsWithSameType (newValue))
return false;
*v = static_cast<var&&> (newValue);
return true;
}
values.add (NamedValue (name, static_cast<var&&> (newValue)));
return true;
}
#endif
bool NamedValueSet::set (const Identifier& name, const var& newValue)
{
if (var* const v = getVarPointer (name))
{
if (v->equalsWithSameType (newValue))
return false;
*v = newValue;
return true;
}
values.add (NamedValue (name, newValue));
return true;
}
bool NamedValueSet::contains (const Identifier& name) const noexcept
{
return getVarPointer (name) != nullptr;
}
int NamedValueSet::indexOf (const Identifier& name) const noexcept
{
const int numValues = values.size();
for (int i = 0; i < numValues; ++i)
if (values.getReference(i).name == name)
return i;
return -1;
}
bool NamedValueSet::remove (const Identifier& name)
{
const int numValues = values.size();
for (int i = 0; i < numValues; ++i)
{
if (values.getReference(i).name == name)
{
values.remove (i);
return true;
}
}
return false;
}
Identifier NamedValueSet::getName (const int index) const noexcept
{
if (isPositiveAndBelow (index, values.size()))
return values.getReference (index).name;
jassertfalse;
return Identifier();
}
const var& NamedValueSet::getValueAt (const int index) const noexcept
{
if (isPositiveAndBelow (index, values.size()))
return values.getReference (index).value;
jassertfalse;
return getNullVarRef();
}
var* NamedValueSet::getVarPointerAt (int index) const noexcept
{
if (isPositiveAndBelow (index, values.size()))
return &(values.getReference (index).value);
return nullptr;
}
void NamedValueSet::setFromXmlAttributes (const XmlElement& xml)
{
values.clearQuick();
for (const XmlElement::XmlAttributeNode* att = xml.attributes; att != nullptr; att = att->nextListItem)
{
if (att->name.toString().startsWith ("base64:"))
{
MemoryBlock mb;
if (mb.fromBase64Encoding (att->value))
{
values.add (NamedValue (att->name.toString().substring (7), var (mb)));
continue;
}
}
values.add (NamedValue (att->name, var (att->value)));
}
}
void NamedValueSet::copyToXmlAttributes (XmlElement& xml) const
{
for (NamedValue* e = values.end(), *i = values.begin(); i != e; ++i)
{
if (const MemoryBlock* mb = i->value.getBinaryData())
{
xml.setAttribute ("base64:" + i->name.toString(), mb->toBase64Encoding());
}
else
{
// These types can't be stored as XML!
jassert (! i->value.isObject());
jassert (! i->value.isMethod());
jassert (! i->value.isArray());
xml.setAttribute (i->name.toString(),
i->value.toString());
}
}
}

+ 188
- 0
source/modules/juce_audio_graph/containers/juce_NamedValueSet.h View File

@@ -0,0 +1,188 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_NAMEDVALUESET_H_INCLUDED
#define JUCE_NAMEDVALUESET_H_INCLUDED
//==============================================================================
/** Holds a set of named var objects.
This can be used as a basic structure to hold a set of var object, which can
be retrieved by using their identifier.
*/
class JUCE_API NamedValueSet
{
public:
/** Creates an empty set. */
NamedValueSet() noexcept;
/** Creates a copy of another set. */
NamedValueSet (const NamedValueSet&);
/** Replaces this set with a copy of another set. */
NamedValueSet& operator= (const NamedValueSet&);
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
NamedValueSet (NamedValueSet&&) noexcept;
NamedValueSet& operator= (NamedValueSet&&) noexcept;
#endif
/** Destructor. */
~NamedValueSet() noexcept;
bool operator== (const NamedValueSet&) const;
bool operator!= (const NamedValueSet&) const;
//==============================================================================
struct NamedValue
{
NamedValue() noexcept {}
NamedValue (const Identifier& n, const var& v) : name (n), value (v) {}
NamedValue (const NamedValue& other) : name (other.name), value (other.value) {}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
NamedValue (NamedValue&& other) noexcept
: name (static_cast<Identifier&&> (other.name)),
value (static_cast<var&&> (other.value))
{
}
NamedValue (Identifier&& n, var&& v) noexcept
: name (static_cast<Identifier&&> (n)),
value (static_cast<var&&> (v))
{
}
NamedValue& operator= (NamedValue&& other) noexcept
{
name = static_cast<Identifier&&> (other.name);
value = static_cast<var&&> (other.value);
return *this;
}
#endif
bool operator== (const NamedValue& other) const noexcept { return name == other.name && value == other.value; }
bool operator!= (const NamedValue& other) const noexcept { return ! operator== (other); }
Identifier name;
var value;
};
NamedValueSet::NamedValue* begin() { return values.begin(); }
NamedValueSet::NamedValue* end() { return values.end(); }
//==============================================================================
/** Returns the total number of values that the set contains. */
int size() const noexcept;
/** Returns true if the set is empty. */
bool isEmpty() const noexcept;
/** Returns the value of a named item.
If the name isn't found, this will return a void variant.
@see getProperty
*/
const var& operator[] (const Identifier& name) const noexcept;
/** Tries to return the named value, but if no such value is found, this will
instead return the supplied default value.
*/
var getWithDefault (const Identifier& name, const var& defaultReturnValue) const;
/** Changes or adds a named value.
@returns true if a value was changed or added; false if the
value was already set the value passed-in.
*/
bool set (const Identifier& name, const var& newValue);
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
/** Changes or adds a named value.
@returns true if a value was changed or added; false if the
value was already set the value passed-in.
*/
bool set (const Identifier& name, var&& newValue);
#endif
/** Returns true if the set contains an item with the specified name. */
bool contains (const Identifier& name) const noexcept;
/** Removes a value from the set.
@returns true if a value was removed; false if there was no value
with the name that was given.
*/
bool remove (const Identifier& name);
/** Returns the name of the value at a given index.
The index must be between 0 and size() - 1.
*/
Identifier getName (int index) const noexcept;
/** Returns a pointer to the var that holds a named value, or null if there is
no value with this name.
Do not use this method unless you really need access to the internal var object
for some reason - for normal reading and writing always prefer operator[]() and set().
*/
var* getVarPointer (const Identifier& name) const noexcept;
/** Returns the value of the item at a given index.
The index must be between 0 and size() - 1.
*/
const var& getValueAt (int index) const noexcept;
/** Returns the value of the item at a given index.
The index must be between 0 and size() - 1, or this will return a nullptr
*/
var* getVarPointerAt (int index) const noexcept;
/** Returns the index of the given name, or -1 if it's not found. */
int indexOf (const Identifier& name) const noexcept;
/** Removes all values. */
void clear();
//==============================================================================
/** Sets properties to the values of all of an XML element's attributes. */
void setFromXmlAttributes (const XmlElement& xml);
/** Sets attributes in an XML element corresponding to each of this object's
properties.
*/
void copyToXmlAttributes (XmlElement& xml) const;
private:
//==============================================================================
Array<NamedValue> values;
};
#endif // JUCE_NAMEDVALUESET_H_INCLUDED

+ 841
- 0
source/modules/juce_audio_graph/containers/juce_OwnedArray.h View File

@@ -0,0 +1,841 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_OWNEDARRAY_H_INCLUDED
#define JUCE_OWNEDARRAY_H_INCLUDED
//==============================================================================
/** An array designed for holding objects.
This holds a list of pointers to objects, and will automatically
delete the objects when they are removed from the array, or when the
array is itself deleted.
Declare it in the form: OwnedArray<MyObjectClass>
..and then add new objects, e.g. myOwnedArray.add (new MyObjectClass());
After adding objects, they are 'owned' by the array and will be deleted when
removed or replaced.
To make all the array's methods thread-safe, pass in "CriticalSection" as the templated
TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
@see Array, ReferenceCountedArray, StringArray, CriticalSection
*/
template <class ObjectClass>
class OwnedArray
{
public:
//==============================================================================
/** Creates an empty array. */
OwnedArray() noexcept
: numUsed (0)
{
}
/** Deletes the array and also deletes any objects inside it.
To get rid of the array without deleting its objects, use its
clear (false) method before deleting it.
*/
~OwnedArray()
{
deleteAllObjects();
}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
OwnedArray (OwnedArray&& other) noexcept
: data (static_cast<ArrayAllocationBase <ObjectClass*>&&> (other.data)),
numUsed (other.numUsed)
{
other.numUsed = 0;
}
OwnedArray& operator= (OwnedArray&& other) noexcept
{
deleteAllObjects();
data = static_cast<ArrayAllocationBase <ObjectClass*>&&> (other.data);
numUsed = other.numUsed;
other.numUsed = 0;
return *this;
}
#endif
//==============================================================================
/** Clears the array, optionally deleting the objects inside it first. */
void clear (bool deleteObjects = true)
{
if (deleteObjects)
deleteAllObjects();
data.setAllocatedSize (0);
numUsed = 0;
}
//==============================================================================
/** Clears the array, optionally deleting the objects inside it first. */
void clearQuick (bool deleteObjects)
{
if (deleteObjects)
deleteAllObjects();
numUsed = 0;
}
//==============================================================================
/** Returns the number of items currently in the array.
@see operator[]
*/
inline int size() const noexcept
{
return numUsed;
}
/** Returns true if the array is empty, false otherwise. */
inline bool isEmpty() const noexcept
{
return size() == 0;
}
/** Returns a pointer to the object at this index in the array.
If the index is out-of-range, this will return a null pointer, (and
it could be null anyway, because it's ok for the array to hold null
pointers as well as objects).
@see getUnchecked
*/
inline ObjectClass* operator[] (const int index) const noexcept
{
if (isPositiveAndBelow (index, numUsed))
{
jassert (data.elements != nullptr);
return data.elements [index];
}
return nullptr;
}
/** Returns a pointer to the object at this index in the array, without checking whether the index is in-range.
This is a faster and less safe version of operator[] which doesn't check the index passed in, so
it can be used when you're sure the index is always going to be legal.
*/
inline ObjectClass* getUnchecked (const int index) const noexcept
{
jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr);
return data.elements [index];
}
/** Returns a pointer to the first object in the array.
This will return a null pointer if the array's empty.
@see getLast
*/
inline ObjectClass* getFirst() const noexcept
{
if (numUsed > 0)
{
jassert (data.elements != nullptr);
return data.elements [0];
}
return nullptr;
}
/** Returns a pointer to the last object in the array.
This will return a null pointer if the array's empty.
@see getFirst
*/
inline ObjectClass* getLast() const noexcept
{
if (numUsed > 0)
{
jassert (data.elements != nullptr);
return data.elements [numUsed - 1];
}
return nullptr;
}
/** Returns a pointer to the actual array data.
This pointer will only be valid until the next time a non-const method
is called on the array.
*/
inline ObjectClass** getRawDataPointer() noexcept
{
return data.elements;
}
//==============================================================================
/** Returns a pointer to the first element in the array.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline ObjectClass** begin() const noexcept
{
return data.elements;
}
/** Returns a pointer to the element which follows the last element in the array.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline ObjectClass** end() const noexcept
{
#if JUCE_DEBUG
if (data.elements == nullptr || numUsed <= 0) // (to keep static analysers happy)
return data.elements;
#endif
return data.elements + numUsed;
}
//==============================================================================
/** Finds the index of an object which might be in the array.
@param objectToLookFor the object to look for
@returns the index at which the object was found, or -1 if it's not found
*/
int indexOf (const ObjectClass* objectToLookFor) const noexcept
{
ObjectClass* const* e = data.elements.getData();
ObjectClass* const* const end_ = e + numUsed;
for (; e != end_; ++e)
if (objectToLookFor == *e)
return static_cast<int> (e - data.elements.getData());
return -1;
}
/** Returns true if the array contains a specified object.
@param objectToLookFor the object to look for
@returns true if the object is in the array
*/
bool contains (const ObjectClass* objectToLookFor) const noexcept
{
ObjectClass* const* e = data.elements.getData();
ObjectClass* const* const end_ = e + numUsed;
for (; e != end_; ++e)
if (objectToLookFor == *e)
return true;
return false;
}
//==============================================================================
/** Appends a new object to the end of the array.
Note that the this object will be deleted by the OwnedArray when it
is removed, so be careful not to delete it somewhere else.
Also be careful not to add the same object to the array more than once,
as this will obviously cause deletion of dangling pointers.
@param newObject the new object to add to the array
@returns the new object that was added
@see set, insert, addIfNotAlreadyThere, addSorted
*/
ObjectClass* add (ObjectClass* newObject) noexcept
{
data.ensureAllocatedSize (numUsed + 1);
jassert (data.elements != nullptr);
data.elements [numUsed++] = newObject;
return newObject;
}
/** Inserts a new object into the array at the given index.
Note that the this object will be deleted by the OwnedArray when it
is removed, so be careful not to delete it somewhere else.
If the index is less than 0 or greater than the size of the array, the
element will be added to the end of the array.
Otherwise, it will be inserted into the array, moving all the later elements
along to make room.
Be careful not to add the same object to the array more than once,
as this will obviously cause deletion of dangling pointers.
@param indexToInsertAt the index at which the new element should be inserted
@param newObject the new object to add to the array
@returns the new object that was added
@see add, addSorted, addIfNotAlreadyThere, set
*/
ObjectClass* insert (int indexToInsertAt, ObjectClass* newObject) noexcept
{
if (indexToInsertAt < 0)
return add (newObject);
if (indexToInsertAt > numUsed)
indexToInsertAt = numUsed;
data.ensureAllocatedSize (numUsed + 1);
jassert (data.elements != nullptr);
ObjectClass** const e = data.elements + indexToInsertAt;
const int numToMove = numUsed - indexToInsertAt;
if (numToMove > 0)
memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove);
*e = newObject;
++numUsed;
return newObject;
}
/** Inserts an array of values into this array at a given position.
If the index is less than 0 or greater than the size of the array, the
new elements will be added to the end of the array.
Otherwise, they will be inserted into the array, moving all the later elements
along to make room.
@param indexToInsertAt the index at which the first new element should be inserted
@param newObjects the new values to add to the array
@param numberOfElements how many items are in the array
@see insert, add, addSorted, set
*/
void insertArray (int indexToInsertAt,
ObjectClass* const* newObjects,
int numberOfElements)
{
if (numberOfElements > 0)
{
data.ensureAllocatedSize (numUsed + numberOfElements);
ObjectClass** insertPos = data.elements;
if (isPositiveAndBelow (indexToInsertAt, numUsed))
{
insertPos += indexToInsertAt;
const size_t numberToMove = (size_t) (numUsed - indexToInsertAt);
memmove (insertPos + numberOfElements, insertPos, numberToMove * sizeof (ObjectClass*));
}
else
{
insertPos += numUsed;
}
numUsed += numberOfElements;
while (--numberOfElements >= 0)
*insertPos++ = *newObjects++;
}
}
/** Appends a new object at the end of the array as long as the array doesn't
already contain it.
If the array already contains a matching object, nothing will be done.
@param newObject the new object to add to the array
@returns true if the new object was added, false otherwise
*/
bool addIfNotAlreadyThere (ObjectClass* newObject) noexcept
{
if (contains (newObject))
return false;
add (newObject);
return true;
}
/** Replaces an object in the array with a different one.
If the index is less than zero, this method does nothing.
If the index is beyond the end of the array, the new object is added to the end of the array.
Be careful not to add the same object to the array more than once,
as this will obviously cause deletion of dangling pointers.
@param indexToChange the index whose value you want to change
@param newObject the new value to set for this index.
@param deleteOldElement whether to delete the object that's being replaced with the new one
@see add, insert, remove
*/
ObjectClass* set (int indexToChange, ObjectClass* newObject, bool deleteOldElement = true)
{
if (indexToChange >= 0)
{
ScopedPointer<ObjectClass> toDelete;
{
if (indexToChange < numUsed)
{
if (deleteOldElement)
{
toDelete = data.elements [indexToChange];
if (toDelete == newObject)
toDelete.release();
}
data.elements [indexToChange] = newObject;
}
else
{
data.ensureAllocatedSize (numUsed + 1);
data.elements [numUsed++] = newObject;
}
}
}
else
{
jassertfalse; // you're trying to set an object at a negative index, which doesn't have
// any effect - but since the object is not being added, it may be leaking..
}
return newObject;
}
/** Adds elements from another array to the end of this array.
@param arrayToAddFrom the array from which to copy the elements
@param startIndex the first element of the other array to start copying from
@param numElementsToAdd how many elements to add from the other array. If this
value is negative or greater than the number of available elements,
all available elements will be copied.
@see add
*/
template <class OtherArrayType>
void addArray (const OtherArrayType& arrayToAddFrom,
int startIndex = 0,
int numElementsToAdd = -1)
{
if (startIndex < 0)
{
jassertfalse;
startIndex = 0;
}
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size())
numElementsToAdd = arrayToAddFrom.size() - startIndex;
data.ensureAllocatedSize (numUsed + numElementsToAdd);
jassert (numElementsToAdd <= 0 || data.elements != nullptr);
while (--numElementsToAdd >= 0)
{
data.elements [numUsed] = arrayToAddFrom.getUnchecked (startIndex++);
++numUsed;
}
}
/** Adds copies of the elements in another array to the end of this array.
The other array must be either an OwnedArray of a compatible type of object, or an Array
containing pointers to the same kind of object. The objects involved must provide
a copy constructor, and this will be used to create new copies of each element, and
add them to this array.
@param arrayToAddFrom the array from which to copy the elements
@param startIndex the first element of the other array to start copying from
@param numElementsToAdd how many elements to add from the other array. If this
value is negative or greater than the number of available elements,
all available elements will be copied.
@see add
*/
template <class OtherArrayType>
void addCopiesOf (const OtherArrayType& arrayToAddFrom,
int startIndex = 0,
int numElementsToAdd = -1)
{
if (startIndex < 0)
{
jassertfalse;
startIndex = 0;
}
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size())
numElementsToAdd = arrayToAddFrom.size() - startIndex;
data.ensureAllocatedSize (numUsed + numElementsToAdd);
jassert (numElementsToAdd <= 0 || data.elements != nullptr);
while (--numElementsToAdd >= 0)
data.elements [numUsed++] = createCopyIfNotNull (arrayToAddFrom.getUnchecked (startIndex++));
}
/** Inserts a new object into the array assuming that the array is sorted.
This will use a comparator to find the position at which the new object
should go. If the array isn't sorted, the behaviour of this
method will be unpredictable.
@param comparator the comparator to use to compare the elements - see the sort method
for details about this object's structure
@param newObject the new object to insert to the array
@returns the index at which the new object was added
@see add, sort, indexOfSorted
*/
template <class ElementComparator>
int addSorted (ElementComparator& comparator, ObjectClass* const newObject) noexcept
{
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this
// avoids getting warning messages about the parameter being unused
const int index = findInsertIndexInSortedArray (comparator, data.elements.getData(), newObject, 0, numUsed);
insert (index, newObject);
return index;
}
/** Finds the index of an object in the array, assuming that the array is sorted.
This will use a comparator to do a binary-chop to find the index of the given
element, if it exists. If the array isn't sorted, the behaviour of this
method will be unpredictable.
@param comparator the comparator to use to compare the elements - see the sort()
method for details about the form this object should take
@param objectToLookFor the object to search for
@returns the index of the element, or -1 if it's not found
@see addSorted, sort
*/
template <typename ElementComparator>
int indexOfSorted (ElementComparator& comparator, const ObjectClass* const objectToLookFor) const noexcept
{
ignoreUnused (comparator);
int s = 0, e = numUsed;
while (s < e)
{
if (comparator.compareElements (objectToLookFor, data.elements [s]) == 0)
return s;
const int halfway = (s + e) / 2;
if (halfway == s)
break;
if (comparator.compareElements (objectToLookFor, data.elements [halfway]) >= 0)
s = halfway;
else
e = halfway;
}
return -1;
}
//==============================================================================
/** Removes an object from the array.
This will remove the object at a given index (optionally also
deleting it) and move back all the subsequent objects to close the gap.
If the index passed in is out-of-range, nothing will happen.
@param indexToRemove the index of the element to remove
@param deleteObject whether to delete the object that is removed
@see removeObject, removeRange
*/
void remove (int indexToRemove, bool deleteObject = true)
{
ScopedPointer<ObjectClass> toDelete;
{
if (isPositiveAndBelow (indexToRemove, numUsed))
{
ObjectClass** const e = data.elements + indexToRemove;
if (deleteObject)
toDelete = *e;
--numUsed;
const int numToShift = numUsed - indexToRemove;
if (numToShift > 0)
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numToShift);
}
}
if ((numUsed << 1) < data.numAllocated)
minimiseStorageOverheads();
}
/** Removes and returns an object from the array without deleting it.
This will remove the object at a given index and return it, moving back all
the subsequent objects to close the gap. If the index passed in is out-of-range,
nothing will happen.
@param indexToRemove the index of the element to remove
@see remove, removeObject, removeRange
*/
ObjectClass* removeAndReturn (int indexToRemove)
{
ObjectClass* removedItem = nullptr;
if (isPositiveAndBelow (indexToRemove, numUsed))
{
ObjectClass** const e = data.elements + indexToRemove;
removedItem = *e;
--numUsed;
const int numToShift = numUsed - indexToRemove;
if (numToShift > 0)
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numToShift);
if ((numUsed << 1) < data.numAllocated)
minimiseStorageOverheads();
}
return removedItem;
}
/** Removes a specified object from the array.
If the item isn't found, no action is taken.
@param objectToRemove the object to try to remove
@param deleteObject whether to delete the object (if it's found)
@see remove, removeRange
*/
void removeObject (const ObjectClass* objectToRemove, bool deleteObject = true)
{
ObjectClass** const e = data.elements.getData();
for (int i = 0; i < numUsed; ++i)
{
if (objectToRemove == e[i])
{
remove (i, deleteObject);
break;
}
}
}
/** Removes a range of objects from the array.
This will remove a set of objects, starting from the given index,
and move any subsequent elements down to close the gap.
If the range extends beyond the bounds of the array, it will
be safely clipped to the size of the array.
@param startIndex the index of the first object to remove
@param numberToRemove how many objects should be removed
@param deleteObjects whether to delete the objects that get removed
@see remove, removeObject
*/
void removeRange (int startIndex, int numberToRemove, bool deleteObjects = true)
{
const int endIndex = jlimit (0, numUsed, startIndex + numberToRemove);
startIndex = jlimit (0, numUsed, startIndex);
if (endIndex > startIndex)
{
if (deleteObjects)
{
for (int i = startIndex; i < endIndex; ++i)
{
delete data.elements [i];
data.elements [i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer)
}
}
const int rangeSize = endIndex - startIndex;
ObjectClass** e = data.elements + startIndex;
int numToShift = numUsed - endIndex;
numUsed -= rangeSize;
while (--numToShift >= 0)
{
*e = e [rangeSize];
++e;
}
if ((numUsed << 1) < data.numAllocated)
minimiseStorageOverheads();
}
}
/** Removes the last n objects from the array.
@param howManyToRemove how many objects to remove from the end of the array
@param deleteObjects whether to also delete the objects that are removed
@see remove, removeObject, removeRange
*/
void removeLast (int howManyToRemove = 1,
bool deleteObjects = true)
{
if (howManyToRemove >= numUsed)
clear (deleteObjects);
else
removeRange (numUsed - howManyToRemove, howManyToRemove, deleteObjects);
}
/** Swaps a pair of objects in the array.
If either of the indexes passed in is out-of-range, nothing will happen,
otherwise the two objects at these positions will be exchanged.
*/
void swap (int index1,
int index2) noexcept
{
if (isPositiveAndBelow (index1, numUsed)
&& isPositiveAndBelow (index2, numUsed))
{
std::swap (data.elements [index1],
data.elements [index2]);
}
}
/** Moves one of the objects to a different position.
This will move the object to a specified index, shuffling along
any intervening elements as required.
So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling
move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }.
@param currentIndex the index of the object to be moved. If this isn't a
valid index, then nothing will be done
@param newIndex the index at which you'd like this object to end up. If this
is less than zero, it will be moved to the end of the array
*/
void move (int currentIndex, int newIndex) noexcept
{
if (currentIndex != newIndex)
{
if (isPositiveAndBelow (currentIndex, numUsed))
{
if (! isPositiveAndBelow (newIndex, numUsed))
newIndex = numUsed - 1;
ObjectClass* const value = data.elements [currentIndex];
if (newIndex > currentIndex)
{
memmove (data.elements + currentIndex,
data.elements + currentIndex + 1,
sizeof (ObjectClass*) * (size_t) (newIndex - currentIndex));
}
else
{
memmove (data.elements + newIndex + 1,
data.elements + newIndex,
sizeof (ObjectClass*) * (size_t) (currentIndex - newIndex));
}
data.elements [newIndex] = value;
}
}
}
/** This swaps the contents of this array with those of another array.
If you need to exchange two arrays, this is vastly quicker than using copy-by-value
because it just swaps their internal pointers.
*/
template <class OtherArrayType>
void swapWith (OtherArrayType& otherArray) noexcept
{
data.swapWith (otherArray.data);
std::swap (numUsed, otherArray.numUsed);
}
//==============================================================================
/** Reduces the amount of storage being used by the array.
Arrays typically allocate slightly more storage than they need, and after
removing elements, they may have quite a lot of unused space allocated.
This method will reduce the amount of allocated storage to a minimum.
*/
void minimiseStorageOverheads() noexcept
{
data.shrinkToNoMoreThan (numUsed);
}
/** Increases the array's internal storage to hold a minimum number of elements.
Calling this before adding a large known number of elements means that
the array won't have to keep dynamically resizing itself as the elements
are added, and it'll therefore be more efficient.
*/
void ensureStorageAllocated (const int minNumElements) noexcept
{
data.ensureAllocatedSize (minNumElements);
}
//==============================================================================
/** Sorts the elements in the array.
This will use a comparator object to sort the elements into order. The object
passed must have a method of the form:
@code
int compareElements (ElementType* first, ElementType* second);
@endcode
..and this method must return:
- a value of < 0 if the first comes before the second
- a value of 0 if the two objects are equivalent
- a value of > 0 if the second comes before the first
To improve performance, the compareElements() method can be declared as static or const.
@param comparator the comparator to use for comparing elements.
@param retainOrderOfEquivalentItems if this is true, then items
which the comparator says are equivalent will be
kept in the order in which they currently appear
in the array. This is slower to perform, but may
be important in some cases. If it's false, a faster
algorithm is used, but equivalent elements may be
rearranged.
@see sortArray, indexOfSorted
*/
template <class ElementComparator>
void sort (ElementComparator& comparator,
bool retainOrderOfEquivalentItems = false) const noexcept
{
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this
// avoids getting warning messages about the parameter being unused
sortArray (comparator, data.elements.getData(), 0, size() - 1, retainOrderOfEquivalentItems);
}
private:
//==============================================================================
ArrayAllocationBase <ObjectClass*> data;
int numUsed;
void deleteAllObjects()
{
while (numUsed > 0)
delete data.elements [--numUsed];
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OwnedArray)
};
#endif // JUCE_OWNEDARRAY_H_INCLUDED

+ 799
- 0
source/modules/juce_audio_graph/containers/juce_Variant.cpp View File

@@ -0,0 +1,799 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
enum VariantStreamMarkers
{
varMarker_Int = 1,
varMarker_BoolTrue = 2,
varMarker_BoolFalse = 3,
varMarker_Double = 4,
varMarker_String = 5,
varMarker_Int64 = 6,
varMarker_Array = 7,
varMarker_Binary = 8,
varMarker_Undefined = 9
};
//==============================================================================
class var::VariantType
{
public:
VariantType() noexcept {}
virtual ~VariantType() noexcept {}
virtual int toInt (const ValueUnion&) const noexcept { return 0; }
virtual int64 toInt64 (const ValueUnion&) const noexcept { return 0; }
virtual double toDouble (const ValueUnion&) const noexcept { return 0; }
virtual String toString (const ValueUnion&) const { return String(); }
virtual bool toBool (const ValueUnion&) const noexcept { return false; }
virtual ReferenceCountedObject* toObject (const ValueUnion&) const noexcept { return nullptr; }
virtual Array<var>* toArray (const ValueUnion&) const noexcept { return nullptr; }
virtual MemoryBlock* toBinary (const ValueUnion&) const noexcept { return nullptr; }
virtual var clone (const var& original) const { return original; }
virtual bool isVoid() const noexcept { return false; }
virtual bool isUndefined() const noexcept { return false; }
virtual bool isInt() const noexcept { return false; }
virtual bool isInt64() const noexcept { return false; }
virtual bool isBool() const noexcept { return false; }
virtual bool isDouble() const noexcept { return false; }
virtual bool isString() const noexcept { return false; }
virtual bool isObject() const noexcept { return false; }
virtual bool isArray() const noexcept { return false; }
virtual bool isBinary() const noexcept { return false; }
virtual bool isMethod() const noexcept { return false; }
virtual void cleanUp (ValueUnion&) const noexcept {}
virtual void createCopy (ValueUnion& dest, const ValueUnion& source) const { dest = source; }
virtual bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept = 0;
virtual void writeToStream (const ValueUnion& data, OutputStream& output) const = 0;
};
//==============================================================================
class var::VariantType_Void : public var::VariantType
{
public:
VariantType_Void() noexcept {}
static const VariantType_Void instance;
bool isVoid() const noexcept override { return true; }
bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); }
void writeToStream (const ValueUnion&, OutputStream& output) const override { output.writeCompressedInt (0); }
};
//==============================================================================
class var::VariantType_Undefined : public var::VariantType
{
public:
VariantType_Undefined() noexcept {}
static const VariantType_Undefined instance;
bool isUndefined() const noexcept override { return true; }
String toString (const ValueUnion&) const override { return "undefined"; }
bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); }
void writeToStream (const ValueUnion&, OutputStream& output) const override
{
output.writeCompressedInt (1);
output.writeByte (varMarker_Undefined);
}
};
//==============================================================================
class var::VariantType_Int : public var::VariantType
{
public:
VariantType_Int() noexcept {}
static const VariantType_Int instance;
int toInt (const ValueUnion& data) const noexcept override { return data.intValue; };
int64 toInt64 (const ValueUnion& data) const noexcept override { return (int64) data.intValue; };
double toDouble (const ValueUnion& data) const noexcept override { return (double) data.intValue; }
String toString (const ValueUnion& data) const override { return String (data.intValue); }
bool toBool (const ValueUnion& data) const noexcept override { return data.intValue != 0; }
bool isInt() const noexcept override { return true; }
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
{
if (otherType.isDouble() || otherType.isInt64() || otherType.isString())
return otherType.equals (otherData, data, *this);
return otherType.toInt (otherData) == data.intValue;
}
void writeToStream (const ValueUnion& data, OutputStream& output) const override
{
output.writeCompressedInt (5);
output.writeByte (varMarker_Int);
output.writeInt (data.intValue);
}
};
//==============================================================================
class var::VariantType_Int64 : public var::VariantType
{
public:
VariantType_Int64() noexcept {}
static const VariantType_Int64 instance;
int toInt (const ValueUnion& data) const noexcept override { return (int) data.int64Value; };
int64 toInt64 (const ValueUnion& data) const noexcept override { return data.int64Value; };
double toDouble (const ValueUnion& data) const noexcept override { return (double) data.int64Value; }
String toString (const ValueUnion& data) const override { return String (data.int64Value); }
bool toBool (const ValueUnion& data) const noexcept override { return data.int64Value != 0; }
bool isInt64() const noexcept override { return true; }
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
{
if (otherType.isDouble() || otherType.isString())
return otherType.equals (otherData, data, *this);
return otherType.toInt64 (otherData) == data.int64Value;
}
void writeToStream (const ValueUnion& data, OutputStream& output) const override
{
output.writeCompressedInt (9);
output.writeByte (varMarker_Int64);
output.writeInt64 (data.int64Value);
}
};
//==============================================================================
class var::VariantType_Double : public var::VariantType
{
public:
VariantType_Double() noexcept {}
static const VariantType_Double instance;
int toInt (const ValueUnion& data) const noexcept override { return (int) data.doubleValue; };
int64 toInt64 (const ValueUnion& data) const noexcept override { return (int64) data.doubleValue; };
double toDouble (const ValueUnion& data) const noexcept override { return data.doubleValue; }
String toString (const ValueUnion& data) const override { return String (data.doubleValue, 20); }
bool toBool (const ValueUnion& data) const noexcept override { return data.doubleValue != 0; }
bool isDouble() const noexcept override { return true; }
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
{
return std::abs (otherType.toDouble (otherData) - data.doubleValue) < std::numeric_limits<double>::epsilon();
}
void writeToStream (const ValueUnion& data, OutputStream& output) const override
{
output.writeCompressedInt (9);
output.writeByte (varMarker_Double);
output.writeDouble (data.doubleValue);
}
};
//==============================================================================
class var::VariantType_Bool : public var::VariantType
{
public:
VariantType_Bool() noexcept {}
static const VariantType_Bool instance;
int toInt (const ValueUnion& data) const noexcept override { return data.boolValue ? 1 : 0; };
int64 toInt64 (const ValueUnion& data) const noexcept override { return data.boolValue ? 1 : 0; };
double toDouble (const ValueUnion& data) const noexcept override { return data.boolValue ? 1.0 : 0.0; }
String toString (const ValueUnion& data) const override { return String::charToString (data.boolValue ? (juce_wchar) '1' : (juce_wchar) '0'); }
bool toBool (const ValueUnion& data) const noexcept override { return data.boolValue; }
bool isBool() const noexcept override { return true; }
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
{
return otherType.toBool (otherData) == data.boolValue;
}
void writeToStream (const ValueUnion& data, OutputStream& output) const override
{
output.writeCompressedInt (1);
output.writeByte (data.boolValue ? (char) varMarker_BoolTrue : (char) varMarker_BoolFalse);
}
};
//==============================================================================
class var::VariantType_String : public var::VariantType
{
public:
VariantType_String() noexcept {}
static const VariantType_String instance;
void cleanUp (ValueUnion& data) const noexcept override { getString (data)-> ~String(); }
void createCopy (ValueUnion& dest, const ValueUnion& source) const override { new (dest.stringValue) String (*getString (source)); }
bool isString() const noexcept override { return true; }
int toInt (const ValueUnion& data) const noexcept override { return getString (data)->getIntValue(); };
int64 toInt64 (const ValueUnion& data) const noexcept override { return getString (data)->getLargeIntValue(); };
double toDouble (const ValueUnion& data) const noexcept override { return getString (data)->getDoubleValue(); }
String toString (const ValueUnion& data) const override { return *getString (data); }
bool toBool (const ValueUnion& data) const noexcept override { return getString (data)->getIntValue() != 0
|| getString (data)->trim().equalsIgnoreCase ("true")
|| getString (data)->trim().equalsIgnoreCase ("yes"); }
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
{
return otherType.toString (otherData) == *getString (data);
}
void writeToStream (const ValueUnion& data, OutputStream& output) const override
{
const String* const s = getString (data);
const size_t len = s->getNumBytesAsUTF8() + 1;
HeapBlock<char> temp (len);
s->copyToUTF8 (temp, len);
output.writeCompressedInt ((int) (len + 1));
output.writeByte (varMarker_String);
output.write (temp, len);
}
private:
static inline const String* getString (const ValueUnion& data) noexcept { return reinterpret_cast<const String*> (data.stringValue); }
static inline String* getString (ValueUnion& data) noexcept { return reinterpret_cast<String*> (data.stringValue); }
};
//==============================================================================
class var::VariantType_Object : public var::VariantType
{
public:
VariantType_Object() noexcept {}
static const VariantType_Object instance;
void cleanUp (ValueUnion& data) const noexcept override { if (data.objectValue != nullptr) data.objectValue->decReferenceCount(); }
void createCopy (ValueUnion& dest, const ValueUnion& source) const override
{
dest.objectValue = source.objectValue;
if (dest.objectValue != nullptr)
dest.objectValue->incReferenceCount();
}
String toString (const ValueUnion& data) const override { return "Object 0x" + String::toHexString ((int) (pointer_sized_int) data.objectValue); }
bool toBool (const ValueUnion& data) const noexcept override { return data.objectValue != nullptr; }
ReferenceCountedObject* toObject (const ValueUnion& data) const noexcept override { return data.objectValue; }
bool isObject() const noexcept override { return true; }
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
{
return otherType.toObject (otherData) == data.objectValue;
}
var clone (const var& original) const override
{
if (DynamicObject* d = original.getDynamicObject())
return d->clone().get();
jassertfalse; // can only clone DynamicObjects!
return var();
}
void writeToStream (const ValueUnion&, OutputStream& output) const override
{
jassertfalse; // Can't write an object to a stream!
output.writeCompressedInt (0);
}
};
//==============================================================================
class var::VariantType_Array : public var::VariantType_Object
{
public:
VariantType_Array() noexcept {}
static const VariantType_Array instance;
String toString (const ValueUnion&) const override { return "[Array]"; }
ReferenceCountedObject* toObject (const ValueUnion&) const noexcept override { return nullptr; }
bool isArray() const noexcept override { return true; }
Array<var>* toArray (const ValueUnion& data) const noexcept override
{
if (RefCountedArray* a = dynamic_cast<RefCountedArray*> (data.objectValue))
return &(a->array);
return nullptr;
}
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
{
const Array<var>* const thisArray = toArray (data);
const Array<var>* const otherArray = otherType.toArray (otherData);
return thisArray == otherArray || (thisArray != nullptr && otherArray != nullptr && *otherArray == *thisArray);
}
var clone (const var& original) const override
{
Array<var> arrayCopy;
if (const Array<var>* array = toArray (original.value))
for (int i = 0; i < array->size(); ++i)
arrayCopy.add (array->getReference(i).clone());
return var (arrayCopy);
}
void writeToStream (const ValueUnion& data, OutputStream& output) const override
{
if (const Array<var>* array = toArray (data))
{
MemoryOutputStream buffer (512);
const int numItems = array->size();
buffer.writeCompressedInt (numItems);
for (int i = 0; i < numItems; ++i)
array->getReference(i).writeToStream (buffer);
output.writeCompressedInt (1 + (int) buffer.getDataSize());
output.writeByte (varMarker_Array);
output << buffer;
}
}
struct RefCountedArray : public ReferenceCountedObject
{
RefCountedArray (const Array<var>& a) : array (a) { incReferenceCount(); }
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
RefCountedArray (Array<var>&& a) : array (static_cast<Array<var>&&> (a)) { incReferenceCount(); }
#endif
Array<var> array;
};
};
//==============================================================================
class var::VariantType_Binary : public var::VariantType
{
public:
VariantType_Binary() noexcept {}
static const VariantType_Binary instance;
void cleanUp (ValueUnion& data) const noexcept override { delete data.binaryValue; }
void createCopy (ValueUnion& dest, const ValueUnion& source) const override { dest.binaryValue = new MemoryBlock (*source.binaryValue); }
String toString (const ValueUnion& data) const override { return data.binaryValue->toBase64Encoding(); }
bool isBinary() const noexcept override { return true; }
MemoryBlock* toBinary (const ValueUnion& data) const noexcept override { return data.binaryValue; }
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
{
const MemoryBlock* const otherBlock = otherType.toBinary (otherData);
return otherBlock != nullptr && *otherBlock == *data.binaryValue;
}
void writeToStream (const ValueUnion& data, OutputStream& output) const override
{
output.writeCompressedInt (1 + (int) data.binaryValue->getSize());
output.writeByte (varMarker_Binary);
output << *data.binaryValue;
}
};
//==============================================================================
class var::VariantType_Method : public var::VariantType
{
public:
VariantType_Method() noexcept {}
static const VariantType_Method instance;
void cleanUp (ValueUnion& data) const noexcept override { if (data.methodValue != nullptr ) delete data.methodValue; }
void createCopy (ValueUnion& dest, const ValueUnion& source) const override { dest.methodValue = new NativeFunction (*source.methodValue); }
String toString (const ValueUnion&) const override { return "Method"; }
bool toBool (const ValueUnion& data) const noexcept override { return data.methodValue != nullptr; }
bool isMethod() const noexcept override { return true; }
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
{
return otherType.isMethod() && otherData.methodValue == data.methodValue;
}
void writeToStream (const ValueUnion&, OutputStream& output) const override
{
jassertfalse; // Can't write a method to a stream!
output.writeCompressedInt (0);
}
};
//==============================================================================
const var::VariantType_Void var::VariantType_Void::instance;
const var::VariantType_Undefined var::VariantType_Undefined::instance;
const var::VariantType_Int var::VariantType_Int::instance;
const var::VariantType_Int64 var::VariantType_Int64::instance;
const var::VariantType_Bool var::VariantType_Bool::instance;
const var::VariantType_Double var::VariantType_Double::instance;
const var::VariantType_String var::VariantType_String::instance;
const var::VariantType_Object var::VariantType_Object::instance;
const var::VariantType_Array var::VariantType_Array::instance;
const var::VariantType_Binary var::VariantType_Binary::instance;
const var::VariantType_Method var::VariantType_Method::instance;
//==============================================================================
var::var() noexcept : type (&VariantType_Void::instance) {}
var::var (const VariantType& t) noexcept : type (&t) {}
var::~var() noexcept { type->cleanUp (value); }
#if JUCE_ALLOW_STATIC_NULL_VARIABLES
const var var::null;
#endif
//==============================================================================
var::var (const var& valueToCopy) : type (valueToCopy.type)
{
type->createCopy (value, valueToCopy.value);
}
var::var (const int v) noexcept : type (&VariantType_Int::instance) { value.intValue = v; }
var::var (const int64 v) noexcept : type (&VariantType_Int64::instance) { value.int64Value = v; }
var::var (const bool v) noexcept : type (&VariantType_Bool::instance) { value.boolValue = v; }
var::var (const double v) noexcept : type (&VariantType_Double::instance) { value.doubleValue = v; }
var::var (NativeFunction m) noexcept : type (&VariantType_Method::instance) { value.methodValue = new NativeFunction (m); }
var::var (const Array<var>& v) : type (&VariantType_Array::instance) { value.objectValue = new VariantType_Array::RefCountedArray(v); }
var::var (const String& v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); }
var::var (const char* const v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); }
var::var (const wchar_t* const v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); }
var::var (const void* v, size_t sz) : type (&VariantType_Binary::instance) { value.binaryValue = new MemoryBlock (v, sz); }
var::var (const MemoryBlock& v) : type (&VariantType_Binary::instance) { value.binaryValue = new MemoryBlock (v); }
var::var (const StringArray& v) : type (&VariantType_Array::instance)
{
Array<var> strings;
const int n = v.size();
for (int i = 0; i < n; ++i)
strings.add (var (v[i]));
value.objectValue = new VariantType_Array::RefCountedArray(strings);
}
var::var (ReferenceCountedObject* const object) : type (&VariantType_Object::instance)
{
value.objectValue = object;
if (object != nullptr)
object->incReferenceCount();
}
var var::undefined() noexcept { return var (VariantType_Undefined::instance); }
//==============================================================================
bool var::isVoid() const noexcept { return type->isVoid(); }
bool var::isUndefined() const noexcept { return type->isUndefined(); }
bool var::isInt() const noexcept { return type->isInt(); }
bool var::isInt64() const noexcept { return type->isInt64(); }
bool var::isBool() const noexcept { return type->isBool(); }
bool var::isDouble() const noexcept { return type->isDouble(); }
bool var::isString() const noexcept { return type->isString(); }
bool var::isObject() const noexcept { return type->isObject(); }
bool var::isArray() const noexcept { return type->isArray(); }
bool var::isBinaryData() const noexcept { return type->isBinary(); }
bool var::isMethod() const noexcept { return type->isMethod(); }
var::operator int() const noexcept { return type->toInt (value); }
var::operator int64() const noexcept { return type->toInt64 (value); }
var::operator bool() const noexcept { return type->toBool (value); }
var::operator float() const noexcept { return (float) type->toDouble (value); }
var::operator double() const noexcept { return type->toDouble (value); }
String var::toString() const { return type->toString (value); }
var::operator String() const { return type->toString (value); }
ReferenceCountedObject* var::getObject() const noexcept { return type->toObject (value); }
Array<var>* var::getArray() const noexcept { return type->toArray (value); }
MemoryBlock* var::getBinaryData() const noexcept { return type->toBinary (value); }
DynamicObject* var::getDynamicObject() const noexcept { return dynamic_cast<DynamicObject*> (getObject()); }
//==============================================================================
void var::swapWith (var& other) noexcept
{
std::swap (type, other.type);
std::swap (value, other.value);
}
var& var::operator= (const var& v) { type->cleanUp (value); type = v.type; type->createCopy (value, v.value); return *this; }
var& var::operator= (const int v) { type->cleanUp (value); type = &VariantType_Int::instance; value.intValue = v; return *this; }
var& var::operator= (const int64 v) { type->cleanUp (value); type = &VariantType_Int64::instance; value.int64Value = v; return *this; }
var& var::operator= (const bool v) { type->cleanUp (value); type = &VariantType_Bool::instance; value.boolValue = v; return *this; }
var& var::operator= (const double v) { type->cleanUp (value); type = &VariantType_Double::instance; value.doubleValue = v; return *this; }
var& var::operator= (const char* const v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; }
var& var::operator= (const wchar_t* const v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; }
var& var::operator= (const String& v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; }
var& var::operator= (const MemoryBlock& v) { type->cleanUp (value); type = &VariantType_Binary::instance; value.binaryValue = new MemoryBlock (v); return *this; }
var& var::operator= (const Array<var>& v) { var v2 (v); swapWith (v2); return *this; }
var& var::operator= (ReferenceCountedObject* v) { var v2 (v); swapWith (v2); return *this; }
var& var::operator= (NativeFunction v) { var v2 (v); swapWith (v2); return *this; }
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
var::var (var&& other) noexcept
: type (other.type),
value (other.value)
{
other.type = &VariantType_Void::instance;
}
var& var::operator= (var&& other) noexcept
{
swapWith (other);
return *this;
}
var::var (String&& v) : type (&VariantType_String::instance)
{
new (value.stringValue) String (static_cast<String&&> (v));
}
var::var (MemoryBlock&& v) : type (&VariantType_Binary::instance)
{
value.binaryValue = new MemoryBlock (static_cast<MemoryBlock&&> (v));
}
var::var (Array<var>&& v) : type (&VariantType_Array::instance)
{
value.objectValue = new VariantType_Array::RefCountedArray (static_cast<Array<var>&&> (v));
}
var& var::operator= (String&& v)
{
type->cleanUp (value);
type = &VariantType_String::instance;
new (value.stringValue) String (static_cast<String&&> (v));
return *this;
}
#endif
//==============================================================================
bool var::equals (const var& other) const noexcept
{
return type->equals (value, other.value, *other.type);
}
bool var::equalsWithSameType (const var& other) const noexcept
{
return type == other.type && equals (other);
}
bool var::hasSameTypeAs (const var& other) const noexcept
{
return type == other.type;
}
bool operator== (const var& v1, const var& v2) noexcept { return v1.equals (v2); }
bool operator!= (const var& v1, const var& v2) noexcept { return ! v1.equals (v2); }
bool operator== (const var& v1, const String& v2) { return v1.toString() == v2; }
bool operator!= (const var& v1, const String& v2) { return v1.toString() != v2; }
bool operator== (const var& v1, const char* const v2) { return v1.toString() == v2; }
bool operator!= (const var& v1, const char* const v2) { return v1.toString() != v2; }
//==============================================================================
var var::clone() const noexcept
{
return type->clone (*this);
}
//==============================================================================
const var& var::operator[] (const Identifier& propertyName) const
{
if (DynamicObject* const o = getDynamicObject())
return o->getProperty (propertyName);
return getNullVarRef();
}
const var& var::operator[] (const char* const propertyName) const
{
return operator[] (Identifier (propertyName));
}
var var::getProperty (const Identifier& propertyName, const var& defaultReturnValue) const
{
if (DynamicObject* const o = getDynamicObject())
return o->getProperties().getWithDefault (propertyName, defaultReturnValue);
return defaultReturnValue;
}
var::NativeFunction var::getNativeFunction() const
{
return isMethod() && (value.methodValue != nullptr) ? *value.methodValue : nullptr;
}
var var::invoke (const Identifier& method, const var* arguments, int numArguments) const
{
if (DynamicObject* const o = getDynamicObject())
return o->invokeMethod (method, var::NativeFunctionArgs (*this, arguments, numArguments));
return var();
}
var var::call (const Identifier& method) const
{
return invoke (method, nullptr, 0);
}
var var::call (const Identifier& method, const var& arg1) const
{
return invoke (method, &arg1, 1);
}
var var::call (const Identifier& method, const var& arg1, const var& arg2) const
{
var args[] = { arg1, arg2 };
return invoke (method, args, 2);
}
var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3)
{
var args[] = { arg1, arg2, arg3 };
return invoke (method, args, 3);
}
var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const
{
var args[] = { arg1, arg2, arg3, arg4 };
return invoke (method, args, 4);
}
var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const
{
var args[] = { arg1, arg2, arg3, arg4, arg5 };
return invoke (method, args, 5);
}
//==============================================================================
int var::size() const
{
if (const Array<var>* const array = getArray())
return array->size();
return 0;
}
const var& var::operator[] (int arrayIndex) const
{
const Array<var>* const array = getArray();
// When using this method, the var must actually be an array, and the index
// must be in-range!
jassert (array != nullptr && isPositiveAndBelow (arrayIndex, array->size()));
return array->getReference (arrayIndex);
}
var& var::operator[] (int arrayIndex)
{
const Array<var>* const array = getArray();
// When using this method, the var must actually be an array, and the index
// must be in-range!
jassert (array != nullptr && isPositiveAndBelow (arrayIndex, array->size()));
return array->getReference (arrayIndex);
}
Array<var>* var::convertToArray()
{
if (Array<var>* array = getArray())
return array;
Array<var> tempVar;
if (! isVoid())
tempVar.add (*this);
*this = tempVar;
return getArray();
}
void var::append (const var& n)
{
convertToArray()->add (n);
}
void var::remove (const int index)
{
if (Array<var>* const array = getArray())
array->remove (index);
}
void var::insert (const int index, const var& n)
{
convertToArray()->insert (index, n);
}
void var::resize (const int numArrayElementsWanted)
{
convertToArray()->resize (numArrayElementsWanted);
}
int var::indexOf (const var& n) const
{
if (const Array<var>* const array = getArray())
return array->indexOf (n);
return -1;
}
//==============================================================================
void var::writeToStream (OutputStream& output) const
{
type->writeToStream (value, output);
}
var var::readFromStream (InputStream& input)
{
const int numBytes = input.readCompressedInt();
if (numBytes > 0)
{
switch (input.readByte())
{
case varMarker_Int: return var (input.readInt());
case varMarker_Int64: return var (input.readInt64());
case varMarker_BoolTrue: return var (true);
case varMarker_BoolFalse: return var (false);
case varMarker_Double: return var (input.readDouble());
case varMarker_String:
{
MemoryOutputStream mo;
mo.writeFromInputStream (input, numBytes - 1);
return var (mo.toUTF8());
}
case varMarker_Binary:
{
MemoryBlock mb ((size_t) numBytes - 1);
if (numBytes > 1)
{
const int numRead = input.read (mb.getData(), numBytes - 1);
mb.setSize ((size_t) numRead);
}
return var (mb);
}
case varMarker_Array:
{
var v;
Array<var>* const destArray = v.convertToArray();
for (int i = input.readCompressedInt(); --i >= 0;)
destArray->add (readFromStream (input));
return v;
}
default:
input.skipNextBytes (numBytes - 1); break;
}
}
return var();
}
var::NativeFunctionArgs::NativeFunctionArgs (const var& t, const var* args, int numArgs) noexcept
: thisObject (t), arguments (args), numArguments (numArgs)
{}

+ 353
- 0
source/modules/juce_audio_graph/containers/juce_Variant.h View File

@@ -0,0 +1,353 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_VARIANT_H_INCLUDED
#define JUCE_VARIANT_H_INCLUDED
//==============================================================================
/**
A variant class, that can be used to hold a range of primitive values.
A var object can hold a range of simple primitive values, strings, or
any kind of ReferenceCountedObject. The var class is intended to act like
the kind of values used in dynamic scripting languages.
You can save/load var objects either in a small, proprietary binary format
using writeToStream()/readFromStream(), or as JSON by using the JSON class.
@see JSON, DynamicObject
*/
class JUCE_API var
{
public:
//==============================================================================
/** This structure is passed to a NativeFunction callback, and contains invocation
details about the function's arguments and context.
*/
struct NativeFunctionArgs
{
NativeFunctionArgs (const var& thisObject, const var* args, int numArgs) noexcept;
const var& thisObject;
const var* arguments;
int numArguments;
JUCE_DECLARE_NON_COPYABLE (NativeFunctionArgs)
};
#if JUCE_COMPILER_SUPPORTS_LAMBDAS
using NativeFunction = std::function<var (const NativeFunctionArgs&)>;
#else
typedef var (*NativeFunction) (const NativeFunctionArgs&);
#endif
//==============================================================================
/** Creates a void variant. */
var() noexcept;
/** Destructor. */
~var() noexcept;
#if JUCE_ALLOW_STATIC_NULL_VARIABLES
/** A static var object that can be used where you need an empty variant object. */
static const var null;
#endif
var (const var& valueToCopy);
var (int value) noexcept;
var (int64 value) noexcept;
var (bool value) noexcept;
var (double value) noexcept;
var (const char* value);
var (const wchar_t* value);
var (const String& value);
var (const Array<var>& value);
var (const StringArray& value);
var (ReferenceCountedObject* object);
var (NativeFunction method) noexcept;
var (const void* binaryData, size_t dataSize);
var (const MemoryBlock& binaryData);
var& operator= (const var& valueToCopy);
var& operator= (int value);
var& operator= (int64 value);
var& operator= (bool value);
var& operator= (double value);
var& operator= (const char* value);
var& operator= (const wchar_t* value);
var& operator= (const String& value);
var& operator= (const MemoryBlock& value);
var& operator= (const Array<var>& value);
var& operator= (ReferenceCountedObject* object);
var& operator= (NativeFunction method);
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
var (var&&) noexcept;
var (String&&);
var (MemoryBlock&&);
var (Array<var>&&);
var& operator= (var&&) noexcept;
var& operator= (String&&);
#endif
void swapWith (var& other) noexcept;
/** Returns a var object that can be used where you need the javascript "undefined" value. */
static var undefined() noexcept;
//==============================================================================
operator int() const noexcept;
operator int64() const noexcept;
operator bool() const noexcept;
operator float() const noexcept;
operator double() const noexcept;
operator String() const;
String toString() const;
/** If this variant holds an array, this provides access to it.
NOTE: Beware when you use this - the array pointer is only valid for the lifetime
of the variant that returned it, so be very careful not to call this method on temporary
var objects that are the return-value of a function, and which may go out of scope before
you use the array!
*/
Array<var>* getArray() const noexcept;
/** If this variant holds a memory block, this provides access to it.
NOTE: Beware when you use this - the MemoryBlock pointer is only valid for the lifetime
of the variant that returned it, so be very careful not to call this method on temporary
var objects that are the return-value of a function, and which may go out of scope before
you use the MemoryBlock!
*/
MemoryBlock* getBinaryData() const noexcept;
ReferenceCountedObject* getObject() const noexcept;
DynamicObject* getDynamicObject() const noexcept;
//==============================================================================
bool isVoid() const noexcept;
bool isUndefined() const noexcept;
bool isInt() const noexcept;
bool isInt64() const noexcept;
bool isBool() const noexcept;
bool isDouble() const noexcept;
bool isString() const noexcept;
bool isObject() const noexcept;
bool isArray() const noexcept;
bool isBinaryData() const noexcept;
bool isMethod() const noexcept;
/** Returns true if this var has the same value as the one supplied.
Note that this ignores the type, so a string var "123" and an integer var with the
value 123 are considered to be equal.
@see equalsWithSameType
*/
bool equals (const var& other) const noexcept;
/** Returns true if this var has the same value and type as the one supplied.
This differs from equals() because e.g. "123" and 123 will be considered different.
@see equals
*/
bool equalsWithSameType (const var& other) const noexcept;
/** Returns true if this var has the same type as the one supplied. */
bool hasSameTypeAs (const var& other) const noexcept;
/** Returns a deep copy of this object.
For simple types this just returns a copy, but if the object contains any arrays
or DynamicObjects, they will be cloned (recursively).
*/
var clone() const noexcept;
//==============================================================================
/** If the var is an array, this returns the number of elements.
If the var isn't actually an array, this will return 0.
*/
int size() const;
/** If the var is an array, this can be used to return one of its elements.
To call this method, you must make sure that the var is actually an array, and
that the index is a valid number. If these conditions aren't met, behaviour is
undefined.
For more control over the array's contents, you can call getArray() and manipulate
it directly as an Array\<var\>.
*/
const var& operator[] (int arrayIndex) const;
/** If the var is an array, this can be used to return one of its elements.
To call this method, you must make sure that the var is actually an array, and
that the index is a valid number. If these conditions aren't met, behaviour is
undefined.
For more control over the array's contents, you can call getArray() and manipulate
it directly as an Array\<var\>.
*/
var& operator[] (int arrayIndex);
/** Appends an element to the var, converting it to an array if it isn't already one.
If the var isn't an array, it will be converted to one, and if its value was non-void,
this value will be kept as the first element of the new array. The parameter value
will then be appended to it.
For more control over the array's contents, you can call getArray() and manipulate
it directly as an Array\<var\>.
*/
void append (const var& valueToAppend);
/** Inserts an element to the var, converting it to an array if it isn't already one.
If the var isn't an array, it will be converted to one, and if its value was non-void,
this value will be kept as the first element of the new array. The parameter value
will then be inserted into it.
For more control over the array's contents, you can call getArray() and manipulate
it directly as an Array\<var\>.
*/
void insert (int index, const var& value);
/** If the var is an array, this removes one of its elements.
If the index is out-of-range or the var isn't an array, nothing will be done.
For more control over the array's contents, you can call getArray() and manipulate
it directly as an Array\<var\>.
*/
void remove (int index);
/** Treating the var as an array, this resizes it to contain the specified number of elements.
If the var isn't an array, it will be converted to one, and if its value was non-void,
this value will be kept as the first element of the new array before resizing.
For more control over the array's contents, you can call getArray() and manipulate
it directly as an Array\<var\>.
*/
void resize (int numArrayElementsWanted);
/** If the var is an array, this searches it for the first occurrence of the specified value,
and returns its index.
If the var isn't an array, or if the value isn't found, this returns -1.
*/
int indexOf (const var& value) const;
//==============================================================================
/** If this variant is an object, this returns one of its properties. */
const var& operator[] (const Identifier& propertyName) const;
/** If this variant is an object, this returns one of its properties. */
const var& operator[] (const char* propertyName) const;
/** If this variant is an object, this returns one of its properties, or a default
fallback value if the property is not set. */
var getProperty (const Identifier& propertyName, const var& defaultReturnValue) const;
/** Invokes a named method call with no arguments. */
var call (const Identifier& method) const;
/** Invokes a named method call with one argument. */
var call (const Identifier& method, const var& arg1) const;
/** Invokes a named method call with 2 arguments. */
var call (const Identifier& method, const var& arg1, const var& arg2) const;
/** Invokes a named method call with 3 arguments. */
var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3);
/** Invokes a named method call with 4 arguments. */
var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const;
/** Invokes a named method call with 5 arguments. */
var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const;
/** Invokes a named method call with a list of arguments. */
var invoke (const Identifier& method, const var* arguments, int numArguments) const;
/** If this object is a method, this returns the function pointer. */
NativeFunction getNativeFunction() const;
//==============================================================================
/** Writes a binary representation of this value to a stream.
The data can be read back later using readFromStream().
@see JSON
*/
void writeToStream (OutputStream& output) const;
/** Reads back a stored binary representation of a value.
The data in the stream must have been written using writeToStream(), or this
will have unpredictable results.
@see JSON
*/
static var readFromStream (InputStream& input);
private:
//==============================================================================
class VariantType; friend class VariantType;
class VariantType_Void; friend class VariantType_Void;
class VariantType_Undefined; friend class VariantType_Undefined;
class VariantType_Int; friend class VariantType_Int;
class VariantType_Int64; friend class VariantType_Int64;
class VariantType_Double; friend class VariantType_Double;
class VariantType_Bool; friend class VariantType_Bool;
class VariantType_String; friend class VariantType_String;
class VariantType_Object; friend class VariantType_Object;
class VariantType_Array; friend class VariantType_Array;
class VariantType_Binary; friend class VariantType_Binary;
class VariantType_Method; friend class VariantType_Method;
union ValueUnion
{
int intValue;
int64 int64Value;
bool boolValue;
double doubleValue;
char stringValue [sizeof (String)];
ReferenceCountedObject* objectValue;
MemoryBlock* binaryValue;
NativeFunction* methodValue;
};
const VariantType* type;
ValueUnion value;
Array<var>* convertToArray();
var (const VariantType&) noexcept;
};
/** Compares the values of two var objects, using the var::equals() comparison. */
JUCE_API bool operator== (const var&, const var&) noexcept;
/** Compares the values of two var objects, using the var::equals() comparison. */
JUCE_API bool operator!= (const var&, const var&) noexcept;
JUCE_API bool operator== (const var&, const String&);
JUCE_API bool operator!= (const var&, const String&);
JUCE_API bool operator== (const var&, const char*);
JUCE_API bool operator!= (const var&, const char*);
//==============================================================================
/** This template-overloaded class can be used to convert between var and custom types. */
template <typename Type>
struct VariantConverter
{
static Type fromVar (const var& v) { return static_cast<Type> (v); }
static var toVar (const Type& t) { return t; }
};
/** This template-overloaded class can be used to convert between var and custom types. */
template <>
struct VariantConverter<String>
{
static String fromVar (const var& v) { return v.toString(); }
static var toVar (const String& s) { return s; }
};
#endif // JUCE_VARIANT_H_INCLUDED

+ 75
- 0
source/modules/juce_audio_graph/juce_audio_graph.cpp View File

@@ -0,0 +1,75 @@
/*
* Standalone Juce AudioProcessorGraph
* Copyright (C) 2015 ROLI Ltd.
* Copyright (C) 2017 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/
#include "juce_audio_graph.h"
// #include "AppConfig.h"
// #define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1
// #include "juce_audio_processors.h"
// #include <juce_gui_extra/juce_gui_extra.h>
#include <locale>
#include <cctype>
#include <cstdarg>
#include <iostream>
// #include <wctype.h>
//==============================================================================
namespace juce
{
#include "memory/juce_MemoryBlock.cpp"
#include "text/juce_CharacterFunctions.cpp"
#include "text/juce_String.cpp"
// #include "streams/juce_InputStream.cpp"
// #include "streams/juce_OutputStream.cpp"
// static inline bool arrayContainsPlugin (const OwnedArray<PluginDescription>& list,
// const PluginDescription& desc)
// {
// for (int i = list.size(); --i >= 0;)
// if (list.getUnchecked(i)->isDuplicateOf (desc))
// return true;
//
// return false;
// }
#include "midi/juce_MidiBuffer.cpp"
#include "midi/juce_MidiMessage.cpp"
// #include "format/juce_AudioPluginFormat.cpp"
// #include "format/juce_AudioPluginFormatManager.cpp"
#include "processors/juce_AudioProcessor.cpp"
#include "processors/juce_AudioProcessorGraph.cpp"
// #include "processors/juce_GenericAudioProcessorEditor.cpp"
// #include "processors/juce_PluginDescription.cpp"
// #include "format_types/juce_LADSPAPluginFormat.cpp"
// #include "format_types/juce_VSTPluginFormat.cpp"
// #include "format_types/juce_VST3PluginFormat.cpp"
// #include "format_types/juce_AudioUnitPluginFormat.mm"
// #include "scanning/juce_KnownPluginList.cpp"
// #include "scanning/juce_PluginDirectoryScanner.cpp"
// #include "scanning/juce_PluginListComponent.cpp"
// #include "utilities/juce_AudioProcessorValueTreeState.cpp"
// #include "utilities/juce_AudioProcessorParameters.cpp"
}

+ 167
- 0
source/modules/juce_audio_graph/juce_audio_graph.h View File

@@ -0,0 +1,167 @@
/*
* Standalone Juce AudioProcessorGraph
* Copyright (C) 2015 ROLI Ltd.
* Copyright (C) 2017 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/
#ifndef JUCE_AUDIO_GRAPH_H_INCLUDED
#define JUCE_AUDIO_GRAPH_H_INCLUDED
#include "CarlaJuceUtils.hpp"
#include "CarlaMutex.hpp"
#include "distrho/extra/ScopedPointer.hpp"
#include <algorithm>
//==============================================================================
#define JUCE_API
#define JUCE_CALLTYPE
#define jassertfalse carla_safe_assert("jassertfalse triggered", __FILE__, __LINE__);
#define jassert(expression) CARLA_SAFE_ASSERT(expression)
#define static_jassert(expression) static_assert(expression, #expression);
#if defined (__LP64__) || defined (_LP64) || defined (__arm64__) || defined(__MINGW64__)
#define JUCE_64BIT 1
#else
#define JUCE_32BIT 1
#endif
#ifdef __LITTLE_ENDIAN__
#define JUCE_LITTLE_ENDIAN 1
#else
#define JUCE_BIG_ENDIAN 1
#endif
#define JUCE_ALIGN(bytes) __attribute__ ((aligned (bytes)))
#if (__cplusplus >= 201103L || defined (__GXX_EXPERIMENTAL_CXX0X__)) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
#define JUCE_COMPILER_SUPPORTS_NOEXCEPT 1
#define JUCE_COMPILER_SUPPORTS_NULLPTR 1
#define JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS 1
#define JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS 1
#define JUCE_COMPILER_SUPPORTS_VARIADIC_TEMPLATES 1
#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && ! defined (JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL)
#define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1
#endif
#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && ! defined (JUCE_DELETED_FUNCTION)
#define JUCE_DELETED_FUNCTION = delete
#endif
#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && ! defined (JUCE_COMPILER_SUPPORTS_LAMBDAS)
#define JUCE_COMPILER_SUPPORTS_LAMBDAS 1
#endif
#endif
#ifndef JUCE_DELETED_FUNCTION
/** This macro can be placed after a method declaration to allow the use of
the C++11 feature "= delete" on all compilers.
On newer compilers that support it, it does the C++11 "= delete", but on
older ones it's just an empty definition.
*/
#define JUCE_DELETED_FUNCTION
#endif
#if ! JUCE_COMPILER_SUPPORTS_NOEXCEPT
#ifdef noexcept
#undef noexcept
#endif
#define noexcept throw()
#endif
#if ! JUCE_COMPILER_SUPPORTS_NULLPTR
#ifdef nullptr
#undef nullptr
#endif
#define nullptr (0)
#endif
#if ! JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL
#undef override
#define override
#endif
#define JUCE_DECLARE_NON_COPYABLE(className) CARLA_DECLARE_NON_COPY_CLASS(className)
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className) \
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
#define JUCE_LEAK_DETECTOR(className) CARLA_LEAK_DETECTOR(className)
#define JUCE_PREVENT_HEAP_ALLOCATION CARLA_PREVENT_HEAP_ALLOCATION
#define NEEDS_TRANS(x) (x)
//==============================================================================
namespace juce
{
class File;
class MidiMessage;
class MemoryBlock;
class OutputStream;
class StringRef;
#include "memory/juce_Memory.h"
#include "maths/juce_MathsFunctions.h"
#include "memory/juce_ByteOrder.h"
#include "memory/juce_Atomic.h"
#include "text/juce_CharacterFunctions.h"
#include "text/juce_CharPointer_UTF8.h"
#include "text/juce_String.h"
#include "text/juce_StringRef.h"
#include "memory/juce_HeapBlock.h"
#include "memory/juce_MemoryBlock.h"
#include "text/juce_NewLine.h"
#include "containers/juce_ElementComparator.h"
#include "containers/juce_ArrayAllocationBase.h"
#include "containers/juce_Array.h"
#include "containers/juce_OwnedArray.h"
// #include "containers/juce_Variant.h"
// #include "containers/juce_NamedValueSet.h"
#include "streams/juce_InputStream.h"
#include "streams/juce_OutputStream.h"
#include "streams/juce_MemoryOutputStream.h"
#include "buffers/juce_AudioSampleBuffer.h"
#include "midi/juce_MidiBuffer.h"
#include "midi/juce_MidiMessage.h"
class AudioProcessor;
#include "processors/juce_AudioPlayHead.h"
// #include "processors/juce_AudioProcessorListener.h"
// #include "processors/juce_AudioProcessorParameter.h"
#include "processors/juce_AudioProcessor.h"
// #include "processors/juce_PluginDescription.h"
// #include "processors/juce_AudioPluginInstance.h"
#include "processors/juce_AudioProcessorGraph.h"
// #include "format/juce_AudioPluginFormat.h"
// #include "format/juce_AudioPluginFormatManager.h"
// #include "scanning/juce_KnownPluginList.h"
// #include "utilities/juce_AudioProcessorValueTreeState.h"
// #include "utilities/juce_AudioProcessorParameterWithID.h"
// #include "utilities/juce_AudioParameterFloat.h"
// #include "utilities/juce_AudioParameterInt.h"
// #include "utilities/juce_AudioParameterBool.h"
// #include "utilities/juce_AudioParameterChoice.h"
}
#endif // JUCE_AUDIO_PROCESSORS_H_INCLUDED

+ 19
- 0
source/modules/juce_audio_graph/juce_audio_graph.mm View File

@@ -0,0 +1,19 @@
/*
* Standalone Juce AudioProcessorGraph
* Copyright (C) 2015 ROLI Ltd.
* Copyright (C) 2017 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/
#include "juce_audio_graph.cpp"

+ 554
- 0
source/modules/juce_audio_graph/maths/juce_MathsFunctions.h View File

@@ -0,0 +1,554 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_MATHSFUNCTIONS_H_INCLUDED
#define JUCE_MATHSFUNCTIONS_H_INCLUDED
//==============================================================================
/*
This file sets up some handy mathematical typdefs and functions.
*/
//==============================================================================
// Definitions for the int8, int16, int32, int64 and pointer_sized_int types.
/** A platform-independent 8-bit signed integer type. */
typedef signed char int8;
/** A platform-independent 8-bit unsigned integer type. */
typedef unsigned char uint8;
/** A platform-independent 16-bit signed integer type. */
typedef signed short int16;
/** A platform-independent 16-bit unsigned integer type. */
typedef unsigned short uint16;
/** A platform-independent 32-bit signed integer type. */
typedef signed int int32;
/** A platform-independent 32-bit unsigned integer type. */
typedef unsigned int uint32;
/** A platform-independent 64-bit integer type. */
typedef long long int64;
/** A platform-independent 64-bit unsigned integer type. */
typedef unsigned long long uint64;
#if JUCE_64BIT
/** A signed integer type that's guaranteed to be large enough to hold a pointer without truncating it. */
typedef int64 pointer_sized_int;
/** An unsigned integer type that's guaranteed to be large enough to hold a pointer without truncating it. */
typedef uint64 pointer_sized_uint;
#else
/** A signed integer type that's guaranteed to be large enough to hold a pointer without truncating it. */
typedef int pointer_sized_int;
/** An unsigned integer type that's guaranteed to be large enough to hold a pointer without truncating it. */
typedef unsigned int pointer_sized_uint;
#endif
//==============================================================================
// Some indispensable min/max functions
/** Returns the larger of two values. */
template <typename Type>
Type jmax (const Type a, const Type b) { return (a < b) ? b : a; }
/** Returns the larger of three values. */
template <typename Type>
Type jmax (const Type a, const Type b, const Type c) { return (a < b) ? ((b < c) ? c : b) : ((a < c) ? c : a); }
/** Returns the larger of four values. */
template <typename Type>
Type jmax (const Type a, const Type b, const Type c, const Type d) { return jmax (a, jmax (b, c, d)); }
/** Returns the smaller of two values. */
template <typename Type>
Type jmin (const Type a, const Type b) { return (b < a) ? b : a; }
/** Returns the smaller of three values. */
template <typename Type>
Type jmin (const Type a, const Type b, const Type c) { return (b < a) ? ((c < b) ? c : b) : ((c < a) ? c : a); }
/** Returns the smaller of four values. */
template <typename Type>
Type jmin (const Type a, const Type b, const Type c, const Type d) { return jmin (a, jmin (b, c, d)); }
/** Remaps a normalised value (between 0 and 1) to a target range.
This effectively returns (targetRangeMin + value0To1 * (targetRangeMax - targetRangeMin)).
*/
template <typename Type>
Type jmap (Type value0To1, Type targetRangeMin, Type targetRangeMax)
{
return targetRangeMin + value0To1 * (targetRangeMax - targetRangeMin);
}
/** Remaps a value from a source range to a target range. */
template <typename Type>
Type jmap (Type sourceValue, Type sourceRangeMin, Type sourceRangeMax, Type targetRangeMin, Type targetRangeMax)
{
jassert (sourceRangeMax != sourceRangeMin); // mapping from a range of zero will produce NaN!
return targetRangeMin + ((targetRangeMax - targetRangeMin) * (sourceValue - sourceRangeMin)) / (sourceRangeMax - sourceRangeMin);
}
/** Scans an array of values, returning the minimum value that it contains. */
template <typename Type>
Type findMinimum (const Type* data, int numValues)
{
if (numValues <= 0)
return Type();
Type result (*data++);
while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample)
{
const Type& v = *data++;
if (v < result) result = v;
}
return result;
}
/** Scans an array of values, returning the maximum value that it contains. */
template <typename Type>
Type findMaximum (const Type* values, int numValues)
{
if (numValues <= 0)
return Type();
Type result (*values++);
while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample)
{
const Type& v = *values++;
if (result < v) result = v;
}
return result;
}
/** Scans an array of values, returning the minimum and maximum values that it contains. */
template <typename Type>
void findMinAndMax (const Type* values, int numValues, Type& lowest, Type& highest)
{
if (numValues <= 0)
{
lowest = Type();
highest = Type();
}
else
{
Type mn (*values++);
Type mx (mn);
while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample)
{
const Type& v = *values++;
if (mx < v) mx = v;
if (v < mn) mn = v;
}
lowest = mn;
highest = mx;
}
}
//==============================================================================
/** Constrains a value to keep it within a given range.
This will check that the specified value lies between the lower and upper bounds
specified, and if not, will return the nearest value that would be in-range. Effectively,
it's like calling jmax (lowerLimit, jmin (upperLimit, value)).
Note that it expects that lowerLimit <= upperLimit. If this isn't true,
the results will be unpredictable.
@param lowerLimit the minimum value to return
@param upperLimit the maximum value to return
@param valueToConstrain the value to try to return
@returns the closest value to valueToConstrain which lies between lowerLimit
and upperLimit (inclusive)
@see jmin, jmax, jmap
*/
template <typename Type>
Type jlimit (const Type lowerLimit,
const Type upperLimit,
const Type valueToConstrain) noexcept
{
jassert (lowerLimit <= upperLimit); // if these are in the wrong order, results are unpredictable..
return (valueToConstrain < lowerLimit) ? lowerLimit
: ((upperLimit < valueToConstrain) ? upperLimit
: valueToConstrain);
}
/** Returns true if a value is at least zero, and also below a specified upper limit.
This is basically a quicker way to write:
@code valueToTest >= 0 && valueToTest < upperLimit
@endcode
*/
template <typename Type>
bool isPositiveAndBelow (Type valueToTest, Type upperLimit) noexcept
{
jassert (Type() <= upperLimit); // makes no sense to call this if the upper limit is itself below zero..
return Type() <= valueToTest && valueToTest < upperLimit;
}
template <>
inline bool isPositiveAndBelow (const int valueToTest, const int upperLimit) noexcept
{
jassert (upperLimit >= 0); // makes no sense to call this if the upper limit is itself below zero..
return static_cast<unsigned int> (valueToTest) < static_cast<unsigned int> (upperLimit);
}
/** Returns true if a value is at least zero, and also less than or equal to a specified upper limit.
This is basically a quicker way to write:
@code valueToTest >= 0 && valueToTest <= upperLimit
@endcode
*/
template <typename Type>
bool isPositiveAndNotGreaterThan (Type valueToTest, Type upperLimit) noexcept
{
jassert (Type() <= upperLimit); // makes no sense to call this if the upper limit is itself below zero..
return Type() <= valueToTest && valueToTest <= upperLimit;
}
template <>
inline bool isPositiveAndNotGreaterThan (const int valueToTest, const int upperLimit) noexcept
{
jassert (upperLimit >= 0); // makes no sense to call this if the upper limit is itself below zero..
return static_cast<unsigned int> (valueToTest) <= static_cast<unsigned int> (upperLimit);
}
//==============================================================================
/** Handy function to swap two values. */
template <typename Type>
void swapVariables (Type& variable1, Type& variable2)
{
std::swap (variable1, variable2);
}
/** Handy function for avoiding unused variables warning. */
template <typename Type1>
void ignoreUnused (const Type1&) noexcept {}
template <typename Type1, typename Type2>
void ignoreUnused (const Type1&, const Type2&) noexcept {}
template <typename Type1, typename Type2, typename Type3>
void ignoreUnused (const Type1&, const Type2&, const Type3&) noexcept {}
template <typename Type1, typename Type2, typename Type3, typename Type4>
void ignoreUnused (const Type1&, const Type2&, const Type3&, const Type4&) noexcept {}
/** Handy function for getting the number of elements in a simple const C array.
E.g.
@code
static int myArray[] = { 1, 2, 3 };
int numElements = numElementsInArray (myArray) // returns 3
@endcode
*/
template <typename Type, int N>
int numElementsInArray (Type (&array)[N])
{
ignoreUnused (array);
(void) sizeof (0[array]); // This line should cause an error if you pass an object with a user-defined subscript operator
return N;
}
//==============================================================================
// Some useful maths functions that aren't always present with all compilers and build settings.
/** Using juce_hypot is easier than dealing with the different types of hypot function
that are provided by the various platforms and compilers. */
template <typename Type>
Type juce_hypot (Type a, Type b) noexcept
{
return static_cast<Type> (hypot (a, b));
}
template <>
inline float juce_hypot (float a, float b) noexcept
{
return hypotf (a, b);
}
/** 64-bit abs function. */
inline int64 abs64 (const int64 n) noexcept
{
return (n >= 0) ? n : -n;
}
//==============================================================================
/** A predefined value for Pi, at double-precision.
@see float_Pi
*/
const double double_Pi = 3.1415926535897932384626433832795;
/** A predefined value for Pi, at single-precision.
@see double_Pi
*/
const float float_Pi = 3.14159265358979323846f;
/** Converts an angle in degrees to radians. */
inline float degreesToRadians (float degrees) noexcept { return degrees * (float_Pi / 180.0f); }
/** Converts an angle in degrees to radians. */
inline double degreesToRadians (double degrees) noexcept { return degrees * (double_Pi / 180.0); }
/** Converts an angle in radians to degrees. */
inline float radiansToDegrees (float radians) noexcept { return radians * (180.0f / float_Pi); }
/** Converts an angle in radians to degrees. */
inline double radiansToDegrees (double radians) noexcept { return radians * (180.0 / double_Pi); }
//==============================================================================
/** The isfinite() method seems to vary between platforms, so this is a
platform-independent function for it.
*/
template <typename NumericType>
bool juce_isfinite (NumericType) noexcept
{
return true; // Integer types are always finite
}
template <>
inline bool juce_isfinite (float value) noexcept
{
return std::isfinite (value);
}
template <>
inline bool juce_isfinite (double value) noexcept
{
return std::isfinite (value);
}
//==============================================================================
/** Fast floating-point-to-integer conversion.
This is faster than using the normal c++ cast to convert a float to an int, and
it will round the value to the nearest integer, rather than rounding it down
like the normal cast does.
Note that this routine gets its speed at the expense of some accuracy, and when
rounding values whose floating point component is exactly 0.5, odd numbers and
even numbers will be rounded up or down differently.
*/
template <typename FloatType>
int roundToInt (const FloatType value) noexcept
{
union { int asInt[2]; double asDouble; } n;
n.asDouble = ((double) value) + 6755399441055744.0;
#if JUCE_BIG_ENDIAN
return n.asInt [1];
#else
return n.asInt [0];
#endif
}
inline int roundToInt (int value) noexcept
{
return value;
}
/** Fast floating-point-to-integer conversion.
This is a slightly slower and slightly more accurate version of roundDoubleToInt(). It works
fine for values above zero, but negative numbers are rounded the wrong way.
*/
inline int roundToIntAccurate (double value) noexcept
{
return roundToInt (value + 1.5e-8);
}
/** Fast floating-point-to-integer conversion.
This is faster than using the normal c++ cast to convert a double to an int, and
it will round the value to the nearest integer, rather than rounding it down
like the normal cast does.
Note that this routine gets its speed at the expense of some accuracy, and when
rounding values whose floating point component is exactly 0.5, odd numbers and
even numbers will be rounded up or down differently. For a more accurate conversion,
see roundDoubleToIntAccurate().
*/
inline int roundDoubleToInt (double value) noexcept
{
return roundToInt (value);
}
/** Fast floating-point-to-integer conversion.
This is faster than using the normal c++ cast to convert a float to an int, and
it will round the value to the nearest integer, rather than rounding it down
like the normal cast does.
Note that this routine gets its speed at the expense of some accuracy, and when
rounding values whose floating point component is exactly 0.5, odd numbers and
even numbers will be rounded up or down differently.
*/
inline int roundFloatToInt (float value) noexcept
{
return roundToInt (value);
}
//==============================================================================
/** Returns true if the specified integer is a power-of-two. */
template <typename IntegerType>
bool isPowerOfTwo (IntegerType value)
{
return (value & (value - 1)) == 0;
}
/** Returns the smallest power-of-two which is equal to or greater than the given integer. */
inline int nextPowerOfTwo (int n) noexcept
{
--n;
n |= (n >> 1);
n |= (n >> 2);
n |= (n >> 4);
n |= (n >> 8);
n |= (n >> 16);
return n + 1;
}
/** Returns the index of the highest set bit in a (non-zero) number.
So for n=3 this would return 1, for n=7 it returns 2, etc.
An input value of 0 is illegal!
*/
int findHighestSetBit (uint32 n) noexcept;
/** Returns the number of bits in a 32-bit integer. */
inline int countNumberOfBits (uint32 n) noexcept
{
n -= ((n >> 1) & 0x55555555);
n = (((n >> 2) & 0x33333333) + (n & 0x33333333));
n = (((n >> 4) + n) & 0x0f0f0f0f);
n += (n >> 8);
n += (n >> 16);
return (int) (n & 0x3f);
}
/** Returns the number of bits in a 64-bit integer. */
inline int countNumberOfBits (uint64 n) noexcept
{
return countNumberOfBits ((uint32) n) + countNumberOfBits ((uint32) (n >> 32));
}
/** Performs a modulo operation, but can cope with the dividend being negative.
The divisor must be greater than zero.
*/
template <typename IntegerType>
IntegerType negativeAwareModulo (IntegerType dividend, const IntegerType divisor) noexcept
{
jassert (divisor > 0);
dividend %= divisor;
return (dividend < 0) ? (dividend + divisor) : dividend;
}
/** Returns the square of its argument. */
template <typename NumericType>
NumericType square (NumericType n) noexcept
{
return n * n;
}
//==============================================================================
/** Writes a number of bits into a memory buffer at a given bit index.
The buffer is treated as a sequence of 8-bit bytes, and the value is encoded in little-endian order,
so for example if startBit = 10, and numBits = 11 then the lower 6 bits of the value would be written
into bits 2-8 of targetBuffer[1], and the upper 5 bits of value into bits 0-5 of targetBuffer[2].
@see readLittleEndianBitsInBuffer
*/
void writeLittleEndianBitsInBuffer (void* targetBuffer, uint32 startBit, uint32 numBits, uint32 value) noexcept;
/** Reads a number of bits from a buffer at a given bit index.
The buffer is treated as a sequence of 8-bit bytes, and the value is encoded in little-endian order,
so for example if startBit = 10, and numBits = 11 then the lower 6 bits of the result would be read
from bits 2-8 of sourceBuffer[1], and the upper 5 bits of the result from bits 0-5 of sourceBuffer[2].
@see writeLittleEndianBitsInBuffer
*/
uint32 readLittleEndianBitsInBuffer (const void* sourceBuffer, uint32 startBit, uint32 numBits) noexcept;
//==============================================================================
/** This namespace contains a few template classes for helping work out class type variations.
*/
namespace TypeHelpers
{
/** The ParameterType struct is used to find the best type to use when passing some kind
of object as a parameter.
Of course, this is only likely to be useful in certain esoteric template situations.
Because "typename TypeHelpers::ParameterType<SomeClass>::type" is a bit of a mouthful, there's
a PARAMETER_TYPE(SomeClass) macro that you can use to get the same effect.
E.g. "myFunction (PARAMETER_TYPE (int), PARAMETER_TYPE (MyObject))"
would evaluate to "myfunction (int, const MyObject&)", keeping any primitive types as
pass-by-value, but passing objects as a const reference, to avoid copying.
*/
template <typename Type> struct ParameterType { typedef const Type& type; };
template <typename Type> struct ParameterType <Type&> { typedef Type& type; };
template <typename Type> struct ParameterType <Type*> { typedef Type* type; };
template <> struct ParameterType <char> { typedef char type; };
template <> struct ParameterType <unsigned char> { typedef unsigned char type; };
template <> struct ParameterType <short> { typedef short type; };
template <> struct ParameterType <unsigned short> { typedef unsigned short type; };
template <> struct ParameterType <int> { typedef int type; };
template <> struct ParameterType <unsigned int> { typedef unsigned int type; };
template <> struct ParameterType <long> { typedef long type; };
template <> struct ParameterType <unsigned long> { typedef unsigned long type; };
template <> struct ParameterType <int64> { typedef int64 type; };
template <> struct ParameterType <uint64> { typedef uint64 type; };
template <> struct ParameterType <bool> { typedef bool type; };
template <> struct ParameterType <float> { typedef float type; };
template <> struct ParameterType <double> { typedef double type; };
/** A helpful macro to simplify the use of the ParameterType template.
@see ParameterType
*/
#define PARAMETER_TYPE(a) typename TypeHelpers::ParameterType<a>::type
/** These templates are designed to take a type, and if it's a double, they return a double
type; for anything else, they return a float type.
*/
template <typename Type> struct SmallestFloatType { typedef float type; };
template <> struct SmallestFloatType <double> { typedef double type; };
}
//==============================================================================
#endif // JUCE_MATHSFUNCTIONS_H_INCLUDED

+ 249
- 0
source/modules/juce_audio_graph/memory/juce_Atomic.h View File

@@ -0,0 +1,249 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_ATOMIC_H_INCLUDED
#define JUCE_ATOMIC_H_INCLUDED
//==============================================================================
/**
Simple class to hold a primitive value and perform atomic operations on it.
The type used must be a 32 or 64 bit primitive, like an int, pointer, etc.
There are methods to perform most of the basic atomic operations.
*/
template <typename Type>
class Atomic
{
public:
/** Creates a new value, initialised to zero. */
inline Atomic() noexcept
: value (0)
{
}
/** Creates a new value, with a given initial value. */
inline explicit Atomic (const Type initialValue) noexcept
: value (initialValue)
{
}
/** Copies another value (atomically). */
inline Atomic (const Atomic& other) noexcept
: value (other.get())
{
}
/** Destructor. */
inline ~Atomic() noexcept
{
// This class can only be used for types which are 32 or 64 bits in size.
static_jassert (sizeof (Type) == 4 || sizeof (Type) == 8);
}
/** Atomically reads and returns the current value. */
Type get() const noexcept;
/** Copies another value onto this one (atomically). */
inline Atomic& operator= (const Atomic& other) noexcept { exchange (other.get()); return *this; }
/** Copies another value onto this one (atomically). */
inline Atomic& operator= (const Type newValue) noexcept { exchange (newValue); return *this; }
/** Atomically sets the current value. */
void set (Type newValue) noexcept { exchange (newValue); }
/** Atomically sets the current value, returning the value that was replaced. */
Type exchange (Type value) noexcept;
/** Atomically adds a number to this value, returning the new value. */
Type operator+= (Type amountToAdd) noexcept;
/** Atomically subtracts a number from this value, returning the new value. */
Type operator-= (Type amountToSubtract) noexcept;
/** Atomically increments this value, returning the new value. */
Type operator++() noexcept;
/** Atomically decrements this value, returning the new value. */
Type operator--() noexcept;
/** Atomically compares this value with a target value, and if it is equal, sets
this to be equal to a new value.
This operation is the atomic equivalent of doing this:
@code
bool compareAndSetBool (Type newValue, Type valueToCompare)
{
if (get() == valueToCompare)
{
set (newValue);
return true;
}
return false;
}
@endcode
@returns true if the comparison was true and the value was replaced; false if
the comparison failed and the value was left unchanged.
@see compareAndSetValue
*/
bool compareAndSetBool (Type newValue, Type valueToCompare) noexcept;
/** Atomically compares this value with a target value, and if it is equal, sets
this to be equal to a new value.
This operation is the atomic equivalent of doing this:
@code
Type compareAndSetValue (Type newValue, Type valueToCompare)
{
Type oldValue = get();
if (oldValue == valueToCompare)
set (newValue);
return oldValue;
}
@endcode
@returns the old value before it was changed.
@see compareAndSetBool
*/
Type compareAndSetValue (Type newValue, Type valueToCompare) noexcept;
/** Implements a memory read/write barrier. */
static void memoryBarrier() noexcept;
//==============================================================================
#if JUCE_64BIT
JUCE_ALIGN (8)
#else
JUCE_ALIGN (4)
#endif
/** The raw value that this class operates on.
This is exposed publicly in case you need to manipulate it directly
for performance reasons.
*/
volatile Type value;
private:
template <typename Dest, typename Source>
static inline Dest castTo (Source value) noexcept { union { Dest d; Source s; } u; u.s = value; return u.d; }
static inline Type castFrom32Bit (int32 value) noexcept { return castTo <Type, int32> (value); }
static inline Type castFrom64Bit (int64 value) noexcept { return castTo <Type, int64> (value); }
static inline int32 castTo32Bit (Type value) noexcept { return castTo <int32, Type> (value); }
static inline int64 castTo64Bit (Type value) noexcept { return castTo <int64, Type> (value); }
Type operator++ (int); // better to just use pre-increment with atomics..
Type operator-- (int);
/** This templated negate function will negate pointers as well as integers */
template <typename ValueType>
inline ValueType negateValue (ValueType n) noexcept
{
return sizeof (ValueType) == 1 ? (ValueType) -(signed char) n
: (sizeof (ValueType) == 2 ? (ValueType) -(short) n
: (sizeof (ValueType) == 4 ? (ValueType) -(int) n
: ((ValueType) -(int64) n)));
}
/** This templated negate function will negate pointers as well as integers */
template <typename PointerType>
inline PointerType* negateValue (PointerType* n) noexcept
{
return reinterpret_cast<PointerType*> (-reinterpret_cast<pointer_sized_int> (n));
}
};
//==============================================================================
template <typename Type>
inline Type Atomic<Type>::get() const noexcept
{
return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_add_and_fetch ((volatile int32*) &value, 0))
: castFrom64Bit ((int64) __sync_add_and_fetch ((volatile int64*) &value, 0));
}
template <typename Type>
inline Type Atomic<Type>::exchange (const Type newValue) noexcept
{
Type currentVal = value;
while (! compareAndSetBool (newValue, currentVal)) { currentVal = value; }
return currentVal;
}
template <typename Type>
inline Type Atomic<Type>::operator+= (const Type amountToAdd) noexcept
{
return (Type) __sync_add_and_fetch (&value, amountToAdd);
}
template <typename Type>
inline Type Atomic<Type>::operator-= (const Type amountToSubtract) noexcept
{
return operator+= (negateValue (amountToSubtract));
}
template <typename Type>
inline Type Atomic<Type>::operator++() noexcept
{
return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (&value, (Type) 1)
: (Type) __sync_add_and_fetch ((int64_t*) &value, 1);
}
template <typename Type>
inline Type Atomic<Type>::operator--() noexcept
{
return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (&value, (Type) -1)
: (Type) __sync_add_and_fetch ((int64_t*) &value, -1);
}
template <typename Type>
inline bool Atomic<Type>::compareAndSetBool (const Type newValue, const Type valueToCompare) noexcept
{
return sizeof (Type) == 4 ? __sync_bool_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue))
: __sync_bool_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue));
}
template <typename Type>
inline Type Atomic<Type>::compareAndSetValue (const Type newValue, const Type valueToCompare) noexcept
{
return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_val_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue)))
: castFrom64Bit ((int64) __sync_val_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue)));
}
template <typename Type>
inline void Atomic<Type>::memoryBarrier() noexcept
{
__sync_synchronize();
}
#endif // JUCE_ATOMIC_H_INCLUDED

+ 241
- 0
source/modules/juce_audio_graph/memory/juce_ByteOrder.h View File

@@ -0,0 +1,241 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_BYTEORDER_H_INCLUDED
#define JUCE_BYTEORDER_H_INCLUDED
//==============================================================================
/** Contains static methods for converting the byte order between different
endiannesses.
*/
class JUCE_API ByteOrder
{
public:
//==============================================================================
/** Swaps the upper and lower bytes of a 16-bit integer. */
static uint16 swap (uint16 value) noexcept;
/** Reverses the order of the 4 bytes in a 32-bit integer. */
static uint32 swap (uint32 value) noexcept;
/** Reverses the order of the 8 bytes in a 64-bit integer. */
static uint64 swap (uint64 value) noexcept;
//==============================================================================
/** Swaps the byte order of a 16-bit unsigned int if the CPU is big-endian */
static uint16 swapIfBigEndian (uint16 value) noexcept;
/** Swaps the byte order of a 32-bit unsigned int if the CPU is big-endian */
static uint32 swapIfBigEndian (uint32 value) noexcept;
/** Swaps the byte order of a 64-bit unsigned int if the CPU is big-endian */
static uint64 swapIfBigEndian (uint64 value) noexcept;
/** Swaps the byte order of a 16-bit signed int if the CPU is big-endian */
static int16 swapIfBigEndian (int16 value) noexcept;
/** Swaps the byte order of a 32-bit signed int if the CPU is big-endian */
static int32 swapIfBigEndian (int32 value) noexcept;
/** Swaps the byte order of a 64-bit signed int if the CPU is big-endian */
static int64 swapIfBigEndian (int64 value) noexcept;
/** Swaps the byte order of a 32-bit float if the CPU is big-endian */
static float swapIfBigEndian (float value) noexcept;
/** Swaps the byte order of a 64-bit float if the CPU is big-endian */
static double swapIfBigEndian (double value) noexcept;
/** Swaps the byte order of a 16-bit unsigned int if the CPU is little-endian */
static uint16 swapIfLittleEndian (uint16 value) noexcept;
/** Swaps the byte order of a 32-bit unsigned int if the CPU is little-endian */
static uint32 swapIfLittleEndian (uint32 value) noexcept;
/** Swaps the byte order of a 64-bit unsigned int if the CPU is little-endian */
static uint64 swapIfLittleEndian (uint64 value) noexcept;
/** Swaps the byte order of a 16-bit signed int if the CPU is little-endian */
static int16 swapIfLittleEndian (int16 value) noexcept;
/** Swaps the byte order of a 32-bit signed int if the CPU is little-endian */
static int32 swapIfLittleEndian (int32 value) noexcept;
/** Swaps the byte order of a 64-bit signed int if the CPU is little-endian */
static int64 swapIfLittleEndian (int64 value) noexcept;
/** Swaps the byte order of a 32-bit float if the CPU is little-endian */
static float swapIfLittleEndian (float value) noexcept;
/** Swaps the byte order of a 64-bit float if the CPU is little-endian */
static double swapIfLittleEndian (double value) noexcept;
//==============================================================================
/** Turns 4 bytes into a little-endian integer. */
static uint32 littleEndianInt (const void* bytes) noexcept;
/** Turns 8 bytes into a little-endian integer. */
static uint64 littleEndianInt64 (const void* bytes) noexcept;
/** Turns 2 bytes into a little-endian integer. */
static uint16 littleEndianShort (const void* bytes) noexcept;
/** Turns 4 bytes into a big-endian integer. */
static uint32 bigEndianInt (const void* bytes) noexcept;
/** Turns 8 bytes into a big-endian integer. */
static uint64 bigEndianInt64 (const void* bytes) noexcept;
/** Turns 2 bytes into a big-endian integer. */
static uint16 bigEndianShort (const void* bytes) noexcept;
//==============================================================================
/** Converts 3 little-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */
static int littleEndian24Bit (const void* bytes) noexcept;
/** Converts 3 big-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */
static int bigEndian24Bit (const void* bytes) noexcept;
/** Copies a 24-bit number to 3 little-endian bytes. */
static void littleEndian24BitToChars (int value, void* destBytes) noexcept;
/** Copies a 24-bit number to 3 big-endian bytes. */
static void bigEndian24BitToChars (int value, void* destBytes) noexcept;
//==============================================================================
/** Returns true if the current CPU is big-endian. */
static bool isBigEndian() noexcept;
private:
ByteOrder() JUCE_DELETED_FUNCTION;
JUCE_DECLARE_NON_COPYABLE (ByteOrder)
};
//==============================================================================
#if JUCE_MSVC && ! defined (__INTEL_COMPILER)
#pragma intrinsic (_byteswap_ulong)
#endif
inline uint16 ByteOrder::swap (uint16 n) noexcept
{
return static_cast<uint16> ((n << 8) | (n >> 8));
}
inline uint32 ByteOrder::swap (uint32 n) noexcept
{
#if JUCE_MAC || JUCE_IOS
return OSSwapInt32 (n);
#elif (JUCE_GCC || JUCE_CLANG) && JUCE_INTEL && ! JUCE_NO_INLINE_ASM
asm("bswap %%eax" : "=a"(n) : "a"(n));
return n;
#elif JUCE_MSVC
return _byteswap_ulong (n);
#elif JUCE_ANDROID
return bswap_32 (n);
#else
return (n << 24) | (n >> 24) | ((n & 0xff00) << 8) | ((n & 0xff0000) >> 8);
#endif
}
inline uint64 ByteOrder::swap (uint64 value) noexcept
{
#if JUCE_MAC || JUCE_IOS
return OSSwapInt64 (value);
#elif JUCE_MSVC
return _byteswap_uint64 (value);
#else
return (((uint64) swap ((uint32) value)) << 32) | swap ((uint32) (value >> 32));
#endif
}
#if JUCE_LITTLE_ENDIAN
inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) noexcept { return v; }
inline uint32 ByteOrder::swapIfBigEndian (const uint32 v) noexcept { return v; }
inline uint64 ByteOrder::swapIfBigEndian (const uint64 v) noexcept { return v; }
inline int16 ByteOrder::swapIfBigEndian (const int16 v) noexcept { return v; }
inline int32 ByteOrder::swapIfBigEndian (const int32 v) noexcept { return v; }
inline int64 ByteOrder::swapIfBigEndian (const int64 v) noexcept { return v; }
inline float ByteOrder::swapIfBigEndian (const float v) noexcept { return v; }
inline double ByteOrder::swapIfBigEndian (const double v) noexcept { return v; }
inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return swap (v); }
inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return swap (v); }
inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return swap (v); }
inline int16 ByteOrder::swapIfLittleEndian (const int16 v) noexcept { return static_cast<int16> (swap (static_cast<uint16> (v))); }
inline int32 ByteOrder::swapIfLittleEndian (const int32 v) noexcept { return static_cast<int32> (swap (static_cast<uint32> (v))); }
inline int64 ByteOrder::swapIfLittleEndian (const int64 v) noexcept { return static_cast<int64> (swap (static_cast<uint64> (v))); }
inline float ByteOrder::swapIfLittleEndian (const float v) noexcept { union { uint32 asUInt; float asFloat; } n; n.asFloat = v; n.asUInt = ByteOrder::swap (n.asUInt); return n.asFloat; }
inline double ByteOrder::swapIfLittleEndian (const double v) noexcept { union { uint64 asUInt; double asFloat; } n; n.asFloat = v; n.asUInt = ByteOrder::swap (n.asUInt); return n.asFloat; }
inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return *static_cast<const uint32*> (bytes); }
inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return *static_cast<const uint64*> (bytes); }
inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return *static_cast<const uint16*> (bytes); }
inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return swap (*static_cast<const uint32*> (bytes)); }
inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast<const uint64*> (bytes)); }
inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return swap (*static_cast<const uint16*> (bytes)); }
inline bool ByteOrder::isBigEndian() noexcept { return false; }
#else
inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) noexcept { return swap (v); }
inline uint32 ByteOrder::swapIfBigEndian (const uint32 v) noexcept { return swap (v); }
inline uint64 ByteOrder::swapIfBigEndian (const uint64 v) noexcept { return swap (v); }
inline int16 ByteOrder::swapIfBigEndian (const int16 v) noexcept { return static_cast<int16> (swap (static_cast<uint16> (v))); }
inline int32 ByteOrder::swapIfBigEndian (const int32 v) noexcept { return static_cast<int16> (swap (static_cast<uint16> (v))); }
inline int64 ByteOrder::swapIfBigEndian (const int64 v) noexcept { return static_cast<int16> (swap (static_cast<uint16> (v))); }
inline float ByteOrder::swapIfBigEndian (const float v) noexcept { union { uint32 asUInt; float asFloat; } n; n.asFloat = v; n.asUInt = ByteOrder::swap (n.asUInt); return n.asFloat; }
inline double ByteOrder::swapIfBigEndian (const double v) noexcept { union { uint64 asUInt; double asFloat; } n; n.asFloat = v; n.asUInt = ByteOrder::swap (n.asUInt); return n.asFloat; }
inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return v; }
inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return v; }
inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return v; }
inline int16 ByteOrder::swapIfLittleEndian (const int16 v) noexcept { return v; }
inline int32 ByteOrder::swapIfLittleEndian (const int32 v) noexcept { return v; }
inline int64 ByteOrder::swapIfLittleEndian (const int64 v) noexcept { return v; }
inline float ByteOrder::swapIfLittleEndian (const float v) noexcept { return v; }
inline double ByteOrder::swapIfLittleEndian (const double v) noexcept { return v; }
inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return swap (*static_cast<const uint32*> (bytes)); }
inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast<const uint64*> (bytes)); }
inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return swap (*static_cast<const uint16*> (bytes)); }
inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return *static_cast<const uint32*> (bytes); }
inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return *static_cast<const uint64*> (bytes); }
inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return *static_cast<const uint16*> (bytes); }
inline bool ByteOrder::isBigEndian() noexcept { return true; }
#endif
inline int ByteOrder::littleEndian24Bit (const void* const bytes) noexcept { return (((int) static_cast<const int8*> (bytes)[2]) << 16) | (((int) static_cast<const uint8*> (bytes)[1]) << 8) | ((int) static_cast<const uint8*> (bytes)[0]); }
inline int ByteOrder::bigEndian24Bit (const void* const bytes) noexcept { return (((int) static_cast<const int8*> (bytes)[0]) << 16) | (((int) static_cast<const uint8*> (bytes)[1]) << 8) | ((int) static_cast<const uint8*> (bytes)[2]); }
inline void ByteOrder::littleEndian24BitToChars (const int value, void* const destBytes) noexcept { static_cast<uint8*> (destBytes)[0] = (uint8) value; static_cast<uint8*> (destBytes)[1] = (uint8) (value >> 8); static_cast<uint8*> (destBytes)[2] = (uint8) (value >> 16); }
inline void ByteOrder::bigEndian24BitToChars (const int value, void* const destBytes) noexcept { static_cast<uint8*> (destBytes)[0] = (uint8) (value >> 16); static_cast<uint8*> (destBytes)[1] = (uint8) (value >> 8); static_cast<uint8*> (destBytes)[2] = (uint8) value; }
#endif // JUCE_BYTEORDER_H_INCLUDED

+ 314
- 0
source/modules/juce_audio_graph/memory/juce_HeapBlock.h View File

@@ -0,0 +1,314 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_HEAPBLOCK_H_INCLUDED
#define JUCE_HEAPBLOCK_H_INCLUDED
#if ! (defined (DOXYGEN) || JUCE_EXCEPTIONS_DISABLED)
namespace HeapBlockHelper
{
template <bool shouldThrow>
struct ThrowOnFail { static void checkPointer (void*) {} };
template<>
struct ThrowOnFail<true> { static void checkPointer (void* data) { if (data == nullptr) throw std::bad_alloc(); } };
}
#endif
//==============================================================================
/**
Very simple container class to hold a pointer to some data on the heap.
When you need to allocate some heap storage for something, always try to use
this class instead of allocating the memory directly using malloc/free.
A HeapBlock<char> object can be treated in pretty much exactly the same way
as an char*, but as long as you allocate it on the stack or as a class member,
it's almost impossible for it to leak memory.
It also makes your code much more concise and readable than doing the same thing
using direct allocations,
E.g. instead of this:
@code
int* temp = (int*) malloc (1024 * sizeof (int));
memcpy (temp, xyz, 1024 * sizeof (int));
free (temp);
temp = (int*) calloc (2048 * sizeof (int));
temp[0] = 1234;
memcpy (foobar, temp, 2048 * sizeof (int));
free (temp);
@endcode
..you could just write this:
@code
HeapBlock<int> temp (1024);
memcpy (temp, xyz, 1024 * sizeof (int));
temp.calloc (2048);
temp[0] = 1234;
memcpy (foobar, temp, 2048 * sizeof (int));
@endcode
The class is extremely lightweight, containing only a pointer to the
data, and exposes malloc/realloc/calloc/free methods that do the same jobs
as their less object-oriented counterparts. Despite adding safety, you probably
won't sacrifice any performance by using this in place of normal pointers.
The throwOnFailure template parameter can be set to true if you'd like the class
to throw a std::bad_alloc exception when an allocation fails. If this is false,
then a failed allocation will just leave the heapblock with a null pointer (assuming
that the system's malloc() function doesn't throw).
@see Array, OwnedArray, MemoryBlock
*/
template <class ElementType, bool throwOnFailure = false>
class HeapBlock
{
public:
//==============================================================================
/** Creates a HeapBlock which is initially just a null pointer.
After creation, you can resize the array using the malloc(), calloc(),
or realloc() methods.
*/
HeapBlock() noexcept : data (nullptr)
{
}
/** Creates a HeapBlock containing a number of elements.
The contents of the block are undefined, as it will have been created by a
malloc call.
If you want an array of zero values, you can use the calloc() method or the
other constructor that takes an InitialisationState parameter.
*/
explicit HeapBlock (const size_t numElements)
: data (static_cast<ElementType*> (std::malloc (numElements * sizeof (ElementType))))
{
throwOnAllocationFailure();
}
/** Creates a HeapBlock containing a number of elements.
The initialiseToZero parameter determines whether the new memory should be cleared,
or left uninitialised.
*/
HeapBlock (const size_t numElements, const bool initialiseToZero)
: data (static_cast<ElementType*> (initialiseToZero
? std::calloc (numElements, sizeof (ElementType))
: std::malloc (numElements * sizeof (ElementType))))
{
throwOnAllocationFailure();
}
/** Destructor.
This will free the data, if any has been allocated.
*/
~HeapBlock()
{
std::free (data);
}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
HeapBlock (HeapBlock&& other) noexcept
: data (other.data)
{
other.data = nullptr;
}
HeapBlock& operator= (HeapBlock&& other) noexcept
{
std::swap (data, other.data);
return *this;
}
#endif
//==============================================================================
/** Returns a raw pointer to the allocated data.
This may be a null pointer if the data hasn't yet been allocated, or if it has been
freed by calling the free() method.
*/
inline operator ElementType*() const noexcept { return data; }
/** Returns a raw pointer to the allocated data.
This may be a null pointer if the data hasn't yet been allocated, or if it has been
freed by calling the free() method.
*/
inline ElementType* getData() const noexcept { return data; }
/** Returns a void pointer to the allocated data.
This may be a null pointer if the data hasn't yet been allocated, or if it has been
freed by calling the free() method.
*/
inline operator void*() const noexcept { return static_cast<void*> (data); }
/** Returns a void pointer to the allocated data.
This may be a null pointer if the data hasn't yet been allocated, or if it has been
freed by calling the free() method.
*/
inline operator const void*() const noexcept { return static_cast<const void*> (data); }
/** Lets you use indirect calls to the first element in the array.
Obviously this will cause problems if the array hasn't been initialised, because it'll
be referencing a null pointer.
*/
inline ElementType* operator->() const noexcept { return data; }
/** Returns a reference to one of the data elements.
Obviously there's no bounds-checking here, as this object is just a dumb pointer and
has no idea of the size it currently has allocated.
*/
template <typename IndexType>
inline ElementType& operator[] (IndexType index) const noexcept { return data [index]; }
/** Returns a pointer to a data element at an offset from the start of the array.
This is the same as doing pointer arithmetic on the raw pointer itself.
*/
template <typename IndexType>
inline ElementType* operator+ (IndexType index) const noexcept { return data + index; }
//==============================================================================
/** Compares the pointer with another pointer.
This can be handy for checking whether this is a null pointer.
*/
inline bool operator== (const ElementType* const otherPointer) const noexcept { return otherPointer == data; }
/** Compares the pointer with another pointer.
This can be handy for checking whether this is a null pointer.
*/
inline bool operator!= (const ElementType* const otherPointer) const noexcept { return otherPointer != data; }
//==============================================================================
/** Allocates a specified amount of memory.
This uses the normal malloc to allocate an amount of memory for this object.
Any previously allocated memory will be freed by this method.
The number of bytes allocated will be (newNumElements * elementSize). Normally
you wouldn't need to specify the second parameter, but it can be handy if you need
to allocate a size in bytes rather than in terms of the number of elements.
The data that is allocated will be freed when this object is deleted, or when you
call free() or any of the allocation methods.
*/
void malloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType))
{
std::free (data);
data = static_cast<ElementType*> (std::malloc (newNumElements * elementSize));
throwOnAllocationFailure();
}
/** Allocates a specified amount of memory and clears it.
This does the same job as the malloc() method, but clears the memory that it allocates.
*/
void calloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType))
{
std::free (data);
data = static_cast<ElementType*> (std::calloc (newNumElements, elementSize));
throwOnAllocationFailure();
}
/** Allocates a specified amount of memory and optionally clears it.
This does the same job as either malloc() or calloc(), depending on the
initialiseToZero parameter.
*/
void allocate (const size_t newNumElements, bool initialiseToZero)
{
std::free (data);
data = static_cast<ElementType*> (initialiseToZero
? std::calloc (newNumElements, sizeof (ElementType))
: std::malloc (newNumElements * sizeof (ElementType)));
throwOnAllocationFailure();
}
/** Re-allocates a specified amount of memory.
The semantics of this method are the same as malloc() and calloc(), but it
uses realloc() to keep as much of the existing data as possible.
*/
void realloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType))
{
data = static_cast<ElementType*> (data == nullptr ? std::malloc (newNumElements * elementSize)
: std::realloc (data, newNumElements * elementSize));
throwOnAllocationFailure();
}
/** Frees any currently-allocated data.
This will free the data and reset this object to be a null pointer.
*/
void free() noexcept
{
std::free (data);
data = nullptr;
}
/** Swaps this object's data with the data of another HeapBlock.
The two objects simply exchange their data pointers.
*/
template <bool otherBlockThrows>
void swapWith (HeapBlock<ElementType, otherBlockThrows>& other) noexcept
{
std::swap (data, other.data);
}
/** This fills the block with zeros, up to the number of elements specified.
Since the block has no way of knowing its own size, you must make sure that the number of
elements you specify doesn't exceed the allocated size.
*/
void clear (size_t numElements) noexcept
{
zeromem (data, sizeof (ElementType) * numElements);
}
/** This typedef can be used to get the type of the heapblock's elements. */
typedef ElementType Type;
private:
//==============================================================================
ElementType* data;
void throwOnAllocationFailure() const
{
#if JUCE_EXCEPTIONS_DISABLED
jassert (data != nullptr); // without exceptions, you'll need to find a better way to handle this failure case.
#else
HeapBlockHelper::ThrowOnFail<throwOnFailure>::checkPointer (data);
#endif
}
#if ! (defined (JUCE_DLL) || defined (JUCE_DLL_BUILD))
JUCE_DECLARE_NON_COPYABLE (HeapBlock)
JUCE_PREVENT_HEAP_ALLOCATION // Creating a 'new HeapBlock' would be missing the point!
#endif
};
#endif // JUCE_HEAPBLOCK_H_INCLUDED

+ 120
- 0
source/modules/juce_audio_graph/memory/juce_Memory.h View File

@@ -0,0 +1,120 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_MEMORY_H_INCLUDED
#define JUCE_MEMORY_H_INCLUDED
//==============================================================================
/** Fills a block of memory with zeros. */
inline void zeromem (void* memory, size_t numBytes) noexcept { memset (memory, 0, numBytes); }
/** Overwrites a structure or object with zeros. */
template <typename Type>
inline void zerostruct (Type& structure) noexcept { memset (&structure, 0, sizeof (structure)); }
/** Delete an object pointer, and sets the pointer to null.
Remember that it's not good c++ practice to use delete directly - always try to use a ScopedPointer
or other automatic lifetime-management system rather than resorting to deleting raw pointers!
*/
template <typename Type>
inline void deleteAndZero (Type& pointer) { delete pointer; pointer = nullptr; }
/** A handy function which adds a number of bytes to any type of pointer and returns the result.
This can be useful to avoid casting pointers to a char* and back when you want to move them by
a specific number of bytes,
*/
template <typename Type, typename IntegerType>
inline Type* addBytesToPointer (Type* basePointer, IntegerType bytes) noexcept { return (Type*) (((char*) basePointer) + bytes); }
/** A handy function which returns the difference between any two pointers, in bytes.
The address of the second pointer is subtracted from the first, and the difference in bytes is returned.
*/
template <typename Type1, typename Type2>
inline int getAddressDifference (Type1* pointer1, Type2* pointer2) noexcept { return (int) (((const char*) pointer1) - (const char*) pointer2); }
/** If a pointer is non-null, this returns a new copy of the object that it points to, or safely returns
nullptr if the pointer is null.
*/
template <class Type>
inline Type* createCopyIfNotNull (const Type* objectToCopy) { return objectToCopy != nullptr ? new Type (*objectToCopy) : nullptr; }
//==============================================================================
/** A handy function to read un-aligned memory without a performance penalty or bus-error. */
template <typename Type>
inline Type readUnaligned (const void* srcPtr) noexcept
{
Type value;
memcpy (&value, srcPtr, sizeof (Type));
return value;
}
/** A handy function to write un-aligned memory without a performance penalty or bus-error. */
template <typename Type>
inline void writeUnaligned (void* dstPtr, Type value) noexcept
{
memcpy (dstPtr, &value, sizeof(Type));
}
//==============================================================================
#if JUCE_MAC || JUCE_IOS || DOXYGEN
/** A handy C++ wrapper that creates and deletes an NSAutoreleasePool object using RAII.
You should use the JUCE_AUTORELEASEPOOL macro to create a local auto-release pool on the stack.
*/
class JUCE_API ScopedAutoReleasePool
{
public:
ScopedAutoReleasePool();
~ScopedAutoReleasePool();
private:
void* pool;
JUCE_DECLARE_NON_COPYABLE (ScopedAutoReleasePool)
};
/** A macro that can be used to easily declare a local ScopedAutoReleasePool
object for RAII-based obj-C autoreleasing.
Because this may use the \@autoreleasepool syntax, you must follow the macro with
a set of braces to mark the scope of the pool.
*/
#if (JUCE_COMPILER_SUPPORTS_ARC && defined (__OBJC__)) || DOXYGEN
#define JUCE_AUTORELEASEPOOL @autoreleasepool
#else
#define JUCE_AUTORELEASEPOOL const juce::ScopedAutoReleasePool JUCE_JOIN_MACRO (autoReleasePool_, __LINE__);
#endif
#else
#define JUCE_AUTORELEASEPOOL
#endif
#endif // JUCE_MEMORY_H_INCLUDED

+ 418
- 0
source/modules/juce_audio_graph/memory/juce_MemoryBlock.cpp View File

@@ -0,0 +1,418 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
MemoryBlock::MemoryBlock() noexcept
: size (0)
{
}
MemoryBlock::MemoryBlock (const size_t initialSize, const bool initialiseToZero)
{
if (initialSize > 0)
{
size = initialSize;
data.allocate (initialSize, initialiseToZero);
}
else
{
size = 0;
}
}
MemoryBlock::MemoryBlock (const MemoryBlock& other)
: size (other.size)
{
if (size > 0)
{
jassert (other.data != nullptr);
data.malloc (size);
memcpy (data, other.data, size);
}
}
MemoryBlock::MemoryBlock (const void* const dataToInitialiseFrom, const size_t sizeInBytes)
: size (sizeInBytes)
{
jassert (((ssize_t) sizeInBytes) >= 0);
if (size > 0)
{
jassert (dataToInitialiseFrom != nullptr); // non-zero size, but a zero pointer passed-in?
data.malloc (size);
if (dataToInitialiseFrom != nullptr)
memcpy (data, dataToInitialiseFrom, size);
}
}
MemoryBlock::~MemoryBlock() noexcept
{
}
MemoryBlock& MemoryBlock::operator= (const MemoryBlock& other)
{
if (this != &other)
{
setSize (other.size, false);
memcpy (data, other.data, size);
}
return *this;
}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
MemoryBlock::MemoryBlock (MemoryBlock&& other) noexcept
: data (static_cast<HeapBlock<char>&&> (other.data)),
size (other.size)
{
}
MemoryBlock& MemoryBlock::operator= (MemoryBlock&& other) noexcept
{
data = static_cast<HeapBlock<char>&&> (other.data);
size = other.size;
return *this;
}
#endif
//==============================================================================
bool MemoryBlock::operator== (const MemoryBlock& other) const noexcept
{
return matches (other.data, other.size);
}
bool MemoryBlock::operator!= (const MemoryBlock& other) const noexcept
{
return ! operator== (other);
}
bool MemoryBlock::matches (const void* dataToCompare, size_t dataSize) const noexcept
{
return size == dataSize
&& memcmp (data, dataToCompare, size) == 0;
}
//==============================================================================
// this will resize the block to this size
void MemoryBlock::setSize (const size_t newSize, const bool initialiseToZero)
{
if (size != newSize)
{
if (newSize <= 0)
{
reset();
}
else
{
if (data != nullptr)
{
data.realloc (newSize);
if (initialiseToZero && (newSize > size))
zeromem (data + size, newSize - size);
}
else
{
data.allocate (newSize, initialiseToZero);
}
size = newSize;
}
}
}
void MemoryBlock::reset()
{
data.free();
size = 0;
}
void MemoryBlock::ensureSize (const size_t minimumSize, const bool initialiseToZero)
{
if (size < minimumSize)
setSize (minimumSize, initialiseToZero);
}
void MemoryBlock::swapWith (MemoryBlock& other) noexcept
{
std::swap (size, other.size);
data.swapWith (other.data);
}
//==============================================================================
void MemoryBlock::fillWith (const uint8 value) noexcept
{
memset (data, (int) value, size);
}
void MemoryBlock::append (const void* const srcData, const size_t numBytes)
{
if (numBytes > 0)
{
jassert (srcData != nullptr); // this must not be null!
const size_t oldSize = size;
setSize (size + numBytes);
memcpy (data + oldSize, srcData, numBytes);
}
}
void MemoryBlock::replaceWith (const void* const srcData, const size_t numBytes)
{
if (numBytes > 0)
{
jassert (srcData != nullptr); // this must not be null!
setSize (numBytes);
memcpy (data, srcData, numBytes);
}
}
void MemoryBlock::insert (const void* const srcData, const size_t numBytes, size_t insertPosition)
{
if (numBytes > 0)
{
jassert (srcData != nullptr); // this must not be null!
insertPosition = jmin (size, insertPosition);
const size_t trailingDataSize = size - insertPosition;
setSize (size + numBytes, false);
if (trailingDataSize > 0)
memmove (data + insertPosition + numBytes,
data + insertPosition,
trailingDataSize);
memcpy (data + insertPosition, srcData, numBytes);
}
}
void MemoryBlock::removeSection (const size_t startByte, const size_t numBytesToRemove)
{
if (startByte + numBytesToRemove >= size)
{
setSize (startByte);
}
else if (numBytesToRemove > 0)
{
memmove (data + startByte,
data + startByte + numBytesToRemove,
size - (startByte + numBytesToRemove));
setSize (size - numBytesToRemove);
}
}
void MemoryBlock::copyFrom (const void* const src, int offset, size_t num) noexcept
{
const char* d = static_cast<const char*> (src);
if (offset < 0)
{
d -= offset;
num += (size_t) -offset;
offset = 0;
}
if ((size_t) offset + num > size)
num = size - (size_t) offset;
if (num > 0)
memcpy (data + offset, d, num);
}
void MemoryBlock::copyTo (void* const dst, int offset, size_t num) const noexcept
{
char* d = static_cast<char*> (dst);
if (offset < 0)
{
zeromem (d, (size_t) -offset);
d -= offset;
num -= (size_t) -offset;
offset = 0;
}
if ((size_t) offset + num > size)
{
const size_t newNum = (size_t) size - (size_t) offset;
zeromem (d + newNum, num - newNum);
num = newNum;
}
if (num > 0)
memcpy (d, data + offset, num);
}
String MemoryBlock::toString() const
{
return String::fromUTF8 (data, (int) size);
}
//==============================================================================
int MemoryBlock::getBitRange (const size_t bitRangeStart, size_t numBits) const noexcept
{
int res = 0;
size_t byte = bitRangeStart >> 3;
size_t offsetInByte = bitRangeStart & 7;
size_t bitsSoFar = 0;
while (numBits > 0 && (size_t) byte < size)
{
const size_t bitsThisTime = jmin (numBits, 8 - offsetInByte);
const int mask = (0xff >> (8 - bitsThisTime)) << offsetInByte;
res |= (((data[byte] & mask) >> offsetInByte) << bitsSoFar);
bitsSoFar += bitsThisTime;
numBits -= bitsThisTime;
++byte;
offsetInByte = 0;
}
return res;
}
void MemoryBlock::setBitRange (const size_t bitRangeStart, size_t numBits, int bitsToSet) noexcept
{
size_t byte = bitRangeStart >> 3;
size_t offsetInByte = bitRangeStart & 7;
uint32 mask = ~((((uint32) 0xffffffff) << (32 - numBits)) >> (32 - numBits));
while (numBits > 0 && (size_t) byte < size)
{
const size_t bitsThisTime = jmin (numBits, 8 - offsetInByte);
const uint32 tempMask = (mask << offsetInByte) | ~((((uint32) 0xffffffff) >> offsetInByte) << offsetInByte);
const uint32 tempBits = (uint32) bitsToSet << offsetInByte;
data[byte] = (char) (((uint32) data[byte] & tempMask) | tempBits);
++byte;
numBits -= bitsThisTime;
bitsToSet >>= bitsThisTime;
mask >>= bitsThisTime;
offsetInByte = 0;
}
}
//==============================================================================
void MemoryBlock::loadFromHexString (StringRef hex)
{
ensureSize ((size_t) hex.length() >> 1);
char* dest = data;
String::CharPointerType t (hex.text);
for (;;)
{
int byte = 0;
for (int loop = 2; --loop >= 0;)
{
byte <<= 4;
for (;;)
{
const juce_wchar c = t.getAndAdvance();
if (c >= '0' && c <= '9') { byte |= c - '0'; break; }
if (c >= 'a' && c <= 'z') { byte |= c - ('a' - 10); break; }
if (c >= 'A' && c <= 'Z') { byte |= c - ('A' - 10); break; }
if (c == 0)
{
setSize (static_cast<size_t> (dest - data));
return;
}
}
}
*dest++ = (char) byte;
}
}
//==============================================================================
static const char base64EncodingTable[] = ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+";
String MemoryBlock::toBase64Encoding() const
{
const size_t numChars = ((size << 3) + 5) / 6;
String destString ((unsigned int) size); // store the length, followed by a '.', and then the data.
const int initialLen = destString.length();
destString.preallocateBytes (sizeof (String::CharPointerType::CharType) * (size_t) initialLen + 2 + numChars);
String::CharPointerType d (destString.getCharPointer());
d += initialLen;
d.write ('.');
for (size_t i = 0; i < numChars; ++i)
d.write ((juce_wchar) (uint8) base64EncodingTable [getBitRange (i * 6, 6)]);
d.writeNull();
return destString;
}
static const char base64DecodingTable[] =
{
63, 0, 0, 0, 0, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 0, 0, 0, 0, 0, 0, 0,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
0, 0, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52
};
bool MemoryBlock::fromBase64Encoding (StringRef s)
{
String::CharPointerType dot (CharacterFunctions::find (s.text, (juce_wchar) '.'));
if (dot.isEmpty())
return false;
const int numBytesNeeded = String (s.text, dot).getIntValue();
setSize ((size_t) numBytesNeeded, true);
String::CharPointerType srcChars (dot + 1);
int pos = 0;
for (;;)
{
int c = (int) srcChars.getAndAdvance();
if (c == 0)
return true;
c -= 43;
if (isPositiveAndBelow (c, numElementsInArray (base64DecodingTable)))
{
setBitRange ((size_t) pos, 6, base64DecodingTable [c]);
pos += 6;
}
}
}

+ 263
- 0
source/modules/juce_audio_graph/memory/juce_MemoryBlock.h View File

@@ -0,0 +1,263 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_MEMORYBLOCK_H_INCLUDED
#define JUCE_MEMORYBLOCK_H_INCLUDED
//==============================================================================
/**
A class to hold a resizable block of raw data.
*/
class JUCE_API MemoryBlock
{
public:
//==============================================================================
/** Create an uninitialised block with 0 size. */
MemoryBlock() noexcept;
/** Creates a memory block with a given initial size.
@param initialSize the size of block to create
@param initialiseToZero whether to clear the memory or just leave it uninitialised
*/
MemoryBlock (const size_t initialSize,
bool initialiseToZero = false);
/** Creates a copy of another memory block. */
MemoryBlock (const MemoryBlock&);
/** Creates a memory block using a copy of a block of data.
@param dataToInitialiseFrom some data to copy into this block
@param sizeInBytes how much space to use
*/
MemoryBlock (const void* dataToInitialiseFrom, size_t sizeInBytes);
/** Destructor. */
~MemoryBlock() noexcept;
/** Copies another memory block onto this one.
This block will be resized and copied to exactly match the other one.
*/
MemoryBlock& operator= (const MemoryBlock&);
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
MemoryBlock (MemoryBlock&&) noexcept;
MemoryBlock& operator= (MemoryBlock&&) noexcept;
#endif
//==============================================================================
/** Compares two memory blocks.
@returns true only if the two blocks are the same size and have identical contents.
*/
bool operator== (const MemoryBlock& other) const noexcept;
/** Compares two memory blocks.
@returns true if the two blocks are different sizes or have different contents.
*/
bool operator!= (const MemoryBlock& other) const noexcept;
/** Returns true if the data in this MemoryBlock matches the raw bytes passed-in. */
bool matches (const void* data, size_t dataSize) const noexcept;
//==============================================================================
/** Returns a void pointer to the data.
Note that the pointer returned will probably become invalid when the
block is resized.
*/
void* getData() const noexcept { return data; }
/** Returns a byte from the memory block.
This returns a reference, so you can also use it to set a byte.
*/
template <typename Type>
char& operator[] (const Type offset) const noexcept { return data [offset]; }
//==============================================================================
/** Returns the block's current allocated size, in bytes. */
size_t getSize() const noexcept { return size; }
/** Resizes the memory block.
Any data that is present in both the old and new sizes will be retained.
When enlarging the block, the new space that is allocated at the end can either be
cleared, or left uninitialised.
@param newSize the new desired size for the block
@param initialiseNewSpaceToZero if the block gets enlarged, this determines
whether to clear the new section or just leave it
uninitialised
@see ensureSize
*/
void setSize (const size_t newSize,
bool initialiseNewSpaceToZero = false);
/** Increases the block's size only if it's smaller than a given size.
@param minimumSize if the block is already bigger than this size, no action
will be taken; otherwise it will be increased to this size
@param initialiseNewSpaceToZero if the block gets enlarged, this determines
whether to clear the new section or just leave it
uninitialised
@see setSize
*/
void ensureSize (const size_t minimumSize,
bool initialiseNewSpaceToZero = false);
/** Frees all the blocks data, setting its size to 0. */
void reset();
//==============================================================================
/** Fills the entire memory block with a repeated byte value.
This is handy for clearing a block of memory to zero.
*/
void fillWith (uint8 valueToUse) noexcept;
/** Adds another block of data to the end of this one.
The data pointer must not be null. This block's size will be increased accordingly.
*/
void append (const void* data, size_t numBytes);
/** Resizes this block to the given size and fills its contents from the supplied buffer.
The data pointer must not be null.
*/
void replaceWith (const void* data, size_t numBytes);
/** Inserts some data into the block.
The dataToInsert pointer must not be null. This block's size will be increased accordingly.
If the insert position lies outside the valid range of the block, it will be clipped to
within the range before being used.
*/
void insert (const void* dataToInsert, size_t numBytesToInsert, size_t insertPosition);
/** Chops out a section of the block.
This will remove a section of the memory block and close the gap around it,
shifting any subsequent data downwards and reducing the size of the block.
If the range specified goes beyond the size of the block, it will be clipped.
*/
void removeSection (size_t startByte, size_t numBytesToRemove);
//==============================================================================
/** Copies data into this MemoryBlock from a memory address.
@param srcData the memory location of the data to copy into this block
@param destinationOffset the offset in this block at which the data being copied should begin
@param numBytes how much to copy in (if this goes beyond the size of the memory block,
it will be clipped so not to do anything nasty)
*/
void copyFrom (const void* srcData,
int destinationOffset,
size_t numBytes) noexcept;
/** Copies data from this MemoryBlock to a memory address.
@param destData the memory location to write to
@param sourceOffset the offset within this block from which the copied data will be read
@param numBytes how much to copy (if this extends beyond the limits of the memory block,
zeros will be used for that portion of the data)
*/
void copyTo (void* destData,
int sourceOffset,
size_t numBytes) const noexcept;
//==============================================================================
/** Exchanges the contents of this and another memory block.
No actual copying is required for this, so it's very fast.
*/
void swapWith (MemoryBlock& other) noexcept;
//==============================================================================
/** Attempts to parse the contents of the block as a zero-terminated UTF8 string. */
String toString() const;
//==============================================================================
/** Parses a string of hexadecimal numbers and writes this data into the memory block.
The block will be resized to the number of valid bytes read from the string.
Non-hex characters in the string will be ignored.
@see String::toHexString()
*/
void loadFromHexString (StringRef sourceHexString);
//==============================================================================
/** Sets a number of bits in the memory block, treating it as a long binary sequence. */
void setBitRange (size_t bitRangeStart,
size_t numBits,
int binaryNumberToApply) noexcept;
/** Reads a number of bits from the memory block, treating it as one long binary sequence */
int getBitRange (size_t bitRangeStart,
size_t numBitsToRead) const noexcept;
//==============================================================================
/** Returns a string of characters in a JUCE-specific text encoding that represents the
binary contents of this block.
This uses a JUCE-specific (i.e. not standard!) 64-bit encoding system to convert binary
data into a string of ASCII characters for purposes like storage in XML.
Note that this proprietary format is mainly kept here for backwards-compatibility, and
you may prefer to use the Base64::toBase64() method if you want to use the standard
base-64 encoding.
@see fromBase64Encoding, Base64::toBase64, Base64::convertToBase64
*/
String toBase64Encoding() const;
/** Takes a string created by MemoryBlock::toBase64Encoding() and extracts the original data.
The string passed in must have been created by to64BitEncoding(), and this
block will be resized to recreate the original data block.
Note that these methods use a JUCE-specific (i.e. not standard!) 64-bit encoding system.
You may prefer to use the Base64::convertFromBase64() method if you want to use the
standard base-64 encoding.
@see toBase64Encoding, Base64::convertFromBase64
*/
bool fromBase64Encoding (StringRef encodedString);
private:
//==============================================================================
HeapBlock<char> data;
size_t size;
JUCE_LEAK_DETECTOR (MemoryBlock)
};
#endif // JUCE_MEMORYBLOCK_H_INCLUDED

+ 235
- 0
source/modules/juce_audio_graph/midi/juce_MidiBuffer.cpp View File

@@ -0,0 +1,235 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
namespace MidiBufferHelpers
{
inline int getEventTime (const void* const d) noexcept
{
return readUnaligned<int32> (d);
}
inline uint16 getEventDataSize (const void* const d) noexcept
{
return readUnaligned<uint16> (static_cast<const char*> (d) + sizeof (int32));
}
inline uint16 getEventTotalSize (const void* const d) noexcept
{
return getEventDataSize (d) + sizeof (int32) + sizeof (uint16);
}
static int findActualEventLength (const uint8* const data, const int maxBytes) noexcept
{
unsigned int byte = (unsigned int) *data;
int size = 0;
if (byte == 0xf0 || byte == 0xf7)
{
const uint8* d = data + 1;
while (d < data + maxBytes)
if (*d++ == 0xf7)
break;
size = (int) (d - data);
}
else if (byte == 0xff)
{
int n;
const int bytesLeft = MidiMessage::readVariableLengthVal (data + 1, n);
size = jmin (maxBytes, n + 2 + bytesLeft);
}
else if (byte >= 0x80)
{
size = jmin (maxBytes, MidiMessage::getMessageLengthFromFirstByte ((uint8) byte));
}
return size;
}
static uint8* findEventAfter (uint8* d, uint8* endData, const int samplePosition) noexcept
{
while (d < endData && getEventTime (d) <= samplePosition)
d += getEventTotalSize (d);
return d;
}
}
//==============================================================================
MidiBuffer::MidiBuffer() noexcept {}
MidiBuffer::~MidiBuffer() {}
MidiBuffer::MidiBuffer (const MidiBuffer& other) noexcept : data (other.data) {}
MidiBuffer& MidiBuffer::operator= (const MidiBuffer& other) noexcept
{
data = other.data;
return *this;
}
MidiBuffer::MidiBuffer (const MidiMessage& message) noexcept
{
addEvent (message, 0);
}
void MidiBuffer::swapWith (MidiBuffer& other) noexcept { data.swapWith (other.data); }
void MidiBuffer::clear() noexcept { data.clearQuick(); }
void MidiBuffer::ensureSize (size_t minimumNumBytes) { data.ensureStorageAllocated ((int) minimumNumBytes); }
bool MidiBuffer::isEmpty() const noexcept { return data.size() == 0; }
void MidiBuffer::clear (const int startSample, const int numSamples)
{
uint8* const start = MidiBufferHelpers::findEventAfter (data.begin(), data.end(), startSample - 1);
uint8* const end = MidiBufferHelpers::findEventAfter (start, data.end(), startSample + numSamples - 1);
data.removeRange ((int) (start - data.begin()), (int) (end - data.begin()));
}
void MidiBuffer::addEvent (const MidiMessage& m, const int sampleNumber)
{
addEvent (m.getRawData(), m.getRawDataSize(), sampleNumber);
}
void MidiBuffer::addEvent (const void* const newData, const int maxBytes, const int sampleNumber)
{
const int numBytes = MidiBufferHelpers::findActualEventLength (static_cast<const uint8*> (newData), maxBytes);
if (numBytes > 0)
{
const size_t newItemSize = (size_t) numBytes + sizeof (int32) + sizeof (uint16);
const int offset = (int) (MidiBufferHelpers::findEventAfter (data.begin(), data.end(), sampleNumber) - data.begin());
data.insertMultiple (offset, 0, (int) newItemSize);
uint8* const d = data.begin() + offset;
writeUnaligned<int32> (d, sampleNumber);
writeUnaligned<uint16> (d + 4, static_cast<uint16> (numBytes));
memcpy (d + 6, newData, (size_t) numBytes);
}
}
void MidiBuffer::addEvents (const MidiBuffer& otherBuffer,
const int startSample,
const int numSamples,
const int sampleDeltaToAdd)
{
Iterator i (otherBuffer);
i.setNextSamplePosition (startSample);
const uint8* eventData;
int eventSize, position;
while (i.getNextEvent (eventData, eventSize, position)
&& (position < startSample + numSamples || numSamples < 0))
{
addEvent (eventData, eventSize, position + sampleDeltaToAdd);
}
}
int MidiBuffer::getNumEvents() const noexcept
{
int n = 0;
const uint8* const end = data.end();
for (const uint8* d = data.begin(); d < end; ++n)
d += MidiBufferHelpers::getEventTotalSize (d);
return n;
}
int MidiBuffer::getFirstEventTime() const noexcept
{
return data.size() > 0 ? MidiBufferHelpers::getEventTime (data.begin()) : 0;
}
int MidiBuffer::getLastEventTime() const noexcept
{
if (data.size() == 0)
return 0;
const uint8* const endData = data.end();
for (const uint8* d = data.begin();;)
{
const uint8* const nextOne = d + MidiBufferHelpers::getEventTotalSize (d);
if (nextOne >= endData)
return MidiBufferHelpers::getEventTime (d);
d = nextOne;
}
}
//==============================================================================
MidiBuffer::Iterator::Iterator (const MidiBuffer& b) noexcept
: buffer (b), data (b.data.begin())
{
}
MidiBuffer::Iterator::~Iterator() noexcept
{
}
void MidiBuffer::Iterator::setNextSamplePosition (const int samplePosition) noexcept
{
data = buffer.data.begin();
const uint8* const dataEnd = buffer.data.end();
while (data < dataEnd && MidiBufferHelpers::getEventTime (data) < samplePosition)
data += MidiBufferHelpers::getEventTotalSize (data);
}
bool MidiBuffer::Iterator::getNextEvent (const uint8* &midiData, int& numBytes, int& samplePosition) noexcept
{
if (data >= buffer.data.end())
return false;
samplePosition = MidiBufferHelpers::getEventTime (data);
const int itemSize = MidiBufferHelpers::getEventDataSize (data);
numBytes = itemSize;
midiData = data + sizeof (int32) + sizeof (uint16);
data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize;
return true;
}
bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePosition) noexcept
{
if (data >= buffer.data.end())
return false;
samplePosition = MidiBufferHelpers::getEventTime (data);
const int itemSize = MidiBufferHelpers::getEventDataSize (data);
result = MidiMessage (data + sizeof (int32) + sizeof (uint16), itemSize, samplePosition);
data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize;
return true;
}

+ 241
- 0
source/modules/juce_audio_graph/midi/juce_MidiBuffer.h View File

@@ -0,0 +1,241 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_MIDIBUFFER_H_INCLUDED
#define JUCE_MIDIBUFFER_H_INCLUDED
//==============================================================================
/**
Holds a sequence of time-stamped midi events.
Analogous to the AudioSampleBuffer, this holds a set of midi events with
integer time-stamps. The buffer is kept sorted in order of the time-stamps.
If you're working with a sequence of midi events that may need to be manipulated
or read/written to a midi file, then MidiMessageSequence is probably a more
appropriate container. MidiBuffer is designed for lower-level streams of raw
midi data.
@see MidiMessage
*/
class JUCE_API MidiBuffer
{
public:
//==============================================================================
/** Creates an empty MidiBuffer. */
MidiBuffer() noexcept;
/** Creates a MidiBuffer containing a single midi message. */
explicit MidiBuffer (const MidiMessage& message) noexcept;
/** Creates a copy of another MidiBuffer. */
MidiBuffer (const MidiBuffer&) noexcept;
/** Makes a copy of another MidiBuffer. */
MidiBuffer& operator= (const MidiBuffer&) noexcept;
/** Destructor */
~MidiBuffer();
//==============================================================================
/** Removes all events from the buffer. */
void clear() noexcept;
/** Removes all events between two times from the buffer.
All events for which (start <= event position < start + numSamples) will
be removed.
*/
void clear (int start, int numSamples);
/** Returns true if the buffer is empty.
To actually retrieve the events, use a MidiBuffer::Iterator object
*/
bool isEmpty() const noexcept;
/** Counts the number of events in the buffer.
This is actually quite a slow operation, as it has to iterate through all
the events, so you might prefer to call isEmpty() if that's all you need
to know.
*/
int getNumEvents() const noexcept;
/** Adds an event to the buffer.
The sample number will be used to determine the position of the event in
the buffer, which is always kept sorted. The MidiMessage's timestamp is
ignored.
If an event is added whose sample position is the same as one or more events
already in the buffer, the new event will be placed after the existing ones.
To retrieve events, use a MidiBuffer::Iterator object
*/
void addEvent (const MidiMessage& midiMessage, int sampleNumber);
/** Adds an event to the buffer from raw midi data.
The sample number will be used to determine the position of the event in
the buffer, which is always kept sorted.
If an event is added whose sample position is the same as one or more events
already in the buffer, the new event will be placed after the existing ones.
The event data will be inspected to calculate the number of bytes in length that
the midi event really takes up, so maxBytesOfMidiData may be longer than the data
that actually gets stored. E.g. if you pass in a note-on and a length of 4 bytes,
it'll actually only store 3 bytes. If the midi data is invalid, it might not
add an event at all.
To retrieve events, use a MidiBuffer::Iterator object
*/
void addEvent (const void* rawMidiData,
int maxBytesOfMidiData,
int sampleNumber);
/** Adds some events from another buffer to this one.
@param otherBuffer the buffer containing the events you want to add
@param startSample the lowest sample number in the source buffer for which
events should be added. Any source events whose timestamp is
less than this will be ignored
@param numSamples the valid range of samples from the source buffer for which
events should be added - i.e. events in the source buffer whose
timestamp is greater than or equal to (startSample + numSamples)
will be ignored. If this value is less than 0, all events after
startSample will be taken.
@param sampleDeltaToAdd a value which will be added to the source timestamps of the events
that are added to this buffer
*/
void addEvents (const MidiBuffer& otherBuffer,
int startSample,
int numSamples,
int sampleDeltaToAdd);
/** Returns the sample number of the first event in the buffer.
If the buffer's empty, this will just return 0.
*/
int getFirstEventTime() const noexcept;
/** Returns the sample number of the last event in the buffer.
If the buffer's empty, this will just return 0.
*/
int getLastEventTime() const noexcept;
//==============================================================================
/** Exchanges the contents of this buffer with another one.
This is a quick operation, because no memory allocating or copying is done, it
just swaps the internal state of the two buffers.
*/
void swapWith (MidiBuffer&) noexcept;
/** Preallocates some memory for the buffer to use.
This helps to avoid needing to reallocate space when the buffer has messages
added to it.
*/
void ensureSize (size_t minimumNumBytes);
//==============================================================================
/**
Used to iterate through the events in a MidiBuffer.
Note that altering the buffer while an iterator is using it isn't a
safe operation.
@see MidiBuffer
*/
class JUCE_API Iterator
{
public:
//==============================================================================
/** Creates an Iterator for this MidiBuffer. */
Iterator (const MidiBuffer&) noexcept;
/** Destructor. */
~Iterator() noexcept;
//==============================================================================
/** Repositions the iterator so that the next event retrieved will be the first
one whose sample position is at greater than or equal to the given position.
*/
void setNextSamplePosition (int samplePosition) noexcept;
/** Retrieves a copy of the next event from the buffer.
@param result on return, this will be the message. The MidiMessage's timestamp
is set to the same value as samplePosition.
@param samplePosition on return, this will be the position of the event, as a
sample index in the buffer
@returns true if an event was found, or false if the iterator has reached
the end of the buffer
*/
bool getNextEvent (MidiMessage& result,
int& samplePosition) noexcept;
/** Retrieves the next event from the buffer.
@param midiData on return, this pointer will be set to a block of data containing
the midi message. Note that to make it fast, this is a pointer
directly into the MidiBuffer's internal data, so is only valid
temporarily until the MidiBuffer is altered.
@param numBytesOfMidiData on return, this is the number of bytes of data used by the
midi message
@param samplePosition on return, this will be the position of the event, as a
sample index in the buffer
@returns true if an event was found, or false if the iterator has reached
the end of the buffer
*/
bool getNextEvent (const uint8* &midiData,
int& numBytesOfMidiData,
int& samplePosition) noexcept;
private:
//==============================================================================
const MidiBuffer& buffer;
const uint8* data;
JUCE_DECLARE_NON_COPYABLE (Iterator)
};
/** The raw data holding this buffer.
Obviously access to this data is provided at your own risk. Its internal format could
change in future, so don't write code that relies on it!
*/
Array<uint8> data;
private:
JUCE_LEAK_DETECTOR (MidiBuffer)
};
#endif // JUCE_MIDIBUFFER_H_INCLUDED

+ 1150
- 0
source/modules/juce_audio_graph/midi/juce_MidiMessage.cpp
File diff suppressed because it is too large
View File


+ 940
- 0
source/modules/juce_audio_graph/midi/juce_MidiMessage.h View File

@@ -0,0 +1,940 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_MIDIMESSAGE_H_INCLUDED
#define JUCE_MIDIMESSAGE_H_INCLUDED
//==============================================================================
/**
Encapsulates a MIDI message.
@see MidiMessageSequence, MidiOutput, MidiInput
*/
class JUCE_API MidiMessage
{
public:
//==============================================================================
/** Creates a 3-byte short midi message.
@param byte1 message byte 1
@param byte2 message byte 2
@param byte3 message byte 3
@param timeStamp the time to give the midi message - this value doesn't
use any particular units, so will be application-specific
*/
MidiMessage (int byte1, int byte2, int byte3, double timeStamp = 0) noexcept;
/** Creates a 2-byte short midi message.
@param byte1 message byte 1
@param byte2 message byte 2
@param timeStamp the time to give the midi message - this value doesn't
use any particular units, so will be application-specific
*/
MidiMessage (int byte1, int byte2, double timeStamp = 0) noexcept;
/** Creates a 1-byte short midi message.
@param byte1 message byte 1
@param timeStamp the time to give the midi message - this value doesn't
use any particular units, so will be application-specific
*/
MidiMessage (int byte1, double timeStamp = 0) noexcept;
/** Creates a midi message from a block of data. */
MidiMessage (const void* data, int numBytes, double timeStamp = 0);
/** Reads the next midi message from some data.
This will read as many bytes from a data stream as it needs to make a
complete message, and will return the number of bytes it used. This lets
you read a sequence of midi messages from a file or stream.
@param data the data to read from
@param maxBytesToUse the maximum number of bytes it's allowed to read
@param numBytesUsed returns the number of bytes that were actually needed
@param lastStatusByte in a sequence of midi messages, the initial byte
can be dropped from a message if it's the same as the
first byte of the previous message, so this lets you
supply the byte to use if the first byte of the message
has in fact been dropped.
@param timeStamp the time to give the midi message - this value doesn't
use any particular units, so will be application-specific
@param sysexHasEmbeddedLength when reading sysexes, this flag indicates whether
to expect the data to begin with a variable-length field
indicating its size
*/
MidiMessage (const void* data, int maxBytesToUse,
int& numBytesUsed, uint8 lastStatusByte,
double timeStamp = 0,
bool sysexHasEmbeddedLength = true);
/** Creates an active-sense message.
Since the MidiMessage has to contain a valid message, this default constructor
just initialises it with an empty sysex message.
*/
MidiMessage() noexcept;
/** Creates a copy of another midi message. */
MidiMessage (const MidiMessage&);
/** Creates a copy of another midi message, with a different timestamp. */
MidiMessage (const MidiMessage&, double newTimeStamp);
/** Destructor. */
~MidiMessage() noexcept;
/** Copies this message from another one. */
MidiMessage& operator= (const MidiMessage& other);
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
MidiMessage (MidiMessage&&) noexcept;
MidiMessage& operator= (MidiMessage&&) noexcept;
#endif
//==============================================================================
/** Returns a pointer to the raw midi data.
@see getRawDataSize
*/
const uint8* getRawData() const noexcept { return getData(); }
/** Returns the number of bytes of data in the message.
@see getRawData
*/
int getRawDataSize() const noexcept { return size; }
//==============================================================================
/** Returns a human-readable description of the midi message as a string,
for example "Note On C#3 Velocity 120 Channel 1".
*/
String getDescription() const;
//==============================================================================
/** Returns the timestamp associated with this message.
The exact meaning of this time and its units will vary, as messages are used in
a variety of different contexts.
If you're getting the message from a midi file, this could be a time in seconds, or
a number of ticks - see MidiFile::convertTimestampTicksToSeconds().
If the message is being used in a MidiBuffer, it might indicate the number of
audio samples from the start of the buffer.
If the message was created by a MidiInput, see MidiInputCallback::handleIncomingMidiMessage()
for details of the way that it initialises this value.
@see setTimeStamp, addToTimeStamp
*/
double getTimeStamp() const noexcept { return timeStamp; }
/** Changes the message's associated timestamp.
The units for the timestamp will be application-specific - see the notes for getTimeStamp().
@see addToTimeStamp, getTimeStamp
*/
void setTimeStamp (double newTimestamp) noexcept { timeStamp = newTimestamp; }
/** Adds a value to the message's timestamp.
The units for the timestamp will be application-specific.
*/
void addToTimeStamp (double delta) noexcept { timeStamp += delta; }
//==============================================================================
/** Returns the midi channel associated with the message.
@returns a value 1 to 16 if the message has a channel, or 0 if it hasn't (e.g.
if it's a sysex)
@see isForChannel, setChannel
*/
int getChannel() const noexcept;
/** Returns true if the message applies to the given midi channel.
@param channelNumber the channel number to look for, in the range 1 to 16
@see getChannel, setChannel
*/
bool isForChannel (int channelNumber) const noexcept;
/** Changes the message's midi channel.
This won't do anything for non-channel messages like sysexes.
@param newChannelNumber the channel number to change it to, in the range 1 to 16
*/
void setChannel (int newChannelNumber) noexcept;
//==============================================================================
/** Returns true if this is a system-exclusive message.
*/
bool isSysEx() const noexcept;
/** Returns a pointer to the sysex data inside the message.
If this event isn't a sysex event, it'll return 0.
@see getSysExDataSize
*/
const uint8* getSysExData() const noexcept;
/** Returns the size of the sysex data.
This value excludes the 0xf0 header byte and the 0xf7 at the end.
@see getSysExData
*/
int getSysExDataSize() const noexcept;
//==============================================================================
/** Returns true if this message is a 'key-down' event.
@param returnTrueForVelocity0 if true, then if this event is a note-on with
velocity 0, it will still be considered to be a note-on and the
method will return true. If returnTrueForVelocity0 is false, then
if this is a note-on event with velocity 0, it'll be regarded as
a note-off, and the method will return false
@see isNoteOff, getNoteNumber, getVelocity, noteOn
*/
bool isNoteOn (bool returnTrueForVelocity0 = false) const noexcept;
/** Creates a key-down message (using a floating-point velocity).
@param channel the midi channel, in the range 1 to 16
@param noteNumber the key number, 0 to 127
@param velocity in the range 0 to 1.0
@see isNoteOn
*/
static MidiMessage noteOn (int channel, int noteNumber, float velocity) noexcept;
/** Creates a key-down message (using an integer velocity).
@param channel the midi channel, in the range 1 to 16
@param noteNumber the key number, 0 to 127
@param velocity in the range 0 to 127
@see isNoteOn
*/
static MidiMessage noteOn (int channel, int noteNumber, uint8 velocity) noexcept;
/** Returns true if this message is a 'key-up' event.
If returnTrueForNoteOnVelocity0 is true, then his will also return true
for a note-on event with a velocity of 0.
@see isNoteOn, getNoteNumber, getVelocity, noteOff
*/
bool isNoteOff (bool returnTrueForNoteOnVelocity0 = true) const noexcept;
/** Creates a key-up message.
@param channel the midi channel, in the range 1 to 16
@param noteNumber the key number, 0 to 127
@param velocity in the range 0 to 1.0
@see isNoteOff
*/
static MidiMessage noteOff (int channel, int noteNumber, float velocity) noexcept;
/** Creates a key-up message.
@param channel the midi channel, in the range 1 to 16
@param noteNumber the key number, 0 to 127
@param velocity in the range 0 to 127
@see isNoteOff
*/
static MidiMessage noteOff (int channel, int noteNumber, uint8 velocity) noexcept;
/** Creates a key-up message.
@param channel the midi channel, in the range 1 to 16
@param noteNumber the key number, 0 to 127
@see isNoteOff
*/
static MidiMessage noteOff (int channel, int noteNumber) noexcept;
/** Returns true if this message is a 'key-down' or 'key-up' event.
@see isNoteOn, isNoteOff
*/
bool isNoteOnOrOff() const noexcept;
/** Returns the midi note number for note-on and note-off messages.
If the message isn't a note-on or off, the value returned is undefined.
@see isNoteOff, getMidiNoteName, getMidiNoteInHertz, setNoteNumber
*/
int getNoteNumber() const noexcept;
/** Changes the midi note number of a note-on or note-off message.
If the message isn't a note on or off, this will do nothing.
*/
void setNoteNumber (int newNoteNumber) noexcept;
//==============================================================================
/** Returns the velocity of a note-on or note-off message.
The value returned will be in the range 0 to 127.
If the message isn't a note-on or off event, it will return 0.
@see getFloatVelocity
*/
uint8 getVelocity() const noexcept;
/** Returns the velocity of a note-on or note-off message.
The value returned will be in the range 0 to 1.0
If the message isn't a note-on or off event, it will return 0.
@see getVelocity, setVelocity
*/
float getFloatVelocity() const noexcept;
/** Changes the velocity of a note-on or note-off message.
If the message isn't a note on or off, this will do nothing.
@param newVelocity the new velocity, in the range 0 to 1.0
@see getFloatVelocity, multiplyVelocity
*/
void setVelocity (float newVelocity) noexcept;
/** Multiplies the velocity of a note-on or note-off message by a given amount.
If the message isn't a note on or off, this will do nothing.
@param scaleFactor the value by which to multiply the velocity
@see setVelocity
*/
void multiplyVelocity (float scaleFactor) noexcept;
//==============================================================================
/** Returns true if this message is a 'sustain pedal down' controller message. */
bool isSustainPedalOn() const noexcept;
/** Returns true if this message is a 'sustain pedal up' controller message. */
bool isSustainPedalOff() const noexcept;
/** Returns true if this message is a 'sostenuto pedal down' controller message. */
bool isSostenutoPedalOn() const noexcept;
/** Returns true if this message is a 'sostenuto pedal up' controller message. */
bool isSostenutoPedalOff() const noexcept;
/** Returns true if this message is a 'soft pedal down' controller message. */
bool isSoftPedalOn() const noexcept;
/** Returns true if this message is a 'soft pedal up' controller message. */
bool isSoftPedalOff() const noexcept;
//==============================================================================
/** Returns true if the message is a program (patch) change message.
@see getProgramChangeNumber, getGMInstrumentName
*/
bool isProgramChange() const noexcept;
/** Returns the new program number of a program change message.
If the message isn't a program change, the value returned is undefined.
@see isProgramChange, getGMInstrumentName
*/
int getProgramChangeNumber() const noexcept;
/** Creates a program-change message.
@param channel the midi channel, in the range 1 to 16
@param programNumber the midi program number, 0 to 127
@see isProgramChange, getGMInstrumentName
*/
static MidiMessage programChange (int channel, int programNumber) noexcept;
//==============================================================================
/** Returns true if the message is a pitch-wheel move.
@see getPitchWheelValue, pitchWheel
*/
bool isPitchWheel() const noexcept;
/** Returns the pitch wheel position from a pitch-wheel move message.
The value returned is a 14-bit number from 0 to 0x3fff, indicating the wheel position.
If called for messages which aren't pitch wheel events, the number returned will be
nonsense.
@see isPitchWheel
*/
int getPitchWheelValue() const noexcept;
/** Creates a pitch-wheel move message.
@param channel the midi channel, in the range 1 to 16
@param position the wheel position, in the range 0 to 16383
@see isPitchWheel
*/
static MidiMessage pitchWheel (int channel, int position) noexcept;
//==============================================================================
/** Returns true if the message is an aftertouch event.
For aftertouch events, use the getNoteNumber() method to find out the key
that it applies to, and getAftertouchValue() to find out the amount. Use
getChannel() to find out the channel.
@see getAftertouchValue, getNoteNumber
*/
bool isAftertouch() const noexcept;
/** Returns the amount of aftertouch from an aftertouch messages.
The value returned is in the range 0 to 127, and will be nonsense for messages
other than aftertouch messages.
@see isAftertouch
*/
int getAfterTouchValue() const noexcept;
/** Creates an aftertouch message.
@param channel the midi channel, in the range 1 to 16
@param noteNumber the key number, 0 to 127
@param aftertouchAmount the amount of aftertouch, 0 to 127
@see isAftertouch
*/
static MidiMessage aftertouchChange (int channel,
int noteNumber,
int aftertouchAmount) noexcept;
/** Returns true if the message is a channel-pressure change event.
This is like aftertouch, but common to the whole channel rather than a specific
note. Use getChannelPressureValue() to find out the pressure, and getChannel()
to find out the channel.
@see channelPressureChange
*/
bool isChannelPressure() const noexcept;
/** Returns the pressure from a channel pressure change message.
@returns the pressure, in the range 0 to 127
@see isChannelPressure, channelPressureChange
*/
int getChannelPressureValue() const noexcept;
/** Creates a channel-pressure change event.
@param channel the midi channel: 1 to 16
@param pressure the pressure, 0 to 127
@see isChannelPressure
*/
static MidiMessage channelPressureChange (int channel, int pressure) noexcept;
//==============================================================================
/** Returns true if this is a midi controller message.
@see getControllerNumber, getControllerValue, controllerEvent
*/
bool isController() const noexcept;
/** Returns the controller number of a controller message.
The name of the controller can be looked up using the getControllerName() method.
Note that the value returned is invalid for messages that aren't controller changes.
@see isController, getControllerName, getControllerValue
*/
int getControllerNumber() const noexcept;
/** Returns the controller value from a controller message.
A value 0 to 127 is returned to indicate the new controller position.
Note that the value returned is invalid for messages that aren't controller changes.
@see isController, getControllerNumber
*/
int getControllerValue() const noexcept;
/** Returns true if this message is a controller message and if it has the specified
controller type.
*/
bool isControllerOfType (int controllerType) const noexcept;
/** Creates a controller message.
@param channel the midi channel, in the range 1 to 16
@param controllerType the type of controller
@param value the controller value
@see isController
*/
static MidiMessage controllerEvent (int channel,
int controllerType,
int value) noexcept;
/** Checks whether this message is an all-notes-off message.
@see allNotesOff
*/
bool isAllNotesOff() const noexcept;
/** Checks whether this message is an all-sound-off message.
@see allSoundOff
*/
bool isAllSoundOff() const noexcept;
/** Creates an all-notes-off message.
@param channel the midi channel, in the range 1 to 16
@see isAllNotesOff
*/
static MidiMessage allNotesOff (int channel) noexcept;
/** Creates an all-sound-off message.
@param channel the midi channel, in the range 1 to 16
@see isAllSoundOff
*/
static MidiMessage allSoundOff (int channel) noexcept;
/** Creates an all-controllers-off message.
@param channel the midi channel, in the range 1 to 16
*/
static MidiMessage allControllersOff (int channel) noexcept;
//==============================================================================
/** Returns true if this event is a meta-event.
Meta-events are things like tempo changes, track names, etc.
@see getMetaEventType, isTrackMetaEvent, isEndOfTrackMetaEvent,
isTextMetaEvent, isTrackNameEvent, isTempoMetaEvent, isTimeSignatureMetaEvent,
isKeySignatureMetaEvent, isMidiChannelMetaEvent
*/
bool isMetaEvent() const noexcept;
/** Returns a meta-event's type number.
If the message isn't a meta-event, this will return -1.
@see isMetaEvent, isTrackMetaEvent, isEndOfTrackMetaEvent,
isTextMetaEvent, isTrackNameEvent, isTempoMetaEvent, isTimeSignatureMetaEvent,
isKeySignatureMetaEvent, isMidiChannelMetaEvent
*/
int getMetaEventType() const noexcept;
/** Returns a pointer to the data in a meta-event.
@see isMetaEvent, getMetaEventLength
*/
const uint8* getMetaEventData() const noexcept;
/** Returns the length of the data for a meta-event.
@see isMetaEvent, getMetaEventData
*/
int getMetaEventLength() const noexcept;
//==============================================================================
/** Returns true if this is a 'track' meta-event. */
bool isTrackMetaEvent() const noexcept;
/** Returns true if this is an 'end-of-track' meta-event. */
bool isEndOfTrackMetaEvent() const noexcept;
/** Creates an end-of-track meta-event.
@see isEndOfTrackMetaEvent
*/
static MidiMessage endOfTrack() noexcept;
/** Returns true if this is an 'track name' meta-event.
You can use the getTextFromTextMetaEvent() method to get the track's name.
*/
bool isTrackNameEvent() const noexcept;
/** Returns true if this is a 'text' meta-event.
@see getTextFromTextMetaEvent
*/
bool isTextMetaEvent() const noexcept;
/** Returns the text from a text meta-event.
@see isTextMetaEvent
*/
String getTextFromTextMetaEvent() const;
/** Creates a text meta-event. */
static MidiMessage textMetaEvent (int type, StringRef text);
//==============================================================================
/** Returns true if this is a 'tempo' meta-event.
@see getTempoMetaEventTickLength, getTempoSecondsPerQuarterNote
*/
bool isTempoMetaEvent() const noexcept;
/** Returns the tick length from a tempo meta-event.
@param timeFormat the 16-bit time format value from the midi file's header.
@returns the tick length (in seconds).
@see isTempoMetaEvent
*/
double getTempoMetaEventTickLength (short timeFormat) const noexcept;
/** Calculates the seconds-per-quarter-note from a tempo meta-event.
@see isTempoMetaEvent, getTempoMetaEventTickLength
*/
double getTempoSecondsPerQuarterNote() const noexcept;
/** Creates a tempo meta-event.
@see isTempoMetaEvent
*/
static MidiMessage tempoMetaEvent (int microsecondsPerQuarterNote) noexcept;
//==============================================================================
/** Returns true if this is a 'time-signature' meta-event.
@see getTimeSignatureInfo
*/
bool isTimeSignatureMetaEvent() const noexcept;
/** Returns the time-signature values from a time-signature meta-event.
@see isTimeSignatureMetaEvent
*/
void getTimeSignatureInfo (int& numerator, int& denominator) const noexcept;
/** Creates a time-signature meta-event.
@see isTimeSignatureMetaEvent
*/
static MidiMessage timeSignatureMetaEvent (int numerator, int denominator);
//==============================================================================
/** Returns true if this is a 'key-signature' meta-event.
@see getKeySignatureNumberOfSharpsOrFlats, isKeySignatureMajorKey
*/
bool isKeySignatureMetaEvent() const noexcept;
/** Returns the key from a key-signature meta-event.
This method must only be called if isKeySignatureMetaEvent() is true.
A positive number here indicates the number of sharps in the key signature,
and a negative number indicates a number of flats. So e.g. 3 = F# + C# + G#,
-2 = Bb + Eb
@see isKeySignatureMetaEvent, isKeySignatureMajorKey
*/
int getKeySignatureNumberOfSharpsOrFlats() const noexcept;
/** Returns true if this key-signature event is major, or false if it's minor.
This method must only be called if isKeySignatureMetaEvent() is true.
*/
bool isKeySignatureMajorKey() const noexcept;
/** Creates a key-signature meta-event.
@param numberOfSharpsOrFlats if positive, this indicates the number of sharps
in the key; if negative, the number of flats
@param isMinorKey if true, the key is minor; if false, it is major
@see isKeySignatureMetaEvent
*/
static MidiMessage keySignatureMetaEvent (int numberOfSharpsOrFlats, bool isMinorKey);
//==============================================================================
/** Returns true if this is a 'channel' meta-event.
A channel meta-event specifies the midi channel that should be used
for subsequent meta-events.
@see getMidiChannelMetaEventChannel
*/
bool isMidiChannelMetaEvent() const noexcept;
/** Returns the channel number from a channel meta-event.
@returns the channel, in the range 1 to 16.
@see isMidiChannelMetaEvent
*/
int getMidiChannelMetaEventChannel() const noexcept;
/** Creates a midi channel meta-event.
@param channel the midi channel, in the range 1 to 16
@see isMidiChannelMetaEvent
*/
static MidiMessage midiChannelMetaEvent (int channel) noexcept;
//==============================================================================
/** Returns true if this is an active-sense message. */
bool isActiveSense() const noexcept;
//==============================================================================
/** Returns true if this is a midi start event.
@see midiStart
*/
bool isMidiStart() const noexcept;
/** Creates a midi start event. */
static MidiMessage midiStart() noexcept;
/** Returns true if this is a midi continue event.
@see midiContinue
*/
bool isMidiContinue() const noexcept;
/** Creates a midi continue event. */
static MidiMessage midiContinue() noexcept;
/** Returns true if this is a midi stop event.
@see midiStop
*/
bool isMidiStop() const noexcept;
/** Creates a midi stop event. */
static MidiMessage midiStop() noexcept;
/** Returns true if this is a midi clock event.
@see midiClock, songPositionPointer
*/
bool isMidiClock() const noexcept;
/** Creates a midi clock event. */
static MidiMessage midiClock() noexcept;
/** Returns true if this is a song-position-pointer message.
@see getSongPositionPointerMidiBeat, songPositionPointer
*/
bool isSongPositionPointer() const noexcept;
/** Returns the midi beat-number of a song-position-pointer message.
@see isSongPositionPointer, songPositionPointer
*/
int getSongPositionPointerMidiBeat() const noexcept;
/** Creates a song-position-pointer message.
The position is a number of midi beats from the start of the song, where 1 midi
beat is 6 midi clocks, and there are 24 midi clocks in a quarter-note. So there
are 4 midi beats in a quarter-note.
@see isSongPositionPointer, getSongPositionPointerMidiBeat
*/
static MidiMessage songPositionPointer (int positionInMidiBeats) noexcept;
//==============================================================================
/** Returns true if this is a quarter-frame midi timecode message.
@see quarterFrame, getQuarterFrameSequenceNumber, getQuarterFrameValue
*/
bool isQuarterFrame() const noexcept;
/** Returns the sequence number of a quarter-frame midi timecode message.
This will be a value between 0 and 7.
@see isQuarterFrame, getQuarterFrameValue, quarterFrame
*/
int getQuarterFrameSequenceNumber() const noexcept;
/** Returns the value from a quarter-frame message.
This will be the lower nybble of the message's data-byte, a value between 0 and 15
*/
int getQuarterFrameValue() const noexcept;
/** Creates a quarter-frame MTC message.
@param sequenceNumber a value 0 to 7 for the upper nybble of the message's data byte
@param value a value 0 to 15 for the lower nybble of the message's data byte
*/
static MidiMessage quarterFrame (int sequenceNumber, int value) noexcept;
/** SMPTE timecode types.
Used by the getFullFrameParameters() and fullFrame() methods.
*/
enum SmpteTimecodeType
{
fps24 = 0,
fps25 = 1,
fps30drop = 2,
fps30 = 3
};
/** Returns true if this is a full-frame midi timecode message. */
bool isFullFrame() const noexcept;
/** Extracts the timecode information from a full-frame midi timecode message.
You should only call this on messages where you've used isFullFrame() to
check that they're the right kind.
*/
void getFullFrameParameters (int& hours,
int& minutes,
int& seconds,
int& frames,
SmpteTimecodeType& timecodeType) const noexcept;
/** Creates a full-frame MTC message. */
static MidiMessage fullFrame (int hours,
int minutes,
int seconds,
int frames,
SmpteTimecodeType timecodeType);
//==============================================================================
/** Types of MMC command.
@see isMidiMachineControlMessage, getMidiMachineControlCommand, midiMachineControlCommand
*/
enum MidiMachineControlCommand
{
mmc_stop = 1,
mmc_play = 2,
mmc_deferredplay = 3,
mmc_fastforward = 4,
mmc_rewind = 5,
mmc_recordStart = 6,
mmc_recordStop = 7,
mmc_pause = 9
};
/** Checks whether this is an MMC message.
If it is, you can use the getMidiMachineControlCommand() to find out its type.
*/
bool isMidiMachineControlMessage() const noexcept;
/** For an MMC message, this returns its type.
Make sure it's actually an MMC message with isMidiMachineControlMessage() before
calling this method.
*/
MidiMachineControlCommand getMidiMachineControlCommand() const noexcept;
/** Creates an MMC message. */
static MidiMessage midiMachineControlCommand (MidiMachineControlCommand command);
/** Checks whether this is an MMC "goto" message.
If it is, the parameters passed-in are set to the time that the message contains.
@see midiMachineControlGoto
*/
bool isMidiMachineControlGoto (int& hours,
int& minutes,
int& seconds,
int& frames) const noexcept;
/** Creates an MMC "goto" message.
This messages tells the device to go to a specific frame.
@see isMidiMachineControlGoto
*/
static MidiMessage midiMachineControlGoto (int hours,
int minutes,
int seconds,
int frames);
//==============================================================================
/** Creates a master-volume change message.
@param volume the volume, 0 to 1.0
*/
static MidiMessage masterVolume (float volume);
//==============================================================================
/** Creates a system-exclusive message.
The data passed in is wrapped with header and tail bytes of 0xf0 and 0xf7.
*/
static MidiMessage createSysExMessage (const void* sysexData,
int dataSize);
//==============================================================================
/** Reads a midi variable-length integer.
@param data the data to read the number from
@param numBytesUsed on return, this will be set to the number of bytes that were read
*/
static int readVariableLengthVal (const uint8* data,
int& numBytesUsed) noexcept;
/** Based on the first byte of a short midi message, this uses a lookup table
to return the message length (either 1, 2, or 3 bytes).
The value passed in must be 0x80 or higher.
*/
static int getMessageLengthFromFirstByte (uint8 firstByte) noexcept;
//==============================================================================
/** Returns the name of a midi note number.
E.g "C", "D#", etc.
@param noteNumber the midi note number, 0 to 127
@param useSharps if true, sharpened notes are used, e.g. "C#", otherwise
they'll be flattened, e.g. "Db"
@param includeOctaveNumber if true, the octave number will be appended to the string,
e.g. "C#4"
@param octaveNumForMiddleC if an octave number is being appended, this indicates the
number that will be used for middle C's octave
@see getMidiNoteInHertz
*/
static String getMidiNoteName (int noteNumber,
bool useSharps,
bool includeOctaveNumber,
int octaveNumForMiddleC);
/** Returns the frequency of a midi note number.
The frequencyOfA parameter is an optional frequency for 'A', normally 440-444Hz for concert pitch.
@see getMidiNoteName
*/
static double getMidiNoteInHertz (int noteNumber, double frequencyOfA = 440.0) noexcept;
/** Returns true if the given midi note number is a black key. */
static bool isMidiNoteBlack (int noteNumber) noexcept;
/** Returns the standard name of a GM instrument, or nullptr if unknown for this index.
@param midiInstrumentNumber the program number 0 to 127
@see getProgramChangeNumber
*/
static const char* getGMInstrumentName (int midiInstrumentNumber);
/** Returns the name of a bank of GM instruments, or nullptr if unknown for this bank number.
@param midiBankNumber the bank, 0 to 15
*/
static const char* getGMInstrumentBankName (int midiBankNumber);
/** Returns the standard name of a channel 10 percussion sound, or nullptr if unknown for this note number.
@param midiNoteNumber the key number, 35 to 81
*/
static const char* getRhythmInstrumentName (int midiNoteNumber);
/** Returns the name of a controller type number, or nullptr if unknown for this controller number.
@see getControllerNumber
*/
static const char* getControllerName (int controllerNumber);
/** Converts a floating-point value between 0 and 1 to a MIDI 7-bit value between 0 and 127. */
static uint8 floatValueToMidiByte (float valueBetween0and1) noexcept;
/** Converts a pitchbend value in semitones to a MIDI 14-bit pitchwheel position value. */
static uint16 pitchbendToPitchwheelPos (float pitchbendInSemitones,
float pitchbendRangeInSemitones) noexcept;
private:
//==============================================================================
#ifndef DOXYGEN
union PackedData
{
uint8* allocatedData;
uint8 asBytes[sizeof (uint8*)];
};
PackedData packedData;
double timeStamp;
int size;
#endif
inline bool isHeapAllocated() const noexcept { return size > (int) sizeof (packedData); }
inline uint8* getData() const noexcept { return isHeapAllocated() ? packedData.allocatedData : (uint8*) packedData.asBytes; }
uint8* allocateSpace (int);
};
#endif // JUCE_MIDIMESSAGE_H_INCLUDED

+ 144
- 0
source/modules/juce_audio_graph/processors/juce_AudioPlayHead.h View File

@@ -0,0 +1,144 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_AUDIOPLAYHEAD_H_INCLUDED
#define JUCE_AUDIOPLAYHEAD_H_INCLUDED
//==============================================================================
/**
A subclass of AudioPlayHead can supply information about the position and
status of a moving play head during audio playback.
One of these can be supplied to an AudioProcessor object so that it can find
out about the position of the audio that it is rendering.
@see AudioProcessor::setPlayHead, AudioProcessor::getPlayHead
*/
class JUCE_API AudioPlayHead
{
protected:
//==============================================================================
AudioPlayHead() {}
public:
virtual ~AudioPlayHead() {}
//==============================================================================
/** Frame rate types. */
enum FrameRateType
{
fps24 = 0,
fps25 = 1,
fps2997 = 2,
fps30 = 3,
fps2997drop = 4,
fps30drop = 5,
fpsUnknown = 99
};
//==============================================================================
/** This structure is filled-in by the AudioPlayHead::getCurrentPosition() method.
*/
struct JUCE_API CurrentPositionInfo
{
/** The tempo in BPM */
double bpm;
/** Time signature numerator, e.g. the 3 of a 3/4 time sig */
int timeSigNumerator;
/** Time signature denominator, e.g. the 4 of a 3/4 time sig */
int timeSigDenominator;
/** The current play position, in samples from the start of the edit. */
int64 timeInSamples;
/** The current play position, in seconds from the start of the edit. */
double timeInSeconds;
/** For timecode, the position of the start of the edit, in seconds from 00:00:00:00. */
double editOriginTime;
/** The current play position, in pulses-per-quarter-note. */
double ppqPosition;
/** The position of the start of the last bar, in pulses-per-quarter-note.
This is the time from the start of the edit to the start of the current
bar, in ppq units.
Note - this value may be unavailable on some hosts, e.g. Pro-Tools. If
it's not available, the value will be 0.
*/
double ppqPositionOfLastBarStart;
/** The video frame rate, if applicable. */
FrameRateType frameRate;
/** True if the transport is currently playing. */
bool isPlaying;
/** True if the transport is currently recording.
(When isRecording is true, then isPlaying will also be true).
*/
bool isRecording;
/** The current cycle start position in pulses-per-quarter-note.
Note that not all hosts or plugin formats may provide this value.
@see isLooping
*/
double ppqLoopStart;
/** The current cycle end position in pulses-per-quarter-note.
Note that not all hosts or plugin formats may provide this value.
@see isLooping
*/
double ppqLoopEnd;
/** True if the transport is currently looping. */
bool isLooping;
//==============================================================================
bool operator== (const CurrentPositionInfo& other) const noexcept;
bool operator!= (const CurrentPositionInfo& other) const noexcept;
void resetToDefault();
};
//==============================================================================
/** Fills-in the given structure with details about the transport's
position at the start of the current processing block. If this method returns
false then the current play head position is not available and the given
structure will be undefined.
You can ONLY call this from your processBlock() method! Calling it at other
times will produce undefined behaviour, as the host may not have any context
in which a time would make sense, and some hosts will almost certainly have
multithreading issues if it's not called on the audio thread.
*/
virtual bool getCurrentPosition (CurrentPositionInfo& result) = 0;
};
#endif // JUCE_AUDIOPLAYHEAD_H_INCLUDED

+ 124
- 0
source/modules/juce_audio_graph/processors/juce_AudioProcessor.cpp View File

@@ -0,0 +1,124 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
// #include "juce_AudioProcessor.h"
AudioProcessor::AudioProcessor()
{
cachedTotalIns = 0;
cachedTotalOuts = 0;
playHead = nullptr;
currentSampleRate = 0;
blockSize = 0;
latencySamples = 0;
suspended = false;
nonRealtime = false;
}
AudioProcessor::~AudioProcessor()
{
}
//==============================================================================
void AudioProcessor::setPlayHead (AudioPlayHead* const newPlayHead)
{
playHead = newPlayHead;
}
void AudioProcessor::setPlayConfigDetails (const int newNumIns,
const int newNumOuts,
const double newSampleRate,
const int newBlockSize)
{
cachedTotalIns = newNumIns;
cachedTotalOuts = newNumOuts;
setRateAndBufferSizeDetails (newSampleRate, newBlockSize);
}
void AudioProcessor::setRateAndBufferSizeDetails (double newSampleRate, int newBlockSize) noexcept
{
currentSampleRate = newSampleRate;
blockSize = newBlockSize;
}
//==============================================================================
void AudioProcessor::setNonRealtime (const bool newNonRealtime) noexcept
{
nonRealtime = newNonRealtime;
}
void AudioProcessor::setLatencySamples (const int newLatency)
{
if (latencySamples != newLatency)
latencySamples = newLatency;
}
void AudioProcessor::suspendProcessing (const bool shouldBeSuspended)
{
const CarlaRecursiveMutexLocker cml (callbackLock);
suspended = shouldBeSuspended;
}
void AudioProcessor::reset() {}
void AudioProcessor::processBypassed (AudioSampleBuffer& buffer, MidiBuffer&)
{
for (int ch = getTotalNumInputChannels(); ch < getTotalNumOutputChannels(); ++ch)
buffer.clear (ch, 0, buffer.getNumSamples());
}
void AudioProcessor::processBlockBypassed (AudioSampleBuffer& buffer, MidiBuffer& midi) { processBypassed (buffer, midi); }
//==============================================================================
bool AudioPlayHead::CurrentPositionInfo::operator== (const CurrentPositionInfo& other) const noexcept
{
return timeInSamples == other.timeInSamples
&& ppqPosition == other.ppqPosition
&& editOriginTime == other.editOriginTime
&& ppqPositionOfLastBarStart == other.ppqPositionOfLastBarStart
&& frameRate == other.frameRate
&& isPlaying == other.isPlaying
&& isRecording == other.isRecording
&& bpm == other.bpm
&& timeSigNumerator == other.timeSigNumerator
&& timeSigDenominator == other.timeSigDenominator
&& ppqLoopStart == other.ppqLoopStart
&& ppqLoopEnd == other.ppqLoopEnd
&& isLooping == other.isLooping;
}
bool AudioPlayHead::CurrentPositionInfo::operator!= (const CurrentPositionInfo& other) const noexcept
{
return ! operator== (other);
}
void AudioPlayHead::CurrentPositionInfo::resetToDefault()
{
zerostruct (*this);
timeSigNumerator = 4;
timeSigDenominator = 4;
bpm = 120;
}

+ 381
- 0
source/modules/juce_audio_graph/processors/juce_AudioProcessor.h View File

@@ -0,0 +1,381 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_AUDIOPROCESSOR_H_INCLUDED
#define JUCE_AUDIOPROCESSOR_H_INCLUDED
// #include "juce_AudioPlayHead.h"
//==============================================================================
/**
Base class for audio processing filters or plugins.
This is intended to act as a base class of audio filter that is general enough to
be wrapped as a VST, AU, RTAS, etc, or used internally.
It is also used by the plugin hosting code as the wrapper around an instance
of a loaded plugin.
Derive your filter class from this base class, and if you're building a plugin,
you should implement a global function called createPluginFilter() which creates
and returns a new instance of your subclass.
*/
class JUCE_API AudioProcessor
{
protected:
//==============================================================================
/** Constructor.
This constructor will create a main input and output bus which are diabled
by default. If you need more fine grain control then use the other
constructors.
*/
AudioProcessor();
public:
//==============================================================================
/** Destructor. */
virtual ~AudioProcessor();
//==============================================================================
/** Returns the name of this processor. */
virtual const String getName() const = 0;
//==============================================================================
/** Called before playback starts, to let the filter prepare itself.
The sample rate is the target sample rate, and will remain constant until
playback stops.
You can call getTotalNumInputChannels and getTotalNumOutputChannels
or query the busLayout member variable to find out the number of
channels your processBlock callback must process.
The maximumExpectedSamplesPerBlock value is a strong hint about the maximum
number of samples that will be provided in each block. You may want to use
this value to resize internal buffers. You should program defensively in
case a buggy host exceeds this value. The actual block sizes that the host
uses may be different each time the callback happens: completely variable
block sizes can be expected from some hosts.
@see busLayout, getTotalNumInputChannels, getTotalNumOutputChannels
*/
virtual void prepareToPlay (double sampleRate,
int maximumExpectedSamplesPerBlock) = 0;
/** Called after playback has stopped, to let the filter free up any resources it
no longer needs.
*/
virtual void releaseResources() = 0;
/** Renders the next block.
When this method is called, the buffer contains a number of channels which is
at least as great as the maximum number of input and output channels that
this filter is using. It will be filled with the filter's input data and
should be replaced with the filter's output.
So for example if your filter has a total of 2 input channels and 4 output
channels, then the buffer will contain 4 channels, the first two being filled
with the input data. Your filter should read these, do its processing, and
replace the contents of all 4 channels with its output.
Or if your filter has a total of 5 inputs and 2 outputs, the buffer will have 5
channels, all filled with data, and your filter should overwrite the first 2 of
these with its output. But be VERY careful not to write anything to the last 3
channels, as these might be mapped to memory that the host assumes is read-only!
If your plug-in has more than one input or output buses then the buffer passed
to the processBlock methods will contain a bundle of all channels of each bus.
Use AudiobusLayout::getBusBuffer to obtain an audio buffer for a
particular bus.
Note that if you have more outputs than inputs, then only those channels that
correspond to an input channel are guaranteed to contain sensible data - e.g.
in the case of 2 inputs and 4 outputs, the first two channels contain the input,
but the last two channels may contain garbage, so you should be careful not to
let this pass through without being overwritten or cleared.
Also note that the buffer may have more channels than are strictly necessary,
but you should only read/write from the ones that your filter is supposed to
be using.
The number of samples in these buffers is NOT guaranteed to be the same for every
callback, and may be more or less than the estimated value given to prepareToPlay().
Your code must be able to cope with variable-sized blocks, or you're going to get
clicks and crashes!
Also note that some hosts will occasionally decide to pass a buffer containing
zero samples, so make sure that your algorithm can deal with that!
If the filter is receiving a midi input, then the midiMessages array will be filled
with the midi messages for this block. Each message's timestamp will indicate the
message's time, as a number of samples from the start of the block.
Any messages left in the midi buffer when this method has finished are assumed to
be the filter's midi output. This means that your filter should be careful to
clear any incoming messages from the array if it doesn't want them to be passed-on.
Be very careful about what you do in this callback - it's going to be called by
the audio thread, so any kind of interaction with the UI is absolutely
out of the question. If you change a parameter in here and need to tell your UI to
update itself, the best way is probably to inherit from a ChangeBroadcaster, let
the UI components register as listeners, and then call sendChangeMessage() inside the
processBlock() method to send out an asynchronous message. You could also use
the AsyncUpdater class in a similar way.
@see AudiobusLayout::getBusBuffer
*/
virtual void processBlock (AudioSampleBuffer& buffer,
MidiBuffer& midiMessages) = 0;
/** Renders the next block when the processor is being bypassed.
The default implementation of this method will pass-through any incoming audio, but
you may override this method e.g. to add latency compensation to the data to match
the processor's latency characteristics. This will avoid situations where bypassing
will shift the signal forward in time, possibly creating pre-echo effects and odd timings.
Another use for this method would be to cross-fade or morph between the wet (not bypassed)
and dry (bypassed) signals.
*/
virtual void processBlockBypassed (AudioSampleBuffer& buffer,
MidiBuffer& midiMessages);
//==============================================================================
/** Returns the current AudioPlayHead object that should be used to find
out the state and position of the playhead.
You can ONLY call this from your processBlock() method! Calling it at other
times will produce undefined behaviour, as the host may not have any context
in which a time would make sense, and some hosts will almost certainly have
multithreading issues if it's not called on the audio thread.
The AudioPlayHead object that is returned can be used to get the details about
the time of the start of the block currently being processed. But do not
store this pointer or use it outside of the current audio callback, because
the host may delete or re-use it.
If the host can't or won't provide any time info, this will return nullptr.
*/
AudioPlayHead* getPlayHead() const noexcept { return playHead; }
//==============================================================================
/** Returns the total number of input channels.
This method will return the total number of input channels by accumulating
the number of channels on each input bus. The number of channels of the
buffer passed to your processBlock callback will be equivalent to either
getTotalNumInputChannels or getTotalNumOutputChannels - which ever
is greater.
Note that getTotalNumInputChannels is equivalent to
getMainBusNumInputChannels if your processor does not have any sidechains
or aux buses.
*/
int getTotalNumInputChannels() const noexcept { return cachedTotalIns; }
/** Returns the total number of output channels.
This method will return the total number of output channels by accumulating
the number of channels on each output bus. The number of channels of the
buffer passed to your processBlock callback will be equivalent to either
getTotalNumInputChannels or getTotalNumOutputChannels - which ever
is greater.
Note that getTotalNumOutputChannels is equivalent to
getMainBusNumOutputChannels if your processor does not have any sidechains
or aux buses.
*/
int getTotalNumOutputChannels() const noexcept { return cachedTotalOuts; }
//==============================================================================
/** Returns the current sample rate.
This can be called from your processBlock() method - it's not guaranteed
to be valid at any other time, and may return 0 if it's unknown.
*/
double getSampleRate() const noexcept { return currentSampleRate; }
/** Returns the current typical block size that is being used.
This can be called from your processBlock() method - it's not guaranteed
to be valid at any other time.
Remember it's not the ONLY block size that may be used when calling
processBlock, it's just the normal one. The actual block sizes used may be
larger or smaller than this, and will vary between successive calls.
*/
int getBlockSize() const noexcept { return blockSize; }
//==============================================================================
/** This returns the number of samples delay that the filter imposes on the audio
passing through it.
The host will call this to find the latency - the filter itself should set this value
by calling setLatencySamples() as soon as it can during its initialisation.
*/
int getLatencySamples() const noexcept { return latencySamples; }
/** The filter should call this to set the number of samples delay that it introduces.
The filter should call this as soon as it can during initialisation, and can call it
later if the value changes.
*/
void setLatencySamples (int newLatency);
/** Returns the length of the filter's tail, in seconds. */
virtual double getTailLengthSeconds() const = 0;
/** Returns true if the processor wants midi messages. */
virtual bool acceptsMidi() const = 0;
/** Returns true if the processor produces midi messages. */
virtual bool producesMidi() const = 0;
/** Returns true if the processor supports MPE. */
virtual bool supportsMPE() const { return false; }
/** Returns true if this is a midi effect plug-in and does no audio processing. */
virtual bool isMidiEffect() const { return false; }
//==============================================================================
/** This returns a critical section that will automatically be locked while the host
is calling the processBlock() method.
Use it from your UI or other threads to lock access to variables that are used
by the process callback, but obviously be careful not to keep it locked for
too long, because that could cause stuttering playback. If you need to do something
that'll take a long time and need the processing to stop while it happens, use the
suspendProcessing() method instead.
@see suspendProcessing
*/
const CarlaRecursiveMutex& getCallbackLock() const noexcept { return callbackLock; }
/** Enables and disables the processing callback.
If you need to do something time-consuming on a thread and would like to make sure
the audio processing callback doesn't happen until you've finished, use this
to disable the callback and re-enable it again afterwards.
E.g.
@code
void loadNewPatch()
{
suspendProcessing (true);
..do something that takes ages..
suspendProcessing (false);
}
@endcode
If the host tries to make an audio callback while processing is suspended, the
filter will return an empty buffer, but won't block the audio thread like it would
do if you use the getCallbackLock() critical section to synchronise access.
Any code that calls processBlock() should call isSuspended() before doing so, and
if the processor is suspended, it should avoid the call and emit silence or
whatever is appropriate.
@see getCallbackLock
*/
void suspendProcessing (bool shouldBeSuspended);
/** Returns true if processing is currently suspended.
@see suspendProcessing
*/
bool isSuspended() const noexcept { return suspended; }
/** A plugin can override this to be told when it should reset any playing voices.
The default implementation does nothing, but a host may call this to tell the
plugin that it should stop any tails or sounds that have been left running.
*/
virtual void reset();
//==============================================================================
/** Returns true if the processor is being run in an offline mode for rendering.
If the processor is being run live on realtime signals, this returns false.
If the mode is unknown, this will assume it's realtime and return false.
This value may be unreliable until the prepareToPlay() method has been called,
and could change each time prepareToPlay() is called.
@see setNonRealtime()
*/
bool isNonRealtime() const noexcept { return nonRealtime; }
/** Called by the host to tell this processor whether it's being used in a non-realtime
capacity for offline rendering or bouncing.
*/
virtual void setNonRealtime (bool isNonRealtime) noexcept;
//==============================================================================
/** Tells the processor to use this playhead object.
The processor will not take ownership of the object, so the caller must delete it when
it is no longer being used.
*/
virtual void setPlayHead (AudioPlayHead* newPlayHead);
//==============================================================================
/** This is called by the processor to specify its details before being played. Use this
version of the function if you are not interested in any sidechain and/or aux buses
and do not care about the layout of channels. Otherwise use setRateAndBufferSizeDetails.*/
void setPlayConfigDetails (int numIns, int numOuts, double sampleRate, int blockSize);
/** This is called by the processor to specify its details before being played. You
should call this function after having informed the processor about the channel
and bus layouts via setBusesLayout.
@see setBusesLayout
*/
void setRateAndBufferSizeDetails (double sampleRate, int blockSize) noexcept;
private:
//==============================================================================
/** @internal */
AudioPlayHead* playHead;
//==============================================================================
double currentSampleRate;
int blockSize, latencySamples;
bool suspended, nonRealtime;
CarlaRecursiveMutex callbackLock;
String cachedInputSpeakerArrString;
String cachedOutputSpeakerArrString;
int cachedTotalIns, cachedTotalOuts;
void processBypassed (AudioSampleBuffer&, MidiBuffer&);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessor)
};
#endif // JUCE_AUDIOPROCESSOR_H_INCLUDED

+ 1630
- 0
source/modules/juce_audio_graph/processors/juce_AudioProcessorGraph.cpp
File diff suppressed because it is too large
View File


+ 387
- 0
source/modules/juce_audio_graph/processors/juce_AudioProcessorGraph.h View File

@@ -0,0 +1,387 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_AUDIOPROCESSORGRAPH_H_INCLUDED
#define JUCE_AUDIOPROCESSORGRAPH_H_INCLUDED
#include "juce_AudioProcessor.h"
//==============================================================================
/**
A type of AudioProcessor which plays back a graph of other AudioProcessors.
Use one of these objects if you want to wire-up a set of AudioProcessors
and play back the result.
Processors can be added to the graph as "nodes" using addNode(), and once
added, you can connect any of their input or output channels to other
nodes using addConnection().
To play back a graph through an audio device, you might want to use an
AudioProcessorPlayer object.
*/
class JUCE_API AudioProcessorGraph : public AudioProcessor
/* private AsyncUpdater*/
{
public:
//==============================================================================
/** Creates an empty graph. */
AudioProcessorGraph();
/** Destructor.
Any processor objects that have been added to the graph will also be deleted.
*/
~AudioProcessorGraph();
//==============================================================================
/** Represents one of the nodes, or processors, in an AudioProcessorGraph.
To create a node, call AudioProcessorGraph::addNode().
*/
class JUCE_API Node /*: public ReferenceCountedObject*/
{
public:
//==============================================================================
/** The ID number assigned to this node.
This is assigned by the graph that owns it, and can't be changed.
*/
const uint32 nodeId;
/** The actual processor object that this node represents. */
AudioProcessor* getProcessor() const noexcept { return processor; }
#if 0
/** A set of user-definable properties that are associated with this node.
This can be used to attach values to the node for whatever purpose seems
useful. For example, you might store an x and y position if your application
is displaying the nodes on-screen.
*/
NamedValueSet properties;
//==============================================================================
/** A convenient typedef for referring to a pointer to a node object. */
typedef ReferenceCountedObjectPtr<Node> Ptr;
#endif
private:
//==============================================================================
friend class AudioProcessorGraph;
const ScopedPointer<AudioProcessor> processor;
bool isPrepared;
Node (uint32 nodeId, AudioProcessor*) noexcept;
void setParentGraph (AudioProcessorGraph*) const;
void prepare (double newSampleRate, int newBlockSize, AudioProcessorGraph*);
void unprepare();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Node)
};
//==============================================================================
/** Represents a connection between two channels of two nodes in an AudioProcessorGraph.
To create a connection, use AudioProcessorGraph::addConnection().
*/
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
*/
uint32 sourceNodeId;
/** The index of the output channel of the source node from which this
connection takes its data.
If this value is the special number AudioProcessorGraph::midiChannelIndex, then
it is referring to the source node's midi output. Otherwise, it is the zero-based
index of an audio output channel in the source node.
*/
int sourceChannelIndex;
/** The ID number of the node which is the destination for this connection.
@see AudioProcessorGraph::getNodeForId
*/
uint32 destNodeId;
/** The index of the input channel of the destination node to which this
connection delivers its data.
If this value is the special number AudioProcessorGraph::midiChannelIndex, then
it is referring to the destination node's midi input. Otherwise, it is the zero-based
index of an audio input channel in the destination node.
*/
int destChannelIndex;
private:
//==============================================================================
JUCE_LEAK_DETECTOR (Connection)
};
//==============================================================================
/** Deletes all nodes and connections from this graph.
Any processor objects in the graph will be deleted.
*/
void clear();
#if 0
/** Returns the number of nodes in the graph. */
int getNumNodes() const noexcept { return nodes.size(); }
/** Returns a pointer to one of the nodes in the graph.
This will return nullptr if the index is out of range.
@see getNodeForId
*/
Node* getNode (const int index) const noexcept { return nodes [index]; }
/** Searches the graph for a node with the given ID number and returns it.
If no such node was found, this returns nullptr.
@see getNode
*/
Node* getNodeForId (const uint32 nodeId) const;
/** Adds a node to the graph.
This creates a new node in the graph, for the specified processor. Once you have
added a processor to the graph, the graph owns it and will delete it later when
it is no longer needed.
The optional nodeId parameter lets you specify an ID to use for the node, but
if the value is already in use, this new node will overwrite the old one.
If this succeeds, it returns a pointer to the newly-created node.
*/
Node* addNode (AudioProcessor* newProcessor, uint32 nodeId = 0);
/** Deletes a node within the graph which has the specified ID.
This will also delete any connections that are attached to this node.
*/
bool removeNode (uint32 nodeId);
/** Deletes a node within the graph which has the specified ID.
This will also delete any connections that are attached to this node.
*/
bool removeNode (Node* node);
#endif
//==============================================================================
/** Returns the number of connections in the graph. */
int getNumConnections() const { return connections.size(); }
/** Returns a pointer to one of the connections in the graph. */
const Connection* getConnection (int index) const { return connections [index]; }
/** Searches for a connection between some specified channels.
If no such connection is found, this returns nullptr.
*/
const Connection* getConnectionBetween (uint32 sourceNodeId,
int sourceChannelIndex,
uint32 destNodeId,
int destChannelIndex) const;
/** Returns true if there is a connection between any of the channels of
two specified nodes.
*/
bool isConnected (uint32 possibleSourceNodeId,
uint32 possibleDestNodeId) const;
/** Returns true if it would be legal to connect the specified points. */
bool canConnect (uint32 sourceNodeId, int sourceChannelIndex,
uint32 destNodeId, int destChannelIndex) const;
/** Attempts to connect two specified channels of two nodes.
If this isn't allowed (e.g. because you're trying to connect a midi channel
to an audio one or other such nonsense), then it'll return false.
*/
bool addConnection (uint32 sourceNodeId, int sourceChannelIndex,
uint32 destNodeId, int destChannelIndex);
/** Deletes the connection with the specified index. */
void removeConnection (int index);
/** Deletes any connection between two specified points.
Returns true if a connection was actually deleted.
*/
bool removeConnection (uint32 sourceNodeId, int sourceChannelIndex,
uint32 destNodeId, int destChannelIndex);
/** Removes all connections from the specified node. */
bool disconnectNode (uint32 nodeId);
/** Returns true if the given connection's channel numbers map on to valid
channels at each end.
Even if a connection is valid when created, its status could change if
a node changes its channel config.
*/
bool isConnectionLegal (const Connection* connection) const;
/** Performs a sanity checks of all the connections.
This might be useful if some of the processors are doing things like changing
their channel counts, which could render some connections obsolete.
*/
bool removeIllegalConnections();
//==============================================================================
/** A special number that represents the midi channel of a node.
This is used as a channel index value if you want to refer to the midi input
or output instead of an audio channel.
*/
static const int midiChannelIndex;
//==============================================================================
/** A special type of AudioProcessor that can live inside an AudioProcessorGraph
in order to use the audio that comes into and out of the graph itself.
If you create an AudioGraphIOProcessor in "input" mode, it will act as a
node in the graph which delivers the audio that is coming into the parent
graph. This allows you to stream the data to other nodes and process the
incoming audio.
Likewise, one of these in "output" mode can be sent data which it will add to
the sum of data being sent to the graph's output.
@see AudioProcessorGraph
*/
class JUCE_API AudioGraphIOProcessor : public AudioProcessor
{
public:
/** Specifies the mode in which this processor will operate.
*/
enum IODeviceType
{
audioInputNode, /**< In this mode, the processor has output channels
representing all the audio input channels that are
coming into its parent audio graph. */
audioOutputNode, /**< In this mode, the processor has input channels
representing all the audio output channels that are
going out of its parent audio graph. */
midiInputNode, /**< In this mode, the processor has a midi output which
delivers the same midi data that is arriving at its
parent graph. */
midiOutputNode /**< In this mode, the processor has a midi input and
any data sent to it will be passed out of the parent
graph. */
};
//==============================================================================
/** Returns the mode of this processor. */
IODeviceType getType() const noexcept { return type; }
/** Returns the parent graph to which this processor belongs, or nullptr if it
hasn't yet been added to one. */
AudioProcessorGraph* getParentGraph() const noexcept { return graph; }
/** True if this is an audio or midi input. */
bool isInput() const noexcept;
/** True if this is an audio or midi output. */
bool isOutput() const noexcept;
//==============================================================================
AudioGraphIOProcessor (const IODeviceType type);
~AudioGraphIOProcessor();
const String getName() const override;
#if 0
void fillInPluginDescription (PluginDescription&) const override;
#endif
void prepareToPlay (double newSampleRate, int estimatedSamplesPerBlock) override;
void releaseResources() override;
void processBlock (AudioSampleBuffer&, MidiBuffer&) override;
double getTailLengthSeconds() const override;
bool acceptsMidi() const override;
bool producesMidi() const override;
/** @internal */
void setParentGraph (AudioProcessorGraph*);
private:
const IODeviceType type;
AudioProcessorGraph* graph;
//==============================================================================
void processAudio (AudioSampleBuffer& buffer, MidiBuffer& midiMessages);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioGraphIOProcessor)
};
//==============================================================================
const String getName() const override;
void prepareToPlay (double, int) override;
void releaseResources() override;
void processBlock (AudioSampleBuffer&, MidiBuffer&) override;
void reset() override;
void setNonRealtime (bool) noexcept override;
void setPlayHead (AudioPlayHead*) override;
double getTailLengthSeconds() const override;
bool acceptsMidi() const override;
bool producesMidi() const override;
private:
//==============================================================================
void processAudio (AudioSampleBuffer& buffer, MidiBuffer& midiMessages);
//==============================================================================
#if 0
ReferenceCountedArray<Node> nodes;
#endif
OwnedArray<Connection> connections;
uint32 lastNodeId;
OwnedArray<MidiBuffer> midiBuffers;
Array<void*> renderingOps;
friend class AudioGraphIOProcessor;
struct AudioProcessorGraphBufferHelpers;
ScopedPointer<AudioProcessorGraphBufferHelpers> audioBuffers;
MidiBuffer* currentMidiInputBuffer;
MidiBuffer currentMidiOutputBuffer;
bool isPrepared;
void clearRenderingSequence();
void buildRenderingSequence();
bool isAnInputTo (uint32 possibleInputId, uint32 possibleDestinationId, int recursionCheck) const;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorGraph)
};
#endif // JUCE_AUDIOPROCESSORGRAPH_H_INCLUDED

+ 238
- 0
source/modules/juce_audio_graph/streams/juce_InputStream.cpp View File

@@ -0,0 +1,238 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
int64 InputStream::getNumBytesRemaining()
{
int64 len = getTotalLength();
if (len >= 0)
len -= getPosition();
return len;
}
char InputStream::readByte()
{
char temp = 0;
read (&temp, 1);
return temp;
}
bool InputStream::readBool()
{
return readByte() != 0;
}
short InputStream::readShort()
{
char temp[2];
if (read (temp, 2) == 2)
return (short) ByteOrder::littleEndianShort (temp);
return 0;
}
short InputStream::readShortBigEndian()
{
char temp[2];
if (read (temp, 2) == 2)
return (short) ByteOrder::bigEndianShort (temp);
return 0;
}
int InputStream::readInt()
{
char temp[4];
if (read (temp, 4) == 4)
return (int) ByteOrder::littleEndianInt (temp);
return 0;
}
int InputStream::readIntBigEndian()
{
char temp[4];
if (read (temp, 4) == 4)
return (int) ByteOrder::bigEndianInt (temp);
return 0;
}
int InputStream::readCompressedInt()
{
const uint8 sizeByte = (uint8) readByte();
if (sizeByte == 0)
return 0;
const int numBytes = (sizeByte & 0x7f);
if (numBytes > 4)
{
jassertfalse; // trying to read corrupt data - this method must only be used
// to read data that was written by OutputStream::writeCompressedInt()
return 0;
}
char bytes[4] = { 0, 0, 0, 0 };
if (read (bytes, numBytes) != numBytes)
return 0;
const int num = (int) ByteOrder::littleEndianInt (bytes);
return (sizeByte >> 7) ? -num : num;
}
int64 InputStream::readInt64()
{
union { uint8 asBytes[8]; uint64 asInt64; } n;
if (read (n.asBytes, 8) == 8)
return (int64) ByteOrder::swapIfBigEndian (n.asInt64);
return 0;
}
int64 InputStream::readInt64BigEndian()
{
union { uint8 asBytes[8]; uint64 asInt64; } n;
if (read (n.asBytes, 8) == 8)
return (int64) ByteOrder::swapIfLittleEndian (n.asInt64);
return 0;
}
float InputStream::readFloat()
{
// the union below relies on these types being the same size...
static_jassert (sizeof (int32) == sizeof (float));
union { int32 asInt; float asFloat; } n;
n.asInt = (int32) readInt();
return n.asFloat;
}
float InputStream::readFloatBigEndian()
{
union { int32 asInt; float asFloat; } n;
n.asInt = (int32) readIntBigEndian();
return n.asFloat;
}
double InputStream::readDouble()
{
union { int64 asInt; double asDouble; } n;
n.asInt = readInt64();
return n.asDouble;
}
double InputStream::readDoubleBigEndian()
{
union { int64 asInt; double asDouble; } n;
n.asInt = readInt64BigEndian();
return n.asDouble;
}
String InputStream::readString()
{
MemoryBlock buffer (256);
char* data = static_cast<char*> (buffer.getData());
size_t i = 0;
while ((data[i] = readByte()) != 0)
{
if (++i >= buffer.getSize())
{
buffer.setSize (buffer.getSize() + 512);
data = static_cast<char*> (buffer.getData());
}
}
return String::fromUTF8 (data, (int) i);
}
String InputStream::readNextLine()
{
MemoryBlock buffer (256);
char* data = static_cast<char*> (buffer.getData());
size_t i = 0;
while ((data[i] = readByte()) != 0)
{
if (data[i] == '\n')
break;
if (data[i] == '\r')
{
const int64 lastPos = getPosition();
if (readByte() != '\n')
setPosition (lastPos);
break;
}
if (++i >= buffer.getSize())
{
buffer.setSize (buffer.getSize() + 512);
data = static_cast<char*> (buffer.getData());
}
}
return String::fromUTF8 (data, (int) i);
}
size_t InputStream::readIntoMemoryBlock (MemoryBlock& block, ssize_t numBytes)
{
MemoryOutputStream mo (block, true);
return (size_t) mo.writeFromInputStream (*this, numBytes);
}
String InputStream::readEntireStreamAsString()
{
MemoryOutputStream mo;
mo << *this;
return mo.toString();
}
//==============================================================================
void InputStream::skipNextBytes (int64 numBytesToSkip)
{
if (numBytesToSkip > 0)
{
const int skipBufferSize = (int) jmin (numBytesToSkip, (int64) 16384);
HeapBlock<char> temp ((size_t) skipBufferSize);
while (numBytesToSkip > 0 && ! isExhausted())
numBytesToSkip -= read (temp, (int) jmin (numBytesToSkip, (int64) skipBufferSize));
}
}

+ 268
- 0
source/modules/juce_audio_graph/streams/juce_InputStream.h View File

@@ -0,0 +1,268 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_INPUTSTREAM_H_INCLUDED
#define JUCE_INPUTSTREAM_H_INCLUDED
//==============================================================================
/** The base class for streams that read data.
Input and output streams are used throughout the library - subclasses can override
some or all of the virtual functions to implement their behaviour.
@see OutputStream, MemoryInputStream, BufferedInputStream, FileInputStream
*/
class JUCE_API InputStream
{
public:
/** Destructor. */
virtual ~InputStream() {}
//==============================================================================
/** Returns the total number of bytes available for reading in this stream.
Note that this is the number of bytes available from the start of the
stream, not from the current position.
If the size of the stream isn't actually known, this will return -1.
@see getNumBytesRemaining
*/
virtual int64 getTotalLength() = 0;
/** Returns the number of bytes available for reading, or a negative value if
the remaining length is not known.
@see getTotalLength
*/
int64 getNumBytesRemaining();
/** Returns true if the stream has no more data to read. */
virtual bool isExhausted() = 0;
//==============================================================================
/** Reads some data from the stream into a memory buffer.
This is the only read method that subclasses actually need to implement, as the
InputStream base class implements the other read methods in terms of this one (although
it's often more efficient for subclasses to implement them directly).
@param destBuffer the destination buffer for the data. This must not be null.
@param maxBytesToRead the maximum number of bytes to read - make sure the
memory block passed in is big enough to contain this
many bytes. This value must not be negative.
@returns the actual number of bytes that were read, which may be less than
maxBytesToRead if the stream is exhausted before it gets that far
*/
virtual int read (void* destBuffer, int maxBytesToRead) = 0;
/** Reads a byte from the stream.
If the stream is exhausted, this will return zero.
@see OutputStream::writeByte
*/
virtual char readByte();
/** Reads a boolean from the stream.
The bool is encoded as a single byte - non-zero for true, 0 for false.
If the stream is exhausted, this will return false.
@see OutputStream::writeBool
*/
virtual bool readBool();
/** Reads two bytes from the stream as a little-endian 16-bit value.
If the next two bytes read are byte1 and byte2, this returns (byte1 | (byte2 << 8)).
If the stream is exhausted partway through reading the bytes, this will return zero.
@see OutputStream::writeShort, readShortBigEndian
*/
virtual short readShort();
/** Reads two bytes from the stream as a little-endian 16-bit value.
If the next two bytes read are byte1 and byte2, this returns (byte2 | (byte1 << 8)).
If the stream is exhausted partway through reading the bytes, this will return zero.
@see OutputStream::writeShortBigEndian, readShort
*/
virtual short readShortBigEndian();
/** Reads four bytes from the stream as a little-endian 32-bit value.
If the next four bytes are byte1 to byte4, this returns
(byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24)).
If the stream is exhausted partway through reading the bytes, this will return zero.
@see OutputStream::writeInt, readIntBigEndian
*/
virtual int readInt();
/** Reads four bytes from the stream as a big-endian 32-bit value.
If the next four bytes are byte1 to byte4, this returns
(byte4 | (byte3 << 8) | (byte2 << 16) | (byte1 << 24)).
If the stream is exhausted partway through reading the bytes, this will return zero.
@see OutputStream::writeIntBigEndian, readInt
*/
virtual int readIntBigEndian();
/** Reads eight bytes from the stream as a little-endian 64-bit value.
If the next eight bytes are byte1 to byte8, this returns
(byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24) | (byte5 << 32) | (byte6 << 40) | (byte7 << 48) | (byte8 << 56)).
If the stream is exhausted partway through reading the bytes, this will return zero.
@see OutputStream::writeInt64, readInt64BigEndian
*/
virtual int64 readInt64();
/** Reads eight bytes from the stream as a big-endian 64-bit value.
If the next eight bytes are byte1 to byte8, this returns
(byte8 | (byte7 << 8) | (byte6 << 16) | (byte5 << 24) | (byte4 << 32) | (byte3 << 40) | (byte2 << 48) | (byte1 << 56)).
If the stream is exhausted partway through reading the bytes, this will return zero.
@see OutputStream::writeInt64BigEndian, readInt64
*/
virtual int64 readInt64BigEndian();
/** Reads four bytes as a 32-bit floating point value.
The raw 32-bit encoding of the float is read from the stream as a little-endian int.
If the stream is exhausted partway through reading the bytes, this will return zero.
@see OutputStream::writeFloat, readDouble
*/
virtual float readFloat();
/** Reads four bytes as a 32-bit floating point value.
The raw 32-bit encoding of the float is read from the stream as a big-endian int.
If the stream is exhausted partway through reading the bytes, this will return zero.
@see OutputStream::writeFloatBigEndian, readDoubleBigEndian
*/
virtual float readFloatBigEndian();
/** Reads eight bytes as a 64-bit floating point value.
The raw 64-bit encoding of the double is read from the stream as a little-endian int64.
If the stream is exhausted partway through reading the bytes, this will return zero.
@see OutputStream::writeDouble, readFloat
*/
virtual double readDouble();
/** Reads eight bytes as a 64-bit floating point value.
The raw 64-bit encoding of the double is read from the stream as a big-endian int64.
If the stream is exhausted partway through reading the bytes, this will return zero.
@see OutputStream::writeDoubleBigEndian, readFloatBigEndian
*/
virtual double readDoubleBigEndian();
/** Reads an encoded 32-bit number from the stream using a space-saving compressed format.
For small values, this is more space-efficient than using readInt() and OutputStream::writeInt()
The format used is: number of significant bytes + up to 4 bytes in little-endian order.
@see OutputStream::writeCompressedInt()
*/
virtual int readCompressedInt();
//==============================================================================
/** Reads a UTF-8 string from the stream, up to the next linefeed or carriage return.
This will read up to the next "\n" or "\r\n" or end-of-stream.
After this call, the stream's position will be left pointing to the next character
following the line-feed, but the linefeeds aren't included in the string that
is returned.
*/
virtual String readNextLine();
/** Reads a zero-terminated UTF-8 string from the stream.
This will read characters from the stream until it hits a null character
or end-of-stream.
@see OutputStream::writeString, readEntireStreamAsString
*/
virtual String readString();
/** Tries to read the whole stream and turn it into a string.
This will read from the stream's current position until the end-of-stream.
It can read from UTF-8 data, or UTF-16 if it detects suitable header-bytes.
*/
virtual String readEntireStreamAsString();
/** Reads from the stream and appends the data to a MemoryBlock.
@param destBlock the block to append the data onto
@param maxNumBytesToRead if this is a positive value, it sets a limit to the number
of bytes that will be read - if it's negative, data
will be read until the stream is exhausted.
@returns the number of bytes that were added to the memory block
*/
virtual size_t readIntoMemoryBlock (MemoryBlock& destBlock,
ssize_t maxNumBytesToRead = -1);
//==============================================================================
/** Returns the offset of the next byte that will be read from the stream.
@see setPosition
*/
virtual int64 getPosition() = 0;
/** Tries to move the current read position of the stream.
The position is an absolute number of bytes from the stream's start.
Some streams might not be able to do this, in which case they should do
nothing and return false. Others might be able to manage it by resetting
themselves and skipping to the correct position, although this is
obviously a bit slow.
@returns true if the stream manages to reposition itself correctly
@see getPosition
*/
virtual bool setPosition (int64 newPosition) = 0;
/** Reads and discards a number of bytes from the stream.
Some input streams might implement this efficiently, but the base
class will just keep reading data until the requisite number of bytes
have been done.
*/
virtual void skipNextBytes (int64 numBytesToSkip);
protected:
//==============================================================================
InputStream() noexcept {}
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InputStream)
};
#endif // JUCE_INPUTSTREAM_H_INCLUDED

+ 216
- 0
source/modules/juce_audio_graph/streams/juce_MemoryOutputStream.cpp View File

@@ -0,0 +1,216 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
MemoryOutputStream::MemoryOutputStream (const size_t initialSize)
: blockToUse (&internalBlock), externalData (nullptr),
position (0), size (0), availableSize (0)
{
internalBlock.setSize (initialSize, false);
}
MemoryOutputStream::MemoryOutputStream (MemoryBlock& memoryBlockToWriteTo,
const bool appendToExistingBlockContent)
: blockToUse (&memoryBlockToWriteTo), externalData (nullptr),
position (0), size (0), availableSize (0)
{
if (appendToExistingBlockContent)
position = size = memoryBlockToWriteTo.getSize();
}
MemoryOutputStream::MemoryOutputStream (void* destBuffer, size_t destBufferSize)
: blockToUse (nullptr), externalData (destBuffer),
position (0), size (0), availableSize (destBufferSize)
{
jassert (externalData != nullptr); // This must be a valid pointer.
}
MemoryOutputStream::~MemoryOutputStream()
{
trimExternalBlockSize();
}
void MemoryOutputStream::flush()
{
trimExternalBlockSize();
}
void MemoryOutputStream::trimExternalBlockSize()
{
if (blockToUse != &internalBlock && blockToUse != nullptr)
blockToUse->setSize (size, false);
}
void MemoryOutputStream::preallocate (const size_t bytesToPreallocate)
{
if (blockToUse != nullptr)
blockToUse->ensureSize (bytesToPreallocate + 1);
}
void MemoryOutputStream::reset() noexcept
{
position = 0;
size = 0;
}
char* MemoryOutputStream::prepareToWrite (size_t numBytes)
{
jassert ((ssize_t) numBytes >= 0);
size_t storageNeeded = position + numBytes;
char* data;
if (blockToUse != nullptr)
{
if (storageNeeded >= blockToUse->getSize())
blockToUse->ensureSize ((storageNeeded + jmin (storageNeeded / 2, (size_t) (1024 * 1024)) + 32) & ~31u);
data = static_cast<char*> (blockToUse->getData());
}
else
{
if (storageNeeded > availableSize)
return nullptr;
data = static_cast<char*> (externalData);
}
char* const writePointer = data + position;
position += numBytes;
size = jmax (size, position);
return writePointer;
}
bool MemoryOutputStream::write (const void* const buffer, size_t howMany)
{
jassert (buffer != nullptr);
if (howMany == 0)
return true;
if (char* dest = prepareToWrite (howMany))
{
memcpy (dest, buffer, howMany);
return true;
}
return false;
}
bool MemoryOutputStream::writeRepeatedByte (uint8 byte, size_t howMany)
{
if (howMany == 0)
return true;
if (char* dest = prepareToWrite (howMany))
{
memset (dest, byte, howMany);
return true;
}
return false;
}
bool MemoryOutputStream::appendUTF8Char (juce_wchar c)
{
if (char* dest = prepareToWrite (CharPointer_UTF8::getBytesRequiredFor (c)))
{
CharPointer_UTF8 (dest).write (c);
return true;
}
return false;
}
MemoryBlock MemoryOutputStream::getMemoryBlock() const
{
return MemoryBlock (getData(), getDataSize());
}
const void* MemoryOutputStream::getData() const noexcept
{
if (blockToUse == nullptr)
return externalData;
if (blockToUse->getSize() > size)
static_cast<char*> (blockToUse->getData()) [size] = 0;
return blockToUse->getData();
}
bool MemoryOutputStream::setPosition (int64 newPosition)
{
if (newPosition <= (int64) size)
{
// ok to seek backwards
position = jlimit ((size_t) 0, size, (size_t) newPosition);
return true;
}
// can't move beyond the end of the stream..
return false;
}
int64 MemoryOutputStream::writeFromInputStream (InputStream& source, int64 maxNumBytesToWrite)
{
// before writing from an input, see if we can preallocate to make it more efficient..
int64 availableData = source.getTotalLength() - source.getPosition();
if (availableData > 0)
{
if (maxNumBytesToWrite > availableData || maxNumBytesToWrite < 0)
maxNumBytesToWrite = availableData;
if (blockToUse != nullptr)
preallocate (blockToUse->getSize() + (size_t) maxNumBytesToWrite);
}
return OutputStream::writeFromInputStream (source, maxNumBytesToWrite);
}
String MemoryOutputStream::toUTF8() const
{
const char* const d = static_cast<const char*> (getData());
return String (CharPointer_UTF8 (d), CharPointer_UTF8 (d + getDataSize()));
}
String MemoryOutputStream::toString() const
{
return String::createStringFromData (getData(), (int) getDataSize());
}
OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const MemoryOutputStream& streamToRead)
{
const size_t dataSize = streamToRead.getDataSize();
if (dataSize > 0)
stream.write (streamToRead.getData(), dataSize);
return stream;
}

+ 141
- 0
source/modules/juce_audio_graph/streams/juce_MemoryOutputStream.h View File

@@ -0,0 +1,141 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_MEMORYOUTPUTSTREAM_H_INCLUDED
#define JUCE_MEMORYOUTPUTSTREAM_H_INCLUDED
//==============================================================================
/**
Writes data to an internal memory buffer, which grows as required.
The data that was written into the stream can then be accessed later as
a contiguous block of memory.
*/
class JUCE_API MemoryOutputStream : public OutputStream
{
public:
//==============================================================================
/** Creates an empty memory stream, ready to be written into.
@param initialSize the intial amount of capacity to allocate for writing into
*/
MemoryOutputStream (size_t initialSize = 256);
/** Creates a memory stream for writing into into a pre-existing MemoryBlock object.
Note that the destination block will always be larger than the amount of data
that has been written to the stream, because the MemoryOutputStream keeps some
spare capactity at its end. To trim the block's size down to fit the actual
data, call flush(), or delete the MemoryOutputStream.
@param memoryBlockToWriteTo the block into which new data will be written.
@param appendToExistingBlockContent if this is true, the contents of the block will be
kept, and new data will be appended to it. If false,
the block will be cleared before use
*/
MemoryOutputStream (MemoryBlock& memoryBlockToWriteTo,
bool appendToExistingBlockContent);
/** Creates a MemoryOutputStream that will write into a user-supplied, fixed-size
block of memory.
When using this mode, the stream will write directly into this memory area until
it's full, at which point write operations will fail.
*/
MemoryOutputStream (void* destBuffer, size_t destBufferSize);
/** Destructor.
This will free any data that was written to it.
*/
~MemoryOutputStream();
//==============================================================================
/** Returns a pointer to the data that has been written to the stream.
@see getDataSize
*/
const void* getData() const noexcept;
/** Returns the number of bytes of data that have been written to the stream.
@see getData
*/
size_t getDataSize() const noexcept { return size; }
/** Resets the stream, clearing any data that has been written to it so far. */
void reset() noexcept;
/** Increases the internal storage capacity to be able to contain at least the specified
amount of data without needing to be resized.
*/
void preallocate (size_t bytesToPreallocate);
/** Appends the utf-8 bytes for a unicode character */
bool appendUTF8Char (juce_wchar character);
/** Returns a String created from the (UTF8) data that has been written to the stream. */
String toUTF8() const;
/** Attempts to detect the encoding of the data and convert it to a string.
@see String::createStringFromData
*/
String toString() const;
/** Returns a copy of the stream's data as a memory block. */
MemoryBlock getMemoryBlock() const;
//==============================================================================
/** If the stream is writing to a user-supplied MemoryBlock, this will trim any excess
capacity off the block, so that its length matches the amount of actual data that
has been written so far.
*/
void flush() override;
bool write (const void*, size_t) override;
int64 getPosition() override { return (int64) position; }
bool setPosition (int64) override;
int64 writeFromInputStream (InputStream&, int64 maxNumBytesToWrite) override;
bool writeRepeatedByte (uint8 byte, size_t numTimesToRepeat) override;
private:
//==============================================================================
MemoryBlock* const blockToUse;
MemoryBlock internalBlock;
void* externalData;
size_t position, size, availableSize;
void trimExternalBlockSize();
char* prepareToWrite (size_t);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryOutputStream)
};
/** Copies all the data that has been written to a MemoryOutputStream into another stream. */
OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const MemoryOutputStream& streamToRead);
#endif // JUCE_MEMORYOUTPUTSTREAM_H_INCLUDED

+ 353
- 0
source/modules/juce_audio_graph/streams/juce_OutputStream.cpp View File

@@ -0,0 +1,353 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#if JUCE_DEBUG
struct DanglingStreamChecker
{
DanglingStreamChecker() {}
~DanglingStreamChecker()
{
/*
It's always a bad idea to leak any object, but if you're leaking output
streams, then there's a good chance that you're failing to flush a file
to disk properly, which could result in corrupted data and other similar
nastiness..
*/
jassert (activeStreams.size() == 0);
}
Array<void*, CriticalSection> activeStreams;
};
static DanglingStreamChecker danglingStreamChecker;
#endif
//==============================================================================
OutputStream::OutputStream()
: newLineString (NewLine::getDefault())
{
#if JUCE_DEBUG
danglingStreamChecker.activeStreams.add (this);
#endif
}
OutputStream::~OutputStream()
{
#if JUCE_DEBUG
danglingStreamChecker.activeStreams.removeFirstMatchingValue (this);
#endif
}
//==============================================================================
bool OutputStream::writeBool (const bool b)
{
return writeByte (b ? (char) 1
: (char) 0);
}
bool OutputStream::writeByte (char byte)
{
return write (&byte, 1);
}
bool OutputStream::writeRepeatedByte (uint8 byte, size_t numTimesToRepeat)
{
for (size_t i = 0; i < numTimesToRepeat; ++i)
if (! writeByte ((char) byte))
return false;
return true;
}
bool OutputStream::writeShort (short value)
{
const unsigned short v = ByteOrder::swapIfBigEndian ((unsigned short) value);
return write (&v, 2);
}
bool OutputStream::writeShortBigEndian (short value)
{
const unsigned short v = ByteOrder::swapIfLittleEndian ((unsigned short) value);
return write (&v, 2);
}
bool OutputStream::writeInt (int value)
{
const unsigned int v = ByteOrder::swapIfBigEndian ((unsigned int) value);
return write (&v, 4);
}
bool OutputStream::writeIntBigEndian (int value)
{
const unsigned int v = ByteOrder::swapIfLittleEndian ((unsigned int) value);
return write (&v, 4);
}
bool OutputStream::writeCompressedInt (int value)
{
unsigned int un = (value < 0) ? (unsigned int) -value
: (unsigned int) value;
uint8 data[5];
int num = 0;
while (un > 0)
{
data[++num] = (uint8) un;
un >>= 8;
}
data[0] = (uint8) num;
if (value < 0)
data[0] |= 0x80;
return write (data, (size_t) num + 1);
}
bool OutputStream::writeInt64 (int64 value)
{
const uint64 v = ByteOrder::swapIfBigEndian ((uint64) value);
return write (&v, 8);
}
bool OutputStream::writeInt64BigEndian (int64 value)
{
const uint64 v = ByteOrder::swapIfLittleEndian ((uint64) value);
return write (&v, 8);
}
bool OutputStream::writeFloat (float value)
{
union { int asInt; float asFloat; } n;
n.asFloat = value;
return writeInt (n.asInt);
}
bool OutputStream::writeFloatBigEndian (float value)
{
union { int asInt; float asFloat; } n;
n.asFloat = value;
return writeIntBigEndian (n.asInt);
}
bool OutputStream::writeDouble (double value)
{
union { int64 asInt; double asDouble; } n;
n.asDouble = value;
return writeInt64 (n.asInt);
}
bool OutputStream::writeDoubleBigEndian (double value)
{
union { int64 asInt; double asDouble; } n;
n.asDouble = value;
return writeInt64BigEndian (n.asInt);
}
bool OutputStream::writeString (const String& text)
{
#if (JUCE_STRING_UTF_TYPE == 8)
return write (text.toRawUTF8(), text.getNumBytesAsUTF8() + 1);
#else
// (This avoids using toUTF8() to prevent the memory bloat that it would leave behind
// if lots of large, persistent strings were to be written to streams).
const size_t numBytes = text.getNumBytesAsUTF8() + 1;
HeapBlock<char> temp (numBytes);
text.copyToUTF8 (temp, numBytes);
return write (temp, numBytes);
#endif
}
bool OutputStream::writeText (const String& text, const bool asUTF16,
const bool writeUTF16ByteOrderMark)
{
if (asUTF16)
{
if (writeUTF16ByteOrderMark)
write ("\x0ff\x0fe", 2);
String::CharPointerType src (text.getCharPointer());
bool lastCharWasReturn = false;
for (;;)
{
const juce_wchar c = src.getAndAdvance();
if (c == 0)
break;
if (c == '\n' && ! lastCharWasReturn)
writeShort ((short) '\r');
lastCharWasReturn = (c == L'\r');
if (! writeShort ((short) c))
return false;
}
}
else
{
const char* src = text.toUTF8();
const char* t = src;
for (;;)
{
if (*t == '\n')
{
if (t > src)
if (! write (src, (size_t) (t - src)))
return false;
if (! write ("\r\n", 2))
return false;
src = t + 1;
}
else if (*t == '\r')
{
if (t[1] == '\n')
++t;
}
else if (*t == 0)
{
if (t > src)
if (! write (src, (size_t) (t - src)))
return false;
break;
}
++t;
}
}
return true;
}
int64 OutputStream::writeFromInputStream (InputStream& source, int64 numBytesToWrite)
{
if (numBytesToWrite < 0)
numBytesToWrite = std::numeric_limits<int64>::max();
int64 numWritten = 0;
while (numBytesToWrite > 0)
{
char buffer [8192];
const int num = source.read (buffer, (int) jmin (numBytesToWrite, (int64) sizeof (buffer)));
if (num <= 0)
break;
write (buffer, (size_t) num);
numBytesToWrite -= num;
numWritten += num;
}
return numWritten;
}
//==============================================================================
void OutputStream::setNewLineString (const String& newLineString_)
{
newLineString = newLineString_;
}
//==============================================================================
template <typename IntegerType>
static void writeIntToStream (OutputStream& stream, IntegerType number)
{
char buffer [NumberToStringConverters::charsNeededForInt];
char* end = buffer + numElementsInArray (buffer);
const char* start = NumberToStringConverters::numberToString (end, number);
stream.write (start, (size_t) (end - start - 1));
}
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const int number)
{
writeIntToStream (stream, number);
return stream;
}
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const int64 number)
{
writeIntToStream (stream, number);
return stream;
}
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const double number)
{
return stream << String (number);
}
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const char character)
{
stream.writeByte (character);
return stream;
}
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const char* const text)
{
stream.write (text, strlen (text));
return stream;
}
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const MemoryBlock& data)
{
if (data.getSize() > 0)
stream.write (data.getData(), data.getSize());
return stream;
}
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const File& fileToRead)
{
FileInputStream in (fileToRead);
if (in.openedOk())
return stream << in;
return stream;
}
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, InputStream& streamToRead)
{
stream.writeFromInputStream (streamToRead, -1);
return stream;
}
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const NewLine&)
{
return stream << stream.getNewLineString();
}

+ 279
- 0
source/modules/juce_audio_graph/streams/juce_OutputStream.h View File

@@ -0,0 +1,279 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_OUTPUTSTREAM_H_INCLUDED
#define JUCE_OUTPUTSTREAM_H_INCLUDED
//==============================================================================
/**
The base class for streams that write data to some kind of destination.
Input and output streams are used throughout the library - subclasses can override
some or all of the virtual functions to implement their behaviour.
@see InputStream, MemoryOutputStream, FileOutputStream
*/
class JUCE_API OutputStream
{
protected:
//==============================================================================
OutputStream();
public:
/** Destructor.
Some subclasses might want to do things like call flush() during their
destructors.
*/
virtual ~OutputStream();
//==============================================================================
/** If the stream is using a buffer, this will ensure it gets written
out to the destination. */
virtual void flush() = 0;
/** Tries to move the stream's output position.
Not all streams will be able to seek to a new position - this will return
false if it fails to work.
@see getPosition
*/
virtual bool setPosition (int64 newPosition) = 0;
/** Returns the stream's current position.
@see setPosition
*/
virtual int64 getPosition() = 0;
//==============================================================================
/** Writes a block of data to the stream.
When creating a subclass of OutputStream, this is the only write method
that needs to be overloaded - the base class has methods for writing other
types of data which use this to do the work.
@param dataToWrite the target buffer to receive the data. This must not be null.
@param numberOfBytes the number of bytes to write.
@returns false if the write operation fails for some reason
*/
virtual bool write (const void* dataToWrite,
size_t numberOfBytes) = 0;
//==============================================================================
/** Writes a single byte to the stream.
@returns false if the write operation fails for some reason
@see InputStream::readByte
*/
virtual bool writeByte (char byte);
/** Writes a boolean to the stream as a single byte.
This is encoded as a binary byte (not as text) with a value of 1 or 0.
@returns false if the write operation fails for some reason
@see InputStream::readBool
*/
virtual bool writeBool (bool boolValue);
/** Writes a 16-bit integer to the stream in a little-endian byte order.
This will write two bytes to the stream: (value & 0xff), then (value >> 8).
@returns false if the write operation fails for some reason
@see InputStream::readShort
*/
virtual bool writeShort (short value);
/** Writes a 16-bit integer to the stream in a big-endian byte order.
This will write two bytes to the stream: (value >> 8), then (value & 0xff).
@returns false if the write operation fails for some reason
@see InputStream::readShortBigEndian
*/
virtual bool writeShortBigEndian (short value);
/** Writes a 32-bit integer to the stream in a little-endian byte order.
@returns false if the write operation fails for some reason
@see InputStream::readInt
*/
virtual bool writeInt (int value);
/** Writes a 32-bit integer to the stream in a big-endian byte order.
@returns false if the write operation fails for some reason
@see InputStream::readIntBigEndian
*/
virtual bool writeIntBigEndian (int value);
/** Writes a 64-bit integer to the stream in a little-endian byte order.
@returns false if the write operation fails for some reason
@see InputStream::readInt64
*/
virtual bool writeInt64 (int64 value);
/** Writes a 64-bit integer to the stream in a big-endian byte order.
@returns false if the write operation fails for some reason
@see InputStream::readInt64BigEndian
*/
virtual bool writeInt64BigEndian (int64 value);
/** Writes a 32-bit floating point value to the stream in a binary format.
The binary 32-bit encoding of the float is written as a little-endian int.
@returns false if the write operation fails for some reason
@see InputStream::readFloat
*/
virtual bool writeFloat (float value);
/** Writes a 32-bit floating point value to the stream in a binary format.
The binary 32-bit encoding of the float is written as a big-endian int.
@returns false if the write operation fails for some reason
@see InputStream::readFloatBigEndian
*/
virtual bool writeFloatBigEndian (float value);
/** Writes a 64-bit floating point value to the stream in a binary format.
The eight raw bytes of the double value are written out as a little-endian 64-bit int.
@returns false if the write operation fails for some reason
@see InputStream::readDouble
*/
virtual bool writeDouble (double value);
/** Writes a 64-bit floating point value to the stream in a binary format.
The eight raw bytes of the double value are written out as a big-endian 64-bit int.
@see InputStream::readDoubleBigEndian
@returns false if the write operation fails for some reason
*/
virtual bool writeDoubleBigEndian (double value);
/** Writes a byte to the output stream a given number of times.
@returns false if the write operation fails for some reason
*/
virtual bool writeRepeatedByte (uint8 byte, size_t numTimesToRepeat);
/** Writes a condensed binary encoding of a 32-bit integer.
If you're storing a lot of integers which are unlikely to have very large values,
this can save a lot of space, because values under 0xff will only take up 2 bytes,
under 0xffff only 3 bytes, etc.
The format used is: number of significant bytes + up to 4 bytes in little-endian order.
@returns false if the write operation fails for some reason
@see InputStream::readCompressedInt
*/
virtual bool writeCompressedInt (int value);
/** Stores a string in the stream in a binary format.
This isn't the method to use if you're trying to append text to the end of a
text-file! It's intended for storing a string so that it can be retrieved later
by InputStream::readString().
It writes the string to the stream as UTF8, including the null termination character.
For appending text to a file, instead use writeText, or operator<<
@returns false if the write operation fails for some reason
@see InputStream::readString, writeText, operator<<
*/
virtual bool writeString (const String& text);
/** Writes a string of text to the stream.
It can either write the text as UTF-8 or UTF-16, and can also add the UTF-16 byte-order-mark
bytes (0xff, 0xfe) to indicate the endianness (these should only be used at the start
of a file).
The method also replaces '\\n' characters in the text with '\\r\\n'.
@returns false if the write operation fails for some reason
*/
virtual bool writeText (const String& text,
bool asUTF16,
bool writeUTF16ByteOrderMark);
/** Reads data from an input stream and writes it to this stream.
@param source the stream to read from
@param maxNumBytesToWrite the number of bytes to read from the stream (if this is
less than zero, it will keep reading until the input
is exhausted)
@returns the number of bytes written
*/
virtual int64 writeFromInputStream (InputStream& source, int64 maxNumBytesToWrite);
//==============================================================================
/** Sets the string to write to the stream when a new line is written.
By default this will be set the value of NewLine::getDefault().
*/
void setNewLineString (const String& newLineString);
/** Returns the current new-line string that was set by setNewLineString(). */
const String& getNewLineString() const noexcept { return newLineString; }
private:
//==============================================================================
String newLineString;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OutputStream)
};
//==============================================================================
/** Writes a number to a stream as 8-bit characters in the default system encoding. */
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, int number);
/** Writes a number to a stream as 8-bit characters in the default system encoding. */
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, int64 number);
/** Writes a number to a stream as 8-bit characters in the default system encoding. */
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, double number);
/** Writes a character to a stream. */
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, char character);
/** Writes a null-terminated text string to a stream. */
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const char* text);
/** Writes a block of data from a MemoryBlock to a stream. */
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const MemoryBlock& data);
/** Writes the contents of a file to a stream. */
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const File& fileToRead);
/** Writes the complete contents of an input stream to an output stream. */
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, InputStream& streamToRead);
/** Writes a new-line to a stream.
You can use the predefined symbol 'newLine' to invoke this, e.g.
@code
myOutputStream << "Hello World" << newLine << newLine;
@endcode
@see OutputStream::setNewLineString
*/
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const NewLine&);
#endif // JUCE_OUTPUTSTREAM_H_INCLUDED

+ 572
- 0
source/modules/juce_audio_graph/text/juce_CharPointer_UTF8.h View File

@@ -0,0 +1,572 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_CHARPOINTER_UTF8_H_INCLUDED
#define JUCE_CHARPOINTER_UTF8_H_INCLUDED
//==============================================================================
/**
Wraps a pointer to a null-terminated UTF-8 character string, and provides
various methods to operate on the data.
@see CharPointer_UTF16, CharPointer_UTF32
*/
class CharPointer_UTF8
{
public:
typedef char CharType;
inline explicit CharPointer_UTF8 (const CharType* const rawPointer) noexcept
: data (const_cast<CharType*> (rawPointer))
{
}
inline CharPointer_UTF8 (const CharPointer_UTF8& other) noexcept
: data (other.data)
{
}
inline CharPointer_UTF8 operator= (CharPointer_UTF8 other) noexcept
{
data = other.data;
return *this;
}
inline CharPointer_UTF8 operator= (const CharType* text) noexcept
{
data = const_cast<CharType*> (text);
return *this;
}
/** This is a pointer comparison, it doesn't compare the actual text. */
inline bool operator== (CharPointer_UTF8 other) const noexcept { return data == other.data; }
inline bool operator!= (CharPointer_UTF8 other) const noexcept { return data != other.data; }
inline bool operator<= (CharPointer_UTF8 other) const noexcept { return data <= other.data; }
inline bool operator< (CharPointer_UTF8 other) const noexcept { return data < other.data; }
inline bool operator>= (CharPointer_UTF8 other) const noexcept { return data >= other.data; }
inline bool operator> (CharPointer_UTF8 other) const noexcept { return data > other.data; }
/** Returns the address that this pointer is pointing to. */
inline CharType* getAddress() const noexcept { return data; }
/** Returns the address that this pointer is pointing to. */
inline operator const CharType*() const noexcept { return data; }
/** Returns true if this pointer is pointing to a null character. */
inline bool isEmpty() const noexcept { return *data == 0; }
/** Returns the unicode character that this pointer is pointing to. */
juce_wchar operator*() const noexcept
{
const signed char byte = (signed char) *data;
if (byte >= 0)
return (juce_wchar) (uint8) byte;
uint32 n = (uint32) (uint8) byte;
uint32 mask = 0x7f;
uint32 bit = 0x40;
int numExtraValues = 0;
while ((n & bit) != 0 && bit > 0x8)
{
mask >>= 1;
++numExtraValues;
bit >>= 1;
}
n &= mask;
for (int i = 1; i <= numExtraValues; ++i)
{
const uint32 nextByte = (uint32) (uint8) data[i];
if ((nextByte & 0xc0) != 0x80)
break;
n <<= 6;
n |= (nextByte & 0x3f);
}
return (juce_wchar) n;
}
/** Moves this pointer along to the next character in the string. */
CharPointer_UTF8& operator++() noexcept
{
jassert (*data != 0); // trying to advance past the end of the string?
const signed char n = (signed char) *data++;
if (n < 0)
{
juce_wchar bit = 0x40;
while ((n & bit) != 0 && bit > 0x8)
{
++data;
bit >>= 1;
}
}
return *this;
}
/** Moves this pointer back to the previous character in the string. */
CharPointer_UTF8 operator--() noexcept
{
int count = 0;
while ((*--data & 0xc0) == 0x80 && ++count < 4)
{}
return *this;
}
/** Returns the character that this pointer is currently pointing to, and then
advances the pointer to point to the next character. */
juce_wchar getAndAdvance() noexcept
{
const signed char byte = (signed char) *data++;
if (byte >= 0)
return (juce_wchar) (uint8) byte;
uint32 n = (uint32) (uint8) byte;
uint32 mask = 0x7f;
uint32 bit = 0x40;
int numExtraValues = 0;
while ((n & bit) != 0 && bit > 0x8)
{
mask >>= 1;
++numExtraValues;
bit >>= 1;
}
n &= mask;
while (--numExtraValues >= 0)
{
const uint32 nextByte = (uint32) (uint8) *data;
if ((nextByte & 0xc0) != 0x80)
break;
++data;
n <<= 6;
n |= (nextByte & 0x3f);
}
return (juce_wchar) n;
}
/** Moves this pointer along to the next character in the string. */
CharPointer_UTF8 operator++ (int) noexcept
{
CharPointer_UTF8 temp (*this);
++*this;
return temp;
}
/** Moves this pointer forwards by the specified number of characters. */
void operator+= (int numToSkip) noexcept
{
if (numToSkip < 0)
{
while (++numToSkip <= 0)
--*this;
}
else
{
while (--numToSkip >= 0)
++*this;
}
}
/** Moves this pointer backwards by the specified number of characters. */
void operator-= (int numToSkip) noexcept
{
operator+= (-numToSkip);
}
/** Returns the character at a given character index from the start of the string. */
juce_wchar operator[] (int characterIndex) const noexcept
{
CharPointer_UTF8 p (*this);
p += characterIndex;
return *p;
}
/** Returns a pointer which is moved forwards from this one by the specified number of characters. */
CharPointer_UTF8 operator+ (int numToSkip) const noexcept
{
CharPointer_UTF8 p (*this);
p += numToSkip;
return p;
}
/** Returns a pointer which is moved backwards from this one by the specified number of characters. */
CharPointer_UTF8 operator- (int numToSkip) const noexcept
{
CharPointer_UTF8 p (*this);
p += -numToSkip;
return p;
}
/** Returns the number of characters in this string. */
size_t length() const noexcept
{
const CharType* d = data;
size_t count = 0;
for (;;)
{
const uint32 n = (uint32) (uint8) *d++;
if ((n & 0x80) != 0)
{
while ((*d & 0xc0) == 0x80)
++d;
}
else if (n == 0)
break;
++count;
}
return count;
}
/** Returns the number of characters in this string, or the given value, whichever is lower. */
size_t lengthUpTo (const size_t maxCharsToCount) const noexcept
{
return CharacterFunctions::lengthUpTo (*this, maxCharsToCount);
}
/** Returns the number of characters in this string, or up to the given end pointer, whichever is lower. */
size_t lengthUpTo (const CharPointer_UTF8 end) const noexcept
{
return CharacterFunctions::lengthUpTo (*this, end);
}
/** Returns the number of bytes that are used to represent this string.
This includes the terminating null character.
*/
size_t sizeInBytes() const noexcept
{
jassert (data != nullptr);
return strlen (data) + 1;
}
/** Returns the number of bytes that would be needed to represent the given
unicode character in this encoding format.
*/
static size_t getBytesRequiredFor (const juce_wchar charToWrite) noexcept
{
size_t num = 1;
const uint32 c = (uint32) charToWrite;
if (c >= 0x80)
{
++num;
if (c >= 0x800)
{
++num;
if (c >= 0x10000)
++num;
}
}
return num;
}
/** Returns the number of bytes that would be needed to represent the given
string in this encoding format.
The value returned does NOT include the terminating null character.
*/
template <class CharPointer>
static size_t getBytesRequiredFor (CharPointer text) noexcept
{
size_t count = 0;
while (juce_wchar n = text.getAndAdvance())
count += getBytesRequiredFor (n);
return count;
}
/** Returns a pointer to the null character that terminates this string. */
CharPointer_UTF8 findTerminatingNull() const noexcept
{
return CharPointer_UTF8 (data + strlen (data));
}
/** Writes a unicode character to this string, and advances this pointer to point to the next position. */
void write (const juce_wchar charToWrite) noexcept
{
const uint32 c = (uint32) charToWrite;
if (c >= 0x80)
{
int numExtraBytes = 1;
if (c >= 0x800)
{
++numExtraBytes;
if (c >= 0x10000)
++numExtraBytes;
}
*data++ = (CharType) ((uint32) (0xff << (7 - numExtraBytes)) | (c >> (numExtraBytes * 6)));
while (--numExtraBytes >= 0)
*data++ = (CharType) (0x80 | (0x3f & (c >> (numExtraBytes * 6))));
}
else
{
*data++ = (CharType) c;
}
}
/** Writes a null character to this string (leaving the pointer's position unchanged). */
inline void writeNull() const noexcept
{
*data = 0;
}
/** Copies a source string to this pointer, advancing this pointer as it goes. */
template <typename CharPointer>
void writeAll (const CharPointer src) noexcept
{
CharacterFunctions::copyAll (*this, src);
}
/** Copies a source string to this pointer, advancing this pointer as it goes. */
void writeAll (const CharPointer_UTF8 src) noexcept
{
const CharType* s = src.data;
while ((*data = *s) != 0)
{
++data;
++s;
}
}
/** Copies a source string to this pointer, advancing this pointer as it goes.
The maxDestBytes parameter specifies the maximum number of bytes that can be written
to the destination buffer before stopping.
*/
template <typename CharPointer>
size_t writeWithDestByteLimit (const CharPointer src, const size_t maxDestBytes) noexcept
{
return CharacterFunctions::copyWithDestByteLimit (*this, src, maxDestBytes);
}
/** Copies a source string to this pointer, advancing this pointer as it goes.
The maxChars parameter specifies the maximum number of characters that can be
written to the destination buffer before stopping (including the terminating null).
*/
template <typename CharPointer>
void writeWithCharLimit (const CharPointer src, const int maxChars) noexcept
{
CharacterFunctions::copyWithCharLimit (*this, src, maxChars);
}
/** Compares this string with another one. */
template <typename CharPointer>
int compare (const CharPointer other) const noexcept
{
return CharacterFunctions::compare (*this, other);
}
/** Compares this string with another one, up to a specified number of characters. */
template <typename CharPointer>
int compareUpTo (const CharPointer other, const int maxChars) const noexcept
{
return CharacterFunctions::compareUpTo (*this, other, maxChars);
}
/** Compares this string with another one. */
template <typename CharPointer>
int compareIgnoreCase (const CharPointer other) const noexcept
{
return CharacterFunctions::compareIgnoreCase (*this, other);
}
/** Compares this string with another one. */
int compareIgnoreCase (const CharPointer_UTF8 other) const noexcept
{
return CharacterFunctions::compareIgnoreCase (*this, other);
}
/** Compares this string with another one, up to a specified number of characters. */
template <typename CharPointer>
int compareIgnoreCaseUpTo (const CharPointer other, const int maxChars) const noexcept
{
return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars);
}
/** Returns the character index of a substring, or -1 if it isn't found. */
template <typename CharPointer>
int indexOf (const CharPointer stringToFind) const noexcept
{
return CharacterFunctions::indexOf (*this, stringToFind);
}
/** Returns the character index of a unicode character, or -1 if it isn't found. */
int indexOf (const juce_wchar charToFind) const noexcept
{
return CharacterFunctions::indexOfChar (*this, charToFind);
}
/** Returns the character index of a unicode character, or -1 if it isn't found. */
int indexOf (const juce_wchar charToFind, const bool ignoreCase) const noexcept
{
return ignoreCase ? CharacterFunctions::indexOfCharIgnoreCase (*this, charToFind)
: CharacterFunctions::indexOfChar (*this, charToFind);
}
/** Returns true if the first character of this string is whitespace. */
bool isWhitespace() const noexcept { const CharType c = *data; return c == ' ' || (c <= 13 && c >= 9); }
/** Returns true if the first character of this string is a digit. */
bool isDigit() const noexcept { const CharType c = *data; return c >= '0' && c <= '9'; }
/** Returns true if the first character of this string is a letter. */
bool isLetter() const noexcept { return CharacterFunctions::isLetter (operator*()) != 0; }
/** Returns true if the first character of this string is a letter or digit. */
bool isLetterOrDigit() const noexcept { return CharacterFunctions::isLetterOrDigit (operator*()) != 0; }
/** Returns true if the first character of this string is upper-case. */
bool isUpperCase() const noexcept { return CharacterFunctions::isUpperCase (operator*()) != 0; }
/** Returns true if the first character of this string is lower-case. */
bool isLowerCase() const noexcept { return CharacterFunctions::isLowerCase (operator*()) != 0; }
/** Returns an upper-case version of the first character of this string. */
juce_wchar toUpperCase() const noexcept { return CharacterFunctions::toUpperCase (operator*()); }
/** Returns a lower-case version of the first character of this string. */
juce_wchar toLowerCase() const noexcept { return CharacterFunctions::toLowerCase (operator*()); }
/** Parses this string as a 32-bit integer. */
int getIntValue32() const noexcept { return atoi (data); }
/** Parses this string as a 64-bit integer. */
int64 getIntValue64() const noexcept
{
#if JUCE_LINUX || JUCE_ANDROID || JUCE_MINGW
return atoll (data);
#elif JUCE_WINDOWS
return _atoi64 (data);
#else
return CharacterFunctions::getIntValue <int64, CharPointer_UTF8> (*this);
#endif
}
/** Parses this string as a floating point double. */
double getDoubleValue() const noexcept { return CharacterFunctions::getDoubleValue (*this); }
/** Returns the first non-whitespace character in the string. */
CharPointer_UTF8 findEndOfWhitespace() const noexcept { return CharacterFunctions::findEndOfWhitespace (*this); }
/** Returns true if the given unicode character can be represented in this encoding. */
static bool canRepresent (juce_wchar character) noexcept
{
return ((unsigned int) character) < (unsigned int) 0x10ffff;
}
/** Returns true if this data contains a valid string in this encoding. */
static bool isValidString (const CharType* dataToTest, int maxBytesToRead)
{
while (--maxBytesToRead >= 0 && *dataToTest != 0)
{
const signed char byte = (signed char) *dataToTest++;
if (byte < 0)
{
int bit = 0x40;
int numExtraValues = 0;
while ((byte & bit) != 0)
{
if (bit < 8)
return false;
++numExtraValues;
bit >>= 1;
if (bit == 8 && (numExtraValues > maxBytesToRead
|| *CharPointer_UTF8 (dataToTest - 1) > 0x10ffff))
return false;
}
if (numExtraValues == 0)
return false;
maxBytesToRead -= numExtraValues;
if (maxBytesToRead < 0)
return false;
while (--numExtraValues >= 0)
if ((*dataToTest++ & 0xc0) != 0x80)
return false;
}
}
return true;
}
/** Atomically swaps this pointer for a new value, returning the previous value. */
CharPointer_UTF8 atomicSwap (const CharPointer_UTF8 newValue)
{
return CharPointer_UTF8 (reinterpret_cast<Atomic<CharType*>&> (data).exchange (newValue.data));
}
/** These values are the byte-order mark (BOM) values for a UTF-8 stream. */
enum
{
byteOrderMark1 = 0xef,
byteOrderMark2 = 0xbb,
byteOrderMark3 = 0xbf
};
/** Returns true if the first three bytes in this pointer are the UTF8 byte-order mark (BOM).
The pointer must not be null, and must point to at least 3 valid bytes.
*/
static bool isByteOrderMark (const void* possibleByteOrder) noexcept
{
jassert (possibleByteOrder != nullptr);
const uint8* const c = static_cast<const uint8*> (possibleByteOrder);
return c[0] == (uint8) byteOrderMark1
&& c[1] == (uint8) byteOrderMark2
&& c[2] == (uint8) byteOrderMark3;
}
private:
CharType* data;
};
#endif // JUCE_CHARPOINTER_UTF8_H_INCLUDED

+ 171
- 0
source/modules/juce_audio_graph/text/juce_CharacterFunctions.cpp View File

@@ -0,0 +1,171 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
//==============================================================================
juce_wchar CharacterFunctions::toUpperCase (const juce_wchar character) noexcept
{
return (juce_wchar) towupper ((wint_t) character);
}
juce_wchar CharacterFunctions::toLowerCase (const juce_wchar character) noexcept
{
return (juce_wchar) towlower ((wint_t) character);
}
bool CharacterFunctions::isUpperCase (const juce_wchar character) noexcept
{
#if JUCE_WINDOWS
return iswupper ((wint_t) character) != 0;
#else
return toLowerCase (character) != character;
#endif
}
bool CharacterFunctions::isLowerCase (const juce_wchar character) noexcept
{
#if JUCE_WINDOWS
return iswlower ((wint_t) character) != 0;
#else
return toUpperCase (character) != character;
#endif
}
//==============================================================================
bool CharacterFunctions::isWhitespace (const char character) noexcept
{
return character == ' ' || (character <= 13 && character >= 9);
}
bool CharacterFunctions::isWhitespace (const juce_wchar character) noexcept
{
return iswspace ((wint_t) character) != 0;
}
bool CharacterFunctions::isDigit (const char character) noexcept
{
return (character >= '0' && character <= '9');
}
bool CharacterFunctions::isDigit (const juce_wchar character) noexcept
{
return iswdigit ((wint_t) character) != 0;
}
bool CharacterFunctions::isLetter (const char character) noexcept
{
return (character >= 'a' && character <= 'z')
|| (character >= 'A' && character <= 'Z');
}
bool CharacterFunctions::isLetter (const juce_wchar character) noexcept
{
return iswalpha ((wint_t) character) != 0;
}
bool CharacterFunctions::isLetterOrDigit (const char character) noexcept
{
return (character >= 'a' && character <= 'z')
|| (character >= 'A' && character <= 'Z')
|| (character >= '0' && character <= '9');
}
bool CharacterFunctions::isLetterOrDigit (const juce_wchar character) noexcept
{
return iswalnum ((wint_t) character) != 0;
}
bool CharacterFunctions::isPrintable (const char character) noexcept
{
return (character >= ' ' && character <= '~');
}
bool CharacterFunctions::isPrintable (const juce_wchar character) noexcept
{
return iswprint ((wint_t) character) != 0;
}
int CharacterFunctions::getHexDigitValue (const juce_wchar digit) noexcept
{
unsigned int d = (unsigned int) digit - '0';
if (d < (unsigned int) 10)
return (int) d;
d += (unsigned int) ('0' - 'a');
if (d < (unsigned int) 6)
return (int) d + 10;
d += (unsigned int) ('a' - 'A');
if (d < (unsigned int) 6)
return (int) d + 10;
return -1;
}
double CharacterFunctions::mulexp10 (const double value, int exponent) noexcept
{
if (exponent == 0)
return value;
if (value == 0)
return 0;
const bool negative = (exponent < 0);
if (negative)
exponent = -exponent;
double result = 1.0, power = 10.0;
for (int bit = 1; exponent != 0; bit <<= 1)
{
if ((exponent & bit) != 0)
{
exponent ^= bit;
result *= power;
if (exponent == 0)
break;
}
power *= power;
}
return negative ? (value / result) : (value * result);
}
juce_wchar CharacterFunctions::getUnicodeCharFromWindows1252Codepage (const uint8 c) noexcept
{
if (c < 0x80 || c >= 0xa0)
return (juce_wchar) c;
static const uint16 lookup[] = { 0x20AC, 0x0007, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0007, 0x017D, 0x0007,
0x0007, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0007, 0x017E, 0x0178 };
return (juce_wchar) lookup[c - 0x80];
}

+ 620
- 0
source/modules/juce_audio_graph/text/juce_CharacterFunctions.h View File

@@ -0,0 +1,620 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_CHARACTERFUNCTIONS_H_INCLUDED
#define JUCE_CHARACTERFUNCTIONS_H_INCLUDED
/** This macro will be set to 1 if the compiler's native wchar_t is an 8-bit type. */
#define JUCE_NATIVE_WCHAR_IS_UTF8 1
/** A platform-independent 32-bit unicode character type. */
typedef uint32 juce_wchar;
//==============================================================================
/**
A collection of functions for manipulating characters and character strings.
Most of these methods are designed for internal use by the String and CharPointer
classes, but some of them may be useful to call directly.
@see String, CharPointer_UTF8, CharPointer_UTF16, CharPointer_UTF32
*/
class JUCE_API CharacterFunctions
{
public:
//==============================================================================
/** Converts a character to upper-case. */
static juce_wchar toUpperCase (juce_wchar character) noexcept;
/** Converts a character to lower-case. */
static juce_wchar toLowerCase (juce_wchar character) noexcept;
/** Checks whether a unicode character is upper-case. */
static bool isUpperCase (juce_wchar character) noexcept;
/** Checks whether a unicode character is lower-case. */
static bool isLowerCase (juce_wchar character) noexcept;
/** Checks whether a character is whitespace. */
static bool isWhitespace (char character) noexcept;
/** Checks whether a character is whitespace. */
static bool isWhitespace (juce_wchar character) noexcept;
/** Checks whether a character is a digit. */
static bool isDigit (char character) noexcept;
/** Checks whether a character is a digit. */
static bool isDigit (juce_wchar character) noexcept;
/** Checks whether a character is alphabetic. */
static bool isLetter (char character) noexcept;
/** Checks whether a character is alphabetic. */
static bool isLetter (juce_wchar character) noexcept;
/** Checks whether a character is alphabetic or numeric. */
static bool isLetterOrDigit (char character) noexcept;
/** Checks whether a character is alphabetic or numeric. */
static bool isLetterOrDigit (juce_wchar character) noexcept;
/** Checks whether a character is a printable character, i.e. alphabetic, numeric,
a punctuation character or a space.
*/
static bool isPrintable (char character) noexcept;
/** Checks whether a character is a printable character, i.e. alphabetic, numeric,
a punctuation character or a space.
*/
static bool isPrintable (juce_wchar character) noexcept;
/** Returns 0 to 16 for '0' to 'F", or -1 for characters that aren't a legal hex digit. */
static int getHexDigitValue (juce_wchar digit) noexcept;
/** Converts a byte of Windows 1252 codepage to unicode. */
static juce_wchar getUnicodeCharFromWindows1252Codepage (uint8 windows1252Char) noexcept;
//==============================================================================
/** Parses a character string to read a floating-point number.
Note that this will advance the pointer that is passed in, leaving it at
the end of the number.
*/
template <typename CharPointerType>
static double readDoubleValue (CharPointerType& text) noexcept
{
double result[3] = { 0 }, accumulator[2] = { 0 };
int exponentAdjustment[2] = { 0 }, exponentAccumulator[2] = { -1, -1 };
int exponent = 0, decPointIndex = 0, digit = 0;
int lastDigit = 0, numSignificantDigits = 0;
bool isNegative = false, digitsFound = false;
const int maxSignificantDigits = 15 + 2;
text = text.findEndOfWhitespace();
juce_wchar c = *text;
switch (c)
{
case '-': isNegative = true; // fall-through..
case '+': c = *++text;
}
switch (c)
{
case 'n':
case 'N':
if ((text[1] == 'a' || text[1] == 'A') && (text[2] == 'n' || text[2] == 'N'))
return std::numeric_limits<double>::quiet_NaN();
break;
case 'i':
case 'I':
if ((text[1] == 'n' || text[1] == 'N') && (text[2] == 'f' || text[2] == 'F'))
return std::numeric_limits<double>::infinity();
break;
}
for (;;)
{
if (text.isDigit())
{
lastDigit = digit;
digit = (int) text.getAndAdvance() - '0';
digitsFound = true;
if (decPointIndex != 0)
exponentAdjustment[1]++;
if (numSignificantDigits == 0 && digit == 0)
continue;
if (++numSignificantDigits > maxSignificantDigits)
{
if (digit > 5)
++accumulator [decPointIndex];
else if (digit == 5 && (lastDigit & 1) != 0)
++accumulator [decPointIndex];
if (decPointIndex > 0)
exponentAdjustment[1]--;
else
exponentAdjustment[0]++;
while (text.isDigit())
{
++text;
if (decPointIndex == 0)
exponentAdjustment[0]++;
}
}
else
{
const double maxAccumulatorValue = (double) ((std::numeric_limits<unsigned int>::max() - 9) / 10);
if (accumulator [decPointIndex] > maxAccumulatorValue)
{
result [decPointIndex] = mulexp10 (result [decPointIndex], exponentAccumulator [decPointIndex])
+ accumulator [decPointIndex];
accumulator [decPointIndex] = 0;
exponentAccumulator [decPointIndex] = 0;
}
accumulator [decPointIndex] = accumulator[decPointIndex] * 10 + digit;
exponentAccumulator [decPointIndex]++;
}
}
else if (decPointIndex == 0 && *text == '.')
{
++text;
decPointIndex = 1;
if (numSignificantDigits > maxSignificantDigits)
{
while (text.isDigit())
++text;
break;
}
}
else
{
break;
}
}
result[0] = mulexp10 (result[0], exponentAccumulator[0]) + accumulator[0];
if (decPointIndex != 0)
result[1] = mulexp10 (result[1], exponentAccumulator[1]) + accumulator[1];
c = *text;
if ((c == 'e' || c == 'E') && digitsFound)
{
bool negativeExponent = false;
switch (*++text)
{
case '-': negativeExponent = true; // fall-through..
case '+': ++text;
}
while (text.isDigit())
exponent = (exponent * 10) + ((int) text.getAndAdvance() - '0');
if (negativeExponent)
exponent = -exponent;
}
double r = mulexp10 (result[0], exponent + exponentAdjustment[0]);
if (decPointIndex != 0)
r += mulexp10 (result[1], exponent - exponentAdjustment[1]);
return isNegative ? -r : r;
}
/** Parses a character string, to read a floating-point value. */
template <typename CharPointerType>
static double getDoubleValue (CharPointerType text) noexcept
{
return readDoubleValue (text);
}
//==============================================================================
/** Parses a character string, to read an integer value. */
template <typename IntType, typename CharPointerType>
static IntType getIntValue (const CharPointerType text) noexcept
{
IntType v = 0;
CharPointerType s (text.findEndOfWhitespace());
const bool isNeg = *s == '-';
if (isNeg)
++s;
for (;;)
{
const juce_wchar c = s.getAndAdvance();
if (c >= '0' && c <= '9')
v = v * 10 + (IntType) (c - '0');
else
break;
}
return isNeg ? -v : v;
}
template <typename ResultType>
struct HexParser
{
template <typename CharPointerType>
static ResultType parse (CharPointerType t) noexcept
{
ResultType result = 0;
while (! t.isEmpty())
{
const int hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance());
if (hexValue >= 0)
result = (result << 4) | hexValue;
}
return result;
}
};
//==============================================================================
/** Counts the number of characters in a given string, stopping if the count exceeds
a specified limit. */
template <typename CharPointerType>
static size_t lengthUpTo (CharPointerType text, const size_t maxCharsToCount) noexcept
{
size_t len = 0;
while (len < maxCharsToCount && text.getAndAdvance() != 0)
++len;
return len;
}
/** Counts the number of characters in a given string, stopping if the count exceeds
a specified end-pointer. */
template <typename CharPointerType>
static size_t lengthUpTo (CharPointerType start, const CharPointerType end) noexcept
{
size_t len = 0;
while (start < end && start.getAndAdvance() != 0)
++len;
return len;
}
/** Copies null-terminated characters from one string to another. */
template <typename DestCharPointerType, typename SrcCharPointerType>
static void copyAll (DestCharPointerType& dest, SrcCharPointerType src) noexcept
{
while (juce_wchar c = src.getAndAdvance())
dest.write (c);
dest.writeNull();
}
/** Copies characters from one string to another, up to a null terminator
or a given byte size limit. */
template <typename DestCharPointerType, typename SrcCharPointerType>
static size_t copyWithDestByteLimit (DestCharPointerType& dest, SrcCharPointerType src, size_t maxBytesToWrite) noexcept
{
typename DestCharPointerType::CharType const* const startAddress = dest.getAddress();
ssize_t maxBytes = (ssize_t) maxBytesToWrite;
maxBytes -= sizeof (typename DestCharPointerType::CharType); // (allow for a terminating null)
for (;;)
{
const juce_wchar c = src.getAndAdvance();
const size_t bytesNeeded = DestCharPointerType::getBytesRequiredFor (c);
maxBytes -= bytesNeeded;
if (c == 0 || maxBytes < 0)
break;
dest.write (c);
}
dest.writeNull();
return (size_t) getAddressDifference (dest.getAddress(), startAddress)
+ sizeof (typename DestCharPointerType::CharType);
}
/** Copies characters from one string to another, up to a null terminator
or a given maximum number of characters. */
template <typename DestCharPointerType, typename SrcCharPointerType>
static void copyWithCharLimit (DestCharPointerType& dest, SrcCharPointerType src, int maxChars) noexcept
{
while (--maxChars > 0)
{
const juce_wchar c = src.getAndAdvance();
if (c == 0)
break;
dest.write (c);
}
dest.writeNull();
}
/** Compares two characters. */
static inline int compare (juce_wchar char1, juce_wchar char2) noexcept
{
if (int diff = static_cast<int> (char1) - static_cast<int> (char2))
return diff < 0 ? -1 : 1;
return 0;
}
/** Compares two null-terminated character strings. */
template <typename CharPointerType1, typename CharPointerType2>
static int compare (CharPointerType1 s1, CharPointerType2 s2) noexcept
{
for (;;)
{
const juce_wchar c1 = s1.getAndAdvance();
if (int diff = compare (c1, s2.getAndAdvance()))
return diff;
if (c1 == 0)
break;
}
return 0;
}
/** Compares two null-terminated character strings, up to a given number of characters. */
template <typename CharPointerType1, typename CharPointerType2>
static int compareUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept
{
while (--maxChars >= 0)
{
const juce_wchar c1 = s1.getAndAdvance();
if (int diff = compare (c1, s2.getAndAdvance()))
return diff;
if (c1 == 0)
break;
}
return 0;
}
/** Compares two characters, using a case-independant match. */
static inline int compareIgnoreCase (juce_wchar char1, juce_wchar char2) noexcept
{
return char1 != char2 ? compare (toUpperCase (char1), toUpperCase (char2)) : 0;
}
/** Compares two null-terminated character strings, using a case-independant match. */
template <typename CharPointerType1, typename CharPointerType2>
static int compareIgnoreCase (CharPointerType1 s1, CharPointerType2 s2) noexcept
{
for (;;)
{
const juce_wchar c1 = s1.getAndAdvance();
if (int diff = compareIgnoreCase (c1, s2.getAndAdvance()))
return diff;
if (c1 == 0)
break;
}
return 0;
}
/** Compares two null-terminated character strings, using a case-independent match. */
template <typename CharPointerType1, typename CharPointerType2>
static int compareIgnoreCaseUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept
{
while (--maxChars >= 0)
{
const juce_wchar c1 = s1.getAndAdvance();
if (int diff = compareIgnoreCase (c1, s2.getAndAdvance()))
return diff;
if (c1 == 0)
break;
}
return 0;
}
/** Finds the character index of a given substring in another string.
Returns -1 if the substring is not found.
*/
template <typename CharPointerType1, typename CharPointerType2>
static int indexOf (CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept
{
int index = 0;
const int substringLength = (int) substringToLookFor.length();
for (;;)
{
if (textToSearch.compareUpTo (substringToLookFor, substringLength) == 0)
return index;
if (textToSearch.getAndAdvance() == 0)
return -1;
++index;
}
}
/** Returns a pointer to the first occurrence of a substring in a string.
If the substring is not found, this will return a pointer to the string's
null terminator.
*/
template <typename CharPointerType1, typename CharPointerType2>
static CharPointerType1 find (CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept
{
const int substringLength = (int) substringToLookFor.length();
while (textToSearch.compareUpTo (substringToLookFor, substringLength) != 0
&& ! textToSearch.isEmpty())
++textToSearch;
return textToSearch;
}
/** Returns a pointer to the first occurrence of a substring in a string.
If the substring is not found, this will return a pointer to the string's
null terminator.
*/
template <typename CharPointerType>
static CharPointerType find (CharPointerType textToSearch, const juce_wchar charToLookFor) noexcept
{
for (;; ++textToSearch)
{
const juce_wchar c = *textToSearch;
if (c == charToLookFor || c == 0)
break;
}
return textToSearch;
}
/** Finds the character index of a given substring in another string, using
a case-independent match.
Returns -1 if the substring is not found.
*/
template <typename CharPointerType1, typename CharPointerType2>
static int indexOfIgnoreCase (CharPointerType1 haystack, const CharPointerType2 needle) noexcept
{
int index = 0;
const int needleLength = (int) needle.length();
for (;;)
{
if (haystack.compareIgnoreCaseUpTo (needle, needleLength) == 0)
return index;
if (haystack.getAndAdvance() == 0)
return -1;
++index;
}
}
/** Finds the character index of a given character in another string.
Returns -1 if the character is not found.
*/
template <typename Type>
static int indexOfChar (Type text, const juce_wchar charToFind) noexcept
{
int i = 0;
while (! text.isEmpty())
{
if (text.getAndAdvance() == charToFind)
return i;
++i;
}
return -1;
}
/** Finds the character index of a given character in another string, using
a case-independent match.
Returns -1 if the character is not found.
*/
template <typename Type>
static int indexOfCharIgnoreCase (Type text, juce_wchar charToFind) noexcept
{
charToFind = CharacterFunctions::toLowerCase (charToFind);
int i = 0;
while (! text.isEmpty())
{
if (text.toLowerCase() == charToFind)
return i;
++text;
++i;
}
return -1;
}
/** Returns a pointer to the first non-whitespace character in a string.
If the string contains only whitespace, this will return a pointer
to its null terminator.
*/
template <typename Type>
static Type findEndOfWhitespace (Type text) noexcept
{
while (text.isWhitespace())
++text;
return text;
}
/** Returns a pointer to the first character in the string which is found in
the breakCharacters string.
*/
template <typename Type, typename BreakType>
static Type findEndOfToken (Type text, const BreakType breakCharacters, const Type quoteCharacters)
{
juce_wchar currentQuoteChar = 0;
while (! text.isEmpty())
{
const juce_wchar c = text.getAndAdvance();
if (currentQuoteChar == 0 && breakCharacters.indexOf (c) >= 0)
{
--text;
break;
}
if (quoteCharacters.indexOf (c) >= 0)
{
if (currentQuoteChar == 0)
currentQuoteChar = c;
else if (currentQuoteChar == c)
currentQuoteChar = 0;
}
}
return text;
}
private:
static double mulexp10 (const double value, int exponent) noexcept;
};
#endif // JUCE_CHARACTERFUNCTIONS_H_INCLUDED

+ 90
- 0
source/modules/juce_audio_graph/text/juce_NewLine.h View File

@@ -0,0 +1,90 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_NEWLINE_H_INCLUDED
#define JUCE_NEWLINE_H_INCLUDED
//==============================================================================
/** This class is used for represent a new-line character sequence.
To write a new-line to a stream, you can use the predefined 'newLine' variable, e.g.
@code
myOutputStream << "Hello World" << newLine << newLine;
@endcode
The exact character sequence that will be used for the new-line can be set and
retrieved with OutputStream::setNewLineString() and OutputStream::getNewLineString().
*/
class JUCE_API NewLine
{
public:
/** Returns the default new-line sequence that the library uses.
@see OutputStream::setNewLineString()
*/
static const char* getDefault() noexcept { return "\r\n"; }
/** Returns the default new-line sequence that the library uses.
@see getDefault()
*/
operator String() const { return getDefault(); }
/** Returns the default new-line sequence that the library uses.
@see OutputStream::setNewLineString()
*/
operator StringRef() const noexcept { return getDefault(); }
};
//==============================================================================
/** A predefined object representing a new-line, which can be written to a string or stream.
To write a new-line to a stream, you can use the predefined 'newLine' variable like this:
@code
myOutputStream << "Hello World" << newLine << newLine;
@endcode
*/
extern NewLine newLine;
//==============================================================================
/** Writes a new-line sequence to a string.
You can use the predefined object 'newLine' to invoke this, e.g.
@code
myString << "Hello World" << newLine << newLine;
@endcode
*/
inline String& operator<< (String& string1, const NewLine&) { return string1 += NewLine::getDefault(); }
inline String& operator+= (String& s1, const NewLine&) { return s1 += NewLine::getDefault(); }
inline String operator+ (const NewLine&, const NewLine&) { return String (NewLine::getDefault()) + NewLine::getDefault(); }
inline String operator+ (String s1, const NewLine&) { return s1 += NewLine::getDefault(); }
inline String operator+ (const NewLine&, const char* s2) { return String (NewLine::getDefault()) + s2; }
#endif // JUCE_NEWLINE_H_INCLUDED

+ 1972
- 0
source/modules/juce_audio_graph/text/juce_String.cpp
File diff suppressed because it is too large
View File


+ 1181
- 0
source/modules/juce_audio_graph/text/juce_String.h
File diff suppressed because it is too large
View File


+ 136
- 0
source/modules/juce_audio_graph/text/juce_StringRef.h View File

@@ -0,0 +1,136 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_STRINGREF_H_INCLUDED
#define JUCE_STRINGREF_H_INCLUDED
//==============================================================================
/**
A simple class for holding temporary references to a string literal or String.
Unlike a real String object, the StringRef does not allocate any memory or
take ownership of the strings you give to it - it simply holds a reference to
a string that has been allocated elsewhere.
The main purpose of the class is to be used instead of a const String& as the type
of function arguments where the caller may pass either a string literal or a String
object. This means that when the called uses a string literal, there's no need
for an temporary String object to be allocated, and this cuts down overheads
substantially.
Because the class is simply a wrapper around a pointer, you should always pass
it by value, not by reference.
@code
void myStringFunction1 (const String&);
void myStringFunction2 (StringRef);
myStringFunction1 ("abc"); // Implicitly allocates a temporary String object.
myStringFunction2 ("abc"); // Much faster, as no local allocations are needed.
@endcode
For examples of it in use, see the XmlElement or StringArray classes.
Bear in mind that there are still many cases where it's better to use an argument
which is a const String&. For example if the function stores the string or needs
to internally create a String from the argument, then it's better for the original
argument to already be a String.
@see String
*/
class JUCE_API StringRef
{
public:
/** Creates a StringRef from a raw string literal.
The StringRef object does NOT take ownership or copy this data, so you must
ensure that the data does not change during the lifetime of the StringRef.
Note that this pointer not be null!
*/
StringRef (const char* stringLiteral) noexcept;
/** Creates a StringRef from a raw char pointer.
The StringRef object does NOT take ownership or copy this data, so you must
ensure that the data does not change during the lifetime of the StringRef.
*/
StringRef (String::CharPointerType stringLiteral) noexcept;
/** Creates a StringRef from a String.
The StringRef object does NOT take ownership or copy the data from the String,
so you must ensure that the String is not modified or deleted during the lifetime
of the StringRef.
*/
StringRef (const String& string) noexcept;
/** Creates a StringRef pointer to an empty string. */
StringRef() noexcept;
//==============================================================================
/** Returns a raw pointer to the underlying string data. */
operator const String::CharPointerType::CharType*() const noexcept { return text.getAddress(); }
/** Returns a pointer to the underlying string data as a char pointer object. */
operator String::CharPointerType() const noexcept { return text; }
/** Returns true if the string is empty. */
bool isEmpty() const noexcept { return text.isEmpty(); }
/** Returns true if the string is not empty. */
bool isNotEmpty() const noexcept { return ! text.isEmpty(); }
/** Returns the number of characters in the string. */
int length() const noexcept { return (int) text.length(); }
/** Retrieves a character by index. */
juce_wchar operator[] (int index) const noexcept { return text[index]; }
/** Compares this StringRef with a String. */
bool operator== (const String& s) const noexcept { return text.compare (s.getCharPointer()) == 0; }
/** Compares this StringRef with a String. */
bool operator!= (const String& s) const noexcept { return text.compare (s.getCharPointer()) != 0; }
/** Case-sensitive comparison of two StringRefs. */
bool operator== (StringRef s) const noexcept { return text.compare (s.text) == 0; }
/** Case-sensitive comparison of two StringRefs. */
bool operator!= (StringRef s) const noexcept { return text.compare (s.text) != 0; }
//==============================================================================
/** The text that is referenced. */
String::CharPointerType text;
};
//==============================================================================
/** Case-sensitive comparison of two strings. */
JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, StringRef string2) noexcept;
/** Case-sensitive comparison of two strings. */
JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, StringRef string2) noexcept;
inline String operator+ (String s1, StringRef s2) { return s1 += String (s2.text); }
inline String operator+ (StringRef s1, const String& s2) { return String (s1.text) + s2; }
inline String operator+ (const char* s1, StringRef s2) { return String (s1) + String (s2.text); }
inline String operator+ (StringRef s1, const char* s2) { return String (s1.text) + String (s2); }
#endif // JUCE_STRINGREF_H_INCLUDED

Loading…
Cancel
Save