@@ -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) | |||
# ---------------------------------------------------------------------------------------------------------------------------- |
@@ -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 |
@@ -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 |
@@ -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()); | |||
} | |||
} | |||
} |
@@ -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 |
@@ -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 |
@@ -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) | |||
{} |
@@ -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 |
@@ -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" | |||
} |
@@ -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 |
@@ -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" |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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 |
@@ -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; | |||
} |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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; | |||
} |
@@ -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 |
@@ -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 |
@@ -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)); | |||
} | |||
} |
@@ -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 |
@@ -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; | |||
} |
@@ -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 |
@@ -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(); | |||
} |
@@ -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 |
@@ -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 |
@@ -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]; | |||
} |
@@ -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 |
@@ -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 |
@@ -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 |