diff --git a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp index b3df73a964..0db217e8ec 100644 --- a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp @@ -421,16 +421,6 @@ public: bool open() { - #if JUCE_WINDOWS - static bool timePeriodSet = false; - - if (! timePeriodSet) - { - timePeriodSet = true; - timeBeginPeriod (2); - } - #endif - pluginName = file.getFileNameWithoutExtension(); module.open (file.getFullPathName()); diff --git a/modules/juce_core/juce_core.cpp b/modules/juce_core/juce_core.cpp index 3435a20946..81ddab7296 100644 --- a/modules/juce_core/juce_core.cpp +++ b/modules/juce_core/juce_core.cpp @@ -80,6 +80,7 @@ #if JUCE_MAC || JUCE_IOS #include + #include #endif #if JUCE_ANDROID @@ -91,9 +92,6 @@ namespace juce { -// START_AUTOINCLUDE containers/*.cpp, files/*.cpp, json/*.cpp, logging/*.cpp, maths/*.cpp, -// memory/*.cpp, misc/*.cpp, network/*.cpp, streams/*.cpp, system/*.cpp, text/*.cpp, threads/*.cpp, -// time/*.cpp, unit_tests/*.cpp, xml/*.cpp, zip/juce_GZIPD*.cpp, zip/juce_GZIPC*.cpp, zip/juce_Zip*.cpp #include "containers/juce_AbstractFifo.cpp" #include "containers/juce_DynamicObject.cpp" #include "containers/juce_NamedValueSet.cpp" @@ -148,7 +146,6 @@ namespace juce #include "zip/juce_GZIPDecompressorInputStream.cpp" #include "zip/juce_GZIPCompressorOutputStream.cpp" #include "zip/juce_ZipFile.cpp" -// END_AUTOINCLUDE //============================================================================== #if JUCE_MAC || JUCE_IOS @@ -197,4 +194,7 @@ namespace juce #include "native/juce_android_Threads.cpp" #endif + +#include "threads/juce_HighResolutionTimer.cpp" + } diff --git a/modules/juce_core/juce_core.h b/modules/juce_core/juce_core.h index 067d2d8527..469b0fd62a 100644 --- a/modules/juce_core/juce_core.h +++ b/modules/juce_core/juce_core.h @@ -376,6 +376,9 @@ namespace juce #ifndef __JUCE_DYNAMICLIBRARY_JUCEHEADER__ #include "threads/juce_DynamicLibrary.h" #endif +#ifndef __JUCE_HIGHRESOLUTIONTIMER_JUCEHEADER__ + #include "threads/juce_HighResolutionTimer.h" +#endif #ifndef __JUCE_INTERPROCESSLOCK_JUCEHEADER__ #include "threads/juce_InterProcessLock.h" #endif diff --git a/modules/juce_core/native/juce_posix_SharedCode.h b/modules/juce_core/native/juce_posix_SharedCode.h index 3c2bb9ea85..67f9a3dbd4 100644 --- a/modules/juce_core/native/juce_posix_SharedCode.h +++ b/modules/juce_core/native/juce_posix_SharedCode.h @@ -1119,3 +1119,140 @@ bool ChildProcess::kill() { return activeProcess == nullptr || activeProcess->killProcess(); } + +//============================================================================== +struct HighResolutionTimer::Pimpl +{ + Pimpl (HighResolutionTimer& t) : owner (t), thread (0), shouldStop (false) + { + } + + ~Pimpl() + { + jassert (thread == 0); + } + + void start (int newPeriod) + { + periodMs = newPeriod; + + if (thread == 0) + { + shouldStop = false; + + if (pthread_create (&thread, nullptr, timerThread, this) == 0) + setThreadToRealtime (thread, newPeriod); + else + jassertfalse; + } + } + + void stop() + { + if (thread != 0) + { + shouldStop = true; + + while (thread != 0 && thread != pthread_self()) + Thread::yield(); + } + } + + HighResolutionTimer& owner; + int volatile periodMs; + +private: + pthread_t thread; + bool volatile shouldStop; + + static void* timerThread (void* param) + { + int dummy; + pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &dummy); + + reinterpret_cast (param)->timerThread(); + return nullptr; + } + + void timerThread() + { + Clock clock (periodMs); + + while (! shouldStop) + { + clock.wait(); + owner.hiResTimerCallback(); + } + + periodMs = 0; + thread = 0; + } + + struct Clock + { + #if JUCE_MAC || JUCE_IOS + Clock (double millis) + { + mach_timebase_info_data_t timebase; + (void) mach_timebase_info (&timebase); + delta = (((uint64_t) (millis * 1000000.0)) * timebase.numer) / timebase.denom; + time = mach_absolute_time(); + } + + void wait() + { + time += delta; + mach_wait_until (time); + } + + uint64_t time, delta; + + #else + Clock (double millis) + : delta ((int64) (millis * 1000000)) + { + struct timespec t; + clock_gettime (CLOCK_MONOTONIC, &t); + time = 1000000000 * (int64) t.tv_sec + t.tv_nsec; + } + + void wait() + { + time += delta; + + struct timespec t; + t.tv_sec = (time_t) (time / 1000000000); + t.tv_nsec = (long) (time % 1000000000); + + clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &t, nullptr); + } + + int64 time, delta; + #endif + }; + + static bool setThreadToRealtime (pthread_t thread, uint64 periodMs) + { + #if JUCE_MAC || JUCE_IOS + thread_time_constraint_policy_data_t policy; + policy.period = (uint32_t) (periodMs * 1000000); + policy.computation = 50000; + policy.constraint = policy.period; + policy.preemptible = true; + + return thread_policy_set (pthread_mach_thread_np (thread), + THREAD_TIME_CONSTRAINT_POLICY, + (thread_policy_t) &policy, + THREAD_TIME_CONSTRAINT_POLICY_COUNT) == KERN_SUCCESS; + + #else + (void) periodMs; + struct sched_param param; + param.sched_priority = sched_get_priority_max (SCHED_RR); + return pthread_setschedparam (thread, SCHED_RR, ¶m) == 0; + + #endif + } + + JUCE_DECLARE_NON_COPYABLE (Pimpl) +}; diff --git a/modules/juce_core/native/juce_win32_Threads.cpp b/modules/juce_core/native/juce_win32_Threads.cpp index 7938a15a91..421349a595 100644 --- a/modules/juce_core/native/juce_win32_Threads.cpp +++ b/modules/juce_core/native/juce_win32_Threads.cpp @@ -582,3 +582,55 @@ bool ChildProcess::kill() { return activeProcess == nullptr || activeProcess->killProcess(); } + +//============================================================================== +struct HighResolutionTimer::Pimpl +{ + Pimpl (HighResolutionTimer& t) noexcept : owner (t), periodMs (0) + { + } + + ~Pimpl() + { + jassert (periodMs == 0); + } + + void start (int newPeriod) + { + if (newPeriod != periodMs) + { + stop(); + periodMs = newPeriod; + + TIMECAPS tc; + if (timeGetDevCaps (&tc, sizeof (tc)) == TIMERR_NOERROR) + { + const int actualPeriod = jlimit ((int) tc.wPeriodMin, (int) tc.wPeriodMax, newPeriod); + + timerID = timeSetEvent (actualPeriod, tc.wPeriodMin, callbackFunction, (DWORD_PTR) this, + TIME_PERIODIC | TIME_CALLBACK_FUNCTION | TIME_KILL_SYNCHRONOUS); + } + } + } + + void stop() + { + periodMs = 0; + timeKillEvent (timerID); + } + + HighResolutionTimer& owner; + int periodMs; + +private: + unsigned int timerID; + + static void __stdcall callbackFunction (UINT, UINT, DWORD_PTR userInfo, DWORD_PTR, DWORD_PTR) + { + if (Pimpl* const timer = reinterpret_cast (userInfo)) + if (timer->periodMs != 0) + timer->owner.hiResTimerCallback(); + } + + JUCE_DECLARE_NON_COPYABLE (Pimpl) +}; diff --git a/modules/juce_core/threads/juce_HighResolutionTimer.cpp b/modules/juce_core/threads/juce_HighResolutionTimer.cpp new file mode 100644 index 0000000000..16c27e1689 --- /dev/null +++ b/modules/juce_core/threads/juce_HighResolutionTimer.cpp @@ -0,0 +1,33 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-11 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online 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.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +HighResolutionTimer::HighResolutionTimer() { pimpl = new Pimpl (*this); } +HighResolutionTimer::~HighResolutionTimer() { stopTimer(); } + +void HighResolutionTimer::startTimer (int periodMs) { pimpl->start (jmax (1, periodMs)); } +void HighResolutionTimer::stopTimer() { pimpl->stop(); } + +bool HighResolutionTimer::isTimerRunning() const noexcept { return pimpl->periodMs != 0; } +int HighResolutionTimer::getTimerInterval() const noexcept { return pimpl->periodMs; } diff --git a/modules/juce_core/threads/juce_HighResolutionTimer.h b/modules/juce_core/threads/juce_HighResolutionTimer.h new file mode 100644 index 0000000000..1c9b69149f --- /dev/null +++ b/modules/juce_core/threads/juce_HighResolutionTimer.h @@ -0,0 +1,92 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-11 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online 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.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#ifndef __JUCE_HIGHRESOLUTIONTIMER_JUCEHEADER__ +#define __JUCE_HIGHRESOLUTIONTIMER_JUCEHEADER__ + +class JUCE_API HighResolutionTimer +{ +protected: + /** Creates a HighResolutionTimer. + When created, the timer is stopped, so use startTimer() to get it going. + */ + HighResolutionTimer(); + +public: + /** Destructor. */ + virtual ~HighResolutionTimer(); + + //============================================================================== + /** The user-defined callback routine that actually gets called periodically. + + This will be called on a dedicated timer thread, so make sure your + implementation is thread-safe! + + It's perfectly ok to call startTimer() or stopTimer() from within this + callback to change the subsequent intervals. + */ + virtual void hiResTimerCallback() = 0; + + //============================================================================== + /** Starts the timer and sets the length of interval required. + + If the timer is already started, this will reset its counter, so the + time between calling this method and the next timer callback will not be + less than the interval length passed in. + + @param intervalInMilliseconds the interval to use (any values less than 1 will be + rounded up to 1) + */ + void startTimer (int intervalInMilliseconds); + + /** Stops the timer. + + This method may block while it waits for pending callbacks to complete. Once it + returns, no more callbacks will be made. If it is called from the timer's own thread, + it will cancel the timer after the current callback returns. + */ + void stopTimer(); + + /** Checks if the timer has been started. + @returns true if the timer is running. + */ + bool isTimerRunning() const noexcept; + + /** Returns the timer's interval. + @returns the timer's interval in milliseconds if it's running, or 0 if it's not. + */ + int getTimerInterval() const noexcept; + +private: + struct Pimpl; + friend struct Pimpl; + friend class ScopedPointer; + ScopedPointer pimpl; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HighResolutionTimer) +}; + + +#endif // __JUCE_HIGHRESOLUTIONTIMER_JUCEHEADER__