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

Updated Timer code to avoid a rare messaging problem. Fixed a couple of minor build errors. Rearranged the atomic functions and added a new compare-and-swap operation. Added a thread-priority tweak to WASAPI. Removed MS-specific classes from the web browser component.

This commit is contained in:
Julian Storer 2010-01-27 20:28:38 +00:00
parent c86c7a8011
commit 35a4b5085f
18 changed files with 559 additions and 640 deletions

View file

@ -4214,6 +4214,7 @@
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_VERSION = 4.2;
PREBINDING = NO;
SDKROOT = iphoneos3.0;
@ -4226,6 +4227,7 @@
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
PREBINDING = NO;
SDKROOT = iphoneos3.0;
ZERO_LINK = NO;
@ -4235,6 +4237,7 @@
84A487F808A22DD800752A2B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
MACOSX_DEPLOYMENT_TARGET = 10.4;
@ -4245,6 +4248,7 @@
84A487F908A22DD800752A2B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
STRIP_STYLE = "non-global";
};

8
juce.h
View file

@ -47,10 +47,6 @@ BEGIN_JUCE_NAMESPACE
#pragma warning (disable: 4786) // (old vc6 warning about long class names)
#endif
#if JUCE_MAC || JUCE_IPHONE
#pragma align=natural
#endif
// this is where all the class header files get brought in..
#include "src/juce_core_includes.h"
@ -65,10 +61,6 @@ BEGIN_JUCE_NAMESPACE
#pragma pack (pop)
#endif
#if JUCE_MAC || JUCE_IPHONE
#pragma align=reset
#endif
END_JUCE_NAMESPACE

View file

@ -41,7 +41,7 @@
//=============================================================================
/** JUCE_FORCE_DEBUG: Normally, JUCE_DEBUG is set to 1 or 0 based on compiler and
project settings, but if you define this value, you can override this can force
project settings, but if you define this value, you can override this to force
it to be true or false.
*/
#ifndef JUCE_FORCE_DEBUG
@ -123,7 +123,7 @@
#define JUCE_USE_FLAC 1
#endif
/** JUCE_USE_OGGBORBIS: Enables the Ogg-Vorbis audio codec classes (available on all platforms).
/** JUCE_USE_OGGVORBIS: Enables the Ogg-Vorbis audio codec classes (available on all platforms).
If your app doesn't need to read Ogg-Vorbis files, you might want to disable this to
reduce the size of your codebase and build time.
*/

View file

@ -361,6 +361,7 @@
#include <MMReg.h>
#include <mmdeviceapi.h>
#include <Audioclient.h>
#include <Avrt.h>
#include <functiondiscoverykeys.h>
#endif
@ -1350,12 +1351,6 @@ void JUCE_PUBLIC_FUNCTION initialiseJuce_NonGUI()
jassert (ByteOrder::swap ((uint16) 0x1122) == 0x2211);
jassert (ByteOrder::swap ((uint32) 0x11223344) == 0x44332211);
// quick test to make sure the run-time lib doesn't crash on freeing a null-pointer.
SystemStats* nullPointer = 0;
juce_free (nullPointer);
delete[] nullPointer;
delete nullPointer;
// Some quick stream tests..
int randomInt = Random::getSystemRandom().nextInt();
int64 randomInt64 = Random::getSystemRandom().nextInt64();
@ -2937,8 +2932,7 @@ MemoryBlock& MemoryBlock::operator= (const MemoryBlock& other) throw()
bool MemoryBlock::operator== (const MemoryBlock& other) const throw()
{
return (size == other.size)
&& (memcmp (data, other.data, size) == 0);
return matches (other.data, other.size);
}
bool MemoryBlock::operator!= (const MemoryBlock& other) const throw()
@ -2946,6 +2940,12 @@ bool MemoryBlock::operator!= (const MemoryBlock& other) const throw()
return ! operator== (other);
}
bool MemoryBlock::matches (const void* dataToCompare, size_t dataSize) const throw()
{
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) throw()
@ -30191,8 +30191,8 @@ private:
{
for (int i = 0; i < supportedChannelsSize / sizeof (AUChannelInfo); ++i)
{
numIns = jmax (numIns, supportedChannels[i].inChannels);
numOuts = jmax (numOuts, supportedChannels[i].outChannels);
numIns = jmax (numIns, (int) supportedChannels[i].inChannels);
numOuts = jmax (numOuts, (int) supportedChannels[i].outChannels);
}
}
else
@ -38011,16 +38011,168 @@ class InternalTimerThread : private Thread,
private DeletedAtShutdown,
private AsyncUpdater
{
public:
InternalTimerThread()
: Thread ("Juce Timer"),
firstTimer (0),
callbackNeeded (false)
{
triggerAsyncUpdate();
}
~InternalTimerThread() throw()
{
stopThread (4000);
jassert (instance == this || instance == 0);
if (instance == this)
instance = 0;
}
void run()
{
uint32 lastTime = Time::getMillisecondCounter();
while (! threadShouldExit())
{
const uint32 now = Time::getMillisecondCounter();
if (now <= lastTime)
{
wait (2);
continue;
}
const int elapsed = now - lastTime;
lastTime = now;
lock.enter();
decrementAllCounters (elapsed);
const int timeUntilFirstTimer = (firstTimer != 0) ? firstTimer->countdownMs
: 1000;
lock.exit();
if (timeUntilFirstTimer <= 0)
{
if (callbackNeeded.set (true))
{
postMessage (new Message());
const uint32 messageDeliveryTimeout = now + 2000;
while (callbackNeeded.get())
{
wait (4);
if (threadShouldExit())
return;
if (Time::getMillisecondCounter() > messageDeliveryTimeout)
break;
}
}
}
else
{
// don't wait for too long because running this loop also helps keep the
// Time::getApproximateMillisecondTimer value stay up-to-date
wait (jlimit (1, 50, timeUntilFirstTimer));
}
}
}
void handleMessage (const Message&)
{
const ScopedLock sl (lock);
while (firstTimer != 0 && firstTimer->countdownMs <= 0)
{
Timer* const t = firstTimer;
t->countdownMs = t->periodMs;
removeTimer (t);
addTimer (t);
const ScopedUnlock ul (lock);
JUCE_TRY
{
t->timerCallback();
}
JUCE_CATCH_EXCEPTION
}
callbackNeeded.set (false);
}
static void callAnyTimersSynchronously()
{
if (InternalTimerThread::instance != 0)
{
const Message m;
InternalTimerThread::instance->handleMessage (m);
}
}
static inline void add (Timer* const tim) throw()
{
if (instance == 0)
instance = new InternalTimerThread();
const ScopedLock sl (instance->lock);
instance->addTimer (tim);
}
static inline void remove (Timer* const tim) throw()
{
if (instance != 0)
{
const ScopedLock sl (instance->lock);
instance->removeTimer (tim);
}
}
static inline void resetCounter (Timer* const tim,
const int newCounter) throw()
{
if (instance != 0)
{
tim->countdownMs = newCounter;
tim->periodMs = newCounter;
if ((tim->next != 0 && tim->next->countdownMs < tim->countdownMs)
|| (tim->previous != 0 && tim->previous->countdownMs > tim->countdownMs))
{
const ScopedLock sl (instance->lock);
instance->removeTimer (tim);
instance->addTimer (tim);
}
}
}
private:
friend class Timer;
static InternalTimerThread* instance;
static CriticalSection lock;
Timer* volatile firstTimer;
bool volatile callbackNeeded;
InternalTimerThread (const InternalTimerThread&);
const InternalTimerThread& operator= (const InternalTimerThread&);
class AtomicBool
{
public:
AtomicBool (const bool value) throw() : value (static_cast<int32> (value)) {}
~AtomicBool() throw() {}
bool get() const throw() { return value != 0; }
bool set (const bool newValue) { return Atomic::compareAndExchange (value, newValue ? 1 : 0, value) != 0; }
private:
int32 value;
AtomicBool (const AtomicBool&);
AtomicBool& operator= (const AtomicBool&);
};
AtomicBool callbackNeeded;
void addTimer (Timer* const t) throw()
{
@ -38123,146 +38275,8 @@ private:
startThread (7);
}
public:
InternalTimerThread()
: Thread ("Juce Timer"),
firstTimer (0),
callbackNeeded (false)
{
triggerAsyncUpdate();
}
~InternalTimerThread() throw()
{
stopThread (4000);
jassert (instance == this || instance == 0);
if (instance == this)
instance = 0;
}
void run()
{
uint32 lastTime = Time::getMillisecondCounter();
while (! threadShouldExit())
{
uint32 now = Time::getMillisecondCounter();
if (now <= lastTime)
{
wait (2);
continue;
}
const int elapsed = now - lastTime;
lastTime = now;
lock.enter();
decrementAllCounters (elapsed);
const int timeUntilFirstTimer = (firstTimer != 0) ? firstTimer->countdownMs
: 1000;
lock.exit();
if (timeUntilFirstTimer <= 0)
{
callbackNeeded = true;
postMessage (new Message());
// sometimes, our message could get discarded by the OS (particularly when running as an RTAS when the app has a modal loop),
// so this is how long to wait before assuming the message has been lost and trying again.
const uint32 messageDeliveryTimeout = now + 2000;
while (callbackNeeded)
{
wait (4);
if (threadShouldExit())
return;
now = Time::getMillisecondCounter();
if (now > messageDeliveryTimeout)
break;
}
}
else
{
// don't wait for too long because running this loop also helps keep the
// Time::getApproximateMillisecondTimer value stay up-to-date
wait (jlimit (1, 50, timeUntilFirstTimer));
}
}
}
void handleMessage (const Message&)
{
const ScopedLock sl (lock);
while (firstTimer != 0 && firstTimer->countdownMs <= 0)
{
Timer* const t = firstTimer;
t->countdownMs = t->periodMs;
removeTimer (t);
addTimer (t);
const ScopedUnlock ul (lock);
JUCE_TRY
{
t->timerCallback();
}
JUCE_CATCH_EXCEPTION
}
callbackNeeded = false;
}
static void callAnyTimersSynchronously()
{
if (InternalTimerThread::instance != 0)
{
const Message m;
InternalTimerThread::instance->handleMessage (m);
}
}
static inline void add (Timer* const tim) throw()
{
if (instance == 0)
instance = new InternalTimerThread();
const ScopedLock sl (instance->lock);
instance->addTimer (tim);
}
static inline void remove (Timer* const tim) throw()
{
if (instance != 0)
{
const ScopedLock sl (instance->lock);
instance->removeTimer (tim);
}
}
static inline void resetCounter (Timer* const tim,
const int newCounter) throw()
{
if (instance != 0)
{
tim->countdownMs = newCounter;
tim->periodMs = newCounter;
if ((tim->next != 0 && tim->next->countdownMs < tim->countdownMs)
|| (tim->previous != 0 && tim->previous->countdownMs > tim->countdownMs))
{
const ScopedLock sl (instance->lock);
instance->removeTimer (tim);
instance->addTimer (tim);
}
}
}
InternalTimerThread (const InternalTimerThread&);
InternalTimerThread& operator= (const InternalTimerThread&);
};
InternalTimerThread* InternalTimerThread::instance = 0;
@ -70888,12 +70902,13 @@ ChoicePropertyComponent::ChoicePropertyComponent (const String& name)
ChoicePropertyComponent::ChoicePropertyComponent (const Value& valueToControl,
const String& name,
const StringArray& choices_)
const StringArray& choices_,
const Array <int>* choiceIDs)
: PropertyComponent (name),
choices (choices_),
comboBox (0)
{
createComboBox();
createComboBox (choiceIDs);
comboBox->getSelectedIdAsValue().referTo (valueToControl);
}
@ -70903,14 +70918,18 @@ ChoicePropertyComponent::~ChoicePropertyComponent()
deleteAllChildren();
}
void ChoicePropertyComponent::createComboBox()
void ChoicePropertyComponent::createComboBox (const Array <int>* choiceIDs)
{
// The array of IDs must contain the same number of values as the choices list!
jassert (choiceIDs == 0 || choiceIDs->size() == choices.size());
addAndMakeVisible (comboBox = new ComboBox (String::empty));
for (int i = 0; i < choices.size(); ++i)
{
if (choices[i].isNotEmpty())
comboBox->addItem (choices[i], i + 1);
comboBox->addItem (choices[i], choiceIDs == 0 ? (i + 1)
: ((*choiceIDs)[i]));
else
comboBox->addSeparator();
}
@ -70937,7 +70956,7 @@ void ChoicePropertyComponent::refresh()
{
if (comboBox == 0)
{
createComboBox();
createComboBox (0);
comboBox->addListener (this);
}
@ -212362,6 +212381,17 @@ int SystemStats::getPageSize() throw()
extern HWND juce_messageWindowHandle;
#endif
#if ! JUCE_USE_INTRINSICS
// In newer compilers, the inline versions of these are used (in juce_Atomic.h), but in
// older ones we have to actually call the ops as win32 functions..
void Atomic::increment (int32& variable) { InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
int32 Atomic::incrementAndReturn (int32& variable) { return InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
void Atomic::decrement (int32& variable) { InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
int32 Atomic::decrementAndReturn (int32& variable) { return InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
int32 Atomic::compareAndExchange (int32& destination, int32 newValue, int32 oldValue)
{ return InterlockedCompareExchange (reinterpret_cast <volatile long*> (&destination), newValue, oldValue); }
#endif
CriticalSection::CriticalSection() throw()
{
// (just to check the MS haven't changed this structure and broken things...)
@ -219239,10 +219269,18 @@ public:
if (browser != 0)
{
LPSAFEARRAY sa = 0;
_variant_t flags, frame, postDataVar, headersVar;
VARIANT flags, frame, postDataVar, headersVar; // (_variant_t isn't available in all compilers)
VariantInit (&flags);
VariantInit (&frame);
VariantInit (&postDataVar);
VariantInit (&headersVar);
if (headers != 0)
headersVar = (const tchar*) headers->joinIntoString ("\r\n");
{
V_VT (&headersVar) = VT_BSTR;
V_BSTR (&headersVar) = SysAllocString ((const tchar*) headers->joinIntoString ("\r\n"));
}
if (postData != 0 && postData->getSize() > 0)
{
@ -219275,6 +219313,11 @@ public:
if (sa != 0)
SafeArrayDestroy (sa);
VariantClear (&flags);
VariantClear (&frame);
VariantClear (&postDataVar);
VariantClear (&headersVar);
}
}
@ -227197,8 +227240,26 @@ public:
}
}
void setMMThreadPriority()
{
DynamicLibraryLoader dll ("avrt.dll");
DynamicLibraryImport (AvSetMmThreadCharacteristics, avSetMmThreadCharacteristics, HANDLE, dll, (LPCTSTR, LPDWORD))
DynamicLibraryImport (AvSetMmThreadPriority, avSetMmThreadPriority, HANDLE, dll, (HANDLE, AVRT_PRIORITY))
if (avSetMmThreadCharacteristics != 0 && avSetMmThreadPriority != 0)
{
DWORD dummy = 0;
HANDLE h = avSetMmThreadCharacteristics (_T("Pro Audio"), &dummy);
if (h != 0)
avSetMmThreadPriority (h, AVRT_PRIORITY_NORMAL);
}
}
void run()
{
setMMThreadPriority();
const int bufferSize = currentBufferSizeSamples;
HANDLE events[2];

View file

@ -1445,10 +1445,6 @@ BEGIN_JUCE_NAMESPACE
#pragma warning (disable: 4786) // (old vc6 warning about long class names)
#endif
#if JUCE_MAC || JUCE_IPHONE
#pragma align=natural
#endif
// this is where all the class header files get brought in..
/********* Start of inlined file: juce_core_includes.h *********/
@ -2701,6 +2697,8 @@ public:
bool operator!= (const MemoryBlock& other) const throw();
bool matches (const void* data, size_t dataSize) const throw();
template <class DataType>
operator DataType*() const throw() { return (DataType*) data; }
@ -4364,165 +4362,49 @@ private:
class JUCE_API Atomic
{
public:
static void increment (int& variable);
static void increment (int32& variable);
static int incrementAndReturn (int& variable);
static int32 incrementAndReturn (int32& variable);
static void decrement (int& variable);
static void decrement (int32& variable);
static int decrementAndReturn (int& variable);
static int32 decrementAndReturn (int32& variable);
static int32 compareAndExchange (int32& destination, int32 newValue, int32 requiredCurrentValue);
};
#if (JUCE_MAC || JUCE_IPHONE) // Mac and iPhone...
#include <libkern/OSAtomic.h>
inline void Atomic::increment (int& variable) { OSAtomicIncrement32 ((int32_t*) &variable); }
inline int Atomic::incrementAndReturn (int& variable) { return OSAtomicIncrement32 ((int32_t*) &variable); }
inline void Atomic::decrement (int& variable) { OSAtomicDecrement32 ((int32_t*) &variable); }
inline int Atomic::decrementAndReturn (int& variable) { return OSAtomicDecrement32 ((int32_t*) &variable); }
inline void Atomic::increment (int32& variable) { OSAtomicIncrement32 ((volatile int32_t*) &variable); }
inline int32 Atomic::incrementAndReturn (int32& variable) { return OSAtomicIncrement32 ((volatile int32_t*) &variable); }
inline void Atomic::decrement (int32& variable) { OSAtomicDecrement32 ((volatile int32_t*) &variable); }
inline int32 Atomic::decrementAndReturn (int32& variable) { return OSAtomicDecrement32 ((volatile int32_t*) &variable); }
inline int32 Atomic::compareAndExchange (int32& destination, int32 newValue, int32 oldValue)
{ return OSAtomicCompareAndSwap32Barrier (oldValue, newValue, (volatile int32_t*) &destination); }
#elif JUCE_GCC
#elif JUCE_GCC // Linux...
#if JUCE_USE_GCC_ATOMIC_INTRINSICS // Linux with intrinsics...
inline void Atomic::increment (int32& variable) { __sync_add_and_fetch (&variable, 1); }
inline int32 Atomic::incrementAndReturn (int32& variable) { return __sync_add_and_fetch (&variable, 1); }
inline void Atomic::decrement (int32& variable) { __sync_add_and_fetch (&variable, -1); }
inline int32 Atomic::decrementAndReturn (int32& variable) { return __sync_add_and_fetch (&variable, -1); }
inline int32 Atomic::compareAndExchange (int32& destination, int32 newValue, int32 oldValue)
{ return __sync_val_compare_and_swap (&destination, oldValue, newValue); }
inline void Atomic::increment (int& variable) { __sync_add_and_fetch (&variable, 1); }
inline int Atomic::incrementAndReturn (int& variable) { return __sync_add_and_fetch (&variable, 1); }
inline void Atomic::decrement (int& variable) { __sync_add_and_fetch (&variable, -1); }
inline int Atomic::decrementAndReturn (int& variable) { return __sync_add_and_fetch (&variable, -1); }
#else // Linux without intrinsics...
inline void Atomic::increment (int& variable)
{
__asm__ __volatile__ (
#if JUCE_64BIT
"lock incl (%%rax)"
:
: "a" (&variable)
: "cc", "memory");
#else
"lock incl %0"
: "=m" (variable)
: "m" (variable));
#endif
}
inline int Atomic::incrementAndReturn (int& variable)
{
int result;
__asm__ __volatile__ (
#if JUCE_64BIT
"lock xaddl %%ebx, (%%rax) \n\
incl %%ebx"
: "=b" (result)
: "a" (&variable), "b" (1)
: "cc", "memory");
#else
"lock xaddl %%eax, (%%ecx) \n\
incl %%eax"
: "=a" (result)
: "c" (&variable), "a" (1)
: "memory");
#endif
return result;
}
inline void Atomic::decrement (int& variable)
{
__asm__ __volatile__ (
#if JUCE_64BIT
"lock decl (%%rax)"
:
: "a" (&variable)
: "cc", "memory");
#else
"lock decl %0"
: "=m" (variable)
: "m" (variable));
#endif
}
inline int Atomic::decrementAndReturn (int& variable)
{
int result;
__asm__ __volatile__ (
#if JUCE_64BIT
"lock xaddl %%ebx, (%%rax) \n\
decl %%ebx"
: "=b" (result)
: "a" (&variable), "b" (-1)
: "cc", "memory");
#else
"lock xaddl %%eax, (%%ecx) \n\
decl %%eax"
: "=a" (result)
: "c" (&variable), "a" (-1)
: "memory");
#endif
return result;
}
#endif
#elif JUCE_USE_INTRINSICS // Windows with intrinsics...
#elif JUCE_USE_INTRINSICS // Windows...
// (If JUCE_USE_INTRINSICS isn't enabled, a fallback version of these methods is
// declared in juce_win32_Threads.cpp)
#pragma intrinsic (_InterlockedIncrement)
#pragma intrinsic (_InterlockedDecrement)
#pragma intrinsic (_InterlockedCompareExchange)
inline void Atomic::increment (int& variable) { _InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
inline int Atomic::incrementAndReturn (int& variable) { return _InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
inline void Atomic::decrement (int& variable) { _InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
inline int Atomic::decrementAndReturn (int& variable) { return _InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
#else // Windows without intrinsics...
inline void Atomic::increment (int& variable)
{
__asm {
mov ecx, dword ptr [variable]
lock inc dword ptr [ecx]
}
}
inline int Atomic::incrementAndReturn (int& variable)
{
int result;
__asm {
mov ecx, dword ptr [variable]
mov eax, 1
lock xadd dword ptr [ecx], eax
inc eax
mov result, eax
}
return result;
}
inline void Atomic::decrement (int& variable)
{
__asm {
mov ecx, dword ptr [variable]
lock dec dword ptr [ecx]
}
}
inline int Atomic::decrementAndReturn (int& variable)
{
int result;
__asm {
mov ecx, dword ptr [variable]
mov eax, -1
lock xadd dword ptr [ecx], eax
dec eax
mov result, eax
}
return result;
}
inline void Atomic::increment (int32& variable) { _InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
inline int32 Atomic::incrementAndReturn (int32& variable) { return _InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
inline void Atomic::decrement (int32& variable) { _InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
inline int32 Atomic::decrementAndReturn (int32& variable) { return _InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
inline int32 Atomic::compareAndExchange (int32& destination, int32 newValue, int32 oldValue)
{ return _InterlockedCompareExchange (reinterpret_cast <volatile long*> (&destination), newValue, oldValue); }
#endif
@ -5118,7 +5000,7 @@ public:
// avoids getting warning messages about the parameter being unused
lock.enter();
sortArray (comparator, (ObjectClass*) data.elements, 0, size() - 1, retainOrderOfEquivalentItems);
sortArray (comparator, (ObjectClass**) data.elements, 0, size() - 1, retainOrderOfEquivalentItems);
lock.exit();
}
@ -25219,7 +25101,8 @@ protected:
public:
ChoicePropertyComponent (const Value& valueToControl,
const String& propertyName,
const StringArray& choices);
const StringArray& choices,
const Array <int>* choiceIDs = 0);
~ChoicePropertyComponent();
@ -25240,7 +25123,7 @@ protected:
private:
ComboBox* comboBox;
void createComboBox();
void createComboBox (const Array <int>* choiceIDs);
ChoicePropertyComponent (const ChoicePropertyComponent&);
const ChoicePropertyComponent& operator= (const ChoicePropertyComponent&);
@ -27620,10 +27503,6 @@ public:
#pragma pack (pop)
#endif
#if JUCE_MAC || JUCE_IPHONE
#pragma align=reset
#endif
END_JUCE_NAMESPACE
#ifndef DONT_SET_USING_JUCE_NAMESPACE

View file

@ -351,8 +351,8 @@ private:
{
for (int i = 0; i < supportedChannelsSize / sizeof (AUChannelInfo); ++i)
{
numIns = jmax (numIns, supportedChannels[i].inChannels);
numOuts = jmax (numOuts, supportedChannels[i].outChannels);
numIns = jmax (numIns, (int) supportedChannels[i].inChannels);
numOuts = jmax (numOuts, (int) supportedChannels[i].outChannels);
}
}
else

View file

@ -98,8 +98,7 @@ MemoryBlock& MemoryBlock::operator= (const MemoryBlock& other) throw()
//==============================================================================
bool MemoryBlock::operator== (const MemoryBlock& other) const throw()
{
return (size == other.size)
&& (memcmp (data, other.data, size) == 0);
return matches (other.data, other.size);
}
bool MemoryBlock::operator!= (const MemoryBlock& other) const throw()
@ -107,6 +106,12 @@ bool MemoryBlock::operator!= (const MemoryBlock& other) const throw()
return ! operator== (other);
}
bool MemoryBlock::matches (const void* dataToCompare, size_t dataSize) const throw()
{
return size == dataSize
&& memcmp (data, dataToCompare, size) == 0;
}
//==============================================================================
// this will resize the block to this size
void MemoryBlock::setSize (const size_t newSize,

View file

@ -83,6 +83,10 @@ public:
*/
bool operator!= (const MemoryBlock& other) const throw();
/** Returns true if the data in this MemoryBlock matches the raw bytes passed-in.
*/
bool matches (const void* data, size_t dataSize) const throw();
//==============================================================================
/** Returns a pointer to the data, casting it to any type of primitive data required.

View file

@ -34,174 +34,60 @@ class JUCE_API Atomic
{
public:
/** Increments an integer in a thread-safe way. */
static void increment (int& variable);
static void increment (int32& variable);
/** Increments an integer in a thread-safe way and returns its new value. */
static int incrementAndReturn (int& variable);
static int32 incrementAndReturn (int32& variable);
/** Decrements an integer in a thread-safe way. */
static void decrement (int& variable);
static void decrement (int32& variable);
/** Decrements an integer in a thread-safe way and returns its new value. */
static int decrementAndReturn (int& variable);
static int32 decrementAndReturn (int32& variable);
/** If the current value of destination is equal to requiredCurrentValue, this
will set it to newValue; otherwise, it will leave it unchanged.
@returns the new value of destination
*/
static int32 compareAndExchange (int32& destination, int32 newValue, int32 requiredCurrentValue);
};
//==============================================================================
#if (JUCE_MAC || JUCE_IPHONE) // Mac and iPhone...
#include <libkern/OSAtomic.h>
inline void Atomic::increment (int& variable) { OSAtomicIncrement32 ((int32_t*) &variable); }
inline int Atomic::incrementAndReturn (int& variable) { return OSAtomicIncrement32 ((int32_t*) &variable); }
inline void Atomic::decrement (int& variable) { OSAtomicDecrement32 ((int32_t*) &variable); }
inline int Atomic::decrementAndReturn (int& variable) { return OSAtomicDecrement32 ((int32_t*) &variable); }
inline void Atomic::increment (int32& variable) { OSAtomicIncrement32 ((volatile int32_t*) &variable); }
inline int32 Atomic::incrementAndReturn (int32& variable) { return OSAtomicIncrement32 ((volatile int32_t*) &variable); }
inline void Atomic::decrement (int32& variable) { OSAtomicDecrement32 ((volatile int32_t*) &variable); }
inline int32 Atomic::decrementAndReturn (int32& variable) { return OSAtomicDecrement32 ((volatile int32_t*) &variable); }
inline int32 Atomic::compareAndExchange (int32& destination, int32 newValue, int32 oldValue)
{ return OSAtomicCompareAndSwap32Barrier (oldValue, newValue, (volatile int32_t*) &destination); }
#elif JUCE_GCC
#elif JUCE_GCC // Linux...
//==============================================================================
#if JUCE_USE_GCC_ATOMIC_INTRINSICS // Linux with intrinsics...
inline void Atomic::increment (int& variable) { __sync_add_and_fetch (&variable, 1); }
inline int Atomic::incrementAndReturn (int& variable) { return __sync_add_and_fetch (&variable, 1); }
inline void Atomic::decrement (int& variable) { __sync_add_and_fetch (&variable, -1); }
inline int Atomic::decrementAndReturn (int& variable) { return __sync_add_and_fetch (&variable, -1); }
inline void Atomic::increment (int32& variable) { __sync_add_and_fetch (&variable, 1); }
inline int32 Atomic::incrementAndReturn (int32& variable) { return __sync_add_and_fetch (&variable, 1); }
inline void Atomic::decrement (int32& variable) { __sync_add_and_fetch (&variable, -1); }
inline int32 Atomic::decrementAndReturn (int32& variable) { return __sync_add_and_fetch (&variable, -1); }
inline int32 Atomic::compareAndExchange (int32& destination, int32 newValue, int32 oldValue)
{ return __sync_val_compare_and_swap (&destination, oldValue, newValue); }
//==============================================================================
#else // Linux without intrinsics...
inline void Atomic::increment (int& variable)
{
__asm__ __volatile__ (
#if JUCE_64BIT
"lock incl (%%rax)"
:
: "a" (&variable)
: "cc", "memory");
#else
"lock incl %0"
: "=m" (variable)
: "m" (variable));
#endif
}
inline int Atomic::incrementAndReturn (int& variable)
{
int result;
__asm__ __volatile__ (
#if JUCE_64BIT
"lock xaddl %%ebx, (%%rax) \n\
incl %%ebx"
: "=b" (result)
: "a" (&variable), "b" (1)
: "cc", "memory");
#else
"lock xaddl %%eax, (%%ecx) \n\
incl %%eax"
: "=a" (result)
: "c" (&variable), "a" (1)
: "memory");
#endif
return result;
}
inline void Atomic::decrement (int& variable)
{
__asm__ __volatile__ (
#if JUCE_64BIT
"lock decl (%%rax)"
:
: "a" (&variable)
: "cc", "memory");
#else
"lock decl %0"
: "=m" (variable)
: "m" (variable));
#endif
}
inline int Atomic::decrementAndReturn (int& variable)
{
int result;
__asm__ __volatile__ (
#if JUCE_64BIT
"lock xaddl %%ebx, (%%rax) \n\
decl %%ebx"
: "=b" (result)
: "a" (&variable), "b" (-1)
: "cc", "memory");
#else
"lock xaddl %%eax, (%%ecx) \n\
decl %%eax"
: "=a" (result)
: "c" (&variable), "a" (-1)
: "memory");
#endif
return result;
}
#endif
//==============================================================================
#elif JUCE_USE_INTRINSICS // Windows with intrinsics...
#elif JUCE_USE_INTRINSICS // Windows...
// (If JUCE_USE_INTRINSICS isn't enabled, a fallback version of these methods is
// declared in juce_win32_Threads.cpp)
#pragma intrinsic (_InterlockedIncrement)
#pragma intrinsic (_InterlockedDecrement)
#pragma intrinsic (_InterlockedCompareExchange)
inline void Atomic::increment (int& variable) { _InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
inline int Atomic::incrementAndReturn (int& variable) { return _InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
inline void Atomic::decrement (int& variable) { _InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
inline int Atomic::decrementAndReturn (int& variable) { return _InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
//==============================================================================
#else // Windows without intrinsics...
inline void Atomic::increment (int& variable)
{
__asm {
mov ecx, dword ptr [variable]
lock inc dword ptr [ecx]
}
}
inline int Atomic::incrementAndReturn (int& variable)
{
int result;
__asm {
mov ecx, dword ptr [variable]
mov eax, 1
lock xadd dword ptr [ecx], eax
inc eax
mov result, eax
}
return result;
}
inline void Atomic::decrement (int& variable)
{
__asm {
mov ecx, dword ptr [variable]
lock dec dword ptr [ecx]
}
}
inline int Atomic::decrementAndReturn (int& variable)
{
int result;
__asm {
mov ecx, dword ptr [variable]
mov eax, -1
lock xadd dword ptr [ecx], eax
dec eax
mov result, eax
}
return result;
}
inline void Atomic::increment (int32& variable) { _InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
inline int32 Atomic::incrementAndReturn (int32& variable) { return _InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
inline void Atomic::decrement (int32& variable) { _InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
inline int32 Atomic::decrementAndReturn (int32& variable) { return _InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
inline int32 Atomic::compareAndExchange (int32& destination, int32 newValue, int32 oldValue)
{ return _InterlockedCompareExchange (reinterpret_cast <volatile long*> (&destination), newValue, oldValue); }
#endif

View file

@ -79,12 +79,6 @@ void JUCE_PUBLIC_FUNCTION initialiseJuce_NonGUI()
jassert (ByteOrder::swap ((uint16) 0x1122) == 0x2211);
jassert (ByteOrder::swap ((uint32) 0x11223344) == 0x44332211);
// quick test to make sure the run-time lib doesn't crash on freeing a null-pointer.
SystemStats* nullPointer = 0;
juce_free (nullPointer);
delete[] nullPointer;
delete nullPointer;
// Some quick stream tests..
int randomInt = Random::getSystemRandom().nextInt();
int64 randomInt64 = Random::getSystemRandom().nextInt64();

View file

@ -44,17 +44,193 @@ class InternalTimerThread : private Thread,
private DeletedAtShutdown,
private AsyncUpdater
{
public:
InternalTimerThread()
: Thread ("Juce Timer"),
firstTimer (0),
callbackNeeded (false)
{
triggerAsyncUpdate();
}
~InternalTimerThread() throw()
{
stopThread (4000);
jassert (instance == this || instance == 0);
if (instance == this)
instance = 0;
}
void run()
{
uint32 lastTime = Time::getMillisecondCounter();
while (! threadShouldExit())
{
const uint32 now = Time::getMillisecondCounter();
if (now <= lastTime)
{
wait (2);
continue;
}
const int elapsed = now - lastTime;
lastTime = now;
lock.enter();
decrementAllCounters (elapsed);
const int timeUntilFirstTimer = (firstTimer != 0) ? firstTimer->countdownMs
: 1000;
lock.exit();
if (timeUntilFirstTimer <= 0)
{
/* If we managed to set the atomic boolean to true then send a message, this is needed
as a memory barrier so the message won't be sent before callbackNeeded is set to true,
but if it fails it means the message-thread changed the value from under us so at least
some processing is happenening and we can just loop around and try again
*/
if (callbackNeeded.set (true))
{
postMessage (new Message());
/* Sometimes our message can get discarded by the OS (e.g. when running as an RTAS
when the app has a modal loop), so this is how long to wait before assuming the
message has been lost and trying again.
*/
const uint32 messageDeliveryTimeout = now + 2000;
while (callbackNeeded.get())
{
wait (4);
if (threadShouldExit())
return;
if (Time::getMillisecondCounter() > messageDeliveryTimeout)
break;
}
}
}
else
{
// don't wait for too long because running this loop also helps keep the
// Time::getApproximateMillisecondTimer value stay up-to-date
wait (jlimit (1, 50, timeUntilFirstTimer));
}
}
}
void handleMessage (const Message&)
{
const ScopedLock sl (lock);
while (firstTimer != 0 && firstTimer->countdownMs <= 0)
{
Timer* const t = firstTimer;
t->countdownMs = t->periodMs;
removeTimer (t);
addTimer (t);
const ScopedUnlock ul (lock);
JUCE_TRY
{
t->timerCallback();
}
JUCE_CATCH_EXCEPTION
}
/* This is needed as a memory barrier to make sure all processing of current timers is done
before the boolean is set. This set should never fail since if it was false in the first place,
we wouldn't get a message (so it can't be changed from false to true from under us), and if we
get a message then the value is true and the other thread can only set it to true again and
we will get another callback to set it to false.
*/
callbackNeeded.set (false);
}
static void callAnyTimersSynchronously()
{
if (InternalTimerThread::instance != 0)
{
const Message m;
InternalTimerThread::instance->handleMessage (m);
}
}
static inline void add (Timer* const tim) throw()
{
if (instance == 0)
instance = new InternalTimerThread();
const ScopedLock sl (instance->lock);
instance->addTimer (tim);
}
static inline void remove (Timer* const tim) throw()
{
if (instance != 0)
{
const ScopedLock sl (instance->lock);
instance->removeTimer (tim);
}
}
static inline void resetCounter (Timer* const tim,
const int newCounter) throw()
{
if (instance != 0)
{
tim->countdownMs = newCounter;
tim->periodMs = newCounter;
if ((tim->next != 0 && tim->next->countdownMs < tim->countdownMs)
|| (tim->previous != 0 && tim->previous->countdownMs > tim->countdownMs))
{
const ScopedLock sl (instance->lock);
instance->removeTimer (tim);
instance->addTimer (tim);
}
}
}
private:
friend class Timer;
static InternalTimerThread* instance;
static CriticalSection lock;
Timer* volatile firstTimer;
bool volatile callbackNeeded;
InternalTimerThread (const InternalTimerThread&);
const InternalTimerThread& operator= (const InternalTimerThread&);
//==============================================================================
class AtomicBool
{
public:
AtomicBool (const bool value) throw() : value (static_cast<int32> (value)) {}
~AtomicBool() throw() {}
bool get() const throw() { return value != 0; }
bool set (const bool newValue) { return Atomic::compareAndExchange (value, newValue ? 1 : 0, value) != 0; }
/*bool setIfNotAlreadyThisValue (const bool newValue)
{
int32 valueNew = newValue ? 1 : 0;
int32 valueCurrent = 1 - valueNew;
return Atomic::compareAndExchange (value, valueNew, valueCurrent);
}*/
private:
int32 value;
AtomicBool (const AtomicBool&);
AtomicBool& operator= (const AtomicBool&);
};
AtomicBool callbackNeeded;
//==============================================================================
void addTimer (Timer* const t) throw()
{
#ifdef JUCE_DEBUG
@ -156,146 +332,8 @@ private:
startThread (7);
}
public:
InternalTimerThread()
: Thread ("Juce Timer"),
firstTimer (0),
callbackNeeded (false)
{
triggerAsyncUpdate();
}
~InternalTimerThread() throw()
{
stopThread (4000);
jassert (instance == this || instance == 0);
if (instance == this)
instance = 0;
}
void run()
{
uint32 lastTime = Time::getMillisecondCounter();
while (! threadShouldExit())
{
uint32 now = Time::getMillisecondCounter();
if (now <= lastTime)
{
wait (2);
continue;
}
const int elapsed = now - lastTime;
lastTime = now;
lock.enter();
decrementAllCounters (elapsed);
const int timeUntilFirstTimer = (firstTimer != 0) ? firstTimer->countdownMs
: 1000;
lock.exit();
if (timeUntilFirstTimer <= 0)
{
callbackNeeded = true;
postMessage (new Message());
// sometimes, our message could get discarded by the OS (particularly when running as an RTAS when the app has a modal loop),
// so this is how long to wait before assuming the message has been lost and trying again.
const uint32 messageDeliveryTimeout = now + 2000;
while (callbackNeeded)
{
wait (4);
if (threadShouldExit())
return;
now = Time::getMillisecondCounter();
if (now > messageDeliveryTimeout)
break;
}
}
else
{
// don't wait for too long because running this loop also helps keep the
// Time::getApproximateMillisecondTimer value stay up-to-date
wait (jlimit (1, 50, timeUntilFirstTimer));
}
}
}
void handleMessage (const Message&)
{
const ScopedLock sl (lock);
while (firstTimer != 0 && firstTimer->countdownMs <= 0)
{
Timer* const t = firstTimer;
t->countdownMs = t->periodMs;
removeTimer (t);
addTimer (t);
const ScopedUnlock ul (lock);
JUCE_TRY
{
t->timerCallback();
}
JUCE_CATCH_EXCEPTION
}
callbackNeeded = false;
}
static void callAnyTimersSynchronously()
{
if (InternalTimerThread::instance != 0)
{
const Message m;
InternalTimerThread::instance->handleMessage (m);
}
}
static inline void add (Timer* const tim) throw()
{
if (instance == 0)
instance = new InternalTimerThread();
const ScopedLock sl (instance->lock);
instance->addTimer (tim);
}
static inline void remove (Timer* const tim) throw()
{
if (instance != 0)
{
const ScopedLock sl (instance->lock);
instance->removeTimer (tim);
}
}
static inline void resetCounter (Timer* const tim,
const int newCounter) throw()
{
if (instance != 0)
{
tim->countdownMs = newCounter;
tim->periodMs = newCounter;
if ((tim->next != 0 && tim->next->countdownMs < tim->countdownMs)
|| (tim->previous != 0 && tim->previous->countdownMs > tim->countdownMs))
{
const ScopedLock sl (instance->lock);
instance->removeTimer (tim);
instance->addTimer (tim);
}
}
}
InternalTimerThread (const InternalTimerThread&);
InternalTimerThread& operator= (const InternalTimerThread&);
};
InternalTimerThread* InternalTimerThread::instance = 0;

View file

@ -127,7 +127,7 @@ public:
This may be different from getCurrentFile(), which returns the value
that is shown in the filename box, and if there are multiple selections,
this will only return one of them.
@see getCurrentFile
@see getSelectedFile
*/
const File getHighlightedFile() const throw();

View file

@ -39,12 +39,13 @@ ChoicePropertyComponent::ChoicePropertyComponent (const String& name)
ChoicePropertyComponent::ChoicePropertyComponent (const Value& valueToControl,
const String& name,
const StringArray& choices_)
const StringArray& choices_,
const Array <int>* choiceIDs)
: PropertyComponent (name),
choices (choices_),
comboBox (0)
{
createComboBox();
createComboBox (choiceIDs);
comboBox->getSelectedIdAsValue().referTo (valueToControl);
}
@ -55,14 +56,18 @@ ChoicePropertyComponent::~ChoicePropertyComponent()
}
//==============================================================================
void ChoicePropertyComponent::createComboBox()
void ChoicePropertyComponent::createComboBox (const Array <int>* choiceIDs)
{
// The array of IDs must contain the same number of values as the choices list!
jassert (choiceIDs == 0 || choiceIDs->size() == choices.size());
addAndMakeVisible (comboBox = new ComboBox (String::empty));
for (int i = 0; i < choices.size(); ++i)
{
if (choices[i].isNotEmpty())
comboBox->addItem (choices[i], i + 1);
comboBox->addItem (choices[i], choiceIDs == 0 ? (i + 1)
: ((*choiceIDs)[i]));
else
comboBox->addSeparator();
}
@ -90,7 +95,7 @@ void ChoicePropertyComponent::refresh()
{
if (comboBox == 0)
{
createComboBox();
createComboBox (0);
comboBox->addListener (this);
}

View file

@ -63,12 +63,20 @@ protected:
public:
/** Creates the component.
Your subclass's constructor must add a list of options to the choices
member variable.
@param valueToControl the value that the combo box will read and control
@param propertyName the name of the property
@param choices the list of possible values that the user can choose between
@param choiceIDs if this is 0, then the value corresponding to each item in the
'choices' StringArray is simply its index + 1. But if the
choiceIDs parameter is specified, it lets you provide a set
of IDs for each item in the choices list. If you use this
parameter, it must contain the same number of elements as
the choices list.
*/
ChoicePropertyComponent (const Value& valueToControl,
const String& propertyName,
const StringArray& choices);
const StringArray& choices,
const Array <int>* choiceIDs = 0);
/** Destructor. */
~ChoicePropertyComponent();
@ -113,7 +121,7 @@ protected:
private:
ComboBox* comboBox;
void createComboBox();
void createComboBox (const Array <int>* choiceIDs);
ChoicePropertyComponent (const ChoicePropertyComponent&);
const ChoicePropertyComponent& operator= (const ChoicePropertyComponent&);

View file

@ -142,6 +142,7 @@
#include <MMReg.h>
#include <mmdeviceapi.h>
#include <Audioclient.h>
#include <Avrt.h>
#include <functiondiscoverykeys.h>
#endif

View file

@ -31,6 +31,17 @@
extern HWND juce_messageWindowHandle;
#endif
//==============================================================================
#if ! JUCE_USE_INTRINSICS
// In newer compilers, the inline versions of these are used (in juce_Atomic.h), but in
// older ones we have to actually call the ops as win32 functions..
void Atomic::increment (int32& variable) { InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
int32 Atomic::incrementAndReturn (int32& variable) { return InterlockedIncrement (reinterpret_cast <volatile long*> (&variable)); }
void Atomic::decrement (int32& variable) { InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
int32 Atomic::decrementAndReturn (int32& variable) { return InterlockedDecrement (reinterpret_cast <volatile long*> (&variable)); }
int32 Atomic::compareAndExchange (int32& destination, int32 newValue, int32 oldValue)
{ return InterlockedCompareExchange (reinterpret_cast <volatile long*> (&destination), newValue, oldValue); }
#endif
//==============================================================================
CriticalSection::CriticalSection() throw()

View file

@ -780,8 +780,26 @@ public:
}
}
void setMMThreadPriority()
{
DynamicLibraryLoader dll ("avrt.dll");
DynamicLibraryImport (AvSetMmThreadCharacteristics, avSetMmThreadCharacteristics, HANDLE, dll, (LPCTSTR, LPDWORD))
DynamicLibraryImport (AvSetMmThreadPriority, avSetMmThreadPriority, HANDLE, dll, (HANDLE, AVRT_PRIORITY))
if (avSetMmThreadCharacteristics != 0 && avSetMmThreadPriority != 0)
{
DWORD dummy = 0;
HANDLE h = avSetMmThreadCharacteristics (_T("Pro Audio"), &dummy);
if (h != 0)
avSetMmThreadPriority (h, AVRT_PRIORITY_NORMAL);
}
}
void run()
{
setMMThreadPriority();
const int bufferSize = currentBufferSizeSamples;
HANDLE events[2];

View file

@ -79,10 +79,18 @@ public:
if (browser != 0)
{
LPSAFEARRAY sa = 0;
_variant_t flags, frame, postDataVar, headersVar;
VARIANT flags, frame, postDataVar, headersVar; // (_variant_t isn't available in all compilers)
VariantInit (&flags);
VariantInit (&frame);
VariantInit (&postDataVar);
VariantInit (&headersVar);
if (headers != 0)
headersVar = (const tchar*) headers->joinIntoString ("\r\n");
{
V_VT (&headersVar) = VT_BSTR;
V_BSTR (&headersVar) = SysAllocString ((const tchar*) headers->joinIntoString ("\r\n"));
}
if (postData != 0 && postData->getSize() > 0)
{
@ -115,6 +123,11 @@ public:
if (sa != 0)
SafeArrayDestroy (sa);
VariantClear (&flags);
VariantClear (&frame);
VariantClear (&postDataVar);
VariantClear (&headersVar);
}
}